okmodel 0.6.0 → 0.7.0

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 (157) hide show
  1. package/README.md +100 -1131
  2. package/dist/adapters/capabilities.d.ts +3 -0
  3. package/dist/adapters/failure.d.ts +13 -2
  4. package/dist/adapters/lazy-errors.d.ts +22 -0
  5. package/dist/adapters/pg/bunsql-extra.d.ts +54 -3
  6. package/dist/adapters/pg/bunsql.d.ts +14 -1
  7. package/dist/adapters/pg/cursor.d.ts +17 -0
  8. package/dist/adapters/pg/no-inflight-timeout.d.ts +11 -8
  9. package/dist/adapters/pg/nodepostgres-extra.d.ts +22 -1
  10. package/dist/adapters/pg/nodepostgres.d.ts +12 -0
  11. package/dist/adapters/pg/pglite-wire.d.ts +24 -0
  12. package/dist/adapters/pg/pglite.d.ts +10 -0
  13. package/dist/adapters/pg/postgres-extra.d.ts +27 -11
  14. package/dist/adapters/pg/reserved.d.ts +42 -0
  15. package/dist/adapters/pg/session.d.ts +0 -18
  16. package/dist/contracts/connection.d.ts +7 -6
  17. package/dist/dialects/pg/floor.d.ts +68 -0
  18. package/dist/dialects/pg/index.d.ts +6 -1
  19. package/dist/dialects/pg/index.js +49 -159
  20. package/dist/dialects/pg/keys.d.ts +11 -8
  21. package/dist/dialects/pg/model.d.ts +40 -0
  22. package/dist/dialects/pg/morph.d.ts +43 -0
  23. package/dist/dialects/pg/operator-fit.d.ts +2 -2
  24. package/dist/dialects/pg/operators.d.ts +14 -7
  25. package/dist/dialects/pg/ops/bucket.d.ts +65 -0
  26. package/dist/dialects/pg/ops/inc.d.ts +2 -2
  27. package/dist/dialects/pg/ops/matches.d.ts +9 -12
  28. package/dist/dialects/pg/ops/relevance.d.ts +13 -0
  29. package/dist/dialects/pg/ops/search-query.d.ts +22 -0
  30. package/dist/dialects/pg/ops/window.d.ts +103 -0
  31. package/dist/dialects/pg/preset.d.ts +1 -1
  32. package/dist/dialects/pg/relations.d.ts +100 -3
  33. package/dist/dialects/pg/schema.d.ts +31 -3
  34. package/dist/dialects/pg/view/index.js +1 -1
  35. package/dist/runtime/aggregate.d.ts +8 -2
  36. package/dist/runtime/bucket-group.d.ts +70 -0
  37. package/dist/runtime/conflict.d.ts +22 -4
  38. package/dist/runtime/include.d.ts +0 -8
  39. package/dist/runtime/inflight.d.ts +1 -1
  40. package/dist/runtime/page.d.ts +4 -1
  41. package/dist/runtime/pg/bun.js +12 -6
  42. package/dist/runtime/pg/pg.js +5 -5
  43. package/dist/runtime/pg/pglite.d.ts +1 -0
  44. package/dist/runtime/pg/pglite.js +9 -9
  45. package/dist/runtime/pg/postgresjs.js +5 -5
  46. package/dist/runtime/plan.d.ts +95 -5
  47. package/dist/runtime/read-stream.d.ts +13 -16
  48. package/dist/runtime/result-name.d.ts +17 -0
  49. package/dist/runtime/server-check.d.ts +3 -3
  50. package/dist/runtime/sort-bind.d.ts +21 -0
  51. package/dist/runtime/sort.d.ts +56 -0
  52. package/dist/runtime/sync.d.ts +70 -0
  53. package/dist/runtime/tenancy/index.js +7 -5
  54. package/dist/runtime/text-search.d.ts +20 -0
  55. package/dist/runtime/traits/index.d.ts +80 -0
  56. package/dist/runtime/traits/index.js +175 -11
  57. package/dist/runtime/tree.d.ts +38 -0
  58. package/dist/runtime/types.d.ts +218 -22
  59. package/dist/runtime/validate/index.js +2 -2
  60. package/dist/runtime/version.d.ts +39 -0
  61. package/dist/runtime/window.d.ts +21 -0
  62. package/dist/runtime/write.d.ts +54 -2
  63. package/dist/shared/{wkme6tep.js → 14vx95q4.js} +8 -16
  64. package/dist/shared/1est4ezg.js +313 -0
  65. package/dist/shared/{4az561fn.js → 1qa0qajz.js} +8 -23
  66. package/dist/shared/{2cg3wnxc.js → 1w508jbr.js} +593 -130
  67. package/dist/shared/2c13q96h.js +18 -0
  68. package/dist/shared/{b4v57v3x.js → 2d3xq4ps.js} +2 -2
  69. package/dist/shared/{1nw1f3vs.js → 2shdbn8q.js} +7 -7
  70. package/dist/shared/3v7t1yrv.js +208 -0
  71. package/dist/shared/{x3jc53kg.js → 5e3tf63j.js} +2 -2
  72. package/dist/shared/5fe3kbsh.js +10 -0
  73. package/dist/shared/5v3xf80k.js +37 -0
  74. package/dist/shared/{c58secgj.js → 63qtkrca.js} +38 -4
  75. package/dist/shared/{2z7wv5tj.js → 67txcrcw.js} +55 -34
  76. package/dist/shared/79dt02vc.js +12 -0
  77. package/dist/shared/{8x72s86k.js → 7k9grvf6.js} +10 -4
  78. package/dist/shared/7kms6fvj.js +20 -0
  79. package/dist/shared/7mnme5tn.js +8 -0
  80. package/dist/shared/7n7ddwt4.js +6 -0
  81. package/dist/shared/{5j36z8dn.js → 81ebdx9p.js} +39 -70
  82. package/dist/shared/{knmes2kh.js → 8skf0cam.js} +1 -1
  83. package/dist/shared/9a4ae4p8.js +31 -0
  84. package/dist/shared/9y3mmmh1.js +436 -0
  85. package/dist/shared/ab9gmbwy.js +213 -0
  86. package/dist/shared/{f0f1n281.js → ax7qv55k.js} +2 -2
  87. package/dist/shared/{g90m9b0g.js → aytqh5y2.js} +6 -5
  88. package/dist/shared/{vjpnzwjz.js → b5pftnn2.js} +1 -1
  89. package/dist/shared/{ag4c7516.js → b76b8d95.js} +2 -2
  90. package/dist/shared/{vmfkhszw.js → bg3rym3g.js} +33 -26
  91. package/dist/shared/{mb2ycs1p.js → bm22fvzr.js} +15 -15
  92. package/dist/shared/{q3j1tq7a.js → bz84tkzm.js} +148 -23
  93. package/dist/shared/{31vm914h.js → cm3w0ywp.js} +15 -16
  94. package/dist/shared/cn957f8d.js +179 -0
  95. package/dist/shared/{gwn6v667.js → cnkky55j.js} +25 -14
  96. package/dist/shared/{ys0xzyaj.js → dkxgzaqd.js} +224 -19
  97. package/dist/shared/dzj09cye.js +10 -0
  98. package/dist/shared/exn3sd90.js +359 -0
  99. package/dist/shared/g6evj1j3.js +66 -0
  100. package/dist/shared/ga9j92qj.js +25 -0
  101. package/dist/shared/{t3g0k88x.js → gs74dbj5.js} +16 -12
  102. package/dist/shared/gzaveazc.js +285 -0
  103. package/dist/shared/{2jd1sn0e.js → hyh73qk6.js} +1 -1
  104. package/dist/shared/{r1fdd0f6.js → je4axnm8.js} +5 -2
  105. package/dist/shared/{1f0xa3cb.js → jvd0mqag.js} +3 -3
  106. package/dist/shared/jznfax0y.js +155 -0
  107. package/dist/shared/{cgbamx3e.js → kb2sdhfb.js} +7 -4
  108. package/dist/shared/{wt5rzv1h.js → kr0jzbw2.js} +5 -5
  109. package/dist/shared/{p6bjzq9q.js → ktz42yez.js} +12 -4
  110. package/dist/shared/{p248q79y.js → m6p12kbr.js} +2 -2
  111. package/dist/shared/{r6nwkxnt.js → m7n9h2a5.js} +4 -4
  112. package/dist/shared/{672t5vym.js → mss6ctgv.js} +2 -2
  113. package/dist/shared/{929h19xp.js → mtaszv5a.js} +40 -10
  114. package/dist/shared/{b3awxn7h.js → n816peh1.js} +1 -1
  115. package/dist/shared/nbw1z4yd.js +18 -0
  116. package/dist/shared/{nx9hh281.js → p6qc5gcp.js} +12 -11
  117. package/dist/shared/{pjr6xrm5.js → pbp1ss85.js} +20 -17
  118. package/dist/shared/pt94kz7f.js +6 -0
  119. package/dist/shared/q5c6ngn3.js +13 -0
  120. package/dist/shared/{xq0mvhts.js → rs73fca9.js} +4 -4
  121. package/dist/shared/{r11n8grd.js → sfz1ksn6.js} +7 -4
  122. package/dist/shared/{wynzeggs.js → svncdeh5.js} +6 -6
  123. package/dist/shared/{j60w0dvr.js → tbrvgw6z.js} +57 -38
  124. package/dist/shared/te5as7z7.js +622 -0
  125. package/dist/shared/{ttmg09vd.js → tsvww22q.js} +1 -1
  126. package/dist/shared/{y2t3jktd.js → ttsqgp4p.js} +19 -12
  127. package/dist/shared/{x72jb7ad.js → v8g1vgv8.js} +1 -1
  128. package/dist/shared/{sbdme3vy.js → vb6qfyve.js} +60 -10
  129. package/dist/shared/{5d8t84hg.js → vczs8gd4.js} +14 -14
  130. package/dist/shared/{5yv4k6a6.js → vqrjpcqn.js} +144 -2
  131. package/dist/shared/{3fqwmr09.js → w72kgg4q.js} +4 -4
  132. package/dist/shared/{1z04fa85.js → w79hsckn.js} +12 -57
  133. package/dist/shared/{09p049xa.js → wa07eas9.js} +42 -14
  134. package/dist/shared/{32cnm7kn.js → x933k9a3.js} +74 -38
  135. package/dist/shared/{n076ydsc.js → xhfsfkgk.js} +62 -28
  136. package/dist/shared/xjk7hnn5.js +272 -0
  137. package/dist/shared/{hazqqecq.js → xnxsed62.js} +50 -21
  138. package/dist/shared/y6njgyda.js +249 -0
  139. package/dist/shared/{8etn7d8w.js → z7s6fpxv.js} +1 -1
  140. package/dist/shared/{n7mcc1gk.js → zchaghkf.js} +4 -3
  141. package/dist/shared/zm0ptry0.js +40 -0
  142. package/dist/shared/{nsva2a6b.js → znm0bpp5.js} +3 -0
  143. package/dist/shared/zp9fmend.js +145 -0
  144. package/dist/tooling/cli.js +11 -11
  145. package/dist/tooling/migrate/engine.d.ts +3 -2
  146. package/dist/tooling/migrate/index.js +7 -7
  147. package/dist/tooling/testing/index.js +1 -1
  148. package/package.json +4 -2
  149. package/dist/shared/3qnhc7w5.js +0 -38
  150. package/dist/shared/7m88x3e9.js +0 -45
  151. package/dist/shared/8dqhth8c.js +0 -12
  152. package/dist/shared/d5azex6s.js +0 -6
  153. package/dist/shared/j3rkk835.js +0 -99
  154. package/dist/shared/k68eedmy.js +0 -85
  155. package/dist/shared/venqrjna.js +0 -216
  156. package/dist/shared/wxr8cwns.js +0 -6
  157. package/dist/shared/zm0mafk4.js +0 -92
@@ -3,10 +3,10 @@ import {
3
3
  } from "../../shared/ze4hxsmy.js";
4
4
  import {
5
5
  addSchemaHook
6
- } from "../../shared/hazqqecq.js";
6
+ } from "../../shared/xnxsed62.js";
7
7
  import {
8
8
  queryHandle
9
- } from "../../shared/j60w0dvr.js";
9
+ } from "../../shared/tbrvgw6z.js";
10
10
  import {
11
11
  OkmError2
12
12
  } from "../../shared/52szzakm.js";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Optimistic version checks.
3
+ *
4
+ * Loaded on the first update of a `versioned()` table. A plain update does
5
+ * not import it. The check and the bump are one statement: a stale row is
6
+ * not written, and the caller hears category `conflict`.
7
+ */
8
+ import type { Statement } from "../contracts/driver.js";
9
+ import type { QuerySchema } from "../dialects/pg/model.js";
10
+ import { type Outputs } from "./plan.js";
11
+ import { type Indexed } from "./plan.js";
12
+ import { type WriteMods } from "./write.js";
13
+ /** What {@link planVersioned} hands back to the write path. */
14
+ export type VersionPlan = {
15
+ readonly statements: readonly Statement[];
16
+ readonly outputs: Outputs;
17
+ readonly shape: "count" | "rows";
18
+ readonly expect: number | undefined;
19
+ /** Fields the caller tried to write, and the versions they sent. */
20
+ readonly versioned: {
21
+ readonly fields: readonly string[];
22
+ readonly sent: readonly number[];
23
+ };
24
+ };
25
+ /**
26
+ * Plans an update that checks `version` and bumps it.
27
+ *
28
+ * A list carries one version per row. `lock: "skip"` claims rows at that
29
+ * version and leaves locked rows. `.all()` is refused. An upsert is refused
30
+ * by the insert planner, not here.
31
+ *
32
+ * @param schema - Connected schema
33
+ * @param table - Target table
34
+ * @param input - `{ where, set, version }` or a per-row list
35
+ * @param options - Returning, expect, signal, timeout
36
+ * @param mods - `.expect`. `.all` is refused
37
+ * @returns One statement, and the versions the caller sent
38
+ */
39
+ export declare function planVersioned(schema: QuerySchema, table: Indexed, input: unknown, options: Record<string, unknown>, mods: WriteMods): VersionPlan;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Window helpers in `select` (spec §10, §13 tool 12).
3
+ *
4
+ * Loaded on the first `find` or `one` whose `select` contains a helper.
5
+ * A plain list of field names never loads this module. `page` does not take
6
+ * a window: its select is field names, and the keyset cursor has no window
7
+ * value to encode.
8
+ */
9
+ import type { QuerySchema } from "../dialects/pg/model.js";
10
+ import { type ReadCall } from "./plan.js";
11
+ /**
12
+ * Plans a read whose `select` contains a window helper.
13
+ *
14
+ * The returned call writes the statement. Includes, tenancy, and presets stay
15
+ * on the planner the plain read uses.
16
+ *
17
+ * @param schema - Connected schema
18
+ * @param call - The read, with a window in `select`
19
+ * @returns The same read, with its statement builder set
20
+ */
21
+ export declare function attach(schema: QuerySchema, call: ReadCall): ReadCall;
@@ -6,9 +6,9 @@
6
6
  */
7
7
  import type { ExecuteOptions, ExecuteResult, Statement } from "../contracts/driver.js";
8
8
  import type { IdGenerators } from "../contracts/generator.js";
9
- import type { PresetUse } from "../dialects/pg/model.js";
9
+ import type { ColumnModel, PresetUse } from "../dialects/pg/model.js";
10
10
  import type { QuerySchema } from "../dialects/pg/model.js";
11
- import { type ArchiveView, type CallScope } from "./plan.js";
11
+ import { type ArchiveView, type CallScope, type Indexed, type Sink } from "./plan.js";
12
12
  import { type RunHost } from "./tx.js";
13
13
  /** Statements in one insert stay under this many parameters. The protocol limit is 65535. */
14
14
  export declare const WRITE_PARAM_BUDGET = 2048;
@@ -41,6 +41,13 @@ export type PreparedWrite = {
41
41
  /** Set when `finish` can refuse a statement that already ran. `batch` refuses such a write (D183). */
42
42
  readonly checked?: "restore" | "expect" | "parent";
43
43
  finish(results: readonly ExecuteResult[]): unknown;
44
+ /**
45
+ * More statements once the planned ones have run.
46
+ *
47
+ * First-or-create uses this when the insert returned fewer rows than it was
48
+ * given. Empty means the insert already returned every row.
49
+ */
50
+ follow?(results: readonly ExecuteResult[]): readonly Statement[];
44
51
  };
45
52
  /**
46
53
  * Runs a write and decodes it.
@@ -71,3 +78,48 @@ export declare function executeWrite(host: WriteHost, op: WriteOp, table: string
71
78
  export declare function explainWrite(schema: QuerySchema, op: WriteOp, table: string, input: unknown, options: object, mods: WriteMods, generators?: IdGenerators, scope?: CallScope): Promise<{
72
79
  readonly statements: readonly Statement[];
73
80
  }>;
81
+ export declare function updateSliceBody(schema: QuerySchema, table: Indexed, rows: readonly {
82
+ readonly where: unknown;
83
+ readonly set: Record<string, unknown>;
84
+ }[], sql: Sql, wrapWhere?: boolean, hooks?: {
85
+ afterWhere?(sql: Sql, index: number): void;
86
+ afterSet?(sql: Sql): void;
87
+ }): void;
88
+ export declare function rejectDuplicateTargets(table: Indexed, rows: readonly {
89
+ readonly where: unknown;
90
+ }[]): void;
91
+ export declare function emitSet(sql: Sql, table: Indexed, set: Record<string, unknown>, alias: string): void;
92
+ export declare class Sql {
93
+ private readonly parts;
94
+ private readonly params;
95
+ text(value: string): void;
96
+ /**
97
+ * Binds one parameter.
98
+ *
99
+ * An empty data type leaves the parameter uncast so `where` can infer it.
100
+ */
101
+ param(wire: string | null, dataType: string): void;
102
+ sink(): Sink;
103
+ statement(): Statement;
104
+ }
105
+ export declare function writeSet(table: Indexed, value: unknown, allow: ReadonlySet<string>, tenancy: QuerySchema["tenancy"]): Record<string, unknown>;
106
+ /**
107
+ * A list that sets one field with an operator in one row and a plain value,
108
+ * or a different operator, in another.
109
+ */
110
+ export declare function rejectMixedSets(rows: readonly {
111
+ readonly set: Record<string, unknown>;
112
+ }[]): void;
113
+ /**
114
+ * `lock: "skip"` with `limit` on one update. Absent when the caller named neither.
115
+ *
116
+ * @param input - The update target
117
+ * @returns The row cap, or `undefined` when this update does not claim
118
+ */
119
+ export declare function readClaim(input: Record<string, unknown>): number | undefined;
120
+ export declare function rejectUpdateKeys(input: Record<string, unknown>, row: boolean): void;
121
+ export declare function requireFilter(table: Indexed, where: unknown, mods: WriteMods, op: WriteOp): void;
122
+ export declare function returningOption(table: Indexed, value: unknown): readonly ColumnModel[] | undefined;
123
+ export declare function expectOf(options: Record<string, unknown>, mods: WriteMods): number | undefined;
124
+ export declare function allowSet(table: Indexed, value: unknown): ReadonlySet<string>;
125
+ export declare function noteGuarded(table: Indexed, allow: ReadonlySet<string>, op: "insert" | "update"): void;
@@ -6,7 +6,7 @@ import {
6
6
  runCall,
7
7
  EMPTY_NOTICES,
8
8
  resultFrom
9
- } from "./cgbamx3e.js";
9
+ } from "./kb2sdhfb.js";
10
10
 
11
11
  // src/adapters/pg/postgresjs.ts
12
12
  import net from "node:net";
@@ -117,26 +117,18 @@ function open2(config) {
117
117
  execute(text, params, options) {
118
118
  return sockets.occupy(() => runCall(closed, options, (watch) => root.query(text, params, watch)));
119
119
  },
120
- async batch(statements, options) {
121
- return sockets.occupy(async () => {
122
- const { checkout } = await import("./r1fdd0f6.js");
123
- const connection = await checkout(held);
124
- try {
125
- return await connection.batch(statements, options);
126
- } finally {
127
- await connection.release();
128
- }
129
- });
120
+ batch(statements, options) {
121
+ return sockets.occupy(() => import("./je4axnm8.js").then((mod) => mod.runBatch(held, statements, options)));
130
122
  },
131
123
  reserve() {
132
124
  const gate = sockets.busyGate();
133
125
  gate.enter();
134
- return import("./r1fdd0f6.js").then((mod) => mod.holdCheckout(gate, () => mod.checkout(held)), (error) => {
126
+ return import("./je4axnm8.js").then((mod) => mod.holdCheckout(gate, () => mod.checkout(held)), (error) => {
135
127
  gate.leave();
136
128
  throw error;
137
129
  });
138
130
  },
139
- describe: (text, params) => sockets.occupy(() => import("./r1fdd0f6.js").then((mod) => mod.describe(sql, text, params))),
131
+ describe: (text, params) => sockets.occupy(() => import("./je4axnm8.js").then((mod) => mod.describe(sql, text, params))),
140
132
  stream(text, params) {
141
133
  const gate = sockets.busyGate();
142
134
  return async function* () {
@@ -151,7 +143,7 @@ function open2(config) {
151
143
  listen(channel, onNotify) {
152
144
  const gate = sockets.listenerGate();
153
145
  gate.enter();
154
- return import("./r1fdd0f6.js").then((mod) => mod.holdListener(gate, async () => {
146
+ return import("./je4axnm8.js").then((mod) => mod.holdListener(gate, async () => {
155
147
  const listening = await sql.listen(channel, onNotify);
156
148
  return async () => {
157
149
  await listening.unlisten();
@@ -221,7 +213,7 @@ class PgSession {
221
213
  onAbort();
222
214
  else
223
215
  watch.signal.addEventListener("abort", onAbort, { once: true });
224
- return import("./r1fdd0f6.js").then((mod) => mod.watchQuery(pending, watch, onAbort, {
216
+ return import("./je4axnm8.js").then((mod) => mod.watchQuery(pending, watch, onAbort, {
225
217
  start,
226
218
  text,
227
219
  notices: this.notices,
@@ -273,7 +265,7 @@ class PgSession {
273
265
  }
274
266
  }
275
267
  async function* loadStream(sql, text, params) {
276
- const { stream } = await import("./r1fdd0f6.js");
268
+ const { stream } = await import("./je4axnm8.js");
277
269
  yield* stream(sql, text, params);
278
270
  }
279
271
  function send(sql, text, params, named) {
@@ -0,0 +1,313 @@
1
+ import {
2
+ queryHandle,
3
+ readHandle,
4
+ readCall,
5
+ attachHttp
6
+ } from "./tbrvgw6z.js";
7
+ import {
8
+ emitWhere,
9
+ emitOrder,
10
+ refuseHidden,
11
+ rejectKeys,
12
+ readLimit,
13
+ projectExpr,
14
+ cell,
15
+ indexes,
16
+ quote,
17
+ list,
18
+ isRecord,
19
+ fail2
20
+ } from "./67txcrcw.js";
21
+ import {
22
+ OkmError2,
23
+ throwNamed2
24
+ } from "./52szzakm.js";
25
+
26
+ // src/runtime/aggregate.ts
27
+ var BUCKET = Symbol.for("okmodel.bucket");
28
+ var bucketChunk;
29
+ function loadBucketGroup() {
30
+ return bucketChunk ??= import("./3v7t1yrv.js");
31
+ }
32
+ var OPTIONS = [
33
+ "where",
34
+ "groupBy",
35
+ "count",
36
+ "sum",
37
+ "avg",
38
+ "min",
39
+ "max",
40
+ "orderBy",
41
+ "limit",
42
+ "route",
43
+ "signal",
44
+ "timeout"
45
+ ];
46
+ var NUMERIC = /^(smallint|integer|bigint|real|double precision|numeric|decimal)\b/;
47
+ var AS_NUMBER = (wire) => Number(wire);
48
+ var AS_TEXT = (wire) => wire;
49
+ var UNORDERED = /^(json|jsonb|bytea|point|line|tsvector)\b|\[\]$/;
50
+ var FUNCTIONS = [
51
+ { option: "sum", tag: "s", numeric: true },
52
+ { option: "avg", tag: "a", numeric: true },
53
+ { option: "min", tag: "n", numeric: false },
54
+ { option: "max", tag: "x", numeric: false }
55
+ ];
56
+ function build(session, table, options, mods, view) {
57
+ try {
58
+ if (seesBucket(options))
59
+ return bucketHandle(session, table, options, mods, view);
60
+ return plan(session, table, options, mods, view);
61
+ } catch (error) {
62
+ throw error instanceof OkmError2 ? attachHttp(session.http, error) : error;
63
+ }
64
+ }
65
+ function bucketHandle(session, table, options, mods, view) {
66
+ const open = () => loadBucketGroup().then((mod) => {
67
+ try {
68
+ return { it: plan(session, table, options, mods, view, mod) };
69
+ } catch (error) {
70
+ throw error instanceof OkmError2 ? attachHttp(session.http, error) : error;
71
+ }
72
+ });
73
+ return queryHandle(() => open().then((box) => box.it), {
74
+ inspect: () => open().then((box) => box.it.inspect()),
75
+ sql: () => open().then((box) => box.it.sql()),
76
+ all: (reason) => build(session, table, options, { ...mods, all: reason }, view)
77
+ });
78
+ }
79
+ function seesBucket(options) {
80
+ if (!isRecord(options))
81
+ return false;
82
+ const groupBy = options.groupBy;
83
+ if (!Array.isArray(groupBy))
84
+ return false;
85
+ for (const item of groupBy) {
86
+ if (typeof item === "object" && item !== null && BUCKET in item)
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ function plan(session, table, options, mods, view, buckets) {
92
+ const record = isRecord(options) ? options : {};
93
+ rejectKeys(record, OPTIONS, "aggregate");
94
+ const indexed = indexes(session.schema).get(table);
95
+ if (indexed === undefined)
96
+ fail2("OKM1120", `Table ${table} is not in the schema.`);
97
+ const spec = parse(indexed, record, buckets);
98
+ if (spec.groups.length > 0 && record.limit === undefined && mods.all === undefined) {
99
+ fail2("OKM1101", "aggregate() with groupBy needs a limit, or .all(reason) for every group.");
100
+ }
101
+ const names = groupNames(spec.groups);
102
+ const orderBy = record.orderBy;
103
+ if (orderBy !== undefined) {
104
+ if (!isRecord(orderBy))
105
+ fail2("OKM1120", "orderBy must be an object of fields.");
106
+ for (const field of Object.keys(orderBy)) {
107
+ if (!names.includes(field)) {
108
+ const column = indexed.columns.get(field);
109
+ if (column !== undefined)
110
+ refuseHidden(column);
111
+ throwNamed2("OKM1120", field, names, `aggregate() orders by a groupBy field. Accepted names: ${list(names)}.`);
112
+ }
113
+ }
114
+ }
115
+ if (record.limit !== undefined)
116
+ readLimit(record.limit);
117
+ const base = readCall("find", table, {
118
+ where: record.where,
119
+ orderBy,
120
+ limit: record.limit,
121
+ signal: record.signal,
122
+ timeout: record.timeout,
123
+ route: record.route
124
+ }, mods.all, session.scope, session.schema.model[table], session.schema, view);
125
+ const call = { ...base, op: "aggregate", build: statement(spec, buckets) };
126
+ return readHandle(session, table, call, mods, (compiled, rows) => decode(compiled, rows));
127
+ }
128
+ function parse(table, record, buckets) {
129
+ const grouped = readGroups(table, record.groupBy, buckets);
130
+ const count = record.count === true;
131
+ if (record.count !== undefined && typeof record.count !== "boolean") {
132
+ fail2("OKM1120", "count is true when you want a row count.");
133
+ }
134
+ const calls = [];
135
+ for (const { option, tag, numeric } of FUNCTIONS) {
136
+ for (const column of columns(table, record[option], option)) {
137
+ const fits = numeric ? NUMERIC.test(column.dataType) : !UNORDERED.test(column.dataType);
138
+ if (!fits) {
139
+ fail2("OKM1124", `${option} does not apply to ${column.dataType} column ${column.field}. ${numeric ? "sum and avg take integer, real, and numeric columns." : "min and max need an ordered type."}`);
140
+ }
141
+ calls.push({ option, tag, column, decode: numeric ? sumDecode(column) : column.decode });
142
+ }
143
+ }
144
+ if (!count && calls.length === 0 && grouped.groups.length === 0) {
145
+ fail2("OKM1120", "aggregate() needs count, sum, avg, min, max, or groupBy.");
146
+ }
147
+ return { groups: grouped.groups, bucketed: grouped.bucketed, count, calls };
148
+ }
149
+ function sumDecode(column) {
150
+ const kind = typeof (column.decode?.("0") ?? "");
151
+ if (kind === "string")
152
+ return AS_TEXT;
153
+ if (kind === "number")
154
+ return AS_NUMBER;
155
+ return fail2("OKM1124", `sum and avg need a column whose value is a number or a string. ${column.field} reads as ${kind}.`);
156
+ }
157
+ function columns(table, value, option) {
158
+ if (value === undefined)
159
+ return [];
160
+ if (!Array.isArray(value))
161
+ fail2("OKM1120", `${option} must be a list of field names.`);
162
+ const out = [];
163
+ for (const name of value) {
164
+ const column = typeof name === "string" ? table.columns.get(name) : undefined;
165
+ if (typeof name !== "string" || column === undefined) {
166
+ throwNamed2("OKM1120", String(name), table.names, `Field ${String(name)} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
167
+ }
168
+ if (column.hidden) {
169
+ fail2("OKM1120", `Field ${column.field} is hidden, so ${option} cannot read it.`);
170
+ }
171
+ out.push(column);
172
+ }
173
+ return out;
174
+ }
175
+ function groupNames(groups) {
176
+ const names = [];
177
+ for (const group of groups)
178
+ names.push(group.kind === "field" ? group.column.field : group.as);
179
+ return names;
180
+ }
181
+ function readGroups(table, value, buckets) {
182
+ if (value === undefined)
183
+ return { groups: [], bucketed: false };
184
+ if (!Array.isArray(value))
185
+ fail2("OKM1120", "groupBy must be a list of field names.");
186
+ const fields = new Set;
187
+ const aliases = new Set;
188
+ const groups = [];
189
+ let bucketed = false;
190
+ for (const item of value) {
191
+ if (typeof item === "object" && item !== null && BUCKET in item) {
192
+ if (buckets === undefined)
193
+ fail2("OKM1120", "bucket group");
194
+ groups.push(buckets.readBucket(table, item, fields, aliases));
195
+ bucketed = true;
196
+ continue;
197
+ }
198
+ const column = typeof item === "string" ? table.columns.get(item) : undefined;
199
+ if (typeof item !== "string" || column === undefined) {
200
+ throwNamed2("OKM1120", String(item), table.names, `Field ${String(item)} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
201
+ }
202
+ if (column.hidden) {
203
+ fail2("OKM1120", `Field ${column.field} is hidden, so groupBy cannot read it.`);
204
+ }
205
+ if (aliases.has(column.field)) {
206
+ fail2("OKM1120", `group field ${column.field} clashes with a bucket as.`);
207
+ }
208
+ fields.add(column.field);
209
+ groups.push({ kind: "field", column });
210
+ }
211
+ return { groups, bucketed };
212
+ }
213
+ function statement(spec, buckets) {
214
+ return (schema, table, sink, outputs, call) => {
215
+ sink.mark(`aggregate|${table.model.name}|g:${groupMark(spec.groups)}|c:${String(spec.count)}|${spec.calls.map((item) => `${item.tag}:${item.column.field}`).join(",")}|`);
216
+ sink.text("select ");
217
+ const wrote = { at: 0 };
218
+ for (const group of spec.groups) {
219
+ if (group.kind === "field") {
220
+ put(sink, outputs, wrote, projectExpr(`t.${quote(group.column.sql)}`, group.column.dataType), {
221
+ key: `g:${group.column.field}`,
222
+ decode: group.column.decode
223
+ });
224
+ } else {
225
+ if (buckets === undefined)
226
+ fail2("OKM1120", "bucket group");
227
+ buckets.writeBucket(sink, outputs, wrote, group);
228
+ }
229
+ }
230
+ if (spec.count)
231
+ put(sink, outputs, wrote, "count(*)::text", { key: "c:", decode: undefined });
232
+ for (const item of spec.calls) {
233
+ const ref = `t.${quote(item.column.sql)}`;
234
+ const expr = item.option === "sum" || item.option === "avg" ? `${item.option}(${ref})::text` : projectExpr(`${item.option}(${ref})`, item.column.dataType);
235
+ put(sink, outputs, wrote, expr, {
236
+ key: `${item.tag}:${item.column.field}`,
237
+ decode: item.decode
238
+ });
239
+ }
240
+ sink.text(" from ");
241
+ sink.text(quote(table.model.sql));
242
+ sink.text(" t");
243
+ emitWhere(schema, table, call.where, sink, "t", 0);
244
+ if (spec.groups.length > 0) {
245
+ sink.text(" group by ");
246
+ if (spec.bucketed) {
247
+ if (buckets === undefined)
248
+ fail2("OKM1120", "bucket group");
249
+ buckets.writeGrouped(sink, groupNames(spec.groups), call.orderBy);
250
+ } else {
251
+ sink.text(spec.groups.map((group) => fieldRef(group)).join(", "));
252
+ if (isRecord(call.orderBy) && Object.keys(call.orderBy).length > 0) {
253
+ emitOrder(table, call.orderBy, sink, "t");
254
+ } else {
255
+ sink.text(" order by ");
256
+ sink.text(spec.groups.map((group) => `${fieldRef(group)} asc nulls last`).join(", "));
257
+ }
258
+ }
259
+ }
260
+ if (call.limit !== undefined) {
261
+ const limit = String(call.limit);
262
+ sink.text(" limit ");
263
+ sink.param(limit);
264
+ sink.mark(`l:${limit}`);
265
+ }
266
+ if (typeof call.all === "string")
267
+ sink.mark(`all:${call.all}`);
268
+ };
269
+ }
270
+ function groupMark(groups) {
271
+ const parts = [];
272
+ for (const group of groups) {
273
+ parts.push(group.kind === "field" ? group.column.field : `b:${group.column.field}:${group.as}:${group.unit}:${String(group.shift)}`);
274
+ }
275
+ return parts.join(",");
276
+ }
277
+ function fieldRef(group) {
278
+ if (group.kind !== "field")
279
+ return fail2("OKM1120", "bucket group");
280
+ return `t.${quote(group.column.sql)}`;
281
+ }
282
+ function put(sink, outputs, wrote, expr, out) {
283
+ if (wrote.at > 0)
284
+ sink.text(", ");
285
+ sink.text(expr);
286
+ outputs?.fields.push({ key: out.key, at: wrote.at, decode: out.decode });
287
+ wrote.at += 1;
288
+ }
289
+ function decode(compiled, rows) {
290
+ const items = [];
291
+ for (const row of rows) {
292
+ const item = {};
293
+ for (const field of compiled.outputs.fields) {
294
+ const value = row[field.at];
295
+ const tag = field.key.slice(0, 2);
296
+ const name = field.key.slice(2);
297
+ if (tag === "g:") {
298
+ item[name] = cell(value, field.decode);
299
+ } else if (tag === "c:") {
300
+ item.count = Number(value ?? 0);
301
+ } else {
302
+ const group = tag === "s:" ? "sum" : tag === "a:" ? "avg" : tag === "n:" ? "min" : "max";
303
+ const into = item[group] ??= {};
304
+ into[name] = cell(value, field.decode);
305
+ }
306
+ }
307
+ items.push(item);
308
+ }
309
+ return items;
310
+ }
311
+ export {
312
+ build
313
+ };
@@ -4,12 +4,9 @@ import {
4
4
  nextTransactionDepth,
5
5
  EMPTY_NOTICES,
6
6
  resultFrom
7
- } from "./cgbamx3e.js";
7
+ } from "./kb2sdhfb.js";
8
8
 
9
9
  // src/adapters/pg/session.ts
10
- function quoteIdent2(name) {
11
- return `"${name.replaceAll('"', '""')}"`;
12
- }
13
10
  function noticeBuffer() {
14
11
  let notices;
15
12
  return {
@@ -63,7 +60,7 @@ class WireSession {
63
60
  return resultFrom(result, this.notices.since(start));
64
61
  }, (error) => {
65
62
  this.onLeave();
66
- return driverErrors().then((errors) => Promise.reject(errors.mapDriverError(error)));
63
+ return driverErrors().then((errors) => Promise.reject(errors.mapDriverError(stampQuery(error, text))));
67
64
  });
68
65
  }
69
66
  return this.watched(pending, watch, start, text);
@@ -110,7 +107,7 @@ class WireSession {
110
107
  return resultFrom(result, this.notices.since(start));
111
108
  }, (error) => {
112
109
  this.finishWatched(pending, watch, onAbort);
113
- return classify(error, watch);
110
+ return classify(stampQuery(error, text), watch);
114
111
  });
115
112
  }
116
113
  finishWatched(pending, watch, onAbort) {
@@ -119,22 +116,10 @@ class WireSession {
119
116
  watch.signal.removeEventListener("abort", onAbort);
120
117
  }
121
118
  }
122
- async function* cursorStream(run, text, params) {
123
- const name = "okm_stream";
124
- await run("BEGIN");
125
- try {
126
- await run(`DECLARE ${name} NO SCROLL CURSOR FOR ${text}`, params);
127
- for (;; ) {
128
- const chunk = await run(`FETCH 64 FROM ${name}`);
129
- if (chunk.rows.length === 0)
130
- break;
131
- yield chunk.rows;
132
- }
133
- } finally {
134
- await run("ROLLBACK").catch(() => {
135
- return;
136
- });
137
- }
119
+ function stampQuery(error, text) {
120
+ if (typeof error === "object" && error !== null && !("query" in error))
121
+ Reflect.set(error, "query", text);
122
+ return error;
138
123
  }
139
124
  async function classify(error, watch) {
140
125
  const errors = await driverErrors();
@@ -151,4 +136,4 @@ async function classify(error, watch) {
151
136
  return Promise.reject(errors.mapDriverError(error));
152
137
  }
153
138
 
154
- export { quoteIdent2, noticeBuffer, WireSession, cursorStream };
139
+ export { noticeBuffer, WireSession };