@crvouga/mockingbird-service-postgres 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/wire/cli.js CHANGED
@@ -3702,7 +3702,7 @@ async function preflightFleet(config, options2) {
3702
3702
  break;
3703
3703
  }
3704
3704
  }
3705
- targets.set(name2, { ...target, packageVersion: packageVersion ?? "3.5.0" });
3705
+ targets.set(name2, { ...target, packageVersion: packageVersion ?? "3.6.0" });
3706
3706
  } catch (error) {
3707
3707
  throw new Error(`service ${name2}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
3708
3708
  }
@@ -5539,6 +5539,13 @@ function isPostgresError(err) {
5539
5539
  return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
5540
5540
  }
5541
5541
 
5542
+ // src/storage/guc.ts
5543
+ function validateStorageSetting(name2, value) {
5544
+ if (name2 === "default_table_access_method" && value !== "heap" || name2 === "default_tablespace" && value !== "" && value !== "pg_default") {
5545
+ throw pgError("invalid_parameter_value", `invalid value for parameter "${name2}": "${value}"`, "22023");
5546
+ }
5547
+ }
5548
+
5542
5549
  // src/runtime/prng.ts
5543
5550
  var Prng = class _Prng {
5544
5551
  state;
@@ -8994,6 +9001,9 @@ var DatabaseState = class _DatabaseState {
8994
9001
  changes = 0;
8995
9002
  oidCounter = 16384;
8996
9003
  inTransaction = false;
9004
+ /** Transaction-only events: included in rollback clones, never in exported snapshots. */
9005
+ pendingTriggers = [];
9006
+ constraintModes = /* @__PURE__ */ new Map();
8997
9007
  /** most recent sequence touched by nextval/setval, for lastval() */
8998
9008
  lastSequence = null;
8999
9009
  /** Session currval per sequence; absent entry means currval is undefined. */
@@ -9036,6 +9046,8 @@ var DatabaseState = class _DatabaseState {
9036
9046
  this.settings.set("row_security", "on");
9037
9047
  this.settings.set("array_nulls", "on");
9038
9048
  this.settings.set("backslash_quote", "safe_encoding");
9049
+ this.settings.set("default_tablespace", "");
9050
+ this.settings.set("default_table_access_method", "heap");
9039
9051
  }
9040
9052
  nextOid() {
9041
9053
  return this.oidCounter++;
@@ -9195,6 +9207,8 @@ var DatabaseState = class _DatabaseState {
9195
9207
  s.prepared = new Map(this.prepared);
9196
9208
  s.changes = this.changes;
9197
9209
  s.inTransaction = this.inTransaction;
9210
+ s.pendingTriggers = this.pendingTriggers.slice();
9211
+ s.constraintModes = new Map(this.constraintModes);
9198
9212
  s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
9199
9213
  s.sequenceCurrval = new Map(this.sequenceCurrval);
9200
9214
  s.tableStats = new Map(this.tableStats);
@@ -9212,6 +9226,8 @@ var DatabaseState = class _DatabaseState {
9212
9226
  s.prepared = new Map(this.prepared);
9213
9227
  s.changes = this.changes;
9214
9228
  s.inTransaction = this.inTransaction;
9229
+ s.pendingTriggers = this.pendingTriggers.slice();
9230
+ s.constraintModes = new Map(this.constraintModes);
9215
9231
  s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
9216
9232
  s.sequenceCurrval = new Map(this.sequenceCurrval);
9217
9233
  s.tableStats = new Map(this.tableStats);
@@ -9284,6 +9300,8 @@ var DatabaseState = class _DatabaseState {
9284
9300
  this.settings = other.settings;
9285
9301
  this.localSettings = other.localSettings;
9286
9302
  this.prepared = other.prepared;
9303
+ this.pendingTriggers = other.pendingTriggers.slice();
9304
+ this.constraintModes = new Map(other.constraintModes);
9287
9305
  this.changes = other.changes;
9288
9306
  this.lastSequence = other.lastSequence;
9289
9307
  this.sequenceCurrval = other.sequenceCurrval;
@@ -9300,90 +9318,192 @@ var DatabaseState = class _DatabaseState {
9300
9318
  }
9301
9319
  };
9302
9320
 
9303
- // src/transactions/manager.ts
9304
- var TransactionManager = class {
9305
- constructor(state) {
9306
- this.state = state;
9321
+ // src/types/timezone.ts
9322
+ var intlCache = /* @__PURE__ */ new Map();
9323
+ function intlFor(zone) {
9324
+ const cached = intlCache.get(zone);
9325
+ if (cached) return cached;
9326
+ try {
9327
+ const fmt = new Intl.DateTimeFormat("en-US", {
9328
+ timeZone: zone,
9329
+ timeZoneName: "longOffset",
9330
+ year: "numeric"
9331
+ });
9332
+ intlCache.set(zone, fmt);
9333
+ return fmt;
9334
+ } catch {
9335
+ return null;
9336
+ }
9337
+ }
9338
+ function parseFixedOffset(zone) {
9339
+ const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
9340
+ if (!m) return null;
9341
+ const sign = m[1] === "-" ? -1 : 1;
9342
+ return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
9343
+ }
9344
+ function parsePosixOffset(zone) {
9345
+ const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
9346
+ if (!m) return null;
9347
+ const sign = m[1] === "-" ? 1 : -1;
9348
+ return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
9349
+ }
9350
+ function pgMicrosToUnixMs(utcMicros) {
9351
+ return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
9352
+ }
9353
+ function zoneOffsetAtUtc(zone, utcMicros) {
9354
+ const z = zone.trim();
9355
+ const lower = z.toLowerCase();
9356
+ if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
9357
+ const posix = parsePosixOffset(z);
9358
+ if (posix !== null) return posix;
9359
+ const fixed = parseFixedOffset(z);
9360
+ if (fixed !== null) return fixed;
9361
+ const fmt = intlFor(z);
9362
+ if (fmt) {
9363
+ const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
9364
+ const parts = fmt.formatToParts(new Date(ms));
9365
+ const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
9366
+ const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
9367
+ if (!m) return 0;
9368
+ const sign = m[1] === "-" ? -1 : 1;
9369
+ return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
9307
9370
  }
9371
+ throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
9372
+ }
9373
+ function clampDateMs(ms) {
9374
+ const MAX = 864e13;
9375
+ return Math.max(-MAX, Math.min(MAX, ms));
9376
+ }
9377
+ function zoneOffsetForNaive(zone, naiveMicros) {
9378
+ const first = zoneOffsetAtUtc(zone, naiveMicros);
9379
+ const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
9380
+ return second;
9381
+ }
9382
+ function validateZone(zone) {
9383
+ zoneOffsetAtUtc(zone, 0n);
9384
+ }
9385
+
9386
+ // src/expressions/context.ts
9387
+ var EngineCtx = class {
9308
9388
  state;
9309
- base = null;
9310
- savepoints = [];
9311
- freezeDepth = 0;
9312
- get inTransaction() {
9313
- return this.base !== null;
9389
+ /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
9390
+ txNow;
9391
+ /** statement timestamp */
9392
+ stmtNow;
9393
+ constructor(state, txNow) {
9394
+ this.state = state;
9395
+ const nowMs = state.clock().getTime();
9396
+ const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
9397
+ this.txNow = txNow ?? nowMicros;
9398
+ this.stmtNow = nowMicros;
9314
9399
  }
9315
- takeSnapshot() {
9316
- this.state.freezeShared();
9317
- this.freezeDepth++;
9318
- return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
9400
+ timezone() {
9401
+ return this.state.getSetting("timezone") ?? "UTC";
9319
9402
  }
9320
- restore(snap) {
9321
- this.state.restoreFrom(snap.state);
9322
- this.state.prng.setState(snap.prngState);
9403
+ namespaceOid(name2) {
9404
+ const oid = builtinNamespaceOid(name2) ?? this.state.schemas.get(name2)?.oid;
9405
+ if (oid === void 0) throw pgError("undefined_object", `schema "${name2}" does not exist`, "3F000");
9406
+ return oid;
9323
9407
  }
9324
- thawOnce() {
9325
- if (this.freezeDepth > 0) {
9326
- this.state.thawShared();
9327
- this.freezeDepth--;
9408
+ namespaceName(oid) {
9409
+ const builtin = builtinNamespaceName(oid);
9410
+ if (builtin !== void 0) return builtin;
9411
+ for (const schema of this.state.schemas.values()) {
9412
+ if (schema.oid === oid) return schema.name;
9328
9413
  }
9414
+ return null;
9329
9415
  }
9330
- begin() {
9331
- if (this.base !== null) {
9332
- return;
9333
- }
9334
- this.base = this.takeSnapshot();
9335
- this.state.inTransaction = true;
9336
- assert(this.freezeDepth === 1, "BEGIN freeze depth");
9416
+ zoneOffsetAt(utcMicros) {
9417
+ return zoneOffsetAtUtc(this.timezone(), utcMicros);
9337
9418
  }
9338
- commit() {
9339
- this.base = null;
9340
- this.savepoints = [];
9341
- this.state.inTransaction = false;
9342
- this.state.localSettings.clear();
9343
- while (this.freezeDepth > 0) this.thawOnce();
9419
+ zoneOffsetForNaive(naiveMicros) {
9420
+ return zoneOffsetForNaive(this.timezone(), naiveMicros);
9344
9421
  }
9345
- rollback() {
9346
- if (this.base !== null) {
9347
- this.restore(this.base);
9348
- }
9349
- this.base = null;
9350
- this.savepoints = [];
9351
- this.state.inTransaction = false;
9352
- this.state.localSettings.clear();
9353
- while (this.freezeDepth > 0) this.thawOnce();
9422
+ enumLabels(enumType) {
9423
+ if (!isEnumType(enumType)) return null;
9424
+ const e = this.state.findEnumByKey(enumType.slice(5));
9425
+ return e?.labels ?? null;
9354
9426
  }
9355
- savepoint(name2) {
9356
- if (this.base === null) {
9357
- throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
9358
- }
9359
- this.savepoints.push({ name: name2, snap: this.takeSnapshot() });
9427
+ enumHasLabel(enumType, label) {
9428
+ const labels = this.enumLabels(enumType);
9429
+ return labels === null ? true : labels.includes(label);
9360
9430
  }
9361
- releaseSavepoint(name2) {
9362
- const idx = this.findSavepoint(name2);
9363
- this.savepoints.splice(idx);
9431
+ enumOrder(enumType, label) {
9432
+ const labels = this.enumLabels(enumType);
9433
+ if (!labels) return 0;
9434
+ const idx = labels.indexOf(label);
9435
+ return idx === -1 ? labels.length : idx;
9364
9436
  }
9365
- rollbackToSavepoint(name2) {
9366
- const idx = this.findSavepoint(name2);
9367
- const sp = this.savepoints[idx];
9368
- this.restore(sp.snap);
9369
- this.savepoints.splice(idx + 1);
9437
+ requireEnum(enumType) {
9438
+ const labels = this.enumLabels(enumType);
9439
+ if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
9440
+ return labels;
9370
9441
  }
9371
- findSavepoint(name2) {
9372
- if (this.base === null) {
9373
- throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
9374
- }
9375
- for (let i = this.savepoints.length - 1; i >= 0; i--) {
9376
- if (this.savepoints[i].name === name2) return i;
9442
+ };
9443
+
9444
+ // src/api/bind.ts
9445
+ var INT4_MIN2 = -2147483648;
9446
+ var INT4_MAX2 = 2147483647;
9447
+ function bindValueToTyped(value, index) {
9448
+ if (value === null || value === void 0) return tv(UNKNOWN, null);
9449
+ switch (typeof value) {
9450
+ case "boolean":
9451
+ return tv("bool", value);
9452
+ case "number":
9453
+ if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
9454
+ return tv("int4", value);
9455
+ }
9456
+ if (Number.isInteger(value) && Number.isSafeInteger(value)) {
9457
+ return tv("int8", BigInt(value));
9458
+ }
9459
+ return tv("float8", value);
9460
+ case "bigint":
9461
+ if (value < -9223372036854775808n || value > 9223372036854775807n) {
9462
+ throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
9463
+ }
9464
+ return tv("int8", value);
9465
+ case "string":
9466
+ return tv(UNKNOWN, value);
9467
+ case "object": {
9468
+ if (value instanceof Uint8Array) return tv("bytea", value);
9469
+ if (value instanceof Date) {
9470
+ const ms = value.getTime();
9471
+ if (Number.isNaN(ms)) {
9472
+ throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
9473
+ }
9474
+ return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
9475
+ }
9476
+ break;
9377
9477
  }
9378
- throw pgError("invalid_savepoint_specification", `savepoint "${name2}" does not exist`, "3B001");
9478
+ default:
9479
+ break;
9379
9480
  }
9380
- reset() {
9381
- this.base = null;
9382
- this.savepoints = [];
9383
- this.state.inTransaction = false;
9384
- this.freezeDepth = 0;
9481
+ throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
9482
+ }
9483
+ function datumToJs(t, v, ctx, int8 = "bigint") {
9484
+ if (v === null) return null;
9485
+ switch (t) {
9486
+ case "bool":
9487
+ return v;
9488
+ case "int2":
9489
+ case "int4":
9490
+ case "oid":
9491
+ return typeof v === "bigint" ? Number(v) : v;
9492
+ case "int8": {
9493
+ const n = typeof v === "bigint" ? v : BigInt(v);
9494
+ if (int8 === "string") return n.toString();
9495
+ if (int8 === "number") return Number(n);
9496
+ return n;
9497
+ }
9498
+ case "float4":
9499
+ case "float8":
9500
+ return v;
9501
+ case "bytea":
9502
+ return v;
9503
+ default:
9504
+ return datumText(t === UNKNOWN ? "text" : t, v, ctx);
9385
9505
  }
9386
- };
9506
+ }
9387
9507
 
9388
9508
  // src/types/cast.ts
9389
9509
  var UTC_CAST_ENV = {
@@ -9861,259 +9981,6 @@ function unifyTypes(a, b) {
9861
9981
  return null;
9862
9982
  }
9863
9983
 
9864
- // src/types/resolve.ts
9865
- var SERIALS = {
9866
- smallserial: "int2",
9867
- serial2: "int2",
9868
- serial: "int4",
9869
- serial4: "int4",
9870
- bigserial: "int8",
9871
- serial8: "int8"
9872
- };
9873
- function modOf(t, mods) {
9874
- if (mods.length === 0) return null;
9875
- if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
9876
- return { a: mods[0] };
9877
- }
9878
- function resolveTypeName(state, tn) {
9879
- const joined = tn.parts.join(".");
9880
- const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
9881
- if (bare !== null) {
9882
- const serial = SERIALS[bare];
9883
- if (serial !== void 0 && tn.arrayDims === 0) {
9884
- return { column: { id: serial, mod: null }, domain: null, serial };
9885
- }
9886
- const builtin = normalizeTypeName(bare);
9887
- if (builtin !== null) {
9888
- const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
9889
- return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
9890
- }
9891
- }
9892
- const enumData = state.findEnum(tn.parts);
9893
- if (enumData) {
9894
- const elem = `enum:${enumData.schema}.${enumData.name}`;
9895
- const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
9896
- return { column: { id, mod: null }, domain: null, serial: null };
9897
- }
9898
- const domainData = state.findDomain(tn.parts);
9899
- if (domainData) {
9900
- if (tn.arrayDims > 0) {
9901
- const id = arrayTypeOf(domainData.baseType.id);
9902
- return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
9903
- }
9904
- return {
9905
- column: domainData.baseType,
9906
- domain: `${domainData.schema}.${domainData.name}`,
9907
- serial: null
9908
- };
9909
- }
9910
- throw pgError("undefined_object", `type "${joined}" does not exist`);
9911
- }
9912
-
9913
- // src/executor/relation.ts
9914
- var RowScope = class {
9915
- constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
9916
- this.columns = columns;
9917
- this.row = row;
9918
- this.parent = parent;
9919
- this.rangeVars = rangeVars;
9920
- }
9921
- columns;
9922
- row;
9923
- parent;
9924
- rangeVars;
9925
- /** Resolve a (possibly qualified) column reference. undefined = not found. */
9926
- lookup(parts) {
9927
- if (parts.length === 1) {
9928
- const name3 = parts[0];
9929
- let found2;
9930
- let count2 = 0;
9931
- for (let i = 0; i < this.columns.length; i++) {
9932
- const c = this.columns[i];
9933
- if (c.name === name3 && !c.hidden) {
9934
- count2++;
9935
- if (count2 > 1) {
9936
- throw pgError("ambiguous_column", `column reference "${name3}" is ambiguous`);
9937
- }
9938
- found2 = tv(c.type, this.row[i] ?? null);
9939
- }
9940
- }
9941
- if (found2 !== void 0) return found2;
9942
- if (this.rangeVars.has(name3)) {
9943
- return this.wholeRow(name3);
9944
- }
9945
- return this.parent?.lookup(parts);
9946
- }
9947
- const table = parts[parts.length - 2];
9948
- const name2 = parts[parts.length - 1];
9949
- let found;
9950
- let count = 0;
9951
- for (let i = 0; i < this.columns.length; i++) {
9952
- const c = this.columns[i];
9953
- if (c.table === table && c.name === name2) {
9954
- count++;
9955
- if (count > 1) {
9956
- throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
9957
- }
9958
- found = tv(c.type, this.row[i] ?? null);
9959
- }
9960
- }
9961
- if (found !== void 0) return found;
9962
- return this.parent?.lookup(parts);
9963
- }
9964
- /** Build a record value from all columns belonging to a range variable. */
9965
- wholeRow(rangeVar) {
9966
- const types = [];
9967
- const values = [];
9968
- const names = [];
9969
- for (let i = 0; i < this.columns.length; i++) {
9970
- const c = this.columns[i];
9971
- if (c.table === rangeVar) {
9972
- types.push(c.type);
9973
- values.push(this.row[i] ?? null);
9974
- names.push(c.name);
9975
- }
9976
- }
9977
- if (types.length === 0) return void 0;
9978
- return tv("record", { kind: "pgrecord", types, values, names });
9979
- }
9980
- hasRangeVar(name2) {
9981
- if (this.rangeVars.has(name2)) return true;
9982
- return this.parent?.hasRangeVar(name2) ?? false;
9983
- }
9984
- };
9985
- function childEnv(env, outer) {
9986
- return {
9987
- ctx: env.ctx,
9988
- params: env.params,
9989
- ctes: env.ctes,
9990
- outer,
9991
- allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
9992
- };
9993
- }
9994
- function relationResult(rel3, command2) {
9995
- const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
9996
- return {
9997
- columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
9998
- rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
9999
- command: command2,
10000
- rowCount: rel3.rows.length
10001
- };
10002
- }
10003
- function commandResult(command2, rowCount) {
10004
- return { columns: [], rows: [], command: command2, rowCount };
10005
- }
10006
- function inferColumnName(e) {
10007
- switch (e.type) {
10008
- case "colref":
10009
- return e.parts[e.parts.length - 1];
10010
- case "func":
10011
- return e.name[e.name.length - 1];
10012
- case "cast": {
10013
- const inner = inferColumnName(e.expr);
10014
- if (inner !== "?column?") return inner;
10015
- return e.target.parts[e.target.parts.length - 1];
10016
- }
10017
- case "collate":
10018
- return inferColumnName(e.expr);
10019
- case "case":
10020
- return "case";
10021
- case "array_ctor":
10022
- case "array_query":
10023
- return "array";
10024
- case "row":
10025
- return "row";
10026
- case "subquery_expr":
10027
- return e.kind === "exists" ? "exists" : "?column?";
10028
- case "bool_lit":
10029
- return "bool";
10030
- case "extract":
10031
- return "extract";
10032
- case "position":
10033
- return "position";
10034
- case "substring_sql":
10035
- return "substring";
10036
- case "overlay":
10037
- return "overlay";
10038
- case "trim":
10039
- return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
10040
- case "field_select":
10041
- return e.field === "*" ? "?column?" : e.field;
10042
- case "at_time_zone":
10043
- return "timezone";
10044
- case "grouping_func":
10045
- return "grouping";
10046
- case "subscript":
10047
- return inferColumnName(e.base);
10048
- default:
10049
- return "?column?";
10050
- }
10051
- }
10052
-
10053
- // src/api/bind.ts
10054
- var INT4_MIN2 = -2147483648;
10055
- var INT4_MAX2 = 2147483647;
10056
- function bindValueToTyped(value, index) {
10057
- if (value === null || value === void 0) return tv(UNKNOWN, null);
10058
- switch (typeof value) {
10059
- case "boolean":
10060
- return tv("bool", value);
10061
- case "number":
10062
- if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
10063
- return tv("int4", value);
10064
- }
10065
- if (Number.isInteger(value) && Number.isSafeInteger(value)) {
10066
- return tv("int8", BigInt(value));
10067
- }
10068
- return tv("float8", value);
10069
- case "bigint":
10070
- if (value < -9223372036854775808n || value > 9223372036854775807n) {
10071
- throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
10072
- }
10073
- return tv("int8", value);
10074
- case "string":
10075
- return tv(UNKNOWN, value);
10076
- case "object": {
10077
- if (value instanceof Uint8Array) return tv("bytea", value);
10078
- if (value instanceof Date) {
10079
- const ms = value.getTime();
10080
- if (Number.isNaN(ms)) {
10081
- throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
10082
- }
10083
- return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
10084
- }
10085
- break;
10086
- }
10087
- default:
10088
- break;
10089
- }
10090
- throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
10091
- }
10092
- function datumToJs(t, v, ctx, int8 = "bigint") {
10093
- if (v === null) return null;
10094
- switch (t) {
10095
- case "bool":
10096
- return v;
10097
- case "int2":
10098
- case "int4":
10099
- case "oid":
10100
- return typeof v === "bigint" ? Number(v) : v;
10101
- case "int8": {
10102
- const n = typeof v === "bigint" ? v : BigInt(v);
10103
- if (int8 === "string") return n.toString();
10104
- if (int8 === "number") return Number(n);
10105
- return n;
10106
- }
10107
- case "float4":
10108
- case "float8":
10109
- return v;
10110
- case "bytea":
10111
- return v;
10112
- default:
10113
- return datumText(t === UNKNOWN ? "text" : t, v, ctx);
10114
- }
10115
- }
10116
-
10117
9984
  // src/functions/datetime-fns.ts
10118
9985
  function extractDatePart(ctx, field0, src0, asNumeric) {
10119
9986
  const field = field0.toLowerCase();
@@ -11028,71 +10895,6 @@ function getArrayFunctions() {
11028
10895
  return m;
11029
10896
  }
11030
10897
 
11031
- // src/types/timezone.ts
11032
- var intlCache = /* @__PURE__ */ new Map();
11033
- function intlFor(zone) {
11034
- const cached = intlCache.get(zone);
11035
- if (cached) return cached;
11036
- try {
11037
- const fmt = new Intl.DateTimeFormat("en-US", {
11038
- timeZone: zone,
11039
- timeZoneName: "longOffset",
11040
- year: "numeric"
11041
- });
11042
- intlCache.set(zone, fmt);
11043
- return fmt;
11044
- } catch {
11045
- return null;
11046
- }
11047
- }
11048
- function parseFixedOffset(zone) {
11049
- const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
11050
- if (!m) return null;
11051
- const sign = m[1] === "-" ? -1 : 1;
11052
- return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
11053
- }
11054
- function parsePosixOffset(zone) {
11055
- const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
11056
- if (!m) return null;
11057
- const sign = m[1] === "-" ? 1 : -1;
11058
- return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
11059
- }
11060
- function pgMicrosToUnixMs(utcMicros) {
11061
- return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
11062
- }
11063
- function zoneOffsetAtUtc(zone, utcMicros) {
11064
- const z = zone.trim();
11065
- const lower = z.toLowerCase();
11066
- if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
11067
- const posix = parsePosixOffset(z);
11068
- if (posix !== null) return posix;
11069
- const fixed = parseFixedOffset(z);
11070
- if (fixed !== null) return fixed;
11071
- const fmt = intlFor(z);
11072
- if (fmt) {
11073
- const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
11074
- const parts = fmt.formatToParts(new Date(ms));
11075
- const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
11076
- const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
11077
- if (!m) return 0;
11078
- const sign = m[1] === "-" ? -1 : 1;
11079
- return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
11080
- }
11081
- throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
11082
- }
11083
- function clampDateMs(ms) {
11084
- const MAX = 864e13;
11085
- return Math.max(-MAX, Math.min(MAX, ms));
11086
- }
11087
- function zoneOffsetForNaive(zone, naiveMicros) {
11088
- const first = zoneOffsetAtUtc(zone, naiveMicros);
11089
- const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
11090
- return second;
11091
- }
11092
- function validateZone(zone) {
11093
- zoneOffsetAtUtc(zone, 0n);
11094
- }
11095
-
11096
10898
  // src/functions/datetime-registry.ts
11097
10899
  function getDatetimeFunctions() {
11098
10900
  const m = /* @__PURE__ */ new Map();
@@ -12692,6 +12494,28 @@ function exactDegTan(x) {
12692
12494
  return Math.tan(x * Math.PI / 180);
12693
12495
  }
12694
12496
 
12497
+ // src/storage/indexable-relation.ts
12498
+ function materializedTable(view) {
12499
+ const table = new TableData(
12500
+ view.schema,
12501
+ view.name,
12502
+ (view.matColumns ?? []).map((c) => ({
12503
+ name: c.name,
12504
+ type: { id: c.type, mod: null },
12505
+ notNull: false,
12506
+ defaultExpr: null,
12507
+ identity: null,
12508
+ generated: null,
12509
+ collate: null,
12510
+ domain: null
12511
+ })),
12512
+ view.oid,
12513
+ view.temp
12514
+ );
12515
+ table.rows = view.matRows ?? [];
12516
+ return table;
12517
+ }
12518
+
12695
12519
  // src/schema/index-catalog.ts
12696
12520
  var isKeyConstraint = (constraint) => constraint.kind === "primary_key" || constraint.kind === "unique";
12697
12521
  var ACCESS_METHODS = /* @__PURE__ */ new Map([
@@ -12720,6 +12544,8 @@ function syncIndexCatalog(state) {
12720
12544
  const tables = /* @__PURE__ */ new Map();
12721
12545
  for (const schema of state.schemas.values()) {
12722
12546
  for (const table of schema.tables.values()) tables.set(table.oid, { schema, table });
12547
+ for (const view of schema.views.values())
12548
+ if (view.materialized) tables.set(view.oid, { schema, table: materializedTable(view) });
12723
12549
  }
12724
12550
  const backing = /* @__PURE__ */ new Map();
12725
12551
  for (const { table } of tables.values()) {
@@ -12917,6 +12743,55 @@ function deparseExpr(e) {
12917
12743
  }
12918
12744
  }
12919
12745
 
12746
+ // src/types/resolve.ts
12747
+ var SERIALS = {
12748
+ smallserial: "int2",
12749
+ serial2: "int2",
12750
+ serial: "int4",
12751
+ serial4: "int4",
12752
+ bigserial: "int8",
12753
+ serial8: "int8"
12754
+ };
12755
+ function modOf(t, mods) {
12756
+ if (mods.length === 0) return null;
12757
+ if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
12758
+ return { a: mods[0] };
12759
+ }
12760
+ function resolveTypeName(state, tn) {
12761
+ const joined = tn.parts.join(".");
12762
+ const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
12763
+ if (bare !== null) {
12764
+ const serial = SERIALS[bare];
12765
+ if (serial !== void 0 && tn.arrayDims === 0) {
12766
+ return { column: { id: serial, mod: null }, domain: null, serial };
12767
+ }
12768
+ const builtin = normalizeTypeName(bare);
12769
+ if (builtin !== null) {
12770
+ const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
12771
+ return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
12772
+ }
12773
+ }
12774
+ const enumData = state.findEnum(tn.parts);
12775
+ if (enumData) {
12776
+ const elem = `enum:${enumData.schema}.${enumData.name}`;
12777
+ const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
12778
+ return { column: { id, mod: null }, domain: null, serial: null };
12779
+ }
12780
+ const domainData = state.findDomain(tn.parts);
12781
+ if (domainData) {
12782
+ if (tn.arrayDims > 0) {
12783
+ const id = arrayTypeOf(domainData.baseType.id);
12784
+ return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
12785
+ }
12786
+ return {
12787
+ column: domainData.baseType,
12788
+ domain: `${domainData.schema}.${domainData.name}`,
12789
+ serial: null
12790
+ };
12791
+ }
12792
+ throw pgError("undefined_object", `type "${joined}" does not exist`);
12793
+ }
12794
+
12920
12795
  // src/sql/ruleutils.ts
12921
12796
  var BOOL = "bool";
12922
12797
  var TEXT = "text";
@@ -13714,7 +13589,11 @@ function getMiscFunctions() {
13714
13589
  }
13715
13590
  const name2 = argText(ctx, args2[0]).toLowerCase();
13716
13591
  const value = args2[1].v === null ? "" : argText(ctx, args2[1]);
13717
- ctx.state.settings.set(name2, value);
13592
+ if (!ctx.state.settings.has(name2) && !name2.includes("."))
13593
+ throw pgError("undefined_object", `unrecognized configuration parameter "${name2}"`, "42704");
13594
+ validateStorageSetting(name2, value);
13595
+ const local = args2[2]?.v === true;
13596
+ if (!local || ctx.state.inTransaction) (local ? ctx.state.localSettings : ctx.state.settings).set(name2, value);
13718
13597
  return tv("text", value);
13719
13598
  });
13720
13599
  m.set(
@@ -13780,10 +13659,7 @@ function getMiscFunctions() {
13780
13659
  if (!col)
13781
13660
  throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
13782
13661
  if (!col.identity) return tv("text", null);
13783
- return tv(
13784
- "text",
13785
- col.identity.sequence.split(".").map(quoteIdent).join(".")
13786
- );
13662
+ return tv("text", col.identity.sequence.split(".").map(quoteIdent).join("."));
13787
13663
  })
13788
13664
  );
13789
13665
  m.set("format_type", (ctx, args2) => {
@@ -18707,6 +18583,146 @@ function isSrfName(name2) {
18707
18583
  return SRFS.has(name2);
18708
18584
  }
18709
18585
 
18586
+ // src/executor/relation.ts
18587
+ var RowScope = class {
18588
+ constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
18589
+ this.columns = columns;
18590
+ this.row = row;
18591
+ this.parent = parent;
18592
+ this.rangeVars = rangeVars;
18593
+ }
18594
+ columns;
18595
+ row;
18596
+ parent;
18597
+ rangeVars;
18598
+ /** Resolve a (possibly qualified) column reference. undefined = not found. */
18599
+ lookup(parts) {
18600
+ if (parts.length === 1) {
18601
+ const name3 = parts[0];
18602
+ let found2;
18603
+ let count2 = 0;
18604
+ for (let i = 0; i < this.columns.length; i++) {
18605
+ const c = this.columns[i];
18606
+ if (c.name === name3 && !c.hidden) {
18607
+ count2++;
18608
+ if (count2 > 1) {
18609
+ throw pgError("ambiguous_column", `column reference "${name3}" is ambiguous`);
18610
+ }
18611
+ found2 = tv(c.type, this.row[i] ?? null);
18612
+ }
18613
+ }
18614
+ if (found2 !== void 0) return found2;
18615
+ if (this.rangeVars.has(name3)) {
18616
+ return this.wholeRow(name3);
18617
+ }
18618
+ return this.parent?.lookup(parts);
18619
+ }
18620
+ const table = parts[parts.length - 2];
18621
+ const name2 = parts[parts.length - 1];
18622
+ let found;
18623
+ let count = 0;
18624
+ for (let i = 0; i < this.columns.length; i++) {
18625
+ const c = this.columns[i];
18626
+ if (c.table === table && c.name === name2) {
18627
+ count++;
18628
+ if (count > 1) {
18629
+ throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
18630
+ }
18631
+ found = tv(c.type, this.row[i] ?? null);
18632
+ }
18633
+ }
18634
+ if (found !== void 0) return found;
18635
+ return this.parent?.lookup(parts);
18636
+ }
18637
+ /** Build a record value from all columns belonging to a range variable. */
18638
+ wholeRow(rangeVar) {
18639
+ const types = [];
18640
+ const values = [];
18641
+ const names = [];
18642
+ for (let i = 0; i < this.columns.length; i++) {
18643
+ const c = this.columns[i];
18644
+ if (c.table === rangeVar) {
18645
+ types.push(c.type);
18646
+ values.push(this.row[i] ?? null);
18647
+ names.push(c.name);
18648
+ }
18649
+ }
18650
+ if (types.length === 0) return void 0;
18651
+ return tv("record", { kind: "pgrecord", types, values, names });
18652
+ }
18653
+ hasRangeVar(name2) {
18654
+ if (this.rangeVars.has(name2)) return true;
18655
+ return this.parent?.hasRangeVar(name2) ?? false;
18656
+ }
18657
+ };
18658
+ function childEnv(env, outer) {
18659
+ return {
18660
+ ctx: env.ctx,
18661
+ params: env.params,
18662
+ ctes: env.ctes,
18663
+ outer,
18664
+ allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
18665
+ };
18666
+ }
18667
+ function relationResult(rel3, command2) {
18668
+ const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
18669
+ return {
18670
+ columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
18671
+ rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
18672
+ command: command2,
18673
+ rowCount: rel3.rows.length
18674
+ };
18675
+ }
18676
+ function commandResult(command2, rowCount) {
18677
+ return { columns: [], rows: [], command: command2, rowCount };
18678
+ }
18679
+ function inferColumnName(e) {
18680
+ switch (e.type) {
18681
+ case "colref":
18682
+ return e.parts[e.parts.length - 1];
18683
+ case "func":
18684
+ return e.name[e.name.length - 1];
18685
+ case "cast": {
18686
+ const inner = inferColumnName(e.expr);
18687
+ if (inner !== "?column?") return inner;
18688
+ return e.target.parts[e.target.parts.length - 1];
18689
+ }
18690
+ case "collate":
18691
+ return inferColumnName(e.expr);
18692
+ case "case":
18693
+ return "case";
18694
+ case "array_ctor":
18695
+ case "array_query":
18696
+ return "array";
18697
+ case "row":
18698
+ return "row";
18699
+ case "subquery_expr":
18700
+ return e.kind === "exists" ? "exists" : "?column?";
18701
+ case "bool_lit":
18702
+ return "bool";
18703
+ case "extract":
18704
+ return "extract";
18705
+ case "position":
18706
+ return "position";
18707
+ case "substring_sql":
18708
+ return "substring";
18709
+ case "overlay":
18710
+ return "overlay";
18711
+ case "trim":
18712
+ return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
18713
+ case "field_select":
18714
+ return e.field === "*" ? "?column?" : e.field;
18715
+ case "at_time_zone":
18716
+ return "timezone";
18717
+ case "grouping_func":
18718
+ return "grouping";
18719
+ case "subscript":
18720
+ return inferColumnName(e.base);
18721
+ default:
18722
+ return "?column?";
18723
+ }
18724
+ }
18725
+
18710
18726
  // src/indexes/index.ts
18711
18727
  var IndexStore = class _IndexStore {
18712
18728
  name;
@@ -18769,53 +18785,228 @@ function ensureIndexStores(env, table) {
18769
18785
  const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
18770
18786
  if (key !== null) store.insert(key, i);
18771
18787
  }
18772
- stores.set(spec.name, store);
18773
- }
18774
- const schema = env.ctx.state.schemas.get(table.schema);
18775
- if (schema) {
18776
- for (const idx of schema.indexes.values()) {
18777
- if (idx.table !== table.name || idx.unique) continue;
18778
- stores.set(idx.name, new IndexStore(idx.name, false));
18788
+ stores.set(spec.name, store);
18789
+ }
18790
+ const schema = env.ctx.state.schemas.get(table.schema);
18791
+ if (schema) {
18792
+ for (const idx of schema.indexes.values()) {
18793
+ if (idx.table !== table.name || idx.unique) continue;
18794
+ stores.set(idx.name, new IndexStore(idx.name, false));
18795
+ }
18796
+ }
18797
+ table.indexStores = stores;
18798
+ return stores;
18799
+ }
18800
+ function indexStoreFor(env, table, spec) {
18801
+ const stores = ensureIndexStores(env, table);
18802
+ let store = stores.get(spec.name);
18803
+ if (!store) {
18804
+ store = new IndexStore(spec.name, true);
18805
+ for (let i = 0; i < table.rowCount(); i++) {
18806
+ const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
18807
+ if (key !== null) store.insert(key, i);
18808
+ }
18809
+ stores.set(spec.name, store);
18810
+ }
18811
+ return store;
18812
+ }
18813
+ function indexInsertRow(env, table, rowIdx, row) {
18814
+ for (const spec of uniqueSpecsFor(env, table)) {
18815
+ const key = uniqueKeyOf(env, table, spec, row);
18816
+ if (key === null) continue;
18817
+ indexStoreFor(env, table, spec).insert(key, rowIdx);
18818
+ }
18819
+ }
18820
+ function indexRemoveRow(env, table, rowIdx, row) {
18821
+ if (!table.indexStores) return;
18822
+ for (const spec of uniqueSpecsFor(env, table)) {
18823
+ const key = uniqueKeyOf(env, table, spec, row);
18824
+ if (key === null) continue;
18825
+ table.indexStores.get(spec.name)?.remove(key, rowIdx);
18826
+ }
18827
+ }
18828
+ function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
18829
+ indexRemoveRow(env, table, rowIdx, oldRow);
18830
+ indexInsertRow(env, table, rowIdx, newRow);
18831
+ }
18832
+ function rebuildTableIndexes(env, table) {
18833
+ table.indexStores = null;
18834
+ ensureIndexStores(env, table);
18835
+ }
18836
+
18837
+ // src/constraints/enforce.ts
18838
+ function tableScope(table, row) {
18839
+ const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
18840
+ return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
18841
+ }
18842
+ function checkNotNull(env, table, row) {
18843
+ void env;
18844
+ for (let i = 0; i < table.columns.length; i++) {
18845
+ const c = table.columns[i];
18846
+ if (c.notNull && (row[i] ?? null) === null) {
18847
+ throw pgError(
18848
+ "not_null_violation",
18849
+ `null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
18850
+ "23502"
18851
+ );
18852
+ }
18853
+ }
18854
+ }
18855
+ function checkChecks(env, table, row) {
18856
+ for (const con of table.constraints) {
18857
+ if (con.kind !== "check") continue;
18858
+ const scope = makeEvalScope(env, tableScope(table, row));
18859
+ const v = evalExpr(env.ctx, scope, con.expr);
18860
+ if (v.v === null) continue;
18861
+ const b = castTo(env.ctx, v, "bool", {});
18862
+ if (b.v !== true) {
18863
+ throw pgError(
18864
+ "check_violation",
18865
+ `new row for relation "${table.name}" violates check constraint "${con.name}"`,
18866
+ "23514"
18867
+ );
18868
+ }
18869
+ }
18870
+ }
18871
+ function uniqueSpecsFor(env, table) {
18872
+ const specs = [];
18873
+ for (const con of table.constraints) {
18874
+ if (con.kind !== "primary_key" && con.kind !== "unique") continue;
18875
+ specs.push({
18876
+ name: con.name,
18877
+ keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
18878
+ columnNames: con.columns,
18879
+ nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
18880
+ where: null,
18881
+ isPrimary: con.kind === "primary_key"
18882
+ });
18883
+ }
18884
+ const schema = env.ctx.state.schemas.get(table.schema);
18885
+ if (schema) {
18886
+ for (const idx of schema.indexes.values()) {
18887
+ if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
18888
+ specs.push(uniqueSpecOfIndex(table, idx));
18889
+ }
18890
+ }
18891
+ return specs;
18892
+ }
18893
+ function uniqueSpecOfIndex(table, idx) {
18894
+ return {
18895
+ name: idx.name,
18896
+ keys: idx.columns.map((c) => ({
18897
+ colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
18898
+ expr: c.expr
18899
+ })),
18900
+ columnNames: idx.columns.map((c) => c.column ?? "expr"),
18901
+ nullsNotDistinct: idx.nullsNotDistinct,
18902
+ where: idx.where,
18903
+ isPrimary: false
18904
+ };
18905
+ }
18906
+ function uniqueKeyOf(env, table, spec, row) {
18907
+ if (spec.where) {
18908
+ const scope = makeEvalScope(env, tableScope(table, row));
18909
+ const v = evalExpr(env.ctx, scope, spec.where);
18910
+ if (v.v !== true) return null;
18911
+ }
18912
+ const parts = [];
18913
+ let hasNull = false;
18914
+ for (const k of spec.keys) {
18915
+ let value;
18916
+ let type;
18917
+ if (k.expr) {
18918
+ const scope = makeEvalScope(env, tableScope(table, row));
18919
+ const v = evalExpr(env.ctx, scope, k.expr);
18920
+ value = v.v;
18921
+ type = v.t === "unknown" ? "text" : v.t;
18922
+ } else {
18923
+ value = row[k.colIdx] ?? null;
18924
+ type = table.columns[k.colIdx].type.id;
18925
+ }
18926
+ if (value === null) {
18927
+ hasNull = true;
18928
+ parts.push("\0N");
18929
+ } else {
18930
+ parts.push(datumKey(type, value));
18931
+ }
18932
+ }
18933
+ if (hasNull && !spec.nullsNotDistinct) return null;
18934
+ return parts.join("");
18935
+ }
18936
+ function checkUnique(env, table, row, selfIdx) {
18937
+ for (const spec of uniqueSpecsFor(env, table)) {
18938
+ const key = uniqueKeyOf(env, table, spec, row);
18939
+ if (key === null) continue;
18940
+ indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
18941
+ }
18942
+ }
18943
+ function findConflict(env, table, spec, row) {
18944
+ const key = uniqueKeyOf(env, table, spec, row);
18945
+ if (key === null) return null;
18946
+ const hits = indexStoreFor(env, table, spec).lookup(key);
18947
+ return hits.length > 0 ? hits[0] : null;
18948
+ }
18949
+ function checkForeignKeys(env, table, row) {
18950
+ const state = env.ctx.state;
18951
+ for (const con of table.constraints) {
18952
+ if (con.kind !== "foreign_key") continue;
18953
+ const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
18954
+ const nulls = values.filter((v) => v === null).length;
18955
+ if (con.match === "simple" && nulls > 0) continue;
18956
+ if (con.match === "full") {
18957
+ if (nulls === values.length) continue;
18958
+ if (nulls > 0) {
18959
+ throw pgError(
18960
+ "constraint_foreign_key",
18961
+ `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
18962
+ "23503"
18963
+ );
18964
+ }
18965
+ }
18966
+ const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
18967
+ if (!refTable) {
18968
+ throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
18969
+ }
18970
+ const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
18971
+ const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
18972
+ const wanted = values.map((v, i) => {
18973
+ const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
18974
+ const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
18975
+ return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
18976
+ }).join("");
18977
+ let found = false;
18978
+ for (let i = 0; i < refTable.rowCount(); i++) {
18979
+ const r = refTable.rowAt(i);
18980
+ const key = refIdxs.map((ri, j) => {
18981
+ const v = r[ri] ?? null;
18982
+ return v === null ? "\0N" : datumKey(keyTypes[j], v);
18983
+ }).join("");
18984
+ if (key === wanted) {
18985
+ found = true;
18986
+ break;
18987
+ }
18988
+ }
18989
+ if (!found) {
18990
+ throw pgError(
18991
+ "constraint_foreign_key",
18992
+ `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
18993
+ "23503"
18994
+ );
18779
18995
  }
18780
18996
  }
18781
- table.indexStores = stores;
18782
- return stores;
18783
18997
  }
18784
- function indexStoreFor(env, table, spec) {
18785
- const stores = ensureIndexStores(env, table);
18786
- let store = stores.get(spec.name);
18787
- if (!store) {
18788
- store = new IndexStore(spec.name, true);
18789
- for (let i = 0; i < table.rowCount(); i++) {
18790
- const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
18791
- if (key !== null) store.insert(key, i);
18998
+ function referencingConstraints(env, table) {
18999
+ const out = [];
19000
+ for (const schema of env.ctx.state.schemas.values()) {
19001
+ for (const t of schema.tables.values()) {
19002
+ for (const con of t.constraints) {
19003
+ if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
19004
+ out.push({ table: t, constraint: con });
19005
+ }
19006
+ }
18792
19007
  }
18793
- stores.set(spec.name, store);
18794
- }
18795
- return store;
18796
- }
18797
- function indexInsertRow(env, table, rowIdx, row) {
18798
- for (const spec of uniqueSpecsFor(env, table)) {
18799
- const key = uniqueKeyOf(env, table, spec, row);
18800
- if (key === null) continue;
18801
- indexStoreFor(env, table, spec).insert(key, rowIdx);
18802
- }
18803
- }
18804
- function indexRemoveRow(env, table, rowIdx, row) {
18805
- if (!table.indexStores) return;
18806
- for (const spec of uniqueSpecsFor(env, table)) {
18807
- const key = uniqueKeyOf(env, table, spec, row);
18808
- if (key === null) continue;
18809
- table.indexStores.get(spec.name)?.remove(key, rowIdx);
18810
19008
  }
18811
- }
18812
- function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
18813
- indexRemoveRow(env, table, rowIdx, oldRow);
18814
- indexInsertRow(env, table, rowIdx, newRow);
18815
- }
18816
- function rebuildTableIndexes(env, table) {
18817
- table.indexStores = null;
18818
- ensureIndexStores(env, table);
19009
+ return out;
18819
19010
  }
18820
19011
 
18821
19012
  // src/planner/access.ts
@@ -20701,10 +20892,9 @@ var Parser = class {
20701
20892
  return this.parseCreateFunction(orReplace);
20702
20893
  }
20703
20894
  if (this.atKw("trigger") || this.atKw("constraint") && this.atKw("trigger", 1)) {
20704
- if (this.eatKw("constraint")) {
20705
- }
20895
+ const constraint = this.eatKw("constraint");
20706
20896
  this.expectKw("trigger");
20707
- return this.parseCreateTrigger(orReplace);
20897
+ return this.parseCreateTrigger(orReplace, constraint);
20708
20898
  }
20709
20899
  if (this.atKw("extension")) {
20710
20900
  this.pos++;
@@ -21525,7 +21715,7 @@ var Parser = class {
21525
21715
  strict: strict2
21526
21716
  };
21527
21717
  }
21528
- parseCreateTrigger(orReplace) {
21718
+ parseCreateTrigger(orReplace, constraint) {
21529
21719
  const name2 = this.ident();
21530
21720
  let timing;
21531
21721
  if (this.eatKw("before")) timing = "before";
@@ -21555,9 +21745,18 @@ var Parser = class {
21555
21745
  this.expectKw("on");
21556
21746
  const table = this.qualifiedName();
21557
21747
  if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
21558
- while (this.atKw("not") || this.atKw("deferrable") || this.atKw("initially")) {
21559
- this.pos++;
21560
- }
21748
+ let deferrable = false;
21749
+ let initiallyDeferred = false;
21750
+ if (this.eatKw("not")) this.expectKw("deferrable");
21751
+ else if (this.eatKw("deferrable")) deferrable = true;
21752
+ if (this.eatKw("initially")) {
21753
+ if (this.eatKw("deferred")) initiallyDeferred = true;
21754
+ else this.expectKw("immediate");
21755
+ }
21756
+ if ((deferrable || initiallyDeferred) && !constraint)
21757
+ this.errorAt(this.peek(), "only constraint triggers can be deferred");
21758
+ if (initiallyDeferred && !deferrable)
21759
+ this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
21561
21760
  let forEachRow = false;
21562
21761
  if (this.eatKw("for")) {
21563
21762
  this.eatKw("each");
@@ -21582,7 +21781,21 @@ var Parser = class {
21582
21781
  } while (this.eatPunct(","));
21583
21782
  }
21584
21783
  this.expectPunct(")");
21585
- return { type: "create_trigger", orReplace, name: name2, timing, events, table, forEachRow, when, funcName, funcArgs };
21784
+ return {
21785
+ type: "create_trigger",
21786
+ constraint,
21787
+ deferrable,
21788
+ initiallyDeferred,
21789
+ orReplace,
21790
+ name: name2,
21791
+ timing,
21792
+ events,
21793
+ table,
21794
+ forEachRow,
21795
+ when,
21796
+ funcName,
21797
+ funcArgs
21798
+ };
21586
21799
  }
21587
21800
  // --- ALTER --------------------------------------------------------------------
21588
21801
  parseAlter() {
@@ -21927,9 +22140,15 @@ var Parser = class {
21927
22140
  this.skipToStatementEnd();
21928
22141
  return { type: "no_op", what: "SET TRANSACTION" };
21929
22142
  }
21930
- if (this.atKw("constraints")) {
21931
- this.skipToStatementEnd();
21932
- return { type: "no_op", what: "SET CONSTRAINTS" };
22143
+ if (this.eatKw("constraints")) {
22144
+ const names = this.eatKw("all") ? null : [];
22145
+ if (names)
22146
+ do {
22147
+ names.push(this.qualifiedName());
22148
+ } while (this.eatPunct(","));
22149
+ const deferred = this.eatKw("deferred");
22150
+ if (!deferred) this.expectKw("immediate");
22151
+ return { type: "set_constraints", names, deferred };
21933
22152
  }
21934
22153
  if (this.atKw("role") || this.atKw("session", 0) && this.atKw("authorization", 1)) {
21935
22154
  this.skipToStatementEnd();
@@ -24304,12 +24523,12 @@ function executeRecursiveCte(env, cte, query) {
24304
24523
  table: null
24305
24524
  }));
24306
24525
  const seen = /* @__PURE__ */ new Set();
24307
- const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
24526
+ const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
24308
24527
  let result = [];
24309
24528
  let working = [];
24310
24529
  for (const row of base.rows) {
24311
24530
  if (!body.all) {
24312
- const k = keyOf(row);
24531
+ const k = keyOf2(row);
24313
24532
  if (seen.has(k)) continue;
24314
24533
  seen.add(k);
24315
24534
  }
@@ -24342,7 +24561,7 @@ function executeRecursiveCte(env, cte, query) {
24342
24561
  return castTo(env.ctx, tv(srcT === UNKNOWN ? "text" : srcT, v), columns[i].type, {}).v;
24343
24562
  });
24344
24563
  if (!body.all) {
24345
- const k = keyOf(row);
24564
+ const k = keyOf2(row);
24346
24565
  if (seen.has(k)) continue;
24347
24566
  seen.add(k);
24348
24567
  }
@@ -25522,7 +25741,7 @@ function setOpRelation(env, op) {
25522
25741
  );
25523
25742
  const lrows = normalize2(left);
25524
25743
  const rrows = normalize2(right);
25525
- const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
25744
+ const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
25526
25745
  let rows;
25527
25746
  switch (op.op) {
25528
25747
  case "union": {
@@ -25530,7 +25749,7 @@ function setOpRelation(env, op) {
25530
25749
  if (!op.all) {
25531
25750
  const seen = /* @__PURE__ */ new Set();
25532
25751
  rows = rows.filter((r) => {
25533
- const k = keyOf(r);
25752
+ const k = keyOf2(r);
25534
25753
  if (seen.has(k)) return false;
25535
25754
  seen.add(k);
25536
25755
  return true;
@@ -25541,13 +25760,13 @@ function setOpRelation(env, op) {
25541
25760
  case "intersect": {
25542
25761
  const rightCounts = /* @__PURE__ */ new Map();
25543
25762
  for (const r of rrows) {
25544
- const k = keyOf(r);
25763
+ const k = keyOf2(r);
25545
25764
  rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
25546
25765
  }
25547
25766
  rows = [];
25548
25767
  const emitted = /* @__PURE__ */ new Set();
25549
25768
  for (const r of lrows) {
25550
- const k = keyOf(r);
25769
+ const k = keyOf2(r);
25551
25770
  const avail = rightCounts.get(k) ?? 0;
25552
25771
  if (avail <= 0) continue;
25553
25772
  if (op.all) {
@@ -25563,13 +25782,13 @@ function setOpRelation(env, op) {
25563
25782
  case "except": {
25564
25783
  const rightCounts = /* @__PURE__ */ new Map();
25565
25784
  for (const r of rrows) {
25566
- const k = keyOf(r);
25785
+ const k = keyOf2(r);
25567
25786
  rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
25568
25787
  }
25569
25788
  rows = [];
25570
25789
  const emitted = /* @__PURE__ */ new Set();
25571
25790
  for (const r of lrows) {
25572
- const k = keyOf(r);
25791
+ const k = keyOf2(r);
25573
25792
  if (op.all) {
25574
25793
  const avail = rightCounts.get(k) ?? 0;
25575
25794
  if (avail > 0) {
@@ -26023,201 +26242,48 @@ function executeCore(env0, core, orderBy, opts) {
26023
26242
  }
26024
26243
  rel3 = stripHidden(rel3);
26025
26244
  return rel3;
26026
- }
26027
- function executeSelectStmt(env0, stmt) {
26028
- const env = applyWith(env0, stmt.with);
26029
- let rel3;
26030
- if (stmt.body.type === "select_core") {
26031
- rel3 = executeCore(env, stmt.body, stmt.orderBy, { hasLimit: stmt.limit !== null });
26032
- } else {
26033
- rel3 = executeBody(env, stmt.body);
26034
- const specs = outputOrderSpecs(env, rel3, stmt.orderBy);
26035
- if (specs.length > 0) sortRows(env, rel3, specs);
26036
- rel3 = stripHidden(rel3);
26037
- }
26038
- if (stmt.limitWithTies) {
26039
- if (stmt.orderBy.length === 0) {
26040
- throw pgError("syntax", "WITH TIES cannot be specified without ORDER BY clause", "42601");
26041
- }
26042
- const tieSpecs = outputOrderSpecs(env, rel3, stmt.orderBy);
26043
- return stripHidden(applyLimitOffset(env, rel3, stmt.limit, stmt.offset, tieSpecs));
26044
- }
26045
- return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
26046
- }
26047
-
26048
- // src/constraints/enforce.ts
26049
- function tableScope(table, row) {
26050
- const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
26051
- return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
26052
- }
26053
- function checkNotNull(env, table, row) {
26054
- void env;
26055
- for (let i = 0; i < table.columns.length; i++) {
26056
- const c = table.columns[i];
26057
- if (c.notNull && (row[i] ?? null) === null) {
26058
- throw pgError(
26059
- "not_null_violation",
26060
- `null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
26061
- "23502"
26062
- );
26063
- }
26064
- }
26065
- }
26066
- function checkChecks(env, table, row) {
26067
- for (const con of table.constraints) {
26068
- if (con.kind !== "check") continue;
26069
- const scope = makeEvalScope(env, tableScope(table, row));
26070
- const v = evalExpr(env.ctx, scope, con.expr);
26071
- if (v.v === null) continue;
26072
- const b = castTo(env.ctx, v, "bool", {});
26073
- if (b.v !== true) {
26074
- throw pgError(
26075
- "check_violation",
26076
- `new row for relation "${table.name}" violates check constraint "${con.name}"`,
26077
- "23514"
26078
- );
26079
- }
26080
- }
26081
- }
26082
- function uniqueSpecsFor(env, table) {
26083
- const specs = [];
26084
- for (const con of table.constraints) {
26085
- if (con.kind !== "primary_key" && con.kind !== "unique") continue;
26086
- specs.push({
26087
- name: con.name,
26088
- keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
26089
- columnNames: con.columns,
26090
- nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
26091
- where: null,
26092
- isPrimary: con.kind === "primary_key"
26093
- });
26094
- }
26095
- const schema = env.ctx.state.schemas.get(table.schema);
26096
- if (schema) {
26097
- for (const idx of schema.indexes.values()) {
26098
- if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
26099
- specs.push(uniqueSpecOfIndex(table, idx));
26100
- }
26101
- }
26102
- return specs;
26103
- }
26104
- function uniqueSpecOfIndex(table, idx) {
26105
- return {
26106
- name: idx.name,
26107
- keys: idx.columns.map((c) => ({
26108
- colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
26109
- expr: c.expr
26110
- })),
26111
- columnNames: idx.columns.map((c) => c.column ?? "expr"),
26112
- nullsNotDistinct: idx.nullsNotDistinct,
26113
- where: idx.where,
26114
- isPrimary: false
26115
- };
26116
- }
26117
- function uniqueKeyOf(env, table, spec, row) {
26118
- if (spec.where) {
26119
- const scope = makeEvalScope(env, tableScope(table, row));
26120
- const v = evalExpr(env.ctx, scope, spec.where);
26121
- if (v.v !== true) return null;
26122
- }
26123
- const parts = [];
26124
- let hasNull = false;
26125
- for (const k of spec.keys) {
26126
- let value;
26127
- let type;
26128
- if (k.expr) {
26129
- const scope = makeEvalScope(env, tableScope(table, row));
26130
- const v = evalExpr(env.ctx, scope, k.expr);
26131
- value = v.v;
26132
- type = v.t === "unknown" ? "text" : v.t;
26133
- } else {
26134
- value = row[k.colIdx] ?? null;
26135
- type = table.columns[k.colIdx].type.id;
26136
- }
26137
- if (value === null) {
26138
- hasNull = true;
26139
- parts.push("\0N");
26140
- } else {
26141
- parts.push(datumKey(type, value));
26142
- }
26143
- }
26144
- if (hasNull && !spec.nullsNotDistinct) return null;
26145
- return parts.join("");
26146
- }
26147
- function checkUnique(env, table, row, selfIdx) {
26148
- for (const spec of uniqueSpecsFor(env, table)) {
26149
- const key = uniqueKeyOf(env, table, spec, row);
26150
- if (key === null) continue;
26151
- indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
26152
- }
26153
- }
26154
- function findConflict(env, table, spec, row) {
26155
- const key = uniqueKeyOf(env, table, spec, row);
26156
- if (key === null) return null;
26157
- const hits = indexStoreFor(env, table, spec).lookup(key);
26158
- return hits.length > 0 ? hits[0] : null;
26159
- }
26160
- function checkForeignKeys(env, table, row) {
26161
- const state = env.ctx.state;
26162
- for (const con of table.constraints) {
26163
- if (con.kind !== "foreign_key") continue;
26164
- const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
26165
- const nulls = values.filter((v) => v === null).length;
26166
- if (con.match === "simple" && nulls > 0) continue;
26167
- if (con.match === "full") {
26168
- if (nulls === values.length) continue;
26169
- if (nulls > 0) {
26170
- throw pgError(
26171
- "constraint_foreign_key",
26172
- `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
26173
- "23503"
26174
- );
26175
- }
26176
- }
26177
- const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
26178
- if (!refTable) {
26179
- throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
26180
- }
26181
- const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
26182
- const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
26183
- const wanted = values.map((v, i) => {
26184
- const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
26185
- const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
26186
- return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
26187
- }).join("");
26188
- let found = false;
26189
- for (let i = 0; i < refTable.rowCount(); i++) {
26190
- const r = refTable.rowAt(i);
26191
- const key = refIdxs.map((ri, j) => {
26192
- const v = r[ri] ?? null;
26193
- return v === null ? "\0N" : datumKey(keyTypes[j], v);
26194
- }).join("");
26195
- if (key === wanted) {
26196
- found = true;
26197
- break;
26198
- }
26199
- }
26200
- if (!found) {
26201
- throw pgError(
26202
- "constraint_foreign_key",
26203
- `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
26204
- "23503"
26205
- );
26245
+ }
26246
+ function executeSelectStmt(env0, stmt) {
26247
+ const env = applyWith(env0, stmt.with);
26248
+ let rel3;
26249
+ if (stmt.body.type === "select_core") {
26250
+ rel3 = executeCore(env, stmt.body, stmt.orderBy, { hasLimit: stmt.limit !== null });
26251
+ } else {
26252
+ rel3 = executeBody(env, stmt.body);
26253
+ const specs = outputOrderSpecs(env, rel3, stmt.orderBy);
26254
+ if (specs.length > 0) sortRows(env, rel3, specs);
26255
+ rel3 = stripHidden(rel3);
26256
+ }
26257
+ if (stmt.limitWithTies) {
26258
+ if (stmt.orderBy.length === 0) {
26259
+ throw pgError("syntax", "WITH TIES cannot be specified without ORDER BY clause", "42601");
26206
26260
  }
26261
+ const tieSpecs = outputOrderSpecs(env, rel3, stmt.orderBy);
26262
+ return stripHidden(applyLimitOffset(env, rel3, stmt.limit, stmt.offset, tieSpecs));
26207
26263
  }
26264
+ return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
26208
26265
  }
26209
- function referencingConstraints(env, table) {
26210
- const out = [];
26211
- for (const schema of env.ctx.state.schemas.values()) {
26212
- for (const t of schema.tables.values()) {
26213
- for (const con of t.constraints) {
26214
- if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
26215
- out.push({ table: t, constraint: con });
26216
- }
26217
- }
26266
+
26267
+ // src/executor/triggers-scope.ts
26268
+ function triggerScope(table, newRow, oldRow) {
26269
+ const cols = [];
26270
+ const row = [];
26271
+ const rangeVars = /* @__PURE__ */ new Set();
26272
+ if (newRow) {
26273
+ rangeVars.add("new");
26274
+ for (let i = 0; i < table.columns.length; i++) {
26275
+ cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
26276
+ row.push(newRow[i] ?? null);
26218
26277
  }
26219
26278
  }
26220
- return out;
26279
+ if (oldRow) {
26280
+ rangeVars.add("old");
26281
+ for (let i = 0; i < table.columns.length; i++) {
26282
+ cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
26283
+ row.push(oldRow[i] ?? null);
26284
+ }
26285
+ }
26286
+ return new RowScope(cols, row, null, rangeVars);
26221
26287
  }
26222
26288
 
26223
26289
  // src/executor/triggers.ts
@@ -26233,6 +26299,18 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
26233
26299
  for (const t of table.triggers) {
26234
26300
  if (t.timing !== timing || !matches(t, event) || !t.forEachRow) continue;
26235
26301
  if (!executor) throw unsupported(`trigger "${t.name}" execution`);
26302
+ if (t.constraint && timing === "after") {
26303
+ if (t.when && !evalPredicate(env, triggerScope(table, newRow, oldRow), t.when)) continue;
26304
+ env.ctx.state.pendingTriggers.push({
26305
+ schema: table.schema,
26306
+ table: table.name,
26307
+ trigger: t.name,
26308
+ event,
26309
+ oldRow: oldRow?.slice() ?? null,
26310
+ newRow: newRow?.slice() ?? null
26311
+ });
26312
+ continue;
26313
+ }
26236
26314
  const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
26237
26315
  if (timing === "before") {
26238
26316
  if (result === null) return { row: null };
@@ -26241,6 +26319,167 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
26241
26319
  }
26242
26320
  return { row };
26243
26321
  }
26322
+ var keyOf = (schema, name2) => JSON.stringify([schema, name2]);
26323
+ function flushConstraintTriggers(state, all = false) {
26324
+ const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
26325
+ for (let i = 0; i < state.pendingTriggers.length; ) {
26326
+ const pending2 = state.pendingTriggers[i];
26327
+ const table = state.schemas.get(pending2.schema)?.tables.get(pending2.table);
26328
+ const trigger = table?.triggers.find((t) => t.name === pending2.trigger);
26329
+ if (!table || !trigger) {
26330
+ state.pendingTriggers.splice(i, 1);
26331
+ continue;
26332
+ }
26333
+ const deferred = trigger.deferrable && (state.constraintModes.get(keyOf(table.schema, trigger.name)) ?? trigger.initiallyDeferred);
26334
+ if (!all && state.inTransaction && deferred) {
26335
+ i++;
26336
+ continue;
26337
+ }
26338
+ if (!executor) throw unsupported(`trigger "${trigger.name}" execution`);
26339
+ state.pendingTriggers.splice(i, 1);
26340
+ const changes = state.changes;
26341
+ try {
26342
+ executor(env, table, { ...trigger, when: null }, pending2.event, pending2.oldRow, pending2.newRow);
26343
+ } catch (error) {
26344
+ state.pendingTriggers.splice(i, 0, pending2);
26345
+ throw error;
26346
+ } finally {
26347
+ state.changes = changes;
26348
+ }
26349
+ }
26350
+ }
26351
+ function setConstraints(state, stmt) {
26352
+ if (!state.inTransaction) return;
26353
+ const matches2 = [];
26354
+ if (stmt.names) {
26355
+ for (const name2 of stmt.names) {
26356
+ const schemas = name2.length > 1 ? [name2[name2.length - 2]] : state.effectiveSearchPath();
26357
+ let found = false;
26358
+ for (const schema of schemas) {
26359
+ const items = [...state.schemas.get(schema)?.tables.values() ?? []].flatMap(
26360
+ (table) => table.triggers.filter((t) => t.constraint && t.name === name2[name2.length - 1])
26361
+ );
26362
+ if (items.length) {
26363
+ for (const trigger of items) matches2.push({ schema, trigger });
26364
+ found = true;
26365
+ break;
26366
+ }
26367
+ }
26368
+ if (!found) throw pgError("undefined_object", `constraint "${name2.join(".")}" does not exist`, "42704");
26369
+ }
26370
+ } else {
26371
+ for (const schema of state.schemas.values())
26372
+ for (const table of schema.tables.values())
26373
+ for (const trigger of table.triggers)
26374
+ if (trigger.constraint && trigger.deferrable) matches2.push({ schema: schema.name, trigger });
26375
+ }
26376
+ for (const { trigger } of matches2)
26377
+ if (stmt.deferred && !trigger.deferrable)
26378
+ throw pgError("misuse", `constraint "${trigger.name}" is not deferrable`, "55000");
26379
+ const previous = new Map(state.constraintModes);
26380
+ for (const { schema, trigger } of matches2) state.constraintModes.set(keyOf(schema, trigger.name), stmt.deferred);
26381
+ try {
26382
+ if (!stmt.deferred) flushConstraintTriggers(state);
26383
+ } catch (error) {
26384
+ state.constraintModes = previous;
26385
+ throw error;
26386
+ }
26387
+ }
26388
+
26389
+ // src/transactions/manager.ts
26390
+ var TransactionManager = class {
26391
+ constructor(state) {
26392
+ this.state = state;
26393
+ }
26394
+ state;
26395
+ base = null;
26396
+ savepoints = [];
26397
+ freezeDepth = 0;
26398
+ get inTransaction() {
26399
+ return this.base !== null;
26400
+ }
26401
+ takeSnapshot() {
26402
+ this.state.freezeShared();
26403
+ this.freezeDepth++;
26404
+ return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
26405
+ }
26406
+ restore(snap) {
26407
+ this.state.restoreFrom(snap.state);
26408
+ this.state.prng.setState(snap.prngState);
26409
+ }
26410
+ thawOnce() {
26411
+ if (this.freezeDepth > 0) {
26412
+ this.state.thawShared();
26413
+ this.freezeDepth--;
26414
+ }
26415
+ }
26416
+ begin() {
26417
+ if (this.base !== null) {
26418
+ return;
26419
+ }
26420
+ this.base = this.takeSnapshot();
26421
+ this.state.inTransaction = true;
26422
+ assert(this.freezeDepth === 1, "BEGIN freeze depth");
26423
+ }
26424
+ commit() {
26425
+ try {
26426
+ flushConstraintTriggers(this.state, true);
26427
+ } catch (error) {
26428
+ this.rollback();
26429
+ throw error;
26430
+ }
26431
+ this.base = null;
26432
+ this.savepoints = [];
26433
+ this.state.inTransaction = false;
26434
+ this.state.localSettings.clear();
26435
+ this.state.pendingTriggers = [];
26436
+ this.state.constraintModes.clear();
26437
+ while (this.freezeDepth > 0) this.thawOnce();
26438
+ }
26439
+ rollback() {
26440
+ if (this.base !== null) {
26441
+ this.restore(this.base);
26442
+ }
26443
+ this.base = null;
26444
+ this.savepoints = [];
26445
+ this.state.inTransaction = false;
26446
+ this.state.localSettings.clear();
26447
+ this.state.pendingTriggers = [];
26448
+ this.state.constraintModes.clear();
26449
+ while (this.freezeDepth > 0) this.thawOnce();
26450
+ }
26451
+ savepoint(name2) {
26452
+ if (this.base === null) {
26453
+ throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
26454
+ }
26455
+ this.savepoints.push({ name: name2, snap: this.takeSnapshot() });
26456
+ }
26457
+ releaseSavepoint(name2) {
26458
+ const idx = this.findSavepoint(name2);
26459
+ this.savepoints.splice(idx);
26460
+ }
26461
+ rollbackToSavepoint(name2) {
26462
+ const idx = this.findSavepoint(name2);
26463
+ const sp = this.savepoints[idx];
26464
+ this.restore(sp.snap);
26465
+ this.savepoints.splice(idx + 1);
26466
+ }
26467
+ findSavepoint(name2) {
26468
+ if (this.base === null) {
26469
+ throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
26470
+ }
26471
+ for (let i = this.savepoints.length - 1; i >= 0; i--) {
26472
+ if (this.savepoints[i].name === name2) return i;
26473
+ }
26474
+ throw pgError("invalid_savepoint_specification", `savepoint "${name2}" does not exist`, "3B001");
26475
+ }
26476
+ reset() {
26477
+ this.base = null;
26478
+ this.savepoints = [];
26479
+ this.state.inTransaction = false;
26480
+ this.freezeDepth = 0;
26481
+ }
26482
+ };
26244
26483
 
26245
26484
  // src/executor/dml.ts
26246
26485
  function withStatementRollback(env, fn) {
@@ -26930,6 +27169,7 @@ function executeSet(env, stmt) {
26930
27169
  else target.delete(name2);
26931
27170
  return commandResult("SET", 0);
26932
27171
  }
27172
+ validateStorageSetting(name2, stmt.value);
26933
27173
  target.set(name2, stmt.value);
26934
27174
  return commandResult("SET", 0);
26935
27175
  }
@@ -27235,64 +27475,6 @@ function executeCopyFromData(env, stmt, data) {
27235
27475
  };
27236
27476
  }
27237
27477
 
27238
- // src/expressions/context.ts
27239
- var EngineCtx = class {
27240
- state;
27241
- /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
27242
- txNow;
27243
- /** statement timestamp */
27244
- stmtNow;
27245
- constructor(state, txNow) {
27246
- this.state = state;
27247
- const nowMs = state.clock().getTime();
27248
- const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
27249
- this.txNow = txNow ?? nowMicros;
27250
- this.stmtNow = nowMicros;
27251
- }
27252
- timezone() {
27253
- return this.state.getSetting("timezone") ?? "UTC";
27254
- }
27255
- namespaceOid(name2) {
27256
- const oid = builtinNamespaceOid(name2) ?? this.state.schemas.get(name2)?.oid;
27257
- if (oid === void 0) throw pgError("undefined_object", `schema "${name2}" does not exist`, "3F000");
27258
- return oid;
27259
- }
27260
- namespaceName(oid) {
27261
- const builtin = builtinNamespaceName(oid);
27262
- if (builtin !== void 0) return builtin;
27263
- for (const schema of this.state.schemas.values()) {
27264
- if (schema.oid === oid) return schema.name;
27265
- }
27266
- return null;
27267
- }
27268
- zoneOffsetAt(utcMicros) {
27269
- return zoneOffsetAtUtc(this.timezone(), utcMicros);
27270
- }
27271
- zoneOffsetForNaive(naiveMicros) {
27272
- return zoneOffsetForNaive(this.timezone(), naiveMicros);
27273
- }
27274
- enumLabels(enumType) {
27275
- if (!isEnumType(enumType)) return null;
27276
- const e = this.state.findEnumByKey(enumType.slice(5));
27277
- return e?.labels ?? null;
27278
- }
27279
- enumHasLabel(enumType, label) {
27280
- const labels = this.enumLabels(enumType);
27281
- return labels === null ? true : labels.includes(label);
27282
- }
27283
- enumOrder(enumType, label) {
27284
- const labels = this.enumLabels(enumType);
27285
- if (!labels) return 0;
27286
- const idx = labels.indexOf(label);
27287
- return idx === -1 ? labels.length : idx;
27288
- }
27289
- requireEnum(enumType) {
27290
- const labels = this.enumLabels(enumType);
27291
- if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
27292
- return labels;
27293
- }
27294
- };
27295
-
27296
27478
  // src/runtime/clock.ts
27297
27479
  var DEFAULT_NOW = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
27298
27480
  function fixedClock(instant = DEFAULT_NOW) {
@@ -28260,7 +28442,7 @@ function pgClass(ctx) {
28260
28442
  v.materialized ? "m" : "v",
28261
28443
  v.columns?.length ?? 0,
28262
28444
  v.matRows?.length ?? 0,
28263
- false,
28445
+ v.materialized && [...schemaData.indexes.values()].some((i) => i.tableOid === v.oid),
28264
28446
  !v.materialized || v.matRows !== null,
28265
28447
  relPersistence(v)
28266
28448
  );
@@ -28526,7 +28708,7 @@ function pgIndex(ctx) {
28526
28708
  const valid = i.valid !== false;
28527
28709
  rows.push([
28528
28710
  i.oid ?? 0,
28529
- table?.oid ?? 0,
28711
+ table?.oid ?? i.tableOid ?? 0,
28530
28712
  indexAttributeCount(i),
28531
28713
  i.columns.length,
28532
28714
  i.unique,
@@ -28698,7 +28880,7 @@ function pgStatUserIndexes(ctx) {
28698
28880
  for (const index of schema.indexes.values()) {
28699
28881
  const table = schema.tables.get(index.table);
28700
28882
  rows.push([
28701
- table?.oid ?? 0,
28883
+ table?.oid ?? index.tableOid ?? 0,
28702
28884
  index.oid ?? 0,
28703
28885
  schema.name,
28704
28886
  index.table,
@@ -29293,26 +29475,6 @@ function runTriggerBody(env, table, stmts, vars) {
29293
29475
  }
29294
29476
  }
29295
29477
  }
29296
- function triggerScope(table, newRow, oldRow) {
29297
- const cols = [];
29298
- const row = [];
29299
- const rangeVars = /* @__PURE__ */ new Set();
29300
- if (newRow) {
29301
- rangeVars.add("new");
29302
- for (let i = 0; i < table.columns.length; i++) {
29303
- cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
29304
- row.push(newRow[i] ?? null);
29305
- }
29306
- }
29307
- if (oldRow) {
29308
- rangeVars.add("old");
29309
- for (let i = 0; i < table.columns.length; i++) {
29310
- cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
29311
- row.push(oldRow[i] ?? null);
29312
- }
29313
- }
29314
- return new RowScope(cols, row, null, rangeVars);
29315
- }
29316
29478
  function executeTrigger(env, table, trigger, _event, oldRow, newRow) {
29317
29479
  const state = env.ctx.state;
29318
29480
  if (trigger.when !== null && !evalPredicate(env, triggerScope(table, newRow, oldRow), trigger.when)) {
@@ -30282,7 +30444,8 @@ function executeCreateTableAs(env, stmt) {
30282
30444
  }
30283
30445
  function executeCreateIndex(env, stmt) {
30284
30446
  const state = env.ctx.state;
30285
- const table = state.findTable(stmt.table);
30447
+ const view = state.findView(stmt.table);
30448
+ const table = state.findTable(stmt.table) ?? (view?.materialized ? materializedTable(view) : void 0);
30286
30449
  if (!table) {
30287
30450
  throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
30288
30451
  }
@@ -30394,13 +30557,22 @@ function executeRefreshMatView(env, stmt) {
30394
30557
  if (!found?.materialized) {
30395
30558
  throw pgError("undefined_table", `materialized view "${stmt.name.join(".")}" does not exist`, "42P01");
30396
30559
  }
30560
+ const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), found.query);
30561
+ const next = {
30562
+ ...found,
30563
+ matColumns: rel3.columns.map((c, i) => ({
30564
+ name: found.columns?.[i] ?? c.name,
30565
+ type: c.type === UNKNOWN ? "text" : c.type
30566
+ })),
30567
+ matRows: stmt.withData ? rel3.rows : null
30568
+ };
30569
+ const table = materializedTable(next);
30570
+ for (const index of env.ctx.state.getSchema(found.schema).indexes.values()) {
30571
+ if (index.tableOid === found.oid && index.valid !== false) assertUniqueIndexBuildable(env, table, index);
30572
+ }
30397
30573
  const view = env.ctx.state.ensureWritableView(found);
30398
- const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), view.query);
30399
- view.matColumns = rel3.columns.map((c, i) => ({
30400
- name: view.columns?.[i] ?? c.name,
30401
- type: c.type === UNKNOWN ? "text" : c.type
30402
- }));
30403
- view.matRows = stmt.withData ? rel3.rows : null;
30574
+ view.matColumns = next.matColumns;
30575
+ view.matRows = next.matRows;
30404
30576
  return commandResult("REFRESH MATERIALIZED VIEW", 0);
30405
30577
  }
30406
30578
  function executeCreateSchema(env, stmt) {
@@ -30542,6 +30714,8 @@ function executeCreateFunction(env, stmt) {
30542
30714
  return commandResult("CREATE FUNCTION", 0);
30543
30715
  }
30544
30716
  function executeCreateTrigger(env, stmt) {
30717
+ if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
30718
+ throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
30545
30719
  const state = env.ctx.state;
30546
30720
  const found = state.findTable(stmt.table);
30547
30721
  if (!found) {
@@ -30560,6 +30734,9 @@ function executeCreateTrigger(env, stmt) {
30560
30734
  }
30561
30735
  const meta = {
30562
30736
  name: stmt.name,
30737
+ constraint: stmt.constraint,
30738
+ deferrable: stmt.deferrable,
30739
+ initiallyDeferred: stmt.initiallyDeferred,
30563
30740
  timing: stmt.timing,
30564
30741
  events: stmt.events,
30565
30742
  forEachRow: stmt.forEachRow,
@@ -31328,7 +31505,16 @@ function alterTableIsSelfContained(stmt) {
31328
31505
  return stmt.actions.length === 1 && action !== void 0 && SELF_CONTAINED_ALTER_ACTIONS.has(action.kind);
31329
31506
  }
31330
31507
  function executeStatement(env, stmt) {
31331
- if (!RESHAPES_TABLES.has(stmt.type)) return dispatchStatement(env, stmt);
31508
+ if (!RESHAPES_TABLES.has(stmt.type)) {
31509
+ if ((stmt.type === "insert" || stmt.type === "update" || stmt.type === "delete") && (env.ctx.state.pendingTriggers.length > 0 || env.ctx.state.findTable(stmt.table)?.triggers.some((t) => t.constraint))) {
31510
+ return withStatementRollback(env, () => {
31511
+ const result = dispatchStatement(env, stmt);
31512
+ flushConstraintTriggers(env.ctx.state);
31513
+ return result;
31514
+ });
31515
+ }
31516
+ return dispatchStatement(env, stmt);
31517
+ }
31332
31518
  try {
31333
31519
  return dispatchStatement(env, stmt);
31334
31520
  } finally {
@@ -31387,6 +31573,9 @@ function dispatchStatement(env, stmt) {
31387
31573
  return executeRefreshMatView(env, stmt);
31388
31574
  case "transaction":
31389
31575
  return executeTransaction(env, stmt);
31576
+ case "set_constraints":
31577
+ setConstraints(env.ctx.state, stmt);
31578
+ return commandResult("SET CONSTRAINTS", 0);
31390
31579
  case "set":
31391
31580
  return executeSet(env, stmt);
31392
31581
  case "show":
@@ -33825,11 +34014,13 @@ var ScramServer = class {
33825
34014
  var splitStatements = (script) => {
33826
34015
  const out = [];
33827
34016
  let start3 = 0;
34017
+ let hasSql = false;
33828
34018
  let i = 0;
33829
34019
  const n = script.length;
33830
34020
  const push = (end) => {
33831
34021
  const text = script.slice(start3, end).trim();
33832
- if (text.length > 0) out.push(text);
34022
+ if (hasSql) out.push(text);
34023
+ hasSql = false;
33833
34024
  };
33834
34025
  while (i < n) {
33835
34026
  const c = script[i];
@@ -33851,9 +34042,11 @@ var splitStatements = (script) => {
33851
34042
  i += 2;
33852
34043
  } else i++;
33853
34044
  }
34045
+ if (depth > 0) hasSql = true;
33854
34046
  continue;
33855
34047
  }
33856
34048
  if (c === "'" || c === '"') {
34049
+ hasSql = true;
33857
34050
  const escaped = c === "'" && /[eE]/.test(script[i - 1] ?? "") && !/[\w$]/.test(script[i - 2] ?? "");
33858
34051
  i++;
33859
34052
  while (i < n) {
@@ -33876,6 +34069,7 @@ var splitStatements = (script) => {
33876
34069
  if (c === "$") {
33877
34070
  const m = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(script.slice(i, i + 64));
33878
34071
  if (m) {
34072
+ hasSql = true;
33879
34073
  const tag = m[0];
33880
34074
  const end = script.indexOf(tag, i + tag.length);
33881
34075
  i = end === -1 ? n : end + tag.length;
@@ -33886,6 +34080,7 @@ var splitStatements = (script) => {
33886
34080
  push(i);
33887
34081
  start3 = i + 1;
33888
34082
  }
34083
+ if (c !== ";" && !/\s/.test(c)) hasSql = true;
33889
34084
  i++;
33890
34085
  }
33891
34086
  push(n);
@@ -34318,7 +34513,16 @@ var binaryValue = (type, text) => {
34318
34513
  }
34319
34514
  };
34320
34515
  var binaryEncodable = (type) => TEXT_TYPES2.has(type) || ["bool", "int2", "int4", "oid", "int8", "float4", "float8", "bytea", "uuid"].includes(type);
34321
- var mutatesDatabase = (sql) => parse(sql).some((stmt) => !["select", "show", "explain", "transaction", "no_op"].includes(stmt.type));
34516
+ var setsSessionConfig = (node) => {
34517
+ if (Array.isArray(node)) return node.some(setsSessionConfig);
34518
+ if (typeof node !== "object" || node === null) return false;
34519
+ const call = node;
34520
+ if (call.type === "func" && call.name?.at(-1) === "set_config") return true;
34521
+ return Object.values(node).some(setsSessionConfig);
34522
+ };
34523
+ var mutatesDatabase = (sql) => parse(sql).some(
34524
+ (stmt) => !["select", "show", "explain", "transaction", "no_op"].includes(stmt.type) || stmt.type === "select" && setsSessionConfig(stmt)
34525
+ );
34322
34526
  var NEVER_WRITES = /* @__PURE__ */ new Set(["show", "set", "reset", "transaction", "no_op", "prepare", "deallocate"]);
34323
34527
  var WRITE_NODES = /* @__PURE__ */ new Set(["insert", "update", "delete"]);
34324
34528
  var containsWrite = (db, node) => {
@@ -34943,6 +35147,7 @@ var Connection = class {
34943
35147
  }
34944
35148
  return outcome;
34945
35149
  } finally {
35150
+ workspace?.transactions.rollback();
34946
35151
  workspace?.close();
34947
35152
  }
34948
35153
  }
@@ -35017,8 +35222,9 @@ var Connection = class {
35017
35222
  });
35018
35223
  workspace.state.settings = new Map(this.settings);
35019
35224
  workspace.state.settingDefaults = this.settingDefaults;
35225
+ workspace.transactions.begin();
35020
35226
  for (const logged of this.transaction?.statements ?? []) {
35021
- workspace.transaction(() => this.replay(workspace, logged));
35227
+ this.replay(workspace, logged);
35022
35228
  }
35023
35229
  return workspace;
35024
35230
  }