okmodel 0.7.0 → 0.9.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 +14 -0
- package/dist/adapters/capabilities.d.ts +32 -0
- package/dist/adapters/failure.d.ts +1 -1
- package/dist/adapters/pg/bun-sql.d.ts +53 -0
- package/dist/adapters/pg/bunsql-extra.d.ts +1 -1
- package/dist/adapters/pg/bunsql.d.ts +1 -1
- package/dist/adapters/pg/neon-extra.d.ts +18 -0
- package/dist/adapters/pg/neon.d.ts +104 -0
- package/dist/adapters/pg/no-inflight-timeout.d.ts +1 -1
- package/dist/contracts/error.d.ts +1 -1
- package/dist/dialects/pg/ext/btree-gin.d.ts +26 -0
- package/dist/dialects/pg/ext/btree-gin.js +20 -0
- package/dist/dialects/pg/ext/btree-gist.d.ts +26 -0
- package/dist/dialects/pg/ext/btree-gist.js +20 -0
- package/dist/dialects/pg/ext/call.d.ts +53 -0
- package/dist/dialects/pg/ext/citext.js +1 -1
- package/dist/dialects/pg/ext/feature.d.ts +26 -0
- package/dist/dialects/pg/ext/fuzzystrmatch.d.ts +34 -0
- package/dist/dialects/pg/ext/fuzzystrmatch.js +33 -0
- package/dist/dialects/pg/ext/hstore.d.ts +41 -0
- package/dist/dialects/pg/ext/hstore.js +29 -0
- package/dist/dialects/pg/ext/index.d.ts +56 -0
- package/dist/dialects/pg/ext/index.js +1 -1
- package/dist/dialects/pg/ext/ltree.d.ts +41 -0
- package/dist/dialects/pg/ext/ltree.js +29 -0
- package/dist/dialects/pg/ext/pg-trgm.js +4 -13
- package/dist/dialects/pg/ext/pgcrypto.d.ts +33 -0
- package/dist/dialects/pg/ext/pgcrypto.js +33 -0
- package/dist/dialects/pg/ext/unaccent.d.ts +26 -0
- package/dist/dialects/pg/ext/unaccent.js +21 -0
- package/dist/dialects/pg/ext/using.d.ts +23 -0
- package/dist/dialects/pg/ext/versions.d.ts +36 -0
- package/dist/dialects/pg/external.d.ts +19 -0
- package/dist/dialects/pg/fn/index.d.ts +39 -1
- package/dist/dialects/pg/fn/index.js +118 -26
- package/dist/dialects/pg/index.d.ts +3 -0
- package/dist/dialects/pg/index.js +394 -14
- package/dist/dialects/pg/introspect.d.ts +11 -0
- package/dist/dialects/pg/operators.d.ts +1 -1
- package/dist/dialects/pg/ops/fragment.d.ts +119 -0
- package/dist/dialects/pg/ops/lane.d.ts +49 -0
- package/dist/dialects/pg/view/client.d.ts +14 -6
- package/dist/dialects/pg/view/index.d.ts +73 -1
- package/dist/dialects/pg/view/index.js +156 -5
- package/dist/dialects/pg/view/scratch.d.ts +11 -5
- package/dist/runtime/artifact-file.d.ts +22 -0
- package/dist/runtime/client.d.ts +10 -0
- package/dist/runtime/drift-detail.d.ts +2 -1
- package/dist/runtime/drift.d.ts +3 -10
- package/dist/runtime/otel.d.ts +105 -0
- package/dist/runtime/otel.js +97 -0
- package/dist/runtime/pg/bun.js +4 -4
- package/dist/runtime/pg/neon.d.ts +38 -0
- package/dist/runtime/pg/neon.js +64 -0
- package/dist/runtime/pg/pg.js +4 -4
- package/dist/runtime/pg/pglite.js +4 -4
- package/dist/runtime/pg/postgresjs.js +4 -4
- package/dist/runtime/plan.d.ts +1 -1
- package/dist/runtime/server-check.d.ts +36 -6
- package/dist/runtime/sql-fragment.d.ts +43 -0
- package/dist/runtime/tenancy/index.js +10 -10
- package/dist/runtime/topology.d.ts +1 -1
- package/dist/runtime/traits/index.js +16 -17
- package/dist/runtime/types.d.ts +15 -1
- package/dist/runtime/validate/index.js +4 -4
- package/dist/shared/{81ebdx9p.js → 0j6qe91y.js} +4 -4
- package/dist/shared/{qq3899ht.js → 12z7aznj.js} +243 -14
- package/dist/shared/{gzaveazc.js → 1d9q9cv2.js} +29 -2
- package/dist/shared/{w79hsckn.js → 1mxm04fy.js} +5 -5
- package/dist/shared/{7k9grvf6.js → 2szgv6yj.js} +5 -5
- package/dist/shared/{gs74dbj5.js → 2zdxkt7b.js} +933 -111
- package/dist/shared/{m6p12kbr.js → 3456242y.js} +23 -23
- package/dist/shared/3bb7wsqt.js +99 -0
- package/dist/shared/{y6njgyda.js → 3t6kw83m.js} +2 -2
- package/dist/shared/{xhfsfkgk.js → 4vbf83xx.js} +9 -2
- package/dist/shared/{pt94kz7f.js → 5kyb3tqx.js} +1 -1
- package/dist/shared/61bmrmhr.js +35 -0
- package/dist/shared/{x933k9a3.js → 62b603gr.js} +10 -10
- package/dist/shared/{jgqj11w4.js → 6f0z3hzh.js} +4 -0
- package/dist/shared/{tsvww22q.js → 6nae0spm.js} +156 -95
- package/dist/shared/{mtaszv5a.js → 71n0jmhc.js} +102 -4
- package/dist/shared/{5e3tf63j.js → 760ppr76.js} +14 -6
- package/dist/shared/7ba5h6pb.js +17 -0
- package/dist/shared/{sfz1ksn6.js → 8jesqsrk.js} +2 -2
- package/dist/shared/{5v3xf80k.js → 8n688yjg.js} +1 -1
- package/dist/shared/{rs73fca9.js → 8qzkd9f3.js} +6 -6
- package/dist/shared/{ax7qv55k.js → 90sg0w5a.js} +202 -25
- package/dist/shared/{zm0ptry0.js → 99qp9vtg.js} +1 -1
- package/dist/shared/{xnxsed62.js → 9dr2qnx7.js} +14 -14
- package/dist/shared/{1w508jbr.js → 9dtgn1z2.js} +21 -17
- package/dist/shared/{h15jx9bc.js → 9dvh1dr6.js} +1 -1
- package/dist/shared/{q5c6ngn3.js → a62rb2da.js} +1 -1
- package/dist/shared/{te5as7z7.js → aksb89mn.js} +3 -3
- package/dist/shared/{bz84tkzm.js → bphrgwz9.js} +2 -2
- package/dist/shared/bsek72fg.js +225 -0
- package/dist/shared/{7mnme5tn.js → c3mvnq7a.js} +1 -1
- package/dist/shared/{p6qc5gcp.js → ccsmhv9r.js} +25 -7
- package/dist/shared/{pbp1ss85.js → cexb7z6b.js} +2 -2
- package/dist/shared/{ab9gmbwy.js → cf8zdz5p.js} +5 -5
- package/dist/shared/{jvd0mqag.js → ckz05z15.js} +5 -5
- package/dist/shared/{dk8v79p3.js → d5nmj7vv.js} +1 -1
- package/dist/shared/{14vx95q4.js → dd5pmnky.js} +7 -7
- package/dist/shared/{vb6qfyve.js → ds229x4r.js} +2 -2
- package/dist/shared/{1qa0qajz.js → e275q7sd.js} +1 -1
- package/dist/shared/{zp9fmend.js → ebhz8vh6.js} +4 -4
- package/dist/shared/{hyh73qk6.js → ecn8mfdq.js} +5 -5
- package/dist/shared/{je4axnm8.js → fdsz3w80.js} +2 -2
- package/dist/shared/{9s0ypp87.js → fk9vef30.js} +85 -74
- package/dist/shared/{g6evj1j3.js → g89fmz5s.js} +1 -1
- package/dist/shared/{aytqh5y2.js → ghqzdp94.js} +2 -2
- package/dist/shared/h9811jjm.js +17 -0
- package/dist/shared/{dkxgzaqd.js → hqpxf4a4.js} +1 -1
- package/dist/shared/{8skf0cam.js → jpybhf98.js} +1 -1
- package/dist/shared/{67txcrcw.js → kgwp4jdd.js} +4 -4
- package/dist/shared/kzgrnckd.js +29 -0
- package/dist/shared/mazpbcdj.js +233 -0
- package/dist/shared/{kb2sdhfb.js → mfvx6kjx.js} +17 -1
- package/dist/shared/{svncdeh5.js → mgaq2sd3.js} +11 -8
- package/dist/shared/{5mtf6cj3.js → mvmm3xpb.js} +5 -2
- package/dist/shared/{b5pftnn2.js → myk8fswx.js} +66 -21
- package/dist/shared/{2shdbn8q.js → n306gqkd.js} +9 -9
- package/dist/shared/{n816peh1.js → n454kcnw.js} +1 -1
- package/dist/shared/n5whk3mt.js +665 -0
- package/dist/shared/n7kg8mmx.js +61 -0
- package/dist/shared/{vqrjpcqn.js → nmnsgj7x.js} +1 -1
- package/dist/shared/{tbrvgw6z.js → npdftanb.js} +19 -26
- package/dist/shared/{ek2wt2dz.js → pg4ezgyf.js} +11 -30
- package/dist/shared/{5fe3kbsh.js → pgsak1yw.js} +1 -1
- package/dist/shared/{bm22fvzr.js → qfw14tna.js} +2 -2
- package/dist/shared/{wa07eas9.js → qvwwp1mp.js} +62 -41
- package/dist/shared/rbxt795q.js +1766 -0
- package/dist/shared/{xjk7hnn5.js → s7he5e5d.js} +2 -2
- package/dist/shared/{w72kgg4q.js → ss2kv2rs.js} +6 -6
- package/dist/shared/stfa9xcw.js +450 -0
- package/dist/shared/{3v7t1yrv.js → th3be82n.js} +2 -2
- package/dist/shared/{79dt02vc.js → tr64w5p0.js} +1 -1
- package/dist/shared/{cn957f8d.js → tw1nvvfj.js} +3 -3
- package/dist/shared/{ed0b8k4s.js → typ3vy7t.js} +1 -1
- package/dist/shared/{9628a9xt.js → vk7rag46.js} +1 -1
- package/dist/shared/{ze4hxsmy.js → w9ejzaqf.js} +1 -1
- package/dist/shared/{vczs8gd4.js → wvtjg5m3.js} +2 -2
- package/dist/shared/{9y3mmmh1.js → xfhe73sb.js} +1 -1
- package/dist/shared/xqacq37x.js +61 -0
- package/dist/shared/{1est4ezg.js → xtsydxjg.js} +3 -3
- package/dist/shared/xv8qj97g.js +96 -0
- package/dist/shared/{bg3rym3g.js → xwhdcrm4.js} +2 -2
- package/dist/shared/{kr0jzbw2.js → yvabyg9k.js} +7 -7
- package/dist/shared/yybhre6n.js +136 -0
- package/dist/shared/zdqf8k5d.js +8 -0
- package/dist/shared/{jznfax0y.js → zq1he88x.js} +4 -4
- package/dist/tooling/cli.js +108 -23
- package/dist/tooling/driver-run.js +4654 -0
- package/dist/tooling/driver-test.d.ts +36 -0
- package/dist/tooling/github/comment.d.ts +50 -0
- package/dist/tooling/github/post-comment.d.ts +17 -0
- package/dist/tooling/migrate/commands.d.ts +3 -2
- package/dist/tooling/migrate/config.d.ts +13 -0
- package/dist/tooling/migrate/export.d.ts +165 -0
- package/dist/tooling/migrate/ext-surface.d.ts +80 -0
- package/dist/tooling/migrate/ext.d.ts +31 -4
- package/dist/tooling/migrate/import-drizzle.d.ts +40 -0
- package/dist/tooling/migrate/index.d.ts +3 -0
- package/dist/tooling/migrate/index.js +55 -10
- package/dist/tooling/migrate/lint-rules.d.ts +18 -1
- package/dist/tooling/migrate/lint.d.ts +14 -1
- package/dist/tooling/migrate/place.d.ts +18 -0
- package/dist/tooling/migrate/plan-json.d.ts +65 -0
- package/dist/tooling/migrate/project.d.ts +13 -1
- package/dist/tooling/migrate/pull-emit.d.ts +98 -0
- package/dist/tooling/migrate/pull-routines.d.ts +126 -0
- package/dist/tooling/migrate/pull-verify.d.ts +26 -0
- package/dist/tooling/migrate/pull.d.ts +62 -0
- package/dist/tooling/migrate/reprint.d.ts +32 -0
- package/dist/tooling/migrate/sandbox.d.ts +8 -2
- package/dist/tooling/migrate/schema-api.d.ts +43 -0
- package/dist/tooling/migrate/sql-lex.d.ts +18 -0
- package/dist/tooling/shared/04svnxxj.js +344 -0
- package/dist/tooling/shared/0j0jtse1.js +13 -0
- package/dist/tooling/shared/0r05zhyq.js +618 -0
- package/dist/tooling/shared/1800eymw.js +49 -0
- package/dist/tooling/shared/35zemrmf.js +12 -0
- package/dist/tooling/shared/3rmaz3pq.js +140 -0
- package/dist/tooling/shared/3yn79q5m.js +12 -0
- package/dist/tooling/shared/54tqkxja.js +51 -0
- package/dist/tooling/shared/57btdkgm.js +209 -0
- package/dist/tooling/shared/5bmksndh.js +118 -0
- package/dist/tooling/shared/5p8km5jb.js +38 -0
- package/dist/tooling/shared/5x3pf400.js +185 -0
- package/dist/tooling/shared/5zrzffnk.js +6 -0
- package/dist/tooling/shared/62fxpc1p.js +332 -0
- package/dist/tooling/shared/68tf18v4.js +20 -0
- package/dist/tooling/shared/6vk28rjp.js +98 -0
- package/dist/tooling/shared/76pv6qff.js +9 -0
- package/dist/tooling/shared/7aews1zg.js +311 -0
- package/dist/tooling/shared/7b3xp9gc.js +195 -0
- package/dist/tooling/shared/7kms6fvj.js +20 -0
- package/dist/tooling/shared/7pxncnzs.js +40 -0
- package/dist/tooling/shared/829fjxyw.js +198 -0
- package/dist/tooling/shared/8m5znpdt.js +66 -0
- package/dist/tooling/shared/8yr7ges1.js +211 -0
- package/dist/tooling/shared/96ps4g1z.js +32 -0
- package/dist/tooling/shared/9qr9rkbh.js +143 -0
- package/dist/tooling/shared/9sfbtwsk.js +49 -0
- package/dist/tooling/shared/9wsnsvbf.js +10 -0
- package/dist/tooling/shared/a2xe04bm.js +18 -0
- package/dist/tooling/shared/a4pzghha.js +20 -0
- package/dist/tooling/shared/am04v8na.js +233 -0
- package/dist/tooling/shared/asgr07mp.js +194 -0
- package/dist/tooling/shared/c0xevn1h.js +312 -0
- package/dist/tooling/shared/dhatassp.js +134 -0
- package/dist/tooling/shared/ezqn88nj.js +1696 -0
- package/dist/tooling/shared/f77nnfqy.js +231 -0
- package/dist/tooling/shared/fh07z1dt.js +15 -0
- package/dist/tooling/shared/gqwevek4.js +81 -0
- package/dist/tooling/shared/hfq3n3bd.js +4116 -0
- package/dist/tooling/shared/htnaj77v.js +17 -0
- package/dist/tooling/shared/j664qjrt.js +64 -0
- package/dist/tooling/shared/jpgmjsx3.js +38 -0
- package/dist/tooling/shared/jr2nmr81.js +497 -0
- package/dist/tooling/shared/jsjjz875.js +139 -0
- package/dist/tooling/shared/kbqnrs1x.js +1425 -0
- package/dist/tooling/shared/kz715295.js +94 -0
- package/dist/tooling/shared/m3xt8b1h.js +22 -0
- package/dist/tooling/shared/m4hhb8aa.js +9 -0
- package/dist/tooling/shared/mw5npye0.js +16 -0
- package/dist/tooling/shared/n4jqhpqz.js +27 -0
- package/dist/tooling/shared/nxq95eb6.js +42 -0
- package/dist/tooling/shared/qartgazh.js +70 -0
- package/dist/tooling/shared/rcywj2w8.js +190 -0
- package/dist/tooling/shared/s6qha57m.js +16 -0
- package/dist/tooling/shared/shjs0379.js +150 -0
- package/dist/tooling/shared/sjwkt1pg.js +16 -0
- package/dist/tooling/shared/stebqyyd.js +401 -0
- package/dist/tooling/shared/t4xveemq.js +250 -0
- package/dist/tooling/shared/t6zjeake.js +261 -0
- package/dist/tooling/shared/tcwpjvxe.js +9 -0
- package/dist/tooling/shared/tpssp7ac.js +160 -0
- package/dist/tooling/shared/v6j1ep6w.js +42 -0
- package/dist/tooling/shared/vwr2y833.js +270 -0
- package/dist/tooling/shared/w17sj05d.js +7 -0
- package/dist/tooling/shared/w5d89rb4.js +131 -0
- package/dist/tooling/shared/wbwq1jsa.js +157 -0
- package/dist/tooling/shared/wezxf3h8.js +17 -0
- package/dist/tooling/shared/yb4kd19s.js +12 -0
- package/dist/tooling/shared/ybdew6j7.js +294 -0
- package/dist/tooling/shared/yfedr1zb.js +6 -0
- package/dist/tooling/shared/yhp829bx.js +177 -0
- package/dist/tooling/shared/ytk1tjs0.js +604 -0
- package/dist/tooling/shared/zs9aymcp.js +210 -0
- package/dist/tooling/shared/zx3v9phv.js +8 -0
- package/dist/tooling/testing/index.js +3 -3
- package/package.json +50 -9
- package/dist/shared/ktz42yez.js +0 -113
- package/dist/shared/ttsqgp4p.js +0 -71
- package/dist/shared/y34a8714.js +0 -150
- package/dist/shared/zy8r7qvz.js +0 -116
- package/dist/shared/{ywr8hmey.js → 606g0nbf.js} +3 -3
- package/dist/shared/{rpycarcr.js → gcefca6p.js} +4 -4
|
@@ -0,0 +1,4116 @@
|
|
|
1
|
+
// src/contracts/nearest.ts
|
|
2
|
+
function nearestName(input, candidates) {
|
|
3
|
+
if (input.length === 0 || candidates.length === 0)
|
|
4
|
+
return;
|
|
5
|
+
const limit = input.length <= 4 ? 1 : input.length <= 12 ? 2 : 3;
|
|
6
|
+
let width = 0;
|
|
7
|
+
for (const candidate of candidates) {
|
|
8
|
+
if (candidate.length > width)
|
|
9
|
+
width = candidate.length;
|
|
10
|
+
}
|
|
11
|
+
const prev = new Uint16Array(width + 1);
|
|
12
|
+
const next = new Uint16Array(width + 1);
|
|
13
|
+
let best;
|
|
14
|
+
let bestDistance = limit;
|
|
15
|
+
for (const candidate of candidates) {
|
|
16
|
+
if (candidate === input)
|
|
17
|
+
continue;
|
|
18
|
+
const gap = candidate.length > input.length ? candidate.length - input.length : input.length - candidate.length;
|
|
19
|
+
if (gap > bestDistance)
|
|
20
|
+
continue;
|
|
21
|
+
const distance = editDistance(input, candidate, bestDistance, prev, next);
|
|
22
|
+
if (distance === undefined)
|
|
23
|
+
continue;
|
|
24
|
+
if (best === undefined || distance < bestDistance || distance === bestDistance && candidate < best) {
|
|
25
|
+
best = candidate;
|
|
26
|
+
bestDistance = distance;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return best;
|
|
30
|
+
}
|
|
31
|
+
function editDistance(left, right, cap, prev, next) {
|
|
32
|
+
const leftLength = left.length;
|
|
33
|
+
const rightLength = right.length;
|
|
34
|
+
if (leftLength === 0)
|
|
35
|
+
return rightLength <= cap ? rightLength : undefined;
|
|
36
|
+
if (rightLength === 0)
|
|
37
|
+
return leftLength <= cap ? leftLength : undefined;
|
|
38
|
+
for (let column = 0;column <= rightLength; column += 1)
|
|
39
|
+
prev[column] = column;
|
|
40
|
+
for (let row = 1;row <= leftLength; row += 1) {
|
|
41
|
+
next[0] = row;
|
|
42
|
+
let rowBest = row;
|
|
43
|
+
const leftCode = left.charCodeAt(row - 1);
|
|
44
|
+
for (let column = 1;column <= rightLength; column += 1) {
|
|
45
|
+
const cost = sameLetter(leftCode, right.charCodeAt(column - 1)) ? 0 : 1;
|
|
46
|
+
const deletion = (prev[column] ?? 0) + 1;
|
|
47
|
+
const insertion = (next[column - 1] ?? 0) + 1;
|
|
48
|
+
const substitution = (prev[column - 1] ?? 0) + cost;
|
|
49
|
+
let value = deletion < insertion ? deletion : insertion;
|
|
50
|
+
if (substitution < value)
|
|
51
|
+
value = substitution;
|
|
52
|
+
next[column] = value;
|
|
53
|
+
if (value < rowBest)
|
|
54
|
+
rowBest = value;
|
|
55
|
+
}
|
|
56
|
+
if (rowBest > cap)
|
|
57
|
+
return;
|
|
58
|
+
for (let column = 0;column <= rightLength; column += 1)
|
|
59
|
+
prev[column] = next[column] ?? 0;
|
|
60
|
+
}
|
|
61
|
+
const distance = prev[rightLength] ?? cap + 1;
|
|
62
|
+
return distance <= cap ? distance : undefined;
|
|
63
|
+
}
|
|
64
|
+
function sameLetter(left, right) {
|
|
65
|
+
if (left === right)
|
|
66
|
+
return true;
|
|
67
|
+
const leftFold = left >= 65 && left <= 90 ? left + 32 : left;
|
|
68
|
+
const rightFold = right >= 65 && right <= 90 ? right + 32 : right;
|
|
69
|
+
return leftFold === rightFold;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/contracts/error.ts
|
|
73
|
+
var NO_COLUMNS = [];
|
|
74
|
+
var NO_FIELDS = {};
|
|
75
|
+
var DEFAULT_STATUS = {
|
|
76
|
+
input: 422,
|
|
77
|
+
conflict: 409,
|
|
78
|
+
not_found: 404,
|
|
79
|
+
forbidden: 403,
|
|
80
|
+
transient: 503,
|
|
81
|
+
internal: 500
|
|
82
|
+
};
|
|
83
|
+
var CATEGORY_OF = {
|
|
84
|
+
invalid: "input",
|
|
85
|
+
not_null: "input",
|
|
86
|
+
check: "input",
|
|
87
|
+
foreign_key: "input",
|
|
88
|
+
unique: "conflict",
|
|
89
|
+
exclusion: "conflict",
|
|
90
|
+
conflict: "conflict",
|
|
91
|
+
not_found: "not_found",
|
|
92
|
+
not_unique: "not_found",
|
|
93
|
+
forbidden: "forbidden",
|
|
94
|
+
serialization: "transient",
|
|
95
|
+
deadlock: "transient",
|
|
96
|
+
lock_timeout: "transient",
|
|
97
|
+
timeout: "transient",
|
|
98
|
+
cancelled: "transient",
|
|
99
|
+
unavailable: "transient",
|
|
100
|
+
schema_drift: "internal",
|
|
101
|
+
driver: "internal",
|
|
102
|
+
read_only: "internal",
|
|
103
|
+
outcome_unknown: "internal"
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
class OkmError extends Error {
|
|
107
|
+
code;
|
|
108
|
+
kind;
|
|
109
|
+
category;
|
|
110
|
+
retryable;
|
|
111
|
+
fix;
|
|
112
|
+
table;
|
|
113
|
+
columns;
|
|
114
|
+
constraint;
|
|
115
|
+
sqlstate;
|
|
116
|
+
batchIndex;
|
|
117
|
+
fieldReason;
|
|
118
|
+
#fields;
|
|
119
|
+
#values;
|
|
120
|
+
#http;
|
|
121
|
+
issues;
|
|
122
|
+
get summary() {
|
|
123
|
+
return this.message;
|
|
124
|
+
}
|
|
125
|
+
constructor(code, message, options) {
|
|
126
|
+
const cause = options?.cause;
|
|
127
|
+
super(message, cause === undefined ? undefined : { cause });
|
|
128
|
+
this.name = "OkmError";
|
|
129
|
+
this.code = code;
|
|
130
|
+
const kind = options?.kind ?? kindFromCode(code);
|
|
131
|
+
this.kind = kind;
|
|
132
|
+
this.category = CATEGORY_OF[kind];
|
|
133
|
+
this.retryable = options?.retryable ?? defaultRetryable(kind);
|
|
134
|
+
this.fix = options?.fix ?? { summary: defaultFix(kind) };
|
|
135
|
+
this.table = options?.table;
|
|
136
|
+
const columns = options?.columns;
|
|
137
|
+
this.columns = columns === undefined || columns.length === 0 ? NO_COLUMNS : columns;
|
|
138
|
+
this.constraint = options?.constraint;
|
|
139
|
+
this.sqlstate = options?.sqlstate;
|
|
140
|
+
this.batchIndex = options?.batchIndex === undefined ? null : options.batchIndex;
|
|
141
|
+
this.fieldReason = options?.fieldReason ?? kind;
|
|
142
|
+
this.#values = options?.includeValues === true ? options.values : undefined;
|
|
143
|
+
this.#http = options?.http;
|
|
144
|
+
if (options?.issues !== undefined)
|
|
145
|
+
this.issues = options.issues;
|
|
146
|
+
}
|
|
147
|
+
static from(error) {
|
|
148
|
+
if (error instanceof OkmError)
|
|
149
|
+
return error;
|
|
150
|
+
if (isDriverError(error))
|
|
151
|
+
return fromDriver(error);
|
|
152
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
153
|
+
return new OkmError("driver", stripRowValues(message), { kind: "driver", cause: error });
|
|
154
|
+
}
|
|
155
|
+
static is(error, kind, table) {
|
|
156
|
+
if (!(error instanceof OkmError) || error.kind !== kind)
|
|
157
|
+
return false;
|
|
158
|
+
return table === undefined || error.table === table;
|
|
159
|
+
}
|
|
160
|
+
fields() {
|
|
161
|
+
const cached = this.#fields;
|
|
162
|
+
if (cached !== undefined)
|
|
163
|
+
return cached;
|
|
164
|
+
if (this.columns.length === 0) {
|
|
165
|
+
this.#fields = NO_FIELDS;
|
|
166
|
+
return NO_FIELDS;
|
|
167
|
+
}
|
|
168
|
+
const built = {};
|
|
169
|
+
const reason = this.fieldReason;
|
|
170
|
+
for (const column of this.columns)
|
|
171
|
+
built[column] = reason;
|
|
172
|
+
this.#fields = built;
|
|
173
|
+
return built;
|
|
174
|
+
}
|
|
175
|
+
values() {
|
|
176
|
+
return this.#values;
|
|
177
|
+
}
|
|
178
|
+
toHttp(statuses) {
|
|
179
|
+
const chosen = statuses ?? this.#http;
|
|
180
|
+
const override = chosen?.[this.category];
|
|
181
|
+
return {
|
|
182
|
+
status: override ?? DEFAULT_STATUS[this.category],
|
|
183
|
+
body: {
|
|
184
|
+
code: this.code,
|
|
185
|
+
reason: this.message,
|
|
186
|
+
fields: this.fields()
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
log() {
|
|
191
|
+
return {
|
|
192
|
+
name: "OkmError",
|
|
193
|
+
code: this.code,
|
|
194
|
+
kind: this.kind,
|
|
195
|
+
category: this.category,
|
|
196
|
+
summary: this.message,
|
|
197
|
+
retryable: this.retryable,
|
|
198
|
+
batchIndex: this.batchIndex,
|
|
199
|
+
fix: this.fix,
|
|
200
|
+
fields: this.fields(),
|
|
201
|
+
...this.table !== undefined ? { table: this.table } : {},
|
|
202
|
+
...this.columns.length > 0 ? { columns: this.columns } : {},
|
|
203
|
+
...this.constraint !== undefined ? { constraint: this.constraint } : {},
|
|
204
|
+
...this.sqlstate !== undefined ? { sqlstate: this.sqlstate } : {},
|
|
205
|
+
...this.issues !== undefined ? { issues: this.issues } : {}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
match(handlers) {
|
|
209
|
+
const handler = handlers[this.category];
|
|
210
|
+
if (handler !== undefined)
|
|
211
|
+
return handler(this);
|
|
212
|
+
return handlers._(this);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function safe(pending) {
|
|
216
|
+
return pending.then((value) => ({ ok: true, value }), (error) => ({ ok: false, error: OkmError.from(error) }));
|
|
217
|
+
}
|
|
218
|
+
function catalogError(code, message, options) {
|
|
219
|
+
throw new OkmError(code, message, options);
|
|
220
|
+
}
|
|
221
|
+
function throwNamed(code, input, candidates, message) {
|
|
222
|
+
const suggestion = nearestName(input, candidates);
|
|
223
|
+
if (suggestion === undefined)
|
|
224
|
+
throw new OkmError(code, message);
|
|
225
|
+
throw new OkmError(code, `${message} Did you mean \`${suggestion}\`?`, {
|
|
226
|
+
fix: { summary: `Use \`${suggestion}\`.`, suggestion }
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function stripRowValues(text) {
|
|
230
|
+
let result = "";
|
|
231
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
232
|
+
if (text.charCodeAt(index) === 61 && text.charCodeAt(index + 1) === 40) {
|
|
233
|
+
const end = matchingParen(text, index + 1);
|
|
234
|
+
if (end !== undefined) {
|
|
235
|
+
index = end;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (startsAt(text, index, "contains (")) {
|
|
240
|
+
const open = index + "contains ".length;
|
|
241
|
+
const end = matchingParen(text, open);
|
|
242
|
+
if (end !== undefined) {
|
|
243
|
+
result += "contains (…)";
|
|
244
|
+
index = end;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
result += text.charAt(index);
|
|
249
|
+
}
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
function kindFromCode(code) {
|
|
253
|
+
if (isErrorKind(code))
|
|
254
|
+
return code;
|
|
255
|
+
switch (code) {
|
|
256
|
+
case "OKM1401":
|
|
257
|
+
return "outcome_unknown";
|
|
258
|
+
case "OKM1846":
|
|
259
|
+
return "timeout";
|
|
260
|
+
case "OKM1520":
|
|
261
|
+
return "schema_drift";
|
|
262
|
+
default:
|
|
263
|
+
return "invalid";
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function isErrorKind(code) {
|
|
267
|
+
return Object.hasOwn(CATEGORY_OF, code);
|
|
268
|
+
}
|
|
269
|
+
function defaultRetryable(kind) {
|
|
270
|
+
if (kind === "cancelled" || kind === "outcome_unknown")
|
|
271
|
+
return false;
|
|
272
|
+
return CATEGORY_OF[kind] === "transient";
|
|
273
|
+
}
|
|
274
|
+
function defaultFix(kind) {
|
|
275
|
+
switch (kind) {
|
|
276
|
+
case "cancelled":
|
|
277
|
+
return "Do not retry. The caller aborted the call.";
|
|
278
|
+
case "outcome_unknown":
|
|
279
|
+
return "Check whether the write landed, using an idempotency key, before retrying.";
|
|
280
|
+
case "timeout":
|
|
281
|
+
case "serialization":
|
|
282
|
+
case "deadlock":
|
|
283
|
+
case "lock_timeout":
|
|
284
|
+
case "unavailable":
|
|
285
|
+
return "Retry the call.";
|
|
286
|
+
case "unique":
|
|
287
|
+
case "exclusion":
|
|
288
|
+
case "conflict":
|
|
289
|
+
return "Change the conflicting value, or update the existing row.";
|
|
290
|
+
case "not_null":
|
|
291
|
+
return "Provide a value for the column.";
|
|
292
|
+
case "check":
|
|
293
|
+
case "invalid":
|
|
294
|
+
return "Change the value so it passes the check.";
|
|
295
|
+
case "foreign_key":
|
|
296
|
+
return "Use a key that exists, or insert the referenced row first.";
|
|
297
|
+
case "not_found":
|
|
298
|
+
return "Look up a row that exists.";
|
|
299
|
+
case "not_unique":
|
|
300
|
+
return "Narrow the filter so it matches one row.";
|
|
301
|
+
case "forbidden":
|
|
302
|
+
return "Use a role that is allowed to perform this operation.";
|
|
303
|
+
case "read_only":
|
|
304
|
+
return "Run the write on the primary.";
|
|
305
|
+
case "schema_drift":
|
|
306
|
+
return "Apply the migrations that match this build.";
|
|
307
|
+
case "driver":
|
|
308
|
+
return "Read the summary and correct the call.";
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function isDriverError(error) {
|
|
312
|
+
return error instanceof Error && error.name === "DriverError";
|
|
313
|
+
}
|
|
314
|
+
function fromDriver(error) {
|
|
315
|
+
const kind = error.kind ?? "driver";
|
|
316
|
+
const code = kind === "outcome_unknown" ? "OKM1401" : kind;
|
|
317
|
+
const columns = error.column === undefined || error.column.length === 0 ? undefined : [error.column];
|
|
318
|
+
return new OkmError(code, stripRowValues(error.message), {
|
|
319
|
+
kind,
|
|
320
|
+
...error.table !== undefined ? { table: error.table } : {},
|
|
321
|
+
...columns !== undefined ? { columns } : {},
|
|
322
|
+
...error.constraint !== undefined ? { constraint: error.constraint } : {},
|
|
323
|
+
...error.sqlstate !== undefined ? { sqlstate: error.sqlstate } : {},
|
|
324
|
+
...error.batchIndex !== undefined ? { batchIndex: error.batchIndex } : {},
|
|
325
|
+
cause: error.cause ?? error
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function matchingParen(text, open) {
|
|
329
|
+
let depth = 0;
|
|
330
|
+
for (let index = open;index < text.length; index += 1) {
|
|
331
|
+
const code = text.charCodeAt(index);
|
|
332
|
+
if (code === 40)
|
|
333
|
+
depth += 1;
|
|
334
|
+
else if (code === 41) {
|
|
335
|
+
depth -= 1;
|
|
336
|
+
if (depth === 0)
|
|
337
|
+
return index;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
function startsAt(text, index, needle) {
|
|
343
|
+
if (index + needle.length > text.length)
|
|
344
|
+
return false;
|
|
345
|
+
for (let offset = 0;offset < needle.length; offset += 1) {
|
|
346
|
+
if (text.charCodeAt(index + offset) !== needle.charCodeAt(offset))
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/adapters/error.ts
|
|
353
|
+
class DriverError2 extends Error {
|
|
354
|
+
sqlstate;
|
|
355
|
+
constraint;
|
|
356
|
+
table;
|
|
357
|
+
column;
|
|
358
|
+
detail;
|
|
359
|
+
cause;
|
|
360
|
+
batchIndex;
|
|
361
|
+
kind;
|
|
362
|
+
constructor(message, fields = {}) {
|
|
363
|
+
super(message);
|
|
364
|
+
this.name = "DriverError";
|
|
365
|
+
this.sqlstate = fields.sqlstate;
|
|
366
|
+
this.constraint = fields.constraint;
|
|
367
|
+
this.table = fields.table;
|
|
368
|
+
this.column = fields.column;
|
|
369
|
+
this.detail = fields.detail;
|
|
370
|
+
this.cause = fields.cause;
|
|
371
|
+
this.batchIndex = fields.batchIndex === undefined ? null : fields.batchIndex;
|
|
372
|
+
this.kind = fields.kind;
|
|
373
|
+
}
|
|
374
|
+
at(batchIndex) {
|
|
375
|
+
if (this.batchIndex === batchIndex)
|
|
376
|
+
return this;
|
|
377
|
+
return new DriverError2(this.message, driverFields({
|
|
378
|
+
sqlstate: this.sqlstate,
|
|
379
|
+
constraint: this.constraint,
|
|
380
|
+
table: this.table,
|
|
381
|
+
column: this.column,
|
|
382
|
+
detail: this.detail,
|
|
383
|
+
cause: this.cause,
|
|
384
|
+
kind: this.kind,
|
|
385
|
+
batchIndex
|
|
386
|
+
}));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function cancelled2(cause) {
|
|
390
|
+
return new DriverError2("The call was cancelled.", driverFields({ kind: "cancelled", cause }));
|
|
391
|
+
}
|
|
392
|
+
function timedOut2(cause) {
|
|
393
|
+
return new DriverError2("The call timed out.", driverFields({ kind: "timeout", cause }));
|
|
394
|
+
}
|
|
395
|
+
function outcomeUnknown2(cause) {
|
|
396
|
+
return new DriverError2("The commit outcome is unknown. The result never arrived.", driverFields({ kind: "outcome_unknown", cause, batchIndex: null }));
|
|
397
|
+
}
|
|
398
|
+
function acquireTimeout2() {
|
|
399
|
+
return new OkmError("OKM1846", "Timed out waiting for a connection. This pool did not hand one out within timeouts.acquire.");
|
|
400
|
+
}
|
|
401
|
+
function errorField2(error, key) {
|
|
402
|
+
if (typeof error !== "object" || error === null || !(key in error))
|
|
403
|
+
return;
|
|
404
|
+
const value = Reflect.get(error, key);
|
|
405
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
406
|
+
}
|
|
407
|
+
function sqlstateOf(error) {
|
|
408
|
+
const code = errorField2(error, "code");
|
|
409
|
+
if (isSqlstate(code))
|
|
410
|
+
return code;
|
|
411
|
+
const errno = errorField2(error, "errno");
|
|
412
|
+
if (isSqlstate(errno))
|
|
413
|
+
return errno;
|
|
414
|
+
return errorField2(error, "sqlstate");
|
|
415
|
+
}
|
|
416
|
+
function isSqlstate(value) {
|
|
417
|
+
return value !== undefined && /^[0-9A-Z]{5}$/.test(value);
|
|
418
|
+
}
|
|
419
|
+
function mapDriverError2(error, batchIndex) {
|
|
420
|
+
if (error instanceof OkmError)
|
|
421
|
+
return error;
|
|
422
|
+
if (error instanceof DriverError2) {
|
|
423
|
+
return batchIndex === undefined ? error : error.at(batchIndex);
|
|
424
|
+
}
|
|
425
|
+
const message = messageOf(error);
|
|
426
|
+
return new DriverError2(message, driverFields({
|
|
427
|
+
sqlstate: sqlstateOf(error),
|
|
428
|
+
constraint: errorField2(error, "constraint_name") ?? errorField2(error, "constraint"),
|
|
429
|
+
table: errorField2(error, "table_name") ?? errorField2(error, "table"),
|
|
430
|
+
column: errorField2(error, "column_name") ?? errorField2(error, "column"),
|
|
431
|
+
detail: errorField2(error, "detail"),
|
|
432
|
+
cause: error,
|
|
433
|
+
batchIndex
|
|
434
|
+
}));
|
|
435
|
+
}
|
|
436
|
+
function messageOf(error) {
|
|
437
|
+
if (!(error instanceof Error))
|
|
438
|
+
return "driver error";
|
|
439
|
+
if (error.message.length > 0)
|
|
440
|
+
return error.message;
|
|
441
|
+
const first = error.errors?.[0];
|
|
442
|
+
return first instanceof Error && first.message.length > 0 ? first.message : "driver error";
|
|
443
|
+
}
|
|
444
|
+
function driverFields(input) {
|
|
445
|
+
const fields = {};
|
|
446
|
+
const keys = [
|
|
447
|
+
"sqlstate",
|
|
448
|
+
"constraint",
|
|
449
|
+
"table",
|
|
450
|
+
"column",
|
|
451
|
+
"detail",
|
|
452
|
+
"cause",
|
|
453
|
+
"batchIndex",
|
|
454
|
+
"kind"
|
|
455
|
+
];
|
|
456
|
+
for (const key of keys) {
|
|
457
|
+
const value = input[key];
|
|
458
|
+
if (value !== undefined)
|
|
459
|
+
fields[key] = value;
|
|
460
|
+
}
|
|
461
|
+
return fields;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/contracts/connection.ts
|
|
465
|
+
function isConnectionSqlstate(sqlstate) {
|
|
466
|
+
return sqlstate.startsWith("08") || sqlstate === "53300" || sqlstate === "57P01" || sqlstate === "57P02" || sqlstate === "57P03";
|
|
467
|
+
}
|
|
468
|
+
function isConnectionErrno(code) {
|
|
469
|
+
switch (code) {
|
|
470
|
+
case "ECONNRESET":
|
|
471
|
+
case "ECONNREFUSED":
|
|
472
|
+
case "EPIPE":
|
|
473
|
+
case "ENOTFOUND":
|
|
474
|
+
case "ETIMEDOUT":
|
|
475
|
+
case "CONNECT_TIMEOUT":
|
|
476
|
+
case "EHOSTUNREACH":
|
|
477
|
+
case "ENETUNREACH":
|
|
478
|
+
case "EAI_AGAIN":
|
|
479
|
+
case "CONNECTION_CLOSED":
|
|
480
|
+
case "CONNECTION_ENDED":
|
|
481
|
+
case "CONNECTION_DESTROYED":
|
|
482
|
+
return true;
|
|
483
|
+
default:
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function isConnectionFailure(error) {
|
|
488
|
+
return at(error, true);
|
|
489
|
+
}
|
|
490
|
+
function isConnectionLost(error) {
|
|
491
|
+
return at(error, false);
|
|
492
|
+
}
|
|
493
|
+
function at(error, failure) {
|
|
494
|
+
if (typeof error !== "object" || error === null)
|
|
495
|
+
return false;
|
|
496
|
+
const named = readCode(error, "sqlstate");
|
|
497
|
+
if (isState(named))
|
|
498
|
+
return lostState(named);
|
|
499
|
+
const code = readCode(error, "code");
|
|
500
|
+
const errno = readCode(error, "errno");
|
|
501
|
+
if (code !== undefined && (isConnectionErrno(code) || code === "ERR_POSTGRES_CONNECTION_CLOSED")) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
if (errno !== undefined && isConnectionErrno(errno))
|
|
505
|
+
return true;
|
|
506
|
+
if (isState(code))
|
|
507
|
+
return lostState(code);
|
|
508
|
+
if (isState(errno))
|
|
509
|
+
return lostState(errno);
|
|
510
|
+
if (!failure && Reflect.get(error, "kind") === "outcome_unknown")
|
|
511
|
+
return true;
|
|
512
|
+
const cause = Reflect.get(error, "cause");
|
|
513
|
+
return cause !== undefined && cause !== error && at(cause, failure);
|
|
514
|
+
}
|
|
515
|
+
function lostState(state) {
|
|
516
|
+
return isConnectionSqlstate(state) && state !== "53300";
|
|
517
|
+
}
|
|
518
|
+
function isState(value) {
|
|
519
|
+
return value !== undefined && /^[0-9A-Z]{5}$/.test(value);
|
|
520
|
+
}
|
|
521
|
+
function readCode(error, key) {
|
|
522
|
+
if (typeof error !== "object" || error === null || !(key in error))
|
|
523
|
+
return;
|
|
524
|
+
const value = Reflect.get(error, key);
|
|
525
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/dialects/pg/errors.ts
|
|
529
|
+
function mapPostgresError2(error, options) {
|
|
530
|
+
if (error instanceof OkmError)
|
|
531
|
+
return error;
|
|
532
|
+
if (!isDriverError2(error))
|
|
533
|
+
return OkmError.from(error);
|
|
534
|
+
const search = searchInputError(error);
|
|
535
|
+
if (search !== undefined)
|
|
536
|
+
return search;
|
|
537
|
+
const kind = kindOf(error);
|
|
538
|
+
const columns = columnsOf(error);
|
|
539
|
+
const hint = hintOf(error, kind);
|
|
540
|
+
const fieldReason = hint?.reason ?? (kind === "not_null" ? "not_null" : reasonOf(kind, error.table, error.constraint, columns, options?.schema));
|
|
541
|
+
const values = options?.includeValues === true ? valuesOf(error.detail, columns) : undefined;
|
|
542
|
+
const code = kind === "outcome_unknown" ? "OKM1401" : kind;
|
|
543
|
+
return new OkmError(code, summaryOf(kind, error, columns, fieldReason), {
|
|
544
|
+
kind,
|
|
545
|
+
fieldReason,
|
|
546
|
+
...hint !== undefined ? { fix: hint.fix } : {},
|
|
547
|
+
...error.table !== undefined ? { table: error.table } : {},
|
|
548
|
+
...columns.length > 0 ? { columns } : {},
|
|
549
|
+
...error.constraint !== undefined ? { constraint: error.constraint } : {},
|
|
550
|
+
...error.sqlstate !== undefined ? { sqlstate: error.sqlstate } : {},
|
|
551
|
+
batchIndex: error.batchIndex,
|
|
552
|
+
cause: error.cause ?? error,
|
|
553
|
+
...options?.includeValues === true && values !== undefined ? { includeValues: true, values } : {},
|
|
554
|
+
...options?.http !== undefined ? { http: options.http } : {}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
var TLS_CODE = /^(ERR_TLS_|ERR_POSTGRES_TLS_|SELF_SIGNED_CERT_|DEPTH_ZERO_SELF_SIGNED_CERT$|UNABLE_TO_VERIFY_LEAF_SIGNATURE$)/;
|
|
558
|
+
function isTlsFailure(error) {
|
|
559
|
+
const cause = error.cause;
|
|
560
|
+
const code = typeof cause === "object" && cause !== null ? Reflect.get(cause, "code") : undefined;
|
|
561
|
+
return typeof code === "string" && TLS_CODE.test(code);
|
|
562
|
+
}
|
|
563
|
+
function isDriverError2(error) {
|
|
564
|
+
if (!(error instanceof Error) || error.name !== "DriverError")
|
|
565
|
+
return false;
|
|
566
|
+
return "batchIndex" in error;
|
|
567
|
+
}
|
|
568
|
+
function searchQuery(error) {
|
|
569
|
+
const cause = error.cause;
|
|
570
|
+
if (typeof cause !== "object" || cause === null)
|
|
571
|
+
return false;
|
|
572
|
+
const query = Reflect.get(cause, "query");
|
|
573
|
+
return typeof query === "string" && query.includes("to_tsquery");
|
|
574
|
+
}
|
|
575
|
+
function searchInputError(error) {
|
|
576
|
+
const sqlstate = error.sqlstate;
|
|
577
|
+
const message = error.message;
|
|
578
|
+
if (sqlstate === "42601" && (message.includes("tsquery") || searchQuery(error))) {
|
|
579
|
+
return new OkmError("OKM1120", `Search input is not valid. ${stripRowValues(message)}`, {
|
|
580
|
+
kind: "invalid",
|
|
581
|
+
sqlstate,
|
|
582
|
+
cause: error.cause ?? error,
|
|
583
|
+
fix: { summary: "Fix the advanced search text, or use natural, all-words, or phrase." }
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
if (sqlstate === "42704" && message.includes("text search configuration") && message.includes("does not exist")) {
|
|
587
|
+
return new OkmError("OKM1122", stripRowValues(message), {
|
|
588
|
+
kind: "invalid",
|
|
589
|
+
sqlstate,
|
|
590
|
+
cause: error.cause ?? error,
|
|
591
|
+
fix: {
|
|
592
|
+
summary: "Use a text search configuration that exists, such as english, or omit language."
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
function kindOf(error) {
|
|
599
|
+
if (error.kind === "timeout" || error.kind === "cancelled" || error.kind === "outcome_unknown") {
|
|
600
|
+
return error.kind;
|
|
601
|
+
}
|
|
602
|
+
const sqlstate = error.sqlstate;
|
|
603
|
+
if (sqlstate !== undefined) {
|
|
604
|
+
const mapped = kindFromSqlstate(sqlstate);
|
|
605
|
+
if (mapped !== undefined)
|
|
606
|
+
return mapped;
|
|
607
|
+
}
|
|
608
|
+
if (isTlsFailure(error) || isConnectionFailure(error))
|
|
609
|
+
return "unavailable";
|
|
610
|
+
return "driver";
|
|
611
|
+
}
|
|
612
|
+
var MIGRATE_FIX = {
|
|
613
|
+
summary: "Run okm migrate apply so the object exists, then check okm migrate status. The reason names the missing object."
|
|
614
|
+
};
|
|
615
|
+
var TLS_FIX = {
|
|
616
|
+
summary: "Check sslmode in the connection URL against the server certificate: use require, verify-ca, or verify-full with the CA the certificate chains to. For node-postgres, add uselibpqcompat=true so sslmode follows libpq."
|
|
617
|
+
};
|
|
618
|
+
function hintOf(error, kind) {
|
|
619
|
+
if (error.sqlstate === "42P01")
|
|
620
|
+
return { reason: "undefined_table", fix: MIGRATE_FIX };
|
|
621
|
+
if (error.sqlstate === "42703")
|
|
622
|
+
return { reason: "undefined_column", fix: MIGRATE_FIX };
|
|
623
|
+
const refresh = refreshHint(error);
|
|
624
|
+
if (refresh !== undefined)
|
|
625
|
+
return refresh;
|
|
626
|
+
if (kind === "unavailable" && error.sqlstate === undefined && isTlsFailure(error)) {
|
|
627
|
+
return { reason: "tls", fix: TLS_FIX };
|
|
628
|
+
}
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
function refreshHint(error) {
|
|
632
|
+
if (error.sqlstate === "0A000" && error.message.includes("materialized view is not populated")) {
|
|
633
|
+
return {
|
|
634
|
+
reason: "not_populated",
|
|
635
|
+
fix: { summary: "Refresh once without CONCURRENTLY so the view is populated." }
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
if (error.sqlstate === "55000" && error.message.includes("cannot refresh materialized view") && error.message.includes("concurrently")) {
|
|
639
|
+
return {
|
|
640
|
+
reason: "no_unique_index",
|
|
641
|
+
fix: {
|
|
642
|
+
summary: "Add a unique index with no WHERE clause, or refresh without CONCURRENTLY."
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
if (error.sqlstate === "42501" && error.message.includes("materialized view")) {
|
|
647
|
+
return {
|
|
648
|
+
reason: "not_owner",
|
|
649
|
+
fix: {
|
|
650
|
+
summary: "Refresh runs as the owner, the migration role. Connect as that role and call refresh(), or run REFRESH MATERIALIZED VIEW as that role. The application role is not the owner."
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
function kindFromSqlstate(sqlstate) {
|
|
657
|
+
switch (sqlstate) {
|
|
658
|
+
case "23505":
|
|
659
|
+
return "unique";
|
|
660
|
+
case "23502":
|
|
661
|
+
return "not_null";
|
|
662
|
+
case "23514":
|
|
663
|
+
return "check";
|
|
664
|
+
case "23503":
|
|
665
|
+
case "23001":
|
|
666
|
+
return "foreign_key";
|
|
667
|
+
case "23P01":
|
|
668
|
+
return "exclusion";
|
|
669
|
+
case "23000":
|
|
670
|
+
return "conflict";
|
|
671
|
+
case "40001":
|
|
672
|
+
return "serialization";
|
|
673
|
+
case "40P01":
|
|
674
|
+
return "deadlock";
|
|
675
|
+
case "55P03":
|
|
676
|
+
return "lock_timeout";
|
|
677
|
+
case "57014":
|
|
678
|
+
return "timeout";
|
|
679
|
+
case "40003":
|
|
680
|
+
return "outcome_unknown";
|
|
681
|
+
case "25006":
|
|
682
|
+
return "read_only";
|
|
683
|
+
default:
|
|
684
|
+
if (isConnectionSqlstate(sqlstate))
|
|
685
|
+
return "unavailable";
|
|
686
|
+
if (sqlstate.startsWith("22"))
|
|
687
|
+
return "invalid";
|
|
688
|
+
if (sqlstate.startsWith("53"))
|
|
689
|
+
return "unavailable";
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function reasonOf(kind, table, constraint, columns, schema) {
|
|
694
|
+
if (schema !== undefined && schema.catalog !== undefined && table !== undefined) {
|
|
695
|
+
const matched = matchObject(schema.catalog, table, constraint, columns);
|
|
696
|
+
if (matched !== undefined)
|
|
697
|
+
return reasonFromObject(schema, table, matched, columns);
|
|
698
|
+
}
|
|
699
|
+
return reasonFromName(kind, table, constraint);
|
|
700
|
+
}
|
|
701
|
+
function reasonFromName(kind, table, constraint) {
|
|
702
|
+
if (constraint === undefined || constraint.length === 0)
|
|
703
|
+
return kind;
|
|
704
|
+
if (table !== undefined) {
|
|
705
|
+
if (constraint === `${table}_pkey`)
|
|
706
|
+
return "pkey";
|
|
707
|
+
const prefix = `${table}_`;
|
|
708
|
+
if (constraint.startsWith(prefix)) {
|
|
709
|
+
const rest = constraint.slice(prefix.length);
|
|
710
|
+
const nameKey = nameKeyOf(rest);
|
|
711
|
+
if (nameKey !== undefined)
|
|
712
|
+
return nameKey;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return constraint;
|
|
716
|
+
}
|
|
717
|
+
function matchObject(catalog, table, constraint, columns) {
|
|
718
|
+
const rows = [];
|
|
719
|
+
for (const object of catalog.objects) {
|
|
720
|
+
if (object.kind !== "constraint" && object.kind !== "index")
|
|
721
|
+
continue;
|
|
722
|
+
if (object.identity.parent.name !== table)
|
|
723
|
+
continue;
|
|
724
|
+
rows.push(object);
|
|
725
|
+
}
|
|
726
|
+
if (columns.length > 0) {
|
|
727
|
+
const key = columns.join(",");
|
|
728
|
+
const hits = rows.filter((object) => object.definition.columns.join(",") === key);
|
|
729
|
+
if (constraint !== undefined) {
|
|
730
|
+
const named = hits.find((object) => object.identity.name === constraint);
|
|
731
|
+
if (named !== undefined)
|
|
732
|
+
return named;
|
|
733
|
+
}
|
|
734
|
+
const only = hits.length === 1 ? hits[0] : undefined;
|
|
735
|
+
if (only !== undefined)
|
|
736
|
+
return only;
|
|
737
|
+
}
|
|
738
|
+
if (constraint === undefined)
|
|
739
|
+
return;
|
|
740
|
+
return rows.find((object) => object.identity.name === constraint);
|
|
741
|
+
}
|
|
742
|
+
function reasonFromObject(schema, table, object, columns) {
|
|
743
|
+
if (object.kind === "index")
|
|
744
|
+
return object.identity.name;
|
|
745
|
+
const nameKey = object.definition.nameKey;
|
|
746
|
+
if (columns.length === 0)
|
|
747
|
+
return nameKey;
|
|
748
|
+
const sqlKey = object.definition.columns.join("_");
|
|
749
|
+
if (nameKey !== sqlKey || object.definition.columns.length < 2)
|
|
750
|
+
return nameKey;
|
|
751
|
+
return fieldKey(schema, table, object.definition.columns) ?? nameKey;
|
|
752
|
+
}
|
|
753
|
+
function fieldKey(schema, table, columns) {
|
|
754
|
+
let model;
|
|
755
|
+
for (const item of Object.values(schema.model)) {
|
|
756
|
+
if (item.sql === table) {
|
|
757
|
+
model = item;
|
|
758
|
+
break;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
if (model === undefined)
|
|
762
|
+
return;
|
|
763
|
+
const fields = [];
|
|
764
|
+
for (const column of columns) {
|
|
765
|
+
const found = model.columns.find((item) => item.sql === column);
|
|
766
|
+
if (found === undefined)
|
|
767
|
+
return;
|
|
768
|
+
fields.push(found.field);
|
|
769
|
+
}
|
|
770
|
+
return fields.join("_");
|
|
771
|
+
}
|
|
772
|
+
function nameKeyOf(rest) {
|
|
773
|
+
const tags = ["_fkey", "_check", "_key"];
|
|
774
|
+
for (const tag of tags) {
|
|
775
|
+
if (rest.endsWith(tag) && rest.length > tag.length)
|
|
776
|
+
return rest.slice(0, -tag.length);
|
|
777
|
+
}
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
780
|
+
function columnsOf(error) {
|
|
781
|
+
if (error.column !== undefined && error.column.length > 0)
|
|
782
|
+
return [unquote(error.column)];
|
|
783
|
+
const detail = error.detail;
|
|
784
|
+
if (detail !== undefined) {
|
|
785
|
+
const fromKey = keyColumns(detail);
|
|
786
|
+
if (fromKey.length > 0)
|
|
787
|
+
return fromKey;
|
|
788
|
+
}
|
|
789
|
+
const fromMessage = quotedColumn(error.message);
|
|
790
|
+
return fromMessage === undefined ? [] : [fromMessage];
|
|
791
|
+
}
|
|
792
|
+
function keyColumns(detail) {
|
|
793
|
+
const marker = "Key (";
|
|
794
|
+
const start = detail.indexOf(marker);
|
|
795
|
+
if (start === -1)
|
|
796
|
+
return [];
|
|
797
|
+
const open = start + marker.length - 1;
|
|
798
|
+
const end = matchingParen(detail, open);
|
|
799
|
+
if (end === undefined)
|
|
800
|
+
return [];
|
|
801
|
+
return splitList(detail.slice(open + 1, end));
|
|
802
|
+
}
|
|
803
|
+
function valuesOf(detail, columns) {
|
|
804
|
+
if (detail === undefined || columns.length === 0)
|
|
805
|
+
return;
|
|
806
|
+
const marker = "=(";
|
|
807
|
+
const start = detail.indexOf(marker);
|
|
808
|
+
if (start === -1)
|
|
809
|
+
return;
|
|
810
|
+
const open = start + 1;
|
|
811
|
+
const end = matchingParen(detail, open);
|
|
812
|
+
if (end === undefined)
|
|
813
|
+
return;
|
|
814
|
+
const parts = splitList(detail.slice(open + 1, end));
|
|
815
|
+
if (parts.length !== columns.length)
|
|
816
|
+
return;
|
|
817
|
+
const values = {};
|
|
818
|
+
for (let index = 0;index < columns.length; index += 1) {
|
|
819
|
+
const column = columns[index];
|
|
820
|
+
const value = parts[index];
|
|
821
|
+
if (column === undefined || value === undefined)
|
|
822
|
+
continue;
|
|
823
|
+
values[column] = value;
|
|
824
|
+
}
|
|
825
|
+
return values;
|
|
826
|
+
}
|
|
827
|
+
function quotedColumn(message) {
|
|
828
|
+
const marker = 'column "';
|
|
829
|
+
const start = message.indexOf(marker);
|
|
830
|
+
if (start === -1)
|
|
831
|
+
return;
|
|
832
|
+
const from = start + marker.length;
|
|
833
|
+
const end = message.indexOf('"', from);
|
|
834
|
+
if (end === -1)
|
|
835
|
+
return;
|
|
836
|
+
return message.slice(from, end);
|
|
837
|
+
}
|
|
838
|
+
function splitList(inner) {
|
|
839
|
+
const parts = inner.split(",");
|
|
840
|
+
const names = [];
|
|
841
|
+
for (const part of parts) {
|
|
842
|
+
const name = unquote(part.trim());
|
|
843
|
+
if (name.length > 0)
|
|
844
|
+
names.push(name);
|
|
845
|
+
}
|
|
846
|
+
return names;
|
|
847
|
+
}
|
|
848
|
+
function unquote(name) {
|
|
849
|
+
if (name.startsWith('"') && name.endsWith('"') && name.length >= 2) {
|
|
850
|
+
return name.slice(1, -1);
|
|
851
|
+
}
|
|
852
|
+
return name;
|
|
853
|
+
}
|
|
854
|
+
function summaryOf(kind, error, columns, fieldReason) {
|
|
855
|
+
const label = labelOf(kind);
|
|
856
|
+
if (kind === "driver" && fieldReason === "not_owner") {
|
|
857
|
+
return "The application role cannot refresh a materialized view. Refresh runs as the owner, the migration role.";
|
|
858
|
+
}
|
|
859
|
+
if (kind === "driver")
|
|
860
|
+
return stripRowValues(error.message);
|
|
861
|
+
const where = place(error.table, columns);
|
|
862
|
+
if (where === undefined)
|
|
863
|
+
return label;
|
|
864
|
+
if (fieldReason === kind)
|
|
865
|
+
return `${label} on ${where}`;
|
|
866
|
+
return `${label} on ${where} (${fieldReason})`;
|
|
867
|
+
}
|
|
868
|
+
function place(table, columns) {
|
|
869
|
+
if (table === undefined && columns.length === 0)
|
|
870
|
+
return;
|
|
871
|
+
if (table === undefined)
|
|
872
|
+
return columns.join(", ");
|
|
873
|
+
if (columns.length === 0)
|
|
874
|
+
return table;
|
|
875
|
+
if (columns.length === 1)
|
|
876
|
+
return `${table}.${columns[0] ?? ""}`;
|
|
877
|
+
return `${table} (${columns.join(", ")})`;
|
|
878
|
+
}
|
|
879
|
+
function labelOf(kind) {
|
|
880
|
+
switch (kind) {
|
|
881
|
+
case "unique":
|
|
882
|
+
return "Unique violation";
|
|
883
|
+
case "not_null":
|
|
884
|
+
return "Not-null violation";
|
|
885
|
+
case "check":
|
|
886
|
+
return "Check violation";
|
|
887
|
+
case "foreign_key":
|
|
888
|
+
return "Foreign key violation";
|
|
889
|
+
case "exclusion":
|
|
890
|
+
return "Exclusion violation";
|
|
891
|
+
case "conflict":
|
|
892
|
+
return "Conflict";
|
|
893
|
+
case "invalid":
|
|
894
|
+
return "Invalid value";
|
|
895
|
+
case "serialization":
|
|
896
|
+
return "Serialization failure";
|
|
897
|
+
case "deadlock":
|
|
898
|
+
return "Deadlock";
|
|
899
|
+
case "lock_timeout":
|
|
900
|
+
return "Lock timeout";
|
|
901
|
+
case "timeout":
|
|
902
|
+
return "The call timed out";
|
|
903
|
+
case "cancelled":
|
|
904
|
+
return "The call was cancelled";
|
|
905
|
+
case "unavailable":
|
|
906
|
+
return "The database is unavailable";
|
|
907
|
+
case "outcome_unknown":
|
|
908
|
+
return "The commit outcome is unknown";
|
|
909
|
+
case "read_only":
|
|
910
|
+
return "The database is read-only";
|
|
911
|
+
case "schema_drift":
|
|
912
|
+
return "The database schema does not match this build";
|
|
913
|
+
case "not_found":
|
|
914
|
+
return "No row was found";
|
|
915
|
+
case "not_unique":
|
|
916
|
+
return "More than one row was found";
|
|
917
|
+
case "forbidden":
|
|
918
|
+
return "The operation is forbidden";
|
|
919
|
+
case "driver":
|
|
920
|
+
return "Driver error";
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/contracts/utf8.ts
|
|
925
|
+
function utf8ByteLength(value) {
|
|
926
|
+
const length = value.length;
|
|
927
|
+
for (let index = 0;index < length; index += 1) {
|
|
928
|
+
if (value.charCodeAt(index) > 127) {
|
|
929
|
+
return utf8ByteLengthFull(value);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return length;
|
|
933
|
+
}
|
|
934
|
+
function utf8ByteLengthFull(value) {
|
|
935
|
+
let bytes = 0;
|
|
936
|
+
for (let index = 0;index < value.length; ) {
|
|
937
|
+
const unit = readCode2(value, index);
|
|
938
|
+
bytes += utf8Width(unit.code);
|
|
939
|
+
index += unit.units;
|
|
940
|
+
}
|
|
941
|
+
return bytes;
|
|
942
|
+
}
|
|
943
|
+
function encodeUtf8(value) {
|
|
944
|
+
const bytes = new Uint8Array(value.length);
|
|
945
|
+
for (let index = 0;index < value.length; index++) {
|
|
946
|
+
const code = value.charCodeAt(index);
|
|
947
|
+
if (code > 127) {
|
|
948
|
+
return encodeUtf8Slow(value);
|
|
949
|
+
}
|
|
950
|
+
bytes[index] = code;
|
|
951
|
+
}
|
|
952
|
+
return bytes;
|
|
953
|
+
}
|
|
954
|
+
function truncateUtf8(value, budget) {
|
|
955
|
+
if (budget <= 0) {
|
|
956
|
+
return "";
|
|
957
|
+
}
|
|
958
|
+
let bytes = 0;
|
|
959
|
+
let end = 0;
|
|
960
|
+
for (let index = 0;index < value.length; ) {
|
|
961
|
+
const unit = readCode2(value, index);
|
|
962
|
+
const width = utf8Width(unit.code);
|
|
963
|
+
if (bytes + width > budget) {
|
|
964
|
+
break;
|
|
965
|
+
}
|
|
966
|
+
bytes += width;
|
|
967
|
+
index += unit.units;
|
|
968
|
+
end = index;
|
|
969
|
+
}
|
|
970
|
+
return value.slice(0, end);
|
|
971
|
+
}
|
|
972
|
+
function encodeUtf8Slow(value) {
|
|
973
|
+
const bytes = new Uint8Array(utf8ByteLength(value));
|
|
974
|
+
let offset = 0;
|
|
975
|
+
for (let index = 0;index < value.length; ) {
|
|
976
|
+
const unit = readCode2(value, index);
|
|
977
|
+
offset = writeUtf8(bytes, offset, unit.code);
|
|
978
|
+
index += unit.units;
|
|
979
|
+
}
|
|
980
|
+
return bytes;
|
|
981
|
+
}
|
|
982
|
+
function readCode2(value, index) {
|
|
983
|
+
const lead = value.charCodeAt(index);
|
|
984
|
+
const next = value.charCodeAt(index + 1);
|
|
985
|
+
if (lead >= 55296 && lead <= 56319 && next >= 56320 && next <= 57343) {
|
|
986
|
+
return {
|
|
987
|
+
code: 65536 + (lead - 55296 << 10) + (next - 56320),
|
|
988
|
+
units: 2
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
return { code: lead, units: 1 };
|
|
992
|
+
}
|
|
993
|
+
function utf8Width(code) {
|
|
994
|
+
if (code <= 127) {
|
|
995
|
+
return 1;
|
|
996
|
+
}
|
|
997
|
+
if (code <= 2047) {
|
|
998
|
+
return 2;
|
|
999
|
+
}
|
|
1000
|
+
if (code <= 65535) {
|
|
1001
|
+
return 3;
|
|
1002
|
+
}
|
|
1003
|
+
return 4;
|
|
1004
|
+
}
|
|
1005
|
+
function writeUtf8(bytes, offset, code) {
|
|
1006
|
+
if (code <= 127) {
|
|
1007
|
+
bytes[offset] = code;
|
|
1008
|
+
return offset + 1;
|
|
1009
|
+
}
|
|
1010
|
+
if (code <= 2047) {
|
|
1011
|
+
bytes[offset] = 192 | code >> 6;
|
|
1012
|
+
bytes[offset + 1] = 128 | code & 63;
|
|
1013
|
+
return offset + 2;
|
|
1014
|
+
}
|
|
1015
|
+
if (code <= 65535) {
|
|
1016
|
+
bytes[offset] = 224 | code >> 12;
|
|
1017
|
+
bytes[offset + 1] = 128 | code >> 6 & 63;
|
|
1018
|
+
bytes[offset + 2] = 128 | code & 63;
|
|
1019
|
+
return offset + 3;
|
|
1020
|
+
}
|
|
1021
|
+
bytes[offset] = 240 | code >> 18;
|
|
1022
|
+
bytes[offset + 1] = 128 | code >> 12 & 63;
|
|
1023
|
+
bytes[offset + 2] = 128 | code >> 6 & 63;
|
|
1024
|
+
bytes[offset + 3] = 128 | code & 63;
|
|
1025
|
+
return offset + 4;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// src/contracts/sha256.ts
|
|
1029
|
+
var HEX = "0123456789abcdef";
|
|
1030
|
+
var constants;
|
|
1031
|
+
function sha256(text) {
|
|
1032
|
+
return toHex(sha256Bytes(encodeUtf8(text)));
|
|
1033
|
+
}
|
|
1034
|
+
function roundConstants() {
|
|
1035
|
+
constants ??= new Uint32Array([
|
|
1036
|
+
1116352408,
|
|
1037
|
+
1899447441,
|
|
1038
|
+
3049323471,
|
|
1039
|
+
3921009573,
|
|
1040
|
+
961987163,
|
|
1041
|
+
1508970993,
|
|
1042
|
+
2453635748,
|
|
1043
|
+
2870763221,
|
|
1044
|
+
3624381080,
|
|
1045
|
+
310598401,
|
|
1046
|
+
607225278,
|
|
1047
|
+
1426881987,
|
|
1048
|
+
1925078388,
|
|
1049
|
+
2162078206,
|
|
1050
|
+
2614888103,
|
|
1051
|
+
3248222580,
|
|
1052
|
+
3835390401,
|
|
1053
|
+
4022224774,
|
|
1054
|
+
264347078,
|
|
1055
|
+
604807628,
|
|
1056
|
+
770255983,
|
|
1057
|
+
1249150122,
|
|
1058
|
+
1555081692,
|
|
1059
|
+
1996064986,
|
|
1060
|
+
2554220882,
|
|
1061
|
+
2821834349,
|
|
1062
|
+
2952996808,
|
|
1063
|
+
3210313671,
|
|
1064
|
+
3336571891,
|
|
1065
|
+
3584528711,
|
|
1066
|
+
113926993,
|
|
1067
|
+
338241895,
|
|
1068
|
+
666307205,
|
|
1069
|
+
773529912,
|
|
1070
|
+
1294757372,
|
|
1071
|
+
1396182291,
|
|
1072
|
+
1695183700,
|
|
1073
|
+
1986661051,
|
|
1074
|
+
2177026350,
|
|
1075
|
+
2456956037,
|
|
1076
|
+
2730485921,
|
|
1077
|
+
2820302411,
|
|
1078
|
+
3259730800,
|
|
1079
|
+
3345764771,
|
|
1080
|
+
3516065817,
|
|
1081
|
+
3600352804,
|
|
1082
|
+
4094571909,
|
|
1083
|
+
275423344,
|
|
1084
|
+
430227734,
|
|
1085
|
+
506948616,
|
|
1086
|
+
659060556,
|
|
1087
|
+
883997877,
|
|
1088
|
+
958139571,
|
|
1089
|
+
1322822218,
|
|
1090
|
+
1537002063,
|
|
1091
|
+
1747873779,
|
|
1092
|
+
1955562222,
|
|
1093
|
+
2024104815,
|
|
1094
|
+
2227730452,
|
|
1095
|
+
2361852424,
|
|
1096
|
+
2428436474,
|
|
1097
|
+
2756734187,
|
|
1098
|
+
3204031479,
|
|
1099
|
+
3329325298
|
|
1100
|
+
]);
|
|
1101
|
+
return constants;
|
|
1102
|
+
}
|
|
1103
|
+
function sha256Bytes(message) {
|
|
1104
|
+
const k = roundConstants();
|
|
1105
|
+
const bitLength = message.length * 8;
|
|
1106
|
+
const pad = (64 - (message.length + 9) % 64) % 64;
|
|
1107
|
+
const total = message.length + 1 + pad + 8;
|
|
1108
|
+
const data = new Uint8Array(total);
|
|
1109
|
+
data.set(message);
|
|
1110
|
+
data[message.length] = 128;
|
|
1111
|
+
const view = new DataView(data.buffer);
|
|
1112
|
+
view.setUint32(total - 8, Math.floor(bitLength / 4294967296));
|
|
1113
|
+
view.setUint32(total - 4, bitLength >>> 0);
|
|
1114
|
+
let h0 = 1779033703;
|
|
1115
|
+
let h1 = 3144134277;
|
|
1116
|
+
let h2 = 1013904242;
|
|
1117
|
+
let h3 = 2773480762;
|
|
1118
|
+
let h4 = 1359893119;
|
|
1119
|
+
let h5 = 2600822924;
|
|
1120
|
+
let h6 = 528734635;
|
|
1121
|
+
let h7 = 1541459225;
|
|
1122
|
+
const w = new Uint32Array(64);
|
|
1123
|
+
for (let offset = 0;offset < data.length; offset += 64) {
|
|
1124
|
+
for (let index = 0;index < 16; index++) {
|
|
1125
|
+
w[index] = view.getUint32(offset + index * 4);
|
|
1126
|
+
}
|
|
1127
|
+
for (let index = 16;index < 64; index++) {
|
|
1128
|
+
const w15 = w[index - 15] ?? 0;
|
|
1129
|
+
const w2 = w[index - 2] ?? 0;
|
|
1130
|
+
const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ w15 >>> 3;
|
|
1131
|
+
const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ w2 >>> 10;
|
|
1132
|
+
w[index] = (w[index - 16] ?? 0) + s0 + (w[index - 7] ?? 0) + s1 >>> 0;
|
|
1133
|
+
}
|
|
1134
|
+
let a = h0;
|
|
1135
|
+
let b = h1;
|
|
1136
|
+
let c = h2;
|
|
1137
|
+
let d = h3;
|
|
1138
|
+
let e = h4;
|
|
1139
|
+
let f = h5;
|
|
1140
|
+
let g = h6;
|
|
1141
|
+
let h = h7;
|
|
1142
|
+
for (let index = 0;index < 64; index++) {
|
|
1143
|
+
const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
1144
|
+
const ch = e & f ^ ~e & g;
|
|
1145
|
+
const temp1 = h + s1 + ch + (k[index] ?? 0) + (w[index] ?? 0) >>> 0;
|
|
1146
|
+
const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
1147
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
1148
|
+
const temp2 = s0 + maj >>> 0;
|
|
1149
|
+
h = g;
|
|
1150
|
+
g = f;
|
|
1151
|
+
f = e;
|
|
1152
|
+
e = d + temp1 >>> 0;
|
|
1153
|
+
d = c;
|
|
1154
|
+
c = b;
|
|
1155
|
+
b = a;
|
|
1156
|
+
a = temp1 + temp2 >>> 0;
|
|
1157
|
+
}
|
|
1158
|
+
h0 = h0 + a >>> 0;
|
|
1159
|
+
h1 = h1 + b >>> 0;
|
|
1160
|
+
h2 = h2 + c >>> 0;
|
|
1161
|
+
h3 = h3 + d >>> 0;
|
|
1162
|
+
h4 = h4 + e >>> 0;
|
|
1163
|
+
h5 = h5 + f >>> 0;
|
|
1164
|
+
h6 = h6 + g >>> 0;
|
|
1165
|
+
h7 = h7 + h >>> 0;
|
|
1166
|
+
}
|
|
1167
|
+
const out = new Uint8Array(32);
|
|
1168
|
+
const outView = new DataView(out.buffer);
|
|
1169
|
+
outView.setUint32(0, h0);
|
|
1170
|
+
outView.setUint32(4, h1);
|
|
1171
|
+
outView.setUint32(8, h2);
|
|
1172
|
+
outView.setUint32(12, h3);
|
|
1173
|
+
outView.setUint32(16, h4);
|
|
1174
|
+
outView.setUint32(20, h5);
|
|
1175
|
+
outView.setUint32(24, h6);
|
|
1176
|
+
outView.setUint32(28, h7);
|
|
1177
|
+
return out;
|
|
1178
|
+
}
|
|
1179
|
+
function rotr(value, bits) {
|
|
1180
|
+
return value >>> bits | value << 32 - bits;
|
|
1181
|
+
}
|
|
1182
|
+
function toHex(bytes) {
|
|
1183
|
+
let hex = "";
|
|
1184
|
+
for (let index = 0;index < bytes.length; index++) {
|
|
1185
|
+
const byte = bytes[index] ?? 0;
|
|
1186
|
+
hex += HEX[byte >>> 4];
|
|
1187
|
+
hex += HEX[byte & 15];
|
|
1188
|
+
}
|
|
1189
|
+
return hex;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// src/contracts/catalog/words.ts
|
|
1193
|
+
var words;
|
|
1194
|
+
function reservedWords() {
|
|
1195
|
+
words ??= new Set("all analyse analyze and any array as asc asymmetric authorization binary both case cast check collate collation column concurrently constraint create cross current_catalog current_date current_role current_schema current_time current_timestamp current_user default deferrable desc distinct do else end except false fetch for foreign freeze from full grant group having ilike in initially inner intersect into is isnull join lateral leading left like limit localtime localtimestamp natural not notnull null offset on only or order outer overlaps placing primary references returning right select session_user similar some symmetric system_user table tablesample then to trailing true union unique user using variadic verbose when where window with".split(" "));
|
|
1196
|
+
return words;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/contracts/catalog/identifier.ts
|
|
1200
|
+
var POSTGRES_IDENTIFIER_MAX_BYTES = 63;
|
|
1201
|
+
var SUFFIX_HEX = 8;
|
|
1202
|
+
var SUFFIX_BYTES = SUFFIX_HEX + 1;
|
|
1203
|
+
var NAME_TAG = {
|
|
1204
|
+
unique: "key",
|
|
1205
|
+
foreignKey: "fkey",
|
|
1206
|
+
check: "check",
|
|
1207
|
+
index: "idx"
|
|
1208
|
+
};
|
|
1209
|
+
function isReservedIdentifier(name) {
|
|
1210
|
+
return reservedWords().has(name.toLowerCase());
|
|
1211
|
+
}
|
|
1212
|
+
function assertIdentifier(name, role, limit = POSTGRES_IDENTIFIER_MAX_BYTES) {
|
|
1213
|
+
assertLimit(limit);
|
|
1214
|
+
assertIdentifierText(name, role);
|
|
1215
|
+
if (utf8ByteLength(name) > limit) {
|
|
1216
|
+
catalogError("OKM1122", `${role} ${name} is ${String(utf8ByteLength(name))} bytes. The limit is ${String(limit)}.`);
|
|
1217
|
+
}
|
|
1218
|
+
if (isReservedIdentifier(name)) {
|
|
1219
|
+
catalogError("OKM1122", `${role} ${name} is a reserved word.`);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
function fitIdentifier(name, limit = POSTGRES_IDENTIFIER_MAX_BYTES) {
|
|
1223
|
+
assertLimit(limit);
|
|
1224
|
+
assertIdentifierText(name, "identifier");
|
|
1225
|
+
if (utf8ByteLength(name) <= limit) {
|
|
1226
|
+
return name;
|
|
1227
|
+
}
|
|
1228
|
+
const suffix = `_${sha256(name).slice(0, SUFFIX_HEX)}`;
|
|
1229
|
+
const budget = limit - SUFFIX_BYTES;
|
|
1230
|
+
return `${truncateUtf8(name, budget)}${suffix}`;
|
|
1231
|
+
}
|
|
1232
|
+
function deterministicName(input) {
|
|
1233
|
+
const limit = input.limit ?? POSTGRES_IDENTIFIER_MAX_BYTES;
|
|
1234
|
+
assertIdentifier(input.parent, "parent", limit);
|
|
1235
|
+
if (input.purpose === "primaryKey") {
|
|
1236
|
+
return fitIdentifier(`${input.parent}_pkey`, limit);
|
|
1237
|
+
}
|
|
1238
|
+
const nameKey = input.nameKey ?? "";
|
|
1239
|
+
assertIdentifierText(nameKey, "name key");
|
|
1240
|
+
return fitIdentifier(`${input.parent}_${nameKey}_${NAME_TAG[input.purpose]}`, limit);
|
|
1241
|
+
}
|
|
1242
|
+
function assertStoredText(value, role) {
|
|
1243
|
+
if (value.includes("\x00") || hasControl(value)) {
|
|
1244
|
+
catalogError("OKM1122", `${role} contains a control character.`);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
function assertIdentifierText(name, role) {
|
|
1248
|
+
if (name.length === 0) {
|
|
1249
|
+
catalogError("OKM1122", `${role} is empty.`);
|
|
1250
|
+
}
|
|
1251
|
+
if (name.includes("\x00")) {
|
|
1252
|
+
catalogError("OKM1122", `${role} ${name} contains NUL.`);
|
|
1253
|
+
}
|
|
1254
|
+
if (hasControl(name)) {
|
|
1255
|
+
catalogError("OKM1122", `${role} ${name} contains a control character.`);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
function assertLimit(limit) {
|
|
1259
|
+
if (!Number.isSafeInteger(limit) || limit < SUFFIX_BYTES) {
|
|
1260
|
+
catalogError("OKM1122", `Identifier limit is ${String(limit)} bytes. It must be at least ${String(SUFFIX_BYTES)} so a fitted name can keep its hash suffix.`);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
function hasControl(value) {
|
|
1264
|
+
for (let index = 0;index < value.length; index++) {
|
|
1265
|
+
const code = value.charCodeAt(index);
|
|
1266
|
+
if (code < 32 || code === 127 || code >= 128 && code <= 159) {
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// src/contracts/catalog/json.ts
|
|
1274
|
+
function canonicalJson(value) {
|
|
1275
|
+
return encode(value);
|
|
1276
|
+
}
|
|
1277
|
+
function encode(value) {
|
|
1278
|
+
if (value === null) {
|
|
1279
|
+
return "null";
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof value === "boolean") {
|
|
1282
|
+
return value ? "true" : "false";
|
|
1283
|
+
}
|
|
1284
|
+
if (typeof value === "number") {
|
|
1285
|
+
if (!Number.isSafeInteger(value)) {
|
|
1286
|
+
catalogError("OKM1020", "Catalog JSON cannot encode a non-integer number.");
|
|
1287
|
+
}
|
|
1288
|
+
return String(value);
|
|
1289
|
+
}
|
|
1290
|
+
if (typeof value === "string") {
|
|
1291
|
+
return encodeString(value);
|
|
1292
|
+
}
|
|
1293
|
+
if (Array.isArray(value)) {
|
|
1294
|
+
return `[${value.map((item) => encode(item)).join(",")}]`;
|
|
1295
|
+
}
|
|
1296
|
+
if (!isJsonRecord(value)) {
|
|
1297
|
+
catalogError("OKM1020", "Catalog JSON cannot encode this value.");
|
|
1298
|
+
}
|
|
1299
|
+
const keys = Object.keys(value).sort();
|
|
1300
|
+
const parts = [];
|
|
1301
|
+
for (const key of keys) {
|
|
1302
|
+
const item = value[key];
|
|
1303
|
+
if (item === undefined) {
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
parts.push(`${encodeString(key)}:${encode(item)}`);
|
|
1307
|
+
}
|
|
1308
|
+
return `{${parts.join(",")}}`;
|
|
1309
|
+
}
|
|
1310
|
+
function isJsonRecord(value) {
|
|
1311
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1312
|
+
}
|
|
1313
|
+
function encodeString(value) {
|
|
1314
|
+
for (let index = 0;index < value.length; index++) {
|
|
1315
|
+
const code = value.charCodeAt(index);
|
|
1316
|
+
if (code === 34 || code === 92 || code < 32 || code > 126) {
|
|
1317
|
+
return encodeEscaped(value);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
return `"${value}"`;
|
|
1321
|
+
}
|
|
1322
|
+
function encodeEscaped(value) {
|
|
1323
|
+
const parts = ['"'];
|
|
1324
|
+
let start = 0;
|
|
1325
|
+
for (let index = 0;index < value.length; ) {
|
|
1326
|
+
const code = value.charCodeAt(index);
|
|
1327
|
+
const next = value.charCodeAt(index + 1);
|
|
1328
|
+
if (code >= 55296 && code <= 56319 && next >= 56320 && next <= 57343) {
|
|
1329
|
+
if (index > start) {
|
|
1330
|
+
parts.push(value.slice(start, index));
|
|
1331
|
+
}
|
|
1332
|
+
parts.push(hexEscape(code), hexEscape(next));
|
|
1333
|
+
index += 2;
|
|
1334
|
+
start = index;
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
if (needsEscape(code)) {
|
|
1338
|
+
if (index > start) {
|
|
1339
|
+
parts.push(value.slice(start, index));
|
|
1340
|
+
}
|
|
1341
|
+
parts.push(escapeUnit(code));
|
|
1342
|
+
index += 1;
|
|
1343
|
+
start = index;
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
index += 1;
|
|
1347
|
+
}
|
|
1348
|
+
if (start < value.length) {
|
|
1349
|
+
parts.push(value.slice(start));
|
|
1350
|
+
}
|
|
1351
|
+
parts.push('"');
|
|
1352
|
+
return parts.join("");
|
|
1353
|
+
}
|
|
1354
|
+
function needsEscape(code) {
|
|
1355
|
+
return code === 34 || code === 92 || code < 32 || code > 126;
|
|
1356
|
+
}
|
|
1357
|
+
function escapeUnit(code) {
|
|
1358
|
+
if (code === 34) {
|
|
1359
|
+
return "\\\"";
|
|
1360
|
+
}
|
|
1361
|
+
if (code === 92) {
|
|
1362
|
+
return "\\\\";
|
|
1363
|
+
}
|
|
1364
|
+
if (code === 8) {
|
|
1365
|
+
return "\\b";
|
|
1366
|
+
}
|
|
1367
|
+
if (code === 12) {
|
|
1368
|
+
return "\\f";
|
|
1369
|
+
}
|
|
1370
|
+
if (code === 10) {
|
|
1371
|
+
return "\\n";
|
|
1372
|
+
}
|
|
1373
|
+
if (code === 13) {
|
|
1374
|
+
return "\\r";
|
|
1375
|
+
}
|
|
1376
|
+
if (code === 9) {
|
|
1377
|
+
return "\\t";
|
|
1378
|
+
}
|
|
1379
|
+
return hexEscape(code);
|
|
1380
|
+
}
|
|
1381
|
+
function hexEscape(code) {
|
|
1382
|
+
return `\\u${code.toString(16).padStart(4, "0")}`;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// src/contracts/catalog/identity.ts
|
|
1386
|
+
function staticNamespace(name) {
|
|
1387
|
+
assertIdentifier(name, "namespace");
|
|
1388
|
+
return { form: "static", name };
|
|
1389
|
+
}
|
|
1390
|
+
function templateNamespace(pattern) {
|
|
1391
|
+
const placeholders = pattern.split("{id}").length - 1;
|
|
1392
|
+
if (placeholders < 1) {
|
|
1393
|
+
catalogError("OKM1122", `Namespace template ${pattern} must contain {id}.`);
|
|
1394
|
+
}
|
|
1395
|
+
const stripped = pattern.replaceAll("{id}", "");
|
|
1396
|
+
if (!/^[A-Za-z0-9_]*$/.test(stripped)) {
|
|
1397
|
+
catalogError("OKM1122", `Namespace template ${pattern} may contain only letters, digits, underscores, and {id}.`);
|
|
1398
|
+
}
|
|
1399
|
+
return { form: "template", pattern };
|
|
1400
|
+
}
|
|
1401
|
+
function assertNamespace(namespace) {
|
|
1402
|
+
if (namespace.form === "static") {
|
|
1403
|
+
assertIdentifier(namespace.name, "namespace");
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
templateNamespace(namespace.pattern);
|
|
1407
|
+
}
|
|
1408
|
+
function identityKey(identity) {
|
|
1409
|
+
const plain = plainIdentityKey(identity);
|
|
1410
|
+
if (plain !== undefined) {
|
|
1411
|
+
return plain;
|
|
1412
|
+
}
|
|
1413
|
+
return canonicalJson(identityToJson(identity));
|
|
1414
|
+
}
|
|
1415
|
+
function plainIdentityKey(identity) {
|
|
1416
|
+
switch (identity.kind) {
|
|
1417
|
+
case "table":
|
|
1418
|
+
case "view":
|
|
1419
|
+
case "materializedView":
|
|
1420
|
+
case "sequence":
|
|
1421
|
+
case "type":
|
|
1422
|
+
return namespaceIdentityKey(identity.kind, identity.name, identity.namespace);
|
|
1423
|
+
case "column":
|
|
1424
|
+
case "index":
|
|
1425
|
+
case "constraint":
|
|
1426
|
+
case "trigger":
|
|
1427
|
+
case "policy":
|
|
1428
|
+
return anchoredIdentityKey(identity.kind, identity.name, identity.parent);
|
|
1429
|
+
case "role":
|
|
1430
|
+
case "extension":
|
|
1431
|
+
if (!isPlainJsonText(identity.name)) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
return `{"kind":"${identity.kind}","name":"${identity.name}"}`;
|
|
1435
|
+
default:
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
function namespaceIdentityKey(kind, name, namespace) {
|
|
1440
|
+
const encoded = staticNamespaceKey(namespace);
|
|
1441
|
+
if (encoded === undefined || !isPlainJsonText(name)) {
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
return `{"kind":"${kind}","name":"${name}","namespace":${encoded}}`;
|
|
1445
|
+
}
|
|
1446
|
+
function anchoredIdentityKey(kind, name, parent) {
|
|
1447
|
+
const encoded = staticNamespaceKey(parent.namespace);
|
|
1448
|
+
if (encoded === undefined || !isPlainJsonText(name) || !isPlainJsonText(parent.name)) {
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
return `{"kind":"${kind}","name":"${name}","parent":{"name":"${parent.name}","namespace":${encoded}}}`;
|
|
1452
|
+
}
|
|
1453
|
+
function staticNamespaceKey(namespace) {
|
|
1454
|
+
if (namespace.form !== "static" || !isPlainJsonText(namespace.name)) {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
return `{"form":"static","name":"${namespace.name}"}`;
|
|
1458
|
+
}
|
|
1459
|
+
function isPlainJsonText(value) {
|
|
1460
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
1461
|
+
const code = value.charCodeAt(index);
|
|
1462
|
+
if (code === 34 || code === 92 || code < 32 || code > 126) {
|
|
1463
|
+
return false;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return true;
|
|
1467
|
+
}
|
|
1468
|
+
function identityLabel(identity) {
|
|
1469
|
+
if (identity.kind === "grant") {
|
|
1470
|
+
return `grant ${identity.role} ${identity.privilege}`;
|
|
1471
|
+
}
|
|
1472
|
+
if (identity.kind === "defaultPrivilege") {
|
|
1473
|
+
return `default privilege ${identity.grantee} ${identity.privilege}`;
|
|
1474
|
+
}
|
|
1475
|
+
return `${identity.kind} ${identity.name}`;
|
|
1476
|
+
}
|
|
1477
|
+
function sameNamespace(left, right) {
|
|
1478
|
+
if (left.form !== right.form) {
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
if (left.form === "static" && right.form === "static") {
|
|
1482
|
+
return left.name === right.name;
|
|
1483
|
+
}
|
|
1484
|
+
if (left.form === "template" && right.form === "template") {
|
|
1485
|
+
return left.pattern === right.pattern;
|
|
1486
|
+
}
|
|
1487
|
+
return false;
|
|
1488
|
+
}
|
|
1489
|
+
function identityToJson(identity) {
|
|
1490
|
+
switch (identity.kind) {
|
|
1491
|
+
case "table":
|
|
1492
|
+
case "view":
|
|
1493
|
+
case "materializedView":
|
|
1494
|
+
case "sequence":
|
|
1495
|
+
case "type":
|
|
1496
|
+
return namespaceIdentityJson(identity);
|
|
1497
|
+
case "column":
|
|
1498
|
+
case "index":
|
|
1499
|
+
case "constraint":
|
|
1500
|
+
case "trigger":
|
|
1501
|
+
case "policy":
|
|
1502
|
+
return {
|
|
1503
|
+
kind: identity.kind,
|
|
1504
|
+
name: identity.name,
|
|
1505
|
+
parent: refToJson(identity.parent)
|
|
1506
|
+
};
|
|
1507
|
+
case "function":
|
|
1508
|
+
return functionIdentityJson(identity);
|
|
1509
|
+
case "grant":
|
|
1510
|
+
return grantIdentityJson(identity);
|
|
1511
|
+
case "defaultPrivilege":
|
|
1512
|
+
return defaultPrivilegeIdentityJson(identity);
|
|
1513
|
+
case "role":
|
|
1514
|
+
case "extension":
|
|
1515
|
+
return { kind: identity.kind, name: identity.name };
|
|
1516
|
+
default:
|
|
1517
|
+
return assertNever(identity);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
function namespaceToJson(namespace) {
|
|
1521
|
+
if (namespace.form === "static") {
|
|
1522
|
+
return { form: "static", name: namespace.name };
|
|
1523
|
+
}
|
|
1524
|
+
return { form: "template", pattern: namespace.pattern };
|
|
1525
|
+
}
|
|
1526
|
+
function namespaceIdentityJson(identity) {
|
|
1527
|
+
return {
|
|
1528
|
+
kind: identity.kind,
|
|
1529
|
+
name: identity.name,
|
|
1530
|
+
namespace: namespaceToJson(identity.namespace)
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
function functionIdentityJson(identity) {
|
|
1534
|
+
return {
|
|
1535
|
+
argTypes: identity.argTypes,
|
|
1536
|
+
kind: identity.kind,
|
|
1537
|
+
name: identity.name,
|
|
1538
|
+
namespace: namespaceToJson(identity.namespace)
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
function grantIdentityJson(identity) {
|
|
1542
|
+
return {
|
|
1543
|
+
kind: identity.kind,
|
|
1544
|
+
object: {
|
|
1545
|
+
kind: identity.object.kind,
|
|
1546
|
+
name: identity.object.name,
|
|
1547
|
+
namespace: namespaceToJson(identity.object.namespace)
|
|
1548
|
+
},
|
|
1549
|
+
privilege: identity.privilege,
|
|
1550
|
+
role: identity.role
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function defaultPrivilegeIdentityJson(identity) {
|
|
1554
|
+
return {
|
|
1555
|
+
forRole: identity.forRole,
|
|
1556
|
+
grantee: identity.grantee,
|
|
1557
|
+
kind: identity.kind,
|
|
1558
|
+
namespace: namespaceToJson(identity.namespace),
|
|
1559
|
+
objectKind: identity.objectKind,
|
|
1560
|
+
privilege: identity.privilege
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function refToJson(ref) {
|
|
1564
|
+
return { name: ref.name, namespace: namespaceToJson(ref.namespace) };
|
|
1565
|
+
}
|
|
1566
|
+
function assertNever(value) {
|
|
1567
|
+
return catalogError("OKM1020", `Unexpected identity ${JSON.stringify(value)}.`);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// src/contracts/catalog/types.ts
|
|
1571
|
+
var REFERENTIAL_ACTIONS = [
|
|
1572
|
+
"cascade",
|
|
1573
|
+
"no action",
|
|
1574
|
+
"restrict",
|
|
1575
|
+
"set default",
|
|
1576
|
+
"set null"
|
|
1577
|
+
];
|
|
1578
|
+
function referentialAction(value) {
|
|
1579
|
+
for (const action of REFERENTIAL_ACTIONS) {
|
|
1580
|
+
if (action === value) {
|
|
1581
|
+
return action;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
var CATALOG_VERSION = 1;
|
|
1587
|
+
|
|
1588
|
+
// src/contracts/catalog/object.ts
|
|
1589
|
+
function table(input) {
|
|
1590
|
+
assertNamespace(input.namespace);
|
|
1591
|
+
assertIdentifier(input.name, "table");
|
|
1592
|
+
assertProvenance(input.provenance);
|
|
1593
|
+
const identity = {
|
|
1594
|
+
kind: "table",
|
|
1595
|
+
namespace: input.namespace,
|
|
1596
|
+
name: input.name
|
|
1597
|
+
};
|
|
1598
|
+
const definition = input.partition === undefined ? {} : { partition: copyPartition(input.partition) };
|
|
1599
|
+
return {
|
|
1600
|
+
kind: "table",
|
|
1601
|
+
identity,
|
|
1602
|
+
owner: input.owner ?? "managed",
|
|
1603
|
+
definition,
|
|
1604
|
+
dependencies: normaliseEdges(input.dependencies ?? []),
|
|
1605
|
+
provenance: input.provenance
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
function column(input) {
|
|
1609
|
+
assertNamespace(input.parent.namespace);
|
|
1610
|
+
assertIdentifier(input.name, "column");
|
|
1611
|
+
assertIdentifier(input.parent.name, "column parent");
|
|
1612
|
+
assertProvenance(input.provenance);
|
|
1613
|
+
assertStoredText(input.dataType, "column type");
|
|
1614
|
+
if (input.dataType.length === 0) {
|
|
1615
|
+
catalogError("OKM1020", `Column ${input.name} has no data type.`);
|
|
1616
|
+
}
|
|
1617
|
+
const modes = [
|
|
1618
|
+
input.defaultExpression !== undefined,
|
|
1619
|
+
input.identity !== undefined,
|
|
1620
|
+
input.generated !== undefined
|
|
1621
|
+
].filter(Boolean).length;
|
|
1622
|
+
if (modes > 1) {
|
|
1623
|
+
catalogError("OKM1020", `Column ${input.name} sets more than one of default, identity, and generated. Set only one.`);
|
|
1624
|
+
}
|
|
1625
|
+
if (input.defaultExpression !== undefined) {
|
|
1626
|
+
assertStoredText(input.defaultExpression, "column default");
|
|
1627
|
+
}
|
|
1628
|
+
if (input.collation !== undefined) {
|
|
1629
|
+
assertStoredText(input.collation, "column collation");
|
|
1630
|
+
if (input.collation.length === 0) {
|
|
1631
|
+
catalogError("OKM1020", `Column ${input.name} has an empty collation.`);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (input.generated !== undefined) {
|
|
1635
|
+
assertStoredText(input.generated.expression, "column generated expression");
|
|
1636
|
+
}
|
|
1637
|
+
const identity = {
|
|
1638
|
+
kind: "column",
|
|
1639
|
+
parent: input.parent,
|
|
1640
|
+
name: input.name
|
|
1641
|
+
};
|
|
1642
|
+
const definition = {
|
|
1643
|
+
dataType: input.dataType,
|
|
1644
|
+
nullable: input.nullable,
|
|
1645
|
+
...input.defaultExpression !== undefined ? { defaultExpression: input.defaultExpression } : {},
|
|
1646
|
+
...input.collation !== undefined ? { collation: input.collation } : {},
|
|
1647
|
+
...input.identity !== undefined ? { identity: { always: input.identity.always } } : {},
|
|
1648
|
+
...input.generated !== undefined ? {
|
|
1649
|
+
generated: {
|
|
1650
|
+
stored: input.generated.stored,
|
|
1651
|
+
expression: input.generated.expression
|
|
1652
|
+
}
|
|
1653
|
+
} : {}
|
|
1654
|
+
};
|
|
1655
|
+
return {
|
|
1656
|
+
kind: "column",
|
|
1657
|
+
identity,
|
|
1658
|
+
owner: input.owner ?? "managed",
|
|
1659
|
+
definition,
|
|
1660
|
+
dependencies: normaliseEdges([tableIdentity(input.parent), ...input.dependencies ?? []]),
|
|
1661
|
+
provenance: input.provenance
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function index(input) {
|
|
1665
|
+
assertNamespace(input.parent.namespace);
|
|
1666
|
+
assertIdentifier(input.parent.name, "index parent");
|
|
1667
|
+
assertProvenance(input.provenance);
|
|
1668
|
+
const columns = [...input.columns ?? []];
|
|
1669
|
+
for (const name of columns) {
|
|
1670
|
+
assertIdentifier(name, "index column");
|
|
1671
|
+
}
|
|
1672
|
+
if (columns.length === 0 && input.expression === undefined) {
|
|
1673
|
+
catalogError("OKM1020", `Index on ${input.parent.name} needs a column or an expression.`);
|
|
1674
|
+
}
|
|
1675
|
+
if (input.predicate !== undefined) {
|
|
1676
|
+
assertStoredText(input.predicate, "index predicate");
|
|
1677
|
+
}
|
|
1678
|
+
if (input.expression !== undefined) {
|
|
1679
|
+
assertStoredText(input.expression, "index expression");
|
|
1680
|
+
}
|
|
1681
|
+
const nameKey = input.nameKey ?? (columns.length > 0 ? columns.join("_") : "expr");
|
|
1682
|
+
assertIdentifierTextKey(nameKey);
|
|
1683
|
+
const name = input.name === undefined ? deterministicName({ parent: input.parent.name, purpose: "index", nameKey }) : fitIdentifier(input.name);
|
|
1684
|
+
const identity = {
|
|
1685
|
+
kind: "index",
|
|
1686
|
+
parent: input.parent,
|
|
1687
|
+
name
|
|
1688
|
+
};
|
|
1689
|
+
const definition = {
|
|
1690
|
+
columns,
|
|
1691
|
+
unique: input.unique ?? false,
|
|
1692
|
+
nameKey,
|
|
1693
|
+
...input.predicate !== undefined ? { predicate: input.predicate } : {},
|
|
1694
|
+
...input.expression !== undefined ? { expression: input.expression } : {}
|
|
1695
|
+
};
|
|
1696
|
+
return {
|
|
1697
|
+
kind: "index",
|
|
1698
|
+
identity,
|
|
1699
|
+
owner: input.owner ?? "managed",
|
|
1700
|
+
definition,
|
|
1701
|
+
dependencies: normaliseEdges([
|
|
1702
|
+
tableIdentity(input.parent),
|
|
1703
|
+
...columns.map((columnName) => columnIdentity(input.parent, columnName)),
|
|
1704
|
+
...input.dependencies ?? []
|
|
1705
|
+
]),
|
|
1706
|
+
provenance: input.provenance
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
function constraint(input) {
|
|
1710
|
+
assertNamespace(input.parent.namespace);
|
|
1711
|
+
assertIdentifier(input.parent.name, "constraint parent");
|
|
1712
|
+
assertProvenance(input.provenance);
|
|
1713
|
+
const columns = [...input.columns ?? []];
|
|
1714
|
+
for (const name of columns) {
|
|
1715
|
+
assertIdentifier(name, "constraint column");
|
|
1716
|
+
}
|
|
1717
|
+
const deferrable = input.deferrable ?? false;
|
|
1718
|
+
const initially = input.initially ?? "immediate";
|
|
1719
|
+
if (initially === "deferred" && !deferrable) {
|
|
1720
|
+
catalogError("OKM1020", `Constraint on ${input.parent.name} is initially deferred but not deferrable.`);
|
|
1721
|
+
}
|
|
1722
|
+
const nullsNotDistinct = input.nullsNotDistinct ?? false;
|
|
1723
|
+
if (nullsNotDistinct && input.constraintKind !== "primaryKey" && input.constraintKind !== "unique") {
|
|
1724
|
+
catalogError("OKM1020", `Constraint on ${input.parent.name} sets nullsNotDistinct on ${input.constraintKind}. nullsNotDistinct applies only to a primary key or a unique constraint.`);
|
|
1725
|
+
}
|
|
1726
|
+
if (input.constraintKind === "foreignKey") {
|
|
1727
|
+
if (input.references === undefined || input.references.columns.length === 0 || columns.length === 0) {
|
|
1728
|
+
catalogError("OKM1021", `Foreign key on ${input.parent.name} is missing its target.`);
|
|
1729
|
+
}
|
|
1730
|
+
} else if (input.constraintKind === "check") {
|
|
1731
|
+
if (input.expression === undefined || input.expression.length === 0) {
|
|
1732
|
+
catalogError("OKM1020", `Check on ${input.parent.name} needs an expression.`);
|
|
1733
|
+
}
|
|
1734
|
+
} else if (columns.length === 0) {
|
|
1735
|
+
catalogError("OKM1020", `${input.constraintKind} on ${input.parent.name} needs a column.`);
|
|
1736
|
+
}
|
|
1737
|
+
if (input.expression !== undefined) {
|
|
1738
|
+
assertStoredText(input.expression, "constraint expression");
|
|
1739
|
+
}
|
|
1740
|
+
const references = input.references;
|
|
1741
|
+
if (references !== undefined) {
|
|
1742
|
+
assertNamespace(references.parent.namespace);
|
|
1743
|
+
assertIdentifier(references.parent.name, "referenced table");
|
|
1744
|
+
for (const name of references.columns) {
|
|
1745
|
+
assertIdentifier(name, "referenced column");
|
|
1746
|
+
}
|
|
1747
|
+
assertAction(references.onDelete, input.parent.name);
|
|
1748
|
+
assertAction(references.onUpdate, input.parent.name);
|
|
1749
|
+
}
|
|
1750
|
+
if (input.constraintKind === "check" && columns.length === 0 && (input.nameKey === undefined || input.nameKey.length === 0)) {
|
|
1751
|
+
catalogError("OKM1020", `Check on ${input.parent.name} needs a name key.`);
|
|
1752
|
+
}
|
|
1753
|
+
const nameKey = input.nameKey ?? defaultNameKey(input.constraintKind, columns);
|
|
1754
|
+
assertIdentifierTextKey(nameKey);
|
|
1755
|
+
const name = input.name === undefined ? deterministicName({
|
|
1756
|
+
parent: input.parent.name,
|
|
1757
|
+
purpose: input.constraintKind,
|
|
1758
|
+
nameKey: input.constraintKind === "check" ? nameKey : defaultNameKey(input.constraintKind, columns)
|
|
1759
|
+
}) : fitIdentifier(input.name);
|
|
1760
|
+
const identity = {
|
|
1761
|
+
kind: "constraint",
|
|
1762
|
+
parent: input.parent,
|
|
1763
|
+
name
|
|
1764
|
+
};
|
|
1765
|
+
const definition = {
|
|
1766
|
+
constraintKind: input.constraintKind,
|
|
1767
|
+
columns,
|
|
1768
|
+
nameKey,
|
|
1769
|
+
deferrable,
|
|
1770
|
+
initially,
|
|
1771
|
+
nullsNotDistinct,
|
|
1772
|
+
...input.expression !== undefined ? { expression: input.expression } : {},
|
|
1773
|
+
...references !== undefined ? {
|
|
1774
|
+
references: {
|
|
1775
|
+
parent: references.parent,
|
|
1776
|
+
columns: [...references.columns],
|
|
1777
|
+
...references.onDelete !== undefined ? { onDelete: references.onDelete } : {},
|
|
1778
|
+
...references.onUpdate !== undefined ? { onUpdate: references.onUpdate } : {}
|
|
1779
|
+
}
|
|
1780
|
+
} : {}
|
|
1781
|
+
};
|
|
1782
|
+
const targets = [
|
|
1783
|
+
tableIdentity(input.parent),
|
|
1784
|
+
...columns.map((columnName) => columnIdentity(input.parent, columnName))
|
|
1785
|
+
];
|
|
1786
|
+
if (references !== undefined) {
|
|
1787
|
+
targets.push(tableIdentity(references.parent));
|
|
1788
|
+
for (const columnName of references.columns) {
|
|
1789
|
+
targets.push(columnIdentity(references.parent, columnName));
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
targets.push(...input.dependencies ?? []);
|
|
1793
|
+
return {
|
|
1794
|
+
kind: "constraint",
|
|
1795
|
+
identity,
|
|
1796
|
+
owner: input.owner ?? "managed",
|
|
1797
|
+
definition,
|
|
1798
|
+
dependencies: normaliseEdges(targets),
|
|
1799
|
+
provenance: input.provenance
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function sequence(input) {
|
|
1803
|
+
assertNamespace(input.namespace);
|
|
1804
|
+
assertIdentifier(input.name, "sequence");
|
|
1805
|
+
assertProvenance(input.provenance);
|
|
1806
|
+
const start = input.start ?? "1";
|
|
1807
|
+
const increment = input.increment ?? "1";
|
|
1808
|
+
assertInteger(start, "sequence start");
|
|
1809
|
+
assertInteger(increment, "sequence increment");
|
|
1810
|
+
if (increment === "0" || increment === "-0") {
|
|
1811
|
+
catalogError("OKM1020", `Sequence ${input.name} has a zero increment.`);
|
|
1812
|
+
}
|
|
1813
|
+
const identity = {
|
|
1814
|
+
kind: "sequence",
|
|
1815
|
+
namespace: input.namespace,
|
|
1816
|
+
name: input.name
|
|
1817
|
+
};
|
|
1818
|
+
return {
|
|
1819
|
+
kind: "sequence",
|
|
1820
|
+
identity,
|
|
1821
|
+
owner: input.owner ?? "managed",
|
|
1822
|
+
definition: {
|
|
1823
|
+
dataType: input.dataType ?? "bigint",
|
|
1824
|
+
start,
|
|
1825
|
+
increment,
|
|
1826
|
+
cycle: input.cycle ?? false
|
|
1827
|
+
},
|
|
1828
|
+
dependencies: normaliseEdges(input.dependencies ?? []),
|
|
1829
|
+
provenance: input.provenance
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
function normaliseEdges(targets) {
|
|
1833
|
+
const byKey = new Map;
|
|
1834
|
+
for (const target of targets) {
|
|
1835
|
+
const key = identityKey(target);
|
|
1836
|
+
if (!byKey.has(key)) {
|
|
1837
|
+
byKey.set(key, target);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
return [...byKey.entries()].sort((left, right) => compareText(left[0], right[0])).map(([, target]) => ({ target }));
|
|
1841
|
+
}
|
|
1842
|
+
function compareText(left, right) {
|
|
1843
|
+
if (left < right) {
|
|
1844
|
+
return -1;
|
|
1845
|
+
}
|
|
1846
|
+
if (left > right) {
|
|
1847
|
+
return 1;
|
|
1848
|
+
}
|
|
1849
|
+
return 0;
|
|
1850
|
+
}
|
|
1851
|
+
function tableIdentity(parent) {
|
|
1852
|
+
return { kind: "table", namespace: parent.namespace, name: parent.name };
|
|
1853
|
+
}
|
|
1854
|
+
function columnIdentity(parent, name) {
|
|
1855
|
+
return { kind: "column", parent, name };
|
|
1856
|
+
}
|
|
1857
|
+
function defaultNameKey(kind, columns) {
|
|
1858
|
+
if (kind === "primaryKey") {
|
|
1859
|
+
return "pkey";
|
|
1860
|
+
}
|
|
1861
|
+
return columns.join("_");
|
|
1862
|
+
}
|
|
1863
|
+
function assertIdentifierTextKey(nameKey) {
|
|
1864
|
+
if (nameKey.length === 0) {
|
|
1865
|
+
catalogError("OKM1122", "Name key is empty.");
|
|
1866
|
+
}
|
|
1867
|
+
if (nameKey.includes("\x00")) {
|
|
1868
|
+
catalogError("OKM1122", "Name key contains NUL.");
|
|
1869
|
+
}
|
|
1870
|
+
assertStoredText(nameKey, "Name key");
|
|
1871
|
+
}
|
|
1872
|
+
function assertInteger(value, role) {
|
|
1873
|
+
assertStoredText(value, role);
|
|
1874
|
+
if (!/^-?\d+$/.test(value)) {
|
|
1875
|
+
catalogError("OKM1020", `${role} ${value} is not an integer.`);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
function assertAction(action, parent) {
|
|
1879
|
+
if (action === undefined) {
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
if (referentialAction(action) === undefined) {
|
|
1883
|
+
catalogError("OKM1020", `Foreign key on ${parent} has referential action ${action}. Accepted actions: cascade, no action, restrict, set default, set null.`);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
function assertProvenance(provenance) {
|
|
1887
|
+
assertStoredText(provenance.name, "provenance");
|
|
1888
|
+
if (provenance.name.length === 0) {
|
|
1889
|
+
catalogError("OKM1020", "Provenance name is empty.");
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
function copyPartition(partition) {
|
|
1893
|
+
if (partition.columns.length === 0) {
|
|
1894
|
+
catalogError("OKM1020", "Partition needs a column.");
|
|
1895
|
+
}
|
|
1896
|
+
for (const name of partition.columns) {
|
|
1897
|
+
assertIdentifier(name, "partition column");
|
|
1898
|
+
}
|
|
1899
|
+
return { method: partition.method, columns: [...partition.columns] };
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// src/contracts/catalog/order.ts
|
|
1903
|
+
function indexObjects(objects) {
|
|
1904
|
+
const seen = new Set;
|
|
1905
|
+
const indexed = [];
|
|
1906
|
+
for (const object of objects) {
|
|
1907
|
+
const key = identityKey(object.identity);
|
|
1908
|
+
if (seen.has(key)) {
|
|
1909
|
+
catalogError("OKM1023", `Duplicate identity for ${identityLabel(object.identity)}.`);
|
|
1910
|
+
}
|
|
1911
|
+
seen.add(key);
|
|
1912
|
+
indexed.push({ key, object });
|
|
1913
|
+
}
|
|
1914
|
+
return indexed;
|
|
1915
|
+
}
|
|
1916
|
+
function dependencyOrder(objects) {
|
|
1917
|
+
return orderIndexed(indexObjects(objects));
|
|
1918
|
+
}
|
|
1919
|
+
function orderIndexed(indexed) {
|
|
1920
|
+
const byKey = new Map;
|
|
1921
|
+
for (const item of indexed) {
|
|
1922
|
+
byKey.set(item.key, item.object);
|
|
1923
|
+
}
|
|
1924
|
+
const dependents = new Map;
|
|
1925
|
+
const remaining = new Map;
|
|
1926
|
+
for (const item of indexed) {
|
|
1927
|
+
dependents.set(item.key, []);
|
|
1928
|
+
remaining.set(item.key, 0);
|
|
1929
|
+
}
|
|
1930
|
+
for (const item of indexed) {
|
|
1931
|
+
const seen = new Set;
|
|
1932
|
+
for (const edge of item.object.dependencies) {
|
|
1933
|
+
const dependency = identityKey(edge.target);
|
|
1934
|
+
if (dependency === item.key) {
|
|
1935
|
+
catalogError("OKM1020", `${identityLabel(item.object.identity)} depends on itself.`);
|
|
1936
|
+
}
|
|
1937
|
+
if (!byKey.has(dependency)) {
|
|
1938
|
+
catalogError("OKM1020", `${identityLabel(item.object.identity)} depends on ${identityLabel(edge.target)}, which is not in the catalog.`);
|
|
1939
|
+
}
|
|
1940
|
+
if (seen.has(dependency)) {
|
|
1941
|
+
continue;
|
|
1942
|
+
}
|
|
1943
|
+
seen.add(dependency);
|
|
1944
|
+
remaining.set(item.key, (remaining.get(item.key) ?? 0) + 1);
|
|
1945
|
+
dependents.get(dependency)?.push(item.key);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
const ready = indexed.filter((item) => remaining.get(item.key) === 0).map((item) => item.key);
|
|
1949
|
+
ready.sort(compareText);
|
|
1950
|
+
const ordered = [];
|
|
1951
|
+
let cursor = 0;
|
|
1952
|
+
while (cursor < ready.length) {
|
|
1953
|
+
const key = ready[cursor];
|
|
1954
|
+
if (key === undefined) {
|
|
1955
|
+
break;
|
|
1956
|
+
}
|
|
1957
|
+
cursor += 1;
|
|
1958
|
+
const object = byKey.get(key);
|
|
1959
|
+
if (object === undefined) {
|
|
1960
|
+
catalogError("OKM1020", "Dependency order lost an object while sorting.");
|
|
1961
|
+
}
|
|
1962
|
+
ordered.push(object);
|
|
1963
|
+
for (const dependent of dependents.get(key) ?? []) {
|
|
1964
|
+
const left = (remaining.get(dependent) ?? 0) - 1;
|
|
1965
|
+
remaining.set(dependent, left);
|
|
1966
|
+
if (left === 0) {
|
|
1967
|
+
insertSorted(ready, cursor, dependent);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
if (ordered.length !== byKey.size) {
|
|
1972
|
+
catalogError("OKM1026", "Catalog has a dependency cycle.");
|
|
1973
|
+
}
|
|
1974
|
+
return ordered;
|
|
1975
|
+
}
|
|
1976
|
+
function insertSorted(ready, from, key) {
|
|
1977
|
+
let index = from;
|
|
1978
|
+
while (index < ready.length && compareText(ready[index] ?? "", key) < 0) {
|
|
1979
|
+
index += 1;
|
|
1980
|
+
}
|
|
1981
|
+
ready.splice(index, 0, key);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
// src/contracts/catalog/enum.ts
|
|
1985
|
+
function enumType(input) {
|
|
1986
|
+
assertNamespace(input.namespace);
|
|
1987
|
+
assertIdentifier(input.name, "type");
|
|
1988
|
+
assertProvenance(input.provenance);
|
|
1989
|
+
if (input.labels.length === 0) {
|
|
1990
|
+
catalogError("OKM1020", `Enum ${input.name} must list at least one label.`);
|
|
1991
|
+
}
|
|
1992
|
+
const labels = [];
|
|
1993
|
+
const seen = new Set;
|
|
1994
|
+
for (const label of input.labels) {
|
|
1995
|
+
assertStoredText(label, "enum label");
|
|
1996
|
+
if (label.length === 0) {
|
|
1997
|
+
catalogError("OKM1020", `Enum ${input.name} has an empty label.`);
|
|
1998
|
+
}
|
|
1999
|
+
if (seen.has(label)) {
|
|
2000
|
+
catalogError("OKM1020", `Enum ${input.name} repeats label ${label}.`);
|
|
2001
|
+
}
|
|
2002
|
+
seen.add(label);
|
|
2003
|
+
labels.push(label);
|
|
2004
|
+
}
|
|
2005
|
+
const identity = {
|
|
2006
|
+
kind: "type",
|
|
2007
|
+
namespace: input.namespace,
|
|
2008
|
+
name: input.name
|
|
2009
|
+
};
|
|
2010
|
+
return {
|
|
2011
|
+
kind: "type",
|
|
2012
|
+
identity,
|
|
2013
|
+
owner: input.owner ?? "managed",
|
|
2014
|
+
definition: { labels },
|
|
2015
|
+
dependencies: normaliseEdges(input.dependencies ?? []),
|
|
2016
|
+
provenance: input.provenance
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
function domainType(input) {
|
|
2020
|
+
assertNamespace(input.namespace);
|
|
2021
|
+
assertIdentifier(input.name, "type");
|
|
2022
|
+
assertProvenance(input.provenance);
|
|
2023
|
+
if (input.base.length === 0) {
|
|
2024
|
+
catalogError("OKM1020", `Domain ${input.name} needs a base type.`);
|
|
2025
|
+
}
|
|
2026
|
+
assertStoredText(input.check, "domain check");
|
|
2027
|
+
if (input.check.length === 0) {
|
|
2028
|
+
catalogError("OKM1020", `Domain ${input.name} needs a check.`);
|
|
2029
|
+
}
|
|
2030
|
+
const identity = {
|
|
2031
|
+
kind: "type",
|
|
2032
|
+
namespace: input.namespace,
|
|
2033
|
+
name: input.name
|
|
2034
|
+
};
|
|
2035
|
+
const definition = { base: input.base, check: input.check };
|
|
2036
|
+
return {
|
|
2037
|
+
kind: "type",
|
|
2038
|
+
identity,
|
|
2039
|
+
owner: input.owner ?? "managed",
|
|
2040
|
+
definition,
|
|
2041
|
+
dependencies: normaliseEdges(input.dependencies ?? []),
|
|
2042
|
+
provenance: input.provenance
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
function isDomain(definition) {
|
|
2046
|
+
return "base" in definition;
|
|
2047
|
+
}
|
|
2048
|
+
function sameEnumLabels(left, right) {
|
|
2049
|
+
if (left.length !== right.length)
|
|
2050
|
+
return false;
|
|
2051
|
+
for (let index = 0;index < left.length; index += 1) {
|
|
2052
|
+
if (left[index] !== right[index])
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
2055
|
+
return true;
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
// src/contracts/catalog/document.ts
|
|
2059
|
+
function creationOrder2(source) {
|
|
2060
|
+
return dependencyOrder(source.objects);
|
|
2061
|
+
}
|
|
2062
|
+
var serializedText = new WeakMap;
|
|
2063
|
+
var serializedHash = new WeakMap;
|
|
2064
|
+
function serializeCatalog(source) {
|
|
2065
|
+
const cached = serializedText.get(source);
|
|
2066
|
+
if (cached !== undefined)
|
|
2067
|
+
return cached;
|
|
2068
|
+
const indexed = source.objects.map((object) => ({
|
|
2069
|
+
object,
|
|
2070
|
+
key: identityKey(object.identity)
|
|
2071
|
+
}));
|
|
2072
|
+
indexed.sort((left, right) => compareText(left.key, right.key));
|
|
2073
|
+
const text = canonicalJson({
|
|
2074
|
+
objects: indexed.map((item) => objectToJson(item.object)),
|
|
2075
|
+
version: source.version
|
|
2076
|
+
});
|
|
2077
|
+
serializedText.set(source, text);
|
|
2078
|
+
return text;
|
|
2079
|
+
}
|
|
2080
|
+
function catalogHash2(source) {
|
|
2081
|
+
const cached = serializedHash.get(source);
|
|
2082
|
+
if (cached !== undefined)
|
|
2083
|
+
return cached;
|
|
2084
|
+
const hash = sha256(serializeCatalog(source));
|
|
2085
|
+
serializedHash.set(source, hash);
|
|
2086
|
+
return hash;
|
|
2087
|
+
}
|
|
2088
|
+
function loadTrustedCatalog2(text, hash) {
|
|
2089
|
+
const canonical = catalogFileText2(text);
|
|
2090
|
+
if (sha256(canonical) !== hash) {
|
|
2091
|
+
catalogError("OKM1027", "Catalog hash does not match the build artifact.");
|
|
2092
|
+
}
|
|
2093
|
+
let parsed;
|
|
2094
|
+
try {
|
|
2095
|
+
parsed = JSON.parse(canonical);
|
|
2096
|
+
} catch {
|
|
2097
|
+
catalogError("OKM1027", "Catalog text is not valid JSON.");
|
|
2098
|
+
}
|
|
2099
|
+
if (!isPlainRecord(parsed) || !Array.isArray(parsed.objects)) {
|
|
2100
|
+
catalogError("OKM1027", "Catalog document is not an object.");
|
|
2101
|
+
}
|
|
2102
|
+
if (parsed.version !== CATALOG_VERSION) {
|
|
2103
|
+
catalogError("OKM1027", `Catalog version ${String(parsed.version)} cannot be read. This build reads version ${String(CATALOG_VERSION)}.`);
|
|
2104
|
+
}
|
|
2105
|
+
const loaded = {
|
|
2106
|
+
version: CATALOG_VERSION,
|
|
2107
|
+
objects: parsed.objects
|
|
2108
|
+
};
|
|
2109
|
+
serializedText.set(loaded, canonical);
|
|
2110
|
+
serializedHash.set(loaded, hash);
|
|
2111
|
+
return loaded;
|
|
2112
|
+
}
|
|
2113
|
+
function catalogFileText2(text, label = "Catalog text") {
|
|
2114
|
+
if (text.includes("\x00")) {
|
|
2115
|
+
catalogError("OKM1027", `${label} is UTF-16. Save the file as UTF-8.`, {
|
|
2116
|
+
fieldReason: "utf16",
|
|
2117
|
+
fix: { summary: "Save the file as UTF-8. This version does not read UTF-16." }
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
2121
|
+
return withoutBom.replaceAll("\r", "");
|
|
2122
|
+
}
|
|
2123
|
+
function dependencyJson(edges) {
|
|
2124
|
+
const indexed = edges.map((edge) => ({
|
|
2125
|
+
key: identityKey(edge.target),
|
|
2126
|
+
target: identityToJson(edge.target)
|
|
2127
|
+
}));
|
|
2128
|
+
indexed.sort((left, right) => compareText(left.key, right.key));
|
|
2129
|
+
return indexed.map((item) => ({ target: item.target }));
|
|
2130
|
+
}
|
|
2131
|
+
function objectToJson(object) {
|
|
2132
|
+
const dependencies = dependencyJson(object.dependencies);
|
|
2133
|
+
return {
|
|
2134
|
+
definition: definitionToJson(object),
|
|
2135
|
+
dependencies,
|
|
2136
|
+
identity: identityToJson(object.identity),
|
|
2137
|
+
kind: object.kind,
|
|
2138
|
+
owner: object.owner,
|
|
2139
|
+
provenance: { name: object.provenance.name, origin: object.provenance.origin }
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
function definitionToJson(object) {
|
|
2143
|
+
switch (object.kind) {
|
|
2144
|
+
case "table": {
|
|
2145
|
+
const partition = object.definition.partition;
|
|
2146
|
+
if (partition === undefined) {
|
|
2147
|
+
return {};
|
|
2148
|
+
}
|
|
2149
|
+
return { partition: { columns: partition.columns, method: partition.method } };
|
|
2150
|
+
}
|
|
2151
|
+
case "column": {
|
|
2152
|
+
const definition = object.definition;
|
|
2153
|
+
return {
|
|
2154
|
+
dataType: definition.dataType,
|
|
2155
|
+
nullable: definition.nullable,
|
|
2156
|
+
...definition.defaultExpression !== undefined ? { defaultExpression: definition.defaultExpression } : {},
|
|
2157
|
+
...definition.collation !== undefined ? { collation: definition.collation } : {},
|
|
2158
|
+
...definition.identity !== undefined ? { identity: { always: definition.identity.always } } : {},
|
|
2159
|
+
...definition.generated !== undefined ? {
|
|
2160
|
+
generated: {
|
|
2161
|
+
expression: definition.generated.expression,
|
|
2162
|
+
stored: definition.generated.stored
|
|
2163
|
+
}
|
|
2164
|
+
} : {}
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
case "index": {
|
|
2168
|
+
const definition = object.definition;
|
|
2169
|
+
return {
|
|
2170
|
+
columns: definition.columns,
|
|
2171
|
+
nameKey: definition.nameKey,
|
|
2172
|
+
unique: definition.unique,
|
|
2173
|
+
...definition.expression !== undefined ? { expression: definition.expression } : {},
|
|
2174
|
+
...definition.predicate !== undefined ? { predicate: definition.predicate } : {}
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
case "constraint": {
|
|
2178
|
+
const definition = object.definition;
|
|
2179
|
+
const references = definition.references;
|
|
2180
|
+
return {
|
|
2181
|
+
columns: definition.columns,
|
|
2182
|
+
constraintKind: definition.constraintKind,
|
|
2183
|
+
deferrable: definition.deferrable,
|
|
2184
|
+
initially: definition.initially,
|
|
2185
|
+
nameKey: definition.nameKey,
|
|
2186
|
+
nullsNotDistinct: definition.nullsNotDistinct,
|
|
2187
|
+
...definition.expression !== undefined ? { expression: definition.expression } : {},
|
|
2188
|
+
...references !== undefined ? {
|
|
2189
|
+
references: {
|
|
2190
|
+
columns: references.columns,
|
|
2191
|
+
...references.onDelete !== undefined ? { onDelete: references.onDelete } : {},
|
|
2192
|
+
...references.onUpdate !== undefined ? { onUpdate: references.onUpdate } : {},
|
|
2193
|
+
parent: {
|
|
2194
|
+
name: references.parent.name,
|
|
2195
|
+
namespace: namespaceToJson(references.parent.namespace)
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
} : {}
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
case "sequence":
|
|
2202
|
+
return {
|
|
2203
|
+
cycle: object.definition.cycle,
|
|
2204
|
+
dataType: object.definition.dataType,
|
|
2205
|
+
increment: object.definition.increment,
|
|
2206
|
+
start: object.definition.start
|
|
2207
|
+
};
|
|
2208
|
+
case "type":
|
|
2209
|
+
return isDomain(object.definition) ? { base: object.definition.base, check: object.definition.check } : { labels: object.definition.labels };
|
|
2210
|
+
case "extension": {
|
|
2211
|
+
const definition = object.definition;
|
|
2212
|
+
return {
|
|
2213
|
+
relocatable: definition.relocatable,
|
|
2214
|
+
schema: definition.schema,
|
|
2215
|
+
...definition.version !== undefined ? { version: definition.version } : {}
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
case "function": {
|
|
2219
|
+
const definition = object.definition;
|
|
2220
|
+
return {
|
|
2221
|
+
arguments: definition.arguments.map((argument) => ({
|
|
2222
|
+
name: argument.name,
|
|
2223
|
+
type: argument.type
|
|
2224
|
+
})),
|
|
2225
|
+
body: definition.body,
|
|
2226
|
+
language: definition.language,
|
|
2227
|
+
returns: definition.returns,
|
|
2228
|
+
security: definition.security,
|
|
2229
|
+
volatility: definition.volatility,
|
|
2230
|
+
...definition.atomic === true ? { atomic: true } : {},
|
|
2231
|
+
...definition.searchPath !== undefined ? { searchPath: definition.searchPath } : {}
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
case "trigger": {
|
|
2235
|
+
const definition = object.definition;
|
|
2236
|
+
return {
|
|
2237
|
+
calls: {
|
|
2238
|
+
argTypes: [...definition.calls.argTypes],
|
|
2239
|
+
name: definition.calls.name,
|
|
2240
|
+
namespace: namespaceToJson(definition.calls.namespace)
|
|
2241
|
+
},
|
|
2242
|
+
events: [...definition.events],
|
|
2243
|
+
level: definition.level,
|
|
2244
|
+
timing: definition.timing,
|
|
2245
|
+
...definition.updateOf !== undefined ? { updateOf: [...definition.updateOf] } : {},
|
|
2246
|
+
...definition.when !== undefined ? { when: definition.when } : {}
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
case "view":
|
|
2250
|
+
return {
|
|
2251
|
+
columns: object.definition.columns.map((column) => ({
|
|
2252
|
+
dataType: column.dataType,
|
|
2253
|
+
name: column.name
|
|
2254
|
+
})),
|
|
2255
|
+
query: object.definition.query,
|
|
2256
|
+
...object.definition.securityInvoker === true ? { securityInvoker: true } : {}
|
|
2257
|
+
};
|
|
2258
|
+
case "policy":
|
|
2259
|
+
return {
|
|
2260
|
+
command: object.definition.command,
|
|
2261
|
+
expression: object.definition.expression,
|
|
2262
|
+
force: object.definition.force
|
|
2263
|
+
};
|
|
2264
|
+
case "materializedView":
|
|
2265
|
+
return {
|
|
2266
|
+
columns: object.definition.columns.map((column) => ({
|
|
2267
|
+
dataType: column.dataType,
|
|
2268
|
+
name: column.name
|
|
2269
|
+
})),
|
|
2270
|
+
query: object.definition.query,
|
|
2271
|
+
...object.definition.refresh !== undefined ? { refresh: object.definition.refresh } : {}
|
|
2272
|
+
};
|
|
2273
|
+
case "role":
|
|
2274
|
+
return { inherit: object.definition.inherit, login: object.definition.login };
|
|
2275
|
+
case "grant":
|
|
2276
|
+
case "defaultPrivilege":
|
|
2277
|
+
return {};
|
|
2278
|
+
default:
|
|
2279
|
+
return assertNever2(object);
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
function isPlainRecord(value) {
|
|
2283
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2284
|
+
}
|
|
2285
|
+
function assertNever2(value) {
|
|
2286
|
+
return catalogError("OKM1020", `Unexpected catalog object ${JSON.stringify(value)}.`);
|
|
2287
|
+
}
|
|
2288
|
+
// src/dialects/pg/quote.ts
|
|
2289
|
+
function quoteLiteral(value) {
|
|
2290
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
2291
|
+
}
|
|
2292
|
+
function arrayElement(encoded, raw) {
|
|
2293
|
+
if (raw && (encoded === "true" || encoded === "false" || isBareNumber(encoded))) {
|
|
2294
|
+
return encoded;
|
|
2295
|
+
}
|
|
2296
|
+
return `"${encoded.replaceAll("\\", "\\\\").replaceAll('"', "\\\"")}"`;
|
|
2297
|
+
}
|
|
2298
|
+
function isBareNumber(encoded) {
|
|
2299
|
+
let pattern = bareNumber;
|
|
2300
|
+
pattern ??= /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
|
|
2301
|
+
bareNumber = pattern;
|
|
2302
|
+
return pattern.test(encoded);
|
|
2303
|
+
}
|
|
2304
|
+
var bareNumber;
|
|
2305
|
+
|
|
2306
|
+
// src/dialects/pg/ddl.ts
|
|
2307
|
+
function quoteIdent2(name) {
|
|
2308
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
2309
|
+
}
|
|
2310
|
+
function renderCatalog2(source, schema) {
|
|
2311
|
+
const folded = foldedKeys(source);
|
|
2312
|
+
const statements = [];
|
|
2313
|
+
const ordered = creationOrder2(source);
|
|
2314
|
+
const created = uniqueBeforeForeignKey(ordered);
|
|
2315
|
+
for (const object of ordered) {
|
|
2316
|
+
if (object.kind !== "type" || object.owner === "ignored")
|
|
2317
|
+
continue;
|
|
2318
|
+
const sql = createObjectSql(object, schema);
|
|
2319
|
+
if (sql !== undefined)
|
|
2320
|
+
statements.push(sql);
|
|
2321
|
+
}
|
|
2322
|
+
for (const object of created) {
|
|
2323
|
+
if (object.kind === "type" || object.kind === "function" || object.kind === "trigger" || object.kind === "view" || object.kind === "materializedView" || object.kind === "policy" || ownedByView(object)) {
|
|
2324
|
+
continue;
|
|
2325
|
+
}
|
|
2326
|
+
if (object.owner === "ignored")
|
|
2327
|
+
continue;
|
|
2328
|
+
const key = identityKey(object.identity);
|
|
2329
|
+
if (object.kind === "table") {
|
|
2330
|
+
statements.push(createTableSql(object, source, schema));
|
|
2331
|
+
continue;
|
|
2332
|
+
}
|
|
2333
|
+
if (folded.has(key))
|
|
2334
|
+
continue;
|
|
2335
|
+
const sql = createObjectSql(object, schema);
|
|
2336
|
+
if (sql !== undefined)
|
|
2337
|
+
statements.push(sql);
|
|
2338
|
+
}
|
|
2339
|
+
for (const object of created) {
|
|
2340
|
+
if (object.kind !== "policy" || object.owner === "ignored")
|
|
2341
|
+
continue;
|
|
2342
|
+
for (const step of policyCreatePlan(object, schema))
|
|
2343
|
+
statements.push(step.sql);
|
|
2344
|
+
}
|
|
2345
|
+
for (const kind of ["function", "trigger"]) {
|
|
2346
|
+
for (const object of ordered) {
|
|
2347
|
+
if (object.kind !== kind || object.owner === "ignored")
|
|
2348
|
+
continue;
|
|
2349
|
+
const sql = createObjectSql(object, schema);
|
|
2350
|
+
if (sql !== undefined)
|
|
2351
|
+
statements.push(sql);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
for (const kind of ["view", "materializedView"]) {
|
|
2355
|
+
for (const object of ordered) {
|
|
2356
|
+
if (object.kind !== kind || object.owner === "ignored")
|
|
2357
|
+
continue;
|
|
2358
|
+
const sql = createObjectSql(object, schema);
|
|
2359
|
+
if (sql !== undefined)
|
|
2360
|
+
statements.push(sql);
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
for (const object of ordered) {
|
|
2364
|
+
if (!ownedByView(object) || object.owner === "ignored")
|
|
2365
|
+
continue;
|
|
2366
|
+
const sql = createObjectSql(object, schema);
|
|
2367
|
+
if (sql !== undefined)
|
|
2368
|
+
statements.push(sql);
|
|
2369
|
+
}
|
|
2370
|
+
return statements;
|
|
2371
|
+
}
|
|
2372
|
+
function uniqueBeforeForeignKey(objects) {
|
|
2373
|
+
const uniques = objects.filter((object) => object.kind === "constraint" && object.definition.constraintKind === "unique");
|
|
2374
|
+
if (uniques.length === 0)
|
|
2375
|
+
return objects;
|
|
2376
|
+
const emitted = new Set;
|
|
2377
|
+
const result = [];
|
|
2378
|
+
for (const object of objects) {
|
|
2379
|
+
if (emitted.has(object))
|
|
2380
|
+
continue;
|
|
2381
|
+
if (object.kind === "constraint" && object.definition.constraintKind === "foreignKey") {
|
|
2382
|
+
const target = object.definition.references;
|
|
2383
|
+
if (target !== undefined && !coveredByPrimary(objects, target.parent, target.columns)) {
|
|
2384
|
+
const columns = target.columns.join("\x00");
|
|
2385
|
+
for (const unique of uniques) {
|
|
2386
|
+
if (emitted.has(unique))
|
|
2387
|
+
continue;
|
|
2388
|
+
if (!sameRef2(unique.identity.parent, target.parent))
|
|
2389
|
+
continue;
|
|
2390
|
+
if (unique.definition.columns.join("\x00") !== columns)
|
|
2391
|
+
continue;
|
|
2392
|
+
result.push(unique);
|
|
2393
|
+
emitted.add(unique);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
result.push(object);
|
|
2398
|
+
emitted.add(object);
|
|
2399
|
+
}
|
|
2400
|
+
return result;
|
|
2401
|
+
}
|
|
2402
|
+
function coveredByPrimary(objects, parent, columns) {
|
|
2403
|
+
const key = columns.join("\x00");
|
|
2404
|
+
return objects.some((object) => object.kind === "constraint" && object.definition.constraintKind === "primaryKey" && sameRef2(object.identity.parent, parent) && object.definition.columns.join("\x00") === key);
|
|
2405
|
+
}
|
|
2406
|
+
function sameRef2(left, right) {
|
|
2407
|
+
if (left.name !== right.name || left.namespace.form !== right.namespace.form)
|
|
2408
|
+
return false;
|
|
2409
|
+
if (left.namespace.form === "static" && right.namespace.form === "static") {
|
|
2410
|
+
return left.namespace.name === right.namespace.name;
|
|
2411
|
+
}
|
|
2412
|
+
return left.namespace.form === "template" && right.namespace.form === "template" ? left.namespace.pattern === right.namespace.pattern : false;
|
|
2413
|
+
}
|
|
2414
|
+
function ownedByView(object) {
|
|
2415
|
+
if (object.kind !== "index")
|
|
2416
|
+
return false;
|
|
2417
|
+
return object.dependencies.some((edge) => edge.target.kind === "view" || edge.target.kind === "materializedView");
|
|
2418
|
+
}
|
|
2419
|
+
function createObjectSql(object, schema) {
|
|
2420
|
+
switch (object.kind) {
|
|
2421
|
+
case "column":
|
|
2422
|
+
return `alter table ${qualify(schema, object.identity.parent.name)} add column ${columnSql(object)}`;
|
|
2423
|
+
case "index":
|
|
2424
|
+
return createIndexSql(object, schema);
|
|
2425
|
+
case "constraint":
|
|
2426
|
+
return `alter table ${qualify(schema, object.identity.parent.name)} add constraint ${quoteIdent2(object.identity.name)} ${constraintBody(object, schema)}`;
|
|
2427
|
+
case "sequence":
|
|
2428
|
+
return createSequenceSql(object, schema);
|
|
2429
|
+
case "type":
|
|
2430
|
+
return createTypeSql(object, schema);
|
|
2431
|
+
case "extension":
|
|
2432
|
+
return createExtensionSql(object);
|
|
2433
|
+
case "function":
|
|
2434
|
+
return functionSql(object, schema, false);
|
|
2435
|
+
case "trigger":
|
|
2436
|
+
return triggerSql(object, schema);
|
|
2437
|
+
case "view":
|
|
2438
|
+
return viewSql(object, schema, false);
|
|
2439
|
+
case "materializedView":
|
|
2440
|
+
return materializedViewSql(object, schema);
|
|
2441
|
+
case "table":
|
|
2442
|
+
return;
|
|
2443
|
+
default:
|
|
2444
|
+
return;
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
function functionSql(object, schema, replace) {
|
|
2448
|
+
const definition = object.definition;
|
|
2449
|
+
const args = definition.arguments.map((argument) => `${quoteIdent2(argument.name)} ${argument.type}`).join(", ");
|
|
2450
|
+
const head = `${replace ? "create or replace" : "create"} function ${qualify(schema, object.identity.name)}(${args})`;
|
|
2451
|
+
const security = ` security ${definition.security}`;
|
|
2452
|
+
const path = definition.searchPath === undefined ? "" : ` set search_path to ${definition.searchPath}`;
|
|
2453
|
+
const tail = `returns ${definition.returns} language ${definition.language} ${definition.volatility}${security}${path}`;
|
|
2454
|
+
if (definition.atomic)
|
|
2455
|
+
return `${head} ${tail} ${definition.body}`;
|
|
2456
|
+
return `${head} ${tail} as ${dollarQuote(definition.body)}`;
|
|
2457
|
+
}
|
|
2458
|
+
function viewSql(object, schema, replace) {
|
|
2459
|
+
const verb = replace ? "create or replace view" : "create view";
|
|
2460
|
+
const invoker = object.definition.securityInvoker === true ? " with (security_invoker = true)" : "";
|
|
2461
|
+
return `${verb} ${qualify(schema, object.identity.name)}${invoker} as ${object.definition.query}`;
|
|
2462
|
+
}
|
|
2463
|
+
function policyCreatePlan(object, schema) {
|
|
2464
|
+
const table = qualify(schema, object.identity.parent.name);
|
|
2465
|
+
const name = quoteIdent2(object.identity.name);
|
|
2466
|
+
const command = object.definition.command === "all" ? "all" : "select";
|
|
2467
|
+
const check = object.definition.command === "all" ? ` with check ${object.definition.expression}` : "";
|
|
2468
|
+
const create = {
|
|
2469
|
+
sql: `create policy ${name} on ${table} as permissive for ${command} using ${object.definition.expression}${check}`,
|
|
2470
|
+
kind: "create-policy"
|
|
2471
|
+
};
|
|
2472
|
+
if (object.definition.command !== "all")
|
|
2473
|
+
return [create];
|
|
2474
|
+
return [
|
|
2475
|
+
{ sql: `alter table ${table} enable row level security`, kind: "enable-rls" },
|
|
2476
|
+
{ sql: `alter table ${table} force row level security`, kind: "enable-rls" },
|
|
2477
|
+
create
|
|
2478
|
+
];
|
|
2479
|
+
}
|
|
2480
|
+
function materializedViewSql(object, schema) {
|
|
2481
|
+
return `create materialized view ${qualify(schema, object.identity.name)} as ${object.definition.query} with no data`;
|
|
2482
|
+
}
|
|
2483
|
+
function triggerSql(object, schema) {
|
|
2484
|
+
const definition = object.definition;
|
|
2485
|
+
const timing = definition.timing === "instead" ? "instead of" : definition.timing;
|
|
2486
|
+
const events = definition.events.map((event) => {
|
|
2487
|
+
if (event !== "update" || definition.updateOf === undefined)
|
|
2488
|
+
return event;
|
|
2489
|
+
return `update of ${definition.updateOf.map((column) => quoteIdent2(column)).join(", ")}`;
|
|
2490
|
+
}).join(" or ");
|
|
2491
|
+
const when = definition.when === undefined ? "" : ` when (${definition.when})`;
|
|
2492
|
+
const args = definition.calls.argTypes.join(", ");
|
|
2493
|
+
const fn = `${qualify(schema, definition.calls.name)}(${args})`;
|
|
2494
|
+
return `create trigger ${quoteIdent2(object.identity.name)} ${timing} ${events} on ${qualify(schema, object.identity.parent.name)} for each ${definition.level}${when} execute function ${fn}`;
|
|
2495
|
+
}
|
|
2496
|
+
function dollarQuote(body) {
|
|
2497
|
+
let tag = "okm";
|
|
2498
|
+
while (body.includes(`$${tag}$`))
|
|
2499
|
+
tag = `${tag}_`;
|
|
2500
|
+
return `$${tag}$${body}$${tag}$`;
|
|
2501
|
+
}
|
|
2502
|
+
function identitySequence(sequence, source) {
|
|
2503
|
+
const key = identityKey(sequence.identity);
|
|
2504
|
+
return source.objects.some((object) => {
|
|
2505
|
+
if (object.kind !== "column" || object.definition.identity === undefined)
|
|
2506
|
+
return false;
|
|
2507
|
+
return object.dependencies.some((edge) => identityKey(edge.target) === key);
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
function foldedKeys(source) {
|
|
2511
|
+
const keys = new Set;
|
|
2512
|
+
for (const object of source.objects) {
|
|
2513
|
+
if (object.kind === "column")
|
|
2514
|
+
keys.add(identityKey(object.identity));
|
|
2515
|
+
if (object.kind === "constraint" && object.definition.constraintKind === "primaryKey") {
|
|
2516
|
+
keys.add(identityKey(object.identity));
|
|
2517
|
+
}
|
|
2518
|
+
if (object.kind === "sequence" && identitySequence(object, source)) {
|
|
2519
|
+
keys.add(identityKey(object.identity));
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
return keys;
|
|
2523
|
+
}
|
|
2524
|
+
function createTableSql(tableObject, source, schema) {
|
|
2525
|
+
const parent = tableObject.identity.name;
|
|
2526
|
+
const columns = source.objects.filter((object) => object.kind === "column" && object.identity.parent.name === parent);
|
|
2527
|
+
const primary = source.objects.find((object) => object.kind === "constraint" && object.definition.constraintKind === "primaryKey" && object.identity.parent.name === parent);
|
|
2528
|
+
const lines = columns.map((column) => columnSql(column));
|
|
2529
|
+
if (primary !== undefined) {
|
|
2530
|
+
const cols = primary.definition.columns.map((name) => quoteIdent2(name)).join(", ");
|
|
2531
|
+
lines.push(`constraint ${quoteIdent2(primary.identity.name)} primary key (${cols})`);
|
|
2532
|
+
}
|
|
2533
|
+
const partition = tableObject.definition.partition;
|
|
2534
|
+
const tail = partition === undefined ? "" : ` partition by ${partition.method} (${partition.columns.map((name) => quoteIdent2(name)).join(", ")})`;
|
|
2535
|
+
return `create table ${qualify(schema, parent)} (
|
|
2536
|
+
${lines.join(`,
|
|
2537
|
+
`)}
|
|
2538
|
+
)${tail}`;
|
|
2539
|
+
}
|
|
2540
|
+
function collateSql(collation) {
|
|
2541
|
+
if (collation === undefined)
|
|
2542
|
+
return "";
|
|
2543
|
+
return ` collate ${quoteIdent2(collation)}`;
|
|
2544
|
+
}
|
|
2545
|
+
function createTypeSql(object, schema) {
|
|
2546
|
+
if (isDomain(object.definition)) {
|
|
2547
|
+
const name = object.identity.name;
|
|
2548
|
+
const constraint = quoteIdent2(fitIdentifier(`${name}_check`));
|
|
2549
|
+
return `create domain ${qualify(schema, name)} as ${object.definition.base} constraint ${constraint} check (${object.definition.check})`;
|
|
2550
|
+
}
|
|
2551
|
+
const labels = object.definition.labels.map((label) => quoteLiteral(label)).join(", ");
|
|
2552
|
+
return `create type ${qualify(schema, object.identity.name)} as enum (${labels})`;
|
|
2553
|
+
}
|
|
2554
|
+
function columnSql(column) {
|
|
2555
|
+
const definition = column.definition;
|
|
2556
|
+
let sql = `${quoteIdent2(column.identity.name)} ${definition.dataType}${collateSql(definition.collation)}`;
|
|
2557
|
+
if (definition.identity !== undefined) {
|
|
2558
|
+
sql += definition.identity.always ? " generated always as identity" : " generated by default as identity";
|
|
2559
|
+
} else if (definition.generated !== undefined) {
|
|
2560
|
+
sql += ` generated always as (${definition.generated.expression}) stored`;
|
|
2561
|
+
}
|
|
2562
|
+
if (!definition.nullable)
|
|
2563
|
+
sql += " not null";
|
|
2564
|
+
if (definition.defaultExpression !== undefined)
|
|
2565
|
+
sql += ` default ${definition.defaultExpression}`;
|
|
2566
|
+
return sql;
|
|
2567
|
+
}
|
|
2568
|
+
function createIndexSql(object, schema) {
|
|
2569
|
+
const unique = object.definition.unique ? "unique " : "";
|
|
2570
|
+
const where = object.definition.predicate === undefined ? "" : ` where ${object.definition.predicate}`;
|
|
2571
|
+
const expression = object.definition.expression;
|
|
2572
|
+
const name = quoteIdent2(object.identity.name);
|
|
2573
|
+
const on = qualify(schema, object.identity.parent.name);
|
|
2574
|
+
if (expression !== undefined && expression.startsWith("using ")) {
|
|
2575
|
+
return `create ${unique}index ${name} on ${on} ${expression}${where}`;
|
|
2576
|
+
}
|
|
2577
|
+
const target = expression !== undefined ? `(${expression})` : object.definition.columns.map((column) => quoteIdent2(column)).join(", ");
|
|
2578
|
+
return `create ${unique}index ${name} on ${on} (${target})${where}`;
|
|
2579
|
+
}
|
|
2580
|
+
function createExtensionSql(object) {
|
|
2581
|
+
const version = object.definition.version;
|
|
2582
|
+
const pinned = version !== undefined && /^\d+(?:\.\d+)*$/.test(version) ? ` version ${quoteLiteral(version)}` : "";
|
|
2583
|
+
return `create extension ${quoteIdent2(object.identity.name)} schema ${quoteIdent2(object.definition.schema)}${pinned}`;
|
|
2584
|
+
}
|
|
2585
|
+
function createSequenceSql(object, schema) {
|
|
2586
|
+
const definition = object.definition;
|
|
2587
|
+
const cycle = definition.cycle ? " cycle" : "";
|
|
2588
|
+
return `create sequence ${qualify(schema, object.identity.name)} as ${definition.dataType} start ${definition.start} increment ${definition.increment}${cycle}`;
|
|
2589
|
+
}
|
|
2590
|
+
function referenceSchema(object, schema) {
|
|
2591
|
+
const references = object.definition.references;
|
|
2592
|
+
if (references === undefined)
|
|
2593
|
+
return schema;
|
|
2594
|
+
const target = references.parent.namespace;
|
|
2595
|
+
const parent = object.identity.parent.namespace;
|
|
2596
|
+
if (target.form === "static" && parent.form === "static" && target.name !== parent.name) {
|
|
2597
|
+
return target.name;
|
|
2598
|
+
}
|
|
2599
|
+
return schema;
|
|
2600
|
+
}
|
|
2601
|
+
function constraintBody(object, schema) {
|
|
2602
|
+
const definition = object.definition;
|
|
2603
|
+
const columns = definition.columns.map((name) => quoteIdent2(name)).join(", ");
|
|
2604
|
+
switch (definition.constraintKind) {
|
|
2605
|
+
case "primaryKey":
|
|
2606
|
+
return `primary key (${columns})`;
|
|
2607
|
+
case "unique": {
|
|
2608
|
+
const nulls = definition.nullsNotDistinct ? " nulls not distinct" : "";
|
|
2609
|
+
return `unique${nulls} (${columns})${deferClause(definition)}`;
|
|
2610
|
+
}
|
|
2611
|
+
case "check":
|
|
2612
|
+
return `check (${definition.expression ?? "true"})${deferClause(definition)}`;
|
|
2613
|
+
case "foreignKey": {
|
|
2614
|
+
const references = definition.references;
|
|
2615
|
+
const target = references === undefined ? "" : ` references ${qualify(referenceSchema(object, schema), references.parent.name)} (${references.columns.map((name) => quoteIdent2(name)).join(", ")})`;
|
|
2616
|
+
const onDelete = references?.onDelete === undefined ? "" : ` on delete ${references.onDelete}`;
|
|
2617
|
+
const onUpdate = references?.onUpdate === undefined ? "" : ` on update ${references.onUpdate}`;
|
|
2618
|
+
return `foreign key (${columns})${target}${onDelete}${onUpdate}${deferClause(definition)}`;
|
|
2619
|
+
}
|
|
2620
|
+
default:
|
|
2621
|
+
return definition.constraintKind;
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
function deferClause(definition) {
|
|
2625
|
+
if (!definition.deferrable)
|
|
2626
|
+
return "";
|
|
2627
|
+
return ` deferrable initially ${definition.initially}`;
|
|
2628
|
+
}
|
|
2629
|
+
function qualify(schema, name) {
|
|
2630
|
+
return `${quoteIdent2(schema)}.${quoteIdent2(name)}`;
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
// src/dialects/pg/floor.ts
|
|
2634
|
+
var POSTGRES_FLOOR = 15;
|
|
2635
|
+
var UUIDV7_POSTGRES = 18;
|
|
2636
|
+
function isPostgresMajor(value) {
|
|
2637
|
+
return typeof value === "number" && (value | 0) === value && value >= 15 && value <= 18;
|
|
2638
|
+
}
|
|
2639
|
+
var FLOOR_TEXT = /^>=(\d+)$/;
|
|
2640
|
+
function parsePostgresFloor(postgres) {
|
|
2641
|
+
return Number(FLOOR_TEXT.exec(postgres ?? "")?.[1]);
|
|
2642
|
+
}
|
|
2643
|
+
function formatPostgresFloor(major) {
|
|
2644
|
+
return `>=${major}`;
|
|
2645
|
+
}
|
|
2646
|
+
function minPostgresDefinition() {
|
|
2647
|
+
return "minPostgres: 15-18.";
|
|
2648
|
+
}
|
|
2649
|
+
function uuidv7NeedsPostgres() {
|
|
2650
|
+
const need = UUIDV7_POSTGRES;
|
|
2651
|
+
return `uuidv7() needs PostgreSQL ${need} or higher. minPostgres: ${need}.`;
|
|
2652
|
+
}
|
|
2653
|
+
function serverNeedsPostgres(server, need) {
|
|
2654
|
+
return `The server is PostgreSQL ${server}. This schema needs PostgreSQL ${need} or higher (minPostgres: ${need}).`;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
// src/dialects/pg/operators.ts
|
|
2658
|
+
var RUNTIME = Symbol("okmodel.operator");
|
|
2659
|
+
function isOperator(value) {
|
|
2660
|
+
return typeof value === "object" && value !== null && RUNTIME in value;
|
|
2661
|
+
}
|
|
2662
|
+
function operatorName(value) {
|
|
2663
|
+
return value[RUNTIME];
|
|
2664
|
+
}
|
|
2665
|
+
function operatorValue(value) {
|
|
2666
|
+
return value.value;
|
|
2667
|
+
}
|
|
2668
|
+
function tag(name, value) {
|
|
2669
|
+
return { [RUNTIME]: name, value };
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
// src/contracts/generator.ts
|
|
2673
|
+
var CLIENT_DEFAULT = Symbol.for("okmodel.clientDefault");
|
|
2674
|
+
function clientGenerator(call, name, options) {
|
|
2675
|
+
const marked = call;
|
|
2676
|
+
Object.defineProperty(marked, CLIENT_DEFAULT, {
|
|
2677
|
+
value: options === undefined ? { label: "client", name } : { label: "client", name, options },
|
|
2678
|
+
enumerable: false
|
|
2679
|
+
});
|
|
2680
|
+
return marked;
|
|
2681
|
+
}
|
|
2682
|
+
function readClientGenerator(value) {
|
|
2683
|
+
if (typeof value !== "function")
|
|
2684
|
+
return;
|
|
2685
|
+
const mark = value[CLIENT_DEFAULT];
|
|
2686
|
+
if (isMark(mark))
|
|
2687
|
+
return { label: "client", name: mark.name, call: value };
|
|
2688
|
+
return { label: "client", name: "function", call: value };
|
|
2689
|
+
}
|
|
2690
|
+
function isMark(value) {
|
|
2691
|
+
if (typeof value !== "object" || value === null)
|
|
2692
|
+
return false;
|
|
2693
|
+
const name = value.name;
|
|
2694
|
+
return value.label === "client" && typeof name === "string" && name !== "function";
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
// src/dialects/pg/misuse.ts
|
|
2698
|
+
var COLUMN_DEFINITION_CODE = "OKM1060";
|
|
2699
|
+
var RESERVED_OPTION_CODE = "OKM1061";
|
|
2700
|
+
var CODEC_VALUE_CODE = "OKM1210";
|
|
2701
|
+
function definition(message) {
|
|
2702
|
+
throw new OkmError(COLUMN_DEFINITION_CODE, message);
|
|
2703
|
+
}
|
|
2704
|
+
function unavailable(message) {
|
|
2705
|
+
throw new OkmError(RESERVED_OPTION_CODE, message);
|
|
2706
|
+
}
|
|
2707
|
+
function rejected(message) {
|
|
2708
|
+
throw new OkmError(CODEC_VALUE_CODE, message);
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
// src/dialects/pg/array-literal.ts
|
|
2712
|
+
function writeArray(value, dims, encode, raw) {
|
|
2713
|
+
return writeLevel(value, dims, encode, raw);
|
|
2714
|
+
}
|
|
2715
|
+
function readArray(wire, dims, decode) {
|
|
2716
|
+
const cursor = { text: wire, index: 0 };
|
|
2717
|
+
const parsed = parseValue(cursor);
|
|
2718
|
+
skipSpace(cursor);
|
|
2719
|
+
if (cursor.index !== cursor.text.length) {
|
|
2720
|
+
rejected("Array literal has trailing text.");
|
|
2721
|
+
}
|
|
2722
|
+
if (!Array.isArray(parsed)) {
|
|
2723
|
+
rejected("Array literal must start with `{`.");
|
|
2724
|
+
}
|
|
2725
|
+
return interpret(parsed, dims, decode);
|
|
2726
|
+
}
|
|
2727
|
+
function writeLevel(value, dims, encode, raw) {
|
|
2728
|
+
if (!Array.isArray(value)) {
|
|
2729
|
+
rejected("Array column expected an array.");
|
|
2730
|
+
}
|
|
2731
|
+
const parts = [];
|
|
2732
|
+
if (dims === 1) {
|
|
2733
|
+
for (const item of value) {
|
|
2734
|
+
if (item === null || item === undefined) {
|
|
2735
|
+
rejected("Array elements cannot be null.");
|
|
2736
|
+
}
|
|
2737
|
+
parts.push(arrayElement(encode(item), raw));
|
|
2738
|
+
}
|
|
2739
|
+
} else {
|
|
2740
|
+
for (const item of value) {
|
|
2741
|
+
parts.push(writeLevel(item, dims - 1, encode, raw));
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
return `{${parts.join(",")}}`;
|
|
2745
|
+
}
|
|
2746
|
+
function interpret(nodes, dims, decode) {
|
|
2747
|
+
if (dims === 1) {
|
|
2748
|
+
return nodes.map((cell) => {
|
|
2749
|
+
if (typeof cell !== "string") {
|
|
2750
|
+
rejected("Array rank does not match dims.");
|
|
2751
|
+
}
|
|
2752
|
+
return decode(cell);
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
return nodes.map((cell) => {
|
|
2756
|
+
if (!Array.isArray(cell)) {
|
|
2757
|
+
rejected("Array rank does not match dims.");
|
|
2758
|
+
}
|
|
2759
|
+
return interpret(cell, dims - 1, decode);
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
function parseValue(cursor) {
|
|
2763
|
+
skipSpace(cursor);
|
|
2764
|
+
if (peek(cursor) === "{") {
|
|
2765
|
+
return parseArray(cursor);
|
|
2766
|
+
}
|
|
2767
|
+
return parseElement(cursor);
|
|
2768
|
+
}
|
|
2769
|
+
function parseArray(cursor) {
|
|
2770
|
+
expectChar(cursor, "{");
|
|
2771
|
+
skipSpace(cursor);
|
|
2772
|
+
if (peek(cursor) === "}") {
|
|
2773
|
+
cursor.index += 1;
|
|
2774
|
+
return [];
|
|
2775
|
+
}
|
|
2776
|
+
const items = [];
|
|
2777
|
+
for (;; ) {
|
|
2778
|
+
items.push(parseValue(cursor));
|
|
2779
|
+
skipSpace(cursor);
|
|
2780
|
+
if (peek(cursor) === ",") {
|
|
2781
|
+
cursor.index += 1;
|
|
2782
|
+
skipSpace(cursor);
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
expectChar(cursor, "}");
|
|
2786
|
+
return items;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
function parseElement(cursor) {
|
|
2790
|
+
if (peek(cursor) === '"') {
|
|
2791
|
+
return parseQuoted(cursor);
|
|
2792
|
+
}
|
|
2793
|
+
const start = cursor.index;
|
|
2794
|
+
while (cursor.index < cursor.text.length) {
|
|
2795
|
+
const char = cursor.text[cursor.index];
|
|
2796
|
+
if (char === "," || char === "}" || char === undefined || isSpace(char)) {
|
|
2797
|
+
break;
|
|
2798
|
+
}
|
|
2799
|
+
cursor.index += 1;
|
|
2800
|
+
}
|
|
2801
|
+
const token = cursor.text.slice(start, cursor.index);
|
|
2802
|
+
if (token.length === 0) {
|
|
2803
|
+
rejected("Array literal is missing an element.");
|
|
2804
|
+
}
|
|
2805
|
+
if (token === "NULL") {
|
|
2806
|
+
return null;
|
|
2807
|
+
}
|
|
2808
|
+
return token;
|
|
2809
|
+
}
|
|
2810
|
+
function parseQuoted(cursor) {
|
|
2811
|
+
expectChar(cursor, '"');
|
|
2812
|
+
let out = "";
|
|
2813
|
+
for (;; ) {
|
|
2814
|
+
const char = next(cursor);
|
|
2815
|
+
if (char === undefined) {
|
|
2816
|
+
rejected("Array literal ended inside a quote.");
|
|
2817
|
+
}
|
|
2818
|
+
if (char === '"') {
|
|
2819
|
+
if (peek(cursor) === '"') {
|
|
2820
|
+
cursor.index += 1;
|
|
2821
|
+
out += '"';
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2824
|
+
return out;
|
|
2825
|
+
}
|
|
2826
|
+
if (char === "\\") {
|
|
2827
|
+
const escaped = next(cursor);
|
|
2828
|
+
if (escaped === undefined) {
|
|
2829
|
+
rejected("Array literal ended inside an escape.");
|
|
2830
|
+
}
|
|
2831
|
+
out += escaped;
|
|
2832
|
+
continue;
|
|
2833
|
+
}
|
|
2834
|
+
out += char;
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
function expectChar(cursor, expected) {
|
|
2838
|
+
if (peek(cursor) !== expected) {
|
|
2839
|
+
rejected(`Array literal expected ${expected}.`);
|
|
2840
|
+
}
|
|
2841
|
+
cursor.index += 1;
|
|
2842
|
+
}
|
|
2843
|
+
function peek(cursor) {
|
|
2844
|
+
return cursor.text[cursor.index];
|
|
2845
|
+
}
|
|
2846
|
+
function next(cursor) {
|
|
2847
|
+
const char = cursor.text[cursor.index];
|
|
2848
|
+
if (char !== undefined) {
|
|
2849
|
+
cursor.index += 1;
|
|
2850
|
+
}
|
|
2851
|
+
return char;
|
|
2852
|
+
}
|
|
2853
|
+
function skipSpace(cursor) {
|
|
2854
|
+
while (cursor.index < cursor.text.length) {
|
|
2855
|
+
const char = cursor.text[cursor.index];
|
|
2856
|
+
if (char === undefined || !isSpace(char)) {
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
cursor.index += 1;
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
function isSpace(char) {
|
|
2863
|
+
return char === " " || char === `
|
|
2864
|
+
` || char === "\t";
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// src/dialects/pg/column.ts
|
|
2868
|
+
class ColumnBuilder {
|
|
2869
|
+
state;
|
|
2870
|
+
constructor(state) {
|
|
2871
|
+
this.state = state;
|
|
2872
|
+
}
|
|
2873
|
+
encode(value) {
|
|
2874
|
+
return this.state.encode(value);
|
|
2875
|
+
}
|
|
2876
|
+
decode(wire) {
|
|
2877
|
+
return this.state.decode(wire);
|
|
2878
|
+
}
|
|
2879
|
+
nullable() {
|
|
2880
|
+
return rebuild(this.state, { nullable: true });
|
|
2881
|
+
}
|
|
2882
|
+
default(value) {
|
|
2883
|
+
const clientDefault = readClientGenerator(value);
|
|
2884
|
+
if (clientDefault !== undefined) {
|
|
2885
|
+
return rebuild(this.state, {
|
|
2886
|
+
hasDefault: true,
|
|
2887
|
+
defaultSql: undefined,
|
|
2888
|
+
defaultValue: undefined,
|
|
2889
|
+
clientDefault,
|
|
2890
|
+
generated: undefined
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
if (value === null && this.state.nullable === false) {
|
|
2894
|
+
definition("A null default is only accepted on a nullable column. Call .nullable() first.");
|
|
2895
|
+
}
|
|
2896
|
+
if (this.state.picklist !== undefined && value !== null) {
|
|
2897
|
+
const text = String(value);
|
|
2898
|
+
if (!this.state.picklist.values.includes(text)) {
|
|
2899
|
+
definition(`Default ${text} is not in the picklist ${this.state.picklist.values.join(", ")}.`);
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
return rebuild(this.state, {
|
|
2903
|
+
hasDefault: true,
|
|
2904
|
+
defaultSql: value === null ? "NULL" : sqlOf(this.state, value),
|
|
2905
|
+
defaultValue: value,
|
|
2906
|
+
clientDefault: undefined,
|
|
2907
|
+
generated: undefined
|
|
2908
|
+
});
|
|
2909
|
+
}
|
|
2910
|
+
defaultSql(expression) {
|
|
2911
|
+
if (expression.length === 0) {
|
|
2912
|
+
definition("defaultSql must be a non-empty SQL expression.");
|
|
2913
|
+
}
|
|
2914
|
+
return rebuild(this.state, {
|
|
2915
|
+
hasDefault: true,
|
|
2916
|
+
defaultSql: expression,
|
|
2917
|
+
defaultValue: undefined,
|
|
2918
|
+
clientDefault: undefined,
|
|
2919
|
+
generated: undefined
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
primaryKey() {
|
|
2923
|
+
return rebuild(this.state, {
|
|
2924
|
+
primaryKey: true,
|
|
2925
|
+
omitUpdate: true
|
|
2926
|
+
});
|
|
2927
|
+
}
|
|
2928
|
+
unique(options) {
|
|
2929
|
+
const marker = options?.global;
|
|
2930
|
+
if (typeof marker === "string" && marker.trim().length === 0) {
|
|
2931
|
+
definition("unique() global needs a reason.");
|
|
2932
|
+
}
|
|
2933
|
+
const named = typeof marker === "string" ? marker.trim() : undefined;
|
|
2934
|
+
const globalFlag = typeof marker === "string" ? true : marker;
|
|
2935
|
+
return rebuild(this.state, {
|
|
2936
|
+
unique: {
|
|
2937
|
+
reason: named ?? options?.reason,
|
|
2938
|
+
global: globalFlag
|
|
2939
|
+
}
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2942
|
+
picklist(values, options) {
|
|
2943
|
+
if (values.length === 0) {
|
|
2944
|
+
definition("A picklist must contain at least one value.");
|
|
2945
|
+
}
|
|
2946
|
+
const allowed = new Set;
|
|
2947
|
+
for (const value of values) {
|
|
2948
|
+
if (value.length === 0) {
|
|
2949
|
+
definition("Picklist values must be non-empty strings.");
|
|
2950
|
+
}
|
|
2951
|
+
if (allowed.has(value)) {
|
|
2952
|
+
definition(`Picklist value ${value} is repeated. Each value is accepted once.`);
|
|
2953
|
+
}
|
|
2954
|
+
allowed.add(value);
|
|
2955
|
+
}
|
|
2956
|
+
if (typeof this.state.defaultValue === "string" && !allowed.has(this.state.defaultValue)) {
|
|
2957
|
+
definition(`Default ${this.state.defaultValue} is not in the picklist ${values.join(", ")}.`);
|
|
2958
|
+
}
|
|
2959
|
+
const previous = this.state.elementEncode;
|
|
2960
|
+
const elementEncode = (value) => {
|
|
2961
|
+
if (typeof value !== "string" || !allowed.has(value)) {
|
|
2962
|
+
rejected(`Picklist rejected ${String(value)}. Accepted values: ${[...allowed].join(", ")}.`);
|
|
2963
|
+
}
|
|
2964
|
+
return previous(value);
|
|
2965
|
+
};
|
|
2966
|
+
return rebuild(this.state, {
|
|
2967
|
+
picklist: { values: values.slice(), check: options?.check !== false },
|
|
2968
|
+
elementEncode,
|
|
2969
|
+
encode: elementEncode
|
|
2970
|
+
});
|
|
2971
|
+
}
|
|
2972
|
+
generated(expression, options) {
|
|
2973
|
+
if (expression.length === 0) {
|
|
2974
|
+
definition("generated() must be a non-empty SQL expression.");
|
|
2975
|
+
}
|
|
2976
|
+
return rebuild(this.state, {
|
|
2977
|
+
generated: { stored: options?.stored !== false, expression },
|
|
2978
|
+
hasDefault: true,
|
|
2979
|
+
defaultSql: undefined,
|
|
2980
|
+
defaultValue: undefined,
|
|
2981
|
+
clientDefault: undefined
|
|
2982
|
+
});
|
|
2983
|
+
}
|
|
2984
|
+
guarded() {
|
|
2985
|
+
return rebuild(this.state, { guarded: true });
|
|
2986
|
+
}
|
|
2987
|
+
hidden(options) {
|
|
2988
|
+
return rebuild(this.state, {
|
|
2989
|
+
hidden: true,
|
|
2990
|
+
...options?.filterable === true ? { filterable: true } : {}
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
sensitive() {
|
|
2994
|
+
return rebuild(this.state, { sensitive: true });
|
|
2995
|
+
}
|
|
2996
|
+
validate(rules) {
|
|
2997
|
+
const list = Array.isArray(rules) ? rules : [rules];
|
|
2998
|
+
return rebuild(this.state, { validate: { rules: list } });
|
|
2999
|
+
}
|
|
3000
|
+
references(table, options) {
|
|
3001
|
+
if (table.length === 0) {
|
|
3002
|
+
definition("references() needs a table name.");
|
|
3003
|
+
}
|
|
3004
|
+
const onDelete = readAction(options?.onDelete, "onDelete");
|
|
3005
|
+
const onUpdate = readAction(options?.onUpdate, "onUpdate");
|
|
3006
|
+
const columns = readReferenceColumns(options?.columns);
|
|
3007
|
+
return rebuild(this.state, {
|
|
3008
|
+
references: {
|
|
3009
|
+
table,
|
|
3010
|
+
...onDelete !== undefined ? { onDelete } : {},
|
|
3011
|
+
...onUpdate !== undefined ? { onUpdate } : {},
|
|
3012
|
+
...columns !== undefined ? { columns } : {}
|
|
3013
|
+
}
|
|
3014
|
+
});
|
|
3015
|
+
}
|
|
3016
|
+
renamedFrom(name) {
|
|
3017
|
+
if (name.length === 0) {
|
|
3018
|
+
definition("renamedFrom must be a non-empty previous column name.");
|
|
3019
|
+
}
|
|
3020
|
+
return rebuild(this.state, { renamedFrom: name });
|
|
3021
|
+
}
|
|
3022
|
+
sqlName(name) {
|
|
3023
|
+
if (name.length === 0) {
|
|
3024
|
+
definition("sqlName must be a non-empty SQL identifier.");
|
|
3025
|
+
}
|
|
3026
|
+
return rebuild(this.state, { sqlName: name });
|
|
3027
|
+
}
|
|
3028
|
+
comment(text) {
|
|
3029
|
+
return rebuild(this.state, { comment: text });
|
|
3030
|
+
}
|
|
3031
|
+
array(options) {
|
|
3032
|
+
if (this.state.dims !== 0) {
|
|
3033
|
+
definition("array() accepts a scalar column. This column is already an array.");
|
|
3034
|
+
}
|
|
3035
|
+
const dims = options?.dims ?? 1;
|
|
3036
|
+
if (!Number.isInteger(dims) || dims < 1 || dims > 6) {
|
|
3037
|
+
definition(`Array dims ${String(dims)} must be an integer from 1 to 6.`);
|
|
3038
|
+
}
|
|
3039
|
+
const elementEncode = this.state.elementEncode;
|
|
3040
|
+
const elementDecode = this.state.elementDecode;
|
|
3041
|
+
const raw = this.state.sqlForm === "raw";
|
|
3042
|
+
return rebuild(this.state, {
|
|
3043
|
+
dims,
|
|
3044
|
+
accepts: ["Array"],
|
|
3045
|
+
encode: (value) => writeArray(value, dims, elementEncode, raw),
|
|
3046
|
+
decode: (wire) => readArray(wire, dims, elementDecode)
|
|
3047
|
+
});
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
function required(body) {
|
|
3051
|
+
return openColumn({
|
|
3052
|
+
nullable: false,
|
|
3053
|
+
hasDefault: false,
|
|
3054
|
+
generated: false,
|
|
3055
|
+
guarded: false,
|
|
3056
|
+
hidden: false,
|
|
3057
|
+
omitWrite: false,
|
|
3058
|
+
...body
|
|
3059
|
+
});
|
|
3060
|
+
}
|
|
3061
|
+
function openColumn(input) {
|
|
3062
|
+
const elementEncode = input.encode;
|
|
3063
|
+
const elementDecode = input.decode;
|
|
3064
|
+
return new ColumnBuilder({
|
|
3065
|
+
baseType: input.baseType,
|
|
3066
|
+
dims: 0,
|
|
3067
|
+
nullable: input.nullable,
|
|
3068
|
+
hasDefault: input.hasDefault,
|
|
3069
|
+
defaultSql: input.defaultSql,
|
|
3070
|
+
defaultValue: undefined,
|
|
3071
|
+
clientDefault: input.clientDefault,
|
|
3072
|
+
collation: input.collation,
|
|
3073
|
+
idSource: input.idSource,
|
|
3074
|
+
identity: input.identity,
|
|
3075
|
+
generated: undefined,
|
|
3076
|
+
unique: undefined,
|
|
3077
|
+
picklist: undefined,
|
|
3078
|
+
guarded: input.guarded,
|
|
3079
|
+
hidden: input.hidden,
|
|
3080
|
+
sensitive: false,
|
|
3081
|
+
omitWrite: input.omitWrite,
|
|
3082
|
+
omitUpdate: input.omitUpdate === true,
|
|
3083
|
+
renamedFrom: undefined,
|
|
3084
|
+
sqlName: undefined,
|
|
3085
|
+
comment: undefined,
|
|
3086
|
+
extension: input.extension,
|
|
3087
|
+
typeDependency: input.typeDependency,
|
|
3088
|
+
enumLabels: input.enumLabels,
|
|
3089
|
+
domain: input.domain,
|
|
3090
|
+
primaryKey: input.primaryKey === true,
|
|
3091
|
+
typeLabel: input.typeLabel,
|
|
3092
|
+
references: undefined,
|
|
3093
|
+
validate: undefined,
|
|
3094
|
+
sqlForm: input.sqlForm,
|
|
3095
|
+
encode: input.encode,
|
|
3096
|
+
decode: input.decode,
|
|
3097
|
+
elementEncode,
|
|
3098
|
+
elementDecode,
|
|
3099
|
+
accepts: input.accepts
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
function formatType(baseType, dims) {
|
|
3103
|
+
if (dims === 0) {
|
|
3104
|
+
return baseType;
|
|
3105
|
+
}
|
|
3106
|
+
return baseType + "[]".repeat(dims);
|
|
3107
|
+
}
|
|
3108
|
+
function sqlOf(state, value) {
|
|
3109
|
+
const encoded = state.encode(value);
|
|
3110
|
+
const typed = formatType(state.baseType, state.dims);
|
|
3111
|
+
if (state.sqlForm === "cast") {
|
|
3112
|
+
return `${quoteLiteral(encoded)}::${typed}`;
|
|
3113
|
+
}
|
|
3114
|
+
if (state.sqlForm === "json") {
|
|
3115
|
+
return `${quoteLiteral(encoded)}::json`;
|
|
3116
|
+
}
|
|
3117
|
+
if (state.sqlForm === "jsonb") {
|
|
3118
|
+
return `${quoteLiteral(encoded)}::jsonb`;
|
|
3119
|
+
}
|
|
3120
|
+
if (state.dims > 0 || state.sqlForm === "quote") {
|
|
3121
|
+
return quoteLiteral(encoded);
|
|
3122
|
+
}
|
|
3123
|
+
return encoded;
|
|
3124
|
+
}
|
|
3125
|
+
function readAction(action, role) {
|
|
3126
|
+
if (action === undefined) {
|
|
3127
|
+
return;
|
|
3128
|
+
}
|
|
3129
|
+
if (referentialAction(action) === undefined) {
|
|
3130
|
+
definition(`references() ${role} ${action} is not a referential action. Accepted actions: ${REFERENTIAL_ACTIONS.join(", ")}.`);
|
|
3131
|
+
}
|
|
3132
|
+
return action;
|
|
3133
|
+
}
|
|
3134
|
+
function readReferenceColumns(columns) {
|
|
3135
|
+
if (columns === undefined) {
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
if (columns.length === 0) {
|
|
3139
|
+
definition("references() columns must name at least one target column.");
|
|
3140
|
+
}
|
|
3141
|
+
const copy = [];
|
|
3142
|
+
for (const name of columns) {
|
|
3143
|
+
if (name.length === 0) {
|
|
3144
|
+
definition("references() columns must be non-empty names.");
|
|
3145
|
+
}
|
|
3146
|
+
copy.push(name);
|
|
3147
|
+
}
|
|
3148
|
+
return copy;
|
|
3149
|
+
}
|
|
3150
|
+
function retarget(column, patch) {
|
|
3151
|
+
return rebuild(column.state, {
|
|
3152
|
+
...patch.dropUnique === true ? { unique: undefined } : {},
|
|
3153
|
+
...patch.dropPrimary === true ? { primaryKey: false } : {},
|
|
3154
|
+
...patch.references !== undefined ? { references: patch.references } : {}
|
|
3155
|
+
});
|
|
3156
|
+
}
|
|
3157
|
+
function rebuild(state, patch) {
|
|
3158
|
+
return new ColumnBuilder({ ...state, ...patch });
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
// src/dialects/pg/json.ts
|
|
3162
|
+
function jsonReplacer(_key, value) {
|
|
3163
|
+
if (typeof value === "bigint") {
|
|
3164
|
+
return value.toString();
|
|
3165
|
+
}
|
|
3166
|
+
return value;
|
|
3167
|
+
}
|
|
3168
|
+
function json() {
|
|
3169
|
+
return required({
|
|
3170
|
+
baseType: "json",
|
|
3171
|
+
encode: encodeJson,
|
|
3172
|
+
decode: decodeJson,
|
|
3173
|
+
accepts: ["Array", "Object"],
|
|
3174
|
+
sqlForm: "json"
|
|
3175
|
+
});
|
|
3176
|
+
}
|
|
3177
|
+
function jsonb() {
|
|
3178
|
+
return required({
|
|
3179
|
+
baseType: "jsonb",
|
|
3180
|
+
encode: encodeJson,
|
|
3181
|
+
decode: decodeJson,
|
|
3182
|
+
accepts: ["Array", "Object"],
|
|
3183
|
+
sqlForm: "jsonb"
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
function encodeJson(value) {
|
|
3187
|
+
const text = JSON.stringify(value, jsonReplacer);
|
|
3188
|
+
if (text === undefined) {
|
|
3189
|
+
rejected("json value must be JSON data. undefined and functions are not accepted. bigint is written as a decimal string.");
|
|
3190
|
+
}
|
|
3191
|
+
return text;
|
|
3192
|
+
}
|
|
3193
|
+
function decodeJson(wire) {
|
|
3194
|
+
try {
|
|
3195
|
+
return JSON.parse(wire);
|
|
3196
|
+
} catch {
|
|
3197
|
+
rejected("json text must be JSON, for example null, a string, a number, true, {}, or [].");
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
// src/runtime/safety-hook.ts
|
|
3202
|
+
var hook;
|
|
3203
|
+
function safetyInstalled() {
|
|
3204
|
+
return hook !== undefined;
|
|
3205
|
+
}
|
|
3206
|
+
function runSafety(contributions, hatches) {
|
|
3207
|
+
hook?.(contributions, hatches);
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
// src/runtime/plan.ts
|
|
3211
|
+
var activeScope;
|
|
3212
|
+
var activeView;
|
|
3213
|
+
function withRowFilters(scope, view, fn) {
|
|
3214
|
+
const previousScope = activeScope;
|
|
3215
|
+
const previousView = activeView;
|
|
3216
|
+
activeScope = scope;
|
|
3217
|
+
activeView = view;
|
|
3218
|
+
try {
|
|
3219
|
+
return fn();
|
|
3220
|
+
} finally {
|
|
3221
|
+
activeScope = previousScope;
|
|
3222
|
+
activeView = previousView;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
var archiveInspect;
|
|
3226
|
+
function archiveRules(model, view) {
|
|
3227
|
+
return archiveInspect?.(model, view);
|
|
3228
|
+
}
|
|
3229
|
+
var FIND_OPTIONS = [
|
|
3230
|
+
"include",
|
|
3231
|
+
"limit",
|
|
3232
|
+
"orderBy",
|
|
3233
|
+
"search",
|
|
3234
|
+
"select",
|
|
3235
|
+
"route",
|
|
3236
|
+
"signal",
|
|
3237
|
+
"timeout",
|
|
3238
|
+
"where"
|
|
3239
|
+
];
|
|
3240
|
+
var ONE_OPTIONS = [
|
|
3241
|
+
"include",
|
|
3242
|
+
"orderBy",
|
|
3243
|
+
"route",
|
|
3244
|
+
"select",
|
|
3245
|
+
"signal",
|
|
3246
|
+
"timeout",
|
|
3247
|
+
"where"
|
|
3248
|
+
];
|
|
3249
|
+
var COUNT_OPTIONS = ["route", "search", "signal", "timeout", "where"];
|
|
3250
|
+
var FILTER_OPTIONS = ["route", "signal", "timeout", "where"];
|
|
3251
|
+
function readOptionNames(op) {
|
|
3252
|
+
if (op === "find")
|
|
3253
|
+
return FIND_OPTIONS;
|
|
3254
|
+
if (op === "one")
|
|
3255
|
+
return ONE_OPTIONS;
|
|
3256
|
+
if (op === "count")
|
|
3257
|
+
return COUNT_OPTIONS;
|
|
3258
|
+
return FILTER_OPTIONS;
|
|
3259
|
+
}
|
|
3260
|
+
var BASE_RULES = [
|
|
3261
|
+
{ rule: "parameterised", contribution: "planner", provenance: "planner" },
|
|
3262
|
+
{ rule: "allowlisted", contribution: "catalog", provenance: "catalog" },
|
|
3263
|
+
{ rule: "identifier", contribution: "catalog", provenance: "catalog" }
|
|
3264
|
+
];
|
|
3265
|
+
var indexesOf = new WeakMap;
|
|
3266
|
+
var operatorEmit;
|
|
3267
|
+
function installOperatorSql(emit) {
|
|
3268
|
+
operatorEmit = emit;
|
|
3269
|
+
}
|
|
3270
|
+
function readNeedsOperatorSql(schema, call) {
|
|
3271
|
+
if (operatorEmit !== undefined)
|
|
3272
|
+
return false;
|
|
3273
|
+
const table = indexes(schema).get(call.table);
|
|
3274
|
+
if (table === undefined)
|
|
3275
|
+
return false;
|
|
3276
|
+
return needsOperatorSql(table, call.where) || orderHasRelevance(call.orderBy);
|
|
3277
|
+
}
|
|
3278
|
+
function orderHasRelevance(orderBy) {
|
|
3279
|
+
if (!isRecord(orderBy))
|
|
3280
|
+
return false;
|
|
3281
|
+
for (const key of Object.keys(orderBy)) {
|
|
3282
|
+
const value = orderBy[key];
|
|
3283
|
+
if (isOperator(value) && operatorName(value) === "relevance")
|
|
3284
|
+
return true;
|
|
3285
|
+
}
|
|
3286
|
+
return false;
|
|
3287
|
+
}
|
|
3288
|
+
function emitRelationFrom(schema, relation, child, sink, parent, alias, depth) {
|
|
3289
|
+
if (relation.through !== undefined) {
|
|
3290
|
+
const planner = { join: emitJoin, where: emitWhere, indexes, quote };
|
|
3291
|
+
relation.through.emit(planner, schema, relation, child, sink, parent, alias, depth);
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
sink.text(quote(child.model.sql));
|
|
3295
|
+
sink.text(" ");
|
|
3296
|
+
sink.text(alias);
|
|
3297
|
+
sink.text(" where ");
|
|
3298
|
+
emitJoin(sink, parent, alias, relation);
|
|
3299
|
+
}
|
|
3300
|
+
var SYNC_OPERATORS = ",lt,lte,gt,gte,between,inList,notIn,";
|
|
3301
|
+
var TEXT_OPERATORS = ",startsWith,endsWith,contains,like,ilike,";
|
|
3302
|
+
function needsOperatorSql(table, value, key) {
|
|
3303
|
+
if (isOperator(value)) {
|
|
3304
|
+
const name = operatorName(value);
|
|
3305
|
+
if (name === "or" || name === "and") {
|
|
3306
|
+
const branches = operatorValue(value);
|
|
3307
|
+
return Array.isArray(branches) && branches.some((branch) => needsOperatorSql(table, branch));
|
|
3308
|
+
}
|
|
3309
|
+
if (name === "not" || name === "eq")
|
|
3310
|
+
return needsOperatorSql(table, operatorValue(value), key);
|
|
3311
|
+
if (TEXT_OPERATORS.includes(`,${name},`)) {
|
|
3312
|
+
const column = key === undefined ? undefined : table.columns.get(key);
|
|
3313
|
+
return column !== undefined && !isTextColumn(column.dataType);
|
|
3314
|
+
}
|
|
3315
|
+
return !SYNC_OPERATORS.includes(`,${name},`);
|
|
3316
|
+
}
|
|
3317
|
+
if (Array.isArray(value))
|
|
3318
|
+
return value.some((item) => needsOperatorSql(table, item, key));
|
|
3319
|
+
if (!isRecord(value))
|
|
3320
|
+
return false;
|
|
3321
|
+
for (const field of Object.keys(value)) {
|
|
3322
|
+
if (needsOperatorSql(table, value[field], field))
|
|
3323
|
+
return true;
|
|
3324
|
+
}
|
|
3325
|
+
return false;
|
|
3326
|
+
}
|
|
3327
|
+
function isTextColumn(dataType) {
|
|
3328
|
+
return dataType === "text" || dataType === "citext" || dataType.startsWith("varchar(") || dataType.startsWith("char(") || dataType.startsWith("character");
|
|
3329
|
+
}
|
|
3330
|
+
function emitLazy(table, column, ref, name, value, sink, order) {
|
|
3331
|
+
const emit = operatorEmit;
|
|
3332
|
+
if (emit === undefined) {
|
|
3333
|
+
fail2("OKM1121", `Operator ${name} on ${column.field} is not loaded.`);
|
|
3334
|
+
}
|
|
3335
|
+
emit(table, column, ref, name, value, sink, order);
|
|
3336
|
+
}
|
|
3337
|
+
function verifyRead(schema, call) {
|
|
3338
|
+
if (!safetyInstalled())
|
|
3339
|
+
return;
|
|
3340
|
+
const source = schema.model[call.table]?.source;
|
|
3341
|
+
const hatches = [];
|
|
3342
|
+
if (call.all !== undefined)
|
|
3343
|
+
hatches.push({ name: "all", reason: call.all });
|
|
3344
|
+
if (call.scope !== undefined && "unscoped" in call.scope) {
|
|
3345
|
+
hatches.push({ name: "unscoped", reason: call.scope.unscoped });
|
|
3346
|
+
}
|
|
3347
|
+
runSafety(appliedRules(call, source), hatches.length === 0 ? undefined : hatches);
|
|
3348
|
+
}
|
|
3349
|
+
function bindCall(schema, call, hooks) {
|
|
3350
|
+
verifyRead(schema, call);
|
|
3351
|
+
const params = [];
|
|
3352
|
+
const marks = [];
|
|
3353
|
+
const sink = {
|
|
3354
|
+
text() {},
|
|
3355
|
+
param(encoded) {
|
|
3356
|
+
params.push(encoded);
|
|
3357
|
+
},
|
|
3358
|
+
mark(token) {
|
|
3359
|
+
marks.push(token);
|
|
3360
|
+
}
|
|
3361
|
+
};
|
|
3362
|
+
emit(schema, call, sink, undefined, hooks);
|
|
3363
|
+
return { key: marks.join(""), params };
|
|
3364
|
+
}
|
|
3365
|
+
function compileCall(schema, call, key, hooks) {
|
|
3366
|
+
const parts = [];
|
|
3367
|
+
let n = 0;
|
|
3368
|
+
const sink = {
|
|
3369
|
+
text(value) {
|
|
3370
|
+
parts.push(value);
|
|
3371
|
+
},
|
|
3372
|
+
param() {
|
|
3373
|
+
n += 1;
|
|
3374
|
+
parts.push(`$${String(n)}`);
|
|
3375
|
+
},
|
|
3376
|
+
mark() {}
|
|
3377
|
+
};
|
|
3378
|
+
const fields = [];
|
|
3379
|
+
const includes = [];
|
|
3380
|
+
emit(schema, call, sink, { fields, includes }, hooks);
|
|
3381
|
+
return {
|
|
3382
|
+
fingerprint: fingerprint(key),
|
|
3383
|
+
text: parts.join(""),
|
|
3384
|
+
mode: call.op,
|
|
3385
|
+
outputs: { fields, includes }
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
function decodeResult(plan, rows, table, rowOf = decodeRow) {
|
|
3389
|
+
if (plan.mode === "count") {
|
|
3390
|
+
const cell = rows[0]?.[0];
|
|
3391
|
+
if (cell === null || cell === undefined)
|
|
3392
|
+
return 0;
|
|
3393
|
+
return Number(cell);
|
|
3394
|
+
}
|
|
3395
|
+
if (plan.mode === "exists") {
|
|
3396
|
+
const cell = rows[0]?.[0];
|
|
3397
|
+
return cell === "t" || cell === "true" || cell === "1";
|
|
3398
|
+
}
|
|
3399
|
+
const decoded = [];
|
|
3400
|
+
for (const row of rows)
|
|
3401
|
+
decoded.push(rowOf(plan.outputs, row));
|
|
3402
|
+
if (plan.mode === "one") {
|
|
3403
|
+
if (decoded.length > 1) {
|
|
3404
|
+
throw new OkmError("not_unique", `one() on ${table} matched more than one row. Pass orderBy to take the first, or narrow where.`, { kind: "not_unique", table });
|
|
3405
|
+
}
|
|
3406
|
+
return decoded[0] ?? null;
|
|
3407
|
+
}
|
|
3408
|
+
return decoded;
|
|
3409
|
+
}
|
|
3410
|
+
function logicalIntent(call) {
|
|
3411
|
+
return {
|
|
3412
|
+
op: call.op,
|
|
3413
|
+
table: call.table,
|
|
3414
|
+
where: plain(call.where),
|
|
3415
|
+
select: call.select,
|
|
3416
|
+
orderBy: plain(call.orderBy),
|
|
3417
|
+
limit: call.limit,
|
|
3418
|
+
include: plain(call.include),
|
|
3419
|
+
all: call.all
|
|
3420
|
+
};
|
|
3421
|
+
}
|
|
3422
|
+
function appliedRules(call, source) {
|
|
3423
|
+
const rules = [];
|
|
3424
|
+
for (const rule of BASE_RULES) {
|
|
3425
|
+
if (source !== undefined && rule.contribution === "catalog") {
|
|
3426
|
+
rules.push({ ...rule, source });
|
|
3427
|
+
} else {
|
|
3428
|
+
rules.push(rule);
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
if (call.where !== undefined) {
|
|
3432
|
+
rules.push({ rule: "filter", contribution: "caller", provenance: "caller" });
|
|
3433
|
+
}
|
|
3434
|
+
if (call.op === "find" || hasMany(call.include)) {
|
|
3435
|
+
rules.push({
|
|
3436
|
+
rule: "bounded",
|
|
3437
|
+
contribution: call.all ?? "limit",
|
|
3438
|
+
provenance: "caller"
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3441
|
+
if (call.tenancyRules !== undefined) {
|
|
3442
|
+
for (const rule of call.tenancyRules)
|
|
3443
|
+
rules.push(rule);
|
|
3444
|
+
}
|
|
3445
|
+
if (call.ruleLines !== undefined) {
|
|
3446
|
+
for (const rule of call.ruleLines)
|
|
3447
|
+
rules.push(rule);
|
|
3448
|
+
}
|
|
3449
|
+
return rules;
|
|
3450
|
+
}
|
|
3451
|
+
function emit(schema, call, sink, outputs, hooks) {
|
|
3452
|
+
const previousScope = activeScope;
|
|
3453
|
+
const previousView = activeView;
|
|
3454
|
+
activeScope = call.scope;
|
|
3455
|
+
activeView = call.archive;
|
|
3456
|
+
try {
|
|
3457
|
+
emitRead(schema, call, sink, outputs, hooks);
|
|
3458
|
+
} finally {
|
|
3459
|
+
activeScope = previousScope;
|
|
3460
|
+
activeView = previousView;
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
function emitRead(schema, call, sink, outputs, hooks) {
|
|
3464
|
+
const table = indexes(schema).get(call.table);
|
|
3465
|
+
if (table === undefined) {
|
|
3466
|
+
throwNamed("OKM1120", call.table, [...indexes(schema).keys()], `Table ${call.table} is not in the schema. Accepted names: ${list([...indexes(schema).keys()])}.`);
|
|
3467
|
+
}
|
|
3468
|
+
if (call.build !== undefined) {
|
|
3469
|
+
call.build(schema, table, sink, outputs, call, hooks);
|
|
3470
|
+
return;
|
|
3471
|
+
}
|
|
3472
|
+
rejectOptions(call);
|
|
3473
|
+
const selected = selectedColumns(table, call.select);
|
|
3474
|
+
sink.mark(`${call.op}|${call.table}|`);
|
|
3475
|
+
if (call.op === "count") {
|
|
3476
|
+
sink.text("select count(*)::text from ");
|
|
3477
|
+
sink.text(quote(table.model.sql));
|
|
3478
|
+
sink.text(" t");
|
|
3479
|
+
emitWhere(schema, table, call.where, sink, "t", 0);
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3482
|
+
if (call.op === "exists") {
|
|
3483
|
+
sink.text("select exists(select 1 from ");
|
|
3484
|
+
sink.text(quote(table.model.sql));
|
|
3485
|
+
sink.text(" t");
|
|
3486
|
+
emitWhere(schema, table, call.where, sink, "t", 0);
|
|
3487
|
+
sink.text(")::text");
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
sink.text("select ");
|
|
3491
|
+
let column = 0;
|
|
3492
|
+
for (let index = 0;index < selected.length; index += 1) {
|
|
3493
|
+
const item = selected[index];
|
|
3494
|
+
if (item === undefined)
|
|
3495
|
+
continue;
|
|
3496
|
+
if (index > 0)
|
|
3497
|
+
sink.text(", ");
|
|
3498
|
+
sink.text(projectExpr(`t.${quote(item.sql)}`, item.dataType));
|
|
3499
|
+
sink.mark(`s:${item.field}`);
|
|
3500
|
+
outputs?.fields.push({ key: item.field, at: column, decode: item.decode });
|
|
3501
|
+
column += 1;
|
|
3502
|
+
}
|
|
3503
|
+
if (call.include !== undefined && hooks === undefined) {
|
|
3504
|
+
fail2("OKM1120", "An include needs the include planner.");
|
|
3505
|
+
}
|
|
3506
|
+
const includes = hooks === undefined || call.include === undefined ? [] : hooks.read(schema, table, call.include, call.all);
|
|
3507
|
+
if (selected.length === 0 && includes.length === 0)
|
|
3508
|
+
sink.text("1");
|
|
3509
|
+
for (let index = 0;index < includes.length; index += 1) {
|
|
3510
|
+
const include = includes[index];
|
|
3511
|
+
if (include === undefined)
|
|
3512
|
+
continue;
|
|
3513
|
+
if (hooks === undefined)
|
|
3514
|
+
continue;
|
|
3515
|
+
column = hooks.select(include, `i${String(index)}`, sink, outputs, column);
|
|
3516
|
+
}
|
|
3517
|
+
sink.text(" from ");
|
|
3518
|
+
sink.text(quote(table.model.sql));
|
|
3519
|
+
sink.text(" t");
|
|
3520
|
+
for (let index = 0;index < includes.length; index += 1) {
|
|
3521
|
+
const include = includes[index];
|
|
3522
|
+
if (include === undefined)
|
|
3523
|
+
continue;
|
|
3524
|
+
hooks?.join(schema, include, sink, "t", `i${String(index)}`, 0);
|
|
3525
|
+
}
|
|
3526
|
+
emitWhere(schema, table, call.where, sink, "t", 0, false, call.after);
|
|
3527
|
+
emitOrder(table, call.orderBy, sink, "t");
|
|
3528
|
+
emitLimit(call, sink);
|
|
3529
|
+
call.tail?.(sink);
|
|
3530
|
+
}
|
|
3531
|
+
function emitJoin(sink, parent, child, relation) {
|
|
3532
|
+
for (let index = 0;index < relation.local.length; index += 1) {
|
|
3533
|
+
const local = relation.local[index];
|
|
3534
|
+
const remote = relation.remote[index];
|
|
3535
|
+
if (local === undefined || remote === undefined)
|
|
3536
|
+
continue;
|
|
3537
|
+
if (index > 0)
|
|
3538
|
+
sink.text(" and ");
|
|
3539
|
+
sink.text(child);
|
|
3540
|
+
sink.text(".");
|
|
3541
|
+
sink.text(quote(remote));
|
|
3542
|
+
sink.text(" = ");
|
|
3543
|
+
sink.text(parent);
|
|
3544
|
+
sink.text(".");
|
|
3545
|
+
sink.text(quote(local));
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
function holdArchive(table, sink, alias, appended) {
|
|
3549
|
+
const archive = table.model.archive;
|
|
3550
|
+
if (archive === undefined || activeView === "with")
|
|
3551
|
+
return false;
|
|
3552
|
+
sink.text(appended ? " and " : " where ");
|
|
3553
|
+
sink.text(alias);
|
|
3554
|
+
sink.text(".");
|
|
3555
|
+
sink.text(quote(archive.at));
|
|
3556
|
+
sink.text(activeView === "only" ? " is not null" : " is null");
|
|
3557
|
+
sink.mark(activeView === "only" ? "archived|" : "active|");
|
|
3558
|
+
return true;
|
|
3559
|
+
}
|
|
3560
|
+
function emitWhere(schema, table, where, sink, alias, depth, appended = false, extra) {
|
|
3561
|
+
const held = schema.tenancy?.predicate({
|
|
3562
|
+
table: table.model.name,
|
|
3563
|
+
fieldSql: (field) => table.columns.get(field)?.sql,
|
|
3564
|
+
encode: table.columns.get(schema.tenancy?.key ?? "")?.encode,
|
|
3565
|
+
alias,
|
|
3566
|
+
appended,
|
|
3567
|
+
scope: activeScope,
|
|
3568
|
+
sink
|
|
3569
|
+
}) === true;
|
|
3570
|
+
const archived = holdArchive(table, sink, alias, appended || held);
|
|
3571
|
+
let started = appended || held || archived;
|
|
3572
|
+
if (extra !== undefined) {
|
|
3573
|
+
sink.text(started ? " and (" : " where (");
|
|
3574
|
+
extra(sink, alias);
|
|
3575
|
+
sink.text(")");
|
|
3576
|
+
started = true;
|
|
3577
|
+
}
|
|
3578
|
+
for (const part of isOperator(where) && operatorName(where) === "and" ? operatorValue(where) : [where]) {
|
|
3579
|
+
if (part === undefined)
|
|
3580
|
+
continue;
|
|
3581
|
+
const joined = isOperator(part);
|
|
3582
|
+
if (!joined && !isRecord(part))
|
|
3583
|
+
fail2("OKM1121", WHERE_FIELDS);
|
|
3584
|
+
if (!joined && Object.keys(part).length === 0)
|
|
3585
|
+
continue;
|
|
3586
|
+
sink.text(started ? " and " : " where ");
|
|
3587
|
+
started = true;
|
|
3588
|
+
if (joined && operatorName(part) === "or")
|
|
3589
|
+
emitOr(schema, table, operatorValue(part), sink, alias, depth);
|
|
3590
|
+
else
|
|
3591
|
+
emitAnd(schema, table, part, sink, alias, depth);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
var WHERE_FIELDS = "where must be an object of fields. Wrap an object value with eq.";
|
|
3595
|
+
function emitAnd(schema, table, where, sink, alias, depth) {
|
|
3596
|
+
if (!isRecord(where)) {
|
|
3597
|
+
fail2("OKM1121", WHERE_FIELDS);
|
|
3598
|
+
}
|
|
3599
|
+
const keys = Object.keys(where);
|
|
3600
|
+
let wrote = false;
|
|
3601
|
+
for (const key of keys) {
|
|
3602
|
+
const value = where[key];
|
|
3603
|
+
if (value === undefined)
|
|
3604
|
+
continue;
|
|
3605
|
+
if (wrote)
|
|
3606
|
+
sink.text(" and ");
|
|
3607
|
+
wrote = true;
|
|
3608
|
+
emitPredicate(schema, table, key, value, sink, alias, depth);
|
|
3609
|
+
}
|
|
3610
|
+
if (!wrote)
|
|
3611
|
+
sink.text("true");
|
|
3612
|
+
}
|
|
3613
|
+
function emitPredicate(schema, table, key, value, sink, alias, depth) {
|
|
3614
|
+
const relation = table.relations.get(key);
|
|
3615
|
+
if (relation !== undefined) {
|
|
3616
|
+
emitRelationFilter(schema, relation, value, sink, alias, depth);
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
schema.tenancy?.guard(table.model.name, key, "where");
|
|
3620
|
+
const column = table.columns.get(key);
|
|
3621
|
+
if (column === undefined) {
|
|
3622
|
+
throwNamed("OKM1120", key, table.names, `Field ${key} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
|
|
3623
|
+
}
|
|
3624
|
+
refuseHidden(column);
|
|
3625
|
+
sink.mark(`w:${key}`);
|
|
3626
|
+
emitOperand(schema, table, column, value, sink, alias, depth);
|
|
3627
|
+
}
|
|
3628
|
+
function emitOperand(schema, table, column, value, sink, alias, depth, wrapped = false) {
|
|
3629
|
+
const ref = `${alias}.${quote(column.sql)}`;
|
|
3630
|
+
if (isOperator(value)) {
|
|
3631
|
+
emitOperator(schema, table, column, ref, operatorName(value), operatorValue(value), sink, alias, depth);
|
|
3632
|
+
return;
|
|
3633
|
+
}
|
|
3634
|
+
if (value === null) {
|
|
3635
|
+
sink.mark(":null");
|
|
3636
|
+
sink.text(ref);
|
|
3637
|
+
sink.text(" is null");
|
|
3638
|
+
return;
|
|
3639
|
+
}
|
|
3640
|
+
if (typeof value === "object")
|
|
3641
|
+
takeObject(column, value, !wrapped);
|
|
3642
|
+
sink.mark(":eq");
|
|
3643
|
+
sink.text(ref);
|
|
3644
|
+
sink.text(" = ");
|
|
3645
|
+
sink.param(column.encode(value));
|
|
3646
|
+
}
|
|
3647
|
+
function emitOperator(schema, table, column, ref, name, value, sink, alias, depth) {
|
|
3648
|
+
sink.mark(`:${name}`);
|
|
3649
|
+
if (name === "or") {
|
|
3650
|
+
emitOr(schema, table, value, sink, alias, depth);
|
|
3651
|
+
return;
|
|
3652
|
+
}
|
|
3653
|
+
if (column === undefined) {
|
|
3654
|
+
fail2("OKM1121", "That operator needs a field.");
|
|
3655
|
+
}
|
|
3656
|
+
if (name === "not") {
|
|
3657
|
+
emitNot(schema, table, column, ref, value, sink, alias, depth);
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
3660
|
+
if (name === "eq") {
|
|
3661
|
+
emitOperand(schema, table, column, value, sink, alias, depth, true);
|
|
3662
|
+
return;
|
|
3663
|
+
}
|
|
3664
|
+
if (name === "lt" || name === "lte" || name === "gt" || name === "gte") {
|
|
3665
|
+
sink.text(ref);
|
|
3666
|
+
sink.text(name === "lt" ? " < " : name === "lte" ? " <= " : name === "gt" ? " > " : " >= ");
|
|
3667
|
+
sink.param(column.encode(requireValue(column, value)));
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
if (name === "between") {
|
|
3671
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
3672
|
+
fail2("OKM1121", `between on ${column.field} needs two values.`);
|
|
3673
|
+
}
|
|
3674
|
+
sink.text(ref);
|
|
3675
|
+
sink.text(" between ");
|
|
3676
|
+
sink.param(column.encode(requireValue(column, value[0])));
|
|
3677
|
+
sink.text(" and ");
|
|
3678
|
+
sink.param(column.encode(requireValue(column, value[1])));
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
if (name === "inList" || name === "notIn") {
|
|
3682
|
+
emitIn(column, ref, name, value, sink);
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
if (TEXT_OPERATORS.includes(`,${name},`) && isTextColumn(column.dataType)) {
|
|
3686
|
+
if (typeof value !== "string") {
|
|
3687
|
+
fail2("OKM1121", `${name} on ${column.field} needs a string.`);
|
|
3688
|
+
}
|
|
3689
|
+
sink.text(ref);
|
|
3690
|
+
if (name === "like" || name === "ilike") {
|
|
3691
|
+
sink.text(name === "like" ? " like " : " ilike ");
|
|
3692
|
+
sink.param(value);
|
|
3693
|
+
return;
|
|
3694
|
+
}
|
|
3695
|
+
sink.text(" like ");
|
|
3696
|
+
if (name !== "startsWith" && name !== "endsWith" && name !== "contains") {
|
|
3697
|
+
fail2("OKM1121", `${name} on ${column.field} needs a string.`);
|
|
3698
|
+
}
|
|
3699
|
+
sink.param(literalPattern(value, name));
|
|
3700
|
+
sink.text(" escape E'\\\\'");
|
|
3701
|
+
return;
|
|
3702
|
+
}
|
|
3703
|
+
emitLazy(table, column, ref, name, value, sink);
|
|
3704
|
+
}
|
|
3705
|
+
function emitNot(schema, table, column, ref, value, sink, alias, depth) {
|
|
3706
|
+
if (value === null) {
|
|
3707
|
+
sink.mark(":null");
|
|
3708
|
+
sink.text(ref);
|
|
3709
|
+
sink.text(" is not null");
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
if (isOperator(value) && operatorName(value) === "inList") {
|
|
3713
|
+
const listed = operatorValue(value);
|
|
3714
|
+
if (Array.isArray(listed) && listed.length === 0) {
|
|
3715
|
+
sink.text("true");
|
|
3716
|
+
return;
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
sink.text("not (");
|
|
3720
|
+
emitOperand(schema, table, column, value, sink, alias, depth, true);
|
|
3721
|
+
sink.text(")");
|
|
3722
|
+
}
|
|
3723
|
+
function emitIn(column, ref, name, value, sink) {
|
|
3724
|
+
if (!Array.isArray(value)) {
|
|
3725
|
+
fail2("OKM1121", `${name} on ${column.field} needs a list.`);
|
|
3726
|
+
}
|
|
3727
|
+
sink.mark(`:${String(value.length)}`);
|
|
3728
|
+
if (value.length === 0) {
|
|
3729
|
+
sink.text(name === "inList" ? "false" : "true");
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
sink.text(ref);
|
|
3733
|
+
sink.text(name === "inList" ? " in (" : " not in (");
|
|
3734
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
3735
|
+
if (index > 0)
|
|
3736
|
+
sink.text(", ");
|
|
3737
|
+
sink.param(column.encode(requireValue(column, value[index])));
|
|
3738
|
+
}
|
|
3739
|
+
sink.text(")");
|
|
3740
|
+
}
|
|
3741
|
+
function emitOr(schema, table, value, sink, alias, depth) {
|
|
3742
|
+
if (!Array.isArray(value))
|
|
3743
|
+
orFail(false);
|
|
3744
|
+
if (value.length === 0) {
|
|
3745
|
+
sink.text("false");
|
|
3746
|
+
sink.mark(":0");
|
|
3747
|
+
return;
|
|
3748
|
+
}
|
|
3749
|
+
sink.mark(`:${String(value.length)}`);
|
|
3750
|
+
sink.text("(");
|
|
3751
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
3752
|
+
const branch = value[index];
|
|
3753
|
+
if (index > 0)
|
|
3754
|
+
sink.text(" or ");
|
|
3755
|
+
sink.text("(");
|
|
3756
|
+
if (!isRecord(branch)) {
|
|
3757
|
+
fail2("OKM1121", "or() branches must be where objects.");
|
|
3758
|
+
}
|
|
3759
|
+
let wrote = false;
|
|
3760
|
+
for (const key of Object.keys(branch)) {
|
|
3761
|
+
const item = branch[key];
|
|
3762
|
+
if (item === undefined)
|
|
3763
|
+
continue;
|
|
3764
|
+
if (wrote)
|
|
3765
|
+
sink.text(" and ");
|
|
3766
|
+
wrote = true;
|
|
3767
|
+
emitPredicate(schema, table, key, item, sink, alias, depth);
|
|
3768
|
+
}
|
|
3769
|
+
if (!wrote)
|
|
3770
|
+
orFail(true);
|
|
3771
|
+
sink.text(")");
|
|
3772
|
+
}
|
|
3773
|
+
sink.text(")");
|
|
3774
|
+
}
|
|
3775
|
+
function emitRelationFilter(schema, relation, value, sink, alias, depth) {
|
|
3776
|
+
if (!isOperator(value)) {
|
|
3777
|
+
fail2("OKM1121", `Relation ${relation.name} needs has, none, or every.`);
|
|
3778
|
+
}
|
|
3779
|
+
const name = operatorName(value);
|
|
3780
|
+
if (name !== "has" && name !== "none" && name !== "every") {
|
|
3781
|
+
fail2("OKM1121", `Relation ${relation.name} needs has, none, or every.`);
|
|
3782
|
+
}
|
|
3783
|
+
const child = indexes(schema).get(relation.table);
|
|
3784
|
+
if (child === undefined) {
|
|
3785
|
+
fail2("OKM1120", `Relation ${relation.name} names ${relation.table}, which is not in the schema.`);
|
|
3786
|
+
}
|
|
3787
|
+
const childAlias = `r${String(depth)}_${relation.name}`;
|
|
3788
|
+
sink.mark(`rel:${relation.name}:${name}`);
|
|
3789
|
+
if (name === "none" || name === "every")
|
|
3790
|
+
sink.text("not ");
|
|
3791
|
+
sink.text("exists (select 1 from ");
|
|
3792
|
+
emitRelationFrom(schema, relation, child, sink, alias, childAlias, depth);
|
|
3793
|
+
const inner = operatorValue(value);
|
|
3794
|
+
if (name === "every") {
|
|
3795
|
+
emitWhere(schema, child, undefined, sink, childAlias, depth + 1, true);
|
|
3796
|
+
sink.text(" and not (");
|
|
3797
|
+
emitAnd(schema, child, inner ?? {}, sink, childAlias, depth + 1);
|
|
3798
|
+
sink.text(")");
|
|
3799
|
+
sink.text(")");
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
emitWhere(schema, child, inner, sink, childAlias, depth + 1, true);
|
|
3803
|
+
sink.text(")");
|
|
3804
|
+
}
|
|
3805
|
+
function emitOrder(table, orderBy, sink, alias) {
|
|
3806
|
+
if (orderBy === undefined)
|
|
3807
|
+
return;
|
|
3808
|
+
if (!isRecord(orderBy)) {
|
|
3809
|
+
fail2("OKM1120", "orderBy must be an object of fields.");
|
|
3810
|
+
}
|
|
3811
|
+
const keys = Object.keys(orderBy);
|
|
3812
|
+
if (keys.length === 0)
|
|
3813
|
+
return;
|
|
3814
|
+
sink.text(" order by ");
|
|
3815
|
+
for (let index = 0;index < keys.length; index += 1) {
|
|
3816
|
+
const key = keys[index];
|
|
3817
|
+
if (key === undefined)
|
|
3818
|
+
continue;
|
|
3819
|
+
const column = table.columns.get(key);
|
|
3820
|
+
if (column === undefined) {
|
|
3821
|
+
throwNamed("OKM1120", key, table.names, `Field ${key} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
|
|
3822
|
+
}
|
|
3823
|
+
refuseHidden(column);
|
|
3824
|
+
const value = orderBy[key];
|
|
3825
|
+
if (index > 0)
|
|
3826
|
+
sink.text(", ");
|
|
3827
|
+
if (isOperator(value) && operatorName(value) === "relevance") {
|
|
3828
|
+
emitLazy(table, column, `${alias}.${quote(column.sql)}`, "relevance", operatorValue(value), sink, true);
|
|
3829
|
+
continue;
|
|
3830
|
+
}
|
|
3831
|
+
const parsed = parseOrder(key, value);
|
|
3832
|
+
sink.text(alias);
|
|
3833
|
+
sink.text(".");
|
|
3834
|
+
sink.text(quote(column.sql));
|
|
3835
|
+
sink.text(parsed.dir === "asc" ? " asc" : " desc");
|
|
3836
|
+
sink.text(parsed.nulls === "first" ? " nulls first" : " nulls last");
|
|
3837
|
+
sink.mark(`o:${key}:${parsed.dir}:${parsed.nulls}`);
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
function emitPrimaryOrder(table, sink, alias) {
|
|
3841
|
+
sink.text(" order by ");
|
|
3842
|
+
for (let index = 0;index < table.model.primary.length; index += 1) {
|
|
3843
|
+
const field = table.model.primary[index];
|
|
3844
|
+
if (field === undefined)
|
|
3845
|
+
continue;
|
|
3846
|
+
const column = table.columns.get(field);
|
|
3847
|
+
if (column === undefined)
|
|
3848
|
+
continue;
|
|
3849
|
+
if (index > 0)
|
|
3850
|
+
sink.text(", ");
|
|
3851
|
+
sink.text(alias);
|
|
3852
|
+
sink.text(".");
|
|
3853
|
+
sink.text(quote(column.sql));
|
|
3854
|
+
sink.text(" asc");
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
function refuseHidden(column) {
|
|
3858
|
+
if (column.hidden !== true)
|
|
3859
|
+
return;
|
|
3860
|
+
fail2("OKM1120", `Field ${column.field} is hidden.`, "Call hidden({ filterable: true }).");
|
|
3861
|
+
}
|
|
3862
|
+
function emitLimit(call, sink) {
|
|
3863
|
+
if (call.op === "one") {
|
|
3864
|
+
const ordered = call.orderBy !== undefined && isRecord(call.orderBy) && Object.keys(call.orderBy).length > 0;
|
|
3865
|
+
sink.text(ordered ? " limit 1" : " limit 2");
|
|
3866
|
+
sink.mark(ordered ? "one:1" : "one:2");
|
|
3867
|
+
return;
|
|
3868
|
+
}
|
|
3869
|
+
if (call.op !== "find")
|
|
3870
|
+
return;
|
|
3871
|
+
if (call.all !== undefined) {
|
|
3872
|
+
if (call.all.length === 0) {
|
|
3873
|
+
fail2("OKM1101", "find() .all needs a reason.");
|
|
3874
|
+
}
|
|
3875
|
+
sink.mark(`all:${call.all}`);
|
|
3876
|
+
if (call.limit !== undefined) {
|
|
3877
|
+
const limit = readLimit(call.limit);
|
|
3878
|
+
sink.text(" limit ");
|
|
3879
|
+
sink.param(String(limit));
|
|
3880
|
+
sink.mark(`l:${String(limit)}`);
|
|
3881
|
+
}
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3884
|
+
if (call.limit === undefined) {
|
|
3885
|
+
fail2("OKM1101", "find() needs a limit, or .all(reason) when the full set is intentional.");
|
|
3886
|
+
}
|
|
3887
|
+
const limit = readLimit(call.limit);
|
|
3888
|
+
sink.text(" limit ");
|
|
3889
|
+
sink.param(String(limit));
|
|
3890
|
+
sink.mark(`l:${String(limit)}`);
|
|
3891
|
+
}
|
|
3892
|
+
function selectedColumns(table, select) {
|
|
3893
|
+
if (select === undefined) {
|
|
3894
|
+
const columns = [];
|
|
3895
|
+
for (const column of table.model.columns) {
|
|
3896
|
+
if (!column.hidden)
|
|
3897
|
+
columns.push(column);
|
|
3898
|
+
}
|
|
3899
|
+
return columns;
|
|
3900
|
+
}
|
|
3901
|
+
if (!Array.isArray(select)) {
|
|
3902
|
+
fail2("OKM1120", "select must be a list of field names.");
|
|
3903
|
+
}
|
|
3904
|
+
const columns = [];
|
|
3905
|
+
for (const name of select) {
|
|
3906
|
+
if (typeof name !== "string") {
|
|
3907
|
+
fail2("OKM1120", "select must be a list of field names.");
|
|
3908
|
+
}
|
|
3909
|
+
const column = table.columns.get(name);
|
|
3910
|
+
if (column === undefined) {
|
|
3911
|
+
throwNamed("OKM1120", name, table.names, `Field ${name} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
|
|
3912
|
+
}
|
|
3913
|
+
columns.push(column);
|
|
3914
|
+
}
|
|
3915
|
+
return columns;
|
|
3916
|
+
}
|
|
3917
|
+
function parseOrder(field, value) {
|
|
3918
|
+
if (value === "asc")
|
|
3919
|
+
return { dir: "asc", nulls: "last" };
|
|
3920
|
+
if (value === "desc")
|
|
3921
|
+
return { dir: "desc", nulls: "first" };
|
|
3922
|
+
if (isRecord(value)) {
|
|
3923
|
+
const dir = value.dir ?? value.direction;
|
|
3924
|
+
if (dir !== "asc" && dir !== "desc") {
|
|
3925
|
+
fail2("OKM1120", `orderBy ${field} must be asc or desc.`);
|
|
3926
|
+
}
|
|
3927
|
+
const nulls = value.nulls;
|
|
3928
|
+
if (nulls === undefined)
|
|
3929
|
+
return { dir, nulls: dir === "asc" ? "last" : "first" };
|
|
3930
|
+
if (nulls !== "first" && nulls !== "last") {
|
|
3931
|
+
fail2("OKM1120", `orderBy ${field} nulls must be first or last.`);
|
|
3932
|
+
}
|
|
3933
|
+
return { dir, nulls };
|
|
3934
|
+
}
|
|
3935
|
+
fail2("OKM1120", `orderBy ${field} must be asc or desc.`);
|
|
3936
|
+
}
|
|
3937
|
+
function rejectOptions(call) {
|
|
3938
|
+
const accepted = readOptionNames(call.op);
|
|
3939
|
+
const present = {};
|
|
3940
|
+
if (call.where !== undefined)
|
|
3941
|
+
present.where = call.where;
|
|
3942
|
+
if (call.select !== undefined)
|
|
3943
|
+
present.select = call.select;
|
|
3944
|
+
if (call.orderBy !== undefined)
|
|
3945
|
+
present.orderBy = call.orderBy;
|
|
3946
|
+
if (call.limit !== undefined)
|
|
3947
|
+
present.limit = call.limit;
|
|
3948
|
+
if (call.include !== undefined)
|
|
3949
|
+
present.include = call.include;
|
|
3950
|
+
rejectKeys(present, accepted, call.op);
|
|
3951
|
+
}
|
|
3952
|
+
function rejectKeys(value, accepted, where, search) {
|
|
3953
|
+
for (const key of Object.keys(value)) {
|
|
3954
|
+
if (accepted.includes(key))
|
|
3955
|
+
continue;
|
|
3956
|
+
throwNamed("OKM1120", key, accepted, key === "search" && search === true ? `${where}() does not take search. Pass search to find or count.` : `Option ${key} is not accepted by ${where}. Accepted options: ${list(accepted)}.`);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
function readLimit(value) {
|
|
3960
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
3961
|
+
throw new OkmError("invalid", "limit must be an integer from 0 up.", { kind: "invalid" });
|
|
3962
|
+
}
|
|
3963
|
+
return value;
|
|
3964
|
+
}
|
|
3965
|
+
function requireValue(column, value) {
|
|
3966
|
+
if (value === undefined || value === null) {
|
|
3967
|
+
fail2("OKM1121", `Field ${column.field} needs a value.`);
|
|
3968
|
+
}
|
|
3969
|
+
if (typeof value === "object")
|
|
3970
|
+
takeObject(column, value);
|
|
3971
|
+
return value;
|
|
3972
|
+
}
|
|
3973
|
+
function takeObject(column, value, bare = false) {
|
|
3974
|
+
if (bare || !column.accepts?.includes({}.toString.call(value).slice(8, -1))) {
|
|
3975
|
+
fail2("OKM1121", `Field ${column.field} rejects that object.`);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
function projectExpr(sql, dataType) {
|
|
3979
|
+
if (dataType === "timestamptz" || dataType === "timestamp")
|
|
3980
|
+
return jsonExpr(sql, dataType);
|
|
3981
|
+
return sql;
|
|
3982
|
+
}
|
|
3983
|
+
function jsonExpr(sql, dataType) {
|
|
3984
|
+
if (dataType === "timestamptz") {
|
|
3985
|
+
return `to_char(${sql} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"')`;
|
|
3986
|
+
}
|
|
3987
|
+
if (dataType === "timestamp") {
|
|
3988
|
+
return `to_char(${sql}, 'YYYY-MM-DD"T"HH24:MI:SS.US')`;
|
|
3989
|
+
}
|
|
3990
|
+
return `${sql}::text`;
|
|
3991
|
+
}
|
|
3992
|
+
function literalPattern(value, mode) {
|
|
3993
|
+
let out = "";
|
|
3994
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
3995
|
+
const char = value[index] ?? "";
|
|
3996
|
+
if (char === "\\" || char === "%" || char === "_")
|
|
3997
|
+
out += "\\";
|
|
3998
|
+
out += char;
|
|
3999
|
+
}
|
|
4000
|
+
if (mode === "startsWith")
|
|
4001
|
+
return `${out}%`;
|
|
4002
|
+
if (mode === "endsWith")
|
|
4003
|
+
return `%${out}`;
|
|
4004
|
+
return `%${out}%`;
|
|
4005
|
+
}
|
|
4006
|
+
function decodeRow(outputs, row) {
|
|
4007
|
+
const result = {};
|
|
4008
|
+
for (const field of outputs.fields) {
|
|
4009
|
+
result[field.key] = cell(row[field.at], field.decode);
|
|
4010
|
+
}
|
|
4011
|
+
return result;
|
|
4012
|
+
}
|
|
4013
|
+
function cell(value, decode) {
|
|
4014
|
+
if (value === null || value === undefined)
|
|
4015
|
+
return null;
|
|
4016
|
+
return decode === undefined ? value : decode(value);
|
|
4017
|
+
}
|
|
4018
|
+
function indexes(schema) {
|
|
4019
|
+
const cached = indexesOf.get(schema);
|
|
4020
|
+
if (cached !== undefined)
|
|
4021
|
+
return cached;
|
|
4022
|
+
const built = new Map;
|
|
4023
|
+
for (const model of Object.values(schema.model)) {
|
|
4024
|
+
assertIdentifier(model.sql, `table ${model.name}`);
|
|
4025
|
+
const columns = new Map;
|
|
4026
|
+
const names = [];
|
|
4027
|
+
for (const column of model.columns) {
|
|
4028
|
+
assertIdentifier(column.sql, `column ${model.name}.${column.field}`);
|
|
4029
|
+
columns.set(column.field, column);
|
|
4030
|
+
names.push(column.field);
|
|
4031
|
+
}
|
|
4032
|
+
names.sort();
|
|
4033
|
+
const relations = new Map;
|
|
4034
|
+
for (const relation of model.relations) {
|
|
4035
|
+
relations.set(relation.name, relation);
|
|
4036
|
+
}
|
|
4037
|
+
built.set(model.name, { model, columns, relations, names });
|
|
4038
|
+
}
|
|
4039
|
+
indexesOf.set(schema, built);
|
|
4040
|
+
return built;
|
|
4041
|
+
}
|
|
4042
|
+
function fingerprint(key) {
|
|
4043
|
+
let hash = 0xcbf29ce484222325n;
|
|
4044
|
+
const prime = 0x100000001b3n;
|
|
4045
|
+
const mask = 0xffffffffffffffffn;
|
|
4046
|
+
for (let index = 0;index < key.length; index += 1) {
|
|
4047
|
+
hash ^= BigInt(key.charCodeAt(index));
|
|
4048
|
+
hash = hash * prime & mask;
|
|
4049
|
+
}
|
|
4050
|
+
return `Q_${hash.toString(16).padStart(16, "0")}`;
|
|
4051
|
+
}
|
|
4052
|
+
function quote(name) {
|
|
4053
|
+
let out = '"';
|
|
4054
|
+
for (let index = 0;index < name.length; index += 1) {
|
|
4055
|
+
const char = name[index] ?? "";
|
|
4056
|
+
out += char === '"' ? '""' : char;
|
|
4057
|
+
}
|
|
4058
|
+
return `${out}"`;
|
|
4059
|
+
}
|
|
4060
|
+
function list(names) {
|
|
4061
|
+
return names.length === 0 ? "(none)" : names.join(", ");
|
|
4062
|
+
}
|
|
4063
|
+
function isRecord(value) {
|
|
4064
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !isOperator(value);
|
|
4065
|
+
}
|
|
4066
|
+
function orFail(empty) {
|
|
4067
|
+
throw new OkmError("OKM1121", empty ? "or() branch is empty." : "Pass an array: or([a, b]).", {
|
|
4068
|
+
fix: {
|
|
4069
|
+
summary: empty ? "Give every or() branch a predicate. An empty branch matches every row." : "Pass an array: or([a, b])."
|
|
4070
|
+
}
|
|
4071
|
+
});
|
|
4072
|
+
}
|
|
4073
|
+
var FAIL_FIX = {
|
|
4074
|
+
OKM1121: "Use eq for an object value, or has, none, or every for a relation.",
|
|
4075
|
+
OKM1101: "Pass limit, or call .all(reason)."
|
|
4076
|
+
};
|
|
4077
|
+
function registerFailFix(code, summary) {
|
|
4078
|
+
FAIL_FIX[code] = summary;
|
|
4079
|
+
}
|
|
4080
|
+
function fail2(code, message, fix) {
|
|
4081
|
+
const summary = fix ?? FAIL_FIX[code];
|
|
4082
|
+
throw new OkmError(code, message, summary === undefined ? undefined : { fix: { summary } });
|
|
4083
|
+
}
|
|
4084
|
+
function plain(value) {
|
|
4085
|
+
if (isOperator(value)) {
|
|
4086
|
+
return { op: operatorName(value), value: plain(operatorValue(value)) };
|
|
4087
|
+
}
|
|
4088
|
+
if (Array.isArray(value))
|
|
4089
|
+
return value.map((item) => plain(item));
|
|
4090
|
+
if (typeof value === "object" && value !== null) {
|
|
4091
|
+
const out = {};
|
|
4092
|
+
for (const key of Object.keys(value))
|
|
4093
|
+
out[key] = plain(value[key]);
|
|
4094
|
+
return out;
|
|
4095
|
+
}
|
|
4096
|
+
return value;
|
|
4097
|
+
}
|
|
4098
|
+
function hasMany(include) {
|
|
4099
|
+
if (!isRecord(include))
|
|
4100
|
+
return false;
|
|
4101
|
+
return Object.keys(include).length > 0;
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
// packages/harness/src/topology.ts
|
|
4105
|
+
var USER = "okm";
|
|
4106
|
+
var PASSWORD = "okm";
|
|
4107
|
+
var DATABASE = "okm";
|
|
4108
|
+
var HOST = "127.0.0.1";
|
|
4109
|
+
function primaryUrl(env = process.env) {
|
|
4110
|
+
return postgresUrl(env.OKM_PRIMARY_PORT ?? "55432");
|
|
4111
|
+
}
|
|
4112
|
+
function postgresUrl(port) {
|
|
4113
|
+
return `postgres://${USER}:${PASSWORD}@${HOST}:${port}/${DATABASE}`;
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
export { OkmError, safe, catalogError, throwNamed, DriverError2, cancelled2, timedOut2, outcomeUnknown2, acquireTimeout2, errorField2, mapDriverError2, isConnectionSqlstate, isConnectionErrno, isConnectionFailure, isConnectionLost, mapPostgresError2, assertIdentifier, fitIdentifier, assertStoredText, staticNamespace, identityKey, identityLabel, sameNamespace, CATALOG_VERSION, table, column, index, constraint, sequence, compareText, enumType, domainType, sameEnumLabels, indexObjects, orderIndexed, creationOrder2, catalogHash2, loadTrustedCatalog2, catalogFileText2, quoteLiteral, quoteIdent2, renderCatalog2, clientGenerator, readClientGenerator, definition, unavailable, rejected, writeArray, ColumnBuilder, required, openColumn, formatType, retarget, POSTGRES_FLOOR, UUIDV7_POSTGRES, isPostgresMajor, parsePostgresFloor, formatPostgresFloor, minPostgresDefinition, uuidv7NeedsPostgres, serverNeedsPostgres, isOperator, operatorName, operatorValue, tag, json, jsonb, encodeJson, safetyInstalled, runSafety, withRowFilters, archiveRules, readOptionNames, installOperatorSql, readNeedsOperatorSql, orderHasRelevance, emitRelationFrom, needsOperatorSql, bindCall, compileCall, decodeResult, logicalIntent, appliedRules, emitJoin, emitWhere, emitOrder, emitPrimaryOrder, refuseHidden, emitLimit, selectedColumns, parseOrder, rejectKeys, readLimit, takeObject, projectExpr, jsonExpr, decodeRow, cell, indexes, quote, list, isRecord, orFail, registerFailFix, fail2, primaryUrl };
|