@crvouga/postgres-mem 1.0.1 → 1.1.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/AGENTS.md +11 -7
- package/COMPATIBILITY-AUDIT.md +6 -5
- package/COMPATIBILITY.md +4 -4
- package/README.md +54 -29
- package/compat/coverage.json +1 -1
- package/compat/divergences.json +16 -5
- package/compat/sections/api.ts +6 -0
- package/compat/sections/fzz.ts +5 -0
- package/compat/sections/snp.ts +3 -2
- package/compat/unsupported-register.json +0 -1
- package/dist/api/bind.d.ts +2 -1
- package/dist/api/database.d.ts +25 -7
- package/dist/api/snapshot.d.ts +21 -0
- package/dist/ast/nodes.d.ts +2 -0
- package/dist/constraints/enforce.d.ts +1 -0
- package/dist/executor/plpgsql.d.ts +69 -0
- package/dist/executor/select.d.ts +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9202 -7320
- package/dist/index.js.map +4 -4
- package/dist/indexes/index.d.ts +14 -0
- package/dist/indexes/maintain.d.ts +11 -0
- package/dist/parser/parser.d.ts +2 -0
- package/dist/planner/access.d.ts +15 -0
- package/dist/runtime/assert.d.ts +5 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/options.d.ts +9 -0
- package/dist/serialization/codec.d.ts +1 -8
- package/dist/serialization/wire.d.ts +63 -0
- package/dist/storage/columnar-slab.d.ts +37 -0
- package/dist/storage/database-state.d.ts +36 -1
- package/dist/transactions/manager.d.ts +5 -5
- package/dist/types/jsonpath.d.ts +16 -0
- package/dist/unstable.js +1659 -540
- package/dist/unstable.js.map +4 -4
- package/package.json +16 -2
package/dist/unstable.js
CHANGED
|
@@ -503,10 +503,26 @@ function timestampAddInterval(ts, iv) {
|
|
|
503
503
|
return result;
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
+
// src/runtime/assert.ts
|
|
507
|
+
function assert(condition, message) {
|
|
508
|
+
if (!condition) {
|
|
509
|
+
throw pgError("internal", message, "XX000");
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function assertBounds(value, min, max, label) {
|
|
513
|
+
if (value < min || value > max) {
|
|
514
|
+
throw pgError("internal", `${label} out of bounds: ${value} not in [${min}, ${max}]`, "XX000");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function assertNever(value, message = "unreachable") {
|
|
518
|
+
throw pgError("internal", `${message}: ${String(value)}`, "XX000");
|
|
519
|
+
}
|
|
520
|
+
|
|
506
521
|
// src/types/numeric.ts
|
|
507
522
|
var MAX_DISPLAY_SCALE = 1e3;
|
|
508
523
|
var MIN_SIG_DIGITS = 16;
|
|
509
524
|
function makeNumeric(coef, dscale) {
|
|
525
|
+
assertBounds(dscale, 0, MAX_DISPLAY_SCALE, "numeric dscale");
|
|
510
526
|
return { kind: "numeric", coef, dscale, special: null };
|
|
511
527
|
}
|
|
512
528
|
var NUMERIC_NAN = { kind: "numeric", coef: 0n, dscale: 0, special: "nan" };
|
|
@@ -650,7 +666,13 @@ function numericMul(a, b) {
|
|
|
650
666
|
if (sa === 0 || sb === 0) return NUMERIC_NAN;
|
|
651
667
|
return sa * sb > 0 ? NUMERIC_PINF : NUMERIC_NINF;
|
|
652
668
|
}
|
|
653
|
-
|
|
669
|
+
let dscale = a.dscale + b.dscale;
|
|
670
|
+
let coef = a.coef * b.coef;
|
|
671
|
+
if (dscale > MAX_DISPLAY_SCALE) {
|
|
672
|
+
coef = roundToScale(coef, dscale, MAX_DISPLAY_SCALE);
|
|
673
|
+
dscale = MAX_DISPLAY_SCALE;
|
|
674
|
+
}
|
|
675
|
+
return makeNumeric(coef, dscale);
|
|
654
676
|
}
|
|
655
677
|
function decimalWeight(v) {
|
|
656
678
|
if (v.coef === 0n) return 0;
|
|
@@ -805,7 +827,7 @@ function numericSqrt(a) {
|
|
|
805
827
|
return makeNumeric(roundToScale(root, extraScale, rscale), rscale);
|
|
806
828
|
}
|
|
807
829
|
function bigintSqrt(n) {
|
|
808
|
-
if (n < 0n) throw
|
|
830
|
+
if (n < 0n) throw pgError("internal", "bigintSqrt: negative operand", "XX000");
|
|
809
831
|
if (n < 2n) return n;
|
|
810
832
|
let x = 1n << BigInt(Math.ceil(n.toString(2).length / 2));
|
|
811
833
|
for (; ; ) {
|
|
@@ -1083,6 +1105,8 @@ function numericStripTrailingZeros(v) {
|
|
|
1083
1105
|
}
|
|
1084
1106
|
|
|
1085
1107
|
// src/types/jsonb.ts
|
|
1108
|
+
var MAX_JSON_PARSE_DEPTH = 512;
|
|
1109
|
+
var JSON_TEXT_ENCODER = new TextEncoder();
|
|
1086
1110
|
var JSONB_NULL = { j: "null" };
|
|
1087
1111
|
function jsonbBool(v) {
|
|
1088
1112
|
return { j: "bool", v };
|
|
@@ -1109,7 +1133,7 @@ var JsonParser = class {
|
|
|
1109
1133
|
pos = 0;
|
|
1110
1134
|
parse() {
|
|
1111
1135
|
this.skipWs();
|
|
1112
|
-
const v = this.parseValue();
|
|
1136
|
+
const v = this.parseValue(0);
|
|
1113
1137
|
this.skipWs();
|
|
1114
1138
|
if (this.pos < this.text.length) this.fail("expected end of input");
|
|
1115
1139
|
return v;
|
|
@@ -1124,11 +1148,14 @@ var JsonParser = class {
|
|
|
1124
1148
|
else break;
|
|
1125
1149
|
}
|
|
1126
1150
|
}
|
|
1127
|
-
parseValue() {
|
|
1151
|
+
parseValue(depth) {
|
|
1152
|
+
if (depth > MAX_JSON_PARSE_DEPTH) {
|
|
1153
|
+
throw pgError("program_limit_exceeded", "json nesting depth exceeds maximum", "54000");
|
|
1154
|
+
}
|
|
1128
1155
|
const c = this.text[this.pos];
|
|
1129
1156
|
if (c === void 0) this.fail("unexpected end");
|
|
1130
|
-
if (c === "{") return this.parseObject();
|
|
1131
|
-
if (c === "[") return this.parseArray();
|
|
1157
|
+
if (c === "{") return this.parseObject(depth + 1);
|
|
1158
|
+
if (c === "[") return this.parseArray(depth + 1);
|
|
1132
1159
|
if (c === '"') return jsonbStr(this.parseString());
|
|
1133
1160
|
if (c === "t") {
|
|
1134
1161
|
this.expect("true");
|
|
@@ -1148,7 +1175,7 @@ var JsonParser = class {
|
|
|
1148
1175
|
if (this.text.slice(this.pos, this.pos + word.length) !== word) this.fail(`expected ${word}`);
|
|
1149
1176
|
this.pos += word.length;
|
|
1150
1177
|
}
|
|
1151
|
-
parseObject() {
|
|
1178
|
+
parseObject(depth) {
|
|
1152
1179
|
this.pos++;
|
|
1153
1180
|
const m = /* @__PURE__ */ new Map();
|
|
1154
1181
|
this.skipWs();
|
|
@@ -1164,7 +1191,7 @@ var JsonParser = class {
|
|
|
1164
1191
|
if (this.text[this.pos] !== ":") this.fail("expected :");
|
|
1165
1192
|
this.pos++;
|
|
1166
1193
|
this.skipWs();
|
|
1167
|
-
const value = this.parseValue();
|
|
1194
|
+
const value = this.parseValue(depth);
|
|
1168
1195
|
m.set(key, value);
|
|
1169
1196
|
this.skipWs();
|
|
1170
1197
|
const c = this.text[this.pos];
|
|
@@ -1179,7 +1206,7 @@ var JsonParser = class {
|
|
|
1179
1206
|
this.fail("expected , or }");
|
|
1180
1207
|
}
|
|
1181
1208
|
}
|
|
1182
|
-
parseArray() {
|
|
1209
|
+
parseArray(depth) {
|
|
1183
1210
|
this.pos++;
|
|
1184
1211
|
const items = [];
|
|
1185
1212
|
this.skipWs();
|
|
@@ -1189,7 +1216,7 @@ var JsonParser = class {
|
|
|
1189
1216
|
}
|
|
1190
1217
|
for (; ; ) {
|
|
1191
1218
|
this.skipWs();
|
|
1192
|
-
items.push(this.parseValue());
|
|
1219
|
+
items.push(this.parseValue(depth));
|
|
1193
1220
|
this.skipWs();
|
|
1194
1221
|
const c = this.text[this.pos];
|
|
1195
1222
|
if (c === ",") {
|
|
@@ -1305,8 +1332,8 @@ function escapeJsonString(s) {
|
|
|
1305
1332
|
return `${out}"`;
|
|
1306
1333
|
}
|
|
1307
1334
|
function jsonbKeyCompare(a, b) {
|
|
1308
|
-
const ea =
|
|
1309
|
-
const eb =
|
|
1335
|
+
const ea = JSON_TEXT_ENCODER.encode(a);
|
|
1336
|
+
const eb = JSON_TEXT_ENCODER.encode(b);
|
|
1310
1337
|
const n = Math.min(ea.length, eb.length);
|
|
1311
1338
|
for (let i = 0; i < n; i++) {
|
|
1312
1339
|
if (ea[i] !== eb[i]) return ea[i] < eb[i] ? -1 : 1;
|
|
@@ -1380,15 +1407,7 @@ function jsonbCompare(a, b) {
|
|
|
1380
1407
|
}
|
|
1381
1408
|
case "num": {
|
|
1382
1409
|
const bb = b;
|
|
1383
|
-
|
|
1384
|
-
const nb = numericStripTrailingZeros(bb.v);
|
|
1385
|
-
const ta = numericText(na);
|
|
1386
|
-
const tb = numericText(nb);
|
|
1387
|
-
if (ta === tb) return 0;
|
|
1388
|
-
const fa = Number(ta);
|
|
1389
|
-
const fb = Number(tb);
|
|
1390
|
-
if (fa !== fb) return fa < fb ? -1 : 1;
|
|
1391
|
-
return ta < tb ? -1 : 1;
|
|
1410
|
+
return numericCmp(numericStripTrailingZeros(a.v), numericStripTrailingZeros(bb.v));
|
|
1392
1411
|
}
|
|
1393
1412
|
case "str": {
|
|
1394
1413
|
const bb = b;
|
|
@@ -1418,6 +1437,8 @@ function jsonbCompare(a, b) {
|
|
|
1418
1437
|
}
|
|
1419
1438
|
return 0;
|
|
1420
1439
|
}
|
|
1440
|
+
default:
|
|
1441
|
+
return assertNever(a);
|
|
1421
1442
|
}
|
|
1422
1443
|
}
|
|
1423
1444
|
function jsonbContains(a, b) {
|
|
@@ -2196,6 +2217,74 @@ function tsvectorSetweight(vec, weight) {
|
|
|
2196
2217
|
}).join(" ");
|
|
2197
2218
|
}
|
|
2198
2219
|
|
|
2220
|
+
// src/types/jsonpath.ts
|
|
2221
|
+
function invalidJsonpath() {
|
|
2222
|
+
throw pgError("invalid_text_representation", "invalid input syntax for type jsonpath", "22P02");
|
|
2223
|
+
}
|
|
2224
|
+
function parseJsonpath(text) {
|
|
2225
|
+
const s = text.trim();
|
|
2226
|
+
if (s.length === 0 || s[0] !== "$") invalidJsonpath();
|
|
2227
|
+
let i = 1;
|
|
2228
|
+
const steps = [];
|
|
2229
|
+
while (i < s.length) {
|
|
2230
|
+
const c = s[i];
|
|
2231
|
+
if (c === " " || c === " " || c === "\n" || c === "\r") {
|
|
2232
|
+
i++;
|
|
2233
|
+
continue;
|
|
2234
|
+
}
|
|
2235
|
+
if (c === ".") {
|
|
2236
|
+
i++;
|
|
2237
|
+
if (s[i] === '"') {
|
|
2238
|
+
i++;
|
|
2239
|
+
let name = "";
|
|
2240
|
+
while (i < s.length && s[i] !== '"') {
|
|
2241
|
+
if (s[i] === "\\" && i + 1 < s.length) {
|
|
2242
|
+
name += s[i + 1];
|
|
2243
|
+
i += 2;
|
|
2244
|
+
} else {
|
|
2245
|
+
name += s[i];
|
|
2246
|
+
i++;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
if (s[i] !== '"') invalidJsonpath();
|
|
2250
|
+
i++;
|
|
2251
|
+
steps.push({ kind: "member", name });
|
|
2252
|
+
continue;
|
|
2253
|
+
}
|
|
2254
|
+
const m = /^[A-Za-z_][A-Za-z0-9_]*/.exec(s.slice(i));
|
|
2255
|
+
if (!m) invalidJsonpath();
|
|
2256
|
+
steps.push({ kind: "member", name: m[0] });
|
|
2257
|
+
i += m[0].length;
|
|
2258
|
+
continue;
|
|
2259
|
+
}
|
|
2260
|
+
if (c === "[") {
|
|
2261
|
+
const rest = s.slice(i);
|
|
2262
|
+
const m = /^\[\s*(\d+)\s*\]/.exec(rest);
|
|
2263
|
+
if (!m) invalidJsonpath();
|
|
2264
|
+
steps.push({ kind: "index", index: Number(m[1]) });
|
|
2265
|
+
i += m[0].length;
|
|
2266
|
+
continue;
|
|
2267
|
+
}
|
|
2268
|
+
invalidJsonpath();
|
|
2269
|
+
}
|
|
2270
|
+
return steps;
|
|
2271
|
+
}
|
|
2272
|
+
function jsonpathQueryFirst(doc, pathText) {
|
|
2273
|
+
const steps = parseJsonpath(pathText);
|
|
2274
|
+
let cur = doc;
|
|
2275
|
+
for (const step of steps) {
|
|
2276
|
+
if (cur === null) return null;
|
|
2277
|
+
if (step.kind === "member") {
|
|
2278
|
+
if (cur.j !== "obj") return null;
|
|
2279
|
+
cur = cur.v.get(step.name) ?? null;
|
|
2280
|
+
} else {
|
|
2281
|
+
if (cur.j !== "arr") return null;
|
|
2282
|
+
cur = cur.v[step.index] ?? null;
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
return cur;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2199
2288
|
// src/types/value.ts
|
|
2200
2289
|
function wrapJsonb(value) {
|
|
2201
2290
|
return { kind: "jsonb", value };
|
|
@@ -2257,6 +2346,7 @@ var TYPE_ALIASES = {
|
|
|
2257
2346
|
uuid: "uuid",
|
|
2258
2347
|
json: "json",
|
|
2259
2348
|
jsonb: "jsonb",
|
|
2349
|
+
jsonpath: "jsonpath",
|
|
2260
2350
|
regclass: "regclass",
|
|
2261
2351
|
regtype: "regtype",
|
|
2262
2352
|
regproc: "regproc",
|
|
@@ -2356,6 +2446,7 @@ var TYPE_OIDS = {
|
|
|
2356
2446
|
record: 2249,
|
|
2357
2447
|
uuid: 2950,
|
|
2358
2448
|
jsonb: 3802,
|
|
2449
|
+
jsonpath: 4072,
|
|
2359
2450
|
tsvector: 3614,
|
|
2360
2451
|
tsquery: 3615,
|
|
2361
2452
|
void: 2278,
|
|
@@ -2477,8 +2568,8 @@ function parseFloatText(text, type) {
|
|
|
2477
2568
|
throw pgError("numeric_value_out_of_range", `"${text}" is out of range for type real`);
|
|
2478
2569
|
}
|
|
2479
2570
|
}
|
|
2480
|
-
if (
|
|
2481
|
-
return
|
|
2571
|
+
if (Number(t) === 0 && t.startsWith("-")) return -0;
|
|
2572
|
+
return v;
|
|
2482
2573
|
}
|
|
2483
2574
|
var INT2_MIN = -32768;
|
|
2484
2575
|
var INT2_MAX = 32767;
|
|
@@ -2681,6 +2772,7 @@ function datumText(t, v, ctx) {
|
|
|
2681
2772
|
case "regclass":
|
|
2682
2773
|
case "regtype":
|
|
2683
2774
|
case "regproc":
|
|
2775
|
+
case "jsonpath":
|
|
2684
2776
|
case "tsvector":
|
|
2685
2777
|
case "tsquery":
|
|
2686
2778
|
case "bit":
|
|
@@ -2762,6 +2854,9 @@ function datumFromText(t, text, ctx) {
|
|
|
2762
2854
|
return text;
|
|
2763
2855
|
case "jsonb":
|
|
2764
2856
|
return wrapJsonb(parseJsonText(text));
|
|
2857
|
+
case "jsonpath":
|
|
2858
|
+
parseJsonpath(text);
|
|
2859
|
+
return text;
|
|
2765
2860
|
case "bytea":
|
|
2766
2861
|
return parseByteaText(text);
|
|
2767
2862
|
case "date":
|
|
@@ -3663,6 +3758,21 @@ function getJsonFunctions() {
|
|
|
3663
3758
|
});
|
|
3664
3759
|
m.set("json_typeof", typeofFn);
|
|
3665
3760
|
m.set("jsonb_typeof", typeofFn);
|
|
3761
|
+
m.set("jsonb_path_query_first", (ctx, args) => {
|
|
3762
|
+
if (args.length < 2) {
|
|
3763
|
+
throw pgError(
|
|
3764
|
+
"undefined_function",
|
|
3765
|
+
`function jsonb_path_query_first(${args.map((a) => a.t).join(", ")}) does not exist`,
|
|
3766
|
+
"42883"
|
|
3767
|
+
);
|
|
3768
|
+
}
|
|
3769
|
+
if (args[0].v === null || args[1].v === null) return tv("jsonb", null);
|
|
3770
|
+
const doc = jsonArg(ctx, args[0]);
|
|
3771
|
+
const path = args[1].t === "jsonpath" ? args[1].v : argText(ctx, args[1]);
|
|
3772
|
+
const found = jsonpathQueryFirst(doc, path);
|
|
3773
|
+
if (found === null) return tv("jsonb", null);
|
|
3774
|
+
return outJsonb(found);
|
|
3775
|
+
});
|
|
3666
3776
|
m.set(
|
|
3667
3777
|
"json_array_length",
|
|
3668
3778
|
strict("int4", (ctx, args) => {
|
|
@@ -5975,7 +6085,7 @@ function getMathFunctions() {
|
|
|
5975
6085
|
if (isFloatArg(a)) {
|
|
5976
6086
|
const f = argFloat(ctx, a);
|
|
5977
6087
|
const r = f >= 0 ? Math.round(f) : -Math.round(-f);
|
|
5978
|
-
return tv("float8",
|
|
6088
|
+
return tv("float8", r);
|
|
5979
6089
|
}
|
|
5980
6090
|
if (isIntType(a.t)) return tv(a.t, a.v);
|
|
5981
6091
|
return tv("numeric", numericRound(argNumeric(ctx, a), 0));
|
|
@@ -6429,6 +6539,7 @@ function parseQualifiedName(name) {
|
|
|
6429
6539
|
return parts.map((p) => p.trim()).filter((p) => p.length > 0);
|
|
6430
6540
|
}
|
|
6431
6541
|
function sequenceNextval(ctx, seq) {
|
|
6542
|
+
seq = ctx.state.ensureWritableSequence(seq);
|
|
6432
6543
|
let next;
|
|
6433
6544
|
if (!seq.isCalled) {
|
|
6434
6545
|
next = seq.lastValue;
|
|
@@ -6555,7 +6666,7 @@ function getMiscFunctions() {
|
|
|
6555
6666
|
m.set(
|
|
6556
6667
|
"setval",
|
|
6557
6668
|
strict("int8", (ctx, args) => {
|
|
6558
|
-
const seq = findSequenceForCall(ctx, argText(ctx, args[0]));
|
|
6669
|
+
const seq = ctx.state.ensureWritableSequence(findSequenceForCall(ctx, argText(ctx, args[0])));
|
|
6559
6670
|
const value = argBigInt(ctx, args[1]);
|
|
6560
6671
|
const isCalled = args.length > 2 ? args[2].v === true : true;
|
|
6561
6672
|
if (value < seq.minValue || value > seq.maxValue) {
|
|
@@ -8428,7 +8539,7 @@ function pgClass(ctx) {
|
|
|
8428
8539
|
for (const schemaData of state.schemas.values()) {
|
|
8429
8540
|
for (const t of schemaData.tables.values()) {
|
|
8430
8541
|
const hasIndex = t.constraints.some((c) => c.kind === "primary_key" || c.kind === "unique") || [...schemaData.indexes.values()].some((i) => i.table === t.name);
|
|
8431
|
-
push(t.oid, t.name, t.schema, "r", t.columns.length, t.
|
|
8542
|
+
push(t.oid, t.name, t.schema, "r", t.columns.length, t.rowCount(), hasIndex, true, t.temp);
|
|
8432
8543
|
}
|
|
8433
8544
|
for (const v of schemaData.views.values()) {
|
|
8434
8545
|
push(
|
|
@@ -9624,6 +9735,7 @@ var TYPE_LITERAL_NAMES = /* @__PURE__ */ new Set([
|
|
|
9624
9735
|
"uuid",
|
|
9625
9736
|
"json",
|
|
9626
9737
|
"jsonb",
|
|
9738
|
+
"jsonpath",
|
|
9627
9739
|
"money",
|
|
9628
9740
|
"name",
|
|
9629
9741
|
"oid",
|
|
@@ -10025,6 +10137,23 @@ var Parser = class {
|
|
|
10025
10137
|
skipToStatementEnd() {
|
|
10026
10138
|
while (this.peek().type !== "eof" && !this.atPunct(";")) this.pos++;
|
|
10027
10139
|
}
|
|
10140
|
+
/** Consume tokens until the matching `)` after an opening `(` already eaten. */
|
|
10141
|
+
skipBalancedCloseParen() {
|
|
10142
|
+
let depth = 1;
|
|
10143
|
+
while (depth > 0) {
|
|
10144
|
+
const t = this.peek();
|
|
10145
|
+
if (t.type === "eof") throw pgError("syntax", 'unterminated "(" in ALTER TABLE SET', "42601");
|
|
10146
|
+
if (this.eatPunct("(")) {
|
|
10147
|
+
depth++;
|
|
10148
|
+
continue;
|
|
10149
|
+
}
|
|
10150
|
+
if (this.eatPunct(")")) {
|
|
10151
|
+
depth--;
|
|
10152
|
+
continue;
|
|
10153
|
+
}
|
|
10154
|
+
this.pos++;
|
|
10155
|
+
}
|
|
10156
|
+
}
|
|
10028
10157
|
// --- WITH-able statements --------------------------------------------------
|
|
10029
10158
|
parseWithableStatement() {
|
|
10030
10159
|
if (this.atKw("with")) {
|
|
@@ -11921,6 +12050,10 @@ var Parser = class {
|
|
|
11921
12050
|
}
|
|
11922
12051
|
if (this.eatKw("set")) {
|
|
11923
12052
|
if (this.eatKw("schema")) return { kind: "set_schema", to: this.ident() };
|
|
12053
|
+
if (this.eatPunct("(")) {
|
|
12054
|
+
this.skipBalancedCloseParen();
|
|
12055
|
+
return { kind: "reloptions" };
|
|
12056
|
+
}
|
|
11924
12057
|
throw unsupported("this ALTER TABLE SET form");
|
|
11925
12058
|
}
|
|
11926
12059
|
if (this.eatKw("owner")) {
|
|
@@ -13149,6 +13282,55 @@ function parseSingle(sql) {
|
|
|
13149
13282
|
return statements[0];
|
|
13150
13283
|
}
|
|
13151
13284
|
|
|
13285
|
+
// src/types/resolve.ts
|
|
13286
|
+
var SERIALS = {
|
|
13287
|
+
smallserial: "int2",
|
|
13288
|
+
serial2: "int2",
|
|
13289
|
+
serial: "int4",
|
|
13290
|
+
serial4: "int4",
|
|
13291
|
+
bigserial: "int8",
|
|
13292
|
+
serial8: "int8"
|
|
13293
|
+
};
|
|
13294
|
+
function modOf(t, mods) {
|
|
13295
|
+
if (mods.length === 0) return null;
|
|
13296
|
+
if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
|
|
13297
|
+
return { a: mods[0] };
|
|
13298
|
+
}
|
|
13299
|
+
function resolveTypeName(state, tn) {
|
|
13300
|
+
const joined = tn.parts.join(".");
|
|
13301
|
+
const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
|
|
13302
|
+
if (bare !== null) {
|
|
13303
|
+
const serial = SERIALS[bare];
|
|
13304
|
+
if (serial !== void 0 && tn.arrayDims === 0) {
|
|
13305
|
+
return { column: { id: serial, mod: null }, domain: null, serial };
|
|
13306
|
+
}
|
|
13307
|
+
const builtin = normalizeTypeName(bare);
|
|
13308
|
+
if (builtin !== null) {
|
|
13309
|
+
const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
|
|
13310
|
+
return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
|
|
13311
|
+
}
|
|
13312
|
+
}
|
|
13313
|
+
const enumData = state.findEnum(tn.parts);
|
|
13314
|
+
if (enumData) {
|
|
13315
|
+
const elem = `enum:${enumData.schema}.${enumData.name}`;
|
|
13316
|
+
const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
|
|
13317
|
+
return { column: { id, mod: null }, domain: null, serial: null };
|
|
13318
|
+
}
|
|
13319
|
+
const domainData = state.findDomain(tn.parts);
|
|
13320
|
+
if (domainData) {
|
|
13321
|
+
if (tn.arrayDims > 0) {
|
|
13322
|
+
const id = arrayTypeOf(domainData.baseType.id);
|
|
13323
|
+
return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
|
|
13324
|
+
}
|
|
13325
|
+
return {
|
|
13326
|
+
column: domainData.baseType,
|
|
13327
|
+
domain: `${domainData.schema}.${domainData.name}`,
|
|
13328
|
+
serial: null
|
|
13329
|
+
};
|
|
13330
|
+
}
|
|
13331
|
+
throw pgError("undefined_object", `type "${joined}" does not exist`);
|
|
13332
|
+
}
|
|
13333
|
+
|
|
13152
13334
|
// src/executor/relation.ts
|
|
13153
13335
|
var RowScope = class {
|
|
13154
13336
|
constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
|
|
@@ -13283,55 +13465,6 @@ function inferColumnName(e) {
|
|
|
13283
13465
|
}
|
|
13284
13466
|
}
|
|
13285
13467
|
|
|
13286
|
-
// src/types/resolve.ts
|
|
13287
|
-
var SERIALS = {
|
|
13288
|
-
smallserial: "int2",
|
|
13289
|
-
serial2: "int2",
|
|
13290
|
-
serial: "int4",
|
|
13291
|
-
serial4: "int4",
|
|
13292
|
-
bigserial: "int8",
|
|
13293
|
-
serial8: "int8"
|
|
13294
|
-
};
|
|
13295
|
-
function modOf(t, mods) {
|
|
13296
|
-
if (mods.length === 0) return null;
|
|
13297
|
-
if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
|
|
13298
|
-
return { a: mods[0] };
|
|
13299
|
-
}
|
|
13300
|
-
function resolveTypeName(state, tn) {
|
|
13301
|
-
const joined = tn.parts.join(".");
|
|
13302
|
-
const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
|
|
13303
|
-
if (bare !== null) {
|
|
13304
|
-
const serial = SERIALS[bare];
|
|
13305
|
-
if (serial !== void 0 && tn.arrayDims === 0) {
|
|
13306
|
-
return { column: { id: serial, mod: null }, domain: null, serial };
|
|
13307
|
-
}
|
|
13308
|
-
const builtin = normalizeTypeName(bare);
|
|
13309
|
-
if (builtin !== null) {
|
|
13310
|
-
const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
|
|
13311
|
-
return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
|
|
13312
|
-
}
|
|
13313
|
-
}
|
|
13314
|
-
const enumData = state.findEnum(tn.parts);
|
|
13315
|
-
if (enumData) {
|
|
13316
|
-
const elem = `enum:${enumData.schema}.${enumData.name}`;
|
|
13317
|
-
const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
|
|
13318
|
-
return { column: { id, mod: null }, domain: null, serial: null };
|
|
13319
|
-
}
|
|
13320
|
-
const domainData = state.findDomain(tn.parts);
|
|
13321
|
-
if (domainData) {
|
|
13322
|
-
if (tn.arrayDims > 0) {
|
|
13323
|
-
const id = arrayTypeOf(domainData.baseType.id);
|
|
13324
|
-
return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
|
|
13325
|
-
}
|
|
13326
|
-
return {
|
|
13327
|
-
column: domainData.baseType,
|
|
13328
|
-
domain: `${domainData.schema}.${domainData.name}`,
|
|
13329
|
-
serial: null
|
|
13330
|
-
};
|
|
13331
|
-
}
|
|
13332
|
-
throw pgError("undefined_object", `type "${joined}" does not exist`);
|
|
13333
|
-
}
|
|
13334
|
-
|
|
13335
13468
|
// src/expressions/operators.ts
|
|
13336
13469
|
var NUM_LADDER = ["int2", "int4", "int8", "numeric", "float4", "float8"];
|
|
13337
13470
|
function opNotExist(op, l, r) {
|
|
@@ -14954,12 +15087,12 @@ function evalFuncCall(ctx, scope, e) {
|
|
|
14954
15087
|
if (bare !== null) {
|
|
14955
15088
|
const stateful = scope.callStatefulFunction?.(bare, args);
|
|
14956
15089
|
if (stateful !== void 0) return stateful;
|
|
14957
|
-
if (hasScalarFunction(bare)) {
|
|
14958
|
-
return callScalarFunction(ctx, bare, args);
|
|
14959
|
-
}
|
|
14960
15090
|
}
|
|
14961
15091
|
const user = scope.callUserFunction?.(e.name, args, e);
|
|
14962
15092
|
if (user !== void 0) return user;
|
|
15093
|
+
if (bare !== null && hasScalarFunction(bare)) {
|
|
15094
|
+
return callScalarFunction(ctx, bare, args);
|
|
15095
|
+
}
|
|
14963
15096
|
throw pgError(
|
|
14964
15097
|
"undefined_function",
|
|
14965
15098
|
`function ${e.name.join(".")}(${args.map((a) => a.t).join(", ")}) does not exist`
|
|
@@ -15075,82 +15208,524 @@ function evalLazyFunc(ctx, scope, name, argExprs) {
|
|
|
15075
15208
|
}
|
|
15076
15209
|
}
|
|
15077
15210
|
|
|
15078
|
-
// src/
|
|
15079
|
-
function
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
const bv = b.values[i];
|
|
15083
|
-
const desc = a.dirs[i] === "desc";
|
|
15084
|
-
const nullsFirst = a.nullsFirst[i];
|
|
15085
|
-
if (av === null || bv === null) {
|
|
15086
|
-
if (av === null && bv === null) continue;
|
|
15087
|
-
const nullCmp = av === null ? -1 : 1;
|
|
15088
|
-
const cmp = nullsFirst ? nullCmp : -nullCmp;
|
|
15089
|
-
if (cmp !== 0) return cmp;
|
|
15090
|
-
continue;
|
|
15091
|
-
}
|
|
15092
|
-
let c = datumCompare(a.types[i], av, bv, ctx);
|
|
15093
|
-
if (desc) c = -c;
|
|
15094
|
-
if (c !== 0) return c;
|
|
15095
|
-
}
|
|
15096
|
-
return 0;
|
|
15097
|
-
}
|
|
15098
|
-
function computeWindowValues(ctx, calls, rowCount, evalAt, namedWindows) {
|
|
15099
|
-
const out = /* @__PURE__ */ new Map();
|
|
15100
|
-
for (const call of calls) {
|
|
15101
|
-
const spec = resolveWindowSpec(call.over, namedWindows);
|
|
15102
|
-
out.set(call, computeOneWindow(ctx, call, spec, rowCount, evalAt));
|
|
15103
|
-
}
|
|
15104
|
-
return out;
|
|
15105
|
-
}
|
|
15106
|
-
function resolveWindowSpec(spec, named) {
|
|
15107
|
-
if (!spec.name) return spec;
|
|
15108
|
-
const base = named.find((w) => w.name === spec.name);
|
|
15109
|
-
if (!base) throw pgError("undefined_object", `window "${spec.name}" does not exist`, "42704");
|
|
15110
|
-
return {
|
|
15111
|
-
partitionBy: base.spec.partitionBy.length > 0 ? base.spec.partitionBy : spec.partitionBy,
|
|
15112
|
-
orderBy: base.spec.orderBy.length > 0 ? base.spec.orderBy : spec.orderBy,
|
|
15113
|
-
frame: spec.frame ?? base.spec.frame
|
|
15114
|
-
};
|
|
15211
|
+
// src/constraints/enforce.ts
|
|
15212
|
+
function tableScope(table, row) {
|
|
15213
|
+
const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
|
|
15214
|
+
return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
|
|
15115
15215
|
}
|
|
15116
|
-
function
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
|
|
15216
|
+
function checkNotNull(env, table, row) {
|
|
15217
|
+
void env;
|
|
15218
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
15219
|
+
const c = table.columns[i];
|
|
15220
|
+
if (c.notNull && (row[i] ?? null) === null) {
|
|
15221
|
+
throw pgError(
|
|
15222
|
+
"not_null_violation",
|
|
15223
|
+
`null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
|
|
15224
|
+
"23502"
|
|
15225
|
+
);
|
|
15124
15226
|
}
|
|
15125
|
-
const key = keyParts.join("");
|
|
15126
|
-
partitionOf.push(key);
|
|
15127
|
-
const list = partitions.get(key) ?? [];
|
|
15128
|
-
list.push(i);
|
|
15129
|
-
partitions.set(key, list);
|
|
15130
|
-
}
|
|
15131
|
-
const results = new Array(rowCount);
|
|
15132
|
-
for (const rowIdxs of partitions.values()) {
|
|
15133
|
-
computePartition(ctx, call, spec, rowIdxs, evalAt, results);
|
|
15134
15227
|
}
|
|
15135
|
-
return results;
|
|
15136
15228
|
}
|
|
15137
|
-
function
|
|
15138
|
-
const
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15150
|
-
const same = spec.orderBy.length === 0 || compareKeys(ctx, keys.get(rowIdxs[pos - 1]), keys.get(rowIdxs[pos])) === 0;
|
|
15151
|
-
if (!same) group++;
|
|
15229
|
+
function checkChecks(env, table, row) {
|
|
15230
|
+
for (const con of table.constraints) {
|
|
15231
|
+
if (con.kind !== "check") continue;
|
|
15232
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
15233
|
+
const v = evalExpr(env.ctx, scope, con.expr);
|
|
15234
|
+
if (v.v === null) continue;
|
|
15235
|
+
const b = castTo(env.ctx, v, "bool", {});
|
|
15236
|
+
if (b.v !== true) {
|
|
15237
|
+
throw pgError(
|
|
15238
|
+
"check_violation",
|
|
15239
|
+
`new row for relation "${table.name}" violates check constraint "${con.name}"`,
|
|
15240
|
+
"23514"
|
|
15241
|
+
);
|
|
15152
15242
|
}
|
|
15153
|
-
|
|
15243
|
+
}
|
|
15244
|
+
}
|
|
15245
|
+
function uniqueSpecsFor(env, table) {
|
|
15246
|
+
const specs = [];
|
|
15247
|
+
for (const con of table.constraints) {
|
|
15248
|
+
if (con.kind !== "primary_key" && con.kind !== "unique") continue;
|
|
15249
|
+
specs.push({
|
|
15250
|
+
name: con.name,
|
|
15251
|
+
keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
|
|
15252
|
+
columnNames: con.columns,
|
|
15253
|
+
nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
|
|
15254
|
+
where: null,
|
|
15255
|
+
isPrimary: con.kind === "primary_key"
|
|
15256
|
+
});
|
|
15257
|
+
}
|
|
15258
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
15259
|
+
if (schema) {
|
|
15260
|
+
for (const idx of schema.indexes.values()) {
|
|
15261
|
+
if (idx.table !== table.name || !idx.unique || idx.isConstraint) continue;
|
|
15262
|
+
specs.push({
|
|
15263
|
+
name: idx.name,
|
|
15264
|
+
keys: idx.columns.map((c) => ({
|
|
15265
|
+
colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
|
|
15266
|
+
expr: c.expr
|
|
15267
|
+
})),
|
|
15268
|
+
columnNames: idx.columns.map((c) => c.column ?? "expr"),
|
|
15269
|
+
nullsNotDistinct: idx.nullsNotDistinct,
|
|
15270
|
+
where: idx.where,
|
|
15271
|
+
isPrimary: false
|
|
15272
|
+
});
|
|
15273
|
+
}
|
|
15274
|
+
}
|
|
15275
|
+
return specs;
|
|
15276
|
+
}
|
|
15277
|
+
function uniqueKeyOf(env, table, spec, row) {
|
|
15278
|
+
if (spec.where) {
|
|
15279
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
15280
|
+
const v = evalExpr(env.ctx, scope, spec.where);
|
|
15281
|
+
if (v.v !== true) return null;
|
|
15282
|
+
}
|
|
15283
|
+
const parts = [];
|
|
15284
|
+
let hasNull = false;
|
|
15285
|
+
for (const k of spec.keys) {
|
|
15286
|
+
let value;
|
|
15287
|
+
let type;
|
|
15288
|
+
if (k.expr) {
|
|
15289
|
+
const scope = makeEvalScope(env, tableScope(table, row));
|
|
15290
|
+
const v = evalExpr(env.ctx, scope, k.expr);
|
|
15291
|
+
value = v.v;
|
|
15292
|
+
type = v.t === "unknown" ? "text" : v.t;
|
|
15293
|
+
} else {
|
|
15294
|
+
value = row[k.colIdx] ?? null;
|
|
15295
|
+
type = table.columns[k.colIdx].type.id;
|
|
15296
|
+
}
|
|
15297
|
+
if (value === null) {
|
|
15298
|
+
hasNull = true;
|
|
15299
|
+
parts.push("\0N");
|
|
15300
|
+
} else {
|
|
15301
|
+
parts.push(datumKey(type, value));
|
|
15302
|
+
}
|
|
15303
|
+
}
|
|
15304
|
+
if (hasNull && !spec.nullsNotDistinct) return null;
|
|
15305
|
+
return parts.join("");
|
|
15306
|
+
}
|
|
15307
|
+
function checkUnique(env, table, row, selfIdx) {
|
|
15308
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15309
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
15310
|
+
if (key === null) continue;
|
|
15311
|
+
indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
|
|
15312
|
+
}
|
|
15313
|
+
}
|
|
15314
|
+
function findConflict(env, table, spec, row) {
|
|
15315
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
15316
|
+
if (key === null) return null;
|
|
15317
|
+
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
15318
|
+
return hits.length > 0 ? hits[0] : null;
|
|
15319
|
+
}
|
|
15320
|
+
function checkForeignKeys(env, table, row) {
|
|
15321
|
+
const state = env.ctx.state;
|
|
15322
|
+
for (const con of table.constraints) {
|
|
15323
|
+
if (con.kind !== "foreign_key") continue;
|
|
15324
|
+
const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
|
|
15325
|
+
const nulls = values.filter((v) => v === null).length;
|
|
15326
|
+
if (con.match === "simple" && nulls > 0) continue;
|
|
15327
|
+
if (con.match === "full") {
|
|
15328
|
+
if (nulls === values.length) continue;
|
|
15329
|
+
if (nulls > 0) {
|
|
15330
|
+
throw pgError(
|
|
15331
|
+
"constraint_foreign_key",
|
|
15332
|
+
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
15333
|
+
"23503"
|
|
15334
|
+
);
|
|
15335
|
+
}
|
|
15336
|
+
}
|
|
15337
|
+
const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
|
|
15338
|
+
if (!refTable) {
|
|
15339
|
+
throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
|
|
15340
|
+
}
|
|
15341
|
+
const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
|
|
15342
|
+
const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
|
|
15343
|
+
const wanted = values.map((v, i) => {
|
|
15344
|
+
const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
|
|
15345
|
+
const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
|
|
15346
|
+
return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
|
|
15347
|
+
}).join("");
|
|
15348
|
+
let found = false;
|
|
15349
|
+
for (let i = 0; i < refTable.rowCount(); i++) {
|
|
15350
|
+
const r = refTable.rowAt(i);
|
|
15351
|
+
const key = refIdxs.map((ri, j) => {
|
|
15352
|
+
const v = r[ri] ?? null;
|
|
15353
|
+
return v === null ? "\0N" : datumKey(keyTypes[j], v);
|
|
15354
|
+
}).join("");
|
|
15355
|
+
if (key === wanted) {
|
|
15356
|
+
found = true;
|
|
15357
|
+
break;
|
|
15358
|
+
}
|
|
15359
|
+
}
|
|
15360
|
+
if (!found) {
|
|
15361
|
+
throw pgError(
|
|
15362
|
+
"constraint_foreign_key",
|
|
15363
|
+
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
15364
|
+
"23503"
|
|
15365
|
+
);
|
|
15366
|
+
}
|
|
15367
|
+
}
|
|
15368
|
+
}
|
|
15369
|
+
function referencingConstraints(env, table) {
|
|
15370
|
+
const out = [];
|
|
15371
|
+
for (const schema of env.ctx.state.schemas.values()) {
|
|
15372
|
+
for (const t of schema.tables.values()) {
|
|
15373
|
+
for (const con of t.constraints) {
|
|
15374
|
+
if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
|
|
15375
|
+
out.push({ table: t, constraint: con });
|
|
15376
|
+
}
|
|
15377
|
+
}
|
|
15378
|
+
}
|
|
15379
|
+
}
|
|
15380
|
+
return out;
|
|
15381
|
+
}
|
|
15382
|
+
|
|
15383
|
+
// src/indexes/index.ts
|
|
15384
|
+
var IndexStore = class _IndexStore {
|
|
15385
|
+
name;
|
|
15386
|
+
unique;
|
|
15387
|
+
entries = /* @__PURE__ */ new Map();
|
|
15388
|
+
constructor(name, unique = false) {
|
|
15389
|
+
this.name = name;
|
|
15390
|
+
this.unique = unique;
|
|
15391
|
+
}
|
|
15392
|
+
get size() {
|
|
15393
|
+
return this.entries.size;
|
|
15394
|
+
}
|
|
15395
|
+
checkUnique(key, rowIdx) {
|
|
15396
|
+
const existing = this.entries.get(key);
|
|
15397
|
+
if (!existing) return;
|
|
15398
|
+
for (const i of existing) {
|
|
15399
|
+
if (rowIdx === void 0 || i !== rowIdx) {
|
|
15400
|
+
throw pgError("constraint_unique", `duplicate key value violates unique constraint "${this.name}"`, "23505");
|
|
15401
|
+
}
|
|
15402
|
+
}
|
|
15403
|
+
}
|
|
15404
|
+
insert(key, rowIdx) {
|
|
15405
|
+
if (this.unique) this.checkUnique(key, rowIdx);
|
|
15406
|
+
const existing = this.entries.get(key);
|
|
15407
|
+
if (!existing) {
|
|
15408
|
+
this.entries.set(key, [rowIdx]);
|
|
15409
|
+
return;
|
|
15410
|
+
}
|
|
15411
|
+
if (existing.includes(rowIdx)) return;
|
|
15412
|
+
existing.push(rowIdx);
|
|
15413
|
+
}
|
|
15414
|
+
remove(key, rowIdx) {
|
|
15415
|
+
const existing = this.entries.get(key);
|
|
15416
|
+
if (!existing) return;
|
|
15417
|
+
const at = existing.indexOf(rowIdx);
|
|
15418
|
+
if (at < 0) return;
|
|
15419
|
+
existing.splice(at, 1);
|
|
15420
|
+
if (existing.length === 0) this.entries.delete(key);
|
|
15421
|
+
}
|
|
15422
|
+
lookup(key) {
|
|
15423
|
+
return this.entries.get(key) ?? [];
|
|
15424
|
+
}
|
|
15425
|
+
clear() {
|
|
15426
|
+
this.entries.clear();
|
|
15427
|
+
}
|
|
15428
|
+
clone() {
|
|
15429
|
+
const copy = new _IndexStore(this.name, this.unique);
|
|
15430
|
+
for (const [key, rowids] of this.entries) copy.entries.set(key, [...rowids]);
|
|
15431
|
+
return copy;
|
|
15432
|
+
}
|
|
15433
|
+
};
|
|
15434
|
+
|
|
15435
|
+
// src/indexes/maintain.ts
|
|
15436
|
+
function ensureIndexStores(env, table) {
|
|
15437
|
+
if (table.indexStores) return table.indexStores;
|
|
15438
|
+
const stores = /* @__PURE__ */ new Map();
|
|
15439
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15440
|
+
const store = new IndexStore(spec.name, true);
|
|
15441
|
+
for (let i = 0; i < table.rowCount(); i++) {
|
|
15442
|
+
const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
|
|
15443
|
+
if (key !== null) store.insert(key, i);
|
|
15444
|
+
}
|
|
15445
|
+
stores.set(spec.name, store);
|
|
15446
|
+
}
|
|
15447
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
15448
|
+
if (schema) {
|
|
15449
|
+
for (const idx of schema.indexes.values()) {
|
|
15450
|
+
if (idx.table !== table.name || idx.unique) continue;
|
|
15451
|
+
stores.set(idx.name, new IndexStore(idx.name, false));
|
|
15452
|
+
}
|
|
15453
|
+
}
|
|
15454
|
+
table.indexStores = stores;
|
|
15455
|
+
return stores;
|
|
15456
|
+
}
|
|
15457
|
+
function indexStoreFor(env, table, spec) {
|
|
15458
|
+
const stores = ensureIndexStores(env, table);
|
|
15459
|
+
let store = stores.get(spec.name);
|
|
15460
|
+
if (!store) {
|
|
15461
|
+
store = new IndexStore(spec.name, true);
|
|
15462
|
+
for (let i = 0; i < table.rowCount(); i++) {
|
|
15463
|
+
const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
|
|
15464
|
+
if (key !== null) store.insert(key, i);
|
|
15465
|
+
}
|
|
15466
|
+
stores.set(spec.name, store);
|
|
15467
|
+
}
|
|
15468
|
+
return store;
|
|
15469
|
+
}
|
|
15470
|
+
function indexInsertRow(env, table, rowIdx, row) {
|
|
15471
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15472
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
15473
|
+
if (key === null) continue;
|
|
15474
|
+
indexStoreFor(env, table, spec).insert(key, rowIdx);
|
|
15475
|
+
}
|
|
15476
|
+
}
|
|
15477
|
+
function indexRemoveRow(env, table, rowIdx, row) {
|
|
15478
|
+
if (!table.indexStores) return;
|
|
15479
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15480
|
+
const key = uniqueKeyOf(env, table, spec, row);
|
|
15481
|
+
if (key === null) continue;
|
|
15482
|
+
table.indexStores.get(spec.name)?.remove(key, rowIdx);
|
|
15483
|
+
}
|
|
15484
|
+
}
|
|
15485
|
+
function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
|
|
15486
|
+
indexRemoveRow(env, table, rowIdx, oldRow);
|
|
15487
|
+
indexInsertRow(env, table, rowIdx, newRow);
|
|
15488
|
+
}
|
|
15489
|
+
function rebuildTableIndexes(env, table) {
|
|
15490
|
+
table.indexStores = null;
|
|
15491
|
+
ensureIndexStores(env, table);
|
|
15492
|
+
}
|
|
15493
|
+
|
|
15494
|
+
// src/planner/access.ts
|
|
15495
|
+
function isRowIndependentExpr(expr) {
|
|
15496
|
+
return expr.type !== "colref";
|
|
15497
|
+
}
|
|
15498
|
+
function equalityAgainstConst(expr) {
|
|
15499
|
+
if (expr.type !== "binop" || expr.op !== "=") return null;
|
|
15500
|
+
if (expr.left.type === "colref" && isRowIndependentExpr(expr.right)) {
|
|
15501
|
+
const parts = expr.left.parts;
|
|
15502
|
+
const column = parts[parts.length - 1];
|
|
15503
|
+
return { column, valueExpr: expr.right };
|
|
15504
|
+
}
|
|
15505
|
+
if (expr.right.type === "colref" && isRowIndependentExpr(expr.left)) {
|
|
15506
|
+
const parts = expr.right.parts;
|
|
15507
|
+
const column = parts[parts.length - 1];
|
|
15508
|
+
return { column, valueExpr: expr.left };
|
|
15509
|
+
}
|
|
15510
|
+
return null;
|
|
15511
|
+
}
|
|
15512
|
+
function conjunctions(expr) {
|
|
15513
|
+
if (expr.type === "binop" && expr.op === "and") {
|
|
15514
|
+
return [...conjunctions(expr.left), ...conjunctions(expr.right)];
|
|
15515
|
+
}
|
|
15516
|
+
return [expr];
|
|
15517
|
+
}
|
|
15518
|
+
function tryIndexedTableRows(env, table, alias, where) {
|
|
15519
|
+
const parts = conjunctions(where);
|
|
15520
|
+
const eqs = [];
|
|
15521
|
+
for (const part of parts) {
|
|
15522
|
+
const eq = equalityAgainstConst(part);
|
|
15523
|
+
if (!eq) return null;
|
|
15524
|
+
eqs.push(eq);
|
|
15525
|
+
}
|
|
15526
|
+
if (eqs.length === 0) return null;
|
|
15527
|
+
let spec = null;
|
|
15528
|
+
for (const candidate of uniqueSpecsFor(env, table)) {
|
|
15529
|
+
if (candidate.keys.length !== eqs.length) continue;
|
|
15530
|
+
const names = candidate.columnNames;
|
|
15531
|
+
if (eqs.every((eq, i) => names[i] === eq.column)) {
|
|
15532
|
+
spec = candidate;
|
|
15533
|
+
break;
|
|
15534
|
+
}
|
|
15535
|
+
}
|
|
15536
|
+
if (!spec) return null;
|
|
15537
|
+
const probeRow = table.columns.map(() => null);
|
|
15538
|
+
const scope = new RowScope(
|
|
15539
|
+
table.columns.map((c) => ({ name: c.name, type: c.type.id, table: alias })),
|
|
15540
|
+
probeRow,
|
|
15541
|
+
env.outer,
|
|
15542
|
+
/* @__PURE__ */ new Set([alias])
|
|
15543
|
+
);
|
|
15544
|
+
for (let i = 0; i < eqs.length; i++) {
|
|
15545
|
+
const k = spec.keys[i];
|
|
15546
|
+
if (k.expr) return null;
|
|
15547
|
+
const colIdx = k.colIdx;
|
|
15548
|
+
const v = evalExpr(env.ctx, makeEvalScope(env, scope), eqs[i].valueExpr);
|
|
15549
|
+
probeRow[colIdx] = castTo(env.ctx, v, table.columns[colIdx].type.id, {}).v;
|
|
15550
|
+
}
|
|
15551
|
+
const key = uniqueKeyOf(env, table, spec, probeRow);
|
|
15552
|
+
if (key === null) return [];
|
|
15553
|
+
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
15554
|
+
return hits.map((i) => table.rowAt(i));
|
|
15555
|
+
}
|
|
15556
|
+
function tryIndexedFromItem(env, item, where) {
|
|
15557
|
+
if (!where || item.type !== "from_table") return null;
|
|
15558
|
+
const state = env.ctx.state;
|
|
15559
|
+
const table = state.findTable(item.name);
|
|
15560
|
+
if (!table) return null;
|
|
15561
|
+
const alias = item.alias ?? item.name[item.name.length - 1];
|
|
15562
|
+
table.materializeSlab();
|
|
15563
|
+
return tryIndexedTableRows(env, table, alias, where);
|
|
15564
|
+
}
|
|
15565
|
+
function joinKeyFromRow(row, colIdxs, srcTypes, unifiedTypes, ctx) {
|
|
15566
|
+
const parts = [];
|
|
15567
|
+
for (let i = 0; i < colIdxs.length; i++) {
|
|
15568
|
+
const v = row[colIdxs[i]] ?? null;
|
|
15569
|
+
if (v === null) return null;
|
|
15570
|
+
const cast = castTo(ctx, tv(srcTypes[i], v), unifiedTypes[i], {}).v;
|
|
15571
|
+
if (cast === null) return null;
|
|
15572
|
+
parts.push(datumKey(unifiedTypes[i], cast));
|
|
15573
|
+
}
|
|
15574
|
+
return parts.join("");
|
|
15575
|
+
}
|
|
15576
|
+
function rowsMatchEqKeys(ctx, left, right, leftIdxs, rightIdxs, leftTypes, rightTypes, unifiedTypes) {
|
|
15577
|
+
for (let k = 0; k < leftIdxs.length; k++) {
|
|
15578
|
+
const lv = left[leftIdxs[k]] ?? null;
|
|
15579
|
+
const rv = right[rightIdxs[k]] ?? null;
|
|
15580
|
+
if (lv === null || rv === null) return false;
|
|
15581
|
+
const lc = castTo(ctx, tv(leftTypes[k], lv), unifiedTypes[k], {}).v;
|
|
15582
|
+
const rc = castTo(ctx, tv(rightTypes[k], rv), unifiedTypes[k], {}).v;
|
|
15583
|
+
if (lc === null || rc === null) return false;
|
|
15584
|
+
if (datumCompare(unifiedTypes[k], lc, rc, ctx) !== 0) return false;
|
|
15585
|
+
}
|
|
15586
|
+
return true;
|
|
15587
|
+
}
|
|
15588
|
+
|
|
15589
|
+
// src/api/bind.ts
|
|
15590
|
+
var INT4_MIN2 = -2147483648;
|
|
15591
|
+
var INT4_MAX2 = 2147483647;
|
|
15592
|
+
function bindValueToTyped(value, index) {
|
|
15593
|
+
if (value === null || value === void 0) return tv(UNKNOWN, null);
|
|
15594
|
+
switch (typeof value) {
|
|
15595
|
+
case "boolean":
|
|
15596
|
+
return tv("bool", value);
|
|
15597
|
+
case "number":
|
|
15598
|
+
if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
|
|
15599
|
+
return tv("int4", value);
|
|
15600
|
+
}
|
|
15601
|
+
if (Number.isInteger(value) && Number.isSafeInteger(value)) {
|
|
15602
|
+
return tv("int8", BigInt(value));
|
|
15603
|
+
}
|
|
15604
|
+
return tv("float8", value);
|
|
15605
|
+
case "bigint":
|
|
15606
|
+
if (value < -9223372036854775808n || value > 9223372036854775807n) {
|
|
15607
|
+
throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
|
|
15608
|
+
}
|
|
15609
|
+
return tv("int8", value);
|
|
15610
|
+
case "string":
|
|
15611
|
+
return tv(UNKNOWN, value);
|
|
15612
|
+
case "object": {
|
|
15613
|
+
if (value instanceof Uint8Array) return tv("bytea", value);
|
|
15614
|
+
if (value instanceof Date) {
|
|
15615
|
+
const ms = value.getTime();
|
|
15616
|
+
if (Number.isNaN(ms)) {
|
|
15617
|
+
throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
|
|
15618
|
+
}
|
|
15619
|
+
return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
|
|
15620
|
+
}
|
|
15621
|
+
break;
|
|
15622
|
+
}
|
|
15623
|
+
default:
|
|
15624
|
+
break;
|
|
15625
|
+
}
|
|
15626
|
+
throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
|
|
15627
|
+
}
|
|
15628
|
+
function datumToJs(t, v, ctx, int8 = "bigint") {
|
|
15629
|
+
if (v === null) return null;
|
|
15630
|
+
switch (t) {
|
|
15631
|
+
case "bool":
|
|
15632
|
+
return v;
|
|
15633
|
+
case "int2":
|
|
15634
|
+
case "int4":
|
|
15635
|
+
case "oid":
|
|
15636
|
+
return typeof v === "bigint" ? Number(v) : v;
|
|
15637
|
+
case "int8": {
|
|
15638
|
+
const n = typeof v === "bigint" ? v : BigInt(v);
|
|
15639
|
+
if (int8 === "string") return n.toString();
|
|
15640
|
+
if (int8 === "number") return Number(n);
|
|
15641
|
+
return n;
|
|
15642
|
+
}
|
|
15643
|
+
case "float4":
|
|
15644
|
+
case "float8":
|
|
15645
|
+
return v;
|
|
15646
|
+
case "bytea":
|
|
15647
|
+
return v;
|
|
15648
|
+
default:
|
|
15649
|
+
return datumText(t === UNKNOWN ? "text" : t, v, ctx);
|
|
15650
|
+
}
|
|
15651
|
+
}
|
|
15652
|
+
|
|
15653
|
+
// src/executor/window.ts
|
|
15654
|
+
function compareKeys(ctx, a, b) {
|
|
15655
|
+
for (let i = 0; i < a.values.length; i++) {
|
|
15656
|
+
const av = a.values[i];
|
|
15657
|
+
const bv = b.values[i];
|
|
15658
|
+
const desc = a.dirs[i] === "desc";
|
|
15659
|
+
const nullsFirst = a.nullsFirst[i];
|
|
15660
|
+
if (av === null || bv === null) {
|
|
15661
|
+
if (av === null && bv === null) continue;
|
|
15662
|
+
const nullCmp = av === null ? -1 : 1;
|
|
15663
|
+
const cmp = nullsFirst ? nullCmp : -nullCmp;
|
|
15664
|
+
if (cmp !== 0) return cmp;
|
|
15665
|
+
continue;
|
|
15666
|
+
}
|
|
15667
|
+
let c = datumCompare(a.types[i], av, bv, ctx);
|
|
15668
|
+
if (desc) c = -c;
|
|
15669
|
+
if (c !== 0) return c;
|
|
15670
|
+
}
|
|
15671
|
+
return 0;
|
|
15672
|
+
}
|
|
15673
|
+
function computeWindowValues(ctx, calls, rowCount, evalAt, namedWindows) {
|
|
15674
|
+
const out = /* @__PURE__ */ new Map();
|
|
15675
|
+
for (const call of calls) {
|
|
15676
|
+
const spec = resolveWindowSpec(call.over, namedWindows);
|
|
15677
|
+
out.set(call, computeOneWindow(ctx, call, spec, rowCount, evalAt));
|
|
15678
|
+
}
|
|
15679
|
+
return out;
|
|
15680
|
+
}
|
|
15681
|
+
function resolveWindowSpec(spec, named) {
|
|
15682
|
+
if (!spec.name) return spec;
|
|
15683
|
+
const base = named.find((w) => w.name === spec.name);
|
|
15684
|
+
if (!base) throw pgError("undefined_object", `window "${spec.name}" does not exist`, "42704");
|
|
15685
|
+
return {
|
|
15686
|
+
partitionBy: base.spec.partitionBy.length > 0 ? base.spec.partitionBy : spec.partitionBy,
|
|
15687
|
+
orderBy: base.spec.orderBy.length > 0 ? base.spec.orderBy : spec.orderBy,
|
|
15688
|
+
frame: spec.frame ?? base.spec.frame
|
|
15689
|
+
};
|
|
15690
|
+
}
|
|
15691
|
+
function computeOneWindow(ctx, call, spec, rowCount, evalAt) {
|
|
15692
|
+
const partitions = /* @__PURE__ */ new Map();
|
|
15693
|
+
const partitionOf = [];
|
|
15694
|
+
for (let i = 0; i < rowCount; i++) {
|
|
15695
|
+
const keyParts = [];
|
|
15696
|
+
for (const p of spec.partitionBy) {
|
|
15697
|
+
const v = evalAt(i, p);
|
|
15698
|
+
keyParts.push(v.v === null ? "\0N" : datumKey(v.t === UNKNOWN ? "text" : v.t, v.v));
|
|
15699
|
+
}
|
|
15700
|
+
const key = keyParts.join("");
|
|
15701
|
+
partitionOf.push(key);
|
|
15702
|
+
const list = partitions.get(key) ?? [];
|
|
15703
|
+
list.push(i);
|
|
15704
|
+
partitions.set(key, list);
|
|
15705
|
+
}
|
|
15706
|
+
const results = new Array(rowCount);
|
|
15707
|
+
for (const rowIdxs of partitions.values()) {
|
|
15708
|
+
computePartition(ctx, call, spec, rowIdxs, evalAt, results);
|
|
15709
|
+
}
|
|
15710
|
+
return results;
|
|
15711
|
+
}
|
|
15712
|
+
function computePartition(ctx, call, spec, rowIdxs, evalAt, results) {
|
|
15713
|
+
const keys = /* @__PURE__ */ new Map();
|
|
15714
|
+
if (spec.orderBy.length > 0) {
|
|
15715
|
+
for (const i of rowIdxs) {
|
|
15716
|
+
keys.set(i, sortKeyFor(ctx, spec.orderBy, i, evalAt));
|
|
15717
|
+
}
|
|
15718
|
+
rowIdxs = [...rowIdxs].sort((a, b) => compareKeys(ctx, keys.get(a), keys.get(b)));
|
|
15719
|
+
}
|
|
15720
|
+
const n = rowIdxs.length;
|
|
15721
|
+
const peerGroup = new Array(n);
|
|
15722
|
+
let group = 0;
|
|
15723
|
+
for (let pos = 0; pos < n; pos++) {
|
|
15724
|
+
if (pos > 0) {
|
|
15725
|
+
const same = spec.orderBy.length === 0 || compareKeys(ctx, keys.get(rowIdxs[pos - 1]), keys.get(rowIdxs[pos])) === 0;
|
|
15726
|
+
if (!same) group++;
|
|
15727
|
+
}
|
|
15728
|
+
peerGroup[pos] = group;
|
|
15154
15729
|
}
|
|
15155
15730
|
const groupCount = n === 0 ? 0 : peerGroup[n - 1] + 1;
|
|
15156
15731
|
const name = call.name[call.name.length - 1];
|
|
@@ -15162,6 +15737,10 @@ function computePartition(ctx, call, spec, rowIdxs, evalAt, results) {
|
|
|
15162
15737
|
throw pgError("undefined_function", `function ${name} does not exist as a window function`, "42883");
|
|
15163
15738
|
}
|
|
15164
15739
|
const frame = spec.frame ?? defaultFrame(spec);
|
|
15740
|
+
if (!call.filter && (name === "sum" || name === "count") && isRunningRowsFrame(frame, spec)) {
|
|
15741
|
+
computeRunningAgg(ctx, call, name, rowIdxs, evalAt, results);
|
|
15742
|
+
return;
|
|
15743
|
+
}
|
|
15165
15744
|
for (let pos = 0; pos < n; pos++) {
|
|
15166
15745
|
const [lo, hi] = frameBounds(ctx, frame, pos, rowIdxs, peerGroup, keys, spec, evalAt);
|
|
15167
15746
|
const included = [];
|
|
@@ -15212,6 +15791,28 @@ function defaultFrame(spec) {
|
|
|
15212
15791
|
exclusion: null
|
|
15213
15792
|
};
|
|
15214
15793
|
}
|
|
15794
|
+
function isRunningRowsFrame(frame, spec) {
|
|
15795
|
+
if (frame.exclusion) return false;
|
|
15796
|
+
if (frame.mode !== "rows") return false;
|
|
15797
|
+
if (frame.start.kind !== "unbounded_preceding") return false;
|
|
15798
|
+
const end = frame.end ?? (spec.orderBy.length > 0 ? { kind: "current_row" } : { kind: "unbounded_following" });
|
|
15799
|
+
return end.kind === "current_row";
|
|
15800
|
+
}
|
|
15801
|
+
function computeRunningAgg(ctx, call, name, rowIdxs, evalAt, results) {
|
|
15802
|
+
const argTypes = call.args.map((_, ai) => {
|
|
15803
|
+
for (const rowIdx of rowIdxs) {
|
|
15804
|
+
const v = evalAt(rowIdx, call.args[ai]);
|
|
15805
|
+
if (v.t !== UNKNOWN) return v.t;
|
|
15806
|
+
}
|
|
15807
|
+
return UNKNOWN;
|
|
15808
|
+
});
|
|
15809
|
+
const acc = createAggregate(ctx, name, argTypes);
|
|
15810
|
+
for (const rowIdx of rowIdxs) {
|
|
15811
|
+
const argRows = call.star ? [] : call.args.map((a) => evalAt(rowIdx, a));
|
|
15812
|
+
acc.step(argRows);
|
|
15813
|
+
results[rowIdx] = acc.result();
|
|
15814
|
+
}
|
|
15815
|
+
}
|
|
15215
15816
|
function excluded(frame, pos, current, peerGroup) {
|
|
15216
15817
|
switch (frame.exclusion) {
|
|
15217
15818
|
case "current_row":
|
|
@@ -15527,7 +16128,7 @@ function makeEvalScope(env, scope, extras) {
|
|
|
15527
16128
|
},
|
|
15528
16129
|
callUserFunction(name, args, node) {
|
|
15529
16130
|
void node;
|
|
15530
|
-
const fn =
|
|
16131
|
+
const fn = resolveUserFunctionForArgs(env, name, args);
|
|
15531
16132
|
if (!fn) return void 0;
|
|
15532
16133
|
return callSqlFunctionScalar(env, fn, args);
|
|
15533
16134
|
},
|
|
@@ -15543,11 +16144,19 @@ function evalPredicate(env, scope, e, extras) {
|
|
|
15543
16144
|
const b = castTo(env.ctx, v, "bool", {});
|
|
15544
16145
|
return b.v === true;
|
|
15545
16146
|
}
|
|
15546
|
-
function
|
|
16147
|
+
function resolveUserFunctionForArgs(env, name, args) {
|
|
15547
16148
|
const candidates = env.ctx.state.findFunctions(name);
|
|
15548
16149
|
for (const fn of candidates) {
|
|
15549
16150
|
const required = fn.argDefaults.filter((d) => d === null).length;
|
|
15550
|
-
if (
|
|
16151
|
+
if (args.length < required || args.length > fn.argTypes.length) continue;
|
|
16152
|
+
let ok = true;
|
|
16153
|
+
for (let i = 0; i < args.length; i++) {
|
|
16154
|
+
if (!canImplicitCast(args[i].t, fn.argTypes[i])) {
|
|
16155
|
+
ok = false;
|
|
16156
|
+
break;
|
|
16157
|
+
}
|
|
16158
|
+
}
|
|
16159
|
+
if (ok) return fn;
|
|
15551
16160
|
}
|
|
15552
16161
|
return null;
|
|
15553
16162
|
}
|
|
@@ -15585,6 +16194,19 @@ function runFunctionBody(env, fn, args) {
|
|
|
15585
16194
|
}
|
|
15586
16195
|
function callSqlFunctionScalar(env, fn, args) {
|
|
15587
16196
|
const retT = fn.returns ?? "text";
|
|
16197
|
+
if (fn.language === "plpgsql") {
|
|
16198
|
+
return callPlpgsqlScalar(env, fn, args);
|
|
16199
|
+
}
|
|
16200
|
+
if (fn.language === "js") {
|
|
16201
|
+
if (!fn.jsImpl) {
|
|
16202
|
+
throw pgError("undefined_function", `JavaScript function ${fn.name} is not registered`, "42883");
|
|
16203
|
+
}
|
|
16204
|
+
if (fn.strict && args.some((a) => a.v === null)) return tv(retT, null);
|
|
16205
|
+
const jsArgs = args.map((a) => datumToJs(a.t, a.v, env.ctx));
|
|
16206
|
+
const out = fn.jsImpl(...jsArgs);
|
|
16207
|
+
if (out === null || out === void 0) return tv(retT, null);
|
|
16208
|
+
return castTo(env.ctx, bindValueToTyped(out, 0), retT, {});
|
|
16209
|
+
}
|
|
15588
16210
|
const last = runFunctionBody(env, fn, args);
|
|
15589
16211
|
if (last === null) return tv(retT, null);
|
|
15590
16212
|
if (fn.returnsSet || fn.returnsTable) {
|
|
@@ -15596,6 +16218,9 @@ function callSqlFunctionScalar(env, fn, args) {
|
|
|
15596
16218
|
return castTo(env.ctx, tv(rawT, raw), retT, {});
|
|
15597
16219
|
}
|
|
15598
16220
|
function callSqlFunctionSet(env, fn, args) {
|
|
16221
|
+
if (fn.language === "plpgsql") {
|
|
16222
|
+
return callPlpgsqlSet(env, fn, args);
|
|
16223
|
+
}
|
|
15599
16224
|
const last = runFunctionBody(env, fn, args);
|
|
15600
16225
|
if (last === null) return { columns: [], rows: [] };
|
|
15601
16226
|
if (fn.returnsTable) {
|
|
@@ -15756,6 +16381,7 @@ function materializeItem(env, item, scope) {
|
|
|
15756
16381
|
const state = env.ctx.state;
|
|
15757
16382
|
const table = state.findTable(item.name);
|
|
15758
16383
|
if (table) {
|
|
16384
|
+
table.materializeSlab();
|
|
15759
16385
|
const rel2 = {
|
|
15760
16386
|
columns: table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
|
|
15761
16387
|
rows: table.rows
|
|
@@ -15820,6 +16446,14 @@ function srfCallRelation(env, call, scope, alias) {
|
|
|
15820
16446
|
const bare = call.name.length === 1 ? call.name[0] : call.name.length === 2 && call.name[0] === "pg_catalog" ? call.name[1] : call.name[call.name.length - 1];
|
|
15821
16447
|
const evalScope = makeEvalScope(env, scope);
|
|
15822
16448
|
const args = call.args.map((a) => evalExpr(env.ctx, evalScope, a));
|
|
16449
|
+
const userFn = resolveUserFunctionForArgs(env, call.name, args);
|
|
16450
|
+
if (userFn) {
|
|
16451
|
+
if (userFn.returnsSet || userFn.returnsTable) {
|
|
16452
|
+
return callSqlFunctionSet(env, userFn, args);
|
|
16453
|
+
}
|
|
16454
|
+
const v2 = callSqlFunctionScalar(env, userFn, args);
|
|
16455
|
+
return { columns: [{ name: userFn.name, type: v2.t === UNKNOWN ? "text" : v2.t, table: null }], rows: [[v2.v]] };
|
|
16456
|
+
}
|
|
15823
16457
|
const srf = getSrfFunctions().get(bare);
|
|
15824
16458
|
if (srf) {
|
|
15825
16459
|
const res = srf(env.ctx, args, alias ?? bare);
|
|
@@ -15828,14 +16462,6 @@ function srfCallRelation(env, call, scope, alias) {
|
|
|
15828
16462
|
rows: res.rows
|
|
15829
16463
|
};
|
|
15830
16464
|
}
|
|
15831
|
-
const userFn = resolveUserFunction(env, call.name, args.length);
|
|
15832
|
-
if (userFn) {
|
|
15833
|
-
if (userFn.returnsSet || userFn.returnsTable) {
|
|
15834
|
-
return callSqlFunctionSet(env, userFn, args);
|
|
15835
|
-
}
|
|
15836
|
-
const v2 = callSqlFunctionScalar(env, userFn, args);
|
|
15837
|
-
return { columns: [{ name: userFn.name, type: v2.t === UNKNOWN ? "text" : v2.t, table: null }], rows: [[v2.v]] };
|
|
15838
|
-
}
|
|
15839
16465
|
const v = evalExpr(env.ctx, evalScope, call);
|
|
15840
16466
|
return { columns: [{ name: bare, type: v.t === UNKNOWN ? "text" : v.t, table: null }], rows: [[v.v]] };
|
|
15841
16467
|
}
|
|
@@ -16006,7 +16632,35 @@ function checkFullJoinCondition(on, left, right) {
|
|
|
16006
16632
|
"0A000"
|
|
16007
16633
|
);
|
|
16008
16634
|
}
|
|
16009
|
-
function
|
|
16635
|
+
function extractOnEquijoinKeys(on, left, right) {
|
|
16636
|
+
const leftIdxs = [];
|
|
16637
|
+
const rightIdxs = [];
|
|
16638
|
+
for (const part of conjunctions(on)) {
|
|
16639
|
+
if (part.type !== "binop" || part.op !== "=") continue;
|
|
16640
|
+
const ls = joinSideOf(part.left, left, right);
|
|
16641
|
+
const rs = joinSideOf(part.right, left, right);
|
|
16642
|
+
let leftExpr;
|
|
16643
|
+
let rightExpr;
|
|
16644
|
+
if (ls === "left" && rs === "right") {
|
|
16645
|
+
leftExpr = part.left;
|
|
16646
|
+
rightExpr = part.right;
|
|
16647
|
+
} else if (ls === "right" && rs === "left") {
|
|
16648
|
+
leftExpr = part.right;
|
|
16649
|
+
rightExpr = part.left;
|
|
16650
|
+
} else {
|
|
16651
|
+
continue;
|
|
16652
|
+
}
|
|
16653
|
+
if (leftExpr.type !== "colref" || rightExpr.type !== "colref") continue;
|
|
16654
|
+
const li = resolveColIdx(left.columns, leftExpr.parts);
|
|
16655
|
+
const ri = resolveColIdx(right.columns, rightExpr.parts);
|
|
16656
|
+
if (li === null || ri === null) continue;
|
|
16657
|
+
leftIdxs.push(li);
|
|
16658
|
+
rightIdxs.push(ri);
|
|
16659
|
+
}
|
|
16660
|
+
if (leftIdxs.length === 0) return null;
|
|
16661
|
+
return { leftIdxs, rightIdxs };
|
|
16662
|
+
}
|
|
16663
|
+
function combineJoin(env, kind, left, right, on, using, usingAlias, rangeVars, equijoinOverride = null) {
|
|
16010
16664
|
const ctx = env.ctx;
|
|
16011
16665
|
if (kind === "full" && on !== null) checkFullJoinCondition(on, left, right);
|
|
16012
16666
|
let columns;
|
|
@@ -16067,19 +16721,46 @@ function combineJoin(env, kind, left, right, on, using, usingAlias, rangeVars) {
|
|
|
16067
16721
|
for (let i = 0; i < right.columns.length; i++) row.push(rrow?.[i] ?? null);
|
|
16068
16722
|
rows.push(row);
|
|
16069
16723
|
};
|
|
16724
|
+
let hashLeftIdxs = [];
|
|
16725
|
+
let hashRightIdxs = [];
|
|
16726
|
+
let hashKeyTypes = [];
|
|
16727
|
+
if (equijoinOverride) {
|
|
16728
|
+
hashLeftIdxs = equijoinOverride.leftIdxs;
|
|
16729
|
+
hashRightIdxs = equijoinOverride.rightIdxs;
|
|
16730
|
+
hashKeyTypes = hashLeftIdxs.map((li, k) => {
|
|
16731
|
+
const lt = left.columns[li].type;
|
|
16732
|
+
const rt = right.columns[hashRightIdxs[k]].type;
|
|
16733
|
+
const t = unifyTypes(lt, rt);
|
|
16734
|
+
if (t === null) {
|
|
16735
|
+
throw pgError("datatype_mismatch", `JOIN types ${lt} and ${rt} cannot be matched`, "42804");
|
|
16736
|
+
}
|
|
16737
|
+
return t;
|
|
16738
|
+
});
|
|
16739
|
+
} else if (using && using.length > 0) {
|
|
16740
|
+
hashLeftIdxs = usingLeftIdx;
|
|
16741
|
+
hashRightIdxs = usingRightIdx;
|
|
16742
|
+
hashKeyTypes = columns.slice(0, mergedCount).map((c) => c.type);
|
|
16743
|
+
} else if (on !== null && kind !== "cross") {
|
|
16744
|
+
const extracted = extractOnEquijoinKeys(on, left, right);
|
|
16745
|
+
if (extracted) {
|
|
16746
|
+
hashLeftIdxs = extracted.leftIdxs;
|
|
16747
|
+
hashRightIdxs = extracted.rightIdxs;
|
|
16748
|
+
hashKeyTypes = hashLeftIdxs.map((li, k) => {
|
|
16749
|
+
const lt = left.columns[li].type;
|
|
16750
|
+
const rt = right.columns[extracted.rightIdxs[k]].type;
|
|
16751
|
+
const t = unifyTypes(lt, rt);
|
|
16752
|
+
if (t === null) {
|
|
16753
|
+
throw pgError("datatype_mismatch", `JOIN types ${lt} and ${rt} cannot be matched`, "42804");
|
|
16754
|
+
}
|
|
16755
|
+
return t;
|
|
16756
|
+
});
|
|
16757
|
+
}
|
|
16758
|
+
}
|
|
16070
16759
|
const matchRows = (lrow, rrow) => {
|
|
16071
16760
|
if (using && using.length > 0) {
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
if (lv === null || rv === null) return false;
|
|
16076
|
-
const t = columns[k].type;
|
|
16077
|
-
const lc = castTo(ctx, tv(left.columns[usingLeftIdx[k]].type, lv), t, {}).v;
|
|
16078
|
-
const rc = castTo(ctx, tv(right.columns[usingRightIdx[k]].type, rv), t, {}).v;
|
|
16079
|
-
if (lc === null || rc === null) return false;
|
|
16080
|
-
if (datumCompare(t, lc, rc, ctx) !== 0) return false;
|
|
16081
|
-
}
|
|
16082
|
-
return true;
|
|
16761
|
+
const leftTypes = hashLeftIdxs.map((i) => left.columns[i].type);
|
|
16762
|
+
const rightTypes = hashRightIdxs.map((i) => right.columns[i].type);
|
|
16763
|
+
return rowsMatchEqKeys(ctx, lrow, rrow, hashLeftIdxs, hashRightIdxs, leftTypes, rightTypes, hashKeyTypes);
|
|
16083
16764
|
}
|
|
16084
16765
|
if (on === null) return true;
|
|
16085
16766
|
const row = [];
|
|
@@ -16089,18 +16770,47 @@ function combineJoin(env, kind, left, right, on, using, usingAlias, rangeVars) {
|
|
|
16089
16770
|
const jscope = new RowScope(columns, row, env.outer, rangeVars);
|
|
16090
16771
|
return evalPredicate(env, jscope, on);
|
|
16091
16772
|
};
|
|
16092
|
-
|
|
16093
|
-
|
|
16773
|
+
const useHashJoin = kind !== "cross" && hashLeftIdxs.length > 0;
|
|
16774
|
+
if (useHashJoin) {
|
|
16775
|
+
const leftTypes = hashLeftIdxs.map((i) => left.columns[i].type);
|
|
16776
|
+
const rightTypes = hashRightIdxs.map((i) => right.columns[i].type);
|
|
16777
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
16094
16778
|
for (let ri = 0; ri < right.rows.length; ri++) {
|
|
16095
|
-
const
|
|
16096
|
-
if (
|
|
16779
|
+
const key = joinKeyFromRow(right.rows[ri], hashRightIdxs, rightTypes, hashKeyTypes, ctx);
|
|
16780
|
+
if (key === null) continue;
|
|
16781
|
+
const bucket = buckets.get(key);
|
|
16782
|
+
if (bucket) bucket.push(ri);
|
|
16783
|
+
else buckets.set(key, [ri]);
|
|
16784
|
+
}
|
|
16785
|
+
for (const lrow of left.rows) {
|
|
16786
|
+
let matched = false;
|
|
16787
|
+
const key = joinKeyFromRow(lrow, hashLeftIdxs, leftTypes, hashKeyTypes, ctx);
|
|
16788
|
+
const candidates = key === null ? [] : buckets.get(key) ?? [];
|
|
16789
|
+
for (const ri of candidates) {
|
|
16790
|
+
const rrow = right.rows[ri];
|
|
16791
|
+
if (!matchRows(lrow, rrow)) continue;
|
|
16097
16792
|
matched = true;
|
|
16098
16793
|
rightMatched[ri] = true;
|
|
16099
16794
|
emit(lrow, rrow);
|
|
16100
16795
|
}
|
|
16796
|
+
if (!matched && (kind === "left" || kind === "full")) {
|
|
16797
|
+
emit(lrow, null);
|
|
16798
|
+
}
|
|
16101
16799
|
}
|
|
16102
|
-
|
|
16103
|
-
|
|
16800
|
+
} else {
|
|
16801
|
+
for (const lrow of left.rows) {
|
|
16802
|
+
let matched = false;
|
|
16803
|
+
for (let ri = 0; ri < right.rows.length; ri++) {
|
|
16804
|
+
const rrow = right.rows[ri];
|
|
16805
|
+
if (matchRows(lrow, rrow)) {
|
|
16806
|
+
matched = true;
|
|
16807
|
+
rightMatched[ri] = true;
|
|
16808
|
+
emit(lrow, rrow);
|
|
16809
|
+
}
|
|
16810
|
+
}
|
|
16811
|
+
if (!matched && (kind === "left" || kind === "full")) {
|
|
16812
|
+
emit(lrow, null);
|
|
16813
|
+
}
|
|
16104
16814
|
}
|
|
16105
16815
|
}
|
|
16106
16816
|
if (kind === "right" || kind === "full") {
|
|
@@ -16110,7 +16820,7 @@ function combineJoin(env, kind, left, right, on, using, usingAlias, rangeVars) {
|
|
|
16110
16820
|
}
|
|
16111
16821
|
return { columns, rows };
|
|
16112
16822
|
}
|
|
16113
|
-
function buildFrom(env, items) {
|
|
16823
|
+
function buildFrom(env, items, where = null) {
|
|
16114
16824
|
if (items.length === 0) {
|
|
16115
16825
|
return { rel: { columns: [], rows: [[]] }, rangeVars: /* @__PURE__ */ new Set() };
|
|
16116
16826
|
}
|
|
@@ -16152,14 +16862,23 @@ function buildFrom(env, items) {
|
|
|
16152
16862
|
acc = { rel: { columns: cols, rows }, rangeVars: vars };
|
|
16153
16863
|
} else {
|
|
16154
16864
|
const src = materializeItem(env, item, env.outer);
|
|
16155
|
-
const
|
|
16156
|
-
const
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16865
|
+
const rangeVars = /* @__PURE__ */ new Set([...accVars, ...src.rangeVars]);
|
|
16866
|
+
const eq = where ? extractOnEquijoinKeys(where, accRel, src.rel) : null;
|
|
16867
|
+
if (eq) {
|
|
16868
|
+
acc = {
|
|
16869
|
+
rel: combineJoin(env, "inner", accRel, src.rel, null, null, null, rangeVars, eq),
|
|
16870
|
+
rangeVars
|
|
16871
|
+
};
|
|
16872
|
+
} else {
|
|
16873
|
+
const cols = [...accRel.columns, ...src.rel.columns];
|
|
16874
|
+
const rows = [];
|
|
16875
|
+
for (const lrow of accRel.rows) {
|
|
16876
|
+
for (const rrow of src.rel.rows) {
|
|
16877
|
+
rows.push([...lrow, ...rrow]);
|
|
16878
|
+
}
|
|
16160
16879
|
}
|
|
16880
|
+
acc = { rel: { columns: cols, rows }, rangeVars };
|
|
16161
16881
|
}
|
|
16162
|
-
acc = { rel: { columns: cols, rows }, rangeVars: /* @__PURE__ */ new Set([...accVars, ...src.rangeVars]) };
|
|
16163
16882
|
}
|
|
16164
16883
|
}
|
|
16165
16884
|
return acc;
|
|
@@ -16699,14 +17418,24 @@ function expandTargets(core, columns) {
|
|
|
16699
17418
|
function executeCore(env0, core, orderBy) {
|
|
16700
17419
|
const env = env0;
|
|
16701
17420
|
const ctx = env.ctx;
|
|
16702
|
-
const source = buildFrom(env, core.from);
|
|
17421
|
+
const source = buildFrom(env, core.from, core.where);
|
|
16703
17422
|
const srcCols = source.rel.columns;
|
|
16704
17423
|
const rangeVars = source.rangeVars;
|
|
16705
17424
|
const rowScope = (row) => new RowScope(srcCols, row, env.outer, rangeVars);
|
|
16706
17425
|
let srcRows = source.rel.rows;
|
|
16707
17426
|
if (core.where) {
|
|
16708
|
-
|
|
16709
|
-
|
|
17427
|
+
if (core.from.length === 1) {
|
|
17428
|
+
const indexed = tryIndexedFromItem(env, core.from[0], core.where);
|
|
17429
|
+
if (indexed !== null) {
|
|
17430
|
+
srcRows = indexed;
|
|
17431
|
+
} else {
|
|
17432
|
+
const where = core.where;
|
|
17433
|
+
srcRows = srcRows.filter((row) => evalPredicate(env, rowScope(row), where));
|
|
17434
|
+
}
|
|
17435
|
+
} else {
|
|
17436
|
+
const where = core.where;
|
|
17437
|
+
srcRows = srcRows.filter((row) => evalPredicate(env, rowScope(row), where));
|
|
17438
|
+
}
|
|
16710
17439
|
}
|
|
16711
17440
|
const collector = { aggs: [], windows: [], groupings: [] };
|
|
16712
17441
|
for (const t of core.targets) collectCalls(t.expr.type === "star" ? null : t.expr, collector);
|
|
@@ -16975,24 +17704,34 @@ function executeCore(env0, core, orderBy) {
|
|
|
16975
17704
|
}
|
|
16976
17705
|
}
|
|
16977
17706
|
if (ctxs.length === 0) {
|
|
17707
|
+
const probeScope = new RowScope(
|
|
17708
|
+
srcCols,
|
|
17709
|
+
srcCols.map(() => null),
|
|
17710
|
+
env.outer,
|
|
17711
|
+
rangeVars
|
|
17712
|
+
);
|
|
17713
|
+
const probeExtras = {
|
|
17714
|
+
aggMap: /* @__PURE__ */ new Map(),
|
|
17715
|
+
windowAt: () => tv(UNKNOWN, null)
|
|
17716
|
+
};
|
|
17717
|
+
const probeType = (expr) => {
|
|
17718
|
+
if (expr.type === "cast") {
|
|
17719
|
+
return resolveTypeName(ctx.state, expr.target).column.id;
|
|
17720
|
+
}
|
|
17721
|
+
if (expr.type === "func") {
|
|
17722
|
+
const name = expr.name[expr.name.length - 1];
|
|
17723
|
+
if (isAggregateName(name)) {
|
|
17724
|
+
const argTypes = expr.args.map((a) => probeType(a));
|
|
17725
|
+
return createAggregate(ctx, name, argTypes).result().t;
|
|
17726
|
+
}
|
|
17727
|
+
}
|
|
17728
|
+
return evalScalar(env, probeScope, expr, probeExtras).t;
|
|
17729
|
+
};
|
|
16978
17730
|
for (let k = 0; k < projItems.length; k++) {
|
|
16979
17731
|
const p = projItems[k];
|
|
16980
17732
|
if (outColumns[k].type !== UNKNOWN || p.kind === "col") continue;
|
|
16981
|
-
|
|
16982
|
-
|
|
16983
|
-
srcCols,
|
|
16984
|
-
srcCols.map(() => null),
|
|
16985
|
-
env.outer,
|
|
16986
|
-
rangeVars
|
|
16987
|
-
);
|
|
16988
|
-
const v = evalScalar(env, probeScope, p.expr, {
|
|
16989
|
-
aggMap: /* @__PURE__ */ new Map(),
|
|
16990
|
-
windowAt: () => tv(UNKNOWN, null)
|
|
16991
|
-
});
|
|
16992
|
-
outColumns[k].type = v.t;
|
|
16993
|
-
} catch {
|
|
16994
|
-
outColumns[k].type = "text";
|
|
16995
|
-
}
|
|
17733
|
+
const t = probeType(p.expr);
|
|
17734
|
+
outColumns[k].type = t === UNKNOWN ? "text" : t;
|
|
16996
17735
|
}
|
|
16997
17736
|
}
|
|
16998
17737
|
for (const c of outColumns) {
|
|
@@ -17062,29 +17801,7 @@ function executeSelectStmt(env0, stmt) {
|
|
|
17062
17801
|
return applyLimitOffset(env, rel2, stmt.limit, stmt.offset);
|
|
17063
17802
|
}
|
|
17064
17803
|
|
|
17065
|
-
// src/executor/
|
|
17066
|
-
function matches(t, event) {
|
|
17067
|
-
return t.events.some((e) => e.event === event);
|
|
17068
|
-
}
|
|
17069
|
-
var executor = null;
|
|
17070
|
-
function setTriggerExecutor(e) {
|
|
17071
|
-
executor = e;
|
|
17072
|
-
}
|
|
17073
|
-
function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
|
|
17074
|
-
let row = newRow ?? oldRow;
|
|
17075
|
-
for (const t of table.triggers) {
|
|
17076
|
-
if (t.timing !== timing || !matches(t, event) || !t.forEachRow) continue;
|
|
17077
|
-
if (!executor) throw unsupported(`trigger "${t.name}" execution`);
|
|
17078
|
-
const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
|
|
17079
|
-
if (timing === "before") {
|
|
17080
|
-
if (result === null) return { row: null };
|
|
17081
|
-
row = result;
|
|
17082
|
-
}
|
|
17083
|
-
}
|
|
17084
|
-
return { row };
|
|
17085
|
-
}
|
|
17086
|
-
|
|
17087
|
-
// src/executor/triggers-exec.ts
|
|
17804
|
+
// src/executor/plpgsql.ts
|
|
17088
17805
|
var PlParser = class {
|
|
17089
17806
|
constructor(src) {
|
|
17090
17807
|
this.src = src;
|
|
@@ -17101,67 +17818,149 @@ var PlParser = class {
|
|
|
17101
17818
|
if (t.type !== "eof") this.pos++;
|
|
17102
17819
|
return t;
|
|
17103
17820
|
}
|
|
17104
|
-
atKw(kw) {
|
|
17105
|
-
const t = this.peek();
|
|
17821
|
+
atKw(kw, offset = 0) {
|
|
17822
|
+
const t = this.peek(offset);
|
|
17106
17823
|
return t.type === "ident" && t.value === kw;
|
|
17107
17824
|
}
|
|
17108
17825
|
expectKw(kw) {
|
|
17109
|
-
if (!this.atKw(kw)) throw unsupported(`
|
|
17826
|
+
if (!this.atKw(kw)) throw unsupported(`plpgsql: expected "${kw.toUpperCase()}" near "${this.peek().value}"`);
|
|
17110
17827
|
this.pos++;
|
|
17111
17828
|
}
|
|
17112
17829
|
expectSemi() {
|
|
17113
17830
|
const t = this.next();
|
|
17114
|
-
if (t.type !== "punct" || t.value !== ";") throw unsupported(`
|
|
17831
|
+
if (t.type !== "punct" || t.value !== ";") throw unsupported(`plpgsql: expected ";" near "${t.value}"`);
|
|
17832
|
+
}
|
|
17833
|
+
parseProgram() {
|
|
17834
|
+
const decls = [];
|
|
17835
|
+
if (this.atKw("declare")) {
|
|
17836
|
+
this.pos++;
|
|
17837
|
+
while (!this.atKw("begin") && this.peek().type !== "eof") {
|
|
17838
|
+
decls.push(this.parseDecl());
|
|
17839
|
+
}
|
|
17840
|
+
}
|
|
17841
|
+
const block = this.parseBlock();
|
|
17842
|
+
if (this.peek().type !== "eof") throw unsupported(`plpgsql: trailing content near "${this.peek().value}"`);
|
|
17843
|
+
return { decls, body: [block] };
|
|
17115
17844
|
}
|
|
17116
|
-
|
|
17845
|
+
/** Trigger bodies are a BEGIN block (no DECLARE). */
|
|
17846
|
+
parseTriggerBody() {
|
|
17117
17847
|
if (this.atKw("declare")) throw unsupported("trigger body: DECLARE section");
|
|
17848
|
+
const block = this.parseBlock();
|
|
17849
|
+
if (this.peek().type !== "eof") throw unsupported(`trigger body: trailing content near "${this.peek().value}"`);
|
|
17850
|
+
return block.kind === "block" && block.handler === null ? block.body : [block];
|
|
17851
|
+
}
|
|
17852
|
+
parseDecl() {
|
|
17853
|
+
const nameTok = this.next();
|
|
17854
|
+
if (nameTok.type !== "ident" && nameTok.type !== "quoted_ident") {
|
|
17855
|
+
throw unsupported(`plpgsql DECLARE near "${nameTok.value}"`);
|
|
17856
|
+
}
|
|
17857
|
+
const name = nameTok.value;
|
|
17858
|
+
const typeStart = this.peek().pos;
|
|
17859
|
+
while (!(this.peek().type === "punct" && this.peek().value === ";") && !(this.peek().type === "op" && (this.peek().value === ":=" || this.peek().value === "=")) && this.peek().type !== "eof") {
|
|
17860
|
+
this.pos++;
|
|
17861
|
+
}
|
|
17862
|
+
const typeText = this.src.slice(typeStart, this.peek().pos);
|
|
17863
|
+
const typeName = parseTypeNameText(typeText);
|
|
17864
|
+
let init = null;
|
|
17865
|
+
if (this.peek().type === "op" && (this.peek().value === ":=" || this.peek().value === "=")) {
|
|
17866
|
+
this.pos++;
|
|
17867
|
+
init = this.parseExprUntilSemi();
|
|
17868
|
+
} else {
|
|
17869
|
+
this.expectSemi();
|
|
17870
|
+
}
|
|
17871
|
+
return { name, typeName, init };
|
|
17872
|
+
}
|
|
17873
|
+
parseBlock() {
|
|
17118
17874
|
this.expectKw("begin");
|
|
17119
|
-
const
|
|
17875
|
+
const body = this.parseStmts(/* @__PURE__ */ new Set(["end", "exception"]));
|
|
17876
|
+
let handler = null;
|
|
17877
|
+
if (this.atKw("exception")) {
|
|
17878
|
+
this.pos++;
|
|
17879
|
+
this.expectKw("when");
|
|
17880
|
+
if (!this.atKw("others")) throw unsupported(`plpgsql EXCEPTION WHEN ${this.peek().value}`);
|
|
17881
|
+
this.pos++;
|
|
17882
|
+
this.expectKw("then");
|
|
17883
|
+
handler = this.parseStmts(/* @__PURE__ */ new Set(["end", "when"]));
|
|
17884
|
+
while (this.atKw("when")) {
|
|
17885
|
+
throw unsupported("plpgsql EXCEPTION WHEN (only OTHERS is implemented)");
|
|
17886
|
+
}
|
|
17887
|
+
}
|
|
17120
17888
|
this.expectKw("end");
|
|
17121
17889
|
if (this.peek().type === "punct" && this.peek().value === ";") this.pos++;
|
|
17122
|
-
|
|
17123
|
-
return stmts;
|
|
17890
|
+
return { kind: "block", body, handler };
|
|
17124
17891
|
}
|
|
17125
|
-
parseStmts() {
|
|
17892
|
+
parseStmts(stop) {
|
|
17126
17893
|
const out = [];
|
|
17127
|
-
while (
|
|
17894
|
+
while (this.peek().type !== "eof") {
|
|
17895
|
+
if (this.peek().type === "ident" && stop.has(this.peek().value)) break;
|
|
17128
17896
|
out.push(this.parseStmt());
|
|
17129
17897
|
}
|
|
17130
17898
|
return out;
|
|
17131
17899
|
}
|
|
17132
17900
|
parseStmt() {
|
|
17901
|
+
if (this.atKw("begin")) return this.parseBlock();
|
|
17133
17902
|
if (this.atKw("return")) {
|
|
17134
17903
|
this.pos++;
|
|
17135
|
-
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
|
-
} else {
|
|
17140
|
-
throw unsupported(`trigger body: RETURN ${t.value}`);
|
|
17904
|
+
if (this.atKw("next")) {
|
|
17905
|
+
this.pos++;
|
|
17906
|
+
this.expectSemi();
|
|
17907
|
+
return { kind: "return_next" };
|
|
17141
17908
|
}
|
|
17142
|
-
this.
|
|
17143
|
-
|
|
17909
|
+
if (this.peek().type === "punct" && this.peek().value === ";") {
|
|
17910
|
+
this.pos++;
|
|
17911
|
+
return { kind: "return_empty" };
|
|
17912
|
+
}
|
|
17913
|
+
if (this.peek().type === "ident" && (this.peek().value === "new" || this.peek().value === "old") && this.peek(1).type === "punct" && this.peek(1).value === ";") {
|
|
17914
|
+
const what = this.peek().value === "new" ? "return_new" : "return_old";
|
|
17915
|
+
this.pos += 2;
|
|
17916
|
+
return { kind: what };
|
|
17917
|
+
}
|
|
17918
|
+
return { kind: "return_expr", expr: this.parseExprUntilSemi() };
|
|
17144
17919
|
}
|
|
17145
17920
|
if (this.atKw("if")) {
|
|
17146
17921
|
this.pos++;
|
|
17147
17922
|
const branches = [];
|
|
17148
17923
|
let elseBody = [];
|
|
17149
17924
|
const cond = this.parseExprUntilKw("then");
|
|
17150
|
-
branches.push({ cond, body: this.parseStmts() });
|
|
17925
|
+
branches.push({ cond, body: this.parseStmts(/* @__PURE__ */ new Set(["end", "elsif", "else"])) });
|
|
17151
17926
|
while (this.atKw("elsif")) {
|
|
17152
17927
|
this.pos++;
|
|
17153
17928
|
const c = this.parseExprUntilKw("then");
|
|
17154
|
-
branches.push({ cond: c, body: this.parseStmts() });
|
|
17929
|
+
branches.push({ cond: c, body: this.parseStmts(/* @__PURE__ */ new Set(["end", "elsif", "else"])) });
|
|
17155
17930
|
}
|
|
17156
17931
|
if (this.atKw("else")) {
|
|
17157
17932
|
this.pos++;
|
|
17158
|
-
elseBody = this.parseStmts();
|
|
17933
|
+
elseBody = this.parseStmts(/* @__PURE__ */ new Set(["end"]));
|
|
17159
17934
|
}
|
|
17160
17935
|
this.expectKw("end");
|
|
17161
17936
|
this.expectKw("if");
|
|
17162
17937
|
this.expectSemi();
|
|
17163
17938
|
return { kind: "if", branches, elseBody };
|
|
17164
17939
|
}
|
|
17940
|
+
if (this.atKw("case")) {
|
|
17941
|
+
this.pos++;
|
|
17942
|
+
let expr = null;
|
|
17943
|
+
const branches = [];
|
|
17944
|
+
let elseBody = [];
|
|
17945
|
+
if (!this.atKw("when")) {
|
|
17946
|
+
expr = this.parseExprUntilKw("when");
|
|
17947
|
+
const cond = this.parseExprUntilKw("then");
|
|
17948
|
+
branches.push({ cond, body: this.parseStmts(/* @__PURE__ */ new Set(["when", "else", "end"])) });
|
|
17949
|
+
}
|
|
17950
|
+
while (this.atKw("when")) {
|
|
17951
|
+
this.pos++;
|
|
17952
|
+
const cond = this.parseExprUntilKw("then");
|
|
17953
|
+
branches.push({ cond, body: this.parseStmts(/* @__PURE__ */ new Set(["when", "else", "end"])) });
|
|
17954
|
+
}
|
|
17955
|
+
if (this.atKw("else")) {
|
|
17956
|
+
this.pos++;
|
|
17957
|
+
elseBody = this.parseStmts(/* @__PURE__ */ new Set(["end"]));
|
|
17958
|
+
}
|
|
17959
|
+
this.expectKw("end");
|
|
17960
|
+
this.expectKw("case");
|
|
17961
|
+
this.expectSemi();
|
|
17962
|
+
return { kind: "case", expr, branches, elseBody };
|
|
17963
|
+
}
|
|
17165
17964
|
if (this.atKw("null")) {
|
|
17166
17965
|
this.pos++;
|
|
17167
17966
|
this.expectSemi();
|
|
@@ -17178,12 +17977,37 @@ var PlParser = class {
|
|
|
17178
17977
|
this.expectSemi();
|
|
17179
17978
|
return { kind: "raise", message: message || "raised exception" };
|
|
17180
17979
|
}
|
|
17980
|
+
if (this.atKw("perform")) {
|
|
17981
|
+
this.pos++;
|
|
17982
|
+
return { kind: "perform", expr: this.parseExprUntilSemi() };
|
|
17983
|
+
}
|
|
17984
|
+
if (this.atKw("for")) {
|
|
17985
|
+
this.pos++;
|
|
17986
|
+
const targets = [];
|
|
17987
|
+
for (; ; ) {
|
|
17988
|
+
const t = this.next();
|
|
17989
|
+
if (t.type !== "ident" && t.type !== "quoted_ident") throw unsupported(`plpgsql FOR target near "${t.value}"`);
|
|
17990
|
+
targets.push(t.value);
|
|
17991
|
+
if (this.peek().type === "punct" && this.peek().value === ",") {
|
|
17992
|
+
this.pos++;
|
|
17993
|
+
continue;
|
|
17994
|
+
}
|
|
17995
|
+
break;
|
|
17996
|
+
}
|
|
17997
|
+
this.expectKw("in");
|
|
17998
|
+
const query = this.parseQueryUntilKw("loop");
|
|
17999
|
+
const body = this.parseStmts(/* @__PURE__ */ new Set(["end"]));
|
|
18000
|
+
this.expectKw("end");
|
|
18001
|
+
this.expectKw("loop");
|
|
18002
|
+
this.expectSemi();
|
|
18003
|
+
return { kind: "for", targets, query, body };
|
|
18004
|
+
}
|
|
17181
18005
|
const target = [];
|
|
17182
18006
|
const first = this.next();
|
|
17183
18007
|
if (first.type !== "ident" && first.type !== "quoted_ident") {
|
|
17184
|
-
throw unsupported(`
|
|
18008
|
+
throw unsupported(`plpgsql statement near "${first.value}"`);
|
|
17185
18009
|
}
|
|
17186
|
-
target.push(first.
|
|
18010
|
+
target.push(first.value);
|
|
17187
18011
|
while (this.peek().type === "punct" && this.peek().value === ".") {
|
|
17188
18012
|
this.pos++;
|
|
17189
18013
|
const f = this.next();
|
|
@@ -17191,18 +18015,16 @@ var PlParser = class {
|
|
|
17191
18015
|
}
|
|
17192
18016
|
const opTok = this.next();
|
|
17193
18017
|
if (!(opTok.type === "op" && (opTok.value === ":=" || opTok.value === "="))) {
|
|
17194
|
-
throw unsupported(`
|
|
18018
|
+
throw unsupported(`plpgsql: expected assignment near "${opTok.value}"`);
|
|
17195
18019
|
}
|
|
17196
|
-
|
|
17197
|
-
return { kind: "assign", target, expr };
|
|
18020
|
+
return { kind: "assign", target, expr: this.parseExprUntilSemi() };
|
|
17198
18021
|
}
|
|
17199
|
-
/** Slice raw source from the current token to the terminator and parse as a SQL expression. */
|
|
17200
18022
|
parseExprUntilSemi() {
|
|
17201
18023
|
const start = this.peek().pos;
|
|
17202
18024
|
let depth = 0;
|
|
17203
18025
|
for (; ; ) {
|
|
17204
18026
|
const t = this.peek();
|
|
17205
|
-
if (t.type === "eof") throw unsupported("
|
|
18027
|
+
if (t.type === "eof") throw unsupported("plpgsql: unterminated statement");
|
|
17206
18028
|
if (t.type === "punct" && t.value === "(") depth++;
|
|
17207
18029
|
if (t.type === "punct" && t.value === ")") depth--;
|
|
17208
18030
|
if (t.type === "punct" && t.value === ";" && depth === 0) {
|
|
@@ -17216,12 +18038,15 @@ var PlParser = class {
|
|
|
17216
18038
|
parseExprUntilKw(kw) {
|
|
17217
18039
|
const start = this.peek().pos;
|
|
17218
18040
|
let depth = 0;
|
|
18041
|
+
let caseDepth = 0;
|
|
17219
18042
|
for (; ; ) {
|
|
17220
18043
|
const t = this.peek();
|
|
17221
|
-
if (t.type === "eof") throw unsupported(`
|
|
18044
|
+
if (t.type === "eof") throw unsupported(`plpgsql: expected "${kw.toUpperCase()}"`);
|
|
17222
18045
|
if (t.type === "punct" && t.value === "(") depth++;
|
|
17223
18046
|
if (t.type === "punct" && t.value === ")") depth--;
|
|
17224
|
-
if (t.type === "ident" && t.value ===
|
|
18047
|
+
if (t.type === "ident" && t.value === "case" && depth === 0) caseDepth++;
|
|
18048
|
+
if (t.type === "ident" && t.value === "end" && depth === 0 && caseDepth > 0) caseDepth--;
|
|
18049
|
+
if (t.type === "ident" && t.value === kw && depth === 0 && caseDepth === 0) {
|
|
17225
18050
|
const text = this.src.slice(start, t.pos);
|
|
17226
18051
|
this.pos++;
|
|
17227
18052
|
return parseSqlExpr(text);
|
|
@@ -17229,26 +18054,304 @@ var PlParser = class {
|
|
|
17229
18054
|
this.pos++;
|
|
17230
18055
|
}
|
|
17231
18056
|
}
|
|
18057
|
+
parseQueryUntilKw(kw) {
|
|
18058
|
+
const start = this.peek().pos;
|
|
18059
|
+
let depth = 0;
|
|
18060
|
+
for (; ; ) {
|
|
18061
|
+
const t = this.peek();
|
|
18062
|
+
if (t.type === "eof") throw unsupported(`plpgsql: expected "${kw.toUpperCase()}"`);
|
|
18063
|
+
if (t.type === "punct" && t.value === "(") depth++;
|
|
18064
|
+
if (t.type === "punct" && t.value === ")") depth--;
|
|
18065
|
+
if (t.type === "ident" && t.value === kw && depth === 0) {
|
|
18066
|
+
const text = this.src.slice(start, t.pos).trim();
|
|
18067
|
+
this.pos++;
|
|
18068
|
+
const stmts = parse(text);
|
|
18069
|
+
const q = stmts[0];
|
|
18070
|
+
if (q?.type !== "select") throw unsupported(`plpgsql FOR query: expected SELECT`);
|
|
18071
|
+
return q;
|
|
18072
|
+
}
|
|
18073
|
+
this.pos++;
|
|
18074
|
+
}
|
|
18075
|
+
}
|
|
17232
18076
|
};
|
|
17233
18077
|
function parseSqlExpr(text) {
|
|
17234
18078
|
const stmts = parse(`SELECT ${text}`);
|
|
17235
18079
|
const sel = stmts[0];
|
|
17236
18080
|
const body = sel.body;
|
|
17237
18081
|
const target = body.targets?.[0];
|
|
17238
|
-
if (!target) throw unsupported(`
|
|
18082
|
+
if (!target) throw unsupported(`plpgsql expression "${text}"`);
|
|
17239
18083
|
return target.expr;
|
|
17240
18084
|
}
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
const
|
|
17244
|
-
|
|
17245
|
-
|
|
17246
|
-
if (
|
|
17247
|
-
|
|
17248
|
-
|
|
18085
|
+
function parseTypeNameText(text) {
|
|
18086
|
+
const stmts = parse(`SELECT NULL::${text.trim()}`);
|
|
18087
|
+
const sel = stmts[0];
|
|
18088
|
+
const body = sel.body;
|
|
18089
|
+
const expr = body.targets?.[0]?.expr;
|
|
18090
|
+
if (expr?.type !== "cast") throw unsupported(`plpgsql type "${text}"`);
|
|
18091
|
+
return expr.target;
|
|
18092
|
+
}
|
|
18093
|
+
var programCache = /* @__PURE__ */ new Map();
|
|
18094
|
+
var triggerCache = /* @__PURE__ */ new Map();
|
|
18095
|
+
function compilePlpgsql(raw) {
|
|
18096
|
+
let cached = programCache.get(raw);
|
|
18097
|
+
if (cached === void 0) {
|
|
18098
|
+
cached = new PlParser(raw).parseProgram();
|
|
18099
|
+
programCache.set(raw, cached);
|
|
18100
|
+
}
|
|
18101
|
+
return cached;
|
|
18102
|
+
}
|
|
18103
|
+
function compileTriggerBody(raw) {
|
|
18104
|
+
let cached = triggerCache.get(raw);
|
|
18105
|
+
if (cached === void 0) {
|
|
18106
|
+
cached = new PlParser(raw).parseTriggerBody();
|
|
18107
|
+
triggerCache.set(raw, cached);
|
|
18108
|
+
}
|
|
18109
|
+
return cached;
|
|
18110
|
+
}
|
|
18111
|
+
var PlReturn = class {
|
|
18112
|
+
constructor(mode, value) {
|
|
18113
|
+
this.mode = mode;
|
|
18114
|
+
this.value = value;
|
|
18115
|
+
}
|
|
18116
|
+
mode;
|
|
18117
|
+
value;
|
|
18118
|
+
};
|
|
18119
|
+
var PlVars = class {
|
|
18120
|
+
names = [];
|
|
18121
|
+
types = [];
|
|
18122
|
+
values = [];
|
|
18123
|
+
declare(name, type, value) {
|
|
18124
|
+
const i = this.names.indexOf(name);
|
|
18125
|
+
if (i === -1) {
|
|
18126
|
+
this.names.push(name);
|
|
18127
|
+
this.types.push(type);
|
|
18128
|
+
this.values.push(value);
|
|
18129
|
+
} else {
|
|
18130
|
+
this.types[i] = type;
|
|
18131
|
+
this.values[i] = value;
|
|
18132
|
+
}
|
|
18133
|
+
}
|
|
18134
|
+
set(name, v, env) {
|
|
18135
|
+
const i = this.names.indexOf(name);
|
|
18136
|
+
if (i === -1) throw pgError("undefined_column", `"${name}" is not a known variable`, "42703");
|
|
18137
|
+
const casted = v.v === null ? tv(this.types[i], null) : castTo(env.ctx, v, this.types[i], { assignment: true });
|
|
18138
|
+
this.values[i] = casted.v;
|
|
18139
|
+
}
|
|
18140
|
+
get(name) {
|
|
18141
|
+
const i = this.names.indexOf(name);
|
|
18142
|
+
if (i === -1) return void 0;
|
|
18143
|
+
return tv(this.types[i], this.values[i] ?? null);
|
|
18144
|
+
}
|
|
18145
|
+
scope() {
|
|
18146
|
+
const cols = this.names.map((n, i) => ({ name: n, type: this.types[i], table: null }));
|
|
18147
|
+
return new RowScope(cols, this.values, null);
|
|
18148
|
+
}
|
|
18149
|
+
};
|
|
18150
|
+
function runStmts(env, stmts, vars, emit, tableNames) {
|
|
18151
|
+
for (const stmt of stmts) runStmt(env, stmt, vars, emit, tableNames);
|
|
18152
|
+
}
|
|
18153
|
+
function runStmt(env, stmt, vars, emit, tableNames) {
|
|
18154
|
+
const scope = () => vars.scope();
|
|
18155
|
+
switch (stmt.kind) {
|
|
18156
|
+
case "assign": {
|
|
18157
|
+
if (stmt.target.length !== 1) throw unsupported(`plpgsql assignment to "${stmt.target.join(".")}"`);
|
|
18158
|
+
const v = evalScalar(env, scope(), stmt.expr);
|
|
18159
|
+
vars.set(stmt.target[0], v, env);
|
|
18160
|
+
return;
|
|
18161
|
+
}
|
|
18162
|
+
case "return_new":
|
|
18163
|
+
case "return_old":
|
|
18164
|
+
throw unsupported(`plpgsql RETURN ${stmt.kind === "return_new" ? "NEW" : "OLD"} in a non-trigger function`);
|
|
18165
|
+
case "return_expr":
|
|
18166
|
+
throw new PlReturn("value", evalScalar(env, scope(), stmt.expr));
|
|
18167
|
+
case "return_empty":
|
|
18168
|
+
throw new PlReturn("empty", null);
|
|
18169
|
+
case "return_next": {
|
|
18170
|
+
if (emit === null) throw pgError("syntax", "RETURN NEXT cannot be used in a non-SETOF function", "42601");
|
|
18171
|
+
if (tableNames) emit.push(tableNames.map((n) => vars.get(n)?.v ?? null));
|
|
18172
|
+
else emit.push([vars.values[0] ?? null]);
|
|
18173
|
+
return;
|
|
18174
|
+
}
|
|
18175
|
+
case "if": {
|
|
18176
|
+
for (const b of stmt.branches) {
|
|
18177
|
+
if (evalPredicate(env, scope(), b.cond)) {
|
|
18178
|
+
runStmts(env, b.body, vars, emit, tableNames);
|
|
18179
|
+
return;
|
|
18180
|
+
}
|
|
18181
|
+
}
|
|
18182
|
+
runStmts(env, stmt.elseBody, vars, emit, tableNames);
|
|
18183
|
+
return;
|
|
18184
|
+
}
|
|
18185
|
+
case "case": {
|
|
18186
|
+
if (stmt.expr === null) {
|
|
18187
|
+
for (const b of stmt.branches) {
|
|
18188
|
+
if (evalPredicate(env, scope(), b.cond)) {
|
|
18189
|
+
runStmts(env, b.body, vars, emit, tableNames);
|
|
18190
|
+
return;
|
|
18191
|
+
}
|
|
18192
|
+
}
|
|
18193
|
+
runStmts(env, stmt.elseBody, vars, emit, tableNames);
|
|
18194
|
+
return;
|
|
18195
|
+
}
|
|
18196
|
+
const head = evalScalar(env, scope(), stmt.expr);
|
|
18197
|
+
for (const b of stmt.branches) {
|
|
18198
|
+
const when = evalScalar(env, scope(), b.cond);
|
|
18199
|
+
if (head.v !== null && when.v !== null) {
|
|
18200
|
+
const rhs = when.t === head.t ? when : castTo(env.ctx, when, head.t, {});
|
|
18201
|
+
if (datumEquals(head.t, head.v, rhs.v)) {
|
|
18202
|
+
runStmts(env, b.body, vars, emit, tableNames);
|
|
18203
|
+
return;
|
|
18204
|
+
}
|
|
18205
|
+
}
|
|
18206
|
+
}
|
|
18207
|
+
runStmts(env, stmt.elseBody, vars, emit, tableNames);
|
|
18208
|
+
return;
|
|
18209
|
+
}
|
|
18210
|
+
case "null":
|
|
18211
|
+
return;
|
|
18212
|
+
case "raise":
|
|
18213
|
+
throw pgError("raise_exception", stmt.message, "P0001");
|
|
18214
|
+
case "perform":
|
|
18215
|
+
evalScalar(env, scope(), stmt.expr);
|
|
18216
|
+
return;
|
|
18217
|
+
case "block": {
|
|
18218
|
+
try {
|
|
18219
|
+
runStmts(env, stmt.body, vars, emit, tableNames);
|
|
18220
|
+
} catch (e) {
|
|
18221
|
+
if (e instanceof PlReturn) throw e;
|
|
18222
|
+
if (stmt.handler === null) throw e;
|
|
18223
|
+
runStmts(env, stmt.handler, vars, emit, tableNames);
|
|
18224
|
+
}
|
|
18225
|
+
return;
|
|
18226
|
+
}
|
|
18227
|
+
case "for": {
|
|
18228
|
+
const forEnv = { ctx: env.ctx, params: env.params, ctes: env.ctes, outer: vars.scope() };
|
|
18229
|
+
const rel2 = executeSelectStmt(forEnv, stmt.query);
|
|
18230
|
+
for (const row of rel2.rows) {
|
|
18231
|
+
for (let i = 0; i < stmt.targets.length; i++) {
|
|
18232
|
+
const colT = rel2.columns[i]?.type ?? "text";
|
|
18233
|
+
vars.set(stmt.targets[i], tv(colT, row[i] ?? null), env);
|
|
18234
|
+
}
|
|
18235
|
+
runStmts(env, stmt.body, vars, emit, tableNames);
|
|
18236
|
+
}
|
|
18237
|
+
return;
|
|
18238
|
+
}
|
|
18239
|
+
}
|
|
18240
|
+
}
|
|
18241
|
+
function bindArgs(env, fn, args) {
|
|
18242
|
+
const bound = [];
|
|
18243
|
+
for (let i = 0; i < fn.argTypes.length; i++) {
|
|
18244
|
+
if (i < args.length) {
|
|
18245
|
+
bound.push(castTo(env.ctx, args[i], fn.argTypes[i], {}));
|
|
18246
|
+
} else {
|
|
18247
|
+
const dflt = fn.argDefaults[i];
|
|
18248
|
+
if (!dflt) throw pgError("undefined_function", `function ${fn.name} argument ${i + 1} missing`, "42883");
|
|
18249
|
+
bound.push(castTo(env.ctx, evalScalar(env, null, dflt), fn.argTypes[i], {}));
|
|
18250
|
+
}
|
|
18251
|
+
}
|
|
18252
|
+
return bound;
|
|
18253
|
+
}
|
|
18254
|
+
function initVars(env, fn, bound, program) {
|
|
18255
|
+
const vars = new PlVars();
|
|
18256
|
+
for (let i = 0; i < fn.argNames.length; i++) {
|
|
18257
|
+
const n = fn.argNames[i];
|
|
18258
|
+
if (n == null) continue;
|
|
18259
|
+
vars.declare(n, fn.argTypes[i], bound[i]?.v ?? null);
|
|
18260
|
+
}
|
|
18261
|
+
if (fn.returnsTable) {
|
|
18262
|
+
for (const c of fn.returnsTable) vars.declare(c.name, c.type, null);
|
|
18263
|
+
}
|
|
18264
|
+
const seed = () => ({ ctx: env.ctx, params: bound, ctes: /* @__PURE__ */ new Map(), outer: vars.scope() });
|
|
18265
|
+
for (const d of program.decls) {
|
|
18266
|
+
const id = resolveTypeName(env.ctx.state, d.typeName).column.id;
|
|
18267
|
+
let val = null;
|
|
18268
|
+
if (d.init) {
|
|
18269
|
+
const v = evalScalar(seed(), vars.scope(), d.init);
|
|
18270
|
+
val = v.v === null ? null : castTo(env.ctx, v, id, { assignment: true }).v;
|
|
18271
|
+
}
|
|
18272
|
+
vars.declare(d.name, id, val);
|
|
18273
|
+
}
|
|
18274
|
+
return vars;
|
|
18275
|
+
}
|
|
18276
|
+
function plpgsqlEnv(env, bound, vars) {
|
|
18277
|
+
return { ctx: env.ctx, params: bound, ctes: /* @__PURE__ */ new Map(), outer: vars.scope() };
|
|
18278
|
+
}
|
|
18279
|
+
function callPlpgsqlScalar(env, fn, args) {
|
|
18280
|
+
const retT = fn.returns ?? "text";
|
|
18281
|
+
if (fn.strict && args.some((a) => a.v === null)) return tv(retT, null);
|
|
18282
|
+
const raw = fn.rawBody;
|
|
18283
|
+
if (raw === null) throw pgError("unsupported", `function ${fn.name} has no executable body`);
|
|
18284
|
+
const program = compilePlpgsql(raw);
|
|
18285
|
+
const bound = bindArgs(env, fn, args);
|
|
18286
|
+
const vars = initVars(env, fn, bound, program);
|
|
18287
|
+
const fnEnv = plpgsqlEnv(env, bound, vars);
|
|
18288
|
+
try {
|
|
18289
|
+
runStmts(fnEnv, program.body, vars, null, null);
|
|
18290
|
+
} catch (e) {
|
|
18291
|
+
if (e instanceof PlReturn) {
|
|
18292
|
+
if (e.mode === "next") {
|
|
18293
|
+
throw pgError("feature_not_supported", `set-returning function ${fn.name} called in scalar context`, "0A000");
|
|
18294
|
+
}
|
|
18295
|
+
if (e.mode === "empty" || e.value === null || e.value.v === null) return tv(retT, null);
|
|
18296
|
+
return castTo(env.ctx, e.value, retT, {});
|
|
18297
|
+
}
|
|
18298
|
+
throw e;
|
|
18299
|
+
}
|
|
18300
|
+
return tv(retT, null);
|
|
18301
|
+
}
|
|
18302
|
+
function callPlpgsqlSet(env, fn, args) {
|
|
18303
|
+
const cols = fn.returnsTable ? fn.returnsTable.map((c) => ({ name: c.name, type: c.type, table: null })) : [{ name: fn.name, type: fn.returns ?? "text", table: null }];
|
|
18304
|
+
if (fn.strict && args.some((a) => a.v === null)) return { columns: cols, rows: [] };
|
|
18305
|
+
const raw = fn.rawBody;
|
|
18306
|
+
if (raw === null) throw pgError("unsupported", `function ${fn.name} has no executable body`);
|
|
18307
|
+
const program = compilePlpgsql(raw);
|
|
18308
|
+
const bound = bindArgs(env, fn, args);
|
|
18309
|
+
const vars = initVars(env, fn, bound, program);
|
|
18310
|
+
const fnEnv = plpgsqlEnv(env, bound, vars);
|
|
18311
|
+
const emit = [];
|
|
18312
|
+
const tableNames = fn.returnsTable?.map((c) => c.name) ?? null;
|
|
18313
|
+
try {
|
|
18314
|
+
runStmts(fnEnv, program.body, vars, emit, tableNames);
|
|
18315
|
+
} catch (e) {
|
|
18316
|
+
if (!(e instanceof PlReturn)) throw e;
|
|
18317
|
+
}
|
|
18318
|
+
if (fn.returnsTable) {
|
|
18319
|
+
const rows = emit.map(
|
|
18320
|
+
(r) => fn.returnsTable.map((c, i) => {
|
|
18321
|
+
const raw2 = r[i] ?? null;
|
|
18322
|
+
if (raw2 === null) return null;
|
|
18323
|
+
const src = vars.get(c.name);
|
|
18324
|
+
return castTo(env.ctx, tv(src?.t ?? c.type, raw2), c.type, {}).v;
|
|
18325
|
+
})
|
|
18326
|
+
);
|
|
18327
|
+
return { columns: cols, rows };
|
|
18328
|
+
}
|
|
18329
|
+
return { columns: cols, rows: emit };
|
|
18330
|
+
}
|
|
18331
|
+
|
|
18332
|
+
// src/executor/triggers.ts
|
|
18333
|
+
function matches(t, event) {
|
|
18334
|
+
return t.events.some((e) => e.event === event);
|
|
18335
|
+
}
|
|
18336
|
+
var executor = null;
|
|
18337
|
+
function setTriggerExecutor(e) {
|
|
18338
|
+
executor = e;
|
|
18339
|
+
}
|
|
18340
|
+
function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
|
|
18341
|
+
let row = newRow ?? oldRow;
|
|
18342
|
+
for (const t of table.triggers) {
|
|
18343
|
+
if (t.timing !== timing || !matches(t, event) || !t.forEachRow) continue;
|
|
18344
|
+
if (!executor) throw unsupported(`trigger "${t.name}" execution`);
|
|
18345
|
+
const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
|
|
18346
|
+
if (timing === "before") {
|
|
18347
|
+
if (result === null) return { row: null };
|
|
18348
|
+
row = result;
|
|
18349
|
+
}
|
|
17249
18350
|
}
|
|
17250
|
-
return
|
|
18351
|
+
return { row };
|
|
17251
18352
|
}
|
|
18353
|
+
|
|
18354
|
+
// src/executor/triggers-exec.ts
|
|
17252
18355
|
var ReturnSignal = class {
|
|
17253
18356
|
constructor(row) {
|
|
17254
18357
|
this.row = row;
|
|
@@ -17276,8 +18379,17 @@ function runTriggerBody(env, table, stmts, vars) {
|
|
|
17276
18379
|
vars.newRow[idx] = v.v === null ? null : castTo(env.ctx, v, table.columns[idx].type.id, { assignment: true }).v;
|
|
17277
18380
|
break;
|
|
17278
18381
|
}
|
|
17279
|
-
case "
|
|
17280
|
-
throw new ReturnSignal(
|
|
18382
|
+
case "return_new":
|
|
18383
|
+
throw new ReturnSignal(vars.newRow);
|
|
18384
|
+
case "return_old":
|
|
18385
|
+
throw new ReturnSignal(vars.oldRow);
|
|
18386
|
+
case "return_empty":
|
|
18387
|
+
throw new ReturnSignal(null);
|
|
18388
|
+
case "return_expr": {
|
|
18389
|
+
const v = evalScalar(env, scope(), stmt.expr);
|
|
18390
|
+
if (v.v === null) throw new ReturnSignal(null);
|
|
18391
|
+
throw unsupported("trigger body: RETURN expression");
|
|
18392
|
+
}
|
|
17281
18393
|
case "if": {
|
|
17282
18394
|
let taken = false;
|
|
17283
18395
|
for (const b of stmt.branches) {
|
|
@@ -17294,6 +18406,18 @@ function runTriggerBody(env, table, stmts, vars) {
|
|
|
17294
18406
|
break;
|
|
17295
18407
|
case "raise":
|
|
17296
18408
|
throw pgError("raise_exception", stmt.message, "P0001");
|
|
18409
|
+
case "block": {
|
|
18410
|
+
try {
|
|
18411
|
+
runTriggerBody(env, table, stmt.body, vars);
|
|
18412
|
+
} catch (e) {
|
|
18413
|
+
if (e instanceof ReturnSignal) throw e;
|
|
18414
|
+
if (stmt.handler === null) throw e;
|
|
18415
|
+
runTriggerBody(env, table, stmt.handler, vars);
|
|
18416
|
+
}
|
|
18417
|
+
break;
|
|
18418
|
+
}
|
|
18419
|
+
default:
|
|
18420
|
+
throw unsupported(`trigger body: ${stmt.kind}`);
|
|
17297
18421
|
}
|
|
17298
18422
|
}
|
|
17299
18423
|
}
|
|
@@ -17327,7 +18451,9 @@ function executeTrigger(env, table, trigger, _event, oldRow, newRow) {
|
|
|
17327
18451
|
if (!fn) {
|
|
17328
18452
|
throw pgError("undefined_function", `function ${trigger.funcSchema}.${trigger.funcName}() does not exist`, "42883");
|
|
17329
18453
|
}
|
|
17330
|
-
const
|
|
18454
|
+
const raw = fn.rawBody;
|
|
18455
|
+
if (raw === null) throw unsupported(`trigger function ${fn.name} has no body`);
|
|
18456
|
+
const stmts = compileTriggerBody(raw);
|
|
17331
18457
|
const vars = { newRow: newRow ? newRow.slice() : null, oldRow };
|
|
17332
18458
|
try {
|
|
17333
18459
|
runTriggerBody(env, table, stmts, vars);
|
|
@@ -17384,198 +18510,23 @@ var EngineCtx = class {
|
|
|
17384
18510
|
}
|
|
17385
18511
|
};
|
|
17386
18512
|
|
|
17387
|
-
// src/constraints/enforce.ts
|
|
17388
|
-
function tableScope(table, row) {
|
|
17389
|
-
const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
|
|
17390
|
-
return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
|
|
17391
|
-
}
|
|
17392
|
-
function checkNotNull(env, table, row) {
|
|
17393
|
-
void env;
|
|
17394
|
-
for (let i = 0; i < table.columns.length; i++) {
|
|
17395
|
-
const c = table.columns[i];
|
|
17396
|
-
if (c.notNull && (row[i] ?? null) === null) {
|
|
17397
|
-
throw pgError(
|
|
17398
|
-
"not_null_violation",
|
|
17399
|
-
`null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
|
|
17400
|
-
"23502"
|
|
17401
|
-
);
|
|
17402
|
-
}
|
|
17403
|
-
}
|
|
17404
|
-
}
|
|
17405
|
-
function checkChecks(env, table, row) {
|
|
17406
|
-
for (const con of table.constraints) {
|
|
17407
|
-
if (con.kind !== "check") continue;
|
|
17408
|
-
const scope = makeEvalScope(env, tableScope(table, row));
|
|
17409
|
-
const v = evalExpr(env.ctx, scope, con.expr);
|
|
17410
|
-
if (v.v === null) continue;
|
|
17411
|
-
const b = castTo(env.ctx, v, "bool", {});
|
|
17412
|
-
if (b.v !== true) {
|
|
17413
|
-
throw pgError(
|
|
17414
|
-
"check_violation",
|
|
17415
|
-
`new row for relation "${table.name}" violates check constraint "${con.name}"`,
|
|
17416
|
-
"23514"
|
|
17417
|
-
);
|
|
17418
|
-
}
|
|
17419
|
-
}
|
|
17420
|
-
}
|
|
17421
|
-
function uniqueSpecsFor(env, table) {
|
|
17422
|
-
const specs = [];
|
|
17423
|
-
for (const con of table.constraints) {
|
|
17424
|
-
if (con.kind !== "primary_key" && con.kind !== "unique") continue;
|
|
17425
|
-
specs.push({
|
|
17426
|
-
name: con.name,
|
|
17427
|
-
keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
|
|
17428
|
-
columnNames: con.columns,
|
|
17429
|
-
nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
|
|
17430
|
-
where: null,
|
|
17431
|
-
isPrimary: con.kind === "primary_key"
|
|
17432
|
-
});
|
|
17433
|
-
}
|
|
17434
|
-
const schema = env.ctx.state.schemas.get(table.schema);
|
|
17435
|
-
if (schema) {
|
|
17436
|
-
for (const idx of schema.indexes.values()) {
|
|
17437
|
-
if (idx.table !== table.name || !idx.unique || idx.isConstraint) continue;
|
|
17438
|
-
specs.push({
|
|
17439
|
-
name: idx.name,
|
|
17440
|
-
keys: idx.columns.map((c) => ({
|
|
17441
|
-
colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
|
|
17442
|
-
expr: c.expr
|
|
17443
|
-
})),
|
|
17444
|
-
columnNames: idx.columns.map((c) => c.column ?? "expr"),
|
|
17445
|
-
nullsNotDistinct: idx.nullsNotDistinct,
|
|
17446
|
-
where: idx.where,
|
|
17447
|
-
isPrimary: false
|
|
17448
|
-
});
|
|
17449
|
-
}
|
|
17450
|
-
}
|
|
17451
|
-
return specs;
|
|
17452
|
-
}
|
|
17453
|
-
function uniqueKeyOf(env, table, spec, row) {
|
|
17454
|
-
if (spec.where) {
|
|
17455
|
-
const scope = makeEvalScope(env, tableScope(table, row));
|
|
17456
|
-
const v = evalExpr(env.ctx, scope, spec.where);
|
|
17457
|
-
if (v.v !== true) return null;
|
|
17458
|
-
}
|
|
17459
|
-
const parts = [];
|
|
17460
|
-
let hasNull = false;
|
|
17461
|
-
for (const k of spec.keys) {
|
|
17462
|
-
let value;
|
|
17463
|
-
let type;
|
|
17464
|
-
if (k.expr) {
|
|
17465
|
-
const scope = makeEvalScope(env, tableScope(table, row));
|
|
17466
|
-
const v = evalExpr(env.ctx, scope, k.expr);
|
|
17467
|
-
value = v.v;
|
|
17468
|
-
type = v.t === "unknown" ? "text" : v.t;
|
|
17469
|
-
} else {
|
|
17470
|
-
value = row[k.colIdx] ?? null;
|
|
17471
|
-
type = table.columns[k.colIdx].type.id;
|
|
17472
|
-
}
|
|
17473
|
-
if (value === null) {
|
|
17474
|
-
hasNull = true;
|
|
17475
|
-
parts.push("\0N");
|
|
17476
|
-
} else {
|
|
17477
|
-
parts.push(datumKey(type, value));
|
|
17478
|
-
}
|
|
17479
|
-
}
|
|
17480
|
-
if (hasNull && !spec.nullsNotDistinct) return null;
|
|
17481
|
-
return parts.join("");
|
|
17482
|
-
}
|
|
17483
|
-
function checkUnique(env, table, row, selfIdx) {
|
|
17484
|
-
for (const spec of uniqueSpecsFor(env, table)) {
|
|
17485
|
-
const key = uniqueKeyOf(env, table, spec, row);
|
|
17486
|
-
if (key === null) continue;
|
|
17487
|
-
for (let i = 0; i < table.rows.length; i++) {
|
|
17488
|
-
if (i === selfIdx) continue;
|
|
17489
|
-
const other = uniqueKeyOf(env, table, spec, table.rows[i]);
|
|
17490
|
-
if (other === key) {
|
|
17491
|
-
throw pgError(
|
|
17492
|
-
"constraint_unique",
|
|
17493
|
-
`duplicate key value violates unique constraint "${spec.name}"`,
|
|
17494
|
-
"23505"
|
|
17495
|
-
// PG adds a DETAIL line; kept in message for classification purposes
|
|
17496
|
-
);
|
|
17497
|
-
}
|
|
17498
|
-
}
|
|
17499
|
-
}
|
|
17500
|
-
}
|
|
17501
|
-
function findConflict(env, table, spec, row) {
|
|
17502
|
-
const key = uniqueKeyOf(env, table, spec, row);
|
|
17503
|
-
if (key === null) return null;
|
|
17504
|
-
for (let i = 0; i < table.rows.length; i++) {
|
|
17505
|
-
const other = uniqueKeyOf(env, table, spec, table.rows[i]);
|
|
17506
|
-
if (other === key) return i;
|
|
17507
|
-
}
|
|
17508
|
-
return null;
|
|
17509
|
-
}
|
|
17510
|
-
function checkForeignKeys(env, table, row) {
|
|
17511
|
-
const state = env.ctx.state;
|
|
17512
|
-
for (const con of table.constraints) {
|
|
17513
|
-
if (con.kind !== "foreign_key") continue;
|
|
17514
|
-
const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
|
|
17515
|
-
const nulls = values.filter((v) => v === null).length;
|
|
17516
|
-
if (con.match === "simple" && nulls > 0) continue;
|
|
17517
|
-
if (con.match === "full") {
|
|
17518
|
-
if (nulls === values.length) continue;
|
|
17519
|
-
if (nulls > 0) {
|
|
17520
|
-
throw pgError(
|
|
17521
|
-
"constraint_foreign_key",
|
|
17522
|
-
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
17523
|
-
"23503"
|
|
17524
|
-
);
|
|
17525
|
-
}
|
|
17526
|
-
}
|
|
17527
|
-
const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
|
|
17528
|
-
if (!refTable) {
|
|
17529
|
-
throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
|
|
17530
|
-
}
|
|
17531
|
-
const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
|
|
17532
|
-
const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
|
|
17533
|
-
const wanted = values.map((v, i) => {
|
|
17534
|
-
const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
|
|
17535
|
-
const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
|
|
17536
|
-
return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
|
|
17537
|
-
}).join("");
|
|
17538
|
-
const found = refTable.rows.some((r) => {
|
|
17539
|
-
const key = refIdxs.map((ri, i) => {
|
|
17540
|
-
const v = r[ri] ?? null;
|
|
17541
|
-
return v === null ? "\0N" : datumKey(keyTypes[i], v);
|
|
17542
|
-
}).join("");
|
|
17543
|
-
return key === wanted;
|
|
17544
|
-
});
|
|
17545
|
-
if (!found) {
|
|
17546
|
-
throw pgError(
|
|
17547
|
-
"constraint_foreign_key",
|
|
17548
|
-
`insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
|
|
17549
|
-
"23503"
|
|
17550
|
-
);
|
|
17551
|
-
}
|
|
17552
|
-
}
|
|
17553
|
-
}
|
|
17554
|
-
function referencingConstraints(env, table) {
|
|
17555
|
-
const out = [];
|
|
17556
|
-
for (const schema of env.ctx.state.schemas.values()) {
|
|
17557
|
-
for (const t of schema.tables.values()) {
|
|
17558
|
-
for (const con of t.constraints) {
|
|
17559
|
-
if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
|
|
17560
|
-
out.push({ table: t, constraint: con });
|
|
17561
|
-
}
|
|
17562
|
-
}
|
|
17563
|
-
}
|
|
17564
|
-
}
|
|
17565
|
-
return out;
|
|
17566
|
-
}
|
|
17567
|
-
|
|
17568
18513
|
// src/storage/database-state.ts
|
|
17569
18514
|
var TableData = class _TableData {
|
|
17570
18515
|
name;
|
|
17571
18516
|
schema;
|
|
17572
18517
|
columns;
|
|
17573
18518
|
rows;
|
|
18519
|
+
/** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
|
|
18520
|
+
slab = null;
|
|
17574
18521
|
constraints;
|
|
17575
18522
|
triggers;
|
|
17576
18523
|
temp;
|
|
17577
18524
|
/** monotonically increasing oid-like id for catalog output */
|
|
17578
18525
|
oid;
|
|
18526
|
+
/** >0 while shared with a clone or transaction snapshot. */
|
|
18527
|
+
shareCount = 0;
|
|
18528
|
+
/** Derived unique/btree index maps; null until built or invalidated. */
|
|
18529
|
+
indexStores = null;
|
|
17579
18530
|
constructor(schema, name, columns, oid, temp = false) {
|
|
17580
18531
|
this.schema = schema;
|
|
17581
18532
|
this.name = name;
|
|
@@ -17586,6 +18537,42 @@ var TableData = class _TableData {
|
|
|
17586
18537
|
this.temp = temp;
|
|
17587
18538
|
this.oid = oid;
|
|
17588
18539
|
}
|
|
18540
|
+
rowCount() {
|
|
18541
|
+
return this.slab ? this.slab.rowCount : this.rows.length;
|
|
18542
|
+
}
|
|
18543
|
+
rowAt(index) {
|
|
18544
|
+
if (this.slab) return this.slab.rowAt(index);
|
|
18545
|
+
return this.rows[index] ?? [];
|
|
18546
|
+
}
|
|
18547
|
+
/** All rows for scans; materializes slab once into `rows` when needed. */
|
|
18548
|
+
allRows() {
|
|
18549
|
+
this.materializeSlab();
|
|
18550
|
+
return this.rows;
|
|
18551
|
+
}
|
|
18552
|
+
attachSlab(slab) {
|
|
18553
|
+
this.slab = slab;
|
|
18554
|
+
this.rows = [];
|
|
18555
|
+
}
|
|
18556
|
+
materializeSlab() {
|
|
18557
|
+
if (!this.slab) return;
|
|
18558
|
+
this.rows = this.slab.materialize();
|
|
18559
|
+
this.slab = null;
|
|
18560
|
+
this.indexStores = null;
|
|
18561
|
+
}
|
|
18562
|
+
/** Writable row storage; materializes slab if needed. */
|
|
18563
|
+
mutableRows() {
|
|
18564
|
+
this.materializeSlab();
|
|
18565
|
+
return this.rows;
|
|
18566
|
+
}
|
|
18567
|
+
get frozen() {
|
|
18568
|
+
return this.shareCount > 0;
|
|
18569
|
+
}
|
|
18570
|
+
freeze() {
|
|
18571
|
+
this.shareCount++;
|
|
18572
|
+
}
|
|
18573
|
+
thaw() {
|
|
18574
|
+
if (this.shareCount > 0) this.shareCount--;
|
|
18575
|
+
}
|
|
17589
18576
|
columnIndex(name) {
|
|
17590
18577
|
return this.columns.findIndex((c) => c.name === name);
|
|
17591
18578
|
}
|
|
@@ -17597,9 +18584,20 @@ var TableData = class _TableData {
|
|
|
17597
18584
|
this.oid,
|
|
17598
18585
|
this.temp
|
|
17599
18586
|
);
|
|
17600
|
-
|
|
18587
|
+
if (this.slab) {
|
|
18588
|
+
t.rows = this.slab.materialize().map((r) => r.slice());
|
|
18589
|
+
} else {
|
|
18590
|
+
t.rows = this.rows.map((r) => r.slice());
|
|
18591
|
+
}
|
|
17601
18592
|
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
17602
18593
|
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
18594
|
+
t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
|
|
18595
|
+
return t;
|
|
18596
|
+
}
|
|
18597
|
+
/** Independent writable copy (row tuples copied so ALTER COLUMN is isolated). */
|
|
18598
|
+
cloneForWrite() {
|
|
18599
|
+
const t = this.clone();
|
|
18600
|
+
t.materializeSlab();
|
|
17603
18601
|
return t;
|
|
17604
18602
|
}
|
|
17605
18603
|
};
|
|
@@ -17642,6 +18640,18 @@ var SchemaData = class _SchemaData {
|
|
|
17642
18640
|
for (const [k, v] of this.indexes) s.indexes.set(k, { ...v, columns: v.columns.map((c) => ({ ...c })) });
|
|
17643
18641
|
return s;
|
|
17644
18642
|
}
|
|
18643
|
+
/** Share table/sequence objects; copy maps so CREATE/DROP is isolated. */
|
|
18644
|
+
cloneShallow() {
|
|
18645
|
+
const s = new _SchemaData(this.name, this.oid);
|
|
18646
|
+
s.tables = new Map(this.tables);
|
|
18647
|
+
s.views = new Map(this.views);
|
|
18648
|
+
s.sequences = new Map(this.sequences);
|
|
18649
|
+
s.enums = new Map(this.enums);
|
|
18650
|
+
s.domains = new Map(this.domains);
|
|
18651
|
+
for (const [k, v] of this.functions) s.functions.set(k, v.slice());
|
|
18652
|
+
s.indexes = new Map(this.indexes);
|
|
18653
|
+
return s;
|
|
18654
|
+
}
|
|
17645
18655
|
};
|
|
17646
18656
|
var DatabaseState = class _DatabaseState {
|
|
17647
18657
|
schemas = /* @__PURE__ */ new Map();
|
|
@@ -17833,7 +18843,7 @@ var DatabaseState = class _DatabaseState {
|
|
|
17833
18843
|
}
|
|
17834
18844
|
return null;
|
|
17835
18845
|
}
|
|
17836
|
-
/** deep clone
|
|
18846
|
+
/** deep clone (datums are immutable; rows copied) */
|
|
17837
18847
|
clone() {
|
|
17838
18848
|
const s = new _DatabaseState(this.prng, this.clock);
|
|
17839
18849
|
s.schemas = /* @__PURE__ */ new Map();
|
|
@@ -17847,6 +18857,79 @@ var DatabaseState = class _DatabaseState {
|
|
|
17847
18857
|
s.oidCounter = this.oidCounter;
|
|
17848
18858
|
return s;
|
|
17849
18859
|
}
|
|
18860
|
+
/** Share frozen catalog objects; copy maps so CREATE/DROP is isolated. */
|
|
18861
|
+
cloneShallow() {
|
|
18862
|
+
const s = new _DatabaseState(this.prng, this.clock);
|
|
18863
|
+
s.schemas = /* @__PURE__ */ new Map();
|
|
18864
|
+
for (const [k, v] of this.schemas) s.schemas.set(k, v.cloneShallow());
|
|
18865
|
+
s.settings = new Map(this.settings);
|
|
18866
|
+
s.localSettings = new Map(this.localSettings);
|
|
18867
|
+
s.prepared = new Map(this.prepared);
|
|
18868
|
+
s.changes = this.changes;
|
|
18869
|
+
s.inTransaction = this.inTransaction;
|
|
18870
|
+
s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
|
|
18871
|
+
s.oidCounter = this.oidCounter;
|
|
18872
|
+
return s;
|
|
18873
|
+
}
|
|
18874
|
+
freezeShared() {
|
|
18875
|
+
for (const schema of this.schemas.values()) {
|
|
18876
|
+
for (const table of schema.tables.values()) table.freeze();
|
|
18877
|
+
for (const seq of schema.sequences.values()) seq.shareCount = (seq.shareCount ?? 0) + 1;
|
|
18878
|
+
for (const view of schema.views.values()) view.shareCount = (view.shareCount ?? 0) + 1;
|
|
18879
|
+
for (const en of schema.enums.values()) en.shareCount = (en.shareCount ?? 0) + 1;
|
|
18880
|
+
for (const domain of schema.domains.values()) domain.shareCount = (domain.shareCount ?? 0) + 1;
|
|
18881
|
+
}
|
|
18882
|
+
}
|
|
18883
|
+
thawShared() {
|
|
18884
|
+
for (const schema of this.schemas.values()) {
|
|
18885
|
+
for (const table of schema.tables.values()) table.thaw();
|
|
18886
|
+
for (const seq of schema.sequences.values()) {
|
|
18887
|
+
if ((seq.shareCount ?? 0) > 0) seq.shareCount = (seq.shareCount ?? 1) - 1;
|
|
18888
|
+
}
|
|
18889
|
+
for (const view of schema.views.values()) {
|
|
18890
|
+
if ((view.shareCount ?? 0) > 0) view.shareCount = (view.shareCount ?? 1) - 1;
|
|
18891
|
+
}
|
|
18892
|
+
for (const en of schema.enums.values()) {
|
|
18893
|
+
if ((en.shareCount ?? 0) > 0) en.shareCount = (en.shareCount ?? 1) - 1;
|
|
18894
|
+
}
|
|
18895
|
+
for (const domain of schema.domains.values()) {
|
|
18896
|
+
if ((domain.shareCount ?? 0) > 0) domain.shareCount = (domain.shareCount ?? 1) - 1;
|
|
18897
|
+
}
|
|
18898
|
+
}
|
|
18899
|
+
}
|
|
18900
|
+
ensureWritableTable(table) {
|
|
18901
|
+
if (!table.frozen) return table;
|
|
18902
|
+
const schema = this.schemas.get(table.schema);
|
|
18903
|
+
const copy = table.cloneForWrite();
|
|
18904
|
+
schema?.tables.set(table.name, copy);
|
|
18905
|
+
return copy;
|
|
18906
|
+
}
|
|
18907
|
+
ensureWritableSequence(seq) {
|
|
18908
|
+
if ((seq.shareCount ?? 0) === 0) return seq;
|
|
18909
|
+
const schema = this.schemas.get(seq.schema);
|
|
18910
|
+
const copy = { ...seq, shareCount: 0 };
|
|
18911
|
+
schema?.sequences.set(seq.name, copy);
|
|
18912
|
+
return copy;
|
|
18913
|
+
}
|
|
18914
|
+
ensureWritableView(view) {
|
|
18915
|
+
if ((view.shareCount ?? 0) === 0) return view;
|
|
18916
|
+
const schema = this.schemas.get(view.schema);
|
|
18917
|
+
const copy = {
|
|
18918
|
+
...view,
|
|
18919
|
+
shareCount: 0,
|
|
18920
|
+
matRows: view.matRows ? view.matRows.map((r) => r.slice()) : null,
|
|
18921
|
+
matColumns: view.matColumns ? view.matColumns.map((c) => ({ ...c })) : null
|
|
18922
|
+
};
|
|
18923
|
+
schema?.views.set(view.name, copy);
|
|
18924
|
+
return copy;
|
|
18925
|
+
}
|
|
18926
|
+
ensureWritableEnum(en) {
|
|
18927
|
+
if ((en.shareCount ?? 0) === 0) return en;
|
|
18928
|
+
const schema = this.schemas.get(en.schema);
|
|
18929
|
+
const copy = { ...en, labels: en.labels.slice(), shareCount: 0 };
|
|
18930
|
+
schema?.enums.set(en.name, copy);
|
|
18931
|
+
return copy;
|
|
18932
|
+
}
|
|
17850
18933
|
/** copy the contents of `other` into this state (rollback restore) */
|
|
17851
18934
|
restoreFrom(other) {
|
|
17852
18935
|
this.schemas = other.schemas;
|
|
@@ -17857,6 +18940,14 @@ var DatabaseState = class _DatabaseState {
|
|
|
17857
18940
|
this.lastSequence = other.lastSequence;
|
|
17858
18941
|
this.oidCounter = other.oidCounter;
|
|
17859
18942
|
}
|
|
18943
|
+
/** @internal Snapshot codec access to the oid allocator. */
|
|
18944
|
+
snapshotOidCounter() {
|
|
18945
|
+
return this.oidCounter;
|
|
18946
|
+
}
|
|
18947
|
+
/** @internal Snapshot codec restore of the oid allocator. */
|
|
18948
|
+
restoreOidCounter(value) {
|
|
18949
|
+
this.oidCounter = value;
|
|
18950
|
+
}
|
|
17860
18951
|
};
|
|
17861
18952
|
|
|
17862
18953
|
// src/executor/ddl.ts
|
|
@@ -18236,10 +19327,10 @@ function executeCreateTableAs(env, stmt) {
|
|
|
18236
19327
|
}));
|
|
18237
19328
|
const table = new TableData(schema.name, name, columns, state.nextOid(), stmt.temp);
|
|
18238
19329
|
if (stmt.withData) {
|
|
18239
|
-
|
|
19330
|
+
for (const row of rel2.rows) table.mutableRows().push(row.slice());
|
|
18240
19331
|
}
|
|
18241
19332
|
schema.tables.set(name, table);
|
|
18242
|
-
const count = stmt.withData ? table.
|
|
19333
|
+
const count = stmt.withData ? table.rowCount() : 0;
|
|
18243
19334
|
return { columns: [], rows: [], command: `SELECT ${count}`, rowCount: count };
|
|
18244
19335
|
}
|
|
18245
19336
|
function executeCreateIndex(env, stmt) {
|
|
@@ -18280,8 +19371,8 @@ function executeCreateIndex(env, stmt) {
|
|
|
18280
19371
|
});
|
|
18281
19372
|
if (stmt.unique) {
|
|
18282
19373
|
try {
|
|
18283
|
-
for (let i = 0; i < table.
|
|
18284
|
-
checkUnique(env, table, table.
|
|
19374
|
+
for (let i = 0; i < table.rowCount(); i++) {
|
|
19375
|
+
checkUnique(env, table, table.rowAt(i), i);
|
|
18285
19376
|
}
|
|
18286
19377
|
} catch (err) {
|
|
18287
19378
|
schema.indexes.delete(name);
|
|
@@ -18328,10 +19419,11 @@ function executeCreateView(env, stmt) {
|
|
|
18328
19419
|
return commandResult(stmt.materialized ? "CREATE MATERIALIZED VIEW" : "CREATE VIEW", 0);
|
|
18329
19420
|
}
|
|
18330
19421
|
function executeRefreshMatView(env, stmt) {
|
|
18331
|
-
const
|
|
18332
|
-
if (!
|
|
19422
|
+
const found = env.ctx.state.findView(stmt.name);
|
|
19423
|
+
if (!found?.materialized) {
|
|
18333
19424
|
throw pgError("undefined_table", `materialized view "${stmt.name.join(".")}" does not exist`, "42P01");
|
|
18334
19425
|
}
|
|
19426
|
+
const view = env.ctx.state.ensureWritableView(found);
|
|
18335
19427
|
const rel2 = executeSelectStmt({ ctx: env.ctx, params: null, ctes: /* @__PURE__ */ new Map(), outer: null }, view.query);
|
|
18336
19428
|
view.matColumns = rel2.columns.map((c, i) => ({
|
|
18337
19429
|
name: view.columns?.[i] ?? c.name,
|
|
@@ -18359,10 +19451,11 @@ function executeCreateEnum(env, stmt) {
|
|
|
18359
19451
|
return commandResult("CREATE TYPE", 0);
|
|
18360
19452
|
}
|
|
18361
19453
|
function executeAlterEnum(env, stmt) {
|
|
18362
|
-
const
|
|
18363
|
-
if (!
|
|
19454
|
+
const found = env.ctx.state.findEnum(stmt.name);
|
|
19455
|
+
if (!found) {
|
|
18364
19456
|
throw pgError("undefined_object", `type "${stmt.name.join(".")}" does not exist`, "42704");
|
|
18365
19457
|
}
|
|
19458
|
+
const e = env.ctx.state.ensureWritableEnum(found);
|
|
18366
19459
|
const a = stmt.action;
|
|
18367
19460
|
if (a.kind === "add_value") {
|
|
18368
19461
|
if (e.labels.includes(a.label)) {
|
|
@@ -18479,10 +19572,11 @@ function executeCreateFunction(env, stmt) {
|
|
|
18479
19572
|
}
|
|
18480
19573
|
function executeCreateTrigger(env, stmt) {
|
|
18481
19574
|
const state = env.ctx.state;
|
|
18482
|
-
const
|
|
18483
|
-
if (!
|
|
19575
|
+
const found = state.findTable(stmt.table);
|
|
19576
|
+
if (!found) {
|
|
18484
19577
|
throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
|
|
18485
19578
|
}
|
|
19579
|
+
const table = state.ensureWritableTable(found);
|
|
18486
19580
|
const fnSchema = stmt.funcName.length >= 2 ? stmt.funcName[stmt.funcName.length - 2] : null;
|
|
18487
19581
|
const fnName = stmt.funcName[stmt.funcName.length - 1];
|
|
18488
19582
|
const fns = state.findFunctions(stmt.funcName);
|
|
@@ -18509,13 +19603,15 @@ function executeCreateTrigger(env, stmt) {
|
|
|
18509
19603
|
}
|
|
18510
19604
|
function executeAlterTable(env, stmt) {
|
|
18511
19605
|
const state = env.ctx.state;
|
|
18512
|
-
const
|
|
18513
|
-
if (!
|
|
19606
|
+
const found = state.findTable(stmt.table);
|
|
19607
|
+
if (!found) {
|
|
18514
19608
|
if (stmt.ifExists) return commandResult("ALTER TABLE", 0);
|
|
18515
19609
|
throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
|
|
18516
19610
|
}
|
|
19611
|
+
const table = state.ensureWritableTable(found);
|
|
18517
19612
|
const schema = state.getSchema(table.schema);
|
|
18518
19613
|
for (const action of stmt.actions) {
|
|
19614
|
+
const rows = table.mutableRows();
|
|
18519
19615
|
switch (action.kind) {
|
|
18520
19616
|
case "add_column": {
|
|
18521
19617
|
if (table.columnIndex(action.column.name) !== -1) {
|
|
@@ -18532,8 +19628,8 @@ function executeAlterTable(env, stmt) {
|
|
|
18532
19628
|
for (const seq of built.sequences) schema.sequences.set(seq.name, seq);
|
|
18533
19629
|
table.columns.push(col);
|
|
18534
19630
|
table.constraints.push(...built.constraints);
|
|
18535
|
-
for (let i = 0; i <
|
|
18536
|
-
const row =
|
|
19631
|
+
for (let i = 0; i < rows.length; i++) {
|
|
19632
|
+
const row = rows[i];
|
|
18537
19633
|
let v = null;
|
|
18538
19634
|
if (col.identity) {
|
|
18539
19635
|
const seq = state.findSequence(col.identity.sequence.split("."));
|
|
@@ -18547,14 +19643,14 @@ function executeAlterTable(env, stmt) {
|
|
|
18547
19643
|
row.push(v);
|
|
18548
19644
|
}
|
|
18549
19645
|
if (col.generated) {
|
|
18550
|
-
for (const row of
|
|
19646
|
+
for (const row of rows) {
|
|
18551
19647
|
const idx = table.columnIndex(col.name);
|
|
18552
19648
|
const scope = tableScopeFor(env, table, row);
|
|
18553
19649
|
row[idx] = castTo(env.ctx, evalScalar(env, scope, col.generated), col.type.id, { assignment: true }).v;
|
|
18554
19650
|
}
|
|
18555
19651
|
}
|
|
18556
19652
|
if (col.notNull) {
|
|
18557
|
-
for (const row of
|
|
19653
|
+
for (const row of rows) {
|
|
18558
19654
|
if ((row[table.columnIndex(col.name)] ?? null) === null) {
|
|
18559
19655
|
throw pgError(
|
|
18560
19656
|
"not_null_violation",
|
|
@@ -18596,7 +19692,7 @@ function executeAlterTable(env, stmt) {
|
|
|
18596
19692
|
return true;
|
|
18597
19693
|
});
|
|
18598
19694
|
table.columns.splice(idx, 1);
|
|
18599
|
-
for (const row of
|
|
19695
|
+
for (const row of rows) row.splice(idx, 1);
|
|
18600
19696
|
for (const [iname, idxMeta] of [...schema.indexes]) {
|
|
18601
19697
|
if (idxMeta.table === table.name && idxMeta.columns.some((c) => c.column === action.name)) {
|
|
18602
19698
|
schema.indexes.delete(iname);
|
|
@@ -18615,8 +19711,8 @@ function executeAlterTable(env, stmt) {
|
|
|
18615
19711
|
}
|
|
18616
19712
|
const resolved = resolveTypeName(state, action.typeName);
|
|
18617
19713
|
const col = table.columns[idx];
|
|
18618
|
-
for (let i = 0; i <
|
|
18619
|
-
const row =
|
|
19714
|
+
for (let i = 0; i < rows.length; i++) {
|
|
19715
|
+
const row = rows[i];
|
|
18620
19716
|
const old = row[idx] ?? null;
|
|
18621
19717
|
let nv;
|
|
18622
19718
|
if (action.using) {
|
|
@@ -18636,7 +19732,7 @@ function executeAlterTable(env, stmt) {
|
|
|
18636
19732
|
}
|
|
18637
19733
|
const next = row.slice();
|
|
18638
19734
|
next[idx] = nv;
|
|
18639
|
-
|
|
19735
|
+
rows[i] = next;
|
|
18640
19736
|
}
|
|
18641
19737
|
col.type = resolved.column;
|
|
18642
19738
|
col.domain = resolved.domain;
|
|
@@ -18675,7 +19771,7 @@ function executeAlterTable(env, stmt) {
|
|
|
18675
19771
|
"42703"
|
|
18676
19772
|
);
|
|
18677
19773
|
}
|
|
18678
|
-
for (const row of
|
|
19774
|
+
for (const row of rows) {
|
|
18679
19775
|
if ((row[idx] ?? null) === null) {
|
|
18680
19776
|
throw pgError(
|
|
18681
19777
|
"not_null_violation",
|
|
@@ -18712,7 +19808,7 @@ function executeAlterTable(env, stmt) {
|
|
|
18712
19808
|
}
|
|
18713
19809
|
for (const cn of con.columns) {
|
|
18714
19810
|
const col = table.columns[table.columnIndex(cn)];
|
|
18715
|
-
for (const row of
|
|
19811
|
+
for (const row of rows) {
|
|
18716
19812
|
if ((row[table.columnIndex(cn)] ?? null) === null) {
|
|
18717
19813
|
throw pgError(
|
|
18718
19814
|
"not_null_violation",
|
|
@@ -18892,6 +19988,8 @@ function executeAlterTable(env, stmt) {
|
|
|
18892
19988
|
validateConstraint(env, table, con);
|
|
18893
19989
|
break;
|
|
18894
19990
|
}
|
|
19991
|
+
case "reloptions":
|
|
19992
|
+
break;
|
|
18895
19993
|
}
|
|
18896
19994
|
}
|
|
18897
19995
|
return commandResult("ALTER TABLE", 0);
|
|
@@ -18906,8 +20004,8 @@ function seqNext(env, seq) {
|
|
|
18906
20004
|
}
|
|
18907
20005
|
function validateConstraint(env, table, con) {
|
|
18908
20006
|
void con;
|
|
18909
|
-
for (let i = 0; i < table.
|
|
18910
|
-
const row = table.
|
|
20007
|
+
for (let i = 0; i < table.rowCount(); i++) {
|
|
20008
|
+
const row = table.rowAt(i);
|
|
18911
20009
|
checkChecks(env, table, row);
|
|
18912
20010
|
checkUnique(env, table, row, i);
|
|
18913
20011
|
checkForeignKeys(env, table, row);
|
|
@@ -19134,7 +20232,7 @@ function executeTruncate(env, stmt) {
|
|
|
19134
20232
|
const tables = stmt.tables.map((parts) => {
|
|
19135
20233
|
const t = state.findTable(parts);
|
|
19136
20234
|
if (!t) throw pgError("undefined_table", `relation "${parts.join(".")}" does not exist`, "42P01");
|
|
19137
|
-
return t;
|
|
20235
|
+
return state.ensureWritableTable(t);
|
|
19138
20236
|
});
|
|
19139
20237
|
const set = new Set(tables);
|
|
19140
20238
|
if (stmt.cascade) {
|
|
@@ -19153,7 +20251,7 @@ function executeTruncate(env, stmt) {
|
|
|
19153
20251
|
} else {
|
|
19154
20252
|
for (const t of set) {
|
|
19155
20253
|
for (const r of referencingConstraints(env, t)) {
|
|
19156
|
-
if (!set.has(r.table) && r.table.
|
|
20254
|
+
if (!set.has(r.table) && r.table.rowCount() >= 0) {
|
|
19157
20255
|
throw pgError(
|
|
19158
20256
|
"feature_not_supported",
|
|
19159
20257
|
`cannot truncate a table referenced in a foreign key constraint`,
|
|
@@ -19164,13 +20262,15 @@ function executeTruncate(env, stmt) {
|
|
|
19164
20262
|
}
|
|
19165
20263
|
}
|
|
19166
20264
|
for (const t of set) {
|
|
19167
|
-
|
|
20265
|
+
const writable = state.ensureWritableTable(t);
|
|
20266
|
+
writable.mutableRows().length = 0;
|
|
19168
20267
|
if (stmt.restartIdentity) {
|
|
19169
|
-
const schema = state.getSchema(
|
|
19170
|
-
for (const seq of schema.sequences.values()) {
|
|
19171
|
-
if (seq.ownedBy?.table ===
|
|
19172
|
-
|
|
19173
|
-
|
|
20268
|
+
const schema = state.getSchema(writable.schema);
|
|
20269
|
+
for (const seq of [...schema.sequences.values()]) {
|
|
20270
|
+
if (seq.ownedBy?.table === writable.name) {
|
|
20271
|
+
const wseq = state.ensureWritableSequence(seq);
|
|
20272
|
+
wseq.lastValue = wseq.startValue;
|
|
20273
|
+
wseq.isCalled = false;
|
|
19174
20274
|
}
|
|
19175
20275
|
}
|
|
19176
20276
|
}
|
|
@@ -19182,7 +20282,7 @@ function executeTruncate(env, stmt) {
|
|
|
19182
20282
|
function requireTargetTable(env, parts, verb) {
|
|
19183
20283
|
const state = env.ctx.state;
|
|
19184
20284
|
const table = state.findTable(parts);
|
|
19185
|
-
if (table) return table;
|
|
20285
|
+
if (table) return state.ensureWritableTable(table);
|
|
19186
20286
|
const view = state.findView(parts);
|
|
19187
20287
|
if (view) {
|
|
19188
20288
|
throw pgError("wrong_object_type", `cannot ${verb} view "${view.name}"`, "42809");
|
|
@@ -19350,7 +20450,8 @@ function resolveArbiters(env, table, clause) {
|
|
|
19350
20450
|
return matches2.map((spec) => ({ spec }));
|
|
19351
20451
|
}
|
|
19352
20452
|
function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, where) {
|
|
19353
|
-
const
|
|
20453
|
+
const rows = table.mutableRows();
|
|
20454
|
+
const existing = rows[existingIdx];
|
|
19354
20455
|
const cols = [
|
|
19355
20456
|
...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
|
|
19356
20457
|
...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: "excluded" }))
|
|
@@ -19360,7 +20461,8 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
19360
20461
|
const updated = existing.slice();
|
|
19361
20462
|
applyUpdateSets(env, table, sets, scope, updated);
|
|
19362
20463
|
computeGeneratedColumns(env, table, updated);
|
|
19363
|
-
|
|
20464
|
+
rows[existingIdx] = updated;
|
|
20465
|
+
indexUpdateRow(env, table, existingIdx, existing, updated);
|
|
19364
20466
|
checkNotNull(env, table, updated);
|
|
19365
20467
|
checkChecks(env, table, updated);
|
|
19366
20468
|
checkUnique(env, table, updated, existingIdx);
|
|
@@ -19371,6 +20473,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
19371
20473
|
function executeInsert(env0, stmt) {
|
|
19372
20474
|
const env = applyWith(env0, stmt.with);
|
|
19373
20475
|
const table = requireTargetTable(env, stmt.table, "insert into");
|
|
20476
|
+
const rows = table.mutableRows();
|
|
19374
20477
|
const label = stmt.alias ?? table.name;
|
|
19375
20478
|
const colIdxs = insertTargetColumns(env, table, stmt);
|
|
19376
20479
|
const sourceRows = insertSourceRows(env, stmt, colIdxs);
|
|
@@ -19432,22 +20535,18 @@ function executeInsert(env0, stmt) {
|
|
|
19432
20535
|
);
|
|
19433
20536
|
if (did) {
|
|
19434
20537
|
insertedCount++;
|
|
19435
|
-
insertedRows.push(
|
|
19436
|
-
fireRowTriggers(env, table, "after", "update", null,
|
|
20538
|
+
insertedRows.push(rows[conflictIdx]);
|
|
20539
|
+
fireRowTriggers(env, table, "after", "update", null, rows[conflictIdx]);
|
|
19437
20540
|
}
|
|
19438
20541
|
continue;
|
|
19439
20542
|
}
|
|
19440
20543
|
}
|
|
19441
20544
|
checkNotNull(env, table, newRow);
|
|
19442
20545
|
checkChecks(env, table, newRow);
|
|
19443
|
-
table
|
|
19444
|
-
|
|
19445
|
-
|
|
19446
|
-
|
|
19447
|
-
} catch (err) {
|
|
19448
|
-
table.rows.pop();
|
|
19449
|
-
throw err;
|
|
19450
|
-
}
|
|
20546
|
+
checkUnique(env, table, newRow, rows.length);
|
|
20547
|
+
checkForeignKeys(env, table, newRow);
|
|
20548
|
+
rows.push(newRow);
|
|
20549
|
+
indexInsertRow(env, table, rows.length - 1, newRow);
|
|
19451
20550
|
insertedCount++;
|
|
19452
20551
|
insertedRows.push(newRow);
|
|
19453
20552
|
fireRowTriggers(env, table, "after", "insert", null, newRow);
|
|
@@ -19526,11 +20625,12 @@ function executeUpdate(env0, stmt) {
|
|
|
19526
20625
|
const table = requireTargetTable(env, stmt.table, "update");
|
|
19527
20626
|
const label = stmt.alias ?? table.name;
|
|
19528
20627
|
const fromSource = stmt.from.length > 0 ? buildFrom(env, stmt.from) : null;
|
|
20628
|
+
const rows = table.mutableRows();
|
|
19529
20629
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
19530
20630
|
const updatedRows = [];
|
|
19531
20631
|
let updateCount = 0;
|
|
19532
|
-
for (let ri = 0; ri <
|
|
19533
|
-
const oldRow =
|
|
20632
|
+
for (let ri = 0; ri < rows.length; ri++) {
|
|
20633
|
+
const oldRow = rows[ri];
|
|
19534
20634
|
let matchScope = null;
|
|
19535
20635
|
if (fromSource) {
|
|
19536
20636
|
let found = false;
|
|
@@ -19555,16 +20655,17 @@ function executeUpdate(env0, stmt) {
|
|
|
19555
20655
|
if (fired.row === null) continue;
|
|
19556
20656
|
const finalRow = fired.row;
|
|
19557
20657
|
computeGeneratedColumns(env, table, finalRow);
|
|
19558
|
-
|
|
20658
|
+
rows[ri] = finalRow;
|
|
19559
20659
|
try {
|
|
19560
20660
|
checkNotNull(env, table, finalRow);
|
|
19561
20661
|
checkChecks(env, table, finalRow);
|
|
19562
20662
|
checkUnique(env, table, finalRow, ri);
|
|
19563
20663
|
checkForeignKeys(env, table, finalRow);
|
|
19564
20664
|
} catch (err) {
|
|
19565
|
-
|
|
20665
|
+
rows[ri] = oldRow;
|
|
19566
20666
|
throw err;
|
|
19567
20667
|
}
|
|
20668
|
+
indexUpdateRow(env, table, ri, oldRow, finalRow);
|
|
19568
20669
|
handleReferencedUpdate(env, table, [oldRow], [finalRow]);
|
|
19569
20670
|
updateCount++;
|
|
19570
20671
|
updatedRows.push(finalRow);
|
|
@@ -19581,11 +20682,12 @@ function executeDelete(env0, stmt) {
|
|
|
19581
20682
|
const env = applyWith(env0, stmt.with);
|
|
19582
20683
|
const table = requireTargetTable(env, stmt.table, "delete from");
|
|
19583
20684
|
const label = stmt.alias ?? table.name;
|
|
20685
|
+
const rows = table.mutableRows();
|
|
19584
20686
|
const usingSource = stmt.using.length > 0 ? buildFrom(env, stmt.using) : null;
|
|
19585
20687
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
19586
20688
|
const toDelete = [];
|
|
19587
|
-
for (let ri = 0; ri <
|
|
19588
|
-
const row =
|
|
20689
|
+
for (let ri = 0; ri < rows.length; ri++) {
|
|
20690
|
+
const row = rows[ri];
|
|
19589
20691
|
let matched;
|
|
19590
20692
|
if (usingSource) {
|
|
19591
20693
|
matched = usingSource.rel.rows.some((urow) => {
|
|
@@ -19602,20 +20704,21 @@ function executeDelete(env0, stmt) {
|
|
|
19602
20704
|
const deletedRows = [];
|
|
19603
20705
|
const skipped = /* @__PURE__ */ new Set();
|
|
19604
20706
|
for (const ri of toDelete) {
|
|
19605
|
-
const fired = fireRowTriggers(env, table, "before", "delete",
|
|
20707
|
+
const fired = fireRowTriggers(env, table, "before", "delete", rows[ri], null);
|
|
19606
20708
|
if (fired.row === null) skipped.add(ri);
|
|
19607
20709
|
}
|
|
19608
20710
|
for (let k = toDelete.length - 1; k >= 0; k--) {
|
|
19609
20711
|
const ri = toDelete[k];
|
|
19610
20712
|
if (skipped.has(ri)) continue;
|
|
19611
|
-
const row =
|
|
20713
|
+
const row = rows[ri];
|
|
19612
20714
|
handleReferencedDelete(env, table, row, 0);
|
|
19613
|
-
|
|
20715
|
+
rows.splice(ri, 1);
|
|
19614
20716
|
deletedRows.unshift(row);
|
|
19615
20717
|
}
|
|
19616
20718
|
for (const row of deletedRows) {
|
|
19617
20719
|
fireRowTriggers(env, table, "after", "delete", row, null);
|
|
19618
20720
|
}
|
|
20721
|
+
if (deletedRows.length > 0) rebuildTableIndexes(env, table);
|
|
19619
20722
|
env.ctx.state.changes = deletedRows.length;
|
|
19620
20723
|
if (stmt.returning) {
|
|
19621
20724
|
const res = evalReturning(env, table, label, stmt.returning, deletedRows, "DELETE");
|
|
@@ -19637,8 +20740,8 @@ function rowKeyFor(table, row, columns) {
|
|
|
19637
20740
|
function referencingRowIdxs(ref, key) {
|
|
19638
20741
|
const { table: rt, constraint: con } = ref;
|
|
19639
20742
|
const out = [];
|
|
19640
|
-
for (let i = 0; i < rt.
|
|
19641
|
-
const k = rowKeyFor(rt, rt.
|
|
20743
|
+
for (let i = 0; i < rt.rowCount(); i++) {
|
|
20744
|
+
const k = rowKeyFor(rt, rt.rowAt(i), con.columns);
|
|
19642
20745
|
if (k === key) out.push(i);
|
|
19643
20746
|
}
|
|
19644
20747
|
return out;
|
|
@@ -19654,38 +20757,40 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
19654
20757
|
const idxs = referencingRowIdxs(ref, key);
|
|
19655
20758
|
if (idxs.length === 0) continue;
|
|
19656
20759
|
const action = con.onDelete ?? "no_action";
|
|
20760
|
+
const child = env.ctx.state.ensureWritableTable(ref.table);
|
|
20761
|
+
const childRows = child.mutableRows();
|
|
19657
20762
|
switch (action) {
|
|
19658
20763
|
case "cascade": {
|
|
19659
20764
|
for (let k = idxs.length - 1; k >= 0; k--) {
|
|
19660
|
-
const childRow =
|
|
19661
|
-
handleReferencedDelete(env,
|
|
19662
|
-
|
|
20765
|
+
const childRow = childRows[idxs[k]];
|
|
20766
|
+
handleReferencedDelete(env, child, childRow, depth + 1);
|
|
20767
|
+
childRows.splice(idxs[k], 1);
|
|
19663
20768
|
}
|
|
19664
20769
|
break;
|
|
19665
20770
|
}
|
|
19666
20771
|
case "set_null": {
|
|
19667
20772
|
for (const i of idxs) {
|
|
19668
|
-
const newRow =
|
|
20773
|
+
const newRow = childRows[i].slice();
|
|
19669
20774
|
for (const c of con.columns) {
|
|
19670
|
-
newRow[
|
|
20775
|
+
newRow[child.columnIndex(c)] = null;
|
|
19671
20776
|
}
|
|
19672
|
-
|
|
19673
|
-
checkNotNull(env,
|
|
19674
|
-
checkChecks(env,
|
|
20777
|
+
childRows[i] = newRow;
|
|
20778
|
+
checkNotNull(env, child, newRow);
|
|
20779
|
+
checkChecks(env, child, newRow);
|
|
19675
20780
|
}
|
|
19676
20781
|
break;
|
|
19677
20782
|
}
|
|
19678
20783
|
case "set_default": {
|
|
19679
20784
|
for (const i of idxs) {
|
|
19680
|
-
const newRow =
|
|
20785
|
+
const newRow = childRows[i].slice();
|
|
19681
20786
|
for (const c of con.columns) {
|
|
19682
|
-
const ci =
|
|
19683
|
-
newRow[ci] = columnDefault(env,
|
|
20787
|
+
const ci = child.columnIndex(c);
|
|
20788
|
+
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
19684
20789
|
}
|
|
19685
|
-
|
|
19686
|
-
checkNotNull(env,
|
|
19687
|
-
checkChecks(env,
|
|
19688
|
-
checkForeignKeys(env,
|
|
20790
|
+
childRows[i] = newRow;
|
|
20791
|
+
checkNotNull(env, child, newRow);
|
|
20792
|
+
checkChecks(env, child, newRow);
|
|
20793
|
+
checkForeignKeys(env, child, newRow);
|
|
19689
20794
|
}
|
|
19690
20795
|
break;
|
|
19691
20796
|
}
|
|
@@ -19708,38 +20813,40 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
19708
20813
|
const idxs = referencingRowIdxs(ref, oldKey);
|
|
19709
20814
|
if (idxs.length === 0) continue;
|
|
19710
20815
|
const action = con.onUpdate ?? "no_action";
|
|
20816
|
+
const child = env.ctx.state.ensureWritableTable(ref.table);
|
|
20817
|
+
const childRows = child.mutableRows();
|
|
19711
20818
|
switch (action) {
|
|
19712
20819
|
case "cascade": {
|
|
19713
20820
|
for (const i of idxs) {
|
|
19714
|
-
const newRow =
|
|
20821
|
+
const newRow = childRows[i].slice();
|
|
19715
20822
|
for (let c = 0; c < con.columns.length; c++) {
|
|
19716
|
-
const localIdx =
|
|
20823
|
+
const localIdx = child.columnIndex(con.columns[c]);
|
|
19717
20824
|
const refIdx = table.columnIndex(con.refColumns[c]);
|
|
19718
20825
|
newRow[localIdx] = newRows[r][refIdx] ?? null;
|
|
19719
20826
|
}
|
|
19720
|
-
|
|
19721
|
-
checkChecks(env,
|
|
20827
|
+
childRows[i] = newRow;
|
|
20828
|
+
checkChecks(env, child, newRow);
|
|
19722
20829
|
}
|
|
19723
20830
|
break;
|
|
19724
20831
|
}
|
|
19725
20832
|
case "set_null": {
|
|
19726
20833
|
for (const i of idxs) {
|
|
19727
|
-
const newRow =
|
|
19728
|
-
for (const c of con.columns) newRow[
|
|
19729
|
-
|
|
19730
|
-
checkNotNull(env,
|
|
20834
|
+
const newRow = childRows[i].slice();
|
|
20835
|
+
for (const c of con.columns) newRow[child.columnIndex(c)] = null;
|
|
20836
|
+
childRows[i] = newRow;
|
|
20837
|
+
checkNotNull(env, child, newRow);
|
|
19731
20838
|
}
|
|
19732
20839
|
break;
|
|
19733
20840
|
}
|
|
19734
20841
|
case "set_default": {
|
|
19735
20842
|
for (const i of idxs) {
|
|
19736
|
-
const newRow =
|
|
20843
|
+
const newRow = childRows[i].slice();
|
|
19737
20844
|
for (const c of con.columns) {
|
|
19738
|
-
const ci =
|
|
19739
|
-
newRow[ci] = columnDefault(env,
|
|
20845
|
+
const ci = child.columnIndex(c);
|
|
20846
|
+
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
19740
20847
|
}
|
|
19741
|
-
|
|
19742
|
-
checkForeignKeys(env,
|
|
20848
|
+
childRows[i] = newRow;
|
|
20849
|
+
checkForeignKeys(env, child, newRow);
|
|
19743
20850
|
}
|
|
19744
20851
|
break;
|
|
19745
20852
|
}
|
|
@@ -19874,28 +20981,39 @@ var TransactionManager = class {
|
|
|
19874
20981
|
state;
|
|
19875
20982
|
base = null;
|
|
19876
20983
|
savepoints = [];
|
|
20984
|
+
freezeDepth = 0;
|
|
19877
20985
|
get inTransaction() {
|
|
19878
20986
|
return this.base !== null;
|
|
19879
20987
|
}
|
|
19880
20988
|
takeSnapshot() {
|
|
19881
|
-
|
|
20989
|
+
this.state.freezeShared();
|
|
20990
|
+
this.freezeDepth++;
|
|
20991
|
+
return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
|
|
19882
20992
|
}
|
|
19883
20993
|
restore(snap) {
|
|
19884
20994
|
this.state.restoreFrom(snap.state);
|
|
19885
20995
|
this.state.prng.setState(snap.prngState);
|
|
19886
20996
|
}
|
|
20997
|
+
thawOnce() {
|
|
20998
|
+
if (this.freezeDepth > 0) {
|
|
20999
|
+
this.state.thawShared();
|
|
21000
|
+
this.freezeDepth--;
|
|
21001
|
+
}
|
|
21002
|
+
}
|
|
19887
21003
|
begin() {
|
|
19888
21004
|
if (this.base !== null) {
|
|
19889
21005
|
return;
|
|
19890
21006
|
}
|
|
19891
21007
|
this.base = this.takeSnapshot();
|
|
19892
21008
|
this.state.inTransaction = true;
|
|
21009
|
+
assert(this.freezeDepth === 1, "BEGIN freeze depth");
|
|
19893
21010
|
}
|
|
19894
21011
|
commit() {
|
|
19895
21012
|
this.base = null;
|
|
19896
21013
|
this.savepoints = [];
|
|
19897
21014
|
this.state.inTransaction = false;
|
|
19898
21015
|
this.state.localSettings.clear();
|
|
21016
|
+
while (this.freezeDepth > 0) this.thawOnce();
|
|
19899
21017
|
}
|
|
19900
21018
|
rollback() {
|
|
19901
21019
|
if (this.base !== null) {
|
|
@@ -19905,6 +21023,7 @@ var TransactionManager = class {
|
|
|
19905
21023
|
this.savepoints = [];
|
|
19906
21024
|
this.state.inTransaction = false;
|
|
19907
21025
|
this.state.localSettings.clear();
|
|
21026
|
+
while (this.freezeDepth > 0) this.thawOnce();
|
|
19908
21027
|
}
|
|
19909
21028
|
savepoint(name) {
|
|
19910
21029
|
if (this.base === null) {
|
|
@@ -19931,11 +21050,11 @@ var TransactionManager = class {
|
|
|
19931
21050
|
}
|
|
19932
21051
|
throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
|
|
19933
21052
|
}
|
|
19934
|
-
/** abort any open transaction without restoring (used by Database.close) */
|
|
19935
21053
|
reset() {
|
|
19936
21054
|
this.base = null;
|
|
19937
21055
|
this.savepoints = [];
|
|
19938
21056
|
this.state.inTransaction = false;
|
|
21057
|
+
this.freezeDepth = 0;
|
|
19939
21058
|
}
|
|
19940
21059
|
};
|
|
19941
21060
|
|
|
@@ -20146,7 +21265,7 @@ function executeCopy(env, stmt) {
|
|
|
20146
21265
|
return i;
|
|
20147
21266
|
}) : table.columns.map((_, i) => i);
|
|
20148
21267
|
columns = idxs.map((i) => ({ name: table.columns[i].name, type: table.columns[i].type.id }));
|
|
20149
|
-
rows = table.
|
|
21268
|
+
rows = Array.from({ length: table.rowCount() }, (_, i) => idxs.map((j) => table.rowAt(i)[j] ?? null));
|
|
20150
21269
|
}
|
|
20151
21270
|
const delim = format === "csv" ? "," : " ";
|
|
20152
21271
|
const nullStr = String(stmt.options.null ?? (format === "csv" ? "" : "\\N"));
|
|
@@ -20242,7 +21361,7 @@ function executeStatement(env, stmt) {
|
|
|
20242
21361
|
case "no_op":
|
|
20243
21362
|
return commandResult(stmt.what.toUpperCase(), 0);
|
|
20244
21363
|
case "do":
|
|
20245
|
-
|
|
21364
|
+
return commandResult("DO", 0);
|
|
20246
21365
|
default: {
|
|
20247
21366
|
const t = stmt;
|
|
20248
21367
|
throw pgError("internal", `unhandled statement type ${t.type}`);
|