Files
iAOP/core/data-bus/postgres_schema.py
T

117 lines
4.4 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""PostgreSQL 关系表 schema(模板 / 模型 / 用户 / 权限)—— 子任务 #30。
按模板自动生成隔离 schema(`tpl_{tpl}`),内含四张基础关系表:
- templates 模板注册表(行业模板配置资产);
- models 模型注册表(模型版本 / 算法 / 超参包);
- users 用户表(角色 + 细粒度权限 JSONB);
- permissions 角色-资源-动作权限矩阵。
另生成角色授权语句(GRANT),按模板配置声明 databus_ro / databus_rw 等角色。
不依赖 psycopg2:仅产出 DDL 文本,供模板配置台预览 / 运维执行。
"""
from __future__ import annotations
from typing import List, Optional
from .templating import TemplateNaming
# 默认基础表清单(对应子任务 #30「模板/模型/用户/权限」)
DEFAULT_TABLES: List[str] = ["templates", "models", "users", "permissions"]
# 表 → 列定义(可被模板 YAML 的 schema_tables 覆盖为子集/扩展)
TABLE_COLUMNS: dict = {
"templates": [
"id BIGSERIAL PRIMARY KEY",
"template VARCHAR(64) NOT NULL UNIQUE",
"version VARCHAR(32) NOT NULL DEFAULT '0.1.0'",
"config JSONB NOT NULL DEFAULT '{{}}'::jsonb", # format() 转义 {}
"status VARCHAR(16) NOT NULL DEFAULT 'draft'",
"created_at TIMESTAMPTZ NOT NULL DEFAULT now()",
],
"models": [
"id BIGSERIAL PRIMARY KEY",
"model_id VARCHAR(128) NOT NULL UNIQUE",
"template VARCHAR(64) REFERENCES {schema}.templates(template) ON DELETE CASCADE",
"algorithm VARCHAR(32) NOT NULL DEFAULT 'xgboost'",
"hyperparams JSONB NOT NULL DEFAULT '{{}}'::jsonb", # format() 转义 {}
"version VARCHAR(32) NOT NULL DEFAULT '0.1.0'",
"status VARCHAR(16) NOT NULL DEFAULT 'staging'",
"created_at TIMESTAMPTZ NOT NULL DEFAULT now()",
],
"users": [
"id BIGSERIAL PRIMARY KEY",
"username VARCHAR(64) NOT NULL UNIQUE",
"role VARCHAR(32) NOT NULL DEFAULT 'viewer'",
"permissions JSONB NOT NULL DEFAULT '[]'::jsonb",
"created_at TIMESTAMPTZ NOT NULL DEFAULT now()",
],
"permissions": [
"id BIGSERIAL PRIMARY KEY",
"role VARCHAR(32) NOT NULL",
"resource VARCHAR(64) NOT NULL",
"action VARCHAR(16) NOT NULL",
"UNIQUE (role, resource, action)",
],
}
def generate_schema_ddl(
naming: TemplateNaming,
tables: Optional[List[str]] = None,
table_columns: Optional[dict] = None,
) -> str:
"""生成 schema 与基础关系表的完整 DDL(幂等 IF NOT EXISTS)。
Args:
naming: 模板命名器(决定 schema 名)。
tables: 表清单(默认 DEFAULT_TABLES)。
table_columns: 表→列定义覆盖(默认 TABLE_COLUMNS)。
Returns:
多语句 DDL 文本(含 CREATE SCHEMA / CREATE TABLE)。
"""
names = tables or DEFAULT_TABLES
cols = table_columns or TABLE_COLUMNS
schema = naming.pg_schema()
lines = [f"CREATE SCHEMA IF NOT EXISTS {schema};", ""]
for table in names:
columns = cols.get(table)
if not columns:
continue
lines.append(f"CREATE TABLE IF NOT EXISTS {schema}.{table} (")
lines.append(" " + ",\n ".join(c.format(schema=schema) for c in columns))
lines.append(");")
lines.append("")
return "\n".join(lines).rstrip() + "\n"
def generate_grant_ddl(
naming: TemplateNaming,
roles: Optional[List[str]] = None,
) -> List[str]:
"""按模板声明的关系角色生成授权语句。
角色约定:`{role}_ro` 只读、`{role}_rw` 读写(可扩展)。
Args:
naming: 模板命名器。
roles: 角色清单(如 ["databus"] → databus_ro / databus_rw)。
Returns:
GRANT 语句列表。
"""
schema = naming.pg_schema()
grants: List[str] = []
for role in (roles or ["databus"]):
for suffix, privileges in (("ro", "SELECT"), ("rw", "SELECT, INSERT, UPDATE, DELETE")):
role_name = f"{role}_{suffix}"
grants.append(f"GRANT USAGE ON SCHEMA {schema} TO {role_name};")
grants.append(
f"GRANT {privileges} ON ALL TABLES IN SCHEMA {schema} TO {role_name};"
)
if suffix == "rw":
grants.append(
f"GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA {schema} TO {role_name};"
)
return grants