okmodel 0.5.1 → 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 -1116
- 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 +20 -0
- 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 +41 -3
- package/dist/adapters/pg/reserved.d.ts +42 -0
- package/dist/adapters/pg/session.d.ts +0 -18
- 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/connection.d.ts +7 -6
- 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/floor.d.ts +68 -0
- package/dist/dialects/pg/fn/index.js +2 -2
- package/dist/dialects/pg/index.d.ts +8 -2
- package/dist/dialects/pg/index.js +67 -171
- package/dist/dialects/pg/introspect.d.ts +7 -0
- package/dist/dialects/pg/keys.d.ts +11 -8
- package/dist/dialects/pg/model.d.ts +47 -2
- 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/owner.d.ts +42 -0
- package/dist/dialects/pg/path-endpoint.d.ts +20 -0
- package/dist/dialects/pg/preset.d.ts +1 -1
- package/dist/dialects/pg/relations.d.ts +100 -3
- 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 +33 -3
- 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/aggregate.d.ts +8 -2
- package/dist/runtime/batch.d.ts +1 -1
- package/dist/runtime/bucket-group.d.ts +70 -0
- package/dist/runtime/conflict.d.ts +24 -6
- package/dist/runtime/drift.d.ts +9 -0
- package/dist/runtime/include.d.ts +0 -8
- package/dist/runtime/inflight.d.ts +14 -0
- package/dist/runtime/page.d.ts +4 -1
- package/dist/runtime/pg/bun.js +12 -5
- package/dist/runtime/pg/pg.js +5 -5
- package/dist/runtime/pg/pglite.d.ts +1 -0
- package/dist/runtime/pg/pglite.js +9 -8
- package/dist/runtime/pg/postgresjs.js +5 -5
- package/dist/runtime/plan.d.ts +113 -15
- package/dist/runtime/predicate.d.ts +19 -0
- package/dist/runtime/read-stream.d.ts +13 -16
- package/dist/runtime/result-name.d.ts +17 -0
- package/dist/runtime/safety/index.js +1 -1
- 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/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 +1511 -38
- 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/text-search.d.ts +20 -0
- package/dist/runtime/traits/index.d.ts +80 -0
- package/dist/runtime/traits/index.js +187 -17
- package/dist/runtime/tree.d.ts +38 -0
- package/dist/runtime/types.d.ts +231 -23
- package/dist/runtime/validate/index.js +4 -4
- package/dist/runtime/version.d.ts +39 -0
- package/dist/runtime/window.d.ts +21 -0
- package/dist/runtime/write.d.ts +55 -3
- package/dist/shared/{krxx77g0.js → 14vx95q4.js} +12 -18
- package/dist/shared/1est4ezg.js +313 -0
- package/dist/shared/{4az561fn.js → 1qa0qajz.js} +8 -23
- package/dist/shared/1w508jbr.js +1611 -0
- package/dist/shared/2c13q96h.js +18 -0
- package/dist/shared/2d3xq4ps.js +9 -0
- package/dist/shared/2shdbn8q.js +23 -0
- package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
- package/dist/shared/3v7t1yrv.js +208 -0
- package/dist/shared/5e3tf63j.js +220 -0
- package/dist/shared/5fe3kbsh.js +10 -0
- package/dist/shared/5v3xf80k.js +37 -0
- package/dist/shared/{51mvgj8w.js → 63qtkrca.js} +130 -3
- package/dist/shared/{gbtcc2d7.js → 67txcrcw.js} +62 -66
- package/dist/shared/79dt02vc.js +12 -0
- package/dist/shared/{v36c5g6p.js → 7k9grvf6.js} +407 -14
- package/dist/shared/7kms6fvj.js +20 -0
- package/dist/shared/7mnme5tn.js +8 -0
- package/dist/shared/7n7ddwt4.js +6 -0
- package/dist/shared/{87jvvhnt.js → 81ebdx9p.js} +48 -68
- package/dist/shared/{szm0cksa.js → 8skf0cam.js} +34 -6
- package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
- package/dist/shared/9a4ae4p8.js +31 -0
- package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
- package/dist/shared/9y3mmmh1.js +436 -0
- package/dist/shared/ab9gmbwy.js +213 -0
- package/dist/shared/{sycnx8gt.js → ax7qv55k.js} +18 -8
- package/dist/shared/{f5cw5q5r.js → aytqh5y2.js} +6 -4
- package/dist/shared/{fancm5pt.js → b5pftnn2.js} +138 -15
- package/dist/shared/{efmy2gcg.js → b76b8d95.js} +2 -2
- package/dist/shared/bdk4sty6.js +58 -0
- package/dist/shared/{32zqc1dw.js → bg3rym3g.js} +63 -32
- package/dist/shared/{0t22wwh1.js → bm22fvzr.js} +16 -14
- package/dist/shared/bz84tkzm.js +342 -0
- package/dist/shared/{31vm914h.js → cm3w0ywp.js} +15 -16
- package/dist/shared/cn957f8d.js +179 -0
- package/dist/shared/{pf0d8f77.js → cnkky55j.js} +25 -14
- package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
- package/dist/shared/{nkaxvt9q.js → dkxgzaqd.js} +224 -19
- package/dist/shared/dzj09cye.js +10 -0
- package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
- package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
- package/dist/shared/exn3sd90.js +359 -0
- package/dist/shared/g6evj1j3.js +66 -0
- package/dist/shared/ga9j92qj.js +25 -0
- package/dist/shared/{qv2ydck9.js → gs74dbj5.js} +651 -76
- package/dist/shared/gzaveazc.js +285 -0
- package/dist/shared/{8rsy3hxz.js → h15jx9bc.js} +1 -1
- package/dist/shared/{dyj86cmj.js → hyh73qk6.js} +3 -3
- package/dist/shared/je4axnm8.js +22 -0
- package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
- package/dist/shared/{6qd27mx0.js → jvd0mqag.js} +5 -5
- package/dist/shared/jznfax0y.js +155 -0
- package/dist/shared/{cgbamx3e.js → kb2sdhfb.js} +7 -4
- package/dist/shared/{ps7a6h14.js → kr0jzbw2.js} +12 -8
- package/dist/shared/{janx09m5.js → ktz42yez.js} +20 -8
- package/dist/shared/{gde73es6.js → m6p12kbr.js} +56 -55
- package/dist/shared/{r6nwkxnt.js → m7n9h2a5.js} +4 -4
- package/dist/shared/{672t5vym.js → mss6ctgv.js} +2 -2
- package/dist/shared/{nxdp2ze3.js → mtaszv5a.js} +144 -12
- package/dist/shared/n816peh1.js +45 -0
- package/dist/shared/nbw1z4yd.js +18 -0
- package/dist/shared/{cctw17hj.js → p6qc5gcp.js} +38 -34
- package/dist/shared/{6rk2gen5.js → pbp1ss85.js} +20 -17
- package/dist/shared/pt94kz7f.js +6 -0
- package/dist/shared/{wq9n6pwr.js → pz6rj8ay.js} +82 -9
- package/dist/shared/q5c6ngn3.js +13 -0
- package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
- 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/{sdc7r2ww.js → rs73fca9.js} +8 -8
- package/dist/shared/{tm5ab8we.js → sfz1ksn6.js} +7 -4
- package/dist/shared/{6mnp99hx.js → svncdeh5.js} +16 -12
- package/dist/shared/{hf22e0wt.js → tbrvgw6z.js} +62 -42
- package/dist/shared/te5as7z7.js +622 -0
- package/dist/shared/{ga3sfy93.js → tsvww22q.js} +535 -200
- package/dist/shared/{47x8x9ze.js → ttsqgp4p.js} +20 -13
- package/dist/shared/{x72jb7ad.js → v8g1vgv8.js} +1 -1
- package/dist/shared/vb6qfyve.js +152 -0
- package/dist/shared/{r2sp8hrc.js → vczs8gd4.js} +22 -13
- package/dist/shared/{1g0drakv.js → vqrjpcqn.js} +145 -3
- package/dist/shared/{2pqx26h8.js → w72kgg4q.js} +31 -22
- package/dist/shared/{96qcj21c.js → w79hsckn.js} +16 -51
- package/dist/shared/wa07eas9.js +730 -0
- package/dist/shared/{k8v5v5wj.js → x933k9a3.js} +75 -34
- package/dist/shared/{cr74e96e.js → xhfsfkgk.js} +62 -28
- package/dist/shared/xjk7hnn5.js +272 -0
- package/dist/shared/{5enne4ma.js → xnxsed62.js} +66 -113
- package/dist/shared/xqve9598.js +53 -0
- package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
- package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
- package/dist/shared/y6njgyda.js +249 -0
- package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
- package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
- package/dist/shared/z7s6fpxv.js +62 -0
- package/dist/shared/{n7mcc1gk.js → zchaghkf.js} +4 -3
- package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
- 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/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/engine.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 +8 -3
- package/dist/shared/7m88x3e9.js +0 -45
- package/dist/shared/7zwwyh0v.js +0 -448
- package/dist/shared/aj2qxyhj.js +0 -90
- package/dist/shared/caw8b9d8.js +0 -204
- package/dist/shared/jwtxdj06.js +0 -76
- package/dist/shared/kfjp0gck.js +0 -212
- package/dist/shared/q58y17hy.js +0 -21
- package/dist/shared/qm7cdtvr.js +0 -912
- package/dist/shared/r8x5qhd7.js +0 -86
- package/dist/shared/zqrnhwsq.js +0 -31
package/dist/shared/qm7cdtvr.js
DELETED
|
@@ -1,912 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
assertOperatorFits,
|
|
3
|
-
textArray,
|
|
4
|
-
arrayElementType
|
|
5
|
-
} from "./n7mcc1gk.js";
|
|
6
|
-
import {
|
|
7
|
-
stack
|
|
8
|
-
} from "./672t5vym.js";
|
|
9
|
-
import {
|
|
10
|
-
touchFields,
|
|
11
|
-
fieldSealed,
|
|
12
|
-
sealingTrait
|
|
13
|
-
} from "./7m88x3e9.js";
|
|
14
|
-
import {
|
|
15
|
-
runWrite
|
|
16
|
-
} from "./n4jqhpqz.js";
|
|
17
|
-
import {
|
|
18
|
-
writeWouldValidate
|
|
19
|
-
} from "./9m80mkac.js";
|
|
20
|
-
import {
|
|
21
|
-
encodeJson
|
|
22
|
-
} from "./gjthds9j.js";
|
|
23
|
-
import {
|
|
24
|
-
withRowFilters,
|
|
25
|
-
archiveRules,
|
|
26
|
-
emitWhere,
|
|
27
|
-
selectedColumns,
|
|
28
|
-
rejectKeys,
|
|
29
|
-
takeObject,
|
|
30
|
-
projectExpr,
|
|
31
|
-
decodeRow,
|
|
32
|
-
indexes,
|
|
33
|
-
quote,
|
|
34
|
-
isRecord,
|
|
35
|
-
effectivePredicate,
|
|
36
|
-
registerFailFix,
|
|
37
|
-
fail
|
|
38
|
-
} from "./gbtcc2d7.js";
|
|
39
|
-
import {
|
|
40
|
-
safetyInstalled,
|
|
41
|
-
runSafety
|
|
42
|
-
} from "./a5sxz52y.js";
|
|
43
|
-
import {
|
|
44
|
-
isOperator,
|
|
45
|
-
operatorName,
|
|
46
|
-
operatorValue
|
|
47
|
-
} from "./cn0n68ft.js";
|
|
48
|
-
import {
|
|
49
|
-
OkmError2,
|
|
50
|
-
throwNamed2
|
|
51
|
-
} from "./52szzakm.js";
|
|
52
|
-
|
|
53
|
-
// src/runtime/write.ts
|
|
54
|
-
registerFailFix("OKM1102", "Pass where, or call .all(reason) to match every row.");
|
|
55
|
-
registerFailFix("OKM1190", "Remove the guarded field. Input cannot set it.");
|
|
56
|
-
var WRITE_PARAM_BUDGET = 2048;
|
|
57
|
-
var REDACTED = "[redacted]";
|
|
58
|
-
function encodeColumn(column, value) {
|
|
59
|
-
if (!column.hidden && !column.sensitive)
|
|
60
|
-
return column.encode(value);
|
|
61
|
-
try {
|
|
62
|
-
return column.encode(value);
|
|
63
|
-
} catch (error) {
|
|
64
|
-
if (error instanceof OkmError2) {
|
|
65
|
-
throw new OkmError2(error.code, `Field ${column.field} was rejected.`, {
|
|
66
|
-
kind: error.kind,
|
|
67
|
-
fix: error.fix
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async function requireValidationImport(schema, op, tableName, options) {
|
|
74
|
-
if (op === "delete" || validationRegistered(schema))
|
|
75
|
-
return;
|
|
76
|
-
const stored = schema;
|
|
77
|
-
const source = stored.tables?.find((item) => item.name === tableName);
|
|
78
|
-
if (source === undefined || !writeWouldValidate(stored.validation, source, options))
|
|
79
|
-
return;
|
|
80
|
-
const { refuseMissingValidation } = await import("./83eaefwr.js");
|
|
81
|
-
refuseMissingValidation();
|
|
82
|
-
}
|
|
83
|
-
function validationRegistered(schema) {
|
|
84
|
-
const hooks = schema.hooks;
|
|
85
|
-
if (hooks === undefined)
|
|
86
|
-
return false;
|
|
87
|
-
for (const hook of hooks) {
|
|
88
|
-
if (hook["~v"] === 1)
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
var INSERT_OPTIONS = [
|
|
94
|
-
"allow",
|
|
95
|
-
"expect",
|
|
96
|
-
"onConflict",
|
|
97
|
-
"returning",
|
|
98
|
-
"signal",
|
|
99
|
-
"timeout",
|
|
100
|
-
"validate"
|
|
101
|
-
];
|
|
102
|
-
var FILTER_OPTIONS = ["allow", "expect", "returning", "signal", "timeout", "validate"];
|
|
103
|
-
var UPDATE_KEYS = ["set", "where"];
|
|
104
|
-
var UPDATE_ROW_KEYS = ["id", "set", "where"];
|
|
105
|
-
var NO_ALLOW = new Set;
|
|
106
|
-
async function executeWrite(host, op, table, input, options, mods, scope, plan) {
|
|
107
|
-
const prepared = await prepareWrite(host, op, table, input, options, mods, scope);
|
|
108
|
-
if (plan !== undefined)
|
|
109
|
-
return prepared;
|
|
110
|
-
if (prepared.statements.length === 0)
|
|
111
|
-
return prepared.finish([]);
|
|
112
|
-
const results = await runWrite(host, prepared.statements, prepared.options);
|
|
113
|
-
return prepared.finish(results);
|
|
114
|
-
}
|
|
115
|
-
async function prepareWrite(host, op, table, input, options, mods, scope) {
|
|
116
|
-
try {
|
|
117
|
-
const planned = await planWrite(host.schema, host.generators, op, table, input, options, mods, scope);
|
|
118
|
-
return {
|
|
119
|
-
statements: planned.statements,
|
|
120
|
-
options: callOptions(options),
|
|
121
|
-
...planned.expect !== undefined ? { checked: "expect" } : {},
|
|
122
|
-
finish: (results) => finish(results, planned, table)
|
|
123
|
-
};
|
|
124
|
-
} catch (error) {
|
|
125
|
-
throw scrubWrite(host.schema, table, input, error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function explainWrite(schema, op, table, input, options, mods, generators, scope) {
|
|
129
|
-
try {
|
|
130
|
-
const planned = await planWrite(schema, generators, op, table, input, options, mods, scope);
|
|
131
|
-
return { statements: planned.statements };
|
|
132
|
-
} catch (error) {
|
|
133
|
-
throw scrubWrite(schema, table, input, error);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async function planWrite(schema, generators, op, tableName, input, options, mods, scope) {
|
|
137
|
-
await requireValidationImport(schema, op, tableName, options);
|
|
138
|
-
const table = indexes(schema).get(tableName);
|
|
139
|
-
if (table === undefined) {
|
|
140
|
-
fail("OKM1120", `Table ${tableName} is not in the schema.`);
|
|
141
|
-
}
|
|
142
|
-
if (mods.uses !== undefined && op !== "insert") {
|
|
143
|
-
const { resolve } = await import("./r6nwkxnt.js");
|
|
144
|
-
mods = { ...mods, presets: resolve(schema, tableName, mods.uses).wheres };
|
|
145
|
-
}
|
|
146
|
-
const record = isRecord(options) ? options : {};
|
|
147
|
-
rejectKeys(record, op === "insert" ? INSERT_OPTIONS : FILTER_OPTIONS, op);
|
|
148
|
-
if (op === "insert") {
|
|
149
|
-
return planInsert(table, input, record, mods, generators, scope, schema.tenancy);
|
|
150
|
-
}
|
|
151
|
-
if (op === "update") {
|
|
152
|
-
return withRowFilters(scope, mods.archive, () => {
|
|
153
|
-
noteArchive(table, mods.archive);
|
|
154
|
-
return planUpdate(schema, table, input, record, mods);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
return withRowFilters(scope, mods.archive, () => {
|
|
158
|
-
noteArchive(table, mods.archive);
|
|
159
|
-
return planDelete(schema, table, input, record, mods);
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
async function planInsert(table, input, options, mods, generators, scope, tenancy) {
|
|
163
|
-
const allow = allowSet(table, options.allow);
|
|
164
|
-
noteGuarded(table, allow, "insert");
|
|
165
|
-
const many = Array.isArray(input);
|
|
166
|
-
const rows = (many ? input : [input]).map((row) => insertRow(table, row, allow, tenancy));
|
|
167
|
-
tenancy?.stamp(table.model.name, rows, scope);
|
|
168
|
-
fillInsert(table, rows, generators);
|
|
169
|
-
const columns = writtenColumns(table, rows, allow, tenancy?.key);
|
|
170
|
-
const returning = selectedColumns(table, options.returning);
|
|
171
|
-
const outputs = outputsOf(returning);
|
|
172
|
-
const clause = returningClause(returning, undefined);
|
|
173
|
-
const expect = expectOf(options, mods);
|
|
174
|
-
const shape = many ? "rows" : "row";
|
|
175
|
-
if (columns.length === 0) {
|
|
176
|
-
const text = `insert into ${quote(table.model.sql)} default values${clause}`;
|
|
177
|
-
return {
|
|
178
|
-
statements: rows.map(() => ({ text })),
|
|
179
|
-
outputs,
|
|
180
|
-
shape,
|
|
181
|
-
expect,
|
|
182
|
-
keys: undefined,
|
|
183
|
-
keyFields: undefined
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./32zqc1dw.js");
|
|
187
|
-
const conflict = conflictMod?.readConflict(table, options.onConflict, allow, tenancy?.key);
|
|
188
|
-
const chunks = chunkRows(rows.map((row) => cellsFor(columns, row)));
|
|
189
|
-
const statements = [];
|
|
190
|
-
const keys = [];
|
|
191
|
-
let offset = 0;
|
|
192
|
-
for (const chunk of chunks) {
|
|
193
|
-
const slice = rows.slice(offset, offset + chunk.length);
|
|
194
|
-
offset += chunk.length;
|
|
195
|
-
const built = insertHead(table, columns, chunk);
|
|
196
|
-
if (conflict === undefined || conflictMod === undefined) {
|
|
197
|
-
statements.push(withParams(`${built.text}${clause}`, built.params));
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
const keyRows = conflict.kind === "return" ? slice.map((row) => keyWire(conflict.columns, row)) : [];
|
|
201
|
-
for (const row of keyRows)
|
|
202
|
-
keys.push([...row]);
|
|
203
|
-
statements.push(conflictMod.finishInsert(built.text, clause, built.params ?? [], conflict, table, returning, keyRows));
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
statements,
|
|
207
|
-
outputs,
|
|
208
|
-
shape,
|
|
209
|
-
expect,
|
|
210
|
-
keys: conflict?.kind === "return" ? keys : undefined,
|
|
211
|
-
keyFields: conflict?.kind === "return" ? conflict.columns.map((column) => column.field) : undefined
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
function planUpdate(schema, table, input, options, mods) {
|
|
215
|
-
const allow = allowSet(table, options.allow);
|
|
216
|
-
noteGuarded(table, allow, "update");
|
|
217
|
-
const returning = returningOption(table, options.returning);
|
|
218
|
-
const outputs = outputsOf(returning);
|
|
219
|
-
const expect = expectOf(options, mods);
|
|
220
|
-
if (Array.isArray(input)) {
|
|
221
|
-
const rows = input.map((item) => {
|
|
222
|
-
const row = updateItem(table, item, allow, schema.tenancy);
|
|
223
|
-
requireFilter(table, row.where, mods, "update");
|
|
224
|
-
return { ...row, where: stack(row.where, mods.presets) };
|
|
225
|
-
});
|
|
226
|
-
return {
|
|
227
|
-
statements: updateList(schema, table, rows, returning),
|
|
228
|
-
outputs,
|
|
229
|
-
shape: returning === undefined ? "count" : "rows",
|
|
230
|
-
expect,
|
|
231
|
-
keys: undefined,
|
|
232
|
-
keyFields: undefined
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
if (!isRecord(input))
|
|
236
|
-
fail("OKM1121", "update expects { where, set } or a list of rows.");
|
|
237
|
-
rejectUpdateKeys(input, false);
|
|
238
|
-
const set = writeSet(table, input.set, allow, schema.tenancy);
|
|
239
|
-
requireFilter(table, input.where, mods, "update");
|
|
240
|
-
if (Object.keys(set).length === 0)
|
|
241
|
-
fail("OKM1120", "update set is empty.");
|
|
242
|
-
const sql = new Sql;
|
|
243
|
-
sql.text("update ");
|
|
244
|
-
sql.text(quote(table.model.sql));
|
|
245
|
-
sql.text(" as t set ");
|
|
246
|
-
emitSet(sql, table, set, "t");
|
|
247
|
-
emitFilter(schema, table, stack(input.where, mods.presets), sql, "t");
|
|
248
|
-
sql.text(returningClause(returning, "t"));
|
|
249
|
-
return {
|
|
250
|
-
statements: [sql.statement()],
|
|
251
|
-
outputs,
|
|
252
|
-
shape: returning === undefined ? "count" : "rows",
|
|
253
|
-
expect,
|
|
254
|
-
keys: undefined,
|
|
255
|
-
keyFields: undefined
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
function planDelete(schema, table, input, options, mods) {
|
|
259
|
-
if (!isRecord(input))
|
|
260
|
-
fail("OKM1121", "delete expects { where }.");
|
|
261
|
-
rejectKeys(input, ["where"], "delete");
|
|
262
|
-
requireFilter(table, input.where, mods, "delete");
|
|
263
|
-
const returning = returningOption(table, options.returning);
|
|
264
|
-
const sql = new Sql;
|
|
265
|
-
sql.text("delete from ");
|
|
266
|
-
sql.text(quote(table.model.sql));
|
|
267
|
-
sql.text(" as t");
|
|
268
|
-
emitFilter(schema, table, stack(input.where, mods.presets), sql, "t");
|
|
269
|
-
sql.text(returningClause(returning, "t"));
|
|
270
|
-
return {
|
|
271
|
-
statements: [sql.statement()],
|
|
272
|
-
outputs: outputsOf(returning),
|
|
273
|
-
shape: returning === undefined ? "count" : "rows",
|
|
274
|
-
expect: expectOf(options, mods),
|
|
275
|
-
keys: undefined,
|
|
276
|
-
keyFields: undefined
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
function finish(results, planned, table) {
|
|
280
|
-
if (planned.shape === "count") {
|
|
281
|
-
let count = 0;
|
|
282
|
-
for (const result of results)
|
|
283
|
-
count += result.count;
|
|
284
|
-
checkExpect(count, planned.expect, table, "write");
|
|
285
|
-
return { count };
|
|
286
|
-
}
|
|
287
|
-
const decoded = [];
|
|
288
|
-
for (const result of results) {
|
|
289
|
-
for (const row of result.rows)
|
|
290
|
-
decoded.push(decodeRow(planned.outputs, row));
|
|
291
|
-
}
|
|
292
|
-
const rows = planned.keys === undefined || planned.keyFields === undefined ? decoded : orderByKeys(decoded, planned.keyFields, planned.keys);
|
|
293
|
-
checkExpect(rows.length, planned.expect, table, "insert");
|
|
294
|
-
if (planned.shape === "row")
|
|
295
|
-
return rows[0] ?? null;
|
|
296
|
-
return rows;
|
|
297
|
-
}
|
|
298
|
-
function orderByKeys(rows, fields, keys) {
|
|
299
|
-
const found = new Map;
|
|
300
|
-
for (const row of rows)
|
|
301
|
-
found.set(fields.map((field) => wireKey(row[field])).join("\x00"), row);
|
|
302
|
-
const ordered = [];
|
|
303
|
-
for (const key of keys) {
|
|
304
|
-
const row = found.get(key.map((value) => value ?? "").join("\x00"));
|
|
305
|
-
if (row !== undefined)
|
|
306
|
-
ordered.push(row);
|
|
307
|
-
}
|
|
308
|
-
return ordered;
|
|
309
|
-
}
|
|
310
|
-
function wireKey(value) {
|
|
311
|
-
if (typeof value === "string")
|
|
312
|
-
return value;
|
|
313
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
314
|
-
return String(value);
|
|
315
|
-
}
|
|
316
|
-
return "";
|
|
317
|
-
}
|
|
318
|
-
function insertHead(table, columns, rows) {
|
|
319
|
-
const params = [];
|
|
320
|
-
const lists = [];
|
|
321
|
-
for (const row of rows) {
|
|
322
|
-
const slots = [];
|
|
323
|
-
for (const cell of row) {
|
|
324
|
-
if (cell.kind === "default") {
|
|
325
|
-
slots.push("default");
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
params.push(cell.kind === "null" ? null : cell.wire);
|
|
329
|
-
slots.push(`$${String(params.length)}::${cell.dataType}`);
|
|
330
|
-
}
|
|
331
|
-
lists.push(`(${slots.join(", ")})`);
|
|
332
|
-
}
|
|
333
|
-
const names = columns.map((column) => quote(column.sql)).join(", ");
|
|
334
|
-
return {
|
|
335
|
-
text: `insert into ${quote(table.model.sql)} (${names}) values ${lists.join(", ")}`,
|
|
336
|
-
params
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
function updateList(schema, table, rows, returning) {
|
|
340
|
-
if (rows.length === 0)
|
|
341
|
-
return [];
|
|
342
|
-
const width = Math.max(1, Object.keys(rows[0]?.set ?? {}).length + 2);
|
|
343
|
-
const size = Math.max(1, Math.floor(WRITE_PARAM_BUDGET / (width * 2)));
|
|
344
|
-
const statements = [];
|
|
345
|
-
for (let index = 0;index < rows.length; index += size) {
|
|
346
|
-
statements.push(updateSlice(schema, table, rows.slice(index, index + size), returning));
|
|
347
|
-
}
|
|
348
|
-
return statements;
|
|
349
|
-
}
|
|
350
|
-
function updateSlice(schema, table, rows, returning) {
|
|
351
|
-
const fields = [];
|
|
352
|
-
const seen = new Set;
|
|
353
|
-
for (const row of rows) {
|
|
354
|
-
for (const key of Object.keys(row.set)) {
|
|
355
|
-
if (seen.has(key))
|
|
356
|
-
continue;
|
|
357
|
-
seen.add(key);
|
|
358
|
-
const column = table.columns.get(key);
|
|
359
|
-
if (column !== undefined)
|
|
360
|
-
fields.push(column);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
if (fields.length === 0)
|
|
364
|
-
fail("OKM1120", "update set is empty.");
|
|
365
|
-
const sql = new Sql;
|
|
366
|
-
sql.text("update ");
|
|
367
|
-
sql.text(quote(table.model.sql));
|
|
368
|
-
sql.text(" as t set ");
|
|
369
|
-
for (let field = 0;field < fields.length; field += 1) {
|
|
370
|
-
const column = fields[field];
|
|
371
|
-
if (column === undefined)
|
|
372
|
-
continue;
|
|
373
|
-
if (field > 0)
|
|
374
|
-
sql.text(", ");
|
|
375
|
-
sql.text(quote(column.sql));
|
|
376
|
-
sql.text(" = case");
|
|
377
|
-
for (const row of rows) {
|
|
378
|
-
sql.text(" when ");
|
|
379
|
-
emitBare(schema, table, row.where, sql, "t");
|
|
380
|
-
sql.text(" then ");
|
|
381
|
-
const value = row.set[column.field];
|
|
382
|
-
if (value === undefined)
|
|
383
|
-
sql.text(`t.${quote(column.sql)}`);
|
|
384
|
-
else
|
|
385
|
-
emitValue(sql, column, value, "t");
|
|
386
|
-
}
|
|
387
|
-
sql.text(" else t.");
|
|
388
|
-
sql.text(quote(column.sql));
|
|
389
|
-
sql.text(" end");
|
|
390
|
-
}
|
|
391
|
-
emitTouch(sql, table, fields.length > 0);
|
|
392
|
-
sql.text(" where ");
|
|
393
|
-
for (let index = 0;index < rows.length; index += 1) {
|
|
394
|
-
const row = rows[index];
|
|
395
|
-
if (row === undefined)
|
|
396
|
-
continue;
|
|
397
|
-
if (index > 0)
|
|
398
|
-
sql.text(" or ");
|
|
399
|
-
sql.text("(");
|
|
400
|
-
emitBare(schema, table, row.where, sql, "t");
|
|
401
|
-
sql.text(")");
|
|
402
|
-
}
|
|
403
|
-
sql.text(returningClause(returning, "t"));
|
|
404
|
-
return sql.statement();
|
|
405
|
-
}
|
|
406
|
-
function emitSet(sql, table, set, alias) {
|
|
407
|
-
const keys = Object.keys(set);
|
|
408
|
-
let wrote = false;
|
|
409
|
-
for (const key of keys) {
|
|
410
|
-
const column = table.columns.get(key);
|
|
411
|
-
if (column === undefined)
|
|
412
|
-
continue;
|
|
413
|
-
if (wrote)
|
|
414
|
-
sql.text(", ");
|
|
415
|
-
wrote = true;
|
|
416
|
-
sql.text(quote(column.sql));
|
|
417
|
-
sql.text(" = ");
|
|
418
|
-
emitValue(sql, column, set[key], alias);
|
|
419
|
-
}
|
|
420
|
-
wrote = emitTouch(sql, table, wrote);
|
|
421
|
-
}
|
|
422
|
-
function emitTouch(sql, table, wrote) {
|
|
423
|
-
const touch = touchFields(table.model);
|
|
424
|
-
if (touch === undefined)
|
|
425
|
-
return wrote;
|
|
426
|
-
for (const field of touch) {
|
|
427
|
-
const column = table.columns.get(field);
|
|
428
|
-
if (column === undefined)
|
|
429
|
-
continue;
|
|
430
|
-
if (wrote)
|
|
431
|
-
sql.text(", ");
|
|
432
|
-
wrote = true;
|
|
433
|
-
sql.text(quote(column.sql));
|
|
434
|
-
sql.text(" = now()");
|
|
435
|
-
}
|
|
436
|
-
return wrote;
|
|
437
|
-
}
|
|
438
|
-
function emitValue(sql, column, value, alias) {
|
|
439
|
-
if (isOperator(value)) {
|
|
440
|
-
const name = operatorName(value);
|
|
441
|
-
if (name === "inc") {
|
|
442
|
-
const amount = operatorValue(value);
|
|
443
|
-
if (typeof amount === "object" && amount !== null) {
|
|
444
|
-
fail("OKM1121", `inc on ${column.field} needs a value.`);
|
|
445
|
-
}
|
|
446
|
-
sql.text(`${alias}.${quote(column.sql)} + `);
|
|
447
|
-
sql.param(encodeColumn(column, amount), column.dataType);
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
if (name === "json.set") {
|
|
451
|
-
emitJsonSet(sql, column, value, alias);
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
if (name === "arr.append" || name === "arr.remove") {
|
|
455
|
-
emitArrayWrite(sql, column, name, value, alias);
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
fail("OKM1121", `Field ${column.field} received an operator. set accepts a value, inc, set, or arr.`);
|
|
459
|
-
}
|
|
460
|
-
if (value === null) {
|
|
461
|
-
sql.text(`null::${column.dataType}`);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
if (typeof value === "object")
|
|
465
|
-
takeObject(column, value);
|
|
466
|
-
sql.param(encodeColumn(column, value), column.dataType);
|
|
467
|
-
}
|
|
468
|
-
function emitJsonSet(sql, column, value, alias) {
|
|
469
|
-
assertOperatorFits(column, "json.set");
|
|
470
|
-
if (!isOperator(value))
|
|
471
|
-
fail("OKM1121", `json.set on ${column.field} needs a path and a value.`);
|
|
472
|
-
const stored = operatorValue(value);
|
|
473
|
-
if (!isRecord(stored) || !Array.isArray(stored.path)) {
|
|
474
|
-
fail("OKM1121", `json.set on ${column.field} needs a path and a value.`);
|
|
475
|
-
}
|
|
476
|
-
const path = stored.path;
|
|
477
|
-
if (path.length === 0 || path.some((item) => typeof item !== "string")) {
|
|
478
|
-
fail("OKM1121", `json.set on ${column.field} needs a list of segments.`);
|
|
479
|
-
}
|
|
480
|
-
const encoded = encodeJson(stored.value);
|
|
481
|
-
const ref = `${alias}.${quote(column.sql)}`;
|
|
482
|
-
const target = column.dataType === "json" ? `${ref}::jsonb` : ref;
|
|
483
|
-
sql.text(`jsonb_set(${target}, `);
|
|
484
|
-
sql.param(textArray(path, `path on ${column.field}`), "text[]");
|
|
485
|
-
sql.text(", ");
|
|
486
|
-
sql.param(encoded, "jsonb");
|
|
487
|
-
sql.text(")");
|
|
488
|
-
if (column.dataType === "json")
|
|
489
|
-
sql.text("::json");
|
|
490
|
-
}
|
|
491
|
-
function emitArrayWrite(sql, column, name, value, alias) {
|
|
492
|
-
assertOperatorFits(column, name);
|
|
493
|
-
const encode = column.elementEncode;
|
|
494
|
-
if (encode === undefined) {
|
|
495
|
-
fail("OKM1124", `Operator ${name} does not apply to ${column.dataType} column ${column.field}. Accepted operators: eq, not, lt, lte, gt, gte, between, inList, notIn, inc.`);
|
|
496
|
-
}
|
|
497
|
-
if (!isOperator(value)) {
|
|
498
|
-
fail("OKM1121", `${name} on ${column.field} needs an element.`);
|
|
499
|
-
}
|
|
500
|
-
const fn = name === "arr.append" ? "array_append" : "array_remove";
|
|
501
|
-
sql.text(`${fn}(${alias}.${quote(column.sql)}, `);
|
|
502
|
-
sql.param(encode(operatorValue(value)), arrayElementType(column.dataType, column));
|
|
503
|
-
sql.text(")");
|
|
504
|
-
}
|
|
505
|
-
function emitFilter(schema, table, where, sql, alias) {
|
|
506
|
-
emitWhere(schema, table, where, sql.sink(), alias, 0);
|
|
507
|
-
}
|
|
508
|
-
function emitBare(schema, table, where, sql, alias) {
|
|
509
|
-
let dropped = false;
|
|
510
|
-
const sink = {
|
|
511
|
-
text(value) {
|
|
512
|
-
if (!dropped) {
|
|
513
|
-
dropped = true;
|
|
514
|
-
if (value === " where " || value === " and ")
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
sql.text(value);
|
|
518
|
-
},
|
|
519
|
-
param(encoded) {
|
|
520
|
-
sql.param(encoded, "");
|
|
521
|
-
},
|
|
522
|
-
mark() {}
|
|
523
|
-
};
|
|
524
|
-
emitWhere(schema, table, where, sink, alias, 0);
|
|
525
|
-
if (!dropped) {
|
|
526
|
-
fail("OKM1102", `update on ${table.model.name} needs a where for each row.`);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
class Sql {
|
|
531
|
-
parts = [];
|
|
532
|
-
params = [];
|
|
533
|
-
text(value) {
|
|
534
|
-
this.parts.push(value);
|
|
535
|
-
}
|
|
536
|
-
param(wire, dataType) {
|
|
537
|
-
this.params.push(wire);
|
|
538
|
-
const slot = `$${String(this.params.length)}`;
|
|
539
|
-
this.parts.push(dataType.length === 0 ? slot : `${slot}::${dataType}`);
|
|
540
|
-
}
|
|
541
|
-
sink() {
|
|
542
|
-
return {
|
|
543
|
-
text: (value) => {
|
|
544
|
-
this.text(value);
|
|
545
|
-
},
|
|
546
|
-
param: (encoded) => {
|
|
547
|
-
this.param(encoded, "");
|
|
548
|
-
},
|
|
549
|
-
mark() {}
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
statement() {
|
|
553
|
-
return this.params.length === 0 ? { text: this.parts.join("") } : { text: this.parts.join(""), params: this.params };
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
function insertRow(table, value, allow, tenancy) {
|
|
557
|
-
if (!isRecord(value))
|
|
558
|
-
fail("OKM1121", "insert expects an object or a list of objects.");
|
|
559
|
-
const row = {};
|
|
560
|
-
for (const key of Object.keys(value)) {
|
|
561
|
-
const column = table.columns.get(key);
|
|
562
|
-
if (column === undefined)
|
|
563
|
-
continue;
|
|
564
|
-
tenancy?.guard(table.model.name, key, "insert");
|
|
565
|
-
if (!writable(table, column, allow))
|
|
566
|
-
refuse(table, column);
|
|
567
|
-
if (value[key] !== undefined)
|
|
568
|
-
row[key] = value[key];
|
|
569
|
-
}
|
|
570
|
-
return row;
|
|
571
|
-
}
|
|
572
|
-
function cellsFor(columns, row) {
|
|
573
|
-
return columns.map((column) => {
|
|
574
|
-
if (!Object.hasOwn(row, column.field))
|
|
575
|
-
return { kind: "default" };
|
|
576
|
-
const value = row[column.field];
|
|
577
|
-
if (value === null)
|
|
578
|
-
return { kind: "null", dataType: column.dataType };
|
|
579
|
-
if (typeof value === "object")
|
|
580
|
-
takeObject(column, value);
|
|
581
|
-
return { kind: "value", wire: encodeColumn(column, value), dataType: column.dataType };
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
function fillInsert(table, rows, generators) {
|
|
585
|
-
const fills = [];
|
|
586
|
-
for (const column of table.model.columns) {
|
|
587
|
-
if (column.fill !== undefined)
|
|
588
|
-
fills.push(column);
|
|
589
|
-
}
|
|
590
|
-
if (fills.length === 0)
|
|
591
|
-
return;
|
|
592
|
-
for (const row of rows) {
|
|
593
|
-
for (const column of fills) {
|
|
594
|
-
if (Object.hasOwn(row, column.field))
|
|
595
|
-
continue;
|
|
596
|
-
const fill = column.fill;
|
|
597
|
-
if (fill === undefined)
|
|
598
|
-
continue;
|
|
599
|
-
row[column.field] = nextFill(fill, generators);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
function nextFill(fill, generators) {
|
|
604
|
-
if (fill.name === "uuidv4" && generators?.uuidv4 !== undefined)
|
|
605
|
-
return generators.uuidv4();
|
|
606
|
-
if (fill.name === "uuidv7" && generators?.uuidv7 !== undefined)
|
|
607
|
-
return generators.uuidv7();
|
|
608
|
-
if (fill.name === "okid" && generators?.okid !== undefined)
|
|
609
|
-
return generators.okid();
|
|
610
|
-
return fill.call();
|
|
611
|
-
}
|
|
612
|
-
function writtenColumns(table, rows, allow, tenantKey) {
|
|
613
|
-
const present = new Set;
|
|
614
|
-
for (const row of rows) {
|
|
615
|
-
for (const key of Object.keys(row))
|
|
616
|
-
present.add(key);
|
|
617
|
-
}
|
|
618
|
-
const columns = [];
|
|
619
|
-
for (const column of table.model.columns) {
|
|
620
|
-
if (present.has(column.field) && (writable(table, column, allow) || column.fill !== undefined || column.field === tenantKey)) {
|
|
621
|
-
columns.push(column);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
return columns;
|
|
625
|
-
}
|
|
626
|
-
function writeSet(table, value, allow, tenancy) {
|
|
627
|
-
if (!isRecord(value))
|
|
628
|
-
fail("OKM1121", "set must be an object of fields.");
|
|
629
|
-
const set = {};
|
|
630
|
-
for (const key of Object.keys(value)) {
|
|
631
|
-
if (value[key] === undefined)
|
|
632
|
-
continue;
|
|
633
|
-
const column = table.columns.get(key);
|
|
634
|
-
if (column === undefined)
|
|
635
|
-
continue;
|
|
636
|
-
tenancy?.guard(table.model.name, key, "update");
|
|
637
|
-
if (column.guardUpdate || !writable(table, column, allow))
|
|
638
|
-
refuse(table, column);
|
|
639
|
-
set[key] = value[key];
|
|
640
|
-
}
|
|
641
|
-
return set;
|
|
642
|
-
}
|
|
643
|
-
function updateItem(table, value, allow, tenancy) {
|
|
644
|
-
if (!isRecord(value))
|
|
645
|
-
fail("OKM1121", "update expects { where, set } or a list of rows.");
|
|
646
|
-
rejectUpdateKeys(value, true);
|
|
647
|
-
const set = writeSet(table, value.set, allow, tenancy);
|
|
648
|
-
if (value.where !== undefined)
|
|
649
|
-
return { where: value.where, set };
|
|
650
|
-
if (value.id !== undefined)
|
|
651
|
-
return { where: { id: value.id }, set };
|
|
652
|
-
fail("OKM1102", `update on ${table.model.name} needs id or where for each row.`);
|
|
653
|
-
}
|
|
654
|
-
function rejectUpdateKeys(input, row) {
|
|
655
|
-
const accepted = row ? UPDATE_ROW_KEYS : UPDATE_KEYS;
|
|
656
|
-
for (const key of Object.keys(input)) {
|
|
657
|
-
if (accepted.includes(key))
|
|
658
|
-
continue;
|
|
659
|
-
throwNamed2("OKM1120", key, [...accepted, ...FILTER_OPTIONS], `Option ${key} is not accepted by update. Accepted options: ${accepted.join(", ")}. timeout and signal go in the second argument.`);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
function requireFilter(table, where, mods, op) {
|
|
663
|
-
if (mods.all !== undefined) {
|
|
664
|
-
if (mods.all.trim().length === 0) {
|
|
665
|
-
fail("OKM1102", `${op} on ${table.model.name} needs a where. .all needs a reason.`);
|
|
666
|
-
}
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
if (!effectivePredicate(where)) {
|
|
670
|
-
fail("OKM1102", `${op} on ${table.model.name} needs a where. Pass a filter, or .all(reason).`);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
function chunkRows(rows) {
|
|
674
|
-
const chunks = [];
|
|
675
|
-
let current = [];
|
|
676
|
-
let count = 0;
|
|
677
|
-
for (const row of rows) {
|
|
678
|
-
let cost = 0;
|
|
679
|
-
for (const cell of row)
|
|
680
|
-
if (cell.kind !== "default")
|
|
681
|
-
cost += 1;
|
|
682
|
-
if (current.length > 0 && count + cost > WRITE_PARAM_BUDGET) {
|
|
683
|
-
chunks.push(current);
|
|
684
|
-
current = [];
|
|
685
|
-
count = 0;
|
|
686
|
-
}
|
|
687
|
-
current.push(row);
|
|
688
|
-
count += cost;
|
|
689
|
-
}
|
|
690
|
-
if (current.length > 0)
|
|
691
|
-
chunks.push(current);
|
|
692
|
-
return chunks;
|
|
693
|
-
}
|
|
694
|
-
function withParams(text, params) {
|
|
695
|
-
if (params === undefined || params.length === 0)
|
|
696
|
-
return { text };
|
|
697
|
-
return { text, params };
|
|
698
|
-
}
|
|
699
|
-
function keyWire(columns, row) {
|
|
700
|
-
return columns.map((column) => {
|
|
701
|
-
const value = row[column.field];
|
|
702
|
-
if (value === undefined || value === null)
|
|
703
|
-
return null;
|
|
704
|
-
return encodeColumn(column, value);
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
function returningOption(table, value) {
|
|
708
|
-
if (value === undefined)
|
|
709
|
-
return;
|
|
710
|
-
return selectedColumns(table, value);
|
|
711
|
-
}
|
|
712
|
-
function returningClause(columns, alias) {
|
|
713
|
-
if (columns === undefined || columns.length === 0)
|
|
714
|
-
return "";
|
|
715
|
-
const parts = columns.map((column) => {
|
|
716
|
-
const sql = alias === undefined ? quote(column.sql) : `${alias}.${quote(column.sql)}`;
|
|
717
|
-
return projectExpr(sql, column.dataType);
|
|
718
|
-
});
|
|
719
|
-
return ` returning ${parts.join(", ")}`;
|
|
720
|
-
}
|
|
721
|
-
function outputsOf(columns) {
|
|
722
|
-
const list = columns ?? [];
|
|
723
|
-
return {
|
|
724
|
-
fields: list.map((column, index) => ({
|
|
725
|
-
key: column.field,
|
|
726
|
-
at: index,
|
|
727
|
-
decode: column.decode
|
|
728
|
-
})),
|
|
729
|
-
includes: []
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
function expectOf(options, mods) {
|
|
733
|
-
const value = mods.expect ?? options.expect;
|
|
734
|
-
if (value === undefined)
|
|
735
|
-
return;
|
|
736
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
737
|
-
throw new OkmError2("invalid", "expect must be an integer from 0 up.", { kind: "invalid" });
|
|
738
|
-
}
|
|
739
|
-
return value;
|
|
740
|
-
}
|
|
741
|
-
function checkExpect(count, expected, table, op) {
|
|
742
|
-
if (expected === undefined || count === expected)
|
|
743
|
-
return;
|
|
744
|
-
throw new OkmError2("not_found", `${op} on ${table} changed ${String(count)} rows. expect was ${String(expected)}.`, {
|
|
745
|
-
kind: "not_found",
|
|
746
|
-
table,
|
|
747
|
-
fix: { summary: "Change the filter, or pass the count this write produced." }
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
function callOptions(options) {
|
|
751
|
-
if (!isRecord(options))
|
|
752
|
-
return;
|
|
753
|
-
const signal = options.signal;
|
|
754
|
-
const timeout = options.timeout;
|
|
755
|
-
const hasSignal = typeof signal === "object" && signal !== null && "aborted" in signal;
|
|
756
|
-
const hasTimeout = typeof timeout === "number";
|
|
757
|
-
if (!hasSignal && !hasTimeout)
|
|
758
|
-
return;
|
|
759
|
-
return {
|
|
760
|
-
...hasSignal ? { signal } : {},
|
|
761
|
-
...hasTimeout ? { timeout } : {}
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
function refuse(table, column) {
|
|
765
|
-
if (fieldSealed(table.model, column.field)) {
|
|
766
|
-
fail("OKM1190", `Field ${table.model.name}.${column.field} is set by a trait. Input cannot set it.`);
|
|
767
|
-
}
|
|
768
|
-
if (column.guardUpdate) {
|
|
769
|
-
fail("OKM1190", `Field ${table.model.name}.${column.field} is a primary key. Input cannot change it.`);
|
|
770
|
-
}
|
|
771
|
-
if (column.guarded) {
|
|
772
|
-
fail("OKM1190", `Field ${table.model.name}.${column.field} is guarded. Input cannot set it.`);
|
|
773
|
-
}
|
|
774
|
-
fail("OKM1120", `Field ${table.model.name}.${column.field} cannot be written.`);
|
|
775
|
-
}
|
|
776
|
-
function writable(table, column, allow) {
|
|
777
|
-
if (fieldSealed(table.model, column.field))
|
|
778
|
-
return false;
|
|
779
|
-
if (column.writable)
|
|
780
|
-
return true;
|
|
781
|
-
return column.guarded && allow.has(column.field);
|
|
782
|
-
}
|
|
783
|
-
function allowSet(table, value) {
|
|
784
|
-
if (value === undefined)
|
|
785
|
-
return NO_ALLOW;
|
|
786
|
-
if (!Array.isArray(value))
|
|
787
|
-
fail("OKM1120", "allow must be a list of field names.");
|
|
788
|
-
const set = new Set;
|
|
789
|
-
for (const name of value) {
|
|
790
|
-
if (typeof name !== "string" || !table.columns.has(name)) {
|
|
791
|
-
fail("OKM1120", "allow must name fields on the table.");
|
|
792
|
-
}
|
|
793
|
-
set.add(name);
|
|
794
|
-
}
|
|
795
|
-
return set;
|
|
796
|
-
}
|
|
797
|
-
function noteArchive(table, view) {
|
|
798
|
-
if (!safetyInstalled())
|
|
799
|
-
return;
|
|
800
|
-
const rules = archiveRules(table.model, view);
|
|
801
|
-
if (rules === undefined)
|
|
802
|
-
return;
|
|
803
|
-
runSafety(rules, undefined);
|
|
804
|
-
}
|
|
805
|
-
function noteGuarded(table, allow, op) {
|
|
806
|
-
if (!safetyInstalled())
|
|
807
|
-
return;
|
|
808
|
-
const contributions = [];
|
|
809
|
-
const hatches = [];
|
|
810
|
-
const touch = touchFields(table.model);
|
|
811
|
-
const source = table.model.source;
|
|
812
|
-
for (const column of table.model.columns) {
|
|
813
|
-
const traitName = sealingTrait(table.model, column.field);
|
|
814
|
-
if (traitName !== undefined) {
|
|
815
|
-
const sets = op === "insert" || touch?.includes(column.field) === true;
|
|
816
|
-
contributions.push({
|
|
817
|
-
rule: traitName,
|
|
818
|
-
contribution: sets ? `${traitName} ${table.model.name}.${column.field} set by ${traitName}` : `${traitName} ${table.model.name}.${column.field} kept`,
|
|
819
|
-
provenance: "planner",
|
|
820
|
-
...source !== undefined ? { source } : {}
|
|
821
|
-
});
|
|
822
|
-
continue;
|
|
823
|
-
}
|
|
824
|
-
if (!column.guarded)
|
|
825
|
-
continue;
|
|
826
|
-
const name = `${table.model.name}.${column.field}`;
|
|
827
|
-
if (allow.has(column.field)) {
|
|
828
|
-
contributions.push({
|
|
829
|
-
rule: "guarded",
|
|
830
|
-
contribution: `guarded ${name} allowed`,
|
|
831
|
-
provenance: "allow"
|
|
832
|
-
});
|
|
833
|
-
hatches.push({ name: "allow", reason: name });
|
|
834
|
-
} else {
|
|
835
|
-
contributions.push({
|
|
836
|
-
rule: "guarded",
|
|
837
|
-
contribution: `guarded ${name} absent`,
|
|
838
|
-
provenance: "planner"
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
if (contributions.length === 0)
|
|
843
|
-
return;
|
|
844
|
-
runSafety(contributions, hatches.length === 0 ? undefined : hatches);
|
|
845
|
-
}
|
|
846
|
-
function scrubWrite(schema, tableName, input, error) {
|
|
847
|
-
if (!(error instanceof OkmError2))
|
|
848
|
-
return error;
|
|
849
|
-
const table = indexes(schema).get(tableName);
|
|
850
|
-
if (table === undefined || table.model.conceal !== true)
|
|
851
|
-
return error;
|
|
852
|
-
const secrets = [];
|
|
853
|
-
const names = new Set;
|
|
854
|
-
for (const column of table.model.columns) {
|
|
855
|
-
if (column.hidden || column.sensitive)
|
|
856
|
-
names.add(column.field);
|
|
857
|
-
}
|
|
858
|
-
collectSecrets(input, names, secrets);
|
|
859
|
-
if (secrets.length === 0)
|
|
860
|
-
return error;
|
|
861
|
-
let message = error.message;
|
|
862
|
-
for (const secret of secrets) {
|
|
863
|
-
if (secret.length < 4 || !message.includes(secret))
|
|
864
|
-
continue;
|
|
865
|
-
message = message.replaceAll(secret, REDACTED);
|
|
866
|
-
}
|
|
867
|
-
const issues = error.issues === undefined ? undefined : redactIssues(error.issues, secrets);
|
|
868
|
-
if (message === error.message && issues === undefined)
|
|
869
|
-
return error;
|
|
870
|
-
return new OkmError2(error.code, message, {
|
|
871
|
-
kind: error.kind,
|
|
872
|
-
...error.table !== undefined ? { table: error.table } : {},
|
|
873
|
-
...error.columns.length > 0 ? { columns: error.columns } : {},
|
|
874
|
-
...error.issues !== undefined ? { issues: issues ?? error.issues } : {},
|
|
875
|
-
fix: error.fix
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
function redactIssues(issues, secrets) {
|
|
879
|
-
let changed = false;
|
|
880
|
-
const next = issues.map((issue) => {
|
|
881
|
-
let message = issue.message;
|
|
882
|
-
for (const secret of secrets) {
|
|
883
|
-
if (secret.length < 4 || !message.includes(secret))
|
|
884
|
-
continue;
|
|
885
|
-
message = message.replaceAll(secret, REDACTED);
|
|
886
|
-
changed = true;
|
|
887
|
-
}
|
|
888
|
-
return message === issue.message ? issue : { path: issue.path, message };
|
|
889
|
-
});
|
|
890
|
-
return changed ? next : undefined;
|
|
891
|
-
}
|
|
892
|
-
function collectSecrets(value, names, found, key) {
|
|
893
|
-
if (typeof value === "string") {
|
|
894
|
-
if (key !== undefined && names.has(key))
|
|
895
|
-
found.push(value);
|
|
896
|
-
return;
|
|
897
|
-
}
|
|
898
|
-
if (Array.isArray(value)) {
|
|
899
|
-
for (const item of value)
|
|
900
|
-
collectSecrets(item, names, found);
|
|
901
|
-
return;
|
|
902
|
-
}
|
|
903
|
-
if (!isRecord(value))
|
|
904
|
-
return;
|
|
905
|
-
for (const child of Object.keys(value))
|
|
906
|
-
collectSecrets(value[child], names, found, child);
|
|
907
|
-
}
|
|
908
|
-
export {
|
|
909
|
-
WRITE_PARAM_BUDGET,
|
|
910
|
-
executeWrite,
|
|
911
|
-
explainWrite
|
|
912
|
-
};
|