diff --git a/src/langbot/pkg/persistence/alembic/versions/0041_resource_urls.py b/src/langbot/pkg/persistence/alembic/versions/0041_resource_urls.py index 3c5c8821f..a3fc73b7c 100644 --- a/src/langbot/pkg/persistence/alembic/versions/0041_resource_urls.py +++ b/src/langbot/pkg/persistence/alembic/versions/0041_resource_urls.py @@ -70,12 +70,29 @@ def upgrade(): cursor = rows[-1][key.name] +def _ensure_sqlite_expansion(connection) -> None: + """Install the expansion UDF on this already-open SQLite connection. + + Downgrade is often run by a standalone migration process whose engine was + created outside the application boot path, so the ``connect`` hook never + registered `langbot_expand_resource_urls` for the connection in use. + """ + from langbot.pkg.persistence.resource_urls import register_sqlite + + dbapi = getattr(connection.connection, 'dbapi_connection', None) + if dbapi is None: + raise RuntimeError('SQLite resource URL downgrade requires a DBAPI connection') + register_sqlite(dbapi, None) + + def downgrade(): from langbot.pkg.entity.persistence.base import Base from langbot.pkg.persistence.media import MediaReferenceText, MediaReferenceJSON from langbot.pkg.persistence.resource_urls import uninstall connection = op.get_bind() + if connection.dialect.name == 'sqlite': + _ensure_sqlite_expansion(connection) existing = set(sa.inspect(connection).get_table_names()) for table in sorted(Base.metadata.tables.values(), key=lambda table: table.name): if table.name not in existing: diff --git a/src/langbot/pkg/persistence/resource_urls.py b/src/langbot/pkg/persistence/resource_urls.py index bdbde85b3..9d0408cab 100644 --- a/src/langbot/pkg/persistence/resource_urls.py +++ b/src/langbot/pkg/persistence/resource_urls.py @@ -148,7 +148,10 @@ def uninstall(engine): sa.event.remove(engine, 'before_cursor_execute', normalize_parameters) engine.dialect._langbot_resource_urls = False engine.clear_compiled_cache() - engine._langbot_resource_aliases.clear() + # A migration process may uninstall an engine that never ran install(). + aliases = getattr(engine, '_langbot_resource_aliases', None) + if aliases is not None: + aliases.clear() def normalize_parameters(connection, cursor, statement, parameters, context, executemany):