Skip to content

Commit c6c2a23

Browse files
committed
Fix for stale internal database bug, closes #2605
1 parent 68f1179 commit c6c2a23

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

datasette/utils/internal_db.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ async def populate_schema_tables(internal_db, db):
116116
database_name = db.name
117117

118118
def delete_everything(conn):
119+
conn.execute(
120+
"DELETE FROM catalog_databases WHERE database_name = ?", [database_name]
121+
)
119122
conn.execute(
120123
"DELETE FROM catalog_tables WHERE database_name = ?", [database_name]
121124
)

tests/test_internal_db.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,51 @@ def inner(conn):
9191
)
9292

9393
await internal_db.execute_fn(inner)
94+
95+
96+
@pytest.mark.asyncio
97+
async def test_stale_catalog_entry_database_fix(tmp_path):
98+
"""
99+
Test for https://github.com/simonw/datasette/issues/2605
100+
101+
When the internal database persists across restarts and has entries in
102+
catalog_databases for databases that no longer exist, accessing the
103+
index page should not cause a 500 error (KeyError).
104+
"""
105+
from datasette.app import Datasette
106+
107+
internal_db_path = str(tmp_path / "internal.db")
108+
data_db_path = str(tmp_path / "data.db")
109+
110+
# Create a data database file
111+
import sqlite3
112+
113+
conn = sqlite3.connect(data_db_path)
114+
conn.execute("CREATE TABLE test_table (id INTEGER PRIMARY KEY)")
115+
conn.close()
116+
117+
# First Datasette instance: with the data database and persistent internal db
118+
ds1 = Datasette(files=[data_db_path], internal=internal_db_path)
119+
await ds1.invoke_startup()
120+
121+
# Access the index page to populate the internal catalog
122+
response = await ds1.client.get("/")
123+
assert "data" in ds1.databases
124+
assert response.status_code == 200
125+
126+
# Second Datasette instance: reusing internal.db but WITHOUT the data database
127+
# This simulates restarting Datasette after removing a database
128+
ds2 = Datasette(internal=internal_db_path)
129+
await ds2.invoke_startup()
130+
131+
# The database is not in ds2.databases
132+
assert "data" not in ds2.databases
133+
134+
# Accessing the index page should NOT cause a 500 error
135+
# This is the bug: it currently raises KeyError when trying to
136+
# access ds.databases["data"] for the stale catalog entry
137+
response = await ds2.client.get("/")
138+
assert response.status_code == 200, (
139+
f"Index page should return 200, not {response.status_code}. "
140+
"This fails due to stale catalog entries causing KeyError."
141+
)

0 commit comments

Comments
 (0)