@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/admin.js CHANGED
@@ -2505,6 +2505,13 @@ function isPostgresError(err) {
2505
2505
  return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
2506
2506
  }
2507
2507
 
2508
+ // src/storage/guc.ts
2509
+ function validateStorageSetting(name, value) {
2510
+ if (name === "default_table_access_method" && value !== "heap" || name === "default_tablespace" && value !== "" && value !== "pg_default") {
2511
+ throw pgError("invalid_parameter_value", `invalid value for parameter "${name}": "${value}"`, "22023");
2512
+ }
2513
+ }
2514
+
2508
2515
  // src/runtime/prng.ts
2509
2516
  var Prng = class _Prng {
2510
2517
  state;
@@ -5959,6 +5966,9 @@ var DatabaseState = class _DatabaseState {
5959
5966
  changes = 0;
5960
5967
  oidCounter = 16384;
5961
5968
  inTransaction = false;
5969
+ /** Transaction-only events: included in rollback clones, never in exported snapshots. */
5970
+ pendingTriggers = [];
5971
+ constraintModes = /* @__PURE__ */ new Map();
5962
5972
  /** most recent sequence touched by nextval/setval, for lastval() */
5963
5973
  lastSequence = null;
5964
5974
  /** Session currval per sequence; absent entry means currval is undefined. */
@@ -6001,6 +6011,8 @@ var DatabaseState = class _DatabaseState {
6001
6011
  this.settings.set("row_security", "on");
6002
6012
  this.settings.set("array_nulls", "on");
6003
6013
  this.settings.set("backslash_quote", "safe_encoding");
6014
+ this.settings.set("default_tablespace", "");
6015
+ this.settings.set("default_table_access_method", "heap");
6004
6016
  }
6005
6017
  nextOid() {
6006
6018
  return this.oidCounter++;
@@ -6160,6 +6172,8 @@ var DatabaseState = class _DatabaseState {
6160
6172
  s.prepared = new Map(this.prepared);
6161
6173
  s.changes = this.changes;
6162
6174
  s.inTransaction = this.inTransaction;
6175
+ s.pendingTriggers = this.pendingTriggers.slice();
6176
+ s.constraintModes = new Map(this.constraintModes);
6163
6177
  s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
6164
6178
  s.sequenceCurrval = new Map(this.sequenceCurrval);
6165
6179
  s.tableStats = new Map(this.tableStats);
@@ -6177,6 +6191,8 @@ var DatabaseState = class _DatabaseState {
6177
6191
  s.prepared = new Map(this.prepared);
6178
6192
  s.changes = this.changes;
6179
6193
  s.inTransaction = this.inTransaction;
6194
+ s.pendingTriggers = this.pendingTriggers.slice();
6195
+ s.constraintModes = new Map(this.constraintModes);
6180
6196
  s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
6181
6197
  s.sequenceCurrval = new Map(this.sequenceCurrval);
6182
6198
  s.tableStats = new Map(this.tableStats);
@@ -6249,6 +6265,8 @@ var DatabaseState = class _DatabaseState {
6249
6265
  this.settings = other.settings;
6250
6266
  this.localSettings = other.localSettings;
6251
6267
  this.prepared = other.prepared;
6268
+ this.pendingTriggers = other.pendingTriggers.slice();
6269
+ this.constraintModes = new Map(other.constraintModes);
6252
6270
  this.changes = other.changes;
6253
6271
  this.lastSequence = other.lastSequence;
6254
6272
  this.sequenceCurrval = other.sequenceCurrval;
@@ -6265,90 +6283,189 @@ var DatabaseState = class _DatabaseState {
6265
6283
  }
6266
6284
  };
6267
6285
 
6268
- // src/transactions/manager.ts
6269
- var TransactionManager = class {
6270
- constructor(state) {
6271
- this.state = state;
6286
+ // src/types/timezone.ts
6287
+ var intlCache = /* @__PURE__ */ new Map();
6288
+ function intlFor(zone) {
6289
+ const cached = intlCache.get(zone);
6290
+ if (cached) return cached;
6291
+ try {
6292
+ const fmt = new Intl.DateTimeFormat("en-US", {
6293
+ timeZone: zone,
6294
+ timeZoneName: "longOffset",
6295
+ year: "numeric"
6296
+ });
6297
+ intlCache.set(zone, fmt);
6298
+ return fmt;
6299
+ } catch {
6300
+ return null;
6301
+ }
6302
+ }
6303
+ function parseFixedOffset(zone) {
6304
+ const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
6305
+ if (!m) return null;
6306
+ const sign = m[1] === "-" ? -1 : 1;
6307
+ return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
6308
+ }
6309
+ function parsePosixOffset(zone) {
6310
+ const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
6311
+ if (!m) return null;
6312
+ const sign = m[1] === "-" ? 1 : -1;
6313
+ return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
6314
+ }
6315
+ function pgMicrosToUnixMs(utcMicros) {
6316
+ return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
6317
+ }
6318
+ function zoneOffsetAtUtc(zone, utcMicros) {
6319
+ const z = zone.trim();
6320
+ const lower = z.toLowerCase();
6321
+ if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
6322
+ const posix = parsePosixOffset(z);
6323
+ if (posix !== null) return posix;
6324
+ const fixed = parseFixedOffset(z);
6325
+ if (fixed !== null) return fixed;
6326
+ const fmt = intlFor(z);
6327
+ if (fmt) {
6328
+ const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
6329
+ const parts = fmt.formatToParts(new Date(ms));
6330
+ const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
6331
+ const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
6332
+ if (!m) return 0;
6333
+ const sign = m[1] === "-" ? -1 : 1;
6334
+ return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
6272
6335
  }
6336
+ throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
6337
+ }
6338
+ function clampDateMs(ms) {
6339
+ const MAX = 864e13;
6340
+ return Math.max(-MAX, Math.min(MAX, ms));
6341
+ }
6342
+ function zoneOffsetForNaive(zone, naiveMicros) {
6343
+ const first = zoneOffsetAtUtc(zone, naiveMicros);
6344
+ const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
6345
+ return second;
6346
+ }
6347
+
6348
+ // src/expressions/context.ts
6349
+ var EngineCtx = class {
6273
6350
  state;
6274
- base = null;
6275
- savepoints = [];
6276
- freezeDepth = 0;
6277
- get inTransaction() {
6278
- return this.base !== null;
6351
+ /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
6352
+ txNow;
6353
+ /** statement timestamp */
6354
+ stmtNow;
6355
+ constructor(state, txNow) {
6356
+ this.state = state;
6357
+ const nowMs = state.clock().getTime();
6358
+ const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
6359
+ this.txNow = txNow ?? nowMicros;
6360
+ this.stmtNow = nowMicros;
6279
6361
  }
6280
- takeSnapshot() {
6281
- this.state.freezeShared();
6282
- this.freezeDepth++;
6283
- return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
6362
+ timezone() {
6363
+ return this.state.getSetting("timezone") ?? "UTC";
6284
6364
  }
6285
- restore(snap) {
6286
- this.state.restoreFrom(snap.state);
6287
- this.state.prng.setState(snap.prngState);
6365
+ namespaceOid(name) {
6366
+ const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
6367
+ if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
6368
+ return oid;
6288
6369
  }
6289
- thawOnce() {
6290
- if (this.freezeDepth > 0) {
6291
- this.state.thawShared();
6292
- this.freezeDepth--;
6370
+ namespaceName(oid) {
6371
+ const builtin = builtinNamespaceName(oid);
6372
+ if (builtin !== void 0) return builtin;
6373
+ for (const schema of this.state.schemas.values()) {
6374
+ if (schema.oid === oid) return schema.name;
6293
6375
  }
6376
+ return null;
6294
6377
  }
6295
- begin() {
6296
- if (this.base !== null) {
6297
- return;
6298
- }
6299
- this.base = this.takeSnapshot();
6300
- this.state.inTransaction = true;
6301
- assert(this.freezeDepth === 1, "BEGIN freeze depth");
6378
+ zoneOffsetAt(utcMicros) {
6379
+ return zoneOffsetAtUtc(this.timezone(), utcMicros);
6302
6380
  }
6303
- commit() {
6304
- this.base = null;
6305
- this.savepoints = [];
6306
- this.state.inTransaction = false;
6307
- this.state.localSettings.clear();
6308
- while (this.freezeDepth > 0) this.thawOnce();
6381
+ zoneOffsetForNaive(naiveMicros) {
6382
+ return zoneOffsetForNaive(this.timezone(), naiveMicros);
6309
6383
  }
6310
- rollback() {
6311
- if (this.base !== null) {
6312
- this.restore(this.base);
6313
- }
6314
- this.base = null;
6315
- this.savepoints = [];
6316
- this.state.inTransaction = false;
6317
- this.state.localSettings.clear();
6318
- while (this.freezeDepth > 0) this.thawOnce();
6384
+ enumLabels(enumType) {
6385
+ if (!isEnumType(enumType)) return null;
6386
+ const e = this.state.findEnumByKey(enumType.slice(5));
6387
+ return e?.labels ?? null;
6319
6388
  }
6320
- savepoint(name) {
6321
- if (this.base === null) {
6322
- throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
6323
- }
6324
- this.savepoints.push({ name, snap: this.takeSnapshot() });
6389
+ enumHasLabel(enumType, label) {
6390
+ const labels = this.enumLabels(enumType);
6391
+ return labels === null ? true : labels.includes(label);
6325
6392
  }
6326
- releaseSavepoint(name) {
6327
- const idx = this.findSavepoint(name);
6328
- this.savepoints.splice(idx);
6393
+ enumOrder(enumType, label) {
6394
+ const labels = this.enumLabels(enumType);
6395
+ if (!labels) return 0;
6396
+ const idx = labels.indexOf(label);
6397
+ return idx === -1 ? labels.length : idx;
6329
6398
  }
6330
- rollbackToSavepoint(name) {
6331
- const idx = this.findSavepoint(name);
6332
- const sp = this.savepoints[idx];
6333
- this.restore(sp.snap);
6334
- this.savepoints.splice(idx + 1);
6399
+ requireEnum(enumType) {
6400
+ const labels = this.enumLabels(enumType);
6401
+ if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
6402
+ return labels;
6335
6403
  }
6336
- findSavepoint(name) {
6337
- if (this.base === null) {
6338
- throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
6339
- }
6340
- for (let i = this.savepoints.length - 1; i >= 0; i--) {
6341
- if (this.savepoints[i].name === name) return i;
6404
+ };
6405
+
6406
+ // src/api/bind.ts
6407
+ var INT4_MIN2 = -2147483648;
6408
+ var INT4_MAX2 = 2147483647;
6409
+ function bindValueToTyped(value, index) {
6410
+ if (value === null || value === void 0) return tv(UNKNOWN, null);
6411
+ switch (typeof value) {
6412
+ case "boolean":
6413
+ return tv("bool", value);
6414
+ case "number":
6415
+ if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
6416
+ return tv("int4", value);
6417
+ }
6418
+ if (Number.isInteger(value) && Number.isSafeInteger(value)) {
6419
+ return tv("int8", BigInt(value));
6420
+ }
6421
+ return tv("float8", value);
6422
+ case "bigint":
6423
+ if (value < -9223372036854775808n || value > 9223372036854775807n) {
6424
+ throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
6425
+ }
6426
+ return tv("int8", value);
6427
+ case "string":
6428
+ return tv(UNKNOWN, value);
6429
+ case "object": {
6430
+ if (value instanceof Uint8Array) return tv("bytea", value);
6431
+ if (value instanceof Date) {
6432
+ const ms = value.getTime();
6433
+ if (Number.isNaN(ms)) {
6434
+ throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
6435
+ }
6436
+ return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
6437
+ }
6438
+ break;
6342
6439
  }
6343
- throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
6440
+ default:
6441
+ break;
6344
6442
  }
6345
- reset() {
6346
- this.base = null;
6347
- this.savepoints = [];
6348
- this.state.inTransaction = false;
6349
- this.freezeDepth = 0;
6443
+ throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
6444
+ }
6445
+ function datumToJs(t, v, ctx, int8 = "bigint") {
6446
+ if (v === null) return null;
6447
+ switch (t) {
6448
+ case "bool":
6449
+ return v;
6450
+ case "int2":
6451
+ case "int4":
6452
+ case "oid":
6453
+ return typeof v === "bigint" ? Number(v) : v;
6454
+ case "int8": {
6455
+ const n = typeof v === "bigint" ? v : BigInt(v);
6456
+ if (int8 === "string") return n.toString();
6457
+ if (int8 === "number") return Number(n);
6458
+ return n;
6459
+ }
6460
+ case "float4":
6461
+ case "float8":
6462
+ return v;
6463
+ case "bytea":
6464
+ return v;
6465
+ default:
6466
+ return datumText(t === UNKNOWN ? "text" : t, v, ctx);
6350
6467
  }
6351
- };
6468
+ }
6352
6469
 
6353
6470
  // src/types/cast.ts
6354
6471
  var UTC_CAST_ENV = {
@@ -6826,259 +6943,6 @@ function unifyTypes(a, b) {
6826
6943
  return null;
6827
6944
  }
6828
6945
 
6829
- // src/types/resolve.ts
6830
- var SERIALS = {
6831
- smallserial: "int2",
6832
- serial2: "int2",
6833
- serial: "int4",
6834
- serial4: "int4",
6835
- bigserial: "int8",
6836
- serial8: "int8"
6837
- };
6838
- function modOf(t, mods) {
6839
- if (mods.length === 0) return null;
6840
- if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
6841
- return { a: mods[0] };
6842
- }
6843
- function resolveTypeName(state, tn) {
6844
- const joined = tn.parts.join(".");
6845
- const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
6846
- if (bare !== null) {
6847
- const serial = SERIALS[bare];
6848
- if (serial !== void 0 && tn.arrayDims === 0) {
6849
- return { column: { id: serial, mod: null }, domain: null, serial };
6850
- }
6851
- const builtin = normalizeTypeName(bare);
6852
- if (builtin !== null) {
6853
- const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
6854
- return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
6855
- }
6856
- }
6857
- const enumData = state.findEnum(tn.parts);
6858
- if (enumData) {
6859
- const elem = `enum:${enumData.schema}.${enumData.name}`;
6860
- const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
6861
- return { column: { id, mod: null }, domain: null, serial: null };
6862
- }
6863
- const domainData = state.findDomain(tn.parts);
6864
- if (domainData) {
6865
- if (tn.arrayDims > 0) {
6866
- const id = arrayTypeOf(domainData.baseType.id);
6867
- return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
6868
- }
6869
- return {
6870
- column: domainData.baseType,
6871
- domain: `${domainData.schema}.${domainData.name}`,
6872
- serial: null
6873
- };
6874
- }
6875
- throw pgError("undefined_object", `type "${joined}" does not exist`);
6876
- }
6877
-
6878
- // src/executor/relation.ts
6879
- var RowScope = class {
6880
- constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
6881
- this.columns = columns;
6882
- this.row = row;
6883
- this.parent = parent;
6884
- this.rangeVars = rangeVars;
6885
- }
6886
- columns;
6887
- row;
6888
- parent;
6889
- rangeVars;
6890
- /** Resolve a (possibly qualified) column reference. undefined = not found. */
6891
- lookup(parts) {
6892
- if (parts.length === 1) {
6893
- const name2 = parts[0];
6894
- let found2;
6895
- let count3 = 0;
6896
- for (let i = 0; i < this.columns.length; i++) {
6897
- const c = this.columns[i];
6898
- if (c.name === name2 && !c.hidden) {
6899
- count3++;
6900
- if (count3 > 1) {
6901
- throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
6902
- }
6903
- found2 = tv(c.type, this.row[i] ?? null);
6904
- }
6905
- }
6906
- if (found2 !== void 0) return found2;
6907
- if (this.rangeVars.has(name2)) {
6908
- return this.wholeRow(name2);
6909
- }
6910
- return this.parent?.lookup(parts);
6911
- }
6912
- const table = parts[parts.length - 2];
6913
- const name = parts[parts.length - 1];
6914
- let found;
6915
- let count2 = 0;
6916
- for (let i = 0; i < this.columns.length; i++) {
6917
- const c = this.columns[i];
6918
- if (c.table === table && c.name === name) {
6919
- count2++;
6920
- if (count2 > 1) {
6921
- throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
6922
- }
6923
- found = tv(c.type, this.row[i] ?? null);
6924
- }
6925
- }
6926
- if (found !== void 0) return found;
6927
- return this.parent?.lookup(parts);
6928
- }
6929
- /** Build a record value from all columns belonging to a range variable. */
6930
- wholeRow(rangeVar) {
6931
- const types = [];
6932
- const values = [];
6933
- const names = [];
6934
- for (let i = 0; i < this.columns.length; i++) {
6935
- const c = this.columns[i];
6936
- if (c.table === rangeVar) {
6937
- types.push(c.type);
6938
- values.push(this.row[i] ?? null);
6939
- names.push(c.name);
6940
- }
6941
- }
6942
- if (types.length === 0) return void 0;
6943
- return tv("record", { kind: "pgrecord", types, values, names });
6944
- }
6945
- hasRangeVar(name) {
6946
- if (this.rangeVars.has(name)) return true;
6947
- return this.parent?.hasRangeVar(name) ?? false;
6948
- }
6949
- };
6950
- function childEnv(env, outer) {
6951
- return {
6952
- ctx: env.ctx,
6953
- params: env.params,
6954
- ctes: env.ctes,
6955
- outer,
6956
- allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
6957
- };
6958
- }
6959
- function relationResult(rel3, command) {
6960
- const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
6961
- return {
6962
- columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
6963
- rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
6964
- command,
6965
- rowCount: rel3.rows.length
6966
- };
6967
- }
6968
- function commandResult(command, rowCount) {
6969
- return { columns: [], rows: [], command, rowCount };
6970
- }
6971
- function inferColumnName(e) {
6972
- switch (e.type) {
6973
- case "colref":
6974
- return e.parts[e.parts.length - 1];
6975
- case "func":
6976
- return e.name[e.name.length - 1];
6977
- case "cast": {
6978
- const inner = inferColumnName(e.expr);
6979
- if (inner !== "?column?") return inner;
6980
- return e.target.parts[e.target.parts.length - 1];
6981
- }
6982
- case "collate":
6983
- return inferColumnName(e.expr);
6984
- case "case":
6985
- return "case";
6986
- case "array_ctor":
6987
- case "array_query":
6988
- return "array";
6989
- case "row":
6990
- return "row";
6991
- case "subquery_expr":
6992
- return e.kind === "exists" ? "exists" : "?column?";
6993
- case "bool_lit":
6994
- return "bool";
6995
- case "extract":
6996
- return "extract";
6997
- case "position":
6998
- return "position";
6999
- case "substring_sql":
7000
- return "substring";
7001
- case "overlay":
7002
- return "overlay";
7003
- case "trim":
7004
- return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
7005
- case "field_select":
7006
- return e.field === "*" ? "?column?" : e.field;
7007
- case "at_time_zone":
7008
- return "timezone";
7009
- case "grouping_func":
7010
- return "grouping";
7011
- case "subscript":
7012
- return inferColumnName(e.base);
7013
- default:
7014
- return "?column?";
7015
- }
7016
- }
7017
-
7018
- // src/api/bind.ts
7019
- var INT4_MIN2 = -2147483648;
7020
- var INT4_MAX2 = 2147483647;
7021
- function bindValueToTyped(value, index) {
7022
- if (value === null || value === void 0) return tv(UNKNOWN, null);
7023
- switch (typeof value) {
7024
- case "boolean":
7025
- return tv("bool", value);
7026
- case "number":
7027
- if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
7028
- return tv("int4", value);
7029
- }
7030
- if (Number.isInteger(value) && Number.isSafeInteger(value)) {
7031
- return tv("int8", BigInt(value));
7032
- }
7033
- return tv("float8", value);
7034
- case "bigint":
7035
- if (value < -9223372036854775808n || value > 9223372036854775807n) {
7036
- throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
7037
- }
7038
- return tv("int8", value);
7039
- case "string":
7040
- return tv(UNKNOWN, value);
7041
- case "object": {
7042
- if (value instanceof Uint8Array) return tv("bytea", value);
7043
- if (value instanceof Date) {
7044
- const ms = value.getTime();
7045
- if (Number.isNaN(ms)) {
7046
- throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
7047
- }
7048
- return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
7049
- }
7050
- break;
7051
- }
7052
- default:
7053
- break;
7054
- }
7055
- throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
7056
- }
7057
- function datumToJs(t, v, ctx, int8 = "bigint") {
7058
- if (v === null) return null;
7059
- switch (t) {
7060
- case "bool":
7061
- return v;
7062
- case "int2":
7063
- case "int4":
7064
- case "oid":
7065
- return typeof v === "bigint" ? Number(v) : v;
7066
- case "int8": {
7067
- const n = typeof v === "bigint" ? v : BigInt(v);
7068
- if (int8 === "string") return n.toString();
7069
- if (int8 === "number") return Number(n);
7070
- return n;
7071
- }
7072
- case "float4":
7073
- case "float8":
7074
- return v;
7075
- case "bytea":
7076
- return v;
7077
- default:
7078
- return datumText(t === UNKNOWN ? "text" : t, v, ctx);
7079
- }
7080
- }
7081
-
7082
6946
  // src/functions/datetime-fns.ts
7083
6947
  function extractDatePart(ctx, field0, src0, asNumeric) {
7084
6948
  const field = field0.toLowerCase();
@@ -7993,68 +7857,6 @@ function getArrayFunctions() {
7993
7857
  return m;
7994
7858
  }
7995
7859
 
7996
- // src/types/timezone.ts
7997
- var intlCache = /* @__PURE__ */ new Map();
7998
- function intlFor(zone) {
7999
- const cached = intlCache.get(zone);
8000
- if (cached) return cached;
8001
- try {
8002
- const fmt = new Intl.DateTimeFormat("en-US", {
8003
- timeZone: zone,
8004
- timeZoneName: "longOffset",
8005
- year: "numeric"
8006
- });
8007
- intlCache.set(zone, fmt);
8008
- return fmt;
8009
- } catch {
8010
- return null;
8011
- }
8012
- }
8013
- function parseFixedOffset(zone) {
8014
- const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
8015
- if (!m) return null;
8016
- const sign = m[1] === "-" ? -1 : 1;
8017
- return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
8018
- }
8019
- function parsePosixOffset(zone) {
8020
- const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
8021
- if (!m) return null;
8022
- const sign = m[1] === "-" ? 1 : -1;
8023
- return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
8024
- }
8025
- function pgMicrosToUnixMs(utcMicros) {
8026
- return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
8027
- }
8028
- function zoneOffsetAtUtc(zone, utcMicros) {
8029
- const z = zone.trim();
8030
- const lower = z.toLowerCase();
8031
- if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
8032
- const posix = parsePosixOffset(z);
8033
- if (posix !== null) return posix;
8034
- const fixed = parseFixedOffset(z);
8035
- if (fixed !== null) return fixed;
8036
- const fmt = intlFor(z);
8037
- if (fmt) {
8038
- const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
8039
- const parts = fmt.formatToParts(new Date(ms));
8040
- const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
8041
- const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
8042
- if (!m) return 0;
8043
- const sign = m[1] === "-" ? -1 : 1;
8044
- return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
8045
- }
8046
- throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
8047
- }
8048
- function clampDateMs(ms) {
8049
- const MAX = 864e13;
8050
- return Math.max(-MAX, Math.min(MAX, ms));
8051
- }
8052
- function zoneOffsetForNaive(zone, naiveMicros) {
8053
- const first = zoneOffsetAtUtc(zone, naiveMicros);
8054
- const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
8055
- return second;
8056
- }
8057
-
8058
7860
  // src/functions/datetime-registry.ts
8059
7861
  function getDatetimeFunctions() {
8060
7862
  const m = /* @__PURE__ */ new Map();
@@ -9654,6 +9456,28 @@ function exactDegTan(x) {
9654
9456
  return Math.tan(x * Math.PI / 180);
9655
9457
  }
9656
9458
 
9459
+ // src/storage/indexable-relation.ts
9460
+ function materializedTable(view) {
9461
+ const table = new TableData(
9462
+ view.schema,
9463
+ view.name,
9464
+ (view.matColumns ?? []).map((c) => ({
9465
+ name: c.name,
9466
+ type: { id: c.type, mod: null },
9467
+ notNull: false,
9468
+ defaultExpr: null,
9469
+ identity: null,
9470
+ generated: null,
9471
+ collate: null,
9472
+ domain: null
9473
+ })),
9474
+ view.oid,
9475
+ view.temp
9476
+ );
9477
+ table.rows = view.matRows ?? [];
9478
+ return table;
9479
+ }
9480
+
9657
9481
  // src/schema/index-catalog.ts
9658
9482
  var isKeyConstraint = (constraint) => constraint.kind === "primary_key" || constraint.kind === "unique";
9659
9483
  var ACCESS_METHODS = /* @__PURE__ */ new Map([
@@ -9682,6 +9506,8 @@ function syncIndexCatalog(state) {
9682
9506
  const tables = /* @__PURE__ */ new Map();
9683
9507
  for (const schema of state.schemas.values()) {
9684
9508
  for (const table of schema.tables.values()) tables.set(table.oid, { schema, table });
9509
+ for (const view of schema.views.values())
9510
+ if (view.materialized) tables.set(view.oid, { schema, table: materializedTable(view) });
9685
9511
  }
9686
9512
  const backing = /* @__PURE__ */ new Map();
9687
9513
  for (const { table } of tables.values()) {
@@ -9879,6 +9705,55 @@ function deparseExpr(e) {
9879
9705
  }
9880
9706
  }
9881
9707
 
9708
+ // src/types/resolve.ts
9709
+ var SERIALS = {
9710
+ smallserial: "int2",
9711
+ serial2: "int2",
9712
+ serial: "int4",
9713
+ serial4: "int4",
9714
+ bigserial: "int8",
9715
+ serial8: "int8"
9716
+ };
9717
+ function modOf(t, mods) {
9718
+ if (mods.length === 0) return null;
9719
+ if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
9720
+ return { a: mods[0] };
9721
+ }
9722
+ function resolveTypeName(state, tn) {
9723
+ const joined = tn.parts.join(".");
9724
+ const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
9725
+ if (bare !== null) {
9726
+ const serial = SERIALS[bare];
9727
+ if (serial !== void 0 && tn.arrayDims === 0) {
9728
+ return { column: { id: serial, mod: null }, domain: null, serial };
9729
+ }
9730
+ const builtin = normalizeTypeName(bare);
9731
+ if (builtin !== null) {
9732
+ const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
9733
+ return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
9734
+ }
9735
+ }
9736
+ const enumData = state.findEnum(tn.parts);
9737
+ if (enumData) {
9738
+ const elem = `enum:${enumData.schema}.${enumData.name}`;
9739
+ const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
9740
+ return { column: { id, mod: null }, domain: null, serial: null };
9741
+ }
9742
+ const domainData = state.findDomain(tn.parts);
9743
+ if (domainData) {
9744
+ if (tn.arrayDims > 0) {
9745
+ const id = arrayTypeOf(domainData.baseType.id);
9746
+ return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
9747
+ }
9748
+ return {
9749
+ column: domainData.baseType,
9750
+ domain: `${domainData.schema}.${domainData.name}`,
9751
+ serial: null
9752
+ };
9753
+ }
9754
+ throw pgError("undefined_object", `type "${joined}" does not exist`);
9755
+ }
9756
+
9882
9757
  // src/sql/ruleutils.ts
9883
9758
  var BOOL = "bool";
9884
9759
  var TEXT = "text";
@@ -10673,7 +10548,11 @@ function getMiscFunctions() {
10673
10548
  }
10674
10549
  const name = argText(ctx, args[0]).toLowerCase();
10675
10550
  const value = args[1].v === null ? "" : argText(ctx, args[1]);
10676
- ctx.state.settings.set(name, value);
10551
+ if (!ctx.state.settings.has(name) && !name.includes("."))
10552
+ throw pgError("undefined_object", `unrecognized configuration parameter "${name}"`, "42704");
10553
+ validateStorageSetting(name, value);
10554
+ const local = args[2]?.v === true;
10555
+ if (!local || ctx.state.inTransaction) (local ? ctx.state.localSettings : ctx.state.settings).set(name, value);
10677
10556
  return tv("text", value);
10678
10557
  });
10679
10558
  m.set(
@@ -10739,10 +10618,7 @@ function getMiscFunctions() {
10739
10618
  if (!col)
10740
10619
  throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
10741
10620
  if (!col.identity) return tv("text", null);
10742
- return tv(
10743
- "text",
10744
- col.identity.sequence.split(".").map(quoteIdent).join(".")
10745
- );
10621
+ return tv("text", col.identity.sequence.split(".").map(quoteIdent).join("."));
10746
10622
  })
10747
10623
  );
10748
10624
  m.set("format_type", (ctx, args) => {
@@ -15666,6 +15542,146 @@ function isSrfName(name) {
15666
15542
  return SRFS.has(name);
15667
15543
  }
15668
15544
 
15545
+ // src/executor/relation.ts
15546
+ var RowScope = class {
15547
+ constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
15548
+ this.columns = columns;
15549
+ this.row = row;
15550
+ this.parent = parent;
15551
+ this.rangeVars = rangeVars;
15552
+ }
15553
+ columns;
15554
+ row;
15555
+ parent;
15556
+ rangeVars;
15557
+ /** Resolve a (possibly qualified) column reference. undefined = not found. */
15558
+ lookup(parts) {
15559
+ if (parts.length === 1) {
15560
+ const name2 = parts[0];
15561
+ let found2;
15562
+ let count3 = 0;
15563
+ for (let i = 0; i < this.columns.length; i++) {
15564
+ const c = this.columns[i];
15565
+ if (c.name === name2 && !c.hidden) {
15566
+ count3++;
15567
+ if (count3 > 1) {
15568
+ throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
15569
+ }
15570
+ found2 = tv(c.type, this.row[i] ?? null);
15571
+ }
15572
+ }
15573
+ if (found2 !== void 0) return found2;
15574
+ if (this.rangeVars.has(name2)) {
15575
+ return this.wholeRow(name2);
15576
+ }
15577
+ return this.parent?.lookup(parts);
15578
+ }
15579
+ const table = parts[parts.length - 2];
15580
+ const name = parts[parts.length - 1];
15581
+ let found;
15582
+ let count2 = 0;
15583
+ for (let i = 0; i < this.columns.length; i++) {
15584
+ const c = this.columns[i];
15585
+ if (c.table === table && c.name === name) {
15586
+ count2++;
15587
+ if (count2 > 1) {
15588
+ throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
15589
+ }
15590
+ found = tv(c.type, this.row[i] ?? null);
15591
+ }
15592
+ }
15593
+ if (found !== void 0) return found;
15594
+ return this.parent?.lookup(parts);
15595
+ }
15596
+ /** Build a record value from all columns belonging to a range variable. */
15597
+ wholeRow(rangeVar) {
15598
+ const types = [];
15599
+ const values = [];
15600
+ const names = [];
15601
+ for (let i = 0; i < this.columns.length; i++) {
15602
+ const c = this.columns[i];
15603
+ if (c.table === rangeVar) {
15604
+ types.push(c.type);
15605
+ values.push(this.row[i] ?? null);
15606
+ names.push(c.name);
15607
+ }
15608
+ }
15609
+ if (types.length === 0) return void 0;
15610
+ return tv("record", { kind: "pgrecord", types, values, names });
15611
+ }
15612
+ hasRangeVar(name) {
15613
+ if (this.rangeVars.has(name)) return true;
15614
+ return this.parent?.hasRangeVar(name) ?? false;
15615
+ }
15616
+ };
15617
+ function childEnv(env, outer) {
15618
+ return {
15619
+ ctx: env.ctx,
15620
+ params: env.params,
15621
+ ctes: env.ctes,
15622
+ outer,
15623
+ allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
15624
+ };
15625
+ }
15626
+ function relationResult(rel3, command) {
15627
+ const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
15628
+ return {
15629
+ columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
15630
+ rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
15631
+ command,
15632
+ rowCount: rel3.rows.length
15633
+ };
15634
+ }
15635
+ function commandResult(command, rowCount) {
15636
+ return { columns: [], rows: [], command, rowCount };
15637
+ }
15638
+ function inferColumnName(e) {
15639
+ switch (e.type) {
15640
+ case "colref":
15641
+ return e.parts[e.parts.length - 1];
15642
+ case "func":
15643
+ return e.name[e.name.length - 1];
15644
+ case "cast": {
15645
+ const inner = inferColumnName(e.expr);
15646
+ if (inner !== "?column?") return inner;
15647
+ return e.target.parts[e.target.parts.length - 1];
15648
+ }
15649
+ case "collate":
15650
+ return inferColumnName(e.expr);
15651
+ case "case":
15652
+ return "case";
15653
+ case "array_ctor":
15654
+ case "array_query":
15655
+ return "array";
15656
+ case "row":
15657
+ return "row";
15658
+ case "subquery_expr":
15659
+ return e.kind === "exists" ? "exists" : "?column?";
15660
+ case "bool_lit":
15661
+ return "bool";
15662
+ case "extract":
15663
+ return "extract";
15664
+ case "position":
15665
+ return "position";
15666
+ case "substring_sql":
15667
+ return "substring";
15668
+ case "overlay":
15669
+ return "overlay";
15670
+ case "trim":
15671
+ return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
15672
+ case "field_select":
15673
+ return e.field === "*" ? "?column?" : e.field;
15674
+ case "at_time_zone":
15675
+ return "timezone";
15676
+ case "grouping_func":
15677
+ return "grouping";
15678
+ case "subscript":
15679
+ return inferColumnName(e.base);
15680
+ default:
15681
+ return "?column?";
15682
+ }
15683
+ }
15684
+
15669
15685
  // src/indexes/index.ts
15670
15686
  var IndexStore = class _IndexStore {
15671
15687
  name;
@@ -15728,53 +15744,228 @@ function ensureIndexStores(env, table) {
15728
15744
  const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
15729
15745
  if (key !== null) store.insert(key, i);
15730
15746
  }
15731
- stores.set(spec.name, store);
15732
- }
15733
- const schema = env.ctx.state.schemas.get(table.schema);
15734
- if (schema) {
15735
- for (const idx of schema.indexes.values()) {
15736
- if (idx.table !== table.name || idx.unique) continue;
15737
- stores.set(idx.name, new IndexStore(idx.name, false));
15747
+ stores.set(spec.name, store);
15748
+ }
15749
+ const schema = env.ctx.state.schemas.get(table.schema);
15750
+ if (schema) {
15751
+ for (const idx of schema.indexes.values()) {
15752
+ if (idx.table !== table.name || idx.unique) continue;
15753
+ stores.set(idx.name, new IndexStore(idx.name, false));
15754
+ }
15755
+ }
15756
+ table.indexStores = stores;
15757
+ return stores;
15758
+ }
15759
+ function indexStoreFor(env, table, spec) {
15760
+ const stores = ensureIndexStores(env, table);
15761
+ let store = stores.get(spec.name);
15762
+ if (!store) {
15763
+ store = new IndexStore(spec.name, true);
15764
+ for (let i = 0; i < table.rowCount(); i++) {
15765
+ const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
15766
+ if (key !== null) store.insert(key, i);
15767
+ }
15768
+ stores.set(spec.name, store);
15769
+ }
15770
+ return store;
15771
+ }
15772
+ function indexInsertRow(env, table, rowIdx, row) {
15773
+ for (const spec of uniqueSpecsFor(env, table)) {
15774
+ const key = uniqueKeyOf(env, table, spec, row);
15775
+ if (key === null) continue;
15776
+ indexStoreFor(env, table, spec).insert(key, rowIdx);
15777
+ }
15778
+ }
15779
+ function indexRemoveRow(env, table, rowIdx, row) {
15780
+ if (!table.indexStores) return;
15781
+ for (const spec of uniqueSpecsFor(env, table)) {
15782
+ const key = uniqueKeyOf(env, table, spec, row);
15783
+ if (key === null) continue;
15784
+ table.indexStores.get(spec.name)?.remove(key, rowIdx);
15785
+ }
15786
+ }
15787
+ function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
15788
+ indexRemoveRow(env, table, rowIdx, oldRow);
15789
+ indexInsertRow(env, table, rowIdx, newRow);
15790
+ }
15791
+ function rebuildTableIndexes(env, table) {
15792
+ table.indexStores = null;
15793
+ ensureIndexStores(env, table);
15794
+ }
15795
+
15796
+ // src/constraints/enforce.ts
15797
+ function tableScope(table, row) {
15798
+ const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
15799
+ return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
15800
+ }
15801
+ function checkNotNull(env, table, row) {
15802
+ void env;
15803
+ for (let i = 0; i < table.columns.length; i++) {
15804
+ const c = table.columns[i];
15805
+ if (c.notNull && (row[i] ?? null) === null) {
15806
+ throw pgError(
15807
+ "not_null_violation",
15808
+ `null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
15809
+ "23502"
15810
+ );
15811
+ }
15812
+ }
15813
+ }
15814
+ function checkChecks(env, table, row) {
15815
+ for (const con of table.constraints) {
15816
+ if (con.kind !== "check") continue;
15817
+ const scope = makeEvalScope(env, tableScope(table, row));
15818
+ const v = evalExpr(env.ctx, scope, con.expr);
15819
+ if (v.v === null) continue;
15820
+ const b = castTo(env.ctx, v, "bool", {});
15821
+ if (b.v !== true) {
15822
+ throw pgError(
15823
+ "check_violation",
15824
+ `new row for relation "${table.name}" violates check constraint "${con.name}"`,
15825
+ "23514"
15826
+ );
15827
+ }
15828
+ }
15829
+ }
15830
+ function uniqueSpecsFor(env, table) {
15831
+ const specs = [];
15832
+ for (const con of table.constraints) {
15833
+ if (con.kind !== "primary_key" && con.kind !== "unique") continue;
15834
+ specs.push({
15835
+ name: con.name,
15836
+ keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
15837
+ columnNames: con.columns,
15838
+ nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
15839
+ where: null,
15840
+ isPrimary: con.kind === "primary_key"
15841
+ });
15842
+ }
15843
+ const schema = env.ctx.state.schemas.get(table.schema);
15844
+ if (schema) {
15845
+ for (const idx of schema.indexes.values()) {
15846
+ if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
15847
+ specs.push(uniqueSpecOfIndex(table, idx));
15848
+ }
15849
+ }
15850
+ return specs;
15851
+ }
15852
+ function uniqueSpecOfIndex(table, idx) {
15853
+ return {
15854
+ name: idx.name,
15855
+ keys: idx.columns.map((c) => ({
15856
+ colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
15857
+ expr: c.expr
15858
+ })),
15859
+ columnNames: idx.columns.map((c) => c.column ?? "expr"),
15860
+ nullsNotDistinct: idx.nullsNotDistinct,
15861
+ where: idx.where,
15862
+ isPrimary: false
15863
+ };
15864
+ }
15865
+ function uniqueKeyOf(env, table, spec, row) {
15866
+ if (spec.where) {
15867
+ const scope = makeEvalScope(env, tableScope(table, row));
15868
+ const v = evalExpr(env.ctx, scope, spec.where);
15869
+ if (v.v !== true) return null;
15870
+ }
15871
+ const parts = [];
15872
+ let hasNull = false;
15873
+ for (const k of spec.keys) {
15874
+ let value;
15875
+ let type;
15876
+ if (k.expr) {
15877
+ const scope = makeEvalScope(env, tableScope(table, row));
15878
+ const v = evalExpr(env.ctx, scope, k.expr);
15879
+ value = v.v;
15880
+ type = v.t === "unknown" ? "text" : v.t;
15881
+ } else {
15882
+ value = row[k.colIdx] ?? null;
15883
+ type = table.columns[k.colIdx].type.id;
15884
+ }
15885
+ if (value === null) {
15886
+ hasNull = true;
15887
+ parts.push("\0N");
15888
+ } else {
15889
+ parts.push(datumKey(type, value));
15890
+ }
15891
+ }
15892
+ if (hasNull && !spec.nullsNotDistinct) return null;
15893
+ return parts.join("");
15894
+ }
15895
+ function checkUnique(env, table, row, selfIdx) {
15896
+ for (const spec of uniqueSpecsFor(env, table)) {
15897
+ const key = uniqueKeyOf(env, table, spec, row);
15898
+ if (key === null) continue;
15899
+ indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
15900
+ }
15901
+ }
15902
+ function findConflict(env, table, spec, row) {
15903
+ const key = uniqueKeyOf(env, table, spec, row);
15904
+ if (key === null) return null;
15905
+ const hits = indexStoreFor(env, table, spec).lookup(key);
15906
+ return hits.length > 0 ? hits[0] : null;
15907
+ }
15908
+ function checkForeignKeys(env, table, row) {
15909
+ const state = env.ctx.state;
15910
+ for (const con of table.constraints) {
15911
+ if (con.kind !== "foreign_key") continue;
15912
+ const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
15913
+ const nulls = values.filter((v) => v === null).length;
15914
+ if (con.match === "simple" && nulls > 0) continue;
15915
+ if (con.match === "full") {
15916
+ if (nulls === values.length) continue;
15917
+ if (nulls > 0) {
15918
+ throw pgError(
15919
+ "constraint_foreign_key",
15920
+ `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
15921
+ "23503"
15922
+ );
15923
+ }
15924
+ }
15925
+ const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
15926
+ if (!refTable) {
15927
+ throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
15928
+ }
15929
+ const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
15930
+ const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
15931
+ const wanted = values.map((v, i) => {
15932
+ const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
15933
+ const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
15934
+ return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
15935
+ }).join("");
15936
+ let found = false;
15937
+ for (let i = 0; i < refTable.rowCount(); i++) {
15938
+ const r = refTable.rowAt(i);
15939
+ const key = refIdxs.map((ri, j) => {
15940
+ const v = r[ri] ?? null;
15941
+ return v === null ? "\0N" : datumKey(keyTypes[j], v);
15942
+ }).join("");
15943
+ if (key === wanted) {
15944
+ found = true;
15945
+ break;
15946
+ }
15947
+ }
15948
+ if (!found) {
15949
+ throw pgError(
15950
+ "constraint_foreign_key",
15951
+ `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
15952
+ "23503"
15953
+ );
15738
15954
  }
15739
15955
  }
15740
- table.indexStores = stores;
15741
- return stores;
15742
15956
  }
15743
- function indexStoreFor(env, table, spec) {
15744
- const stores = ensureIndexStores(env, table);
15745
- let store = stores.get(spec.name);
15746
- if (!store) {
15747
- store = new IndexStore(spec.name, true);
15748
- for (let i = 0; i < table.rowCount(); i++) {
15749
- const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
15750
- if (key !== null) store.insert(key, i);
15957
+ function referencingConstraints(env, table) {
15958
+ const out = [];
15959
+ for (const schema of env.ctx.state.schemas.values()) {
15960
+ for (const t of schema.tables.values()) {
15961
+ for (const con of t.constraints) {
15962
+ if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
15963
+ out.push({ table: t, constraint: con });
15964
+ }
15965
+ }
15751
15966
  }
15752
- stores.set(spec.name, store);
15753
- }
15754
- return store;
15755
- }
15756
- function indexInsertRow(env, table, rowIdx, row) {
15757
- for (const spec of uniqueSpecsFor(env, table)) {
15758
- const key = uniqueKeyOf(env, table, spec, row);
15759
- if (key === null) continue;
15760
- indexStoreFor(env, table, spec).insert(key, rowIdx);
15761
- }
15762
- }
15763
- function indexRemoveRow(env, table, rowIdx, row) {
15764
- if (!table.indexStores) return;
15765
- for (const spec of uniqueSpecsFor(env, table)) {
15766
- const key = uniqueKeyOf(env, table, spec, row);
15767
- if (key === null) continue;
15768
- table.indexStores.get(spec.name)?.remove(key, rowIdx);
15769
15967
  }
15770
- }
15771
- function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
15772
- indexRemoveRow(env, table, rowIdx, oldRow);
15773
- indexInsertRow(env, table, rowIdx, newRow);
15774
- }
15775
- function rebuildTableIndexes(env, table) {
15776
- table.indexStores = null;
15777
- ensureIndexStores(env, table);
15968
+ return out;
15778
15969
  }
15779
15970
 
15780
15971
  // src/planner/access.ts
@@ -17660,10 +17851,9 @@ var Parser = class {
17660
17851
  return this.parseCreateFunction(orReplace);
17661
17852
  }
17662
17853
  if (this.atKw("trigger") || this.atKw("constraint") && this.atKw("trigger", 1)) {
17663
- if (this.eatKw("constraint")) {
17664
- }
17854
+ const constraint = this.eatKw("constraint");
17665
17855
  this.expectKw("trigger");
17666
- return this.parseCreateTrigger(orReplace);
17856
+ return this.parseCreateTrigger(orReplace, constraint);
17667
17857
  }
17668
17858
  if (this.atKw("extension")) {
17669
17859
  this.pos++;
@@ -18484,7 +18674,7 @@ var Parser = class {
18484
18674
  strict: strict2
18485
18675
  };
18486
18676
  }
18487
- parseCreateTrigger(orReplace) {
18677
+ parseCreateTrigger(orReplace, constraint) {
18488
18678
  const name = this.ident();
18489
18679
  let timing;
18490
18680
  if (this.eatKw("before")) timing = "before";
@@ -18514,9 +18704,18 @@ var Parser = class {
18514
18704
  this.expectKw("on");
18515
18705
  const table = this.qualifiedName();
18516
18706
  if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
18517
- while (this.atKw("not") || this.atKw("deferrable") || this.atKw("initially")) {
18518
- this.pos++;
18519
- }
18707
+ let deferrable = false;
18708
+ let initiallyDeferred = false;
18709
+ if (this.eatKw("not")) this.expectKw("deferrable");
18710
+ else if (this.eatKw("deferrable")) deferrable = true;
18711
+ if (this.eatKw("initially")) {
18712
+ if (this.eatKw("deferred")) initiallyDeferred = true;
18713
+ else this.expectKw("immediate");
18714
+ }
18715
+ if ((deferrable || initiallyDeferred) && !constraint)
18716
+ this.errorAt(this.peek(), "only constraint triggers can be deferred");
18717
+ if (initiallyDeferred && !deferrable)
18718
+ this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
18520
18719
  let forEachRow = false;
18521
18720
  if (this.eatKw("for")) {
18522
18721
  this.eatKw("each");
@@ -18541,7 +18740,21 @@ var Parser = class {
18541
18740
  } while (this.eatPunct(","));
18542
18741
  }
18543
18742
  this.expectPunct(")");
18544
- return { type: "create_trigger", orReplace, name, timing, events, table, forEachRow, when, funcName, funcArgs };
18743
+ return {
18744
+ type: "create_trigger",
18745
+ constraint,
18746
+ deferrable,
18747
+ initiallyDeferred,
18748
+ orReplace,
18749
+ name,
18750
+ timing,
18751
+ events,
18752
+ table,
18753
+ forEachRow,
18754
+ when,
18755
+ funcName,
18756
+ funcArgs
18757
+ };
18545
18758
  }
18546
18759
  // --- ALTER --------------------------------------------------------------------
18547
18760
  parseAlter() {
@@ -18886,9 +19099,15 @@ var Parser = class {
18886
19099
  this.skipToStatementEnd();
18887
19100
  return { type: "no_op", what: "SET TRANSACTION" };
18888
19101
  }
18889
- if (this.atKw("constraints")) {
18890
- this.skipToStatementEnd();
18891
- return { type: "no_op", what: "SET CONSTRAINTS" };
19102
+ if (this.eatKw("constraints")) {
19103
+ const names = this.eatKw("all") ? null : [];
19104
+ if (names)
19105
+ do {
19106
+ names.push(this.qualifiedName());
19107
+ } while (this.eatPunct(","));
19108
+ const deferred = this.eatKw("deferred");
19109
+ if (!deferred) this.expectKw("immediate");
19110
+ return { type: "set_constraints", names, deferred };
18892
19111
  }
18893
19112
  if (this.atKw("role") || this.atKw("session", 0) && this.atKw("authorization", 1)) {
18894
19113
  this.skipToStatementEnd();
@@ -21263,12 +21482,12 @@ function executeRecursiveCte(env, cte, query) {
21263
21482
  table: null
21264
21483
  }));
21265
21484
  const seen = /* @__PURE__ */ new Set();
21266
- const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
21485
+ const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
21267
21486
  let result = [];
21268
21487
  let working = [];
21269
21488
  for (const row of base.rows) {
21270
21489
  if (!body.all) {
21271
- const k = keyOf(row);
21490
+ const k = keyOf2(row);
21272
21491
  if (seen.has(k)) continue;
21273
21492
  seen.add(k);
21274
21493
  }
@@ -21301,7 +21520,7 @@ function executeRecursiveCte(env, cte, query) {
21301
21520
  return castTo(env.ctx, tv(srcT === UNKNOWN ? "text" : srcT, v), columns[i].type, {}).v;
21302
21521
  });
21303
21522
  if (!body.all) {
21304
- const k = keyOf(row);
21523
+ const k = keyOf2(row);
21305
21524
  if (seen.has(k)) continue;
21306
21525
  seen.add(k);
21307
21526
  }
@@ -22481,7 +22700,7 @@ function setOpRelation(env, op) {
22481
22700
  );
22482
22701
  const lrows = normalize(left);
22483
22702
  const rrows = normalize(right);
22484
- const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
22703
+ const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
22485
22704
  let rows;
22486
22705
  switch (op.op) {
22487
22706
  case "union": {
@@ -22489,7 +22708,7 @@ function setOpRelation(env, op) {
22489
22708
  if (!op.all) {
22490
22709
  const seen = /* @__PURE__ */ new Set();
22491
22710
  rows = rows.filter((r) => {
22492
- const k = keyOf(r);
22711
+ const k = keyOf2(r);
22493
22712
  if (seen.has(k)) return false;
22494
22713
  seen.add(k);
22495
22714
  return true;
@@ -22500,13 +22719,13 @@ function setOpRelation(env, op) {
22500
22719
  case "intersect": {
22501
22720
  const rightCounts = /* @__PURE__ */ new Map();
22502
22721
  for (const r of rrows) {
22503
- const k = keyOf(r);
22722
+ const k = keyOf2(r);
22504
22723
  rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
22505
22724
  }
22506
22725
  rows = [];
22507
22726
  const emitted = /* @__PURE__ */ new Set();
22508
22727
  for (const r of lrows) {
22509
- const k = keyOf(r);
22728
+ const k = keyOf2(r);
22510
22729
  const avail = rightCounts.get(k) ?? 0;
22511
22730
  if (avail <= 0) continue;
22512
22731
  if (op.all) {
@@ -22522,13 +22741,13 @@ function setOpRelation(env, op) {
22522
22741
  case "except": {
22523
22742
  const rightCounts = /* @__PURE__ */ new Map();
22524
22743
  for (const r of rrows) {
22525
- const k = keyOf(r);
22744
+ const k = keyOf2(r);
22526
22745
  rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
22527
22746
  }
22528
22747
  rows = [];
22529
22748
  const emitted = /* @__PURE__ */ new Set();
22530
22749
  for (const r of lrows) {
22531
- const k = keyOf(r);
22750
+ const k = keyOf2(r);
22532
22751
  if (op.all) {
22533
22752
  const avail = rightCounts.get(k) ?? 0;
22534
22753
  if (avail > 0) {
@@ -22976,207 +23195,54 @@ function executeCore(env0, core, orderBy, opts) {
22976
23195
  }).join("");
22977
23196
  if (seen.has(k)) return false;
22978
23197
  seen.add(k);
22979
- return true;
22980
- });
22981
- }
22982
- }
22983
- rel3 = stripHidden(rel3);
22984
- return rel3;
22985
- }
22986
- function executeSelectStmt(env0, stmt) {
22987
- const env = applyWith(env0, stmt.with);
22988
- let rel3;
22989
- if (stmt.body.type === "select_core") {
22990
- rel3 = executeCore(env, stmt.body, stmt.orderBy, { hasLimit: stmt.limit !== null });
22991
- } else {
22992
- rel3 = executeBody(env, stmt.body);
22993
- const specs = outputOrderSpecs(env, rel3, stmt.orderBy);
22994
- if (specs.length > 0) sortRows(env, rel3, specs);
22995
- rel3 = stripHidden(rel3);
22996
- }
22997
- if (stmt.limitWithTies) {
22998
- if (stmt.orderBy.length === 0) {
22999
- throw pgError("syntax", "WITH TIES cannot be specified without ORDER BY clause", "42601");
23000
- }
23001
- const tieSpecs = outputOrderSpecs(env, rel3, stmt.orderBy);
23002
- return stripHidden(applyLimitOffset(env, rel3, stmt.limit, stmt.offset, tieSpecs));
23003
- }
23004
- return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
23005
- }
23006
-
23007
- // src/constraints/enforce.ts
23008
- function tableScope(table, row) {
23009
- const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
23010
- return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
23011
- }
23012
- function checkNotNull(env, table, row) {
23013
- void env;
23014
- for (let i = 0; i < table.columns.length; i++) {
23015
- const c = table.columns[i];
23016
- if (c.notNull && (row[i] ?? null) === null) {
23017
- throw pgError(
23018
- "not_null_violation",
23019
- `null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
23020
- "23502"
23021
- );
23022
- }
23023
- }
23024
- }
23025
- function checkChecks(env, table, row) {
23026
- for (const con of table.constraints) {
23027
- if (con.kind !== "check") continue;
23028
- const scope = makeEvalScope(env, tableScope(table, row));
23029
- const v = evalExpr(env.ctx, scope, con.expr);
23030
- if (v.v === null) continue;
23031
- const b = castTo(env.ctx, v, "bool", {});
23032
- if (b.v !== true) {
23033
- throw pgError(
23034
- "check_violation",
23035
- `new row for relation "${table.name}" violates check constraint "${con.name}"`,
23036
- "23514"
23037
- );
23038
- }
23039
- }
23040
- }
23041
- function uniqueSpecsFor(env, table) {
23042
- const specs = [];
23043
- for (const con of table.constraints) {
23044
- if (con.kind !== "primary_key" && con.kind !== "unique") continue;
23045
- specs.push({
23046
- name: con.name,
23047
- keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
23048
- columnNames: con.columns,
23049
- nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
23050
- where: null,
23051
- isPrimary: con.kind === "primary_key"
23052
- });
23053
- }
23054
- const schema = env.ctx.state.schemas.get(table.schema);
23055
- if (schema) {
23056
- for (const idx of schema.indexes.values()) {
23057
- if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
23058
- specs.push(uniqueSpecOfIndex(table, idx));
23059
- }
23060
- }
23061
- return specs;
23062
- }
23063
- function uniqueSpecOfIndex(table, idx) {
23064
- return {
23065
- name: idx.name,
23066
- keys: idx.columns.map((c) => ({
23067
- colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
23068
- expr: c.expr
23069
- })),
23070
- columnNames: idx.columns.map((c) => c.column ?? "expr"),
23071
- nullsNotDistinct: idx.nullsNotDistinct,
23072
- where: idx.where,
23073
- isPrimary: false
23074
- };
23075
- }
23076
- function uniqueKeyOf(env, table, spec, row) {
23077
- if (spec.where) {
23078
- const scope = makeEvalScope(env, tableScope(table, row));
23079
- const v = evalExpr(env.ctx, scope, spec.where);
23080
- if (v.v !== true) return null;
23081
- }
23082
- const parts = [];
23083
- let hasNull = false;
23084
- for (const k of spec.keys) {
23085
- let value;
23086
- let type;
23087
- if (k.expr) {
23088
- const scope = makeEvalScope(env, tableScope(table, row));
23089
- const v = evalExpr(env.ctx, scope, k.expr);
23090
- value = v.v;
23091
- type = v.t === "unknown" ? "text" : v.t;
23092
- } else {
23093
- value = row[k.colIdx] ?? null;
23094
- type = table.columns[k.colIdx].type.id;
23095
- }
23096
- if (value === null) {
23097
- hasNull = true;
23098
- parts.push("\0N");
23099
- } else {
23100
- parts.push(datumKey(type, value));
23101
- }
23102
- }
23103
- if (hasNull && !spec.nullsNotDistinct) return null;
23104
- return parts.join("");
23105
- }
23106
- function checkUnique(env, table, row, selfIdx) {
23107
- for (const spec of uniqueSpecsFor(env, table)) {
23108
- const key = uniqueKeyOf(env, table, spec, row);
23109
- if (key === null) continue;
23110
- indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
23111
- }
23112
- }
23113
- function findConflict(env, table, spec, row) {
23114
- const key = uniqueKeyOf(env, table, spec, row);
23115
- if (key === null) return null;
23116
- const hits = indexStoreFor(env, table, spec).lookup(key);
23117
- return hits.length > 0 ? hits[0] : null;
23118
- }
23119
- function checkForeignKeys(env, table, row) {
23120
- const state = env.ctx.state;
23121
- for (const con of table.constraints) {
23122
- if (con.kind !== "foreign_key") continue;
23123
- const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
23124
- const nulls = values.filter((v) => v === null).length;
23125
- if (con.match === "simple" && nulls > 0) continue;
23126
- if (con.match === "full") {
23127
- if (nulls === values.length) continue;
23128
- if (nulls > 0) {
23129
- throw pgError(
23130
- "constraint_foreign_key",
23131
- `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
23132
- "23503"
23133
- );
23134
- }
23135
- }
23136
- const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
23137
- if (!refTable) {
23138
- throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
23139
- }
23140
- const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
23141
- const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
23142
- const wanted = values.map((v, i) => {
23143
- const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
23144
- const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
23145
- return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
23146
- }).join("");
23147
- let found = false;
23148
- for (let i = 0; i < refTable.rowCount(); i++) {
23149
- const r = refTable.rowAt(i);
23150
- const key = refIdxs.map((ri, j) => {
23151
- const v = r[ri] ?? null;
23152
- return v === null ? "\0N" : datumKey(keyTypes[j], v);
23153
- }).join("");
23154
- if (key === wanted) {
23155
- found = true;
23156
- break;
23157
- }
23158
- }
23159
- if (!found) {
23160
- throw pgError(
23161
- "constraint_foreign_key",
23162
- `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
23163
- "23503"
23164
- );
23198
+ return true;
23199
+ });
23165
23200
  }
23166
23201
  }
23202
+ rel3 = stripHidden(rel3);
23203
+ return rel3;
23167
23204
  }
23168
- function referencingConstraints(env, table) {
23169
- const out = [];
23170
- for (const schema of env.ctx.state.schemas.values()) {
23171
- for (const t of schema.tables.values()) {
23172
- for (const con of t.constraints) {
23173
- if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
23174
- out.push({ table: t, constraint: con });
23175
- }
23176
- }
23205
+ function executeSelectStmt(env0, stmt) {
23206
+ const env = applyWith(env0, stmt.with);
23207
+ let rel3;
23208
+ if (stmt.body.type === "select_core") {
23209
+ rel3 = executeCore(env, stmt.body, stmt.orderBy, { hasLimit: stmt.limit !== null });
23210
+ } else {
23211
+ rel3 = executeBody(env, stmt.body);
23212
+ const specs = outputOrderSpecs(env, rel3, stmt.orderBy);
23213
+ if (specs.length > 0) sortRows(env, rel3, specs);
23214
+ rel3 = stripHidden(rel3);
23215
+ }
23216
+ if (stmt.limitWithTies) {
23217
+ if (stmt.orderBy.length === 0) {
23218
+ throw pgError("syntax", "WITH TIES cannot be specified without ORDER BY clause", "42601");
23177
23219
  }
23220
+ const tieSpecs = outputOrderSpecs(env, rel3, stmt.orderBy);
23221
+ return stripHidden(applyLimitOffset(env, rel3, stmt.limit, stmt.offset, tieSpecs));
23178
23222
  }
23179
- return out;
23223
+ return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
23224
+ }
23225
+
23226
+ // src/executor/triggers-scope.ts
23227
+ function triggerScope(table, newRow, oldRow) {
23228
+ const cols = [];
23229
+ const row = [];
23230
+ const rangeVars = /* @__PURE__ */ new Set();
23231
+ if (newRow) {
23232
+ rangeVars.add("new");
23233
+ for (let i = 0; i < table.columns.length; i++) {
23234
+ cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
23235
+ row.push(newRow[i] ?? null);
23236
+ }
23237
+ }
23238
+ if (oldRow) {
23239
+ rangeVars.add("old");
23240
+ for (let i = 0; i < table.columns.length; i++) {
23241
+ cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
23242
+ row.push(oldRow[i] ?? null);
23243
+ }
23244
+ }
23245
+ return new RowScope(cols, row, null, rangeVars);
23180
23246
  }
23181
23247
 
23182
23248
  // src/executor/triggers.ts
@@ -23192,6 +23258,18 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
23192
23258
  for (const t of table.triggers) {
23193
23259
  if (t.timing !== timing || !matches2(t, event) || !t.forEachRow) continue;
23194
23260
  if (!executor) throw unsupported(`trigger "${t.name}" execution`);
23261
+ if (t.constraint && timing === "after") {
23262
+ if (t.when && !evalPredicate(env, triggerScope(table, newRow, oldRow), t.when)) continue;
23263
+ env.ctx.state.pendingTriggers.push({
23264
+ schema: table.schema,
23265
+ table: table.name,
23266
+ trigger: t.name,
23267
+ event,
23268
+ oldRow: oldRow?.slice() ?? null,
23269
+ newRow: newRow?.slice() ?? null
23270
+ });
23271
+ continue;
23272
+ }
23195
23273
  const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
23196
23274
  if (timing === "before") {
23197
23275
  if (result === null) return { row: null };
@@ -23200,6 +23278,167 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
23200
23278
  }
23201
23279
  return { row };
23202
23280
  }
23281
+ var keyOf = (schema, name) => JSON.stringify([schema, name]);
23282
+ function flushConstraintTriggers(state, all = false) {
23283
+ const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
23284
+ for (let i = 0; i < state.pendingTriggers.length; ) {
23285
+ const pending = state.pendingTriggers[i];
23286
+ const table = state.schemas.get(pending.schema)?.tables.get(pending.table);
23287
+ const trigger = table?.triggers.find((t) => t.name === pending.trigger);
23288
+ if (!table || !trigger) {
23289
+ state.pendingTriggers.splice(i, 1);
23290
+ continue;
23291
+ }
23292
+ const deferred = trigger.deferrable && (state.constraintModes.get(keyOf(table.schema, trigger.name)) ?? trigger.initiallyDeferred);
23293
+ if (!all && state.inTransaction && deferred) {
23294
+ i++;
23295
+ continue;
23296
+ }
23297
+ if (!executor) throw unsupported(`trigger "${trigger.name}" execution`);
23298
+ state.pendingTriggers.splice(i, 1);
23299
+ const changes = state.changes;
23300
+ try {
23301
+ executor(env, table, { ...trigger, when: null }, pending.event, pending.oldRow, pending.newRow);
23302
+ } catch (error) {
23303
+ state.pendingTriggers.splice(i, 0, pending);
23304
+ throw error;
23305
+ } finally {
23306
+ state.changes = changes;
23307
+ }
23308
+ }
23309
+ }
23310
+ function setConstraints(state, stmt) {
23311
+ if (!state.inTransaction) return;
23312
+ const matches3 = [];
23313
+ if (stmt.names) {
23314
+ for (const name of stmt.names) {
23315
+ const schemas = name.length > 1 ? [name[name.length - 2]] : state.effectiveSearchPath();
23316
+ let found = false;
23317
+ for (const schema of schemas) {
23318
+ const items = [...state.schemas.get(schema)?.tables.values() ?? []].flatMap(
23319
+ (table) => table.triggers.filter((t) => t.constraint && t.name === name[name.length - 1])
23320
+ );
23321
+ if (items.length) {
23322
+ for (const trigger of items) matches3.push({ schema, trigger });
23323
+ found = true;
23324
+ break;
23325
+ }
23326
+ }
23327
+ if (!found) throw pgError("undefined_object", `constraint "${name.join(".")}" does not exist`, "42704");
23328
+ }
23329
+ } else {
23330
+ for (const schema of state.schemas.values())
23331
+ for (const table of schema.tables.values())
23332
+ for (const trigger of table.triggers)
23333
+ if (trigger.constraint && trigger.deferrable) matches3.push({ schema: schema.name, trigger });
23334
+ }
23335
+ for (const { trigger } of matches3)
23336
+ if (stmt.deferred && !trigger.deferrable)
23337
+ throw pgError("misuse", `constraint "${trigger.name}" is not deferrable`, "55000");
23338
+ const previous = new Map(state.constraintModes);
23339
+ for (const { schema, trigger } of matches3) state.constraintModes.set(keyOf(schema, trigger.name), stmt.deferred);
23340
+ try {
23341
+ if (!stmt.deferred) flushConstraintTriggers(state);
23342
+ } catch (error) {
23343
+ state.constraintModes = previous;
23344
+ throw error;
23345
+ }
23346
+ }
23347
+
23348
+ // src/transactions/manager.ts
23349
+ var TransactionManager = class {
23350
+ constructor(state) {
23351
+ this.state = state;
23352
+ }
23353
+ state;
23354
+ base = null;
23355
+ savepoints = [];
23356
+ freezeDepth = 0;
23357
+ get inTransaction() {
23358
+ return this.base !== null;
23359
+ }
23360
+ takeSnapshot() {
23361
+ this.state.freezeShared();
23362
+ this.freezeDepth++;
23363
+ return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
23364
+ }
23365
+ restore(snap) {
23366
+ this.state.restoreFrom(snap.state);
23367
+ this.state.prng.setState(snap.prngState);
23368
+ }
23369
+ thawOnce() {
23370
+ if (this.freezeDepth > 0) {
23371
+ this.state.thawShared();
23372
+ this.freezeDepth--;
23373
+ }
23374
+ }
23375
+ begin() {
23376
+ if (this.base !== null) {
23377
+ return;
23378
+ }
23379
+ this.base = this.takeSnapshot();
23380
+ this.state.inTransaction = true;
23381
+ assert(this.freezeDepth === 1, "BEGIN freeze depth");
23382
+ }
23383
+ commit() {
23384
+ try {
23385
+ flushConstraintTriggers(this.state, true);
23386
+ } catch (error) {
23387
+ this.rollback();
23388
+ throw error;
23389
+ }
23390
+ this.base = null;
23391
+ this.savepoints = [];
23392
+ this.state.inTransaction = false;
23393
+ this.state.localSettings.clear();
23394
+ this.state.pendingTriggers = [];
23395
+ this.state.constraintModes.clear();
23396
+ while (this.freezeDepth > 0) this.thawOnce();
23397
+ }
23398
+ rollback() {
23399
+ if (this.base !== null) {
23400
+ this.restore(this.base);
23401
+ }
23402
+ this.base = null;
23403
+ this.savepoints = [];
23404
+ this.state.inTransaction = false;
23405
+ this.state.localSettings.clear();
23406
+ this.state.pendingTriggers = [];
23407
+ this.state.constraintModes.clear();
23408
+ while (this.freezeDepth > 0) this.thawOnce();
23409
+ }
23410
+ savepoint(name) {
23411
+ if (this.base === null) {
23412
+ throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
23413
+ }
23414
+ this.savepoints.push({ name, snap: this.takeSnapshot() });
23415
+ }
23416
+ releaseSavepoint(name) {
23417
+ const idx = this.findSavepoint(name);
23418
+ this.savepoints.splice(idx);
23419
+ }
23420
+ rollbackToSavepoint(name) {
23421
+ const idx = this.findSavepoint(name);
23422
+ const sp = this.savepoints[idx];
23423
+ this.restore(sp.snap);
23424
+ this.savepoints.splice(idx + 1);
23425
+ }
23426
+ findSavepoint(name) {
23427
+ if (this.base === null) {
23428
+ throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
23429
+ }
23430
+ for (let i = this.savepoints.length - 1; i >= 0; i--) {
23431
+ if (this.savepoints[i].name === name) return i;
23432
+ }
23433
+ throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
23434
+ }
23435
+ reset() {
23436
+ this.base = null;
23437
+ this.savepoints = [];
23438
+ this.state.inTransaction = false;
23439
+ this.freezeDepth = 0;
23440
+ }
23441
+ };
23203
23442
 
23204
23443
  // src/executor/dml.ts
23205
23444
  function withStatementRollback(env, fn) {
@@ -23889,6 +24128,7 @@ function executeSet(env, stmt) {
23889
24128
  else target.delete(name);
23890
24129
  return commandResult("SET", 0);
23891
24130
  }
24131
+ validateStorageSetting(name, stmt.value);
23892
24132
  target.set(name, stmt.value);
23893
24133
  return commandResult("SET", 0);
23894
24134
  }
@@ -24194,64 +24434,6 @@ function executeCopyFromData(env, stmt, data) {
24194
24434
  };
24195
24435
  }
24196
24436
 
24197
- // src/expressions/context.ts
24198
- var EngineCtx = class {
24199
- state;
24200
- /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
24201
- txNow;
24202
- /** statement timestamp */
24203
- stmtNow;
24204
- constructor(state, txNow) {
24205
- this.state = state;
24206
- const nowMs = state.clock().getTime();
24207
- const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
24208
- this.txNow = txNow ?? nowMicros;
24209
- this.stmtNow = nowMicros;
24210
- }
24211
- timezone() {
24212
- return this.state.getSetting("timezone") ?? "UTC";
24213
- }
24214
- namespaceOid(name) {
24215
- const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
24216
- if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
24217
- return oid;
24218
- }
24219
- namespaceName(oid) {
24220
- const builtin = builtinNamespaceName(oid);
24221
- if (builtin !== void 0) return builtin;
24222
- for (const schema of this.state.schemas.values()) {
24223
- if (schema.oid === oid) return schema.name;
24224
- }
24225
- return null;
24226
- }
24227
- zoneOffsetAt(utcMicros) {
24228
- return zoneOffsetAtUtc(this.timezone(), utcMicros);
24229
- }
24230
- zoneOffsetForNaive(naiveMicros) {
24231
- return zoneOffsetForNaive(this.timezone(), naiveMicros);
24232
- }
24233
- enumLabels(enumType) {
24234
- if (!isEnumType(enumType)) return null;
24235
- const e = this.state.findEnumByKey(enumType.slice(5));
24236
- return e?.labels ?? null;
24237
- }
24238
- enumHasLabel(enumType, label) {
24239
- const labels = this.enumLabels(enumType);
24240
- return labels === null ? true : labels.includes(label);
24241
- }
24242
- enumOrder(enumType, label) {
24243
- const labels = this.enumLabels(enumType);
24244
- if (!labels) return 0;
24245
- const idx = labels.indexOf(label);
24246
- return idx === -1 ? labels.length : idx;
24247
- }
24248
- requireEnum(enumType) {
24249
- const labels = this.enumLabels(enumType);
24250
- if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
24251
- return labels;
24252
- }
24253
- };
24254
-
24255
24437
  // src/runtime/clock.ts
24256
24438
  var DEFAULT_NOW = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
24257
24439
  function fixedClock(instant = DEFAULT_NOW) {
@@ -25219,7 +25401,7 @@ function pgClass(ctx) {
25219
25401
  v.materialized ? "m" : "v",
25220
25402
  v.columns?.length ?? 0,
25221
25403
  v.matRows?.length ?? 0,
25222
- false,
25404
+ v.materialized && [...schemaData.indexes.values()].some((i) => i.tableOid === v.oid),
25223
25405
  !v.materialized || v.matRows !== null,
25224
25406
  relPersistence(v)
25225
25407
  );
@@ -25485,7 +25667,7 @@ function pgIndex(ctx) {
25485
25667
  const valid = i.valid !== false;
25486
25668
  rows.push([
25487
25669
  i.oid ?? 0,
25488
- table?.oid ?? 0,
25670
+ table?.oid ?? i.tableOid ?? 0,
25489
25671
  indexAttributeCount(i),
25490
25672
  i.columns.length,
25491
25673
  i.unique,
@@ -25657,7 +25839,7 @@ function pgStatUserIndexes(ctx) {
25657
25839
  for (const index of schema.indexes.values()) {
25658
25840
  const table = schema.tables.get(index.table);
25659
25841
  rows.push([
25660
- table?.oid ?? 0,
25842
+ table?.oid ?? index.tableOid ?? 0,
25661
25843
  index.oid ?? 0,
25662
25844
  schema.name,
25663
25845
  index.table,
@@ -26252,26 +26434,6 @@ function runTriggerBody(env, table, stmts, vars) {
26252
26434
  }
26253
26435
  }
26254
26436
  }
26255
- function triggerScope(table, newRow, oldRow) {
26256
- const cols = [];
26257
- const row = [];
26258
- const rangeVars = /* @__PURE__ */ new Set();
26259
- if (newRow) {
26260
- rangeVars.add("new");
26261
- for (let i = 0; i < table.columns.length; i++) {
26262
- cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
26263
- row.push(newRow[i] ?? null);
26264
- }
26265
- }
26266
- if (oldRow) {
26267
- rangeVars.add("old");
26268
- for (let i = 0; i < table.columns.length; i++) {
26269
- cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
26270
- row.push(oldRow[i] ?? null);
26271
- }
26272
- }
26273
- return new RowScope(cols, row, null, rangeVars);
26274
- }
26275
26437
  function executeTrigger(env, table, trigger, _event, oldRow, newRow) {
26276
26438
  const state = env.ctx.state;
26277
26439
  if (trigger.when !== null && !evalPredicate(env, triggerScope(table, newRow, oldRow), trigger.when)) {
@@ -27238,7 +27400,8 @@ function executeCreateTableAs(env, stmt) {
27238
27400
  }
27239
27401
  function executeCreateIndex(env, stmt) {
27240
27402
  const state = env.ctx.state;
27241
- const table = state.findTable(stmt.table);
27403
+ const view = state.findView(stmt.table);
27404
+ const table = state.findTable(stmt.table) ?? (view?.materialized ? materializedTable(view) : void 0);
27242
27405
  if (!table) {
27243
27406
  throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
27244
27407
  }
@@ -27350,13 +27513,22 @@ function executeRefreshMatView(env, stmt) {
27350
27513
  if (!found?.materialized) {
27351
27514
  throw pgError("undefined_table", `materialized view "${stmt.name.join(".")}" does not exist`, "42P01");
27352
27515
  }
27516
+ const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), found.query);
27517
+ const next = {
27518
+ ...found,
27519
+ matColumns: rel3.columns.map((c, i) => ({
27520
+ name: found.columns?.[i] ?? c.name,
27521
+ type: c.type === UNKNOWN ? "text" : c.type
27522
+ })),
27523
+ matRows: stmt.withData ? rel3.rows : null
27524
+ };
27525
+ const table = materializedTable(next);
27526
+ for (const index of env.ctx.state.getSchema(found.schema).indexes.values()) {
27527
+ if (index.tableOid === found.oid && index.valid !== false) assertUniqueIndexBuildable(env, table, index);
27528
+ }
27353
27529
  const view = env.ctx.state.ensureWritableView(found);
27354
- const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), view.query);
27355
- view.matColumns = rel3.columns.map((c, i) => ({
27356
- name: view.columns?.[i] ?? c.name,
27357
- type: c.type === UNKNOWN ? "text" : c.type
27358
- }));
27359
- view.matRows = stmt.withData ? rel3.rows : null;
27530
+ view.matColumns = next.matColumns;
27531
+ view.matRows = next.matRows;
27360
27532
  return commandResult("REFRESH MATERIALIZED VIEW", 0);
27361
27533
  }
27362
27534
  function executeCreateSchema(env, stmt) {
@@ -27498,6 +27670,8 @@ function executeCreateFunction(env, stmt) {
27498
27670
  return commandResult("CREATE FUNCTION", 0);
27499
27671
  }
27500
27672
  function executeCreateTrigger(env, stmt) {
27673
+ if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
27674
+ throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
27501
27675
  const state = env.ctx.state;
27502
27676
  const found = state.findTable(stmt.table);
27503
27677
  if (!found) {
@@ -27516,6 +27690,9 @@ function executeCreateTrigger(env, stmt) {
27516
27690
  }
27517
27691
  const meta = {
27518
27692
  name: stmt.name,
27693
+ constraint: stmt.constraint,
27694
+ deferrable: stmt.deferrable,
27695
+ initiallyDeferred: stmt.initiallyDeferred,
27519
27696
  timing: stmt.timing,
27520
27697
  events: stmt.events,
27521
27698
  forEachRow: stmt.forEachRow,
@@ -28284,7 +28461,16 @@ function alterTableIsSelfContained(stmt) {
28284
28461
  return stmt.actions.length === 1 && action !== void 0 && SELF_CONTAINED_ALTER_ACTIONS.has(action.kind);
28285
28462
  }
28286
28463
  function executeStatement(env, stmt) {
28287
- if (!RESHAPES_TABLES.has(stmt.type)) return dispatchStatement(env, stmt);
28464
+ if (!RESHAPES_TABLES.has(stmt.type)) {
28465
+ 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))) {
28466
+ return withStatementRollback(env, () => {
28467
+ const result = dispatchStatement(env, stmt);
28468
+ flushConstraintTriggers(env.ctx.state);
28469
+ return result;
28470
+ });
28471
+ }
28472
+ return dispatchStatement(env, stmt);
28473
+ }
28288
28474
  try {
28289
28475
  return dispatchStatement(env, stmt);
28290
28476
  } finally {
@@ -28343,6 +28529,9 @@ function dispatchStatement(env, stmt) {
28343
28529
  return executeRefreshMatView(env, stmt);
28344
28530
  case "transaction":
28345
28531
  return executeTransaction(env, stmt);
28532
+ case "set_constraints":
28533
+ setConstraints(env.ctx.state, stmt);
28534
+ return commandResult("SET CONSTRAINTS", 0);
28346
28535
  case "set":
28347
28536
  return executeSet(env, stmt);
28348
28537
  case "show":