@morlay/rdbkit 0.0.0-stage → 0.0.1-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -2
  3. package/dist/index.d.mts +518 -0
  4. package/dist/index.mjs +1422 -0
  5. package/dist/testing.d.mts +28 -0
  6. package/dist/testing.mjs +42 -0
  7. package/package.json +49 -3
  8. package/src/__tests__/database-introspect.spec.ts +71 -0
  9. package/src/__tests__/expr-functions.spec.ts +64 -0
  10. package/src/__tests__/migration.spec.ts +81 -0
  11. package/src/__tests__/pg.spec.ts +123 -0
  12. package/src/__tests__/query-join.spec.ts +114 -0
  13. package/src/__tests__/query-p1.spec.ts +108 -0
  14. package/src/__tests__/query-write.spec.ts +293 -0
  15. package/src/__tests__/query.spec.ts +88 -0
  16. package/src/__tests__/schema.spec.ts +144 -0
  17. package/src/__tests__/transaction.spec.ts +83 -0
  18. package/src/catalog.ts +24 -0
  19. package/src/compound.ts +64 -0
  20. package/src/database/capabilities.ts +70 -0
  21. package/src/database/create.ts +119 -0
  22. package/src/database/index.ts +9 -0
  23. package/src/database/postgres.ts +133 -0
  24. package/src/database/queue.ts +24 -0
  25. package/src/database/sqlite.ts +66 -0
  26. package/src/database/types.ts +87 -0
  27. package/src/database/wrap.ts +48 -0
  28. package/src/defs.ts +82 -0
  29. package/src/dialect.ts +219 -0
  30. package/src/error.ts +72 -0
  31. package/src/expr.ts +346 -0
  32. package/src/fragment.ts +96 -0
  33. package/src/index.ts +114 -0
  34. package/src/migrate.ts +133 -0
  35. package/src/migration.ts +82 -0
  36. package/src/table/handle.ts +142 -0
  37. package/src/table/index.ts +14 -0
  38. package/src/table/render.ts +140 -0
  39. package/src/table/select.ts +229 -0
  40. package/src/table/types.ts +53 -0
  41. package/src/testing.ts +56 -0
@@ -0,0 +1,518 @@
1
+ /** 支持的方言。 */
2
+ type DialectName = "sqlite" | "postgres";
3
+ /** 现有结构的一小块:迁移只关心「有没有这张表 / 这一列 / 这个索引」。 */
4
+ interface TableShape {
5
+ columns: ReadonlySet<string>;
6
+ indexes: ReadonlySet<string>;
7
+ }
8
+ /** 一个方言:类型映射、标识符、DDL 片段,以及读现有结构的查询。 */
9
+ interface Dialect {
10
+ readonly name: DialectName;
11
+ /** 标识符引用(表名 / 列名 / 索引名)。 */
12
+ quote(identifier: string): string;
13
+ /** 带 schema 的表名(sqlite 忽略 schema)。 */
14
+ tableName(table: string, schema?: string): string;
15
+ /** 列类型(含自增主键的特殊写法)。 */
16
+ columnType(column: ColumnDef): string;
17
+ /** 读现有表的列与索引;表不存在返回 undefined。 */
18
+ readShape(database: ShapeReader, table: string, schema?: string): Promise<TableShape | undefined>;
19
+ }
20
+ /** 读结构所需的最小执行面(`database/wrap.ts` 的 `shapeReader` 把 `Database` 包成它)。 */
21
+ interface ShapeReader {
22
+ query<T>(text: string, params?: readonly unknown[]): Promise<T[]>;
23
+ }
24
+ export declare function createDialect(name: DialectName): Dialect;
25
+ /** 一列的定义 SQL(`"name" TEXT NOT NULL ...`)。 */
26
+ declare function columnSql$1(dialect: Dialect, name: string, column: ColumnDef): string;
27
+ /** 建表语句(IF NOT EXISTS;列与表级约束都由定义算出来)。 */
28
+ export declare function createTableSql(dialect: Dialect, table: TableDef, schema?: string): string;
29
+ /** 索引语句(`unique` 时是唯一索引)。 */
30
+ export declare function createIndexSql(dialect: Dialect, table: TableDef, name: string, columns: readonly string[], unique?: boolean): string;
31
+ /** 删列(只在显式允许破坏性变更时用)。 */
32
+ export declare function dropColumnSql(dialect: Dialect, table: string, name: string): string;
33
+ /** 删索引(只在显式允许破坏性变更时用)。 */
34
+ export declare function dropIndexSql(dialect: Dialect, name: string): string;
35
+ /** 加列语句(启动迁移只做「缺列则补」,不改类型、不删列)。 */
36
+ export declare function addColumnSql(dialect: Dialect, table: string, name: string, column: ColumnDef): string;
37
+ /**
38
+ * 片段里的一步:要么是一段 SQL 文本,要么是一个绑定参数。
39
+ * 参数不在片段里决定占位符——渲染时按方言给 `?` 或 `$n`。
40
+ */
41
+ type FragPart = {
42
+ readonly text: string;
43
+ readonly only?: DialectName;
44
+ } | {
45
+ readonly param: unknown;
46
+ readonly only?: DialectName;
47
+ };
48
+ /**
49
+ * SQL 片段是**惰性的迭代器**(与 Golang `sqlfrag` 的 `iter.Seq2[string, []any]` 对齐):
50
+ * 组合时不拼字符串,只有渲染 / 执行时才逐段展开。可重复消费(每次调用重新生成)。
51
+ */
52
+ type Fragment = () => Iterable<FragPart>;
53
+ /**
54
+ * 把 `?` 占位符与参数列表编成片段(便捷构造;我们生成的 SQL 只含占位符,不含字面量 `?`)。
55
+ */
56
+ export declare function fragment(sql: string, params?: readonly unknown[]): Fragment;
57
+ /** 按顺序拼接(`separator` 只在两个非空片段之间插入,片段内部的各步不拆开)。 */
58
+ export declare function concat(fragments: readonly (Fragment | string)[], separator?: string): Fragment;
59
+ /** 渲染成某方言的 SQL:SQLite 保持 `?`,PostgreSQL 用 `$1..$n`。 */
60
+ export declare function render(fragment: Fragment, dialect: DialectName): {
61
+ text: string;
62
+ params: unknown[];
63
+ };
64
+ declare namespace expr_d_exports {
65
+ export { ColumnRef, Condition, ExprOperand, Operand, OperandInput, Value, add, always, and, between, coalesce, column, columnSql, countIf, eq, excluded, exists, expr, gt, gte, inList, inSubquery, isDistinctFrom, isNotNull, isNull, jsonInt, jsonText, like, localDay, lower, lt, lte, maxOf, ne, never, not, notInList, notInSubquery, notNull, operandSql, or, raw, toInt, value };
66
+ }
67
+ /** 列引用:查询构建器统一用双引号(两种方言都认);`__value` 只做类型携带。 */
68
+ interface ColumnRef<T = unknown> {
69
+ readonly kind: "column";
70
+ readonly table?: string;
71
+ readonly name: string;
72
+ readonly __value?: T;
73
+ }
74
+ /** 字面参数(值走绑定,不拼字符串)。 */
75
+ interface Value {
76
+ readonly kind: "value";
77
+ readonly value: unknown;
78
+ }
79
+ /** 表达式操作数(聚合、函数调用这类不是列也不是字面量值的东西)。 */
80
+ interface ExprOperand<T = unknown> {
81
+ readonly kind: "expr";
82
+ readonly sql: string;
83
+ readonly params: readonly unknown[];
84
+ /** 方言相关的表达式(JSON 提取、本地日这类)用片段表达;给了它就不用 `sql` / `params`。 */
85
+ readonly fragment?: Fragment;
86
+ readonly __value?: T;
87
+ }
88
+ /** 列、值或表达式。 */
89
+ type Operand = ColumnRef | Value | ExprOperand;
90
+ /** 条件里可以直接写原始值(自动包成绑定参数);`null` 走 IS NULL / IS NOT NULL。 */
91
+ type OperandInput = Operand | string | number | boolean | bigint | null;
92
+ /** 一个条件(可再组合的片段)。 */
93
+ type Condition = Fragment;
94
+ export declare function column<T = unknown>(name: string, table?: string): ColumnRef<T>;
95
+ export declare function value(input: unknown): Value;
96
+ /** 列名引用成 SQL:`"表"."列"` 或 `"列"`。 */
97
+ declare function columnSql(ref: ColumnRef): string;
98
+ /** 把操作数渲染成片段(投影 / 赋值里复用)。 */
99
+ export declare function operandSql(ref: Operand): Fragment;
100
+ /** 表达式操作数的便捷构造(如 `expr('"f_count" + 1')`)。 */
101
+ export declare function expr(sql: string, params?: readonly unknown[]): ExprOperand;
102
+ export declare const eq: (left: OperandInput, right: OperandInput) => Condition;
103
+ export declare const ne: (left: OperandInput, right: OperandInput) => Condition;
104
+ export declare const gt: (left: OperandInput, right: OperandInput) => Condition;
105
+ export declare const gte: (left: OperandInput, right: OperandInput) => Condition;
106
+ export declare const lt: (left: OperandInput, right: OperandInput) => Condition;
107
+ export declare const lte: (left: OperandInput, right: OperandInput) => Condition;
108
+ export declare const like: (left: OperandInput, right: OperandInput) => Condition;
109
+ /** `BETWEEN ? AND ?`。 */
110
+ export declare const between: (ref: OperandInput, low: OperandInput, high: OperandInput) => Condition;
111
+ /** `列 IN (子查询)`。 */
112
+ export declare function inSubquery(ref: Operand, subquery: Fragment): Condition;
113
+ /** `列 NOT IN (子查询)`。 */
114
+ export declare function notInSubquery(ref: OperandInput, subquery: Fragment): Condition;
115
+ /** 恒真条件(占位用,避免业务侧写 `raw("1 = 1")`)。 */
116
+ declare function always(): Condition;
117
+ /** 恒假条件。 */
118
+ declare function never(): Condition;
119
+ /** `COALESCE(a, b, …)`:值走绑定参数,列/表达式原样渲染。 */
120
+ declare function coalesce(...inputs: OperandInput[]): ExprOperand;
121
+ /** `lower(x)`(表达式位置用)。 */
122
+ declare function lower(input: OperandInput): ExprOperand;
123
+ /** `x IS NOT NULL` 的表达式形式(投影 / 分组位置用)。 */
124
+ declare function notNull(input: OperandInput): ExprOperand;
125
+ /** `a IS DISTINCT FROM b`(NULL 也参与比较,不会退化成 `IS NULL`)。 */
126
+ declare function isDistinctFrom(left: OperandInput, right: OperandInput): Condition;
127
+ /** `EXISTS (子查询)`。 */
128
+ export declare function exists(subquery: Fragment): Condition;
129
+ /** `NOT (...)`。 */
130
+ export declare const not: (condition: Condition) => Condition;
131
+ /**
132
+ * 显式逃生口:少数表达不了的片段(窗口函数、方言专属函数)才用它。
133
+ * 占位符仍只由 `?` + 参数生成,避免把值拼进文本。
134
+ */
135
+ export declare function raw(sql: string, params?: readonly unknown[]): Condition;
136
+ /** `a + b`(列自增、upsert 累加)。 */
137
+ export declare function add(left: OperandInput, right: OperandInput): ExprOperand<number>;
138
+ /** upsert 冲突时的「新值」引用:`excluded."列"`(只用于 `upsertExpr` 的赋值)。 */
139
+ export declare function excluded(name: string): ExprOperand;
140
+ /** 条件渲染成整数列的值(`CASE WHEN cond THEN 1 ELSE 0 END`,两方言同一份)。 */
141
+ export declare function toInt(condition: Condition): ExprOperand<number>;
142
+ /** 条件计数:`SUM(CASE WHEN cond THEN 1 ELSE 0 END)`(投影 / 分组位置用)。 */
143
+ export declare function countIf(condition: Condition): ExprOperand<number>;
144
+ /** 只增不减:`max(a, b)` 的方言无关写法(NULL 视作 0,两方言同一份 CASE)。 */
145
+ export declare function maxOf(first: OperandInput, ...rest: readonly OperandInput[]): ExprOperand<number>;
146
+ /** JSON 文本提取:SQLite `json_extract(列, 路径)`、PostgreSQL `列::json #>> '{a,b}'`。 */
147
+ export declare function jsonText(input: OperandInput, path: string): ExprOperand<string | null>;
148
+ /** JSON 整数提取:文本提取后再转整数(SQLite 的 `json_extract` 本身就按类型给值)。 */
149
+ export declare function jsonInt(input: OperandInput, path: string): ExprOperand<number | null>;
150
+ /**
151
+ * 毫秒时间戳 → 本地日(`YYYY-MM-DD`)。
152
+ *
153
+ * 按**数据库所在机器的本地时区**算(SQLite `localtime` / PG 会话时区),两方言口径一致。
154
+ */
155
+ export declare function localDay(input: OperandInput): ExprOperand<string>;
156
+ export declare const isNull: (ref: Operand) => Condition;
157
+ export declare const isNotNull: (ref: Operand) => Condition;
158
+ /** `IN (?, ?, …)`;空列表渲染成恒假(`1 = 0`),避免拼出 `IN ()`。 */
159
+ export declare function inList(left: OperandInput, values: readonly unknown[]): Condition;
160
+ /** `NOT IN (...)`;空列表恒真。 */
161
+ export declare function notInList(left: OperandInput, values: readonly unknown[]): Condition;
162
+ export declare const and: (...conditions: readonly Condition[]) => Condition;
163
+ export declare const or: (...conditions: readonly Condition[]) => Condition;
164
+ /** 一个按表名解析的 catalog(装配层注册一次,业务按名字取表)。 */
165
+ interface CatalogIndex {
166
+ get(name: string): TableDef | undefined;
167
+ has(name: string): boolean;
168
+ names(): string[];
169
+ }
170
+ /** 把表定义列表编成索引(重复表名 fail loud)。 */
171
+ export declare function indexCatalog(catalog: Catalog): CatalogIndex;
172
+ /**
173
+ * 注入给业务的库句柄:只回答「哪张表」与「怎么读/写/开事务」;不暴露连接与 SQL。
174
+ * `dialect` 只给迁移 / 日志这类基础层用,业务查询不需要它。
175
+ * 连接生命周期由创建它的一方(装配层)管理,业务拿到的是这个接口。
176
+ */
177
+ interface Database<C extends Catalog = Catalog> {
178
+ /** 迁移 / 日志用;业务不需要读它。 */
179
+ readonly dialect: Dialect;
180
+ /** 装配层注册的 catalog(按表名解析;没注册时只能传定义)。 */
181
+ readonly catalog: CatalogIndex;
182
+ /** 按表名取(catalog 里有定义时保留强类型)。 */
183
+ table<K extends C[number]["name"] & string>(name: K): TableHandle<Extract<C[number], {
184
+ name: K;
185
+ }>>;
186
+ /** 直接传定义(catalog 未注册时)。 */
187
+ table<T extends TableDef>(definition: T): TableHandle<T>;
188
+ /** 主读路径:按行流式迭代(与 Golang 的 `iter.Seq2` 对齐,不一次性物化大结果)。 */
189
+ iterate<T>(query: Fragment): AsyncIterable<T>;
190
+ /** 便捷读:把迭代器收成数组(报表 / 迁移读取用)。 */
191
+ read<T>(query: Fragment): Promise<T[]>;
192
+ /** 写入并拿回行(`RETURNING`;没有 RETURNING 的语句返回空数组)。 */
193
+ write<T>(query: Fragment): Promise<T[]>;
194
+ run(query: Fragment): Promise<void>;
195
+ transaction<T>(fn: (database: Database<C>) => Promise<T>): Promise<T>;
196
+ /** 现有的表与视图名(迁移探测旧表、拒绝别人的库都要它)。 */
197
+ objectNames(): Promise<string[]>;
198
+ /** 回收空间与统计(SQLite `VACUUM` / PostgreSQL `VACUUM ANALYZE`);不得在事务内调用。 */
199
+ vacuum(): Promise<void>;
200
+ /** 读 SQLite 的 `PRAGMA`(其它方言直接报不支持)。 */
201
+ sqlitePragma(name: SqlitePragmaName): Promise<string | number | undefined>;
202
+ /** 写 SQLite 的 `PRAGMA`(值只允许安全字面量,见 `assertPragmaValue`)。 */
203
+ setSqlitePragma(name: SqlitePragmaName, value: string | number): Promise<void>;
204
+ close(): Promise<void>;
205
+ }
206
+ /** 业务侧用得到的 SQLite PRAGMA(库身份与日志模式;其余不开口子)。 */
207
+ type SqlitePragmaName = "user_version" | "application_id" | "journal_mode";
208
+ type OpenConfig = {
209
+ dialect: "sqlite";
210
+ path: string;
211
+ journalMode?: "wal" | "delete" | "truncate" | "persist";
212
+ busyTimeout?: number;
213
+ /** SQLite 页缓存(KB,负数表示 KB 数;不传用 SQLite 默认)。 */
214
+ cacheSizeKb?: number;
215
+ /** mmap 窗口(字节;0 关闭)。 */
216
+ mmapSize?: number;
217
+ /** 是否在打开时立即设置 `journal_mode`(默认 true;需要先校验库再切的调用方传 false)。 */
218
+ applyJournalMode?: boolean;
219
+ } | {
220
+ dialect: "postgres";
221
+ connectionString: string;
222
+ schema?: string;
223
+ /** 事务隔离级别(session-rdb 的写路径要 `serializable`)。 */
224
+ isolationLevel?: "read committed" | "repeatable read" | "serializable";
225
+ };
226
+ /** 一条已执行语句的观测(日志 / 慢查询 / 耗时统计)。 */
227
+ interface QueryEvent {
228
+ readonly dialect: DialectName;
229
+ readonly sql: string;
230
+ readonly params: readonly unknown[];
231
+ readonly durationMs: number;
232
+ readonly error?: unknown;
233
+ }
234
+ /** 打开时可带 catalog 与查询观测钩子。 */
235
+ type OpenConfigWithCatalog<C extends Catalog = Catalog> = OpenConfig & {
236
+ catalog?: C;
237
+ onQuery?: (event: QueryEvent) => void;
238
+ };
239
+ /**
240
+ * 一列对外的 TS 值类型:文本 → string、布尔 → boolean、64 位整数(`bigint`)→ JS `bigint`(精确,
241
+ * 对应 Go 的 int64 / uint64;不能用 number,超过 `2^53` 会丢精度)、其余 → number。
242
+ * 非 NOT NULL 允许 null。
243
+ */
244
+ type ColumnValue<C extends ColumnDef> = C["type"] extends "text" ? string : C["type"] extends "boolean" ? boolean : C["type"] extends "bigint" ? bigint : number;
245
+ type RowOf<T extends TableDef> = { [K in keyof T["columns"]]: T["columns"][K] extends {
246
+ notNull: true;
247
+ } ? ColumnValue<T["columns"][K]> : T["columns"][K] extends {
248
+ type: "serial";
249
+ } ? ColumnValue<T["columns"][K]> : ColumnValue<T["columns"][K]> | null; };
250
+ /** 写入时 `bigint` 列也接受 number(安全整数范围内两者等价)。 */
251
+ type Writable<T> = T extends bigint ? bigint | number : T;
252
+ /**
253
+ * 写入行:列可选(有默认值 / 自增列不必给)。值允许 number 写进 `bigint` 列,业务不必到处写 `n` 后缀;
254
+ * 读回来仍然是 `bigint`(不丢精度)。
255
+ */
256
+ type InsertRow<Row> = Partial<{ [K in keyof Row]: Writable<Row[K]>; }>;
257
+ /** 更新时的值:普通值(绑定参数)或表达式(`expr('"f_revision" + 1')` 等)。 */
258
+ type UpdatePatch<Row> = { [K in keyof Row]?: Writable<Row[K]> | Operand; };
259
+ /** 列名(字符串键)。 */
260
+ type ColumnName<Row> = Extract<keyof Row, string>;
261
+ /**
262
+ * 聚合表达式(`count()` / `sum(col)`):投影与条件里都能用。
263
+ *
264
+ * 值是 64 位整数:PostgreSQL 给 `bigint`、SQLite 给 number,业务要 number 时显式 `Number(x)`。
265
+ */
266
+ type Aggregate<T = number | bigint> = ExprOperand<T>;
267
+ /** 投影:别名 → 列引用或聚合。结果类型由它推导。 */
268
+ type Projection = Record<string, ColumnRef<any> | Aggregate<any>>;
269
+ /** 投影的强类型结果。 */
270
+ type Projected<P extends Projection> = { [K in keyof P]: P[K] extends ColumnRef<infer T> ? T : P[K] extends Aggregate<infer T> ? T : never; };
271
+ /** COUNT(*),投影里写 `{ n: count() }`。 */
272
+ export declare function count(): Aggregate<number | bigint>;
273
+ /**
274
+ * SUM(列);多表查询时传表名避免歧义。
275
+ *
276
+ * PostgreSQL 的 `SUM(bigint)` 返回 `numeric`(驱动给字符串),SQLite 返回整数——统一 cast 回 int8,
277
+ * 声明的 `number | bigint` 才成立。列类型只有 integer / bigint,所以这个 cast 不丢小数。
278
+ */
279
+ export declare function sum(name: string, table?: string): Aggregate<number | bigint>;
280
+ /** 一个查询:实现 `AsyncIterable`,`for await` 直接消费(对应 Golang 的 `iter.Seq2`)。 */
281
+ interface SelectQuery<T> extends AsyncIterable<T> {
282
+ where(condition: Condition): this;
283
+ /** `cross` 不需要 ON;`alias` 给被 join 的表起别名(投影里用 `column(名字, 别名)`)。 */
284
+ join(table: TableDef, on?: Condition, kind?: "inner" | "left" | "right" | "full" | "cross", alias?: string): this;
285
+ /** `WITH "name" AS (<query>)`,多条按顺序拼。 */
286
+ with(name: string, query: Fragment, options?: {
287
+ recursive?: boolean;
288
+ columns?: readonly string[];
289
+ }): this;
290
+ /** 分组:列名(主表)、带表名的列引用或表达式。 */
291
+ groupBy(...columns: GroupByItem[]): this;
292
+ having(condition: Condition): this;
293
+ distinct(): this;
294
+ orderBy(column: string | ColumnRef | ExprOperand, direction?: "asc" | "desc"): this;
295
+ limit(count: number): this;
296
+ offset(count: number): this;
297
+ /** 行锁(PostgreSQL 生效,SQLite 渲染时丢弃)。 */
298
+ forUpdate(options?: {
299
+ of?: readonly string[];
300
+ noWait?: boolean;
301
+ skipLocked?: boolean;
302
+ }): this;
303
+ toFragment(): Fragment;
304
+ }
305
+ /** `groupBy` 的入参:列名、带表名的列引用或表达式。 */
306
+ type GroupByItem = string | ColumnRef | ExprOperand;
307
+ /** 列访问器:`t.f_domain` 就是这一列的引用(带值类型)。 */
308
+ type ColumnAccessors<Row> = { readonly [K in ColumnName<Row>]: ColumnRef<Row[K]>; };
309
+ /** 一张表的业务面:投影查询 + 写入,调用方不接触方言与 SQL。 */
310
+ type TableHandle<T extends TableDef> = TableHandleBase<T> & ColumnAccessors<RowOf<T>>;
311
+ interface TableHandleBase<T extends TableDef> {
312
+ readonly name: string;
313
+ readonly definition: T;
314
+ /** 拿一列(带值类型的引用),投影 / 条件里用。 */
315
+ column<K extends ColumnName<RowOf<T>>>(name: K): ColumnRef<RowOf<T>[K]>;
316
+ /** 投影省略时取全部列(结果按列名原样)。 */
317
+ select<P extends Projection = Record<string, ColumnRef>>(projection?: P): SelectQuery<Projected<P>>;
318
+ /** 插入;传 `returning` 时拿回这些列(`RETURNING`,两方言都支持)。 */
319
+ insert(row: InsertRow<RowOf<T>>, returning?: readonly ColumnName<RowOf<T>>[]): Promise<RowOf<T>[]>;
320
+ insertIgnore(row: InsertRow<RowOf<T>>, conflict: readonly ColumnName<RowOf<T>>[]): Promise<void>;
321
+ /** `INSERT … SELECT`:把一条查询的结果按列顺序插进来(Go 的 `InsertFrom`)。 */
322
+ insertFromSelect(columns: readonly ColumnName<RowOf<T>>[], source: Fragment, options?: {
323
+ conflict?: readonly ColumnName<RowOf<T>>[];
324
+ update?: readonly ColumnName<RowOf<T>>[];
325
+ }): Promise<void>;
326
+ /** 批量插入;传 `conflict` / `update` 时是批量 upsert(`ON CONFLICT … DO UPDATE`)。 */
327
+ insertMany(rows: readonly InsertRow<RowOf<T>>[], options?: {
328
+ conflict?: readonly ColumnName<RowOf<T>>[];
329
+ update?: readonly ColumnName<RowOf<T>>[];
330
+ }): Promise<void>;
331
+ upsert(row: InsertRow<RowOf<T>>, conflict: readonly ColumnName<RowOf<T>>[], update?: readonly ColumnName<RowOf<T>>[]): Promise<void>;
332
+ update(patch: UpdatePatch<RowOf<T>>, condition?: Condition, returning?: readonly ColumnName<RowOf<T>>[]): Promise<RowOf<T>[]>;
333
+ /** `ON CONFLICT ... DO UPDATE SET 列 = 表达式`(如自增计数)。 */
334
+ upsertExpr(row: InsertRow<RowOf<T>>, conflict: readonly ColumnName<RowOf<T>>[], assignments: Record<string, Operand>): Promise<void>;
335
+ /** 删除;传 `returning` 时拿回被删行的这些列(`RETURNING`)。 */
336
+ delete(condition?: Condition, returning?: readonly ColumnName<RowOf<T>>[]): Promise<RowOf<T>[]>;
337
+ }
338
+ export declare function createTableHandle<T extends TableDef>(database: Database, definition: T): TableHandle<T>;
339
+ /** 列类型:只保留跨方言能一一映射的那几种。 */
340
+ type ColumnTypeName = "serial" | "integer" | "bigint" | "text" | "boolean";
341
+ /** 外键的删除动作(与 SQL 的 ON DELETE 同名)。 */
342
+ type DeleteAction = "cascade" | "set null" | "restrict" | "no action";
343
+ /** 一列的定义。 */
344
+ interface ColumnDef {
345
+ type: ColumnTypeName;
346
+ notNull?: boolean;
347
+ /** 单列主键;复合主键写在表级的 `primaryKey` 上。 */
348
+ primaryKey?: boolean;
349
+ default?: string | number;
350
+ unique?: boolean;
351
+ references?: {
352
+ table: string;
353
+ column: string;
354
+ onDelete?: DeleteAction;
355
+ };
356
+ }
357
+ /** 表级约束与索引。 */
358
+ interface CheckDef {
359
+ expression: string;
360
+ }
361
+ interface UniqueDef {
362
+ columns: string[];
363
+ }
364
+ interface IndexDef {
365
+ columns: string[];
366
+ }
367
+ /** 一张表的完整定义。 */
368
+ interface TableDef {
369
+ name: string;
370
+ columns: Record<string, ColumnDef>;
371
+ /** 复合主键(按顺序);单列主键用列上的 `primaryKey`。 */
372
+ primaryKey?: readonly string[];
373
+ checks?: Record<string, CheckDef>;
374
+ uniques?: Record<string, UniqueDef>;
375
+ indexes?: Record<string, IndexDef>;
376
+ }
377
+ /** 一组表定义(一个库的结构)。 */
378
+ type Catalog = readonly TableDef[];
379
+ /** `defineTable` 的返回值:表定义 + 操作钩子(`users.table(database)` 直接拿句柄)。 */
380
+ type Table<T extends TableDef> = T & {
381
+ table(database: Database): TableHandle<T>;
382
+ };
383
+ /** 声明一张表(形状原样保留,另挂一个取句柄的钩子)。 */
384
+ export declare function defineTable<const T extends TableDef>(def: T): Table<T>;
385
+ /** 声明一组表。 */
386
+ export declare function defineCatalog<const T extends readonly TableDef[]>(tables: T): T;
387
+ /** 打开一个库;这是连接创建的唯一入口(实际 connect 在 `sqlite.ts` / `postgres.ts`,业务只拿 `Database`)。 */
388
+ export declare function openDatabase<const C extends Catalog = Catalog>(config: OpenConfigWithCatalog<C>): Promise<Database<C>>;
389
+ /** 把「已有执行面」包成 rdbkit 的 `Database`(宿主自己管连接与事务时用)。 */
390
+ export declare function wrapDatabase(dialectName: DialectName, ports: {
391
+ iterate(text: string, params: readonly unknown[]): AsyncIterable<unknown>;
392
+ run(text: string, params: readonly unknown[]): Promise<void>;
393
+ /** 宿主自己管事务时提供:把当前事务的执行面做成 scoped `Database`(对应 Go 的 `Tx(ctx, fn)`)。 */
394
+ transaction?: <T>(fn: (scoped: Database) => Promise<T>) => Promise<T>;
395
+ close?(): Promise<void>;
396
+ }): Database;
397
+ /** 结构迁移读取现状用的最小查询面。 */
398
+ export declare function shapeReader(database: Database): {
399
+ query<T>(sql: string, params?: readonly unknown[]): Promise<T[]>;
400
+ };
401
+ /** 一条结构变更(按顺序执行)。 */
402
+ interface SchemaChange {
403
+ readonly table: string;
404
+ readonly kind: "create-table" | "add-column" | "create-index" | "drop-column";
405
+ readonly statement: string;
406
+ }
407
+ interface EnsureSchemaOptions {
408
+ /** PostgreSQL 的 schema(默认 `public`);SQLite 忽略。 */
409
+ schema?: string;
410
+ /** 允许删掉定义里已经不存在、且确实多出来的列(默认关闭:升级不丢数据)。 */
411
+ allowDestructive?: boolean;
412
+ /** 需要看到实际执行的语句时(排查 / 测试)。 */
413
+ onStatement?: (statement: string) => void;
414
+ }
415
+ /**
416
+ * 结构差异(纯函数):缺表建表、缺列补列、缺索引补索引。
417
+ *
418
+ * 默认只做「增」(缺表建表、缺列补列、缺索引补索引),不改类型、不删索引;删多出来的列只由
419
+ * `allowDestructive: true` 显式打开(旧库升级默认不丢数据)。
420
+ * @param dialect - 目标方言。
421
+ * @param catalog - 表定义(唯一事实来源)。
422
+ * @param shapes - 现有结构(表名 → 列 / 索引;不存在则 undefined)。
423
+ * @param options - schema 名。
424
+ * @returns 需要执行的变更,按依赖顺序(表 → 列 → 索引)。
425
+ */
426
+ export declare function planSchema(dialect: Dialect, catalog: Catalog, shapes: ReadonlyMap<string, TableShape | undefined>, options?: {
427
+ schema?: string;
428
+ allowDestructive?: boolean;
429
+ }): SchemaChange[];
430
+ /** 让库结构与表定义对齐,并返回实际执行的语句。 */
431
+ export declare function ensureSchema(database: Database, catalog: Catalog, options?: EnsureSchemaOptions): Promise<string[]>;
432
+ /** 一条 CTE:`"name" AS (<query>)`;`recursive` 时由 `withCtes` 渲染成 `WITH RECURSIVE` 前缀。 */
433
+ interface Cte {
434
+ readonly name: string;
435
+ readonly query: Fragment;
436
+ readonly recursive?: boolean;
437
+ readonly columns?: readonly string[];
438
+ }
439
+ /** 把一条 CTE 拼在 `WITH` 之后(调用方负责逗号)。 */
440
+ export declare function cteSql(cte: Cte): Fragment;
441
+ /** 组装 `WITH [RECURSIVE] cte, … SELECT …`。 */
442
+ export declare function withCtes(ctes: readonly Cte[], query: Fragment): Fragment;
443
+ /** `UNION` / `UNION ALL`。 */
444
+ export declare function union(left: Fragment, right: Fragment, options?: {
445
+ all?: boolean;
446
+ }): Fragment;
447
+ /** `INTERSECT`。 */
448
+ export declare function intersect(left: Fragment, right: Fragment): Fragment;
449
+ /** `EXCEPT`。 */
450
+ export declare function except(left: Fragment, right: Fragment): Fragment;
451
+ /** 行锁:只有 PostgreSQL 认,SQLite 渲染时整段丢掉(分隔符也不能漏出来)。 */
452
+ export declare function forUpdate(options?: {
453
+ of?: readonly string[];
454
+ noWait?: boolean;
455
+ skipLocked?: boolean;
456
+ }): Fragment;
457
+ /** 表别名(join / CTE 里用)。 */
458
+ export declare function aliasedTable(table: TableDef, alias: string): string;
459
+ /** 数据库错误的可判定类别(调用方据此决定重试 / 上报 / 走业务分支)。 */
460
+ type DbErrorKind = "unique" | "foreign-key" | "not-null" | "check" | "timeout" | "connection" | "unknown";
461
+ /** 统一的数据库错误:`kind` 是方言无关的判定,原始错误在 `cause`。 */
462
+ export declare class DbError extends Error {
463
+ readonly kind: DbErrorKind;
464
+ constructor(kind: DbErrorKind, message: string, options?: {
465
+ cause?: unknown;
466
+ });
467
+ }
468
+ /**
469
+ * 把驱动错误翻译成 {@link DbErrorKind}:SQLite 用错误文本前缀,PostgreSQL 用 SQLSTATE。
470
+ * @param error - 驱动抛出的错误。
471
+ * @param dialect - 产生它的方言。
472
+ * @returns 已分类的错误(原错误在 `cause`)。
473
+ */
474
+ export declare function classifyError(error: unknown, dialect: DialectName): DbError;
475
+ /** 内置迁移账本:一条已执行的数据迁移一行(`f_id` 唯一)。 */
476
+ export declare const schemaMigrations: Table<{
477
+ readonly name: "t_schema_migrations";
478
+ readonly columns: {
479
+ readonly f_id: {
480
+ readonly type: "text";
481
+ readonly primaryKey: true;
482
+ readonly notNull: true;
483
+ };
484
+ readonly f_applied_at: {
485
+ readonly type: "bigint";
486
+ readonly notNull: true;
487
+ };
488
+ };
489
+ }>;
490
+ /** 一条数据迁移:`id` 唯一且按字典序执行;`up` 在事务里跑,抛错则整条不记。 */
491
+ interface Migration {
492
+ readonly id: string;
493
+ /**
494
+ * 返回 `false` 表示这次无需执行(例如旧表还不存在),**不记账**——下次打开还会再试。
495
+ * 返回其它值(含 `undefined`)表示已执行,记账。
496
+ */
497
+ readonly up: (database: Database) => Promise<boolean | void>;
498
+ }
499
+ /** 迁移注册表:业务把自己的迁移登记进来,打开库时一起执行。 */
500
+ export declare class MigrationRegistry {
501
+ #private;
502
+ register(migration: Migration): this;
503
+ list(): readonly Migration[];
504
+ }
505
+ /** 打开库时做的事:结构(catalog diff)+ 数据迁移(注册表)。 */
506
+ interface RunMigrationsOptions {
507
+ /** 业务表定义(`ensureSchema` 用)。 */
508
+ catalog?: Catalog;
509
+ migrations: readonly Migration[] | MigrationRegistry;
510
+ }
511
+ /**
512
+ * 先按 catalog 对齐结构(建表 / 补列 / 补索引),再执行未应用的数据迁移。
513
+ *
514
+ * 账本是 rdbkit 内置的 `t_schema_migrations`:每条迁移在**同一个事务**里执行并记账,
515
+ * 失败即回滚,重跑时只跑没记过的。返回本次实际执行了哪些 `id`。
516
+ */
517
+ export declare function runMigrations(database: Database, options: RunMigrationsOptions): Promise<readonly string[]>;
518
+ export { type Aggregate, type Catalog, type CatalogIndex, type CheckDef, type ColumnDef, type ColumnName, type ColumnRef, type ColumnTypeName, type ColumnValue, type Condition, type Cte, type Database, type DbErrorKind, type DeleteAction, type Dialect, type DialectName, expr_d_exports as E, type EnsureSchemaOptions, type ExprOperand, type Fragment, type IndexDef, type InsertRow, type Migration, type OpenConfig, type Operand, type OperandInput, type Projected, type Projection, type QueryEvent, type RowOf, type RunMigrationsOptions, type SchemaChange, type SelectQuery, type SqlitePragmaName, type Table, type TableDef, type TableHandle, type TableShape, type UniqueDef, columnSql as columnRefSql, columnSql$1 as columnSql };