mirror of
https://github.com/simonw/datasette.git
synced 2026-07-08 16:44:34 +02:00
Refine column_details metadata shape
This commit is contained in:
parent
8856914be8
commit
b476218edb
4 changed files with 101 additions and 25 deletions
|
|
@ -347,18 +347,32 @@ def column_detail_as_json(column):
|
|||
return {
|
||||
"type": column.type,
|
||||
"sqlite_type": SQLiteType.from_declared_type(column.type).value,
|
||||
"notnull": column.notnull,
|
||||
"notnull": bool(column.notnull),
|
||||
"default": column.default_value,
|
||||
"is_pk": bool(column.is_pk),
|
||||
"hidden": bool(column.hidden),
|
||||
"pk_position": column.is_pk,
|
||||
"hidden": column.hidden,
|
||||
}
|
||||
|
||||
|
||||
class ColumnDetailsExtra(Extra):
|
||||
description = (
|
||||
"SQLite schema details for columns in this table. The dictionary maps "
|
||||
"column names to ``type``, ``sqlite_type``, ``notnull``, ``default``, "
|
||||
"``is_pk`` and ``hidden`` values."
|
||||
"column names to objects describing the schema for each column."
|
||||
)
|
||||
docs_note = (
|
||||
"Each object has ``type`` as the declared type string returned by "
|
||||
'SQLite, or ``""`` if no type was declared; ``sqlite_type`` as the '
|
||||
"normalized SQLite affinity, one of ``TEXT``, ``INTEGER``, ``REAL``, "
|
||||
"``BLOB`` or ``NUMERIC``; ``notnull`` as a boolean; ``default`` "
|
||||
'as the raw SQL default expression string, such as ``"42"``, '
|
||||
"``\"'hello'\"`` or ``\"datetime('now')\"``, or ``null`` if there is "
|
||||
"no default; ``is_pk`` as a boolean; ``pk_position`` as the integer "
|
||||
"primary key position reported by SQLite, or ``0`` for columns that "
|
||||
"are not part of the primary key; and ``hidden`` as the integer value "
|
||||
"reported by SQLite's ``PRAGMA table_xinfo``. ``hidden`` is ``0`` for "
|
||||
"normal columns, ``1`` for hidden virtual table columns, ``2`` for "
|
||||
"virtual generated columns and ``3`` for stored generated columns."
|
||||
)
|
||||
example = ExtraExample("/fixtures/binary_data.json?_size=0&_extra=column_details")
|
||||
examples = {
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ The available table extras are listed below.
|
|||
]
|
||||
|
||||
``column_details``
|
||||
SQLite schema details for columns in this table. The dictionary maps column names to ``type``, ``sqlite_type``, ``notnull``, ``default``, ``is_pk`` and ``hidden`` values.
|
||||
SQLite schema details for columns in this table. The dictionary maps column names to objects describing the schema for each column. (Each object has ``type`` as the declared type string returned by SQLite, or ``""`` if no type was declared; ``sqlite_type`` as the normalized SQLite affinity, one of ``TEXT``, ``INTEGER``, ``REAL``, ``BLOB`` or ``NUMERIC``; ``notnull`` as a boolean; ``default`` as the raw SQL default expression string, such as ``"42"``, ``"'hello'"`` or ``"datetime('now')"``, or ``null`` if there is no default; ``is_pk`` as a boolean; ``pk_position`` as the integer primary key position reported by SQLite, or ``0`` for columns that are not part of the primary key; and ``hidden`` as the integer value reported by SQLite's ``PRAGMA table_xinfo``. ``hidden`` is ``0`` for normal columns, ``1`` for hidden virtual table columns, ``2`` for virtual generated columns and ``3`` for stored generated columns.)
|
||||
|
||||
``GET /fixtures/binary_data.json?_size=0&_extra=column_details``
|
||||
|
||||
|
|
@ -413,10 +413,11 @@ The available table extras are listed below.
|
|||
"data": {
|
||||
"type": "BLOB",
|
||||
"sqlite_type": "BLOB",
|
||||
"notnull": 0,
|
||||
"notnull": false,
|
||||
"default": null,
|
||||
"is_pk": false,
|
||||
"hidden": false
|
||||
"pk_position": 0,
|
||||
"hidden": 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -826,7 +827,7 @@ The following extras are available for row JSON responses.
|
|||
]
|
||||
|
||||
``column_details``
|
||||
SQLite schema details for columns in this table. The dictionary maps column names to ``type``, ``sqlite_type``, ``notnull``, ``default``, ``is_pk`` and ``hidden`` values.
|
||||
SQLite schema details for columns in this table. The dictionary maps column names to objects describing the schema for each column. (Each object has ``type`` as the declared type string returned by SQLite, or ``""`` if no type was declared; ``sqlite_type`` as the normalized SQLite affinity, one of ``TEXT``, ``INTEGER``, ``REAL``, ``BLOB`` or ``NUMERIC``; ``notnull`` as a boolean; ``default`` as the raw SQL default expression string, such as ``"42"``, ``"'hello'"`` or ``"datetime('now')"``, or ``null`` if there is no default; ``is_pk`` as a boolean; ``pk_position`` as the integer primary key position reported by SQLite, or ``0`` for columns that are not part of the primary key; and ``hidden`` as the integer value reported by SQLite's ``PRAGMA table_xinfo``. ``hidden`` is ``0`` for normal columns, ``1`` for hidden virtual table columns, ``2`` for virtual generated columns and ``3`` for stored generated columns.)
|
||||
|
||||
``GET /fixtures/binary_data/1.json?_extra=column_details``
|
||||
|
||||
|
|
@ -836,10 +837,11 @@ The following extras are available for row JSON responses.
|
|||
"data": {
|
||||
"type": "BLOB",
|
||||
"sqlite_type": "BLOB",
|
||||
"notnull": 0,
|
||||
"notnull": false,
|
||||
"default": null,
|
||||
"is_pk": false,
|
||||
"hidden": false
|
||||
"pk_position": 0,
|
||||
"hidden": 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -449,35 +449,36 @@ async def test_row_extras(ds_client):
|
|||
assert data["column_details"]["id"] == {
|
||||
"type": "INTEGER",
|
||||
"sqlite_type": "INTEGER",
|
||||
"notnull": 0,
|
||||
"notnull": False,
|
||||
"default": None,
|
||||
"is_pk": True,
|
||||
"hidden": False,
|
||||
"pk_position": 1,
|
||||
"hidden": 0,
|
||||
}
|
||||
assert data["column_details"]["content"] == {
|
||||
"type": "TEXT",
|
||||
"sqlite_type": "TEXT",
|
||||
"notnull": 0,
|
||||
"notnull": False,
|
||||
"default": None,
|
||||
"is_pk": False,
|
||||
"hidden": False,
|
||||
"pk_position": 0,
|
||||
"hidden": 0,
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_column_details_extra_row_for_null_blob(ds_client):
|
||||
response = await ds_client.get(
|
||||
"/fixtures/binary_data/3.json?_extra=column_details"
|
||||
)
|
||||
response = await ds_client.get("/fixtures/binary_data/3.json?_extra=column_details")
|
||||
assert response.status_code == 200
|
||||
assert response.json()["column_details"] == {
|
||||
"data": {
|
||||
"type": "BLOB",
|
||||
"sqlite_type": "BLOB",
|
||||
"notnull": 0,
|
||||
"notnull": False,
|
||||
"default": None,
|
||||
"is_pk": False,
|
||||
"hidden": False,
|
||||
"pk_position": 0,
|
||||
"hidden": 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1346,10 +1346,11 @@ async def test_column_details_extra_table(ds_client):
|
|||
"data": {
|
||||
"type": "BLOB",
|
||||
"sqlite_type": "BLOB",
|
||||
"notnull": 0,
|
||||
"notnull": False,
|
||||
"default": None,
|
||||
"is_pk": False,
|
||||
"hidden": False,
|
||||
"pk_position": 0,
|
||||
"hidden": 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1361,21 +1362,79 @@ async def test_column_details_extra_table(ds_client):
|
|||
"id": {
|
||||
"type": "INTEGER",
|
||||
"sqlite_type": "INTEGER",
|
||||
"notnull": 0,
|
||||
"notnull": False,
|
||||
"default": None,
|
||||
"is_pk": True,
|
||||
"hidden": False,
|
||||
"pk_position": 1,
|
||||
"hidden": 0,
|
||||
},
|
||||
"content": {
|
||||
"type": "TEXT",
|
||||
"sqlite_type": "TEXT",
|
||||
"notnull": 0,
|
||||
"notnull": False,
|
||||
"default": None,
|
||||
"is_pk": False,
|
||||
"hidden": False,
|
||||
"pk_position": 0,
|
||||
"hidden": 0,
|
||||
},
|
||||
}
|
||||
|
||||
response = await ds_client.get(
|
||||
"/fixtures/compound_three_primary_keys.json?_size=0&_extra=column_details"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
column_details = response.json()["column_details"]
|
||||
assert column_details["pk1"]["is_pk"] is True
|
||||
assert column_details["pk1"]["pk_position"] == 1
|
||||
assert column_details["pk2"]["is_pk"] is True
|
||||
assert column_details["pk2"]["pk_position"] == 2
|
||||
assert column_details["pk3"]["is_pk"] is True
|
||||
assert column_details["pk3"]["pk_position"] == 3
|
||||
assert column_details["content"]["is_pk"] is False
|
||||
assert column_details["content"]["pk_position"] == 0
|
||||
|
||||
|
||||
def test_column_details_extra_defaults_and_notnull():
|
||||
with make_app_client(extra_databases={"defaults.db": """
|
||||
CREATE TABLE defaults (
|
||||
i INTEGER NOT NULL DEFAULT 42,
|
||||
s TEXT DEFAULT 'hello',
|
||||
dt TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
"""}) as client:
|
||||
response = client.get("/defaults/defaults.json?_size=0&_extra=column_details")
|
||||
assert response.status == 200
|
||||
column_details = response.json["column_details"]
|
||||
assert column_details["i"]["notnull"] is True
|
||||
assert column_details["i"]["default"] == "42"
|
||||
assert column_details["s"]["notnull"] is False
|
||||
assert column_details["s"]["default"] == "'hello'"
|
||||
assert column_details["dt"]["default"] == "datetime('now')"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sqlite_version() < (3, 31, 0),
|
||||
reason="generated columns were added in SQLite 3.31.0",
|
||||
)
|
||||
def test_column_details_extra_generated_columns():
|
||||
with make_app_client(extra_databases={"generated.db": """
|
||||
CREATE TABLE generated_columns (
|
||||
body TEXT,
|
||||
body_length_virtual INTEGER
|
||||
GENERATED ALWAYS AS (length(body)) VIRTUAL,
|
||||
body_length_stored INTEGER
|
||||
GENERATED ALWAYS AS (length(body)) STORED
|
||||
);
|
||||
"""}) as client:
|
||||
response = client.get(
|
||||
"/generated/generated_columns.json?_size=0&_extra=column_details"
|
||||
)
|
||||
assert response.status == 200
|
||||
column_details = response.json["column_details"]
|
||||
assert column_details["body"]["hidden"] == 0
|
||||
assert column_details["body_length_virtual"]["hidden"] == 2
|
||||
assert column_details["body_length_stored"]["hidden"] == 3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue