/** * 收编迁移语句的**双方言文本铸造**([ref] §4.3 form b)。 * * 🔴 为什么这里是**生成**而不是像别的店那样把两句话手写并排(sql-driver.ts 的 A12 判据):那条纪律的 * 目的是「读代码的人能一眼对比两方言」。本车的语句是从 {@link REBIND_LEGS} 计划表派生的 —— 手抄 26 条 * 腿 × 3 类语句 × 2 方言 = 156 段几乎一样的 SQL,抄漏一个列名是**静默**的(库上照样跑,只是少迁一列)。 * 所以判据换了个执行面、强度不降反升:每条腿每个方言的**成品文本**由 * `test/adoption-sql-text.test.ts` 逐字钉成 golden —— 计划表改一个字,红的是那条腿的文本对照, * 而不是等真库上撞。 * * 方言差异全部集中在 {@link ph}(占位符 `?` vs `$n`)与 `LEFT()` 的大小写无关形上;除此之外两方言的 * 文本**逐字相同**(本车的语句只用等值谓词、COUNT、UPDATE —— 刻意不碰任何方言分岔的语法面: * 没有 upsert、没有 JSON 转型、没有 null-safe 比较)。 */ import type { SqlDialect } from "../plugins/sql-driver.js"; import type { RebindLegSpec } from "./plan.js"; /** 第 `i` 个占位符(1-based)。TiDB `?` / PG `$i` —— 这是本文件唯一的方言分岔点。 */ export declare function ph(dialect: SqlDialect, i: number): string; /** * 目的地冲突探针(phase 0,**任何 UPDATE 之前**)。 * * 判据 = 「to 侧已有的行,与 from 侧的行,是否共享同一个**逻辑键**」。逻辑键 = * `leg.residualKey`(该表最紧的唯一键**去掉身份轴**之后剩下的列): * · `undefined` ⇒ 没有唯一键含这根轴 ⇒ 结构性无冲突,本函数不该被调用(调用方按 `undefined` 短路); * · `[]` ⇒ 唯一键**就是**这根轴(PK(scope))⇒ 两侧各有任意一行即冲突,退化成笛卡尔计数; * · 非空 ⇒ 逐列相等的自连接计数。 * * 参数序:`[toValue, fromValue]`。 */ export declare function buildConflictProbeSql(dialect: SqlDialect, leg: RebindLegSpec): string; /** * 桶迁/行迁的等值 UPDATE。`columns` 里的每一列都被设成**同一个**新值(字节孪生 `*_key` 与可读列一起走), * 谓词打在 `matchColumn` 上。参数序:`[...新值 × columns.length, 旧值]`。 * * 幂等性来自谓词本身:重跑时旧值已经不在库里 ⇒ 零行匹配 ⇒ 零行变化。 */ export declare function buildRebindSql(dialect: SqlDialect, leg: RebindLegSpec): string; /** * memory 族的 scope 值扫描:把「属于这个 principal 的 scope 键」从库里**读出来**,而不是用 LIKE 去猜。 * * 🔴 为什么不是 `LIKE '<前缀>%'`:scope 的 tenant 段是**百分号编码**的(`user%3Aweb-demo`),而 `%` 在 * LIKE 里是通配符 —— 不逐字符转义就会误命中别的租户的盘(**跨租户误迁**,不是性能问题)。`LEFT(col, n) = ?` * 是纯等值比较,没有元字符面,两方言同形。 * * 参数序:`[userScope, projHead.length, projHead, userProjHead.length, userProjHead]`。 */ export declare function buildMemoryScopeScanSql(dialect: SqlDialect, table: string): string; /** * 残留计数:某条腿上**仍挂在旧身份名下**的行数。参数序:`[fromValue]`。 * * 用途见 wire.ts 的 `current.residualSourceRows`:收编不给普通写者上栅栏(183 I1 = 运维前提「引擎先停」), * 所以「终态之后旧身份下又长出行」是一个真实可能的形 —— 这条查询把它变成可见读数,而不是靠祈祷。 */ export declare function buildResidualCountSql(dialect: SqlDialect, table: string, matchColumns: readonly string[]): string; /** * `blob-rewrite` 腿的扫描:按**旧**身份值把行连同 JSON 载荷读出来。 * 匹配面 = `matchColumn` 与 `blob.alsoMatch` 的**并**(同表两根轴都可能带旧值)。 * 参数序:`[fromValue × (1 + alsoMatch.length)]`。 */ export declare function buildBlobScanSql(dialect: SqlDialect, leg: RebindLegSpec): string; /** * `blob-rewrite` 腿的逐行回写。参数序:`[新载荷文本, ...pk 值]`。 * * 幂等性:重跑时载荷里的身份字段已是新值 ⇒ 调用方算出的新载荷与库里逐字节相同 ⇒ 语义无操作 * (行数可能仍报 1,故回执的行数按「**内容真变了**才计」在调用侧收窄)。 */ export declare function buildBlobWriteSql(dialect: SqlDialect, leg: RebindLegSpec): string; /** * 规则桶的**整桶改键**([ref])。参数序:`[newOwnerKey, toPrincipal, oldOwnerKey]`。 * * `owner_key = sha256("principal:" + principal)` —— 身份在**键的 hash 原像**里,与 session_policy * 同族。差别在于这把键**不含逐行变量**(session_policy 的原像里还有 session_id),所以不必逐行扫出来 * 重算:一对 (旧键, 新键) 算一次,一条等值 UPDATE 走完全表。 * * `principal` 列跟着同改:它是 owner_key 的**明文原像列**(店按前者寻址、`GET /v1/rules` 的回执与运维 * 的 `WHERE principal = …` 排查按后者读)。只改一列,库里就有两份互相矛盾的身份说法。 * * 幂等性来自谓词:重跑时旧桶键已经不在库里 ⇒ 零行匹配 ⇒ 零行变化。 * `owner_kind = 'local-owner'` 的那只桶天然不受影响 —— 它的 owner_key 是 `sha256("local-owner")`, * 与任何 principal 派生的键都不相等,所以**匹配不到**(不需要额外的 owner_kind 谓词来护住它)。 */ export declare function buildRuleOwnerRekeySql(dialect: SqlDialect, leg: RebindLegSpec): string; /** session_policy 重键腿的扫描:拿到 from 侧每一行的 `(policy_key, session_id)`。参数序:`[fromPrincipal]`。 */ export declare function buildSessionPolicyScanSql(dialect: SqlDialect): string; /** * session_policy 的逐行重键改写。`policy_key = sha256(JSON.stringify([sessionId, principal ?? null]))`, * 身份在**主键的 hash 原像**里 ⇒ 等值 UPDATE 够不着,必须读出来重算再改写。 * 参数序:`[newPolicyKey, toPrincipal, oldPolicyKey]`。 */ export declare function buildSessionPolicyRewriteSql(dialect: SqlDialect): string; //# sourceMappingURL=sql.d.ts.map