okmodel 0.0.0 → 0.1.1
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 +246 -1
- package/dist/adapters/capabilities.d.ts +62 -0
- package/dist/adapters/error.d.ts +91 -0
- package/dist/adapters/failure.d.ts +39 -0
- package/dist/adapters/pg/batch.d.ts +36 -0
- package/dist/adapters/pg/call.d.ts +59 -0
- package/dist/adapters/pg/pglite.d.ts +21 -0
- package/dist/adapters/pg/postgres-extra.d.ts +43 -0
- package/dist/adapters/pg/postgresjs.d.ts +95 -0
- package/dist/adapters/pg/result.d.ts +30 -0
- package/dist/contracts/catalog/build.d.ts +18 -0
- package/dist/contracts/catalog/document.d.ts +79 -0
- package/dist/contracts/catalog/enum.d.ts +35 -0
- package/dist/contracts/catalog/identifier.d.ts +71 -0
- package/dist/contracts/catalog/identity.d.ts +98 -0
- package/dist/contracts/catalog/index.d.ts +11 -0
- package/dist/contracts/catalog/json.d.ts +17 -0
- package/dist/contracts/catalog/object.d.ts +142 -0
- package/dist/contracts/catalog/order.d.ts +43 -0
- package/dist/contracts/catalog/rewrite.d.ts +18 -0
- package/dist/contracts/catalog/types.d.ts +292 -0
- package/dist/contracts/catalog/words.d.ts +13 -0
- package/dist/contracts/connection.d.ts +33 -0
- package/dist/contracts/driver.d.ts +163 -0
- package/dist/contracts/error.d.ts +298 -0
- package/dist/contracts/index.d.ts +11 -0
- package/dist/contracts/index.js +16 -0
- package/dist/contracts/internal.d.ts +15 -0
- package/dist/contracts/internal.js +68 -0
- package/dist/contracts/nearest.d.ts +15 -0
- package/dist/contracts/rows.d.ts +84 -0
- package/dist/contracts/sha256.d.ts +14 -0
- package/dist/contracts/utf8.d.ts +33 -0
- package/dist/dialects/pg/array-literal.d.ts +25 -0
- package/dist/dialects/pg/bool.d.ts +46 -0
- package/dist/dialects/pg/column.d.ts +386 -0
- package/dist/dialects/pg/compile.d.ts +81 -0
- package/dist/dialects/pg/custom.d.ts +21 -0
- package/dist/dialects/pg/ddl.d.ts +70 -0
- package/dist/dialects/pg/decimal.d.ts +62 -0
- package/dist/dialects/pg/declarations.d.ts +34 -0
- package/dist/dialects/pg/emit.d.ts +18 -0
- package/dist/dialects/pg/enum.d.ts +30 -0
- package/dist/dialects/pg/errors.d.ts +30 -0
- package/dist/dialects/pg/finite.d.ts +15 -0
- package/dist/dialects/pg/geometry.d.ts +55 -0
- package/dist/dialects/pg/index.d.ts +45 -0
- package/dist/dialects/pg/index.js +1821 -0
- package/dist/dialects/pg/integer.d.ts +96 -0
- package/dist/dialects/pg/introspect.d.ts +43 -0
- package/dist/dialects/pg/json.d.ts +43 -0
- package/dist/dialects/pg/keys.d.ts +32 -0
- package/dist/dialects/pg/misuse.d.ts +31 -0
- package/dist/dialects/pg/model.d.ts +71 -0
- package/dist/dialects/pg/namespace.d.ts +63 -0
- package/dist/dialects/pg/network.d.ts +28 -0
- package/dist/dialects/pg/operators.d.ts +71 -0
- package/dist/dialects/pg/ops/between.d.ts +10 -0
- package/dist/dialects/pg/ops/contains.d.ts +8 -0
- package/dist/dialects/pg/ops/endsWith.d.ts +8 -0
- package/dist/dialects/pg/ops/eq.d.ts +9 -0
- package/dist/dialects/pg/ops/every.d.ts +9 -0
- package/dist/dialects/pg/ops/gt.d.ts +9 -0
- package/dist/dialects/pg/ops/gte.d.ts +9 -0
- package/dist/dialects/pg/ops/has.d.ts +9 -0
- package/dist/dialects/pg/ops/ilike.d.ts +8 -0
- package/dist/dialects/pg/ops/inList.d.ts +9 -0
- package/dist/dialects/pg/ops/inc.d.ts +13 -0
- package/dist/dialects/pg/ops/like.d.ts +8 -0
- package/dist/dialects/pg/ops/lt.d.ts +9 -0
- package/dist/dialects/pg/ops/lte.d.ts +9 -0
- package/dist/dialects/pg/ops/none.d.ts +9 -0
- package/dist/dialects/pg/ops/not.d.ts +9 -0
- package/dist/dialects/pg/ops/notIn.d.ts +9 -0
- package/dist/dialects/pg/ops/or.d.ts +9 -0
- package/dist/dialects/pg/ops/startsWith.d.ts +8 -0
- package/dist/dialects/pg/quote.d.ts +24 -0
- package/dist/dialects/pg/range.d.ts +50 -0
- package/dist/dialects/pg/relations.d.ts +45 -0
- package/dist/dialects/pg/schema.d.ts +77 -0
- package/dist/dialects/pg/search.d.ts +23 -0
- package/dist/dialects/pg/table.d.ts +201 -0
- package/dist/dialects/pg/temporal.d.ts +211 -0
- package/dist/dialects/pg/text.d.ts +44 -0
- package/dist/dialects/pg/time.d.ts +46 -0
- package/dist/okm.js +2 -0
- package/dist/okmodel.js +2 -0
- package/dist/runtime/client.d.ts +47 -0
- package/dist/runtime/conflict.d.ts +38 -0
- package/dist/runtime/drift-detail.d.ts +32 -0
- package/dist/runtime/drift.d.ts +29 -0
- package/dist/runtime/include.d.ts +15 -0
- package/dist/runtime/pg/pglite.d.ts +25 -0
- package/dist/runtime/pg/pglite.js +48 -0
- package/dist/runtime/pg/postgresjs.d.ts +29 -0
- package/dist/runtime/pg/postgresjs.js +62 -0
- package/dist/runtime/plan.d.ts +167 -0
- package/dist/runtime/tx.d.ts +29 -0
- package/dist/runtime/types.d.ts +385 -0
- package/dist/runtime/write.d.ts +48 -0
- package/dist/shared/05w44d8g.js +1524 -0
- package/dist/shared/523ft3e8.js +171 -0
- package/dist/shared/8b04d28c.js +14 -0
- package/dist/shared/cn0n68ft.js +16 -0
- package/dist/shared/dkn3y60a.js +374 -0
- package/dist/shared/dynq16vb.js +81 -0
- package/dist/shared/fgqgsb5z.js +281 -0
- package/dist/shared/g5fhqmwe.js +188 -0
- package/dist/shared/gk7cs7sp.js +566 -0
- package/dist/shared/hadrfb9h.js +398 -0
- package/dist/shared/hd293j9g.js +98 -0
- package/dist/shared/j5cn2963.js +701 -0
- package/dist/shared/jjb7yht9.js +37 -0
- package/dist/shared/kprcwaha.js +30 -0
- package/dist/shared/m3ypagh5.js +347 -0
- package/dist/shared/mkjmqjv4.js +456 -0
- package/dist/shared/mtkwv8wx.js +18 -0
- package/dist/shared/ngk8gj61.js +40 -0
- package/dist/shared/p0cbr3g2.js +79 -0
- package/dist/shared/qk3hxfja.js +83 -0
- package/dist/shared/rgs0st28.js +1066 -0
- package/dist/shared/w8samvk1.js +18 -0
- package/dist/shared/x5x59srr.js +566 -0
- package/dist/shared/xqbhg6cn.js +119 -0
- package/dist/shared/xy79j5hq.js +177 -0
- package/dist/shared/y9m15dfv.js +888 -0
- package/dist/shared/ycwz42az.js +412 -0
- package/dist/shared/yd1kzwn2.js +148 -0
- package/dist/shared/zhc5vmgf.js +257 -0
- package/dist/tooling/cli.d.ts +1 -0
- package/dist/tooling/cli.js +193 -0
- package/dist/tooling/errors/registry.d.ts +33 -0
- package/dist/tooling/migrate/apply.d.ts +86 -0
- package/dist/tooling/migrate/commands.d.ts +21 -0
- package/dist/tooling/migrate/config.d.ts +48 -0
- package/dist/tooling/migrate/dev.d.ts +16 -0
- package/dist/tooling/migrate/equal.d.ts +15 -0
- package/dist/tooling/migrate/files.d.ts +19 -0
- package/dist/tooling/migrate/index.d.ts +7 -0
- package/dist/tooling/migrate/index.js +7 -0
- package/dist/tooling/migrate/plan.d.ts +76 -0
- package/dist/tooling/migrate/policy.d.ts +78 -0
- package/dist/tooling/migrate/project.d.ts +69 -0
- package/dist/tooling/migrate/status.d.ts +74 -0
- package/dist/tooling/migrate/values.d.ts +29 -0
- package/package.json +109 -7
- package/dist/index.js +0 -0
|
@@ -0,0 +1,1066 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isOperator,
|
|
3
|
+
operatorName,
|
|
4
|
+
operatorValue
|
|
5
|
+
} from "./cn0n68ft.js";
|
|
6
|
+
import {
|
|
7
|
+
assertIdentifier2
|
|
8
|
+
} from "./mkjmqjv4.js";
|
|
9
|
+
import {
|
|
10
|
+
OkmError2,
|
|
11
|
+
safe2,
|
|
12
|
+
throwNamed2
|
|
13
|
+
} from "./ycwz42az.js";
|
|
14
|
+
|
|
15
|
+
// src/runtime/plan.ts
|
|
16
|
+
var FIND_OPTIONS = ["include", "limit", "orderBy", "select", "where"];
|
|
17
|
+
var ONE_OPTIONS = ["include", "orderBy", "select", "where"];
|
|
18
|
+
var FILTER_OPTIONS = ["where"];
|
|
19
|
+
var BASE_RULES = [
|
|
20
|
+
{ rule: "parameterised", contribution: "planner", provenance: "planner" },
|
|
21
|
+
{ rule: "allowlisted", contribution: "catalog", provenance: "catalog" },
|
|
22
|
+
{ rule: "identifier", contribution: "catalog", provenance: "catalog" }
|
|
23
|
+
];
|
|
24
|
+
var indexesOf = new WeakMap;
|
|
25
|
+
function verifyRead(rules) {}
|
|
26
|
+
function bindCall(schema, call, hooks) {
|
|
27
|
+
const params = [];
|
|
28
|
+
const marks = [];
|
|
29
|
+
const sink = {
|
|
30
|
+
text() {},
|
|
31
|
+
param(encoded) {
|
|
32
|
+
params.push(encoded);
|
|
33
|
+
},
|
|
34
|
+
mark(token) {
|
|
35
|
+
marks.push(token);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
emit(schema, call, sink, undefined, hooks);
|
|
39
|
+
verifyRead(BASE_RULES);
|
|
40
|
+
return { key: marks.join(""), params };
|
|
41
|
+
}
|
|
42
|
+
function compileCall(schema, call, key, hooks) {
|
|
43
|
+
const parts = [];
|
|
44
|
+
let n = 0;
|
|
45
|
+
const sink = {
|
|
46
|
+
text(value) {
|
|
47
|
+
parts.push(value);
|
|
48
|
+
},
|
|
49
|
+
param() {
|
|
50
|
+
n += 1;
|
|
51
|
+
parts.push(`$${String(n)}`);
|
|
52
|
+
},
|
|
53
|
+
mark() {}
|
|
54
|
+
};
|
|
55
|
+
const fields = [];
|
|
56
|
+
const includes = [];
|
|
57
|
+
emit(schema, call, sink, { fields, includes }, hooks);
|
|
58
|
+
return {
|
|
59
|
+
fingerprint: fingerprint(key),
|
|
60
|
+
text: parts.join(""),
|
|
61
|
+
mode: call.op,
|
|
62
|
+
outputs: { fields, includes }
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function decodeResult(plan, rows, table, rowOf = decodeRow) {
|
|
66
|
+
if (plan.mode === "count") {
|
|
67
|
+
const cell = rows[0]?.[0];
|
|
68
|
+
if (cell === null || cell === undefined)
|
|
69
|
+
return 0;
|
|
70
|
+
return Number(cell);
|
|
71
|
+
}
|
|
72
|
+
if (plan.mode === "exists") {
|
|
73
|
+
const cell = rows[0]?.[0];
|
|
74
|
+
return cell === "t" || cell === "true" || cell === "1";
|
|
75
|
+
}
|
|
76
|
+
const decoded = [];
|
|
77
|
+
for (const row of rows)
|
|
78
|
+
decoded.push(rowOf(plan.outputs, row));
|
|
79
|
+
if (plan.mode === "one") {
|
|
80
|
+
if (decoded.length > 1) {
|
|
81
|
+
throw new OkmError2("not_unique", `one() on ${table} matched more than one row. Pass orderBy to take the first, or narrow where.`, { kind: "not_unique", table });
|
|
82
|
+
}
|
|
83
|
+
return decoded[0] ?? null;
|
|
84
|
+
}
|
|
85
|
+
return decoded;
|
|
86
|
+
}
|
|
87
|
+
function logicalIntent(call) {
|
|
88
|
+
return {
|
|
89
|
+
op: call.op,
|
|
90
|
+
table: call.table,
|
|
91
|
+
where: plain(call.where),
|
|
92
|
+
select: call.select,
|
|
93
|
+
orderBy: call.orderBy,
|
|
94
|
+
limit: call.limit,
|
|
95
|
+
include: plain(call.include),
|
|
96
|
+
all: call.all
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function appliedRules(call) {
|
|
100
|
+
const rules = [...BASE_RULES];
|
|
101
|
+
if (call.where !== undefined) {
|
|
102
|
+
rules.push({ rule: "filter", contribution: "caller", provenance: "caller" });
|
|
103
|
+
}
|
|
104
|
+
if (call.op === "find" || hasMany(call.include)) {
|
|
105
|
+
rules.push({
|
|
106
|
+
rule: "bounded",
|
|
107
|
+
contribution: call.all ?? "limit",
|
|
108
|
+
provenance: "caller"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return rules;
|
|
112
|
+
}
|
|
113
|
+
function emit(schema, call, sink, outputs, hooks) {
|
|
114
|
+
const table = indexes(schema).get(call.table);
|
|
115
|
+
if (table === undefined) {
|
|
116
|
+
throwNamed2("OKM1120", call.table, [...indexes(schema).keys()], `Table ${call.table} is not in the schema. Accepted names: ${list([...indexes(schema).keys()])}.`);
|
|
117
|
+
}
|
|
118
|
+
rejectOptions(call);
|
|
119
|
+
const selected = selectedColumns(table, call.select);
|
|
120
|
+
sink.mark(`${call.op}|${call.table}|`);
|
|
121
|
+
if (call.op === "count") {
|
|
122
|
+
sink.text("select count(*)::text from ");
|
|
123
|
+
sink.text(quote(table.model.sql));
|
|
124
|
+
sink.text(" t");
|
|
125
|
+
emitWhere(schema, table, call.where, sink, "t", 0);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (call.op === "exists") {
|
|
129
|
+
sink.text("select exists(select 1 from ");
|
|
130
|
+
sink.text(quote(table.model.sql));
|
|
131
|
+
sink.text(" t");
|
|
132
|
+
emitWhere(schema, table, call.where, sink, "t", 0);
|
|
133
|
+
sink.text(")::text");
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
sink.text("select ");
|
|
137
|
+
let column = 0;
|
|
138
|
+
for (let index = 0;index < selected.length; index += 1) {
|
|
139
|
+
const item = selected[index];
|
|
140
|
+
if (item === undefined)
|
|
141
|
+
continue;
|
|
142
|
+
if (index > 0)
|
|
143
|
+
sink.text(", ");
|
|
144
|
+
sink.text(projectExpr(`t.${quote(item.sql)}`, item.dataType));
|
|
145
|
+
sink.mark(`s:${item.field}`);
|
|
146
|
+
outputs?.fields.push({ key: item.field, at: column, decode: item.decode });
|
|
147
|
+
column += 1;
|
|
148
|
+
}
|
|
149
|
+
if (call.include !== undefined && hooks === undefined) {
|
|
150
|
+
fail("OKM1120", "An include needs the include planner.");
|
|
151
|
+
}
|
|
152
|
+
const includes = hooks === undefined || call.include === undefined ? [] : hooks.read(schema, table, call.include, call.all);
|
|
153
|
+
if (selected.length === 0 && includes.length === 0)
|
|
154
|
+
sink.text("1");
|
|
155
|
+
for (let index = 0;index < includes.length; index += 1) {
|
|
156
|
+
const include = includes[index];
|
|
157
|
+
if (include === undefined)
|
|
158
|
+
continue;
|
|
159
|
+
if (hooks === undefined)
|
|
160
|
+
continue;
|
|
161
|
+
column = hooks.select(include, `i${String(index)}`, sink, outputs, column);
|
|
162
|
+
}
|
|
163
|
+
sink.text(" from ");
|
|
164
|
+
sink.text(quote(table.model.sql));
|
|
165
|
+
sink.text(" t");
|
|
166
|
+
for (let index = 0;index < includes.length; index += 1) {
|
|
167
|
+
const include = includes[index];
|
|
168
|
+
if (include === undefined)
|
|
169
|
+
continue;
|
|
170
|
+
hooks?.join(schema, include, sink, "t", `i${String(index)}`, 0);
|
|
171
|
+
}
|
|
172
|
+
emitWhere(schema, table, call.where, sink, "t", 0);
|
|
173
|
+
emitOrder(table, call.orderBy, sink, "t");
|
|
174
|
+
emitLimit(call, sink);
|
|
175
|
+
}
|
|
176
|
+
function emitJoin(sink, parent, child, relation) {
|
|
177
|
+
for (let index = 0;index < relation.local.length; index += 1) {
|
|
178
|
+
const local = relation.local[index];
|
|
179
|
+
const remote = relation.remote[index];
|
|
180
|
+
if (local === undefined || remote === undefined)
|
|
181
|
+
continue;
|
|
182
|
+
if (index > 0)
|
|
183
|
+
sink.text(" and ");
|
|
184
|
+
sink.text(child);
|
|
185
|
+
sink.text(".");
|
|
186
|
+
sink.text(quote(remote));
|
|
187
|
+
sink.text(" = ");
|
|
188
|
+
sink.text(parent);
|
|
189
|
+
sink.text(".");
|
|
190
|
+
sink.text(quote(local));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function emitWhere(schema, table, where, sink, alias, depth, appended = false) {
|
|
194
|
+
if (where === undefined)
|
|
195
|
+
return;
|
|
196
|
+
if (isOperator(where) && operatorName(where) === "or") {
|
|
197
|
+
sink.text(appended ? " and " : " where ");
|
|
198
|
+
emitOr(schema, table, operatorValue(where), sink, alias, depth);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (!isRecord(where)) {
|
|
202
|
+
fail("OKM1121", "where must be an object of fields. Wrap an object value with eq.");
|
|
203
|
+
}
|
|
204
|
+
const keys = Object.keys(where);
|
|
205
|
+
if (keys.length === 0)
|
|
206
|
+
return;
|
|
207
|
+
sink.text(appended ? " and " : " where ");
|
|
208
|
+
emitAnd(schema, table, where, sink, alias, depth);
|
|
209
|
+
}
|
|
210
|
+
function emitAnd(schema, table, where, sink, alias, depth) {
|
|
211
|
+
if (!isRecord(where)) {
|
|
212
|
+
fail("OKM1121", "where must be an object of fields. Wrap an object value with eq.");
|
|
213
|
+
}
|
|
214
|
+
const keys = Object.keys(where);
|
|
215
|
+
let wrote = false;
|
|
216
|
+
for (const key of keys) {
|
|
217
|
+
const value = where[key];
|
|
218
|
+
if (value === undefined)
|
|
219
|
+
continue;
|
|
220
|
+
if (wrote)
|
|
221
|
+
sink.text(" and ");
|
|
222
|
+
wrote = true;
|
|
223
|
+
emitPredicate(schema, table, key, value, sink, alias, depth);
|
|
224
|
+
}
|
|
225
|
+
if (!wrote)
|
|
226
|
+
sink.text("true");
|
|
227
|
+
}
|
|
228
|
+
function emitPredicate(schema, table, key, value, sink, alias, depth) {
|
|
229
|
+
const relation = table.relations.get(key);
|
|
230
|
+
if (relation !== undefined) {
|
|
231
|
+
emitRelationFilter(schema, relation, value, sink, alias, depth);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const column = table.columns.get(key);
|
|
235
|
+
if (column === undefined) {
|
|
236
|
+
throwNamed2("OKM1120", key, table.names, `Field ${key} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
|
|
237
|
+
}
|
|
238
|
+
sink.mark(`w:${key}`);
|
|
239
|
+
emitOperand(schema, table, column, value, sink, alias, depth);
|
|
240
|
+
}
|
|
241
|
+
function emitOperand(schema, table, column, value, sink, alias, depth) {
|
|
242
|
+
const ref = `${alias}.${quote(column.sql)}`;
|
|
243
|
+
if (isOperator(value)) {
|
|
244
|
+
emitOperator(schema, table, column, ref, operatorName(value), operatorValue(value), sink, alias, depth);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (value === null) {
|
|
248
|
+
sink.mark(":null");
|
|
249
|
+
sink.text(ref);
|
|
250
|
+
sink.text(" is null");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (typeof value === "object") {
|
|
254
|
+
fail("OKM1121", `Field ${column.field} received an object. Wrap it with eq. eq is the equality form for json and jsonb.`);
|
|
255
|
+
}
|
|
256
|
+
sink.mark(":eq");
|
|
257
|
+
sink.text(ref);
|
|
258
|
+
sink.text(" = ");
|
|
259
|
+
sink.param(column.encode(value));
|
|
260
|
+
}
|
|
261
|
+
function emitOperator(schema, table, column, ref, name, value, sink, alias, depth) {
|
|
262
|
+
sink.mark(`:${name}`);
|
|
263
|
+
if (name === "or") {
|
|
264
|
+
emitOr(schema, table, value, sink, alias, depth);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (column === undefined) {
|
|
268
|
+
fail("OKM1121", "That operator needs a field.");
|
|
269
|
+
}
|
|
270
|
+
if (name === "not") {
|
|
271
|
+
emitNot(schema, table, column, ref, value, sink, alias, depth);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (name === "eq") {
|
|
275
|
+
emitOperand(schema, table, column, value, sink, alias, depth);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (name === "lt" || name === "lte" || name === "gt" || name === "gte") {
|
|
279
|
+
sink.text(ref);
|
|
280
|
+
sink.text(name === "lt" ? " < " : name === "lte" ? " <= " : name === "gt" ? " > " : " >= ");
|
|
281
|
+
sink.param(column.encode(requireValue(column, value)));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (name === "between") {
|
|
285
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
286
|
+
fail("OKM1121", `between on ${column.field} needs two values.`);
|
|
287
|
+
}
|
|
288
|
+
sink.text(ref);
|
|
289
|
+
sink.text(" between ");
|
|
290
|
+
sink.param(column.encode(requireValue(column, value[0])));
|
|
291
|
+
sink.text(" and ");
|
|
292
|
+
sink.param(column.encode(requireValue(column, value[1])));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (name === "inList" || name === "notIn") {
|
|
296
|
+
emitIn(column, ref, name, value, sink);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (name === "startsWith" || name === "contains" || name === "endsWith") {
|
|
300
|
+
if (typeof value !== "string") {
|
|
301
|
+
fail("OKM1121", `${name} on ${column.field} needs a string.`);
|
|
302
|
+
}
|
|
303
|
+
sink.text(ref);
|
|
304
|
+
sink.text(" like ");
|
|
305
|
+
sink.param(literalPattern(value, name));
|
|
306
|
+
sink.text(" escape E'\\\\'");
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (name === "like" || name === "ilike") {
|
|
310
|
+
if (typeof value !== "string") {
|
|
311
|
+
fail("OKM1121", `${name} on ${column.field} needs a string.`);
|
|
312
|
+
}
|
|
313
|
+
sink.text(ref);
|
|
314
|
+
sink.text(name === "like" ? " like " : " ilike ");
|
|
315
|
+
sink.param(value);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
fail("OKM1121", `${name} is not a value operator on ${column.field}. Use has, none, or every on a relation.`);
|
|
319
|
+
}
|
|
320
|
+
function emitNot(schema, table, column, ref, value, sink, alias, depth) {
|
|
321
|
+
if (value === null) {
|
|
322
|
+
sink.mark(":null");
|
|
323
|
+
sink.text(ref);
|
|
324
|
+
sink.text(" is not null");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (isOperator(value) && operatorName(value) === "inList") {
|
|
328
|
+
const listed = operatorValue(value);
|
|
329
|
+
if (Array.isArray(listed) && listed.length === 0) {
|
|
330
|
+
sink.text("true");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
sink.text("not (");
|
|
335
|
+
emitOperand(schema, table, column, value, sink, alias, depth);
|
|
336
|
+
sink.text(")");
|
|
337
|
+
}
|
|
338
|
+
function emitIn(column, ref, name, value, sink) {
|
|
339
|
+
if (!Array.isArray(value)) {
|
|
340
|
+
fail("OKM1121", `${name} on ${column.field} needs a list.`);
|
|
341
|
+
}
|
|
342
|
+
sink.mark(`:${String(value.length)}`);
|
|
343
|
+
if (value.length === 0) {
|
|
344
|
+
sink.text(name === "inList" ? "false" : "true");
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
sink.text(ref);
|
|
348
|
+
sink.text(name === "inList" ? " in (" : " not in (");
|
|
349
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
350
|
+
if (index > 0)
|
|
351
|
+
sink.text(", ");
|
|
352
|
+
sink.param(column.encode(requireValue(column, value[index])));
|
|
353
|
+
}
|
|
354
|
+
sink.text(")");
|
|
355
|
+
}
|
|
356
|
+
function emitOr(schema, table, value, sink, alias, depth) {
|
|
357
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
358
|
+
sink.text("false");
|
|
359
|
+
sink.mark(":0");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
sink.mark(`:${String(value.length)}`);
|
|
363
|
+
sink.text("(");
|
|
364
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
365
|
+
const branch = value[index];
|
|
366
|
+
if (index > 0)
|
|
367
|
+
sink.text(" or ");
|
|
368
|
+
sink.text("(");
|
|
369
|
+
if (!isRecord(branch)) {
|
|
370
|
+
fail("OKM1121", "or() branches must be where objects.");
|
|
371
|
+
}
|
|
372
|
+
let wrote = false;
|
|
373
|
+
for (const key of Object.keys(branch)) {
|
|
374
|
+
const item = branch[key];
|
|
375
|
+
if (item === undefined)
|
|
376
|
+
continue;
|
|
377
|
+
if (wrote)
|
|
378
|
+
sink.text(" and ");
|
|
379
|
+
wrote = true;
|
|
380
|
+
emitPredicate(schema, table, key, item, sink, alias, depth);
|
|
381
|
+
}
|
|
382
|
+
if (!wrote)
|
|
383
|
+
sink.text("true");
|
|
384
|
+
sink.text(")");
|
|
385
|
+
}
|
|
386
|
+
sink.text(")");
|
|
387
|
+
}
|
|
388
|
+
function emitRelationFilter(schema, relation, value, sink, alias, depth) {
|
|
389
|
+
if (!isOperator(value)) {
|
|
390
|
+
fail("OKM1121", `Relation ${relation.name} needs has, none, or every.`);
|
|
391
|
+
}
|
|
392
|
+
const name = operatorName(value);
|
|
393
|
+
if (name !== "has" && name !== "none" && name !== "every") {
|
|
394
|
+
fail("OKM1121", `Relation ${relation.name} needs has, none, or every.`);
|
|
395
|
+
}
|
|
396
|
+
const child = indexes(schema).get(relation.table);
|
|
397
|
+
if (child === undefined) {
|
|
398
|
+
fail("OKM1120", `Relation ${relation.name} names ${relation.table}, which is not in the schema.`);
|
|
399
|
+
}
|
|
400
|
+
const childAlias = `r${String(depth)}_${relation.name}`;
|
|
401
|
+
sink.mark(`rel:${relation.name}:${name}`);
|
|
402
|
+
if (name === "none" || name === "every")
|
|
403
|
+
sink.text("not ");
|
|
404
|
+
sink.text("exists (select 1 from ");
|
|
405
|
+
sink.text(quote(child.model.sql));
|
|
406
|
+
sink.text(" ");
|
|
407
|
+
sink.text(childAlias);
|
|
408
|
+
sink.text(" where ");
|
|
409
|
+
emitJoin(sink, alias, childAlias, relation);
|
|
410
|
+
const inner = operatorValue(value);
|
|
411
|
+
if (name === "every") {
|
|
412
|
+
sink.text(" and not (");
|
|
413
|
+
emitAnd(schema, child, inner ?? {}, sink, childAlias, depth + 1);
|
|
414
|
+
sink.text(")");
|
|
415
|
+
sink.text(")");
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
emitWhere(schema, child, inner, sink, childAlias, depth + 1, true);
|
|
419
|
+
sink.text(")");
|
|
420
|
+
}
|
|
421
|
+
function emitOrder(table, orderBy, sink, alias) {
|
|
422
|
+
if (orderBy === undefined)
|
|
423
|
+
return;
|
|
424
|
+
if (!isRecord(orderBy)) {
|
|
425
|
+
fail("OKM1120", "orderBy must be an object of fields.");
|
|
426
|
+
}
|
|
427
|
+
const keys = Object.keys(orderBy);
|
|
428
|
+
if (keys.length === 0)
|
|
429
|
+
return;
|
|
430
|
+
sink.text(" order by ");
|
|
431
|
+
for (let index = 0;index < keys.length; index += 1) {
|
|
432
|
+
const key = keys[index];
|
|
433
|
+
if (key === undefined)
|
|
434
|
+
continue;
|
|
435
|
+
const column = table.columns.get(key);
|
|
436
|
+
if (column === undefined) {
|
|
437
|
+
throwNamed2("OKM1120", key, table.names, `Field ${key} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
|
|
438
|
+
}
|
|
439
|
+
const parsed = parseOrder(key, orderBy[key]);
|
|
440
|
+
if (index > 0)
|
|
441
|
+
sink.text(", ");
|
|
442
|
+
sink.text(alias);
|
|
443
|
+
sink.text(".");
|
|
444
|
+
sink.text(quote(column.sql));
|
|
445
|
+
sink.text(parsed.dir === "asc" ? " asc" : " desc");
|
|
446
|
+
sink.text(parsed.nulls === "first" ? " nulls first" : " nulls last");
|
|
447
|
+
sink.mark(`o:${key}:${parsed.dir}:${parsed.nulls}`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function emitPrimaryOrder(table, sink, alias) {
|
|
451
|
+
sink.text(" order by ");
|
|
452
|
+
for (let index = 0;index < table.model.primary.length; index += 1) {
|
|
453
|
+
const field = table.model.primary[index];
|
|
454
|
+
if (field === undefined)
|
|
455
|
+
continue;
|
|
456
|
+
const column = table.columns.get(field);
|
|
457
|
+
if (column === undefined)
|
|
458
|
+
continue;
|
|
459
|
+
if (index > 0)
|
|
460
|
+
sink.text(", ");
|
|
461
|
+
sink.text(alias);
|
|
462
|
+
sink.text(".");
|
|
463
|
+
sink.text(quote(column.sql));
|
|
464
|
+
sink.text(" asc");
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function emitLimit(call, sink) {
|
|
468
|
+
if (call.op === "one") {
|
|
469
|
+
const ordered = call.orderBy !== undefined && isRecord(call.orderBy) && Object.keys(call.orderBy).length > 0;
|
|
470
|
+
sink.text(ordered ? " limit 1" : " limit 2");
|
|
471
|
+
sink.mark(ordered ? "one:1" : "one:2");
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (call.op !== "find")
|
|
475
|
+
return;
|
|
476
|
+
if (call.all !== undefined) {
|
|
477
|
+
if (call.all.length === 0) {
|
|
478
|
+
fail("OKM1101", "find() .all needs a reason.");
|
|
479
|
+
}
|
|
480
|
+
sink.mark(`all:${call.all}`);
|
|
481
|
+
if (call.limit !== undefined) {
|
|
482
|
+
const limit = readLimit(call.limit);
|
|
483
|
+
sink.text(" limit ");
|
|
484
|
+
sink.param(String(limit));
|
|
485
|
+
sink.mark(`l:${String(limit)}`);
|
|
486
|
+
}
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (call.limit === undefined) {
|
|
490
|
+
fail("OKM1101", "find() needs a limit, or .all(reason) when the full set is intentional.");
|
|
491
|
+
}
|
|
492
|
+
const limit = readLimit(call.limit);
|
|
493
|
+
sink.text(" limit ");
|
|
494
|
+
sink.param(String(limit));
|
|
495
|
+
sink.mark(`l:${String(limit)}`);
|
|
496
|
+
}
|
|
497
|
+
function selectedColumns(table, select) {
|
|
498
|
+
if (select === undefined) {
|
|
499
|
+
const columns = [];
|
|
500
|
+
for (const column of table.model.columns) {
|
|
501
|
+
if (!column.hidden)
|
|
502
|
+
columns.push(column);
|
|
503
|
+
}
|
|
504
|
+
return columns;
|
|
505
|
+
}
|
|
506
|
+
if (!Array.isArray(select)) {
|
|
507
|
+
fail("OKM1120", "select must be a list of field names.");
|
|
508
|
+
}
|
|
509
|
+
const columns = [];
|
|
510
|
+
for (const name of select) {
|
|
511
|
+
if (typeof name !== "string") {
|
|
512
|
+
fail("OKM1120", "select must be a list of field names.");
|
|
513
|
+
}
|
|
514
|
+
const column = table.columns.get(name);
|
|
515
|
+
if (column === undefined) {
|
|
516
|
+
throwNamed2("OKM1120", name, table.names, `Field ${name} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
|
|
517
|
+
}
|
|
518
|
+
columns.push(column);
|
|
519
|
+
}
|
|
520
|
+
return columns;
|
|
521
|
+
}
|
|
522
|
+
function parseOrder(field, value) {
|
|
523
|
+
if (value === "asc")
|
|
524
|
+
return { dir: "asc", nulls: "last" };
|
|
525
|
+
if (value === "desc")
|
|
526
|
+
return { dir: "desc", nulls: "first" };
|
|
527
|
+
if (isRecord(value)) {
|
|
528
|
+
const dir = value.dir ?? value.direction;
|
|
529
|
+
if (dir !== "asc" && dir !== "desc") {
|
|
530
|
+
fail("OKM1120", `orderBy ${field} must be asc or desc.`);
|
|
531
|
+
}
|
|
532
|
+
const nulls = value.nulls;
|
|
533
|
+
if (nulls === undefined)
|
|
534
|
+
return { dir, nulls: dir === "asc" ? "last" : "first" };
|
|
535
|
+
if (nulls !== "first" && nulls !== "last") {
|
|
536
|
+
fail("OKM1120", `orderBy ${field} nulls must be first or last.`);
|
|
537
|
+
}
|
|
538
|
+
return { dir, nulls };
|
|
539
|
+
}
|
|
540
|
+
fail("OKM1120", `orderBy ${field} must be asc or desc.`);
|
|
541
|
+
}
|
|
542
|
+
function rejectOptions(call) {
|
|
543
|
+
const record = call;
|
|
544
|
+
const accepted = call.op === "find" ? FIND_OPTIONS : call.op === "one" ? ONE_OPTIONS : FILTER_OPTIONS;
|
|
545
|
+
const present = {};
|
|
546
|
+
if (call.where !== undefined)
|
|
547
|
+
present.where = call.where;
|
|
548
|
+
if (call.select !== undefined)
|
|
549
|
+
present.select = call.select;
|
|
550
|
+
if (call.orderBy !== undefined)
|
|
551
|
+
present.orderBy = call.orderBy;
|
|
552
|
+
if (call.limit !== undefined)
|
|
553
|
+
present.limit = call.limit;
|
|
554
|
+
if (call.include !== undefined)
|
|
555
|
+
present.include = call.include;
|
|
556
|
+
rejectKeys(present, accepted, call.op);
|
|
557
|
+
}
|
|
558
|
+
function rejectKeys(value, accepted, where) {
|
|
559
|
+
for (const key of Object.keys(value)) {
|
|
560
|
+
if (accepted.includes(key))
|
|
561
|
+
continue;
|
|
562
|
+
throwNamed2("OKM1120", key, accepted, `Option ${key} is not accepted by ${where}. Accepted options: ${list(accepted)}.`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function readLimit(value) {
|
|
566
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
567
|
+
throw new OkmError2("invalid", "limit must be an integer from 0 up.", { kind: "invalid" });
|
|
568
|
+
}
|
|
569
|
+
return value;
|
|
570
|
+
}
|
|
571
|
+
function requireValue(column, value) {
|
|
572
|
+
if (value === undefined || value === null || typeof value === "object") {
|
|
573
|
+
fail("OKM1121", `Field ${column.field} needs a value. Wrap an object with eq.`);
|
|
574
|
+
}
|
|
575
|
+
return value;
|
|
576
|
+
}
|
|
577
|
+
function projectExpr(sql, dataType) {
|
|
578
|
+
if (dataType === "timestamptz" || dataType === "timestamp")
|
|
579
|
+
return jsonExpr(sql, dataType);
|
|
580
|
+
return sql;
|
|
581
|
+
}
|
|
582
|
+
function jsonExpr(sql, dataType) {
|
|
583
|
+
if (dataType === "timestamptz") {
|
|
584
|
+
return `to_char(${sql} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
|
|
585
|
+
}
|
|
586
|
+
if (dataType === "timestamp") {
|
|
587
|
+
return `to_char(${sql}, 'YYYY-MM-DD"T"HH24:MI:SS.US')`;
|
|
588
|
+
}
|
|
589
|
+
return `${sql}::text`;
|
|
590
|
+
}
|
|
591
|
+
function literalPattern(value, mode) {
|
|
592
|
+
let out = "";
|
|
593
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
594
|
+
const char = value[index] ?? "";
|
|
595
|
+
if (char === "\\" || char === "%" || char === "_")
|
|
596
|
+
out += "\\";
|
|
597
|
+
out += char;
|
|
598
|
+
}
|
|
599
|
+
if (mode === "startsWith")
|
|
600
|
+
return `${out}%`;
|
|
601
|
+
if (mode === "endsWith")
|
|
602
|
+
return `%${out}`;
|
|
603
|
+
return `%${out}%`;
|
|
604
|
+
}
|
|
605
|
+
function decodeRow(outputs, row) {
|
|
606
|
+
const result = {};
|
|
607
|
+
for (const field of outputs.fields) {
|
|
608
|
+
result[field.key] = cell(row[field.at], field.decode);
|
|
609
|
+
}
|
|
610
|
+
return result;
|
|
611
|
+
}
|
|
612
|
+
function cell(value, decode) {
|
|
613
|
+
if (value === null || value === undefined)
|
|
614
|
+
return null;
|
|
615
|
+
return decode === undefined ? value : decode(value);
|
|
616
|
+
}
|
|
617
|
+
function indexes(schema) {
|
|
618
|
+
const cached = indexesOf.get(schema);
|
|
619
|
+
if (cached !== undefined)
|
|
620
|
+
return cached;
|
|
621
|
+
const built = new Map;
|
|
622
|
+
for (const model of Object.values(schema.model)) {
|
|
623
|
+
assertIdentifier2(model.sql, `table ${model.name}`);
|
|
624
|
+
const columns = new Map;
|
|
625
|
+
const names = [];
|
|
626
|
+
for (const column of model.columns) {
|
|
627
|
+
assertIdentifier2(column.sql, `column ${model.name}.${column.field}`);
|
|
628
|
+
columns.set(column.field, column);
|
|
629
|
+
names.push(column.field);
|
|
630
|
+
}
|
|
631
|
+
names.sort();
|
|
632
|
+
const relations = new Map;
|
|
633
|
+
for (const relation of model.relations)
|
|
634
|
+
relations.set(relation.name, relation);
|
|
635
|
+
built.set(model.name, { model, columns, relations, names });
|
|
636
|
+
}
|
|
637
|
+
indexesOf.set(schema, built);
|
|
638
|
+
return built;
|
|
639
|
+
}
|
|
640
|
+
function fingerprint(key) {
|
|
641
|
+
let hash = 0xcbf29ce484222325n;
|
|
642
|
+
const prime = 0x100000001b3n;
|
|
643
|
+
const mask = 0xffffffffffffffffn;
|
|
644
|
+
for (let index = 0;index < key.length; index += 1) {
|
|
645
|
+
hash ^= BigInt(key.charCodeAt(index));
|
|
646
|
+
hash = hash * prime & mask;
|
|
647
|
+
}
|
|
648
|
+
return `Q_${hash.toString(16).padStart(16, "0")}`;
|
|
649
|
+
}
|
|
650
|
+
function quote(name) {
|
|
651
|
+
let out = '"';
|
|
652
|
+
for (let index = 0;index < name.length; index += 1) {
|
|
653
|
+
const char = name[index] ?? "";
|
|
654
|
+
out += char === '"' ? '""' : char;
|
|
655
|
+
}
|
|
656
|
+
return `${out}"`;
|
|
657
|
+
}
|
|
658
|
+
function list(names) {
|
|
659
|
+
return names.length === 0 ? "(none)" : names.join(", ");
|
|
660
|
+
}
|
|
661
|
+
function isRecord(value) {
|
|
662
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !isOperator(value);
|
|
663
|
+
}
|
|
664
|
+
function fail(code, message) {
|
|
665
|
+
const fix = code === "OKM1121" ? { summary: "Use eq for an object value, or has, none, or every for a relation." } : code === "OKM1101" ? { summary: "Pass limit, or call .all(reason)." } : code === "OKM1102" ? { summary: "Pass where, or call .all(reason) to match every row." } : code === "OKM1104" ? { summary: "Set on to the columns of a unique constraint or the primary key." } : code === "OKM1105" ? { summary: "Pass limit on the include, or call .all(reason)." } : code === "OKM1190" ? { summary: "Remove the guarded field. Input cannot set it." } : undefined;
|
|
666
|
+
throw new OkmError2(code, message, fix === undefined ? undefined : { fix });
|
|
667
|
+
}
|
|
668
|
+
function plain(value) {
|
|
669
|
+
if (isOperator(value)) {
|
|
670
|
+
return { op: operatorName(value), value: plain(operatorValue(value)) };
|
|
671
|
+
}
|
|
672
|
+
if (Array.isArray(value))
|
|
673
|
+
return value.map((item) => plain(item));
|
|
674
|
+
if (typeof value === "object" && value !== null) {
|
|
675
|
+
const out = {};
|
|
676
|
+
for (const key of Object.keys(value))
|
|
677
|
+
out[key] = plain(value[key]);
|
|
678
|
+
return out;
|
|
679
|
+
}
|
|
680
|
+
return value;
|
|
681
|
+
}
|
|
682
|
+
function hasMany(include) {
|
|
683
|
+
if (!isRecord(include))
|
|
684
|
+
return false;
|
|
685
|
+
return Object.keys(include).length > 0;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/runtime/client.ts
|
|
689
|
+
var PLAN_LIMIT = 64;
|
|
690
|
+
var SINGLE_ENDPOINT = {
|
|
691
|
+
endpoint: "primary",
|
|
692
|
+
role: "primary",
|
|
693
|
+
reason: "single-endpoint"
|
|
694
|
+
};
|
|
695
|
+
function createClient(schema, pool, options) {
|
|
696
|
+
const connected = checkServer(pool, schema, options.http, options.requireMeta === true, callOptions(options), {
|
|
697
|
+
...options.catalog !== undefined ? { catalog: options.catalog } : {},
|
|
698
|
+
...options.catalogDir !== undefined ? { catalogDir: options.catalogDir } : {}
|
|
699
|
+
});
|
|
700
|
+
const session = {
|
|
701
|
+
schema,
|
|
702
|
+
pool,
|
|
703
|
+
ownsPool: options.ownsPool,
|
|
704
|
+
http: options.http,
|
|
705
|
+
includeValues: options.includeValues === true,
|
|
706
|
+
logger: options.logger,
|
|
707
|
+
connected,
|
|
708
|
+
cache: new Map
|
|
709
|
+
};
|
|
710
|
+
const tables = {};
|
|
711
|
+
for (const name of Object.keys(schema.model)) {
|
|
712
|
+
tables[name] = tableApi(session, name);
|
|
713
|
+
}
|
|
714
|
+
let closing;
|
|
715
|
+
const client = {
|
|
716
|
+
...tables,
|
|
717
|
+
table(name) {
|
|
718
|
+
const found = tables[name];
|
|
719
|
+
if (found !== undefined)
|
|
720
|
+
return found;
|
|
721
|
+
try {
|
|
722
|
+
throwNamed2("OKM1120", name, Object.keys(tables), `Table ${name} is not in the schema. Accepted names: ${Object.keys(tables).join(", ")}.`);
|
|
723
|
+
} catch (error) {
|
|
724
|
+
if (error instanceof OkmError2)
|
|
725
|
+
throw attachHttp(session.http, error);
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
},
|
|
729
|
+
close() {
|
|
730
|
+
if (!options.ownsPool)
|
|
731
|
+
return Promise.resolve();
|
|
732
|
+
closing ??= pool.close();
|
|
733
|
+
return closing;
|
|
734
|
+
},
|
|
735
|
+
connected
|
|
736
|
+
};
|
|
737
|
+
attachAsyncDispose(client, () => client.close());
|
|
738
|
+
return client;
|
|
739
|
+
}
|
|
740
|
+
function attachAsyncDispose(client, close) {
|
|
741
|
+
const symbol = Symbol.asyncDispose;
|
|
742
|
+
if (typeof symbol !== "symbol")
|
|
743
|
+
return;
|
|
744
|
+
Object.defineProperty(client, symbol, {
|
|
745
|
+
configurable: true,
|
|
746
|
+
enumerable: false,
|
|
747
|
+
writable: true,
|
|
748
|
+
value: () => close()
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
function tableApi(session, table) {
|
|
752
|
+
return {
|
|
753
|
+
find(options = {}) {
|
|
754
|
+
return start(session, "find", table, options, {});
|
|
755
|
+
},
|
|
756
|
+
one(options = {}) {
|
|
757
|
+
return start(session, "one", table, options, {});
|
|
758
|
+
},
|
|
759
|
+
count(options = {}) {
|
|
760
|
+
return start(session, "count", table, options, {});
|
|
761
|
+
},
|
|
762
|
+
exists(options = {}) {
|
|
763
|
+
return start(session, "exists", table, options, {});
|
|
764
|
+
},
|
|
765
|
+
insert(data, options = {}) {
|
|
766
|
+
return writeHandle(session, "insert", table, data, options, {});
|
|
767
|
+
},
|
|
768
|
+
update(target, options = {}) {
|
|
769
|
+
return writeHandle(session, "update", table, target, options, {});
|
|
770
|
+
},
|
|
771
|
+
delete(target, options = {}) {
|
|
772
|
+
return writeHandle(session, "delete", table, target, options, {});
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
var writers;
|
|
777
|
+
function loadWrite() {
|
|
778
|
+
writers ??= import("./x5x59srr.js");
|
|
779
|
+
return writers;
|
|
780
|
+
}
|
|
781
|
+
function writeHandle(session, op, table, input, options, mods) {
|
|
782
|
+
let pending;
|
|
783
|
+
const run = () => {
|
|
784
|
+
pending ??= runWrite(session, op, table, input, options, mods);
|
|
785
|
+
return pending;
|
|
786
|
+
};
|
|
787
|
+
const self = {
|
|
788
|
+
then(onFulfilled, onRejected) {
|
|
789
|
+
return run().then(onFulfilled, onRejected);
|
|
790
|
+
},
|
|
791
|
+
catch(onRejected) {
|
|
792
|
+
return self.then(undefined, onRejected);
|
|
793
|
+
},
|
|
794
|
+
finally(fn) {
|
|
795
|
+
return self.then((value) => {
|
|
796
|
+
fn();
|
|
797
|
+
return value;
|
|
798
|
+
}, (error) => {
|
|
799
|
+
fn();
|
|
800
|
+
throw error;
|
|
801
|
+
});
|
|
802
|
+
},
|
|
803
|
+
safe() {
|
|
804
|
+
return safe2(run());
|
|
805
|
+
},
|
|
806
|
+
sql() {
|
|
807
|
+
return loadWrite().then((mod) => mod.explainWrite(session.schema, op, table, input, options, mods));
|
|
808
|
+
},
|
|
809
|
+
expect(count) {
|
|
810
|
+
return writeHandle(session, op, table, input, options, { ...mods, expect: count });
|
|
811
|
+
},
|
|
812
|
+
all(reason) {
|
|
813
|
+
return writeHandle(session, op, table, input, options, { ...mods, all: reason });
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
return self;
|
|
817
|
+
}
|
|
818
|
+
async function runWrite(session, op, table, input, options, mods) {
|
|
819
|
+
await session.connected;
|
|
820
|
+
const mod = await loadWrite();
|
|
821
|
+
try {
|
|
822
|
+
return await mod.executeWrite({ schema: session.schema, pool: session.pool }, op, table, input, options, mods);
|
|
823
|
+
} catch (error) {
|
|
824
|
+
throw logged(session, await mapError(session, error));
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
function start(session, op, table, options, mods) {
|
|
828
|
+
const call = readCall(op, table, options, mods.all);
|
|
829
|
+
let prepared;
|
|
830
|
+
const prepare = async () => {
|
|
831
|
+
if (prepared !== undefined)
|
|
832
|
+
return prepared;
|
|
833
|
+
const hooks = await hooksFor(call);
|
|
834
|
+
const bound = bindCall(session.schema, call, hooks);
|
|
835
|
+
let plan = session.cache.get(bound.key);
|
|
836
|
+
if (plan === undefined) {
|
|
837
|
+
plan = compileCall(session.schema, call, bound.key, hooks);
|
|
838
|
+
if (session.cache.size >= PLAN_LIMIT) {
|
|
839
|
+
const oldest = session.cache.keys().next().value;
|
|
840
|
+
if (oldest !== undefined)
|
|
841
|
+
session.cache.delete(oldest);
|
|
842
|
+
}
|
|
843
|
+
session.cache.set(bound.key, plan);
|
|
844
|
+
}
|
|
845
|
+
prepared = { plan, params: bound.params };
|
|
846
|
+
return prepared;
|
|
847
|
+
};
|
|
848
|
+
const run = async () => {
|
|
849
|
+
await session.connected;
|
|
850
|
+
const { plan, params } = await prepare();
|
|
851
|
+
try {
|
|
852
|
+
const result = await session.pool.execute(plan.text, params, callOptions(mods));
|
|
853
|
+
const value = plan.outputs.includes.length === 0 ? decodeResult(plan, result.rows, table) : (await import("./hadrfb9h.js")).decodeIncluded(plan, result.rows, table);
|
|
854
|
+
if (mods.required === true && value === null) {
|
|
855
|
+
throw new OkmError2("not_found", `one() on ${table} matched no row.`, withHttp(session.http, { kind: "not_found", table }));
|
|
856
|
+
}
|
|
857
|
+
return value;
|
|
858
|
+
} catch (error) {
|
|
859
|
+
throw logged(session, await mapError(session, error));
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
let pending;
|
|
863
|
+
const once = () => {
|
|
864
|
+
pending ??= run();
|
|
865
|
+
return pending;
|
|
866
|
+
};
|
|
867
|
+
const self = {
|
|
868
|
+
then(onFulfilled, onRejected) {
|
|
869
|
+
return once().then(onFulfilled, onRejected);
|
|
870
|
+
},
|
|
871
|
+
catch(onRejected) {
|
|
872
|
+
return this.then(undefined, onRejected);
|
|
873
|
+
},
|
|
874
|
+
finally(fn) {
|
|
875
|
+
return this.then((value) => {
|
|
876
|
+
fn();
|
|
877
|
+
return value;
|
|
878
|
+
}, (error) => {
|
|
879
|
+
fn();
|
|
880
|
+
throw error;
|
|
881
|
+
});
|
|
882
|
+
},
|
|
883
|
+
inspect() {
|
|
884
|
+
if (call.include === undefined && prepared !== undefined) {
|
|
885
|
+
return inspection(prepared.plan, prepared.params, call);
|
|
886
|
+
}
|
|
887
|
+
if (call.include === undefined) {
|
|
888
|
+
const bound = bindNow(session.schema, call);
|
|
889
|
+
prepared = bound;
|
|
890
|
+
return inspection(bound.plan, bound.params, call);
|
|
891
|
+
}
|
|
892
|
+
return prepare().then(({ plan, params }) => inspection(plan, params, call));
|
|
893
|
+
},
|
|
894
|
+
sql() {
|
|
895
|
+
if (call.include === undefined && prepared !== undefined) {
|
|
896
|
+
return { text: prepared.plan.text, params: prepared.params };
|
|
897
|
+
}
|
|
898
|
+
if (call.include === undefined) {
|
|
899
|
+
const { plan, params } = bindNow(session.schema, call);
|
|
900
|
+
prepared = { plan, params };
|
|
901
|
+
return { text: plan.text, params };
|
|
902
|
+
}
|
|
903
|
+
return prepare().then(({ plan, params }) => ({ text: plan.text, params }));
|
|
904
|
+
},
|
|
905
|
+
safe() {
|
|
906
|
+
return safe2(once());
|
|
907
|
+
},
|
|
908
|
+
all(reason) {
|
|
909
|
+
return start(session, op, table, options, { ...mods, all: reason });
|
|
910
|
+
},
|
|
911
|
+
required() {
|
|
912
|
+
return start(session, op, table, options, { ...mods, required: true });
|
|
913
|
+
},
|
|
914
|
+
stream() {
|
|
915
|
+
return streamRows(session, call, prepare);
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
return self;
|
|
919
|
+
}
|
|
920
|
+
async function* streamRows(session, call, prepare) {
|
|
921
|
+
await session.connected;
|
|
922
|
+
if (session.pool.capabilities.stream !== true || session.pool.stream === undefined) {
|
|
923
|
+
throw logged(session, new OkmError2("OKM1111", "stream needs a driver with stream: true. This driver does not have it.", withHttp(session.http, {
|
|
924
|
+
fix: { summary: "Use a driver that sets stream, or read with find." }
|
|
925
|
+
})));
|
|
926
|
+
}
|
|
927
|
+
const { plan, params } = await prepare();
|
|
928
|
+
const decode = plan.outputs.includes.length === 0 ? decodeResult : (await import("./hadrfb9h.js")).decodeIncluded;
|
|
929
|
+
for await (const chunk of session.pool.stream(plan.text, params)) {
|
|
930
|
+
const decoded = decode(plan, chunk, call.table);
|
|
931
|
+
if (!Array.isArray(decoded))
|
|
932
|
+
continue;
|
|
933
|
+
for (const row of decoded)
|
|
934
|
+
yield row;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
function readCall(op, table, options, all) {
|
|
938
|
+
const record = options;
|
|
939
|
+
const accepted = op === "find" ? ["include", "limit", "orderBy", "select", "where"] : op === "one" ? ["include", "orderBy", "select", "where"] : ["where"];
|
|
940
|
+
for (const key of Object.keys(record)) {
|
|
941
|
+
if (!accepted.includes(key)) {
|
|
942
|
+
throwNamed2("OKM1120", key, accepted, `Option ${key} is not accepted by ${op}. Accepted options: ${accepted.join(", ")}.`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return {
|
|
946
|
+
op,
|
|
947
|
+
table,
|
|
948
|
+
...record.where !== undefined ? { where: record.where } : {},
|
|
949
|
+
...record.select !== undefined ? { select: record.select } : {},
|
|
950
|
+
...record.orderBy !== undefined ? { orderBy: record.orderBy } : {},
|
|
951
|
+
...record.limit !== undefined ? { limit: record.limit } : {},
|
|
952
|
+
...record.include !== undefined ? { include: record.include } : {},
|
|
953
|
+
...all !== undefined ? { all } : {}
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
async function checkServer(pool, schema, http, requireMeta, options, source) {
|
|
957
|
+
let rows;
|
|
958
|
+
try {
|
|
959
|
+
const result = await pool.execute(SERVER_CHECK, undefined, options);
|
|
960
|
+
rows = result.rows;
|
|
961
|
+
} catch (error) {
|
|
962
|
+
const { mapPostgresError } = await import("./zhc5vmgf.js");
|
|
963
|
+
throw mapPostgresError(error, mapOptions(http, false));
|
|
964
|
+
}
|
|
965
|
+
const version = rows[0]?.[1];
|
|
966
|
+
if (version === null || version === undefined || !version.startsWith("PostgreSQL")) {
|
|
967
|
+
throw new OkmError2("OKM1801", "The server is not PostgreSQL. The schema dialect is postgres.", withHttp(http, {
|
|
968
|
+
fix: { summary: "Open the schema with the Postgres driver that matches it." }
|
|
969
|
+
}));
|
|
970
|
+
}
|
|
971
|
+
const recorded = rows[0]?.[2];
|
|
972
|
+
if (recorded === null || recorded === undefined || recorded.length === 0) {
|
|
973
|
+
if (requireMeta) {
|
|
974
|
+
throw new OkmError2("OKM1520", "okm_meta has no catalog hash.", withHttp(http, {
|
|
975
|
+
fix: {
|
|
976
|
+
summary: "Apply migrations so okm_meta records the catalog, or omit requireMeta to adopt this database."
|
|
977
|
+
}
|
|
978
|
+
}));
|
|
979
|
+
}
|
|
980
|
+
} else {
|
|
981
|
+
const { assertCompatible } = await import("./qk3hxfja.js");
|
|
982
|
+
try {
|
|
983
|
+
await assertCompatible(pool, schema, recorded, source, options);
|
|
984
|
+
} catch (error) {
|
|
985
|
+
if (error instanceof OkmError2)
|
|
986
|
+
throw attachHttp(http, error);
|
|
987
|
+
throw error;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
const requires = schema.requires?.postgres;
|
|
991
|
+
if (requires === undefined)
|
|
992
|
+
return;
|
|
993
|
+
const major = Math.floor(Number(rows[0]?.[0] ?? "0") / 1e4);
|
|
994
|
+
const match = /^>=(\d+)$/.exec(requires.trim());
|
|
995
|
+
const need = match?.[1] === undefined ? undefined : Number(match[1]);
|
|
996
|
+
if (need === undefined || major < need) {
|
|
997
|
+
throw new OkmError2("OKM1802", `The server is PostgreSQL ${String(major)}. schema({ requires }) asks for ${requires}.`, withHttp(http, {
|
|
998
|
+
fix: { summary: "Upgrade the server, or lower requires to a version the server meets." }
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
async function mapError(session, error) {
|
|
1003
|
+
if (error instanceof OkmError2) {
|
|
1004
|
+
return session.http === undefined ? error : new OkmError2(error.code, error.message, { ...errorFields(error), http: session.http });
|
|
1005
|
+
}
|
|
1006
|
+
const { mapPostgresError } = await import("./zhc5vmgf.js");
|
|
1007
|
+
return mapPostgresError(error, mapOptions(session.http, session.includeValues));
|
|
1008
|
+
}
|
|
1009
|
+
function errorFields(error) {
|
|
1010
|
+
return {
|
|
1011
|
+
kind: error.kind,
|
|
1012
|
+
...error.table !== undefined ? { table: error.table } : {},
|
|
1013
|
+
...error.columns.length > 0 ? { columns: error.columns } : {},
|
|
1014
|
+
fix: error.fix
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
var includeHooks;
|
|
1018
|
+
function hooksFor(call) {
|
|
1019
|
+
if (call.include === undefined)
|
|
1020
|
+
return Promise.resolve(undefined);
|
|
1021
|
+
includeHooks ??= import("./hadrfb9h.js").then((mod) => mod.includeHooks);
|
|
1022
|
+
return includeHooks;
|
|
1023
|
+
}
|
|
1024
|
+
function bindNow(schema, call) {
|
|
1025
|
+
const bound = bindCall(schema, call);
|
|
1026
|
+
const plan = compileCall(schema, call, bound.key);
|
|
1027
|
+
return { plan, params: bound.params };
|
|
1028
|
+
}
|
|
1029
|
+
function inspection(plan, params, call) {
|
|
1030
|
+
return {
|
|
1031
|
+
intent: logicalIntent(call),
|
|
1032
|
+
rules: appliedRules(call),
|
|
1033
|
+
plan: { strategy: "postgres:single-statement", statements: 1, fingerprint: plan.fingerprint },
|
|
1034
|
+
sql: { text: plan.text, params },
|
|
1035
|
+
routing: SINGLE_ENDPOINT
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
function withHttp(http, options) {
|
|
1039
|
+
return http === undefined ? options : { ...options, http };
|
|
1040
|
+
}
|
|
1041
|
+
function attachHttp(http, error) {
|
|
1042
|
+
if (http === undefined)
|
|
1043
|
+
return error;
|
|
1044
|
+
return new OkmError2(error.code, error.message, { ...errorFields(error), http });
|
|
1045
|
+
}
|
|
1046
|
+
function callOptions(input) {
|
|
1047
|
+
if (input.signal === undefined && input.timeout === undefined)
|
|
1048
|
+
return;
|
|
1049
|
+
return {
|
|
1050
|
+
...input.signal !== undefined ? { signal: input.signal } : {},
|
|
1051
|
+
...input.timeout !== undefined ? { timeout: input.timeout } : {}
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
var SERVER_CHECK = "select current_setting('server_version_num'), version(), (select (xpath('//catalog_hash/text()', query_to_xml('select catalog_hash from okm_meta where id = ''head''', true, false, '')))[1]::text where to_regclass('okm_meta') is not null)";
|
|
1055
|
+
function mapOptions(http, includeValues) {
|
|
1056
|
+
return {
|
|
1057
|
+
...http !== undefined ? { http } : {},
|
|
1058
|
+
...includeValues ? { includeValues: true } : {}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
function logged(session, error) {
|
|
1062
|
+
session.logger?.error?.({ code: error.code, summary: error.summary });
|
|
1063
|
+
return error;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
export { decodeResult, emitJoin, emitWhere, emitOrder, emitPrimaryOrder, selectedColumns, rejectKeys, readLimit, projectExpr, jsonExpr, decodeRow, cell, indexes, quote, list, isRecord, fail, createClient };
|