Skip to content

Enhancement: Way to hook into the database CLI #365

@DataDaoDe

Description

@DataDaoDe

Summary

I would like to extend the database command to be able to do something like the following:

class DatabaseCLIPlugin(CLIPluginProtocol):
    def on_cli_init(self, cli: Group) -> None:
        db_group = get_alchemy_group()

        @db_group.command(name="create-database", help="Uses the config connection_string to create a new database if none exists")
        def create_database():
            ....

I can't seem to figure out a way to hook into the cli and extend the advanced alchemy command though. I can roll my db cli command and integrate that with litestar, but I'd rather not have two commands i.e. one called database and then my custom one called db.

Does anyone have any ideas?

Alternatively or additionally, is there any interest in taking up two new commands: create-db and drop-db which create and drop the database respectively. I tend to have these in a lot of my projects.

Thanks!

Basic Example

class CLIPlugin(CLIPluginProtocol):
    def on_cli_init(self, cli: Group) -> None:
        console = get_console()

        # for create/drop commands we cannot be connected to the db we want to create/drop
        app_db_url = urlparse(settings.DATABASE_URL)
        app_db_name = app_db_url.path.replace("/", "")
        pg_db_url_result = app_db_url._replace(path="/postgres")
        pg_db_url = urlunparse(pg_db_url_result)

        async_engine = create_async_engine(
            url=pg_db_url,
            execution_options={"isolation_level": "AUTOCOMMIT"},
        )

        @cli.command(name="db-create")
        def create_database(app: Litestar) -> None:
            async def _create_db():
                async with async_engine.begin() as conn:
                    try:
                        sql = "CREATE DATABASE {} ENCODING '{}'".format(
                            app_db_name, "utf-8"
                        )
                        await conn.execute(text(sql))
                    except DatabaseError as err:
                        console.print(err)

            asyncio.run(_create_db())

        @cli.command(name="db-drop")
        def drop_database(app: Litestar) -> None:
            async def _drop_db():
                async with async_engine.connect() as conn:
                    try:
                        conn = await conn.execution_options(
                            isolation_level="AUTOCOMMIT"
                        )

                        # We must first disconnect all users from the database we are dropping.
                        version = conn.dialect.server_version_info
                        pid_column = "pid" if (version >= (9, 2)) else "procpid"
                        drop_user_conn_sql = """SELECT pg_terminate_backend(pg_stat_activity.{pid_column})
                          FROM pg_stat_activity
                          WHERE pg_stat_activity.datname = '{database}'
                          AND {pid_column} <> pg_backend_pid();""".format(
                            pid_column=pid_column, database=app_db_name
                        )
                        await conn.execute(text(drop_user_conn_sql))

                        drop_sql = "DROP DATABASE {}".format(app_db_name)
                        await conn.execute(text(drop_sql))

                        console.print(f"dropped database '{app_db_name}'")
                    except DatabaseError as err:
                        console.print(f"Error dropping database: {err}")
                    except Exception as ex:
                        console.print(f"Unexpected error: {ex}")
                await async_engine.dispose()

            asyncio.run(_drop_db())

Drawbacks and Impact

No response

Unresolved questions

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions