"""匠厂数据管理展示元数据(``_jiangchang_tables`` / ``_jiangchang_columns``)。 SQLite 没有原生表/字段 COMMENT,宿主「数据管理」页面靠这两张元数据表 拿到中文表名/列名/可编辑性等。字段顺序由 ``CREATE TABLE`` + ``PRAGMA table_info`` 的 ``cid`` 决定,不读 ``display_order``。 本文件是模板 ``scripts/db/display_metadata.py`` 的最小可用子集: - ``init_display_metadata_tables``:建元数据表 - ``upsert_table_metadata`` / ``upsert_column_metadata``:写入展示元数据 - ``list_physical_column_names``:按 cid 列出物理字段名 """ from __future__ import annotations from typing import Iterable # 宿主隐藏这些表,不作为业务表展示 INTERNAL_METADATA_TABLES = frozenset({"_jiangchang_tables", "_jiangchang_columns"}) _JIANGCHANG_TABLES_DDL = """ CREATE TABLE IF NOT EXISTS _jiangchang_tables ( table_name TEXT PRIMARY KEY, display_name TEXT NOT NULL, description TEXT, grid_help TEXT, visible INTEGER NOT NULL DEFAULT 1, readonly INTEGER NOT NULL DEFAULT 0, default_sort_json TEXT, default_visible_columns_json TEXT ) """ _JIANGCHANG_COLUMNS_DDL = """ CREATE TABLE IF NOT EXISTS _jiangchang_columns ( table_name TEXT NOT NULL, column_name TEXT NOT NULL, display_name TEXT NOT NULL, description TEXT, grid_help TEXT, display_order INTEGER, visible INTEGER NOT NULL DEFAULT 1, searchable INTEGER NOT NULL DEFAULT 1, editable INTEGER NOT NULL DEFAULT 1, display_type TEXT, options_json TEXT, PRIMARY KEY (table_name, column_name) ) """ def init_display_metadata_tables(cursor) -> None: cursor.execute(_JIANGCHANG_TABLES_DDL) cursor.execute(_JIANGCHANG_COLUMNS_DDL) def upsert_table_metadata( cursor, *, table_name: str, display_name: str, description: str | None = None, grid_help: str | None = None, visible: int = 1, readonly: int = 0, default_sort_json: str | None = None, default_visible_columns_json: str | None = None, ) -> None: cursor.execute( """ INSERT INTO _jiangchang_tables ( table_name, display_name, description, grid_help, visible, readonly, default_sort_json, default_visible_columns_json ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(table_name) DO UPDATE SET display_name = excluded.display_name, description = excluded.description, grid_help = excluded.grid_help, visible = excluded.visible, readonly = excluded.readonly, default_sort_json = excluded.default_sort_json, default_visible_columns_json = excluded.default_visible_columns_json """, ( table_name, display_name, description, grid_help, visible, readonly, default_sort_json, default_visible_columns_json, ), ) def upsert_column_metadata( cursor, *, table_name: str, column_name: str, display_name: str, description: str | None = None, grid_help: str | None = None, visible: int = 1, searchable: int = 1, editable: int = 1, display_type: str | None = None, options_json: str | None = None, ) -> None: cursor.execute( """ INSERT INTO _jiangchang_columns ( table_name, column_name, display_name, description, grid_help, display_order, visible, searchable, editable, display_type, options_json ) VALUES (?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?) ON CONFLICT(table_name, column_name) DO UPDATE SET display_name = excluded.display_name, description = excluded.description, grid_help = excluded.grid_help, display_order = NULL, visible = excluded.visible, searchable = excluded.searchable, editable = excluded.editable, display_type = excluded.display_type, options_json = excluded.options_json """, ( table_name, column_name, display_name, description, grid_help, visible, searchable, editable, display_type, options_json, ), ) def seed_column_metadata_batch( cursor, *, table_name: str, columns: Iterable[dict[str, object]], ) -> None: """批量 upsert 字段元数据。""" for column in columns: upsert_column_metadata( cursor, table_name=table_name, column_name=str(column["column_name"]), display_name=str(column["display_name"]), description=str(column["description"]) if column.get("description") else None, grid_help=str(column["grid_help"]) if column.get("grid_help") else None, visible=int(column.get("visible", 1)), searchable=int(column.get("searchable", 1)), editable=int(column.get("editable", 1)), display_type=str(column["display_type"]) if column.get("display_type") else None, options_json=str(column["options_json"]) if column.get("options_json") else None, ) def _quote_identifier(name: str) -> str: return '"' + name.replace('"', '""') + '"' def list_physical_column_names(cursor, table_name: str) -> list[str]: cursor.execute(f"PRAGMA table_info({_quote_identifier(table_name)})") rows = cursor.fetchall() rows.sort(key=lambda row: int(row[0])) return [str(row[1]) for row in rows]