mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 01:51:57 +00:00
7febf2bcdb
- 编排者的决定:PostgreSQL 下文本列写入时去掉 NUL 字符(before_cursor_execute 钩子,只装在应用库引擎上,看所有字符串参数,没有 NUL 的原样交回),SQLite 不变;同 JSON 列的处理 - R-a4 应用库地址里的 sslmode= 换成 asyncpg 的 ssl=(取值同一套),sslrootcert 等证书参数、application_name 这类 asyncpg 不收的参数启动时就报错并说怎么写(证书用 PGSSLROOTCERT 等环境变量);断点库连接串换回 sslmode= - R-a6 不分大小写的搜索换成 db/portable.contains_ci:SQLite 上同 ilike(lower LIKE lower),PostgreSQL 上模式带 COLLATE "default"(列是 COLLATE "C",列上的 ILIKE 只认 ASCII,É/é、Ж/ж、全角A/a 匹配不上);运行列表按名字、对象索引按注释两处都改;改正 db/postgres 里那句说明 - R-a7 JSON 守卫改成编译层:tests/jsonguard 挂在应用库编译器上(conftest,两种库、跑全量时一直生效),整个 JSON 值出现在 = / != / IN / IS DISTINCT FROM / 大小比较、SELECT DISTINCT、GROUP BY、ORDER BY、UNION(去重的几种)里就报错;按类型认,变量别名、跨行 order_by、select(Run.origin).distinct()、JSON 下标的比较都抓得到,测试拿这些反例证明,json_eq、IS NULL、UNION ALL 不受影响。原来的逐行正则测试删掉 - R-a10 json_eq 两种库同一语义、按 JSON 类型比:字符串只等于字符串,数只等于数(1 等于 1.0),True / False 只等于 JSON 的 true / false(SQLite 上用 json_type 分开,以前 1 能匹配 true);非标量 TypeError。json_text 两种库都是「字符串取本身、别的取 JSON 写法」(SQLite 上同 PostgreSQL 的 ->>),数 1 的 json_text 两边都是 '1' - db/postgres 说明里写清 NULL 排序补写的范围(只认直接按列排的)
64 lines
3.4 KiB
Python
64 lines
3.4 KiB
Python
"""测试时挂在应用库编译器上的守卫:整个 JSON 值做比较、去重、分组、排序就报错(两种库都挂,跑全量时一直生效)。
|
|
|
|
PostgreSQL 的 json 没有相等和大小的运算符:`=`、`!=`、`IN`、`IS DISTINCT FROM`、`<` 之类,SELECT DISTINCT、GROUP BY、
|
|
ORDER BY、UNION(去重的那几种)碰到 json 类型的值都报错;SQLite 照常执行。只在 PostgreSQL 上跑到才发现太晚:这里在编译
|
|
那一刻就拦下,SQLite 的测试也拦得到。按表达式的类型认,不按写法认:跨行的 order_by、变量别名、
|
|
`select(Run.origin).distinct()`、JSON 的下标(`Run.origin["k"] == 1`,取出来的还是 json)都算。按键筛选用 db/portable 的
|
|
json_eq 等(类型是布尔、文本),不受影响。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from sqlalchemy import JSON
|
|
from sqlalchemy.sql import operators
|
|
from sqlalchemy.sql.elements import UnaryExpression
|
|
|
|
|
|
class JsonMisuse(AssertionError):
|
|
"""整个 JSON 值被拿去比较、去重、分组或排序。"""
|
|
|
|
|
|
_COMPARE = {operators.eq, operators.ne, operators.in_op, operators.not_in_op, operators.is_distinct_from,
|
|
operators.is_not_distinct_from, operators.lt, operators.le, operators.gt, operators.ge}
|
|
|
|
|
|
def _json(element: Any) -> bool:
|
|
while isinstance(element, UnaryExpression) and element.modifier is not None:
|
|
element = element.element
|
|
return isinstance(getattr(element, "type", None), JSON)
|
|
|
|
|
|
class JsonGuard:
|
|
"""混进编译器类里用:class Guarded(JsonGuard, 原来的编译器)。"""
|
|
|
|
def visit_binary(self, binary: Any, **kw: Any) -> str:
|
|
if binary.operator in _COMPARE and (_json(binary.left) or _json(binary.right)):
|
|
raise JsonMisuse(f"整个 JSON 值做了比较({binary.operator.__name__}):PostgreSQL 的 json 没有这个运算符,"
|
|
"按键筛选用 db/portable.json_eq 等")
|
|
return super().visit_binary(binary, **kw) # type: ignore[misc]
|
|
|
|
def visit_select(self, select: Any, **kw: Any) -> str:
|
|
if select._distinct and any(_json(c) for c in select.selected_columns):
|
|
raise JsonMisuse("SELECT DISTINCT 里有 JSON 列:PostgreSQL 的 json 没有相等运算符,去不了重")
|
|
if any(_json(c) for c in select._group_by_clauses):
|
|
raise JsonMisuse("GROUP BY 了 JSON 值:PostgreSQL 的 json 没有相等运算符")
|
|
if any(_json(c) for c in select._order_by_clauses):
|
|
raise JsonMisuse("ORDER BY 了 JSON 值:PostgreSQL 的 json 没有大小")
|
|
return super().visit_select(select, **kw) # type: ignore[misc]
|
|
|
|
def visit_compound_select(self, cs: Any, **kw: Any) -> str:
|
|
keyword = str(getattr(cs.keyword, "value", cs.keyword)).upper()
|
|
if "ALL" not in keyword and any(_json(c) for c in cs.selected_columns):
|
|
raise JsonMisuse(f"{keyword or 'UNION'} 去重时碰到 JSON 列:PostgreSQL 的 json 没有相等运算符")
|
|
return super().visit_compound_select(cs, **kw) # type: ignore[misc]
|
|
|
|
|
|
def install(dialect: Any) -> None:
|
|
"""挂到应用库引擎的方言实例上(PostgreSQL 时是 db/postgres 换过的编译器,SQLite 时是原来的)。"""
|
|
base = dialect.statement_compiler
|
|
if issubclass(base, JsonGuard):
|
|
return
|
|
dialect.statement_compiler = type(f"JsonGuarded{base.__name__}", (JsonGuard, base), {})
|