Skip to content

drivers(sql family): 文本算子的大小写折叠是「方言的」而非「契约的」—— $contains 在 SQLite 过折叠、$icontains 在 PG/MySQL 过折叠 #6518

Description

@os-zhuang

Part of #4706(裁决 Q1=A / Q2=A)。从 #5702 实施期实测拆出 —— #5702 交付了 $regex 拒收与 $icontains 实现,没有交付这一条,原因见下「为什么不能在 #5702 里顺手做」。

两个方向的同一个缺陷

SqlDriver.applyLike 发的 SQL 里,大小写由方言决定,不由契约决定。两个算子各在一半方言上跑偏:

算子 契约(#4706) SQLite / turso / sqlite-wasm Postgres MySQL
$contains / $notContains / $startsWith / $endsWith 大小写敏感(Q2=A) LIKE 自带 ASCII 折叠 ✅ 恰好正确 ⚠️ 随 collation
$icontains 折叠 ASCII only(Q1=A) ✅ SQLite lower() 只折 ASCII LOWER() 折全 Unicode ❌ 同左

两格都是过匹配(返回了不该返回的行),不是漏匹配。

实测(better-sqlite3,FILTER_TEXT_ROWS 九行,origin/main + PR of #5702)

$contains  'acme'  → ["1","2"]     契约要求 ["2"]      (行 1 = "ACME Corp")
$contains  'ACME'  → ["1","2"]     契约要求 ["1"]
$icontains 'acme'  → ["1","2"]     ✅
$icontains 'café'  → ["4"]         ✅(É 不折)

顺带一条对分诊有用的读数:在 SQLite 上 $contains$icontains 对任意比较数返回完全相同的行 —— 因为 LIKE 已经折了 ASCII,外面再套一层 LOWER() 是幂等的。也就是说这条不修,$icontains 在 SQLite 上是行为不可观测的(它的正确性只能靠编译出的 SQL 文本和 PG 见证)。#5702remote-transport-text-predicates.test.ts / sql-driver-icontains-and-retired-operators.test.ts 已把这两条如实钉住。

为什么不能在 #5702 里顺手做(这是本单独立的理由)

$contains 的大小写需要在 SQLite 上换构造(GLOB / instr() / 二进制 collation —— LIKE 不受 collation 影响,只能靠 PRAGMA case_sensitive_like,而那是连接级全局开关)。这个下译在三处并存,必须同批移动:

  1. packages/drivers/driver-sql/src/sql-driver.tsapplyLike;
  2. packages/drivers/driver-turso/src/remote-transport.tspushLike(remote 不走 knex,自带一份);
  3. RLS / 分析面的孪生实现 —— read-scope-sqlpackages/services/service-analytics/src/like-pattern.ts(后者的注释自陈「held to THIS expression, character for character」)。

只改 driver 不改第 3 组,等于一条权限规则编出两个行集 —— #3948 那一类 P0,比现状更糟。所以它不是 domain:drivers 单车道能闭环的改动。

验收

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions