okmodel 0.5.1 → 0.6.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 +29 -14
- package/dist/adapters/pg/no-inflight-timeout.d.ts +17 -0
- package/dist/adapters/pg/postgres-extra.d.ts +22 -0
- package/dist/contracts/catalog/document.d.ts +19 -5
- package/dist/contracts/catalog/object.d.ts +2 -1
- package/dist/contracts/catalog/policy.d.ts +48 -0
- package/dist/contracts/catalog/types.d.ts +25 -1
- package/dist/contracts/catalog/view.d.ts +2 -0
- package/dist/contracts/internal.js +1 -1
- package/dist/contracts/location.d.ts +10 -0
- package/dist/dialects/pg/column.d.ts +13 -2
- package/dist/dialects/pg/ddl.d.ts +37 -1
- package/dist/dialects/pg/emit.d.ts +19 -1
- package/dist/dialects/pg/errors.d.ts +32 -1
- package/dist/dialects/pg/ext/citext.js +1 -1
- package/dist/dialects/pg/ext/index.js +1 -1
- package/dist/dialects/pg/ext/pg-trgm.js +1 -1
- package/dist/dialects/pg/filter-parse.d.ts +5 -4
- package/dist/dialects/pg/fn/index.js +2 -2
- package/dist/dialects/pg/index.d.ts +2 -1
- package/dist/dialects/pg/index.js +23 -17
- package/dist/dialects/pg/introspect.d.ts +7 -0
- package/dist/dialects/pg/model.d.ts +7 -2
- package/dist/dialects/pg/owner.d.ts +42 -0
- package/dist/dialects/pg/path-endpoint.d.ts +20 -0
- package/dist/dialects/pg/role/check.d.ts +3 -0
- package/dist/dialects/pg/role/index.d.ts +71 -6
- package/dist/dialects/pg/role/sql.d.ts +11 -0
- package/dist/dialects/pg/schema.d.ts +2 -0
- package/dist/dialects/pg/table.d.ts +25 -3
- package/dist/dialects/pg/tenancy.d.ts +45 -3
- package/dist/dialects/pg/view/client.d.ts +19 -1
- package/dist/dialects/pg/view/index.d.ts +4 -0
- package/dist/dialects/pg/view/index.js +21 -178
- package/dist/dialects/pg/view/refresh.d.ts +24 -0
- package/dist/runtime/batch.d.ts +1 -1
- package/dist/runtime/conflict.d.ts +2 -2
- package/dist/runtime/drift.d.ts +9 -0
- package/dist/runtime/inflight.d.ts +14 -0
- package/dist/runtime/pg/bun.js +4 -3
- package/dist/runtime/pg/pg.js +3 -3
- package/dist/runtime/pg/pglite.js +4 -3
- package/dist/runtime/pg/postgresjs.js +3 -3
- package/dist/runtime/plan.d.ts +18 -10
- package/dist/runtime/predicate.d.ts +19 -0
- package/dist/runtime/safety/index.js +1 -1
- package/dist/runtime/tenancy/composite.d.ts +26 -0
- package/dist/runtime/tenancy/context.d.ts +16 -0
- package/dist/runtime/tenancy/index.d.ts +9 -5
- package/dist/runtime/tenancy/index.js +1508 -37
- package/dist/runtime/tenancy/path.d.ts +87 -0
- package/dist/runtime/tenancy/rls.d.ts +67 -0
- package/dist/runtime/tenancy/via.d.ts +22 -0
- package/dist/runtime/traits/index.js +17 -11
- package/dist/runtime/types.d.ts +13 -1
- package/dist/runtime/validate/index.js +4 -4
- package/dist/runtime/write.d.ts +1 -1
- package/dist/shared/09p049xa.js +702 -0
- package/dist/shared/{6qd27mx0.js → 1f0xa3cb.js} +5 -5
- package/dist/shared/1nw1f3vs.js +23 -0
- package/dist/shared/{96qcj21c.js → 1z04fa85.js} +19 -9
- package/dist/shared/{qm7cdtvr.js → 2cg3wnxc.js} +296 -60
- package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
- package/dist/shared/{gbtcc2d7.js → 2z7wv5tj.js} +10 -35
- package/dist/shared/{k8v5v5wj.js → 32cnm7kn.js} +6 -1
- package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
- package/dist/shared/{2pqx26h8.js → 3fqwmr09.js} +31 -22
- package/dist/shared/{zqrnhwsq.js → 3qnhc7w5.js} +16 -9
- package/dist/shared/{r2sp8hrc.js → 5d8t84hg.js} +12 -3
- package/dist/shared/{87jvvhnt.js → 5j36z8dn.js} +16 -5
- package/dist/shared/{1g0drakv.js → 5yv4k6a6.js} +1 -1
- package/dist/shared/8dqhth8c.js +12 -0
- package/dist/shared/8etn7d8w.js +62 -0
- package/dist/shared/{v36c5g6p.js → 8x72s86k.js} +400 -13
- package/dist/shared/{nxdp2ze3.js → 929h19xp.js} +107 -5
- package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
- package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
- package/dist/shared/{efmy2gcg.js → ag4c7516.js} +1 -1
- package/dist/shared/b3awxn7h.js +45 -0
- package/dist/shared/b4v57v3x.js +9 -0
- package/dist/shared/bdk4sty6.js +58 -0
- package/dist/shared/{51mvgj8w.js → c58secgj.js} +100 -7
- package/dist/shared/d5azex6s.js +6 -0
- package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
- package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
- package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
- package/dist/shared/{sycnx8gt.js → f0f1n281.js} +18 -8
- package/dist/shared/{f5cw5q5r.js → g90m9b0g.js} +2 -1
- package/dist/shared/{pf0d8f77.js → gwn6v667.js} +1 -1
- package/dist/shared/{8rsy3hxz.js → h15jx9bc.js} +1 -1
- package/dist/shared/{5enne4ma.js → hazqqecq.js} +17 -93
- package/dist/shared/{jwtxdj06.js → j3rkk835.js} +32 -9
- package/dist/shared/{hf22e0wt.js → j60w0dvr.js} +19 -18
- package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
- package/dist/shared/k68eedmy.js +85 -0
- package/dist/shared/{szm0cksa.js → knmes2kh.js} +34 -6
- package/dist/shared/{0t22wwh1.js → mb2ycs1p.js} +4 -2
- package/dist/shared/{cr74e96e.js → n076ydsc.js} +1 -1
- package/dist/shared/{cctw17hj.js → nx9hh281.js} +31 -28
- package/dist/shared/{gde73es6.js → p248q79y.js} +56 -55
- package/dist/shared/{janx09m5.js → p6bjzq9q.js} +11 -7
- package/dist/shared/{6rk2gen5.js → pjr6xrm5.js} +2 -2
- package/dist/shared/{wq9n6pwr.js → pz6rj8ay.js} +82 -9
- package/dist/shared/{caw8b9d8.js → q3j1tq7a.js} +35 -22
- package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
- package/dist/shared/{tm5ab8we.js → r11n8grd.js} +2 -2
- package/dist/shared/r1fdd0f6.js +19 -0
- package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
- package/dist/shared/rhs3xe4k.js +7 -0
- package/dist/shared/{ntneex2a.js → rpycarcr.js} +6 -6
- package/dist/shared/{aj2qxyhj.js → sbdme3vy.js} +14 -2
- package/dist/shared/{qv2ydck9.js → t3g0k88x.js} +638 -67
- package/dist/shared/{ga3sfy93.js → ttmg09vd.js} +535 -200
- package/dist/shared/{kfjp0gck.js → venqrjna.js} +8 -4
- package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
- package/dist/shared/{32zqc1dw.js → vmfkhszw.js} +31 -7
- package/dist/shared/{krxx77g0.js → wkme6tep.js} +10 -8
- package/dist/shared/{ps7a6h14.js → wt5rzv1h.js} +12 -8
- package/dist/shared/wxr8cwns.js +6 -0
- package/dist/shared/{6mnp99hx.js → wynzeggs.js} +16 -12
- package/dist/shared/x3jc53kg.js +220 -0
- package/dist/shared/{sdc7r2ww.js → xq0mvhts.js} +8 -8
- package/dist/shared/xqve9598.js +53 -0
- package/dist/shared/{47x8x9ze.js → y2t3jktd.js} +2 -2
- package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
- package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
- package/dist/shared/{nkaxvt9q.js → ys0xzyaj.js} +1 -1
- package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
- package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
- package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
- package/dist/shared/{r8x5qhd7.js → zm0mafk4.js} +10 -4
- package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
- package/dist/tooling/cli.js +41 -15
- package/dist/tooling/migrate/apply.d.ts +14 -0
- package/dist/tooling/migrate/check.d.ts +3 -4
- package/dist/tooling/migrate/classify.d.ts +4 -0
- package/dist/tooling/migrate/config.d.ts +3 -2
- package/dist/tooling/migrate/files.d.ts +11 -1
- package/dist/tooling/migrate/index.js +14 -14
- package/dist/tooling/migrate/lint.d.ts +1 -1
- package/dist/tooling/migrate/lost.d.ts +28 -0
- package/dist/tooling/migrate/paths.d.ts +14 -0
- package/dist/tooling/migrate/plan.d.ts +6 -3
- package/dist/tooling/migrate/runner.d.ts +25 -0
- package/dist/tooling/migrate/sandbox.d.ts +57 -0
- package/dist/tooling/migrate/sql-lex.d.ts +20 -3
- package/dist/tooling/migrate/text.d.ts +42 -0
- package/dist/tooling/node-version.d.ts +21 -0
- package/dist/tooling/testing/facts.d.ts +33 -5
- package/dist/tooling/testing/index.d.ts +1 -1
- package/dist/tooling/testing/index.js +3 -3
- package/dist/tooling/testing/isolation.d.ts +11 -5
- package/package.json +5 -2
- package/dist/shared/7zwwyh0v.js +0 -448
- package/dist/shared/q58y17hy.js +0 -21
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
assertAuthorDrift2,
|
|
3
3
|
catalogQuery2,
|
|
4
4
|
managedRoleOptions2
|
|
5
|
-
} from "./
|
|
6
|
-
import"./
|
|
7
|
-
import"./
|
|
8
|
-
import"./
|
|
9
|
-
import"./
|
|
5
|
+
} from "./f0f1n281.js";
|
|
6
|
+
import"./vjpnzwjz.js";
|
|
7
|
+
import"./ttmg09vd.js";
|
|
8
|
+
import"./9s0ypp87.js";
|
|
9
|
+
import"./qq3899ht.js";
|
|
10
10
|
export {
|
|
11
11
|
assertAuthorDrift2 as assertAuthorDrift,
|
|
12
12
|
catalogQuery2 as catalogQuery,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureMigrationTables2,
|
|
3
|
+
applyTarget2,
|
|
4
|
+
applyProject2,
|
|
5
|
+
pushProject2,
|
|
6
|
+
backfillTiming2
|
|
7
|
+
} from "./nx9hh281.js";
|
|
8
|
+
import"./p248q79y.js";
|
|
9
|
+
import"./knmes2kh.js";
|
|
10
|
+
import"./vjpnzwjz.js";
|
|
11
|
+
import"./t3g0k88x.js";
|
|
12
|
+
import"./ttmg09vd.js";
|
|
13
|
+
import"./n442h54c.js";
|
|
14
|
+
import"./9s0ypp87.js";
|
|
15
|
+
import"./qq3899ht.js";
|
|
16
|
+
import"./wkme6tep.js";
|
|
17
|
+
export {
|
|
18
|
+
applyProject2 as applyProject,
|
|
19
|
+
applyTarget2 as applyTarget,
|
|
20
|
+
backfillTiming2 as backfillTiming,
|
|
21
|
+
ensureMigrationTables2 as ensureMigrationTables,
|
|
22
|
+
pushProject2 as pushProject
|
|
23
|
+
};
|
|
@@ -17,6 +17,9 @@ import net from "node:net";
|
|
|
17
17
|
import pg from "pg";
|
|
18
18
|
var CANCEL_CODE = 80877102;
|
|
19
19
|
var identity = (value) => value;
|
|
20
|
+
function armDriverError(source, handle = () => {}) {
|
|
21
|
+
source.on("error", handle);
|
|
22
|
+
}
|
|
20
23
|
var WIRE = {
|
|
21
24
|
getTypeParser() {
|
|
22
25
|
return identity;
|
|
@@ -42,6 +45,7 @@ function open4(config) {
|
|
|
42
45
|
...config.ssl !== undefined ? { ssl: config.ssl } : {},
|
|
43
46
|
...config.searchPath !== undefined ? { options: `-c search_path=${config.searchPath}` } : {}
|
|
44
47
|
});
|
|
48
|
+
armDriverError(pool);
|
|
45
49
|
pool.on("connect", (client) => {
|
|
46
50
|
client.on("notice", (notice) => {
|
|
47
51
|
notices.push(notice);
|
|
@@ -53,7 +57,7 @@ function open4(config) {
|
|
|
53
57
|
counters.busy = Math.max(0, counters.busy - 1);
|
|
54
58
|
}, inflight);
|
|
55
59
|
const held = { pool, acquireMs, counters, notices, named, isClosed };
|
|
56
|
-
const checkout = () => closed ? rejectClosed() : import("./
|
|
60
|
+
const checkout = () => closed ? rejectClosed() : import("./zm0mafk4.js").then((mod) => mod.checkout(held));
|
|
57
61
|
return {
|
|
58
62
|
capabilities: NODE_POSTGRES_CAPABILITIES,
|
|
59
63
|
execute(text, params, options) {
|
|
@@ -121,24 +125,30 @@ function open4(config) {
|
|
|
121
125
|
onNotify(message.payload ?? "");
|
|
122
126
|
};
|
|
123
127
|
client.on("notification", onNotification);
|
|
128
|
+
let released = false;
|
|
129
|
+
const release = (dead) => {
|
|
130
|
+
if (released)
|
|
131
|
+
return;
|
|
132
|
+
released = true;
|
|
133
|
+
client.removeListener("notification", onNotification);
|
|
134
|
+
client.removeListener("error", release);
|
|
135
|
+
counters.reserved = Math.max(0, counters.reserved - 1);
|
|
136
|
+
client.release(Boolean(dead));
|
|
137
|
+
};
|
|
138
|
+
armDriverError(client, release);
|
|
124
139
|
try {
|
|
125
140
|
await client.query(`LISTEN ${quoted}`);
|
|
126
141
|
} catch (error) {
|
|
127
|
-
|
|
128
|
-
client.release();
|
|
142
|
+
release(false);
|
|
129
143
|
throw error;
|
|
130
144
|
}
|
|
131
|
-
let stopped = false;
|
|
132
145
|
return async () => {
|
|
133
|
-
if (
|
|
146
|
+
if (released)
|
|
134
147
|
return;
|
|
135
|
-
stopped = true;
|
|
136
148
|
try {
|
|
137
149
|
await client.query(`UNLISTEN ${quoted}`);
|
|
138
150
|
} finally {
|
|
139
|
-
|
|
140
|
-
counters.reserved = Math.max(0, counters.reserved - 1);
|
|
141
|
-
client.release();
|
|
151
|
+
release(false);
|
|
142
152
|
}
|
|
143
153
|
};
|
|
144
154
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
effectivePredicate
|
|
3
|
+
} from "./b3awxn7h.js";
|
|
1
4
|
import {
|
|
2
5
|
assertOperatorFits,
|
|
3
6
|
textArray,
|
|
@@ -16,10 +19,10 @@ import {
|
|
|
16
19
|
} from "./n4jqhpqz.js";
|
|
17
20
|
import {
|
|
18
21
|
writeWouldValidate
|
|
19
|
-
} from "./
|
|
22
|
+
} from "./ze4hxsmy.js";
|
|
20
23
|
import {
|
|
21
24
|
encodeJson
|
|
22
|
-
} from "./
|
|
25
|
+
} from "./9628a9xt.js";
|
|
23
26
|
import {
|
|
24
27
|
withRowFilters,
|
|
25
28
|
archiveRules,
|
|
@@ -32,10 +35,9 @@ import {
|
|
|
32
35
|
indexes,
|
|
33
36
|
quote,
|
|
34
37
|
isRecord,
|
|
35
|
-
effectivePredicate,
|
|
36
38
|
registerFailFix,
|
|
37
39
|
fail
|
|
38
|
-
} from "./
|
|
40
|
+
} from "./2z7wv5tj.js";
|
|
39
41
|
import {
|
|
40
42
|
safetyInstalled,
|
|
41
43
|
runSafety
|
|
@@ -118,7 +120,7 @@ async function prepareWrite(host, op, table, input, options, mods, scope) {
|
|
|
118
120
|
return {
|
|
119
121
|
statements: planned.statements,
|
|
120
122
|
options: callOptions(options),
|
|
121
|
-
...planned.expect !== undefined ? { checked: "expect" } : {},
|
|
123
|
+
...planned.parentGuard === true ? { checked: "parent" } : planned.expect !== undefined ? { checked: "expect" } : {},
|
|
122
124
|
finish: (results) => finish(results, planned, table)
|
|
123
125
|
};
|
|
124
126
|
} catch (error) {
|
|
@@ -151,7 +153,7 @@ async function planWrite(schema, generators, op, tableName, input, options, mods
|
|
|
151
153
|
if (op === "update") {
|
|
152
154
|
return withRowFilters(scope, mods.archive, () => {
|
|
153
155
|
noteArchive(table, mods.archive);
|
|
154
|
-
return planUpdate(schema, table, input, record, mods);
|
|
156
|
+
return planUpdate(schema, table, input, record, mods, scope);
|
|
155
157
|
});
|
|
156
158
|
}
|
|
157
159
|
return withRowFilters(scope, mods.archive, () => {
|
|
@@ -166,12 +168,16 @@ async function planInsert(table, input, options, mods, generators, scope, tenanc
|
|
|
166
168
|
const rows = (many ? input : [input]).map((row) => insertRow(table, row, allow, tenancy));
|
|
167
169
|
tenancy?.stamp(table.model.name, rows, scope);
|
|
168
170
|
fillInsert(table, rows, generators);
|
|
169
|
-
const columns = writtenColumns(table, rows, allow, tenancy
|
|
171
|
+
const columns = writtenColumns(table, rows, allow, tenantKeyList(tenancy));
|
|
170
172
|
const returning = selectedColumns(table, options.returning);
|
|
171
173
|
const outputs = outputsOf(returning);
|
|
172
174
|
const clause = returningClause(returning, undefined);
|
|
173
175
|
const expect = expectOf(options, mods);
|
|
174
176
|
const shape = many ? "rows" : "row";
|
|
177
|
+
const path = tenancy?.isPath?.(table.model.name) === true;
|
|
178
|
+
if (path && options.onConflict !== undefined && options.onConflict !== "error") {
|
|
179
|
+
fail("OKM1120", `onConflict on ${table.model.name} is not available. The insert checks the parent.`);
|
|
180
|
+
}
|
|
175
181
|
if (columns.length === 0) {
|
|
176
182
|
const text = `insert into ${quote(table.model.sql)} default values${clause}`;
|
|
177
183
|
return {
|
|
@@ -183,8 +189,8 @@ async function planInsert(table, input, options, mods, generators, scope, tenanc
|
|
|
183
189
|
keyFields: undefined
|
|
184
190
|
};
|
|
185
191
|
}
|
|
186
|
-
const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./
|
|
187
|
-
const conflict = conflictMod?.readConflict(table, options.onConflict, allow, tenancy
|
|
192
|
+
const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./vmfkhszw.js");
|
|
193
|
+
const conflict = conflictMod?.readConflict(table, options.onConflict, allow, tenantKeyList(tenancy));
|
|
188
194
|
const chunks = chunkRows(rows.map((row) => cellsFor(columns, row)));
|
|
189
195
|
const statements = [];
|
|
190
196
|
const keys = [];
|
|
@@ -192,7 +198,7 @@ async function planInsert(table, input, options, mods, generators, scope, tenanc
|
|
|
192
198
|
for (const chunk of chunks) {
|
|
193
199
|
const slice = rows.slice(offset, offset + chunk.length);
|
|
194
200
|
offset += chunk.length;
|
|
195
|
-
const built = insertHead(table, columns, chunk);
|
|
201
|
+
const built = path ? pathInsert(table, columns, chunk, scope, tenancy) : insertHead(table, columns, chunk);
|
|
196
202
|
if (conflict === undefined || conflictMod === undefined) {
|
|
197
203
|
statements.push(withParams(`${built.text}${clause}`, built.params));
|
|
198
204
|
continue;
|
|
@@ -208,10 +214,11 @@ async function planInsert(table, input, options, mods, generators, scope, tenanc
|
|
|
208
214
|
shape,
|
|
209
215
|
expect,
|
|
210
216
|
keys: conflict?.kind === "return" ? keys : undefined,
|
|
211
|
-
keyFields: conflict?.kind === "return" ? conflict.columns.map((column) => column.field) : undefined
|
|
217
|
+
keyFields: conflict?.kind === "return" ? conflict.columns.map((column) => column.field) : undefined,
|
|
218
|
+
...path ? { parent: rows.length } : {}
|
|
212
219
|
};
|
|
213
220
|
}
|
|
214
|
-
function planUpdate(schema, table, input, options, mods) {
|
|
221
|
+
function planUpdate(schema, table, input, options, mods, scope) {
|
|
215
222
|
const allow = allowSet(table, options.allow);
|
|
216
223
|
noteGuarded(table, allow, "update");
|
|
217
224
|
const returning = returningOption(table, options.returning);
|
|
@@ -221,8 +228,21 @@ function planUpdate(schema, table, input, options, mods) {
|
|
|
221
228
|
const rows = input.map((item) => {
|
|
222
229
|
const row = updateItem(table, item, allow, schema.tenancy);
|
|
223
230
|
requireFilter(table, row.where, mods, "update");
|
|
231
|
+
pathAssignment(schema.tenancy, table.model.name, row.set, scope);
|
|
224
232
|
return { ...row, where: stack(row.where, mods.presets) };
|
|
225
233
|
});
|
|
234
|
+
const moves = rows.some((row) => pathField(schema.tenancy, table.model.name, row.set) !== undefined);
|
|
235
|
+
if (moves) {
|
|
236
|
+
return {
|
|
237
|
+
statements: [pathListUpdate(schema, table, rows, scope, returning)],
|
|
238
|
+
outputs: parentOutputs(returning),
|
|
239
|
+
shape: returning === undefined ? "count" : "rows",
|
|
240
|
+
expect,
|
|
241
|
+
keys: undefined,
|
|
242
|
+
keyFields: undefined,
|
|
243
|
+
parentGuard: true
|
|
244
|
+
};
|
|
245
|
+
}
|
|
226
246
|
return {
|
|
227
247
|
statements: updateList(schema, table, rows, returning),
|
|
228
248
|
outputs,
|
|
@@ -239,12 +259,25 @@ function planUpdate(schema, table, input, options, mods) {
|
|
|
239
259
|
requireFilter(table, input.where, mods, "update");
|
|
240
260
|
if (Object.keys(set).length === 0)
|
|
241
261
|
fail("OKM1120", "update set is empty.");
|
|
262
|
+
const where = stack(input.where, mods.presets);
|
|
263
|
+
if (pathAssignment(schema.tenancy, table.model.name, set, scope) !== undefined) {
|
|
264
|
+
return {
|
|
265
|
+
statements: [pathSetUpdate(schema, table, where, set, scope, returning)],
|
|
266
|
+
outputs: parentOutputs(returning),
|
|
267
|
+
shape: returning === undefined ? "count" : "rows",
|
|
268
|
+
expect,
|
|
269
|
+
keys: undefined,
|
|
270
|
+
keyFields: undefined,
|
|
271
|
+
parentGuard: true
|
|
272
|
+
};
|
|
273
|
+
}
|
|
242
274
|
const sql = new Sql;
|
|
243
275
|
sql.text("update ");
|
|
244
276
|
sql.text(quote(table.model.sql));
|
|
245
277
|
sql.text(" as t set ");
|
|
246
278
|
emitSet(sql, table, set, "t");
|
|
247
|
-
emitFilter(schema, table,
|
|
279
|
+
emitFilter(schema, table, where, sql, "t");
|
|
280
|
+
schema.tenancy?.noteParent?.(table.model.name, set, sql.sink(), scope, (field) => table.columns.get(field)?.sql);
|
|
248
281
|
sql.text(returningClause(returning, "t"));
|
|
249
282
|
return {
|
|
250
283
|
statements: [sql.statement()],
|
|
@@ -277,6 +310,8 @@ function planDelete(schema, table, input, options, mods) {
|
|
|
277
310
|
};
|
|
278
311
|
}
|
|
279
312
|
function finish(results, planned, table) {
|
|
313
|
+
if (planned.parentGuard === true)
|
|
314
|
+
return finishParent(results, planned, table);
|
|
280
315
|
if (planned.shape === "count") {
|
|
281
316
|
let count = 0;
|
|
282
317
|
for (const result of results)
|
|
@@ -291,63 +326,164 @@ function finish(results, planned, table) {
|
|
|
291
326
|
}
|
|
292
327
|
const rows = planned.keys === undefined || planned.keyFields === undefined ? decoded : orderByKeys(decoded, planned.keyFields, planned.keys);
|
|
293
328
|
checkExpect(rows.length, planned.expect, table, "insert");
|
|
329
|
+
if (planned.parent !== undefined && rows.length < planned.parent) {
|
|
330
|
+
throw new OkmError2("OKM1705", `Insert on ${table} did not find a parent in this tenant.`, {
|
|
331
|
+
fix: { summary: "Insert under a parent that belongs to this tenant." }
|
|
332
|
+
});
|
|
333
|
+
}
|
|
294
334
|
if (planned.shape === "row")
|
|
295
335
|
return rows[0] ?? null;
|
|
296
336
|
return rows;
|
|
297
337
|
}
|
|
298
|
-
function
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
ordered.push(row);
|
|
338
|
+
function finishParent(results, planned, table) {
|
|
339
|
+
const row = results[0]?.rows[0];
|
|
340
|
+
const matched = countCell(row?.[0]);
|
|
341
|
+
const updated = countCell(row?.[1]);
|
|
342
|
+
if (updated < matched) {
|
|
343
|
+
throw new OkmError2("OKM1705", `Update on ${table} did not find a parent in this tenant.`, {
|
|
344
|
+
fix: { summary: "Point the foreign key at a parent in this tenant." }
|
|
345
|
+
});
|
|
307
346
|
}
|
|
308
|
-
|
|
347
|
+
if (planned.shape === "count") {
|
|
348
|
+
checkExpect(updated, planned.expect, table, "write");
|
|
349
|
+
return { count: updated };
|
|
350
|
+
}
|
|
351
|
+
const decoded = [];
|
|
352
|
+
if (updated > 0) {
|
|
353
|
+
for (const result of results) {
|
|
354
|
+
for (const item of result.rows)
|
|
355
|
+
decoded.push(decodeRow(planned.outputs, item));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
checkExpect(decoded.length, planned.expect, table, "write");
|
|
359
|
+
if (planned.shape === "row")
|
|
360
|
+
return decoded[0] ?? null;
|
|
361
|
+
return decoded;
|
|
309
362
|
}
|
|
310
|
-
function
|
|
311
|
-
if (typeof value === "
|
|
363
|
+
function countCell(value) {
|
|
364
|
+
if (typeof value === "number")
|
|
312
365
|
return value;
|
|
313
|
-
if (typeof value === "
|
|
314
|
-
return
|
|
315
|
-
|
|
316
|
-
|
|
366
|
+
if (typeof value === "bigint")
|
|
367
|
+
return Number(value);
|
|
368
|
+
if (typeof value === "string" && value.length > 0)
|
|
369
|
+
return Number(value);
|
|
370
|
+
return 0;
|
|
317
371
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
372
|
+
function pathField(tenancy, table, set) {
|
|
373
|
+
if (tenancy?.isPath?.(table) !== true)
|
|
374
|
+
return;
|
|
375
|
+
const field = tenancy.pathOf?.(table)?.[0]?.localField;
|
|
376
|
+
if (field === undefined || !Object.hasOwn(set, field))
|
|
377
|
+
return;
|
|
378
|
+
return field;
|
|
379
|
+
}
|
|
380
|
+
function pathAssignment(tenancy, table, set, scope) {
|
|
381
|
+
const field = pathField(tenancy, table, set);
|
|
382
|
+
if (field === undefined)
|
|
383
|
+
return;
|
|
384
|
+
const value = set[field];
|
|
385
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
386
|
+
throw new OkmError2("OKM1705", `Update on ${table} cannot verify ${field}. Pass the parent id as a string.`, { fix: { summary: "Pass the parent id as a string so the tenant can be checked." } });
|
|
332
387
|
}
|
|
333
|
-
|
|
388
|
+
if (scope === undefined || "unscoped" in scope) {
|
|
389
|
+
throw new OkmError2("OKM1705", `Update on ${table} cannot verify ${field} without a tenant.`, {
|
|
390
|
+
fix: { summary: `Call for({ ... }) so ${field} can be checked.` }
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return field;
|
|
394
|
+
}
|
|
395
|
+
function parentOutputs(returning) {
|
|
396
|
+
const outputs = outputsOf(returning);
|
|
397
|
+
if (returning === undefined)
|
|
398
|
+
return outputs;
|
|
334
399
|
return {
|
|
335
|
-
|
|
336
|
-
|
|
400
|
+
fields: outputs.fields.map((field) => ({ ...field, at: field.at + 2 })),
|
|
401
|
+
includes: outputs.includes
|
|
337
402
|
};
|
|
338
403
|
}
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
404
|
+
function pathSetUpdate(schema, table, where, set, scope, returning) {
|
|
405
|
+
const sql = new Sql;
|
|
406
|
+
sql.text("with matched as (select t.ctid as tid from ");
|
|
407
|
+
sql.text(quote(table.model.sql));
|
|
408
|
+
sql.text(" as t");
|
|
409
|
+
emitFilter(schema, table, where, sql, "t");
|
|
410
|
+
sql.text("), updated as (update ");
|
|
411
|
+
sql.text(quote(table.model.sql));
|
|
412
|
+
sql.text(" as t set ");
|
|
413
|
+
emitSet(sql, table, set, "t");
|
|
414
|
+
sql.text(" where t.ctid in (select tid from matched)");
|
|
415
|
+
schema.tenancy?.noteParent?.(table.model.name, set, sql.sink(), scope, (field) => table.columns.get(field)?.sql);
|
|
416
|
+
endParent(sql, returning);
|
|
417
|
+
return sql.statement();
|
|
418
|
+
}
|
|
419
|
+
function pathListUpdate(schema, table, rows, scope, returning) {
|
|
420
|
+
const sql = new Sql;
|
|
421
|
+
sql.text("with matched as (");
|
|
422
|
+
for (let index = 0;index < rows.length; index += 1) {
|
|
423
|
+
const row = rows[index];
|
|
424
|
+
if (row === undefined)
|
|
425
|
+
continue;
|
|
426
|
+
if (index > 0)
|
|
427
|
+
sql.text(" union all ");
|
|
428
|
+
sql.text("select t.ctid as tid, ");
|
|
429
|
+
sql.text(String(index));
|
|
430
|
+
sql.text(" as ord from ");
|
|
431
|
+
sql.text(quote(table.model.sql));
|
|
432
|
+
sql.text(" as t where ");
|
|
433
|
+
emitBare(schema, table, row.where, sql, "t");
|
|
347
434
|
}
|
|
348
|
-
|
|
435
|
+
sql.text("), rejected as (");
|
|
436
|
+
let rejected = false;
|
|
437
|
+
for (let index = 0;index < rows.length; index += 1) {
|
|
438
|
+
const row = rows[index];
|
|
439
|
+
if (row === undefined)
|
|
440
|
+
continue;
|
|
441
|
+
if (pathField(schema.tenancy, table.model.name, row.set) === undefined)
|
|
442
|
+
continue;
|
|
443
|
+
if (rejected)
|
|
444
|
+
sql.text(" union all ");
|
|
445
|
+
rejected = true;
|
|
446
|
+
sql.text("select ");
|
|
447
|
+
sql.text(String(index));
|
|
448
|
+
sql.text(" as ord where exists (select 1 from matched where ord = ");
|
|
449
|
+
sql.text(String(index));
|
|
450
|
+
sql.text(")");
|
|
451
|
+
noteParentNot(schema, table, row.set, scope, sql);
|
|
452
|
+
}
|
|
453
|
+
if (!rejected)
|
|
454
|
+
sql.text("select 0 as ord where false");
|
|
455
|
+
sql.text("), updated as (");
|
|
456
|
+
updateSliceBody(schema, table, rows, sql, true);
|
|
457
|
+
sql.text(" and not exists (select 1 from rejected)");
|
|
458
|
+
endParent(sql, returning);
|
|
459
|
+
return sql.statement();
|
|
349
460
|
}
|
|
350
|
-
function
|
|
461
|
+
function noteParentNot(schema, table, set, scope, sql) {
|
|
462
|
+
let first = true;
|
|
463
|
+
schema.tenancy?.noteParent?.(table.model.name, set, {
|
|
464
|
+
text(value) {
|
|
465
|
+
if (first) {
|
|
466
|
+
first = false;
|
|
467
|
+
sql.text(" and not ");
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
sql.text(value);
|
|
471
|
+
},
|
|
472
|
+
param(encoded) {
|
|
473
|
+
sql.param(encoded, "");
|
|
474
|
+
},
|
|
475
|
+
mark() {}
|
|
476
|
+
}, scope, (field) => table.columns.get(field)?.sql);
|
|
477
|
+
}
|
|
478
|
+
function endParent(sql, returning) {
|
|
479
|
+
if (returning === undefined || returning.length === 0) {
|
|
480
|
+
sql.text(" returning 1) select (select count(*)::int from matched), (select count(*)::int from updated)");
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
sql.text(returningClause(returning, "t"));
|
|
484
|
+
sql.text(") select c.matched, c.updated, u.* from (select (select count(*)::int from matched) as matched, (select count(*)::int from updated) as updated) c left join updated u on true");
|
|
485
|
+
}
|
|
486
|
+
function updateSliceBody(schema, table, rows, sql, wrapWhere = false) {
|
|
351
487
|
const fields = [];
|
|
352
488
|
const seen = new Set;
|
|
353
489
|
for (const row of rows) {
|
|
@@ -362,7 +498,6 @@ function updateSlice(schema, table, rows, returning) {
|
|
|
362
498
|
}
|
|
363
499
|
if (fields.length === 0)
|
|
364
500
|
fail("OKM1120", "update set is empty.");
|
|
365
|
-
const sql = new Sql;
|
|
366
501
|
sql.text("update ");
|
|
367
502
|
sql.text(quote(table.model.sql));
|
|
368
503
|
sql.text(" as t set ");
|
|
@@ -389,7 +524,7 @@ function updateSlice(schema, table, rows, returning) {
|
|
|
389
524
|
sql.text(" end");
|
|
390
525
|
}
|
|
391
526
|
emitTouch(sql, table, fields.length > 0);
|
|
392
|
-
sql.text(" where ");
|
|
527
|
+
sql.text(wrapWhere ? " where (" : " where ");
|
|
393
528
|
for (let index = 0;index < rows.length; index += 1) {
|
|
394
529
|
const row = rows[index];
|
|
395
530
|
if (row === undefined)
|
|
@@ -400,6 +535,64 @@ function updateSlice(schema, table, rows, returning) {
|
|
|
400
535
|
emitBare(schema, table, row.where, sql, "t");
|
|
401
536
|
sql.text(")");
|
|
402
537
|
}
|
|
538
|
+
if (wrapWhere)
|
|
539
|
+
sql.text(")");
|
|
540
|
+
}
|
|
541
|
+
function orderByKeys(rows, fields, keys) {
|
|
542
|
+
const found = new Map;
|
|
543
|
+
for (const row of rows)
|
|
544
|
+
found.set(fields.map((field) => wireKey(row[field])).join("\x00"), row);
|
|
545
|
+
const ordered = [];
|
|
546
|
+
for (const key of keys) {
|
|
547
|
+
const row = found.get(key.map((value) => value ?? "").join("\x00"));
|
|
548
|
+
if (row !== undefined)
|
|
549
|
+
ordered.push(row);
|
|
550
|
+
}
|
|
551
|
+
return ordered;
|
|
552
|
+
}
|
|
553
|
+
function wireKey(value) {
|
|
554
|
+
if (typeof value === "string")
|
|
555
|
+
return value;
|
|
556
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
557
|
+
return String(value);
|
|
558
|
+
}
|
|
559
|
+
return "";
|
|
560
|
+
}
|
|
561
|
+
function insertHead(table, columns, rows) {
|
|
562
|
+
const params = [];
|
|
563
|
+
const lists = [];
|
|
564
|
+
for (const row of rows) {
|
|
565
|
+
const slots = [];
|
|
566
|
+
for (const cell of row) {
|
|
567
|
+
if (cell.kind === "default") {
|
|
568
|
+
slots.push("default");
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
params.push(cell.kind === "null" ? null : cell.wire);
|
|
572
|
+
slots.push(`$${String(params.length)}::${cell.dataType}`);
|
|
573
|
+
}
|
|
574
|
+
lists.push(`(${slots.join(", ")})`);
|
|
575
|
+
}
|
|
576
|
+
const names = columns.map((column) => quote(column.sql)).join(", ");
|
|
577
|
+
return {
|
|
578
|
+
text: `insert into ${quote(table.model.sql)} (${names}) values ${lists.join(", ")}`,
|
|
579
|
+
params
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function updateList(schema, table, rows, returning) {
|
|
583
|
+
if (rows.length === 0)
|
|
584
|
+
return [];
|
|
585
|
+
const width = Math.max(1, Object.keys(rows[0]?.set ?? {}).length + 2);
|
|
586
|
+
const size = Math.max(1, Math.floor(WRITE_PARAM_BUDGET / (width * 2)));
|
|
587
|
+
const statements = [];
|
|
588
|
+
for (let index = 0;index < rows.length; index += size) {
|
|
589
|
+
statements.push(updateSlice(schema, table, rows.slice(index, index + size), returning));
|
|
590
|
+
}
|
|
591
|
+
return statements;
|
|
592
|
+
}
|
|
593
|
+
function updateSlice(schema, table, rows, returning) {
|
|
594
|
+
const sql = new Sql;
|
|
595
|
+
updateSliceBody(schema, table, rows, sql);
|
|
403
596
|
sql.text(returningClause(returning, "t"));
|
|
404
597
|
return sql.statement();
|
|
405
598
|
}
|
|
@@ -609,7 +802,7 @@ function nextFill(fill, generators) {
|
|
|
609
802
|
return generators.okid();
|
|
610
803
|
return fill.call();
|
|
611
804
|
}
|
|
612
|
-
function writtenColumns(table, rows, allow,
|
|
805
|
+
function writtenColumns(table, rows, allow, tenantKeys) {
|
|
613
806
|
const present = new Set;
|
|
614
807
|
for (const row of rows) {
|
|
615
808
|
for (const key of Object.keys(row))
|
|
@@ -617,12 +810,55 @@ function writtenColumns(table, rows, allow, tenantKey) {
|
|
|
617
810
|
}
|
|
618
811
|
const columns = [];
|
|
619
812
|
for (const column of table.model.columns) {
|
|
620
|
-
if (present.has(column.field) && (writable(table, column, allow) || column.fill !== undefined || column.field ===
|
|
813
|
+
if (present.has(column.field) && (writable(table, column, allow) || column.fill !== undefined || tenantKeys?.includes(column.field) === true)) {
|
|
621
814
|
columns.push(column);
|
|
622
815
|
}
|
|
623
816
|
}
|
|
624
817
|
return columns;
|
|
625
818
|
}
|
|
819
|
+
function tenantKeyList(tenancy) {
|
|
820
|
+
if (tenancy === undefined)
|
|
821
|
+
return;
|
|
822
|
+
if (tenancy.keys !== undefined && tenancy.keys.length > 0)
|
|
823
|
+
return tenancy.keys;
|
|
824
|
+
return [tenancy.key];
|
|
825
|
+
}
|
|
826
|
+
function pathInsert(table, columns, rows, scope, tenancy) {
|
|
827
|
+
const sql = new Sql;
|
|
828
|
+
const names = columns.map((column) => quote(column.sql)).join(", ");
|
|
829
|
+
const selected = columns.map((column) => `v.${quote(column.sql)}`).join(", ");
|
|
830
|
+
sql.text(`insert into ${quote(table.model.sql)} (${names}) select ${selected} from (values `);
|
|
831
|
+
for (let row = 0;row < rows.length; row += 1) {
|
|
832
|
+
const cells = rows[row];
|
|
833
|
+
if (cells === undefined)
|
|
834
|
+
continue;
|
|
835
|
+
if (row > 0)
|
|
836
|
+
sql.text(", ");
|
|
837
|
+
sql.text("(");
|
|
838
|
+
for (let index = 0;index < cells.length; index += 1) {
|
|
839
|
+
const cell = cells[index];
|
|
840
|
+
if (index > 0)
|
|
841
|
+
sql.text(", ");
|
|
842
|
+
if (cell === undefined || cell.kind === "default") {
|
|
843
|
+
sql.text("default");
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
sql.param(cell.kind === "null" ? null : cell.wire, cell.dataType);
|
|
847
|
+
}
|
|
848
|
+
sql.text(")");
|
|
849
|
+
}
|
|
850
|
+
sql.text(`) as v(${names})`);
|
|
851
|
+
tenancy?.lockPredicate?.({
|
|
852
|
+
table: table.model.name,
|
|
853
|
+
fieldSql: (field) => table.columns.get(field)?.sql,
|
|
854
|
+
encode: table.columns.get(tenancy.key)?.encode,
|
|
855
|
+
alias: "v",
|
|
856
|
+
appended: false,
|
|
857
|
+
scope,
|
|
858
|
+
sink: sql.sink()
|
|
859
|
+
});
|
|
860
|
+
return sql.statement();
|
|
861
|
+
}
|
|
626
862
|
function writeSet(table, value, allow, tenancy) {
|
|
627
863
|
if (!isRecord(value))
|
|
628
864
|
fail("OKM1121", "set must be an object of fields.");
|