Skip to content

Commit 0836e50

Browse files
lesebclaude
andcommitted
fix(sqlstore): create tables registered after engine initialization
When a table was registered via create_table() after _ensure_engine() had already called metadata.create_all(), the table was added to SQLAlchemy's MetaData but never physically created in the database. This caused "no such table" errors when providers initialized in a sequence where one provider triggered the engine before another registered its tables (e.g. vector_io before responses). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Sébastien Han <seb@redhat.com>
1 parent 41fb6a3 commit 0836e50

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

src/ogx/core/storage/sqlstore/sqlalchemy_sqlstore.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,13 @@ async def create_table(
185185
# Register table in metadata - actual creation happens in _ensure_engine()
186186
if table not in self.metadata.tables:
187187
Table(table, self.metadata, *sqlalchemy_columns)
188-
# If table already exists in metadata, we're done (no need to recreate)
188+
189+
# If engine is already running, create the new table immediately.
190+
# _ensure_engine() only calls create_all once, so tables registered
191+
# after that first call would never be physically created.
192+
if self._engine is not None:
193+
async with self._engine.begin() as conn:
194+
await conn.run_sync(self.metadata.create_all, checkfirst=True)
189195

190196
async def insert(self, table: str, data: Mapping[str, Any] | Sequence[Mapping[str, Any]]) -> None:
191197
await self._ensure_engine() # Lazy init in current event loop

tests/unit/utils/sqlstore/test_sqlstore.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,34 @@ async def test_pool_recycle_is_configurable():
602602
pool_recycle=300,
603603
)
604604
assert cfg.pool_recycle == 300
605+
606+
607+
async def test_late_table_creation_after_engine_init():
608+
"""Tables registered after the engine has started are still physically created.
609+
610+
When one provider triggers _ensure_engine (via a data operation) before another
611+
provider registers its tables, the late tables must still be created in the
612+
database. Regression test for the 'no such table: responses' CI failure.
613+
"""
614+
with TemporaryDirectory() as tmp_dir:
615+
db_path = tmp_dir + "/late_table.db"
616+
config = SqliteSqlStoreConfig(db_path=db_path)
617+
store = SqlAlchemySqlStoreImpl(config)
618+
619+
await store.create_table(
620+
"early_table",
621+
{"id": ColumnDefinition(type=ColumnType.STRING, primary_key=True), "data": ColumnType.STRING},
622+
)
623+
await store.insert("early_table", {"id": "1", "data": "hello"})
624+
625+
await store.create_table(
626+
"late_table",
627+
{"id": ColumnDefinition(type=ColumnType.STRING, primary_key=True), "value": ColumnType.STRING},
628+
)
629+
await store.insert("late_table", {"id": "a", "value": "world"})
630+
631+
result = await store.fetch_all("late_table")
632+
assert len(result.data) == 1
633+
assert result.data[0]["value"] == "world"
634+
635+
await store.shutdown()

0 commit comments

Comments
 (0)