catalog_views table, closes #2495

Refs https://github.com/datasette/datasette-queries/issues/1#issuecomment-3074491003
This commit is contained in:
Simon Willison 2025-07-15 10:22:56 -07:00
commit 7a602140df
3 changed files with 46 additions and 1 deletions

View file

@ -19,6 +19,14 @@ async def init_internal_db(db):
PRIMARY KEY (database_name, table_name), PRIMARY KEY (database_name, table_name),
FOREIGN KEY (database_name) REFERENCES catalog_databases(database_name) FOREIGN KEY (database_name) REFERENCES catalog_databases(database_name)
); );
CREATE TABLE IF NOT EXISTS catalog_views (
database_name TEXT,
view_name TEXT,
rootpage INTEGER,
sql TEXT,
PRIMARY KEY (database_name, view_name),
FOREIGN KEY (database_name) REFERENCES catalog_databases(database_name)
);
CREATE TABLE IF NOT EXISTS catalog_columns ( CREATE TABLE IF NOT EXISTS catalog_columns (
database_name TEXT, database_name TEXT,
table_name TEXT, table_name TEXT,
@ -111,6 +119,9 @@ async def populate_schema_tables(internal_db, db):
conn.execute( conn.execute(
"DELETE FROM catalog_tables WHERE database_name = ?", [database_name] "DELETE FROM catalog_tables WHERE database_name = ?", [database_name]
) )
conn.execute(
"DELETE FROM catalog_views WHERE database_name = ?", [database_name]
)
conn.execute( conn.execute(
"DELETE FROM catalog_columns WHERE database_name = ?", [database_name] "DELETE FROM catalog_columns WHERE database_name = ?", [database_name]
) )
@ -125,13 +136,21 @@ async def populate_schema_tables(internal_db, db):
await internal_db.execute_write_fn(delete_everything) await internal_db.execute_write_fn(delete_everything)
tables = (await db.execute("select * from sqlite_master WHERE type = 'table'")).rows tables = (await db.execute("select * from sqlite_master WHERE type = 'table'")).rows
views = (await db.execute("select * from sqlite_master WHERE type = 'view'")).rows
def collect_info(conn): def collect_info(conn):
tables_to_insert = [] tables_to_insert = []
views_to_insert = []
columns_to_insert = [] columns_to_insert = []
foreign_keys_to_insert = [] foreign_keys_to_insert = []
indexes_to_insert = [] indexes_to_insert = []
for view in views:
view_name = view["name"]
views_to_insert.append(
(database_name, view_name, view["rootpage"], view["sql"])
)
for table in tables: for table in tables:
table_name = table["name"] table_name = table["name"]
tables_to_insert.append( tables_to_insert.append(
@ -165,6 +184,7 @@ async def populate_schema_tables(internal_db, db):
) )
return ( return (
tables_to_insert, tables_to_insert,
views_to_insert,
columns_to_insert, columns_to_insert,
foreign_keys_to_insert, foreign_keys_to_insert,
indexes_to_insert, indexes_to_insert,
@ -172,6 +192,7 @@ async def populate_schema_tables(internal_db, db):
( (
tables_to_insert, tables_to_insert,
views_to_insert,
columns_to_insert, columns_to_insert,
foreign_keys_to_insert, foreign_keys_to_insert,
indexes_to_insert, indexes_to_insert,
@ -184,6 +205,13 @@ async def populate_schema_tables(internal_db, db):
""", """,
tables_to_insert, tables_to_insert,
) )
await internal_db.execute_write_many(
"""
INSERT INTO catalog_views (database_name, view_name, rootpage, sql)
values (?, ?, ?, ?)
""",
views_to_insert,
)
await internal_db.execute_write_many( await internal_db.execute_write_many(
""" """
INSERT INTO catalog_columns ( INSERT INTO catalog_columns (

View file

@ -1378,7 +1378,7 @@ Datasette's internal database
Datasette maintains an "internal" SQLite database used for configuration, caching, and storage. Plugins can store configuration, settings, and other data inside this database. By default, Datasette will use a temporary in-memory SQLite database as the internal database, which is created at startup and destroyed at shutdown. Users of Datasette can optionally pass in a ``--internal`` flag to specify the path to a SQLite database to use as the internal database, which will persist internal data across Datasette instances. Datasette maintains an "internal" SQLite database used for configuration, caching, and storage. Plugins can store configuration, settings, and other data inside this database. By default, Datasette will use a temporary in-memory SQLite database as the internal database, which is created at startup and destroyed at shutdown. Users of Datasette can optionally pass in a ``--internal`` flag to specify the path to a SQLite database to use as the internal database, which will persist internal data across Datasette instances.
Datasette maintains tables called ``catalog_databases``, ``catalog_tables``, ``catalog_columns``, ``catalog_indexes``, ``catalog_foreign_keys`` with details of the attached databases and their schemas. These tables should not be considered a stable API - they may change between Datasette releases. Datasette maintains tables called ``catalog_databases``, ``catalog_tables``, ``catalog_views``, ``catalog_columns``, ``catalog_indexes``, ``catalog_foreign_keys`` with details of the attached databases and their schemas. These tables should not be considered a stable API - they may change between Datasette releases.
Metadata is stored in tables ``metadata_instance``, ``metadata_databases``, ``metadata_resources`` and ``metadata_columns``. Plugins can interact with these tables via the :ref:`get_*_metadata() and set_*_metadata() methods <datasette_get_set_metadata>`. Metadata is stored in tables ``metadata_instance``, ``metadata_databases``, ``metadata_resources`` and ``metadata_columns``. Plugins can interact with these tables via the :ref:`get_*_metadata() and set_*_metadata() methods <datasette_get_set_metadata>`.
@ -1421,6 +1421,14 @@ The internal database schema is as follows:
PRIMARY KEY (database_name, table_name), PRIMARY KEY (database_name, table_name),
FOREIGN KEY (database_name) REFERENCES catalog_databases(database_name) FOREIGN KEY (database_name) REFERENCES catalog_databases(database_name)
); );
CREATE TABLE catalog_views (
database_name TEXT,
view_name TEXT,
rootpage INTEGER,
sql TEXT,
PRIMARY KEY (database_name, view_name),
FOREIGN KEY (database_name) REFERENCES catalog_databases(database_name)
);
CREATE TABLE catalog_columns ( CREATE TABLE catalog_columns (
database_name TEXT, database_name TEXT,
table_name TEXT, table_name TEXT,

View file

@ -25,6 +25,15 @@ async def test_internal_tables(ds_client):
assert set(table.keys()) == {"rootpage", "table_name", "database_name", "sql"} assert set(table.keys()) == {"rootpage", "table_name", "database_name", "sql"}
@pytest.mark.asyncio
async def test_internal_views(ds_client):
internal_db = await ensure_internal(ds_client)
views = await internal_db.execute("select * from catalog_views")
assert len(views) >= 4
view = views.rows[0]
assert set(view.keys()) == {"rootpage", "view_name", "database_name", "sql"}
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_internal_indexes(ds_client): async def test_internal_indexes(ds_client):
internal_db = await ensure_internal(ds_client) internal_db = await ensure_internal(ds_client)