/** 本原语认得的两种方言(闭集;新方言进来是编译期的事,不是运行期的 `default` 臂)。 */ export type SqlDialect = "mysql" | "pg"; /** * 一条索引的**声明**(纯数据)。两方言共用同一份声明 —— 这就是"一处属主"的兑现物: * per-store 文件各导出自己那份 `readonly IndexSpec[]`,PG 侧由该 store 的 `ensurePg*Schema` 应用, * MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 统一应用(与 `SCHEMA_STATEMENTS` 的组合姿势同形)。 */ export interface IndexSpec { /** 表名(与两方言的 `CREATE TABLE` 同名——本仓两方言表名逐字相同)。 */ readonly table: string; /** 索引名(两方言同名;MySQL 侧它同时是 `CREATE TABLE` 里那条内联 `KEY` 的名字)。 */ readonly name: string; /** 列序(等值前缀在前、排序列在后;与 MySQL 内联 `KEY` 的列序**必须**逐字一致)。 */ readonly columns: readonly string[]; /** UNIQUE 索引(MySQL 内联 `UNIQUE KEY` 的孪生)。缺省 = 普通索引。 */ readonly unique?: boolean; } /** * 两方言的查询面。mysql2 的 `conn.query` 答 `[rows, fields]`,node-pg 答 `{ rows }` —— 两种形的读法 * 都关在本文件里(调用点只管把自己手上那只 `query` 传进来)。 * * 🔴 **本原语一条绑定参数都不发**(探针与 DDL 全是字面量)。两条理由: * ① 它的输入是**声明**不是数据 —— `IndexSpec` 的表名/索引名都是编译期常量,没有用户输入可注入, * 字面量与占位符在安全性上等价; * ② PG 那半的 `q` 是**调用方递进来的** `PgQueryFn`(十来个 `ensurePg*Schema` 都收它),而那些 ensure * 腿在本批之前**从不带参**,于是仓里真有丢掉 `params` 只转发 sql 的包装(如 * `test/permission-rule-store-db-integration.test.ts` 的 `(sql) => db.query(sql, [])`)。一条带 `$1` * 的探针会在那种包装上报 `there is no parameter $1` —— 而那是**调用方**的形,不是本原语能管的。 * 不带参 ⇒ 这一整类接缝脆弱性结构上不存在。两方言同律,不留一边带参一边不带的不对称。 */ export type IndexQueryFn = (sql: string, params?: unknown[]) => Promise; /** * 幂等地保证一条索引在场。**这是本仓唯一允许发加索引 DDL 的地方**(SCHEMA POLICY 窄放的全部内容)。 * * @param q 方言对应的查询面(mysql2 conn/pool 的 `query`,或裸 `PgQueryFn`) * @param dialect 方言闭集 * @param spec 索引声明(两方言共用同一份) */ export declare function ensureIndex(q: IndexQueryFn, dialect: SqlDialect, spec: IndexSpec): Promise; /** {@link ensureIndex} 的**逐条串行**遍历(DDL 并发打同一张表会踩 catalog 竞争,故不并行)。 */ export declare function ensureIndexes(q: IndexQueryFn, dialect: SqlDialect, specs: readonly IndexSpec[]): Promise; //# sourceMappingURL=ensure-index.d.ts.map