From 8f69dd00861c79834bbd03948250f5f50ad8687e Mon Sep 17 00:00:00 2001 From: Fantix King Date: Tue, 27 Aug 2024 19:57:28 -0400 Subject: [PATCH] Add sys::Branch.last_migration (#7654) `select sys::Branch { name, last_migration }` will list all branch names with the names of their latest migrations. Note that `last_migration` could be an empty set if the branch is empty. --- edb/buildmeta.py | 2 +- edb/lib/sys.edgeql | 1 + edb/pgsql/dbops/databases.py | 75 +++++++++++++++-------------- edb/pgsql/delta.py | 16 +++++- edb/pgsql/metaschema.py | 1 + tests/test_edgeql_data_migration.py | 22 +++++++++ 6 files changed, 79 insertions(+), 38 deletions(-) diff --git a/edb/buildmeta.py b/edb/buildmeta.py index 76abff2612a..26a0edbadf1 100644 --- a/edb/buildmeta.py +++ b/edb/buildmeta.py @@ -60,7 +60,7 @@ # The merge conflict there is a nice reminder that you probably need # to write a patch in edb/pgsql/patches.py, and then you should preserve # the old value. -EDGEDB_CATALOG_VERSION = 2024_08_23_00_00 +EDGEDB_CATALOG_VERSION = 2024_08_27_00_00 EDGEDB_MAJOR_VERSION = 6 diff --git a/edb/lib/sys.edgeql b/edb/lib/sys.edgeql index 371eb1b2e46..9b3f4b9f5c7 100644 --- a/edb/lib/sys.edgeql +++ b/edb/lib/sys.edgeql @@ -39,6 +39,7 @@ CREATE TYPE sys::Branch EXTENDING ALTER PROPERTY name { CREATE CONSTRAINT std::exclusive; }; + CREATE PROPERTY last_migration-> std::str; }; CREATE ALIAS sys::Database := sys::Branch; diff --git a/edb/pgsql/dbops/databases.py b/edb/pgsql/dbops/databases.py index 72ebcbdb94b..5672952d946 100644 --- a/edb/pgsql/dbops/databases.py +++ b/edb/pgsql/dbops/databases.py @@ -30,7 +30,32 @@ from . import ddl -class Database(base.DBObject): +class AbstractDatabase(base.DBObject): + def get_type(self): + return 'DATABASE' + + def is_shared(self) -> bool: + return True + + def _get_id_expr(self) -> str: + raise NotImplementedError() + + def get_oid(self) -> base.Query: + qry = textwrap.dedent(f'''\ + SELECT + 'pg_database'::regclass::oid AS classoid, + pg_database.oid AS objectoid, + 0 + FROM + pg_database + WHERE + datname = {self._get_id_expr()} + ''') + + return base.Query(text=qry) + + +class Database(AbstractDatabase): def __init__( self, name: str, @@ -50,28 +75,11 @@ def __init__( self.lc_collate = lc_collate self.lc_ctype = lc_ctype - def get_type(self): - return 'DATABASE' - def get_id(self): return qi(self.name) - def is_shared(self) -> bool: - return True - - def get_oid(self) -> base.Query: - qry = textwrap.dedent(f'''\ - SELECT - 'pg_database'::regclass::oid AS classoid, - pg_database.oid AS objectoid, - 0 - FROM - pg_database - WHERE - datname = {ql(self.name)} - ''') - - return base.Query(text=qry) + def _get_id_expr(self) -> str: + return ql(self.name) class DatabaseWithTenant(Database): @@ -81,24 +89,19 @@ def __init__( ) -> None: super().__init__(name=name) - def get_id(self): - dyn_db = f"{V('edgedb')}.get_database_backend_name({ql(self.name)})" - return f"' || quote_ident({dyn_db}) || '" + def get_id(self) -> str: + return f"' || quote_ident({self._get_id_expr()}) || '" - def get_oid(self) -> base.Query: - qry = textwrap.dedent(f'''\ - SELECT - 'pg_database'::regclass::oid AS classoid, - pg_database.oid AS objectoid, - 0 - FROM - pg_database - WHERE - datname = - {V("edgedb")}.get_database_backend_name({ql(self.name)}) - ''') + def _get_id_expr(self) -> str: + return f'{V("edgedb")}.get_database_backend_name({ql(self.name)})' - return base.Query(text=qry) + +class CurrentDatabase(AbstractDatabase): + def get_id(self) -> str: + return f"' || quote_ident({self._get_id_expr()}) || '" + + def _get_id_expr(self) -> str: + return 'current_database()' class DatabaseExists(base.Condition): diff --git a/edb/pgsql/delta.py b/edb/pgsql/delta.py index e525fa3c2c5..89b10166c1f 100644 --- a/edb/pgsql/delta.py +++ b/edb/pgsql/delta.py @@ -7301,7 +7301,21 @@ def apply( class MigrationCommand(MetaCommand): - pass + def apply( + self, + schema: s_schema.Schema, + context: sd.CommandContext, + ) -> s_schema.Schema: + schema = super().apply(schema, context) + if last_mig := schema.get_last_migration(): + last_mig_name = last_mig.get_name(schema).name + else: + last_mig_name = None + self.pgops.add(dbops.UpdateMetadata( + dbops.CurrentDatabase(), + {'last_migration': last_mig_name}, + )) + return schema class CreateMigration( diff --git a/edb/pgsql/metaschema.py b/edb/pgsql/metaschema.py index adfec07bc93..d6b07af1baf 100644 --- a/edb/pgsql/metaschema.py +++ b/edb/pgsql/metaschema.py @@ -5098,6 +5098,7 @@ def _generate_branch_views(schema: s_schema.Schema) -> List[dbops.View]: ), 'computed_fields': 'ARRAY[]::text[]', 'builtin': "((d.description)->>'builtin')::bool", + 'last_migration': "(d.description)->>'last_migration'", } view_query = f''' diff --git a/tests/test_edgeql_data_migration.py b/tests/test_edgeql_data_migration.py index 7015d49d350..b4ee6cbd549 100644 --- a/tests/test_edgeql_data_migration.py +++ b/tests/test_edgeql_data_migration.py @@ -216,6 +216,7 @@ async def migrate( explicit_modules=explicit_modules, ) await self.fast_forward_describe_migration(user_input=user_input) + await self.assert_last_migration() async def interact(self, parts, check_complete=True): for part in parts: @@ -243,6 +244,25 @@ async def interact(self, parts, check_complete=True): 'complete': True }) + async def assert_last_migration(self): + last_name = await self.con.query_single( + ''' + select assert_single( + sys::Branch.last_migration + filter sys::Branch.name = sys::get_current_database()); + ''' + ) + last_mig = await self.con.query_single( + ''' + select assert_single( + schema::Migration { name } filter not exists .