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.
- package/README.md +100 -1131
- package/dist/adapters/capabilities.d.ts +3 -0
- package/dist/adapters/failure.d.ts +13 -2
- package/dist/adapters/lazy-errors.d.ts +22 -0
- package/dist/adapters/pg/bunsql-extra.d.ts +54 -3
- package/dist/adapters/pg/bunsql.d.ts +14 -1
- package/dist/adapters/pg/cursor.d.ts +17 -0
- package/dist/adapters/pg/no-inflight-timeout.d.ts +11 -8
- package/dist/adapters/pg/nodepostgres-extra.d.ts +22 -1
- package/dist/adapters/pg/nodepostgres.d.ts +12 -0
- package/dist/adapters/pg/pglite-wire.d.ts +24 -0
- package/dist/adapters/pg/pglite.d.ts +10 -0
- package/dist/adapters/pg/postgres-extra.d.ts +27 -11
- package/dist/adapters/pg/reserved.d.ts +42 -0
- package/dist/adapters/pg/session.d.ts +0 -18
- package/dist/contracts/connection.d.ts +7 -6
- package/dist/dialects/pg/floor.d.ts +68 -0
- package/dist/dialects/pg/index.d.ts +6 -1
- package/dist/dialects/pg/index.js +49 -159
- package/dist/dialects/pg/keys.d.ts +11 -8
- package/dist/dialects/pg/model.d.ts +40 -0
- package/dist/dialects/pg/morph.d.ts +43 -0
- package/dist/dialects/pg/operator-fit.d.ts +2 -2
- package/dist/dialects/pg/operators.d.ts +14 -7
- package/dist/dialects/pg/ops/bucket.d.ts +65 -0
- package/dist/dialects/pg/ops/inc.d.ts +2 -2
- package/dist/dialects/pg/ops/matches.d.ts +9 -12
- package/dist/dialects/pg/ops/relevance.d.ts +13 -0
- package/dist/dialects/pg/ops/search-query.d.ts +22 -0
- package/dist/dialects/pg/ops/window.d.ts +103 -0
- package/dist/dialects/pg/preset.d.ts +1 -1
- package/dist/dialects/pg/relations.d.ts +100 -3
- package/dist/dialects/pg/schema.d.ts +31 -3
- package/dist/dialects/pg/view/index.js +1 -1
- package/dist/runtime/aggregate.d.ts +8 -2
- package/dist/runtime/bucket-group.d.ts +70 -0
- package/dist/runtime/conflict.d.ts +22 -4
- package/dist/runtime/include.d.ts +0 -8
- package/dist/runtime/inflight.d.ts +1 -1
- package/dist/runtime/page.d.ts +4 -1
- package/dist/runtime/pg/bun.js +12 -6
- package/dist/runtime/pg/pg.js +5 -5
- package/dist/runtime/pg/pglite.d.ts +1 -0
- package/dist/runtime/pg/pglite.js +9 -9
- package/dist/runtime/pg/postgresjs.js +5 -5
- package/dist/runtime/plan.d.ts +95 -5
- package/dist/runtime/read-stream.d.ts +13 -16
- package/dist/runtime/result-name.d.ts +17 -0
- package/dist/runtime/server-check.d.ts +3 -3
- package/dist/runtime/sort-bind.d.ts +21 -0
- package/dist/runtime/sort.d.ts +56 -0
- package/dist/runtime/sync.d.ts +70 -0
- package/dist/runtime/tenancy/index.js +7 -5
- package/dist/runtime/text-search.d.ts +20 -0
- package/dist/runtime/traits/index.d.ts +80 -0
- package/dist/runtime/traits/index.js +175 -11
- package/dist/runtime/tree.d.ts +38 -0
- package/dist/runtime/types.d.ts +218 -22
- package/dist/runtime/validate/index.js +2 -2
- package/dist/runtime/version.d.ts +39 -0
- package/dist/runtime/window.d.ts +21 -0
- package/dist/runtime/write.d.ts +54 -2
- package/dist/shared/{wkme6tep.js → 14vx95q4.js} +8 -16
- package/dist/shared/1est4ezg.js +313 -0
- package/dist/shared/{4az561fn.js → 1qa0qajz.js} +8 -23
- package/dist/shared/{2cg3wnxc.js → 1w508jbr.js} +593 -130
- package/dist/shared/2c13q96h.js +18 -0
- package/dist/shared/{b4v57v3x.js → 2d3xq4ps.js} +2 -2
- package/dist/shared/{1nw1f3vs.js → 2shdbn8q.js} +7 -7
- package/dist/shared/3v7t1yrv.js +208 -0
- package/dist/shared/{x3jc53kg.js → 5e3tf63j.js} +2 -2
- package/dist/shared/5fe3kbsh.js +10 -0
- package/dist/shared/5v3xf80k.js +37 -0
- package/dist/shared/{c58secgj.js → 63qtkrca.js} +38 -4
- package/dist/shared/{2z7wv5tj.js → 67txcrcw.js} +55 -34
- package/dist/shared/79dt02vc.js +12 -0
- package/dist/shared/{8x72s86k.js → 7k9grvf6.js} +10 -4
- package/dist/shared/7kms6fvj.js +20 -0
- package/dist/shared/7mnme5tn.js +8 -0
- package/dist/shared/7n7ddwt4.js +6 -0
- package/dist/shared/{5j36z8dn.js → 81ebdx9p.js} +39 -70
- package/dist/shared/{knmes2kh.js → 8skf0cam.js} +1 -1
- package/dist/shared/9a4ae4p8.js +31 -0
- package/dist/shared/9y3mmmh1.js +436 -0
- package/dist/shared/ab9gmbwy.js +213 -0
- package/dist/shared/{f0f1n281.js → ax7qv55k.js} +2 -2
- package/dist/shared/{g90m9b0g.js → aytqh5y2.js} +6 -5
- package/dist/shared/{vjpnzwjz.js → b5pftnn2.js} +1 -1
- package/dist/shared/{ag4c7516.js → b76b8d95.js} +2 -2
- package/dist/shared/{vmfkhszw.js → bg3rym3g.js} +33 -26
- package/dist/shared/{mb2ycs1p.js → bm22fvzr.js} +15 -15
- package/dist/shared/{q3j1tq7a.js → bz84tkzm.js} +148 -23
- package/dist/shared/{31vm914h.js → cm3w0ywp.js} +15 -16
- package/dist/shared/cn957f8d.js +179 -0
- package/dist/shared/{gwn6v667.js → cnkky55j.js} +25 -14
- package/dist/shared/{ys0xzyaj.js → dkxgzaqd.js} +224 -19
- package/dist/shared/dzj09cye.js +10 -0
- package/dist/shared/exn3sd90.js +359 -0
- package/dist/shared/g6evj1j3.js +66 -0
- package/dist/shared/ga9j92qj.js +25 -0
- package/dist/shared/{t3g0k88x.js → gs74dbj5.js} +16 -12
- package/dist/shared/gzaveazc.js +285 -0
- package/dist/shared/{2jd1sn0e.js → hyh73qk6.js} +1 -1
- package/dist/shared/{r1fdd0f6.js → je4axnm8.js} +5 -2
- package/dist/shared/{1f0xa3cb.js → jvd0mqag.js} +3 -3
- package/dist/shared/jznfax0y.js +155 -0
- package/dist/shared/{cgbamx3e.js → kb2sdhfb.js} +7 -4
- package/dist/shared/{wt5rzv1h.js → kr0jzbw2.js} +5 -5
- package/dist/shared/{p6bjzq9q.js → ktz42yez.js} +12 -4
- package/dist/shared/{p248q79y.js → m6p12kbr.js} +2 -2
- package/dist/shared/{r6nwkxnt.js → m7n9h2a5.js} +4 -4
- package/dist/shared/{672t5vym.js → mss6ctgv.js} +2 -2
- package/dist/shared/{929h19xp.js → mtaszv5a.js} +40 -10
- package/dist/shared/{b3awxn7h.js → n816peh1.js} +1 -1
- package/dist/shared/nbw1z4yd.js +18 -0
- package/dist/shared/{nx9hh281.js → p6qc5gcp.js} +12 -11
- package/dist/shared/{pjr6xrm5.js → pbp1ss85.js} +20 -17
- package/dist/shared/pt94kz7f.js +6 -0
- package/dist/shared/q5c6ngn3.js +13 -0
- package/dist/shared/{xq0mvhts.js → rs73fca9.js} +4 -4
- package/dist/shared/{r11n8grd.js → sfz1ksn6.js} +7 -4
- package/dist/shared/{wynzeggs.js → svncdeh5.js} +6 -6
- package/dist/shared/{j60w0dvr.js → tbrvgw6z.js} +57 -38
- package/dist/shared/te5as7z7.js +622 -0
- package/dist/shared/{ttmg09vd.js → tsvww22q.js} +1 -1
- package/dist/shared/{y2t3jktd.js → ttsqgp4p.js} +19 -12
- package/dist/shared/{x72jb7ad.js → v8g1vgv8.js} +1 -1
- package/dist/shared/{sbdme3vy.js → vb6qfyve.js} +60 -10
- package/dist/shared/{5d8t84hg.js → vczs8gd4.js} +14 -14
- package/dist/shared/{5yv4k6a6.js → vqrjpcqn.js} +144 -2
- package/dist/shared/{3fqwmr09.js → w72kgg4q.js} +4 -4
- package/dist/shared/{1z04fa85.js → w79hsckn.js} +12 -57
- package/dist/shared/{09p049xa.js → wa07eas9.js} +42 -14
- package/dist/shared/{32cnm7kn.js → x933k9a3.js} +74 -38
- package/dist/shared/{n076ydsc.js → xhfsfkgk.js} +62 -28
- package/dist/shared/xjk7hnn5.js +272 -0
- package/dist/shared/{hazqqecq.js → xnxsed62.js} +50 -21
- package/dist/shared/y6njgyda.js +249 -0
- package/dist/shared/{8etn7d8w.js → z7s6fpxv.js} +1 -1
- package/dist/shared/{n7mcc1gk.js → zchaghkf.js} +4 -3
- package/dist/shared/zm0ptry0.js +40 -0
- package/dist/shared/{nsva2a6b.js → znm0bpp5.js} +3 -0
- package/dist/shared/zp9fmend.js +145 -0
- package/dist/tooling/cli.js +11 -11
- package/dist/tooling/migrate/engine.d.ts +3 -2
- package/dist/tooling/migrate/index.js +7 -7
- package/dist/tooling/testing/index.js +1 -1
- package/package.json +4 -2
- package/dist/shared/3qnhc7w5.js +0 -38
- package/dist/shared/7m88x3e9.js +0 -45
- package/dist/shared/8dqhth8c.js +0 -12
- package/dist/shared/d5azex6s.js +0 -6
- package/dist/shared/j3rkk835.js +0 -99
- package/dist/shared/k68eedmy.js +0 -85
- package/dist/shared/venqrjna.js +0 -216
- package/dist/shared/wxr8cwns.js +0 -6
- 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/
|
|
6
|
+
} from "../../shared/xnxsed62.js";
|
|
7
7
|
import {
|
|
8
8
|
queryHandle
|
|
9
|
-
} from "../../shared/
|
|
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;
|
package/dist/runtime/write.d.ts
CHANGED
|
@@ -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 "./
|
|
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
|
-
|
|
121
|
-
return sockets.occupy(
|
|
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("./
|
|
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("./
|
|
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("./
|
|
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("./
|
|
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("./
|
|
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 "./
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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 {
|
|
139
|
+
export { noticeBuffer, WireSession };
|