@crvouga/postgres-mem 1.0.0 → 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/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
- return makeNumeric(a.coef * b.coef, a.dscale + b.dscale);
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 new Error("negative");
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 = new TextEncoder().encode(a);
1309
- const eb = new TextEncoder().encode(b);
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
- const na = numericStripTrailingZeros(a.v);
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 (v === 0) v = 0;
2481
- return Number(t) === 0 && t.startsWith("-") ? -0 : v;
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", Object.is(r, -0) ? 0 : r);
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.rows.length, hasIndex, true, t.temp);
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/executor/window.ts
15079
- function compareKeys(ctx, a, b) {
15080
- for (let i = 0; i < a.values.length; i++) {
15081
- const av = a.values[i];
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 computeOneWindow(ctx, call, spec, rowCount, evalAt) {
15117
- const partitions = /* @__PURE__ */ new Map();
15118
- const partitionOf = [];
15119
- for (let i = 0; i < rowCount; i++) {
15120
- const keyParts = [];
15121
- for (const p of spec.partitionBy) {
15122
- const v = evalAt(i, p);
15123
- keyParts.push(v.v === null ? "\0N" : datumKey(v.t === UNKNOWN ? "text" : v.t, v.v));
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 computePartition(ctx, call, spec, rowIdxs, evalAt, results) {
15138
- const keys = /* @__PURE__ */ new Map();
15139
- if (spec.orderBy.length > 0) {
15140
- for (const i of rowIdxs) {
15141
- keys.set(i, sortKeyFor(ctx, spec.orderBy, i, evalAt));
15142
- }
15143
- rowIdxs = [...rowIdxs].sort((a, b) => compareKeys(ctx, keys.get(a), keys.get(b)));
15144
- }
15145
- const n = rowIdxs.length;
15146
- const peerGroup = new Array(n);
15147
- let group = 0;
15148
- for (let pos = 0; pos < n; pos++) {
15149
- if (pos > 0) {
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
- peerGroup[pos] = group;
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 = resolveUserFunction(env, name, args.length);
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 resolveUserFunction(env, name, argCount) {
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 (argCount >= required && argCount <= fn.argTypes.length) return fn;
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 combineJoin(env, kind, left, right, on, using, usingAlias, rangeVars) {
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
- for (let k = 0; k < using.length; k++) {
16073
- const lv = lrow[usingLeftIdx[k]] ?? null;
16074
- const rv = rrow[usingRightIdx[k]] ?? null;
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
- for (const lrow of left.rows) {
16093
- let matched = false;
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 rrow = right.rows[ri];
16096
- if (matchRows(lrow, rrow)) {
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
- if (!matched && (kind === "left" || kind === "full")) {
16103
- emit(lrow, null);
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 cols = [...accRel.columns, ...src.rel.columns];
16156
- const rows = [];
16157
- for (const lrow of accRel.rows) {
16158
- for (const rrow of src.rel.rows) {
16159
- rows.push([...lrow, ...rrow]);
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
- const where = core.where;
16709
- srcRows = srcRows.filter((row) => evalPredicate(env, rowScope(row), where));
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
- try {
16982
- const probeScope = new RowScope(
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/triggers.ts
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(`trigger body: expected "${kw.toUpperCase()}" near "${this.peek().value}"`);
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(`trigger body: expected ";" near "${t.value}"`);
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
- parseBody() {
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 stmts = this.parseStmts();
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
- if (this.peek().type !== "eof") throw unsupported(`trigger body: trailing content near "${this.peek().value}"`);
17123
- return stmts;
17890
+ return { kind: "block", body, handler };
17124
17891
  }
17125
- parseStmts() {
17892
+ parseStmts(stop) {
17126
17893
  const out = [];
17127
- while (!this.atKw("end") && !this.atKw("elsif") && !this.atKw("else") && this.peek().type !== "eof") {
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
- const t = this.next();
17136
- let what;
17137
- if (t.type === "ident" && (t.value === "new" || t.value === "old" || t.value === "null")) {
17138
- what = t.value;
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.expectSemi();
17143
- return { kind: "return", what };
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(`trigger body statement near "${first.value}"`);
18008
+ throw unsupported(`plpgsql statement near "${first.value}"`);
17185
18009
  }
17186
- target.push(first.type === "ident" ? first.value : first.value);
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(`trigger body: expected assignment near "${opTok.value}"`);
18018
+ throw unsupported(`plpgsql: expected assignment near "${opTok.value}"`);
17195
18019
  }
17196
- const expr = this.parseExprUntilSemi();
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("trigger body: unterminated statement");
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(`trigger body: expected "${kw.toUpperCase()}"`);
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 === kw && depth === 0) {
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(`trigger body expression "${text}"`);
18082
+ if (!target) throw unsupported(`plpgsql expression "${text}"`);
17239
18083
  return target.expr;
17240
18084
  }
17241
- var bodyCache = /* @__PURE__ */ new Map();
17242
- function compiledBody(fn) {
17243
- const raw = fn.rawBody;
17244
- if (raw === null) throw unsupported(`trigger function ${fn.name} has no body`);
17245
- let cached = bodyCache.get(raw);
17246
- if (cached === void 0) {
17247
- cached = new PlParser(raw).parseBody();
17248
- bodyCache.set(raw, cached);
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 cached;
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 "return":
17280
- throw new ReturnSignal(stmt.what === "new" ? vars.newRow : stmt.what === "old" ? vars.oldRow : null);
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 stmts = compiledBody(fn);
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
- t.rows = this.rows.map((r) => r.slice());
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 for transaction snapshots (datums are immutable; rows copied) */
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
- table.rows = rel2.rows.map((r) => r.slice());
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.rows.length : 0;
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.rows.length; i++) {
18284
- checkUnique(env, table, table.rows[i], i);
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 view = env.ctx.state.findView(stmt.name);
18332
- if (!view?.materialized) {
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 e = env.ctx.state.findEnum(stmt.name);
18363
- if (!e) {
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 table = state.findTable(stmt.table);
18483
- if (!table) {
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 table = state.findTable(stmt.table);
18513
- if (!table) {
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 < table.rows.length; i++) {
18536
- const row = table.rows[i];
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 table.rows) {
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 table.rows) {
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 table.rows) row.splice(idx, 1);
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 < table.rows.length; i++) {
18619
- const row = table.rows[i];
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
- table.rows[i] = next;
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 table.rows) {
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 table.rows) {
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.rows.length; i++) {
18910
- const row = table.rows[i];
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.rows.length >= 0) {
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
- t.rows = [];
20265
+ const writable = state.ensureWritableTable(t);
20266
+ writable.mutableRows().length = 0;
19168
20267
  if (stmt.restartIdentity) {
19169
- const schema = state.getSchema(t.schema);
19170
- for (const seq of schema.sequences.values()) {
19171
- if (seq.ownedBy?.table === t.name) {
19172
- seq.lastValue = seq.startValue;
19173
- seq.isCalled = false;
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 existing = table.rows[existingIdx];
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
- table.rows[existingIdx] = updated;
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(table.rows[conflictIdx]);
19436
- fireRowTriggers(env, table, "after", "update", null, table.rows[conflictIdx]);
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.rows.push(newRow);
19444
- try {
19445
- checkUnique(env, table, newRow, table.rows.length - 1);
19446
- checkForeignKeys(env, table, newRow);
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 < table.rows.length; ri++) {
19533
- const oldRow = table.rows[ri];
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
- table.rows[ri] = finalRow;
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
- table.rows[ri] = oldRow;
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 < table.rows.length; ri++) {
19588
- const row = table.rows[ri];
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", table.rows[ri], null);
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 = table.rows[ri];
20713
+ const row = rows[ri];
19612
20714
  handleReferencedDelete(env, table, row, 0);
19613
- table.rows.splice(ri, 1);
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.rows.length; i++) {
19641
- const k = rowKeyFor(rt, rt.rows[i], con.columns);
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 = ref.table.rows[idxs[k]];
19661
- handleReferencedDelete(env, ref.table, childRow, depth + 1);
19662
- ref.table.rows.splice(idxs[k], 1);
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 = ref.table.rows[i].slice();
20773
+ const newRow = childRows[i].slice();
19669
20774
  for (const c of con.columns) {
19670
- newRow[ref.table.columnIndex(c)] = null;
20775
+ newRow[child.columnIndex(c)] = null;
19671
20776
  }
19672
- ref.table.rows[i] = newRow;
19673
- checkNotNull(env, ref.table, newRow);
19674
- checkChecks(env, ref.table, newRow);
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 = ref.table.rows[i].slice();
20785
+ const newRow = childRows[i].slice();
19681
20786
  for (const c of con.columns) {
19682
- const ci = ref.table.columnIndex(c);
19683
- newRow[ci] = columnDefault(env, ref.table, ref.table.columns[ci]);
20787
+ const ci = child.columnIndex(c);
20788
+ newRow[ci] = columnDefault(env, child, child.columns[ci]);
19684
20789
  }
19685
- ref.table.rows[i] = newRow;
19686
- checkNotNull(env, ref.table, newRow);
19687
- checkChecks(env, ref.table, newRow);
19688
- checkForeignKeys(env, ref.table, newRow);
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 = ref.table.rows[i].slice();
20821
+ const newRow = childRows[i].slice();
19715
20822
  for (let c = 0; c < con.columns.length; c++) {
19716
- const localIdx = ref.table.columnIndex(con.columns[c]);
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
- ref.table.rows[i] = newRow;
19721
- checkChecks(env, ref.table, newRow);
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 = ref.table.rows[i].slice();
19728
- for (const c of con.columns) newRow[ref.table.columnIndex(c)] = null;
19729
- ref.table.rows[i] = newRow;
19730
- checkNotNull(env, ref.table, newRow);
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 = ref.table.rows[i].slice();
20843
+ const newRow = childRows[i].slice();
19737
20844
  for (const c of con.columns) {
19738
- const ci = ref.table.columnIndex(c);
19739
- newRow[ci] = columnDefault(env, ref.table, ref.table.columns[ci]);
20845
+ const ci = child.columnIndex(c);
20846
+ newRow[ci] = columnDefault(env, child, child.columns[ci]);
19740
20847
  }
19741
- ref.table.rows[i] = newRow;
19742
- checkForeignKeys(env, ref.table, newRow);
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
- return { state: this.state.clone(), prngState: this.state.prng.getState() };
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.rows.map((r) => idxs.map((i) => r[i] ?? null));
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
- throw unsupported(`DO blocks (language ${stmt.language})`);
21364
+ return commandResult("DO", 0);
20246
21365
  default: {
20247
21366
  const t = stmt;
20248
21367
  throw pgError("internal", `unhandled statement type ${t.type}`);