@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/index.js CHANGED
@@ -254,6 +254,13 @@ function isPostgresError(err) {
254
254
  return typeof sqlState === "string" && /^[0-9A-Z]{5}$/.test(sqlState);
255
255
  }
256
256
 
257
+ // src/storage/guc.ts
258
+ function validateStorageSetting(name, value) {
259
+ if (name === "default_table_access_method" && value !== "heap" || name === "default_tablespace" && value !== "" && value !== "pg_default") {
260
+ throw pgError("invalid_parameter_value", `invalid value for parameter "${name}": "${value}"`, "22023");
261
+ }
262
+ }
263
+
257
264
  // src/runtime/prng.ts
258
265
  var Prng = class _Prng {
259
266
  state;
@@ -3708,6 +3715,9 @@ var DatabaseState = class _DatabaseState {
3708
3715
  changes = 0;
3709
3716
  oidCounter = 16384;
3710
3717
  inTransaction = false;
3718
+ /** Transaction-only events: included in rollback clones, never in exported snapshots. */
3719
+ pendingTriggers = [];
3720
+ constraintModes = /* @__PURE__ */ new Map();
3711
3721
  /** most recent sequence touched by nextval/setval, for lastval() */
3712
3722
  lastSequence = null;
3713
3723
  /** Session currval per sequence; absent entry means currval is undefined. */
@@ -3750,6 +3760,8 @@ var DatabaseState = class _DatabaseState {
3750
3760
  this.settings.set("row_security", "on");
3751
3761
  this.settings.set("array_nulls", "on");
3752
3762
  this.settings.set("backslash_quote", "safe_encoding");
3763
+ this.settings.set("default_tablespace", "");
3764
+ this.settings.set("default_table_access_method", "heap");
3753
3765
  }
3754
3766
  nextOid() {
3755
3767
  return this.oidCounter++;
@@ -3909,6 +3921,8 @@ var DatabaseState = class _DatabaseState {
3909
3921
  s.prepared = new Map(this.prepared);
3910
3922
  s.changes = this.changes;
3911
3923
  s.inTransaction = this.inTransaction;
3924
+ s.pendingTriggers = this.pendingTriggers.slice();
3925
+ s.constraintModes = new Map(this.constraintModes);
3912
3926
  s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
3913
3927
  s.sequenceCurrval = new Map(this.sequenceCurrval);
3914
3928
  s.tableStats = new Map(this.tableStats);
@@ -3926,6 +3940,8 @@ var DatabaseState = class _DatabaseState {
3926
3940
  s.prepared = new Map(this.prepared);
3927
3941
  s.changes = this.changes;
3928
3942
  s.inTransaction = this.inTransaction;
3943
+ s.pendingTriggers = this.pendingTriggers.slice();
3944
+ s.constraintModes = new Map(this.constraintModes);
3929
3945
  s.lastSequence = this.lastSequence ? { ...this.lastSequence } : null;
3930
3946
  s.sequenceCurrval = new Map(this.sequenceCurrval);
3931
3947
  s.tableStats = new Map(this.tableStats);
@@ -3998,6 +4014,8 @@ var DatabaseState = class _DatabaseState {
3998
4014
  this.settings = other.settings;
3999
4015
  this.localSettings = other.localSettings;
4000
4016
  this.prepared = other.prepared;
4017
+ this.pendingTriggers = other.pendingTriggers.slice();
4018
+ this.constraintModes = new Map(other.constraintModes);
4001
4019
  this.changes = other.changes;
4002
4020
  this.lastSequence = other.lastSequence;
4003
4021
  this.sequenceCurrval = other.sequenceCurrval;
@@ -4014,90 +4032,189 @@ var DatabaseState = class _DatabaseState {
4014
4032
  }
4015
4033
  };
4016
4034
 
4017
- // src/transactions/manager.ts
4018
- var TransactionManager = class {
4019
- constructor(state) {
4020
- this.state = state;
4035
+ // src/types/timezone.ts
4036
+ var intlCache = /* @__PURE__ */ new Map();
4037
+ function intlFor(zone) {
4038
+ const cached = intlCache.get(zone);
4039
+ if (cached) return cached;
4040
+ try {
4041
+ const fmt = new Intl.DateTimeFormat("en-US", {
4042
+ timeZone: zone,
4043
+ timeZoneName: "longOffset",
4044
+ year: "numeric"
4045
+ });
4046
+ intlCache.set(zone, fmt);
4047
+ return fmt;
4048
+ } catch {
4049
+ return null;
4050
+ }
4051
+ }
4052
+ function parseFixedOffset(zone) {
4053
+ const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
4054
+ if (!m) return null;
4055
+ const sign = m[1] === "-" ? -1 : 1;
4056
+ return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
4057
+ }
4058
+ function parsePosixOffset(zone) {
4059
+ const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
4060
+ if (!m) return null;
4061
+ const sign = m[1] === "-" ? 1 : -1;
4062
+ return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
4063
+ }
4064
+ function pgMicrosToUnixMs(utcMicros) {
4065
+ return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
4066
+ }
4067
+ function zoneOffsetAtUtc(zone, utcMicros) {
4068
+ const z = zone.trim();
4069
+ const lower = z.toLowerCase();
4070
+ if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
4071
+ const posix = parsePosixOffset(z);
4072
+ if (posix !== null) return posix;
4073
+ const fixed = parseFixedOffset(z);
4074
+ if (fixed !== null) return fixed;
4075
+ const fmt = intlFor(z);
4076
+ if (fmt) {
4077
+ const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
4078
+ const parts = fmt.formatToParts(new Date(ms));
4079
+ const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
4080
+ const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
4081
+ if (!m) return 0;
4082
+ const sign = m[1] === "-" ? -1 : 1;
4083
+ return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
4021
4084
  }
4085
+ throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
4086
+ }
4087
+ function clampDateMs(ms) {
4088
+ const MAX = 864e13;
4089
+ return Math.max(-MAX, Math.min(MAX, ms));
4090
+ }
4091
+ function zoneOffsetForNaive(zone, naiveMicros) {
4092
+ const first = zoneOffsetAtUtc(zone, naiveMicros);
4093
+ const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
4094
+ return second;
4095
+ }
4096
+
4097
+ // src/expressions/context.ts
4098
+ var EngineCtx = class {
4022
4099
  state;
4023
- base = null;
4024
- savepoints = [];
4025
- freezeDepth = 0;
4026
- get inTransaction() {
4027
- return this.base !== null;
4100
+ /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
4101
+ txNow;
4102
+ /** statement timestamp */
4103
+ stmtNow;
4104
+ constructor(state, txNow) {
4105
+ this.state = state;
4106
+ const nowMs = state.clock().getTime();
4107
+ const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
4108
+ this.txNow = txNow ?? nowMicros;
4109
+ this.stmtNow = nowMicros;
4028
4110
  }
4029
- takeSnapshot() {
4030
- this.state.freezeShared();
4031
- this.freezeDepth++;
4032
- return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
4111
+ timezone() {
4112
+ return this.state.getSetting("timezone") ?? "UTC";
4033
4113
  }
4034
- restore(snap) {
4035
- this.state.restoreFrom(snap.state);
4036
- this.state.prng.setState(snap.prngState);
4114
+ namespaceOid(name) {
4115
+ const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
4116
+ if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
4117
+ return oid;
4037
4118
  }
4038
- thawOnce() {
4039
- if (this.freezeDepth > 0) {
4040
- this.state.thawShared();
4041
- this.freezeDepth--;
4119
+ namespaceName(oid) {
4120
+ const builtin = builtinNamespaceName(oid);
4121
+ if (builtin !== void 0) return builtin;
4122
+ for (const schema of this.state.schemas.values()) {
4123
+ if (schema.oid === oid) return schema.name;
4042
4124
  }
4125
+ return null;
4043
4126
  }
4044
- begin() {
4045
- if (this.base !== null) {
4046
- return;
4047
- }
4048
- this.base = this.takeSnapshot();
4049
- this.state.inTransaction = true;
4050
- assert(this.freezeDepth === 1, "BEGIN freeze depth");
4127
+ zoneOffsetAt(utcMicros) {
4128
+ return zoneOffsetAtUtc(this.timezone(), utcMicros);
4051
4129
  }
4052
- commit() {
4053
- this.base = null;
4054
- this.savepoints = [];
4055
- this.state.inTransaction = false;
4056
- this.state.localSettings.clear();
4057
- while (this.freezeDepth > 0) this.thawOnce();
4130
+ zoneOffsetForNaive(naiveMicros) {
4131
+ return zoneOffsetForNaive(this.timezone(), naiveMicros);
4058
4132
  }
4059
- rollback() {
4060
- if (this.base !== null) {
4061
- this.restore(this.base);
4062
- }
4063
- this.base = null;
4064
- this.savepoints = [];
4065
- this.state.inTransaction = false;
4066
- this.state.localSettings.clear();
4067
- while (this.freezeDepth > 0) this.thawOnce();
4133
+ enumLabels(enumType) {
4134
+ if (!isEnumType(enumType)) return null;
4135
+ const e = this.state.findEnumByKey(enumType.slice(5));
4136
+ return e?.labels ?? null;
4068
4137
  }
4069
- savepoint(name) {
4070
- if (this.base === null) {
4071
- throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
4072
- }
4073
- this.savepoints.push({ name, snap: this.takeSnapshot() });
4138
+ enumHasLabel(enumType, label) {
4139
+ const labels = this.enumLabels(enumType);
4140
+ return labels === null ? true : labels.includes(label);
4074
4141
  }
4075
- releaseSavepoint(name) {
4076
- const idx = this.findSavepoint(name);
4077
- this.savepoints.splice(idx);
4142
+ enumOrder(enumType, label) {
4143
+ const labels = this.enumLabels(enumType);
4144
+ if (!labels) return 0;
4145
+ const idx = labels.indexOf(label);
4146
+ return idx === -1 ? labels.length : idx;
4078
4147
  }
4079
- rollbackToSavepoint(name) {
4080
- const idx = this.findSavepoint(name);
4081
- const sp = this.savepoints[idx];
4082
- this.restore(sp.snap);
4083
- this.savepoints.splice(idx + 1);
4148
+ requireEnum(enumType) {
4149
+ const labels = this.enumLabels(enumType);
4150
+ if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
4151
+ return labels;
4084
4152
  }
4085
- findSavepoint(name) {
4086
- if (this.base === null) {
4087
- throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
4088
- }
4089
- for (let i = this.savepoints.length - 1; i >= 0; i--) {
4090
- if (this.savepoints[i].name === name) return i;
4153
+ };
4154
+
4155
+ // src/api/bind.ts
4156
+ var INT4_MIN2 = -2147483648;
4157
+ var INT4_MAX2 = 2147483647;
4158
+ function bindValueToTyped(value, index) {
4159
+ if (value === null || value === void 0) return tv(UNKNOWN, null);
4160
+ switch (typeof value) {
4161
+ case "boolean":
4162
+ return tv("bool", value);
4163
+ case "number":
4164
+ if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
4165
+ return tv("int4", value);
4166
+ }
4167
+ if (Number.isInteger(value) && Number.isSafeInteger(value)) {
4168
+ return tv("int8", BigInt(value));
4169
+ }
4170
+ return tv("float8", value);
4171
+ case "bigint":
4172
+ if (value < -9223372036854775808n || value > 9223372036854775807n) {
4173
+ throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
4174
+ }
4175
+ return tv("int8", value);
4176
+ case "string":
4177
+ return tv(UNKNOWN, value);
4178
+ case "object": {
4179
+ if (value instanceof Uint8Array) return tv("bytea", value);
4180
+ if (value instanceof Date) {
4181
+ const ms = value.getTime();
4182
+ if (Number.isNaN(ms)) {
4183
+ throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
4184
+ }
4185
+ return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
4186
+ }
4187
+ break;
4091
4188
  }
4092
- throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
4189
+ default:
4190
+ break;
4093
4191
  }
4094
- reset() {
4095
- this.base = null;
4096
- this.savepoints = [];
4097
- this.state.inTransaction = false;
4098
- this.freezeDepth = 0;
4192
+ throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
4193
+ }
4194
+ function datumToJs(t, v, ctx, int8 = "bigint") {
4195
+ if (v === null) return null;
4196
+ switch (t) {
4197
+ case "bool":
4198
+ return v;
4199
+ case "int2":
4200
+ case "int4":
4201
+ case "oid":
4202
+ return typeof v === "bigint" ? Number(v) : v;
4203
+ case "int8": {
4204
+ const n = typeof v === "bigint" ? v : BigInt(v);
4205
+ if (int8 === "string") return n.toString();
4206
+ if (int8 === "number") return Number(n);
4207
+ return n;
4208
+ }
4209
+ case "float4":
4210
+ case "float8":
4211
+ return v;
4212
+ case "bytea":
4213
+ return v;
4214
+ default:
4215
+ return datumText(t === UNKNOWN ? "text" : t, v, ctx);
4099
4216
  }
4100
- };
4217
+ }
4101
4218
 
4102
4219
  // src/types/cast.ts
4103
4220
  var UTC_CAST_ENV = {
@@ -4575,259 +4692,6 @@ function unifyTypes(a, b) {
4575
4692
  return null;
4576
4693
  }
4577
4694
 
4578
- // src/types/resolve.ts
4579
- var SERIALS = {
4580
- smallserial: "int2",
4581
- serial2: "int2",
4582
- serial: "int4",
4583
- serial4: "int4",
4584
- bigserial: "int8",
4585
- serial8: "int8"
4586
- };
4587
- function modOf(t, mods) {
4588
- if (mods.length === 0) return null;
4589
- if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
4590
- return { a: mods[0] };
4591
- }
4592
- function resolveTypeName(state, tn) {
4593
- const joined = tn.parts.join(".");
4594
- const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
4595
- if (bare !== null) {
4596
- const serial = SERIALS[bare];
4597
- if (serial !== void 0 && tn.arrayDims === 0) {
4598
- return { column: { id: serial, mod: null }, domain: null, serial };
4599
- }
4600
- const builtin = normalizeTypeName(bare);
4601
- if (builtin !== null) {
4602
- const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
4603
- return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
4604
- }
4605
- }
4606
- const enumData = state.findEnum(tn.parts);
4607
- if (enumData) {
4608
- const elem = `enum:${enumData.schema}.${enumData.name}`;
4609
- const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
4610
- return { column: { id, mod: null }, domain: null, serial: null };
4611
- }
4612
- const domainData = state.findDomain(tn.parts);
4613
- if (domainData) {
4614
- if (tn.arrayDims > 0) {
4615
- const id = arrayTypeOf(domainData.baseType.id);
4616
- return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
4617
- }
4618
- return {
4619
- column: domainData.baseType,
4620
- domain: `${domainData.schema}.${domainData.name}`,
4621
- serial: null
4622
- };
4623
- }
4624
- throw pgError("undefined_object", `type "${joined}" does not exist`);
4625
- }
4626
-
4627
- // src/executor/relation.ts
4628
- var RowScope = class {
4629
- constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
4630
- this.columns = columns;
4631
- this.row = row;
4632
- this.parent = parent;
4633
- this.rangeVars = rangeVars;
4634
- }
4635
- columns;
4636
- row;
4637
- parent;
4638
- rangeVars;
4639
- /** Resolve a (possibly qualified) column reference. undefined = not found. */
4640
- lookup(parts) {
4641
- if (parts.length === 1) {
4642
- const name2 = parts[0];
4643
- let found2;
4644
- let count2 = 0;
4645
- for (let i = 0; i < this.columns.length; i++) {
4646
- const c = this.columns[i];
4647
- if (c.name === name2 && !c.hidden) {
4648
- count2++;
4649
- if (count2 > 1) {
4650
- throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
4651
- }
4652
- found2 = tv(c.type, this.row[i] ?? null);
4653
- }
4654
- }
4655
- if (found2 !== void 0) return found2;
4656
- if (this.rangeVars.has(name2)) {
4657
- return this.wholeRow(name2);
4658
- }
4659
- return this.parent?.lookup(parts);
4660
- }
4661
- const table = parts[parts.length - 2];
4662
- const name = parts[parts.length - 1];
4663
- let found;
4664
- let count = 0;
4665
- for (let i = 0; i < this.columns.length; i++) {
4666
- const c = this.columns[i];
4667
- if (c.table === table && c.name === name) {
4668
- count++;
4669
- if (count > 1) {
4670
- throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
4671
- }
4672
- found = tv(c.type, this.row[i] ?? null);
4673
- }
4674
- }
4675
- if (found !== void 0) return found;
4676
- return this.parent?.lookup(parts);
4677
- }
4678
- /** Build a record value from all columns belonging to a range variable. */
4679
- wholeRow(rangeVar) {
4680
- const types = [];
4681
- const values = [];
4682
- const names = [];
4683
- for (let i = 0; i < this.columns.length; i++) {
4684
- const c = this.columns[i];
4685
- if (c.table === rangeVar) {
4686
- types.push(c.type);
4687
- values.push(this.row[i] ?? null);
4688
- names.push(c.name);
4689
- }
4690
- }
4691
- if (types.length === 0) return void 0;
4692
- return tv("record", { kind: "pgrecord", types, values, names });
4693
- }
4694
- hasRangeVar(name) {
4695
- if (this.rangeVars.has(name)) return true;
4696
- return this.parent?.hasRangeVar(name) ?? false;
4697
- }
4698
- };
4699
- function childEnv(env, outer) {
4700
- return {
4701
- ctx: env.ctx,
4702
- params: env.params,
4703
- ctes: env.ctes,
4704
- outer,
4705
- allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
4706
- };
4707
- }
4708
- function relationResult(rel3, command) {
4709
- const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
4710
- return {
4711
- columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
4712
- rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
4713
- command,
4714
- rowCount: rel3.rows.length
4715
- };
4716
- }
4717
- function commandResult(command, rowCount) {
4718
- return { columns: [], rows: [], command, rowCount };
4719
- }
4720
- function inferColumnName(e) {
4721
- switch (e.type) {
4722
- case "colref":
4723
- return e.parts[e.parts.length - 1];
4724
- case "func":
4725
- return e.name[e.name.length - 1];
4726
- case "cast": {
4727
- const inner = inferColumnName(e.expr);
4728
- if (inner !== "?column?") return inner;
4729
- return e.target.parts[e.target.parts.length - 1];
4730
- }
4731
- case "collate":
4732
- return inferColumnName(e.expr);
4733
- case "case":
4734
- return "case";
4735
- case "array_ctor":
4736
- case "array_query":
4737
- return "array";
4738
- case "row":
4739
- return "row";
4740
- case "subquery_expr":
4741
- return e.kind === "exists" ? "exists" : "?column?";
4742
- case "bool_lit":
4743
- return "bool";
4744
- case "extract":
4745
- return "extract";
4746
- case "position":
4747
- return "position";
4748
- case "substring_sql":
4749
- return "substring";
4750
- case "overlay":
4751
- return "overlay";
4752
- case "trim":
4753
- return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
4754
- case "field_select":
4755
- return e.field === "*" ? "?column?" : e.field;
4756
- case "at_time_zone":
4757
- return "timezone";
4758
- case "grouping_func":
4759
- return "grouping";
4760
- case "subscript":
4761
- return inferColumnName(e.base);
4762
- default:
4763
- return "?column?";
4764
- }
4765
- }
4766
-
4767
- // src/api/bind.ts
4768
- var INT4_MIN2 = -2147483648;
4769
- var INT4_MAX2 = 2147483647;
4770
- function bindValueToTyped(value, index) {
4771
- if (value === null || value === void 0) return tv(UNKNOWN, null);
4772
- switch (typeof value) {
4773
- case "boolean":
4774
- return tv("bool", value);
4775
- case "number":
4776
- if (Number.isInteger(value) && value >= INT4_MIN2 && value <= INT4_MAX2) {
4777
- return tv("int4", value);
4778
- }
4779
- if (Number.isInteger(value) && Number.isSafeInteger(value)) {
4780
- return tv("int8", BigInt(value));
4781
- }
4782
- return tv("float8", value);
4783
- case "bigint":
4784
- if (value < -9223372036854775808n || value > 9223372036854775807n) {
4785
- throw pgError("numeric_value_out_of_range", `bigint parameter $${index + 1} out of int8 range`, "22003");
4786
- }
4787
- return tv("int8", value);
4788
- case "string":
4789
- return tv(UNKNOWN, value);
4790
- case "object": {
4791
- if (value instanceof Uint8Array) return tv("bytea", value);
4792
- if (value instanceof Date) {
4793
- const ms = value.getTime();
4794
- if (Number.isNaN(ms)) {
4795
- throw pgError("invalid_datetime", `invalid Date parameter $${index + 1}`, "22008");
4796
- }
4797
- return tv("timestamptz", BigInt(Math.round(ms)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG);
4798
- }
4799
- break;
4800
- }
4801
- default:
4802
- break;
4803
- }
4804
- throw pgError("misuse", `unsupported parameter type for $${index + 1}: ${typeof value}`, "XX000");
4805
- }
4806
- function datumToJs(t, v, ctx, int8 = "bigint") {
4807
- if (v === null) return null;
4808
- switch (t) {
4809
- case "bool":
4810
- return v;
4811
- case "int2":
4812
- case "int4":
4813
- case "oid":
4814
- return typeof v === "bigint" ? Number(v) : v;
4815
- case "int8": {
4816
- const n = typeof v === "bigint" ? v : BigInt(v);
4817
- if (int8 === "string") return n.toString();
4818
- if (int8 === "number") return Number(n);
4819
- return n;
4820
- }
4821
- case "float4":
4822
- case "float8":
4823
- return v;
4824
- case "bytea":
4825
- return v;
4826
- default:
4827
- return datumText(t === UNKNOWN ? "text" : t, v, ctx);
4828
- }
4829
- }
4830
-
4831
4695
  // src/functions/datetime-fns.ts
4832
4696
  function extractDatePart(ctx, field0, src0, asNumeric) {
4833
4697
  const field = field0.toLowerCase();
@@ -5742,68 +5606,6 @@ function getArrayFunctions() {
5742
5606
  return m;
5743
5607
  }
5744
5608
 
5745
- // src/types/timezone.ts
5746
- var intlCache = /* @__PURE__ */ new Map();
5747
- function intlFor(zone) {
5748
- const cached = intlCache.get(zone);
5749
- if (cached) return cached;
5750
- try {
5751
- const fmt = new Intl.DateTimeFormat("en-US", {
5752
- timeZone: zone,
5753
- timeZoneName: "longOffset",
5754
- year: "numeric"
5755
- });
5756
- intlCache.set(zone, fmt);
5757
- return fmt;
5758
- } catch {
5759
- return null;
5760
- }
5761
- }
5762
- function parseFixedOffset(zone) {
5763
- const m = /^([+-]?)(\d{1,2})(?::(\d{2}))?(?::(\d{2}))?$/.exec(zone.trim());
5764
- if (!m) return null;
5765
- const sign = m[1] === "-" ? -1 : 1;
5766
- return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60 + Number(m[4] ?? 0));
5767
- }
5768
- function parsePosixOffset(zone) {
5769
- const m = /^(?:utc|gmt)([+-])(\d{1,2})(?::(\d{2}))?$/i.exec(zone.trim());
5770
- if (!m) return null;
5771
- const sign = m[1] === "-" ? 1 : -1;
5772
- return sign * (Number(m[2]) * 3600 + Number(m[3] ?? 0) * 60);
5773
- }
5774
- function pgMicrosToUnixMs(utcMicros) {
5775
- return Number((utcMicros - UNIX_EPOCH_MICROS_FROM_PG) / 1000n);
5776
- }
5777
- function zoneOffsetAtUtc(zone, utcMicros) {
5778
- const z = zone.trim();
5779
- const lower = z.toLowerCase();
5780
- if (lower === "utc" || lower === "gmt" || lower === "z" || lower === "zulu" || lower === "universal") return 0;
5781
- const posix = parsePosixOffset(z);
5782
- if (posix !== null) return posix;
5783
- const fixed = parseFixedOffset(z);
5784
- if (fixed !== null) return fixed;
5785
- const fmt = intlFor(z);
5786
- if (fmt) {
5787
- const ms = clampDateMs(pgMicrosToUnixMs(utcMicros));
5788
- const parts = fmt.formatToParts(new Date(ms));
5789
- const tzName = parts.find((p) => p.type === "timeZoneName")?.value ?? "GMT";
5790
- const m = /GMT([+-])(\d{2}):(\d{2})(?::(\d{2}))?/.exec(tzName);
5791
- if (!m) return 0;
5792
- const sign = m[1] === "-" ? -1 : 1;
5793
- return sign * (Number(m[2]) * 3600 + Number(m[3]) * 60 + Number(m[4] ?? 0));
5794
- }
5795
- throw pgError("invalid_parameter_value", `time zone "${zone}" not recognized`);
5796
- }
5797
- function clampDateMs(ms) {
5798
- const MAX = 864e13;
5799
- return Math.max(-MAX, Math.min(MAX, ms));
5800
- }
5801
- function zoneOffsetForNaive(zone, naiveMicros) {
5802
- const first = zoneOffsetAtUtc(zone, naiveMicros);
5803
- const second = zoneOffsetAtUtc(zone, naiveMicros - BigInt(first) * 1000000n);
5804
- return second;
5805
- }
5806
-
5807
5609
  // src/functions/datetime-registry.ts
5808
5610
  function getDatetimeFunctions() {
5809
5611
  const m = /* @__PURE__ */ new Map();
@@ -7403,6 +7205,28 @@ function exactDegTan(x) {
7403
7205
  return Math.tan(x * Math.PI / 180);
7404
7206
  }
7405
7207
 
7208
+ // src/storage/indexable-relation.ts
7209
+ function materializedTable(view) {
7210
+ const table = new TableData(
7211
+ view.schema,
7212
+ view.name,
7213
+ (view.matColumns ?? []).map((c) => ({
7214
+ name: c.name,
7215
+ type: { id: c.type, mod: null },
7216
+ notNull: false,
7217
+ defaultExpr: null,
7218
+ identity: null,
7219
+ generated: null,
7220
+ collate: null,
7221
+ domain: null
7222
+ })),
7223
+ view.oid,
7224
+ view.temp
7225
+ );
7226
+ table.rows = view.matRows ?? [];
7227
+ return table;
7228
+ }
7229
+
7406
7230
  // src/schema/index-catalog.ts
7407
7231
  var isKeyConstraint = (constraint) => constraint.kind === "primary_key" || constraint.kind === "unique";
7408
7232
  var ACCESS_METHODS = /* @__PURE__ */ new Map([
@@ -7431,6 +7255,8 @@ function syncIndexCatalog(state) {
7431
7255
  const tables = /* @__PURE__ */ new Map();
7432
7256
  for (const schema of state.schemas.values()) {
7433
7257
  for (const table of schema.tables.values()) tables.set(table.oid, { schema, table });
7258
+ for (const view of schema.views.values())
7259
+ if (view.materialized) tables.set(view.oid, { schema, table: materializedTable(view) });
7434
7260
  }
7435
7261
  const backing = /* @__PURE__ */ new Map();
7436
7262
  for (const { table } of tables.values()) {
@@ -7628,6 +7454,55 @@ function deparseExpr(e) {
7628
7454
  }
7629
7455
  }
7630
7456
 
7457
+ // src/types/resolve.ts
7458
+ var SERIALS = {
7459
+ smallserial: "int2",
7460
+ serial2: "int2",
7461
+ serial: "int4",
7462
+ serial4: "int4",
7463
+ bigserial: "int8",
7464
+ serial8: "int8"
7465
+ };
7466
+ function modOf(t, mods) {
7467
+ if (mods.length === 0) return null;
7468
+ if (t === "numeric") return { a: mods[0], b: mods[1] ?? 0 };
7469
+ return { a: mods[0] };
7470
+ }
7471
+ function resolveTypeName(state, tn) {
7472
+ const joined = tn.parts.join(".");
7473
+ const bare = tn.parts.length === 1 ? tn.parts[0] : tn.parts.length === 2 && tn.parts[0] === "pg_catalog" ? tn.parts[1] : null;
7474
+ if (bare !== null) {
7475
+ const serial = SERIALS[bare];
7476
+ if (serial !== void 0 && tn.arrayDims === 0) {
7477
+ return { column: { id: serial, mod: null }, domain: null, serial };
7478
+ }
7479
+ const builtin = normalizeTypeName(bare);
7480
+ if (builtin !== null) {
7481
+ const id = tn.arrayDims > 0 ? arrayTypeOf(builtin) : builtin;
7482
+ return { column: { id, mod: modOf(builtin, tn.mods) }, domain: null, serial: null };
7483
+ }
7484
+ }
7485
+ const enumData = state.findEnum(tn.parts);
7486
+ if (enumData) {
7487
+ const elem = `enum:${enumData.schema}.${enumData.name}`;
7488
+ const id = tn.arrayDims > 0 ? arrayTypeOf(elem) : elem;
7489
+ return { column: { id, mod: null }, domain: null, serial: null };
7490
+ }
7491
+ const domainData = state.findDomain(tn.parts);
7492
+ if (domainData) {
7493
+ if (tn.arrayDims > 0) {
7494
+ const id = arrayTypeOf(domainData.baseType.id);
7495
+ return { column: { id, mod: domainData.baseType.mod }, domain: null, serial: null };
7496
+ }
7497
+ return {
7498
+ column: domainData.baseType,
7499
+ domain: `${domainData.schema}.${domainData.name}`,
7500
+ serial: null
7501
+ };
7502
+ }
7503
+ throw pgError("undefined_object", `type "${joined}" does not exist`);
7504
+ }
7505
+
7631
7506
  // src/sql/ruleutils.ts
7632
7507
  var BOOL = "bool";
7633
7508
  var TEXT = "text";
@@ -8422,7 +8297,11 @@ function getMiscFunctions() {
8422
8297
  }
8423
8298
  const name = argText(ctx, args[0]).toLowerCase();
8424
8299
  const value = args[1].v === null ? "" : argText(ctx, args[1]);
8425
- ctx.state.settings.set(name, value);
8300
+ if (!ctx.state.settings.has(name) && !name.includes("."))
8301
+ throw pgError("undefined_object", `unrecognized configuration parameter "${name}"`, "42704");
8302
+ validateStorageSetting(name, value);
8303
+ const local = args[2]?.v === true;
8304
+ if (!local || ctx.state.inTransaction) (local ? ctx.state.localSettings : ctx.state.settings).set(name, value);
8426
8305
  return tv("text", value);
8427
8306
  });
8428
8307
  m.set(
@@ -8488,10 +8367,7 @@ function getMiscFunctions() {
8488
8367
  if (!col)
8489
8368
  throw pgError("undefined_column", `column "${column}" of relation "${table.name}" does not exist`, "42703");
8490
8369
  if (!col.identity) return tv("text", null);
8491
- return tv(
8492
- "text",
8493
- col.identity.sequence.split(".").map(quoteIdent).join(".")
8494
- );
8370
+ return tv("text", col.identity.sequence.split(".").map(quoteIdent).join("."));
8495
8371
  })
8496
8372
  );
8497
8373
  m.set("format_type", (ctx, args) => {
@@ -13415,6 +13291,146 @@ function isSrfName(name) {
13415
13291
  return SRFS.has(name);
13416
13292
  }
13417
13293
 
13294
+ // src/executor/relation.ts
13295
+ var RowScope = class {
13296
+ constructor(columns, row, parent = null, rangeVars = /* @__PURE__ */ new Set()) {
13297
+ this.columns = columns;
13298
+ this.row = row;
13299
+ this.parent = parent;
13300
+ this.rangeVars = rangeVars;
13301
+ }
13302
+ columns;
13303
+ row;
13304
+ parent;
13305
+ rangeVars;
13306
+ /** Resolve a (possibly qualified) column reference. undefined = not found. */
13307
+ lookup(parts) {
13308
+ if (parts.length === 1) {
13309
+ const name2 = parts[0];
13310
+ let found2;
13311
+ let count2 = 0;
13312
+ for (let i = 0; i < this.columns.length; i++) {
13313
+ const c = this.columns[i];
13314
+ if (c.name === name2 && !c.hidden) {
13315
+ count2++;
13316
+ if (count2 > 1) {
13317
+ throw pgError("ambiguous_column", `column reference "${name2}" is ambiguous`);
13318
+ }
13319
+ found2 = tv(c.type, this.row[i] ?? null);
13320
+ }
13321
+ }
13322
+ if (found2 !== void 0) return found2;
13323
+ if (this.rangeVars.has(name2)) {
13324
+ return this.wholeRow(name2);
13325
+ }
13326
+ return this.parent?.lookup(parts);
13327
+ }
13328
+ const table = parts[parts.length - 2];
13329
+ const name = parts[parts.length - 1];
13330
+ let found;
13331
+ let count = 0;
13332
+ for (let i = 0; i < this.columns.length; i++) {
13333
+ const c = this.columns[i];
13334
+ if (c.table === table && c.name === name) {
13335
+ count++;
13336
+ if (count > 1) {
13337
+ throw pgError("ambiguous_column", `column reference "${name}" is ambiguous`);
13338
+ }
13339
+ found = tv(c.type, this.row[i] ?? null);
13340
+ }
13341
+ }
13342
+ if (found !== void 0) return found;
13343
+ return this.parent?.lookup(parts);
13344
+ }
13345
+ /** Build a record value from all columns belonging to a range variable. */
13346
+ wholeRow(rangeVar) {
13347
+ const types = [];
13348
+ const values = [];
13349
+ const names = [];
13350
+ for (let i = 0; i < this.columns.length; i++) {
13351
+ const c = this.columns[i];
13352
+ if (c.table === rangeVar) {
13353
+ types.push(c.type);
13354
+ values.push(this.row[i] ?? null);
13355
+ names.push(c.name);
13356
+ }
13357
+ }
13358
+ if (types.length === 0) return void 0;
13359
+ return tv("record", { kind: "pgrecord", types, values, names });
13360
+ }
13361
+ hasRangeVar(name) {
13362
+ if (this.rangeVars.has(name)) return true;
13363
+ return this.parent?.hasRangeVar(name) ?? false;
13364
+ }
13365
+ };
13366
+ function childEnv(env, outer) {
13367
+ return {
13368
+ ctx: env.ctx,
13369
+ params: env.params,
13370
+ ctes: env.ctes,
13371
+ outer,
13372
+ allowUnpopulatedMatviews: env.allowUnpopulatedMatviews
13373
+ };
13374
+ }
13375
+ function relationResult(rel3, command) {
13376
+ const visible = rel3.columns.map((c, i) => ({ i, c })).filter(({ c }) => !c.hidden);
13377
+ return {
13378
+ columns: visible.map(({ c }) => ({ name: c.name, type: c.type })),
13379
+ rows: rel3.rows.map((r) => visible.map(({ i }) => r[i] ?? null)),
13380
+ command,
13381
+ rowCount: rel3.rows.length
13382
+ };
13383
+ }
13384
+ function commandResult(command, rowCount) {
13385
+ return { columns: [], rows: [], command, rowCount };
13386
+ }
13387
+ function inferColumnName(e) {
13388
+ switch (e.type) {
13389
+ case "colref":
13390
+ return e.parts[e.parts.length - 1];
13391
+ case "func":
13392
+ return e.name[e.name.length - 1];
13393
+ case "cast": {
13394
+ const inner = inferColumnName(e.expr);
13395
+ if (inner !== "?column?") return inner;
13396
+ return e.target.parts[e.target.parts.length - 1];
13397
+ }
13398
+ case "collate":
13399
+ return inferColumnName(e.expr);
13400
+ case "case":
13401
+ return "case";
13402
+ case "array_ctor":
13403
+ case "array_query":
13404
+ return "array";
13405
+ case "row":
13406
+ return "row";
13407
+ case "subquery_expr":
13408
+ return e.kind === "exists" ? "exists" : "?column?";
13409
+ case "bool_lit":
13410
+ return "bool";
13411
+ case "extract":
13412
+ return "extract";
13413
+ case "position":
13414
+ return "position";
13415
+ case "substring_sql":
13416
+ return "substring";
13417
+ case "overlay":
13418
+ return "overlay";
13419
+ case "trim":
13420
+ return e.side === "leading" ? "ltrim" : e.side === "trailing" ? "rtrim" : "btrim";
13421
+ case "field_select":
13422
+ return e.field === "*" ? "?column?" : e.field;
13423
+ case "at_time_zone":
13424
+ return "timezone";
13425
+ case "grouping_func":
13426
+ return "grouping";
13427
+ case "subscript":
13428
+ return inferColumnName(e.base);
13429
+ default:
13430
+ return "?column?";
13431
+ }
13432
+ }
13433
+
13418
13434
  // src/indexes/index.ts
13419
13435
  var IndexStore = class _IndexStore {
13420
13436
  name;
@@ -13477,53 +13493,228 @@ function ensureIndexStores(env, table) {
13477
13493
  const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
13478
13494
  if (key !== null) store.insert(key, i);
13479
13495
  }
13480
- stores.set(spec.name, store);
13481
- }
13482
- const schema = env.ctx.state.schemas.get(table.schema);
13483
- if (schema) {
13484
- for (const idx of schema.indexes.values()) {
13485
- if (idx.table !== table.name || idx.unique) continue;
13486
- stores.set(idx.name, new IndexStore(idx.name, false));
13496
+ stores.set(spec.name, store);
13497
+ }
13498
+ const schema = env.ctx.state.schemas.get(table.schema);
13499
+ if (schema) {
13500
+ for (const idx of schema.indexes.values()) {
13501
+ if (idx.table !== table.name || idx.unique) continue;
13502
+ stores.set(idx.name, new IndexStore(idx.name, false));
13503
+ }
13504
+ }
13505
+ table.indexStores = stores;
13506
+ return stores;
13507
+ }
13508
+ function indexStoreFor(env, table, spec) {
13509
+ const stores = ensureIndexStores(env, table);
13510
+ let store = stores.get(spec.name);
13511
+ if (!store) {
13512
+ store = new IndexStore(spec.name, true);
13513
+ for (let i = 0; i < table.rowCount(); i++) {
13514
+ const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
13515
+ if (key !== null) store.insert(key, i);
13516
+ }
13517
+ stores.set(spec.name, store);
13518
+ }
13519
+ return store;
13520
+ }
13521
+ function indexInsertRow(env, table, rowIdx, row) {
13522
+ for (const spec of uniqueSpecsFor(env, table)) {
13523
+ const key = uniqueKeyOf(env, table, spec, row);
13524
+ if (key === null) continue;
13525
+ indexStoreFor(env, table, spec).insert(key, rowIdx);
13526
+ }
13527
+ }
13528
+ function indexRemoveRow(env, table, rowIdx, row) {
13529
+ if (!table.indexStores) return;
13530
+ for (const spec of uniqueSpecsFor(env, table)) {
13531
+ const key = uniqueKeyOf(env, table, spec, row);
13532
+ if (key === null) continue;
13533
+ table.indexStores.get(spec.name)?.remove(key, rowIdx);
13534
+ }
13535
+ }
13536
+ function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
13537
+ indexRemoveRow(env, table, rowIdx, oldRow);
13538
+ indexInsertRow(env, table, rowIdx, newRow);
13539
+ }
13540
+ function rebuildTableIndexes(env, table) {
13541
+ table.indexStores = null;
13542
+ ensureIndexStores(env, table);
13543
+ }
13544
+
13545
+ // src/constraints/enforce.ts
13546
+ function tableScope(table, row) {
13547
+ const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
13548
+ return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
13549
+ }
13550
+ function checkNotNull(env, table, row) {
13551
+ void env;
13552
+ for (let i = 0; i < table.columns.length; i++) {
13553
+ const c = table.columns[i];
13554
+ if (c.notNull && (row[i] ?? null) === null) {
13555
+ throw pgError(
13556
+ "not_null_violation",
13557
+ `null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
13558
+ "23502"
13559
+ );
13560
+ }
13561
+ }
13562
+ }
13563
+ function checkChecks(env, table, row) {
13564
+ for (const con of table.constraints) {
13565
+ if (con.kind !== "check") continue;
13566
+ const scope = makeEvalScope(env, tableScope(table, row));
13567
+ const v = evalExpr(env.ctx, scope, con.expr);
13568
+ if (v.v === null) continue;
13569
+ const b = castTo(env.ctx, v, "bool", {});
13570
+ if (b.v !== true) {
13571
+ throw pgError(
13572
+ "check_violation",
13573
+ `new row for relation "${table.name}" violates check constraint "${con.name}"`,
13574
+ "23514"
13575
+ );
13576
+ }
13577
+ }
13578
+ }
13579
+ function uniqueSpecsFor(env, table) {
13580
+ const specs = [];
13581
+ for (const con of table.constraints) {
13582
+ if (con.kind !== "primary_key" && con.kind !== "unique") continue;
13583
+ specs.push({
13584
+ name: con.name,
13585
+ keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
13586
+ columnNames: con.columns,
13587
+ nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
13588
+ where: null,
13589
+ isPrimary: con.kind === "primary_key"
13590
+ });
13591
+ }
13592
+ const schema = env.ctx.state.schemas.get(table.schema);
13593
+ if (schema) {
13594
+ for (const idx of schema.indexes.values()) {
13595
+ if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
13596
+ specs.push(uniqueSpecOfIndex(table, idx));
13597
+ }
13598
+ }
13599
+ return specs;
13600
+ }
13601
+ function uniqueSpecOfIndex(table, idx) {
13602
+ return {
13603
+ name: idx.name,
13604
+ keys: idx.columns.map((c) => ({
13605
+ colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
13606
+ expr: c.expr
13607
+ })),
13608
+ columnNames: idx.columns.map((c) => c.column ?? "expr"),
13609
+ nullsNotDistinct: idx.nullsNotDistinct,
13610
+ where: idx.where,
13611
+ isPrimary: false
13612
+ };
13613
+ }
13614
+ function uniqueKeyOf(env, table, spec, row) {
13615
+ if (spec.where) {
13616
+ const scope = makeEvalScope(env, tableScope(table, row));
13617
+ const v = evalExpr(env.ctx, scope, spec.where);
13618
+ if (v.v !== true) return null;
13619
+ }
13620
+ const parts = [];
13621
+ let hasNull = false;
13622
+ for (const k of spec.keys) {
13623
+ let value;
13624
+ let type;
13625
+ if (k.expr) {
13626
+ const scope = makeEvalScope(env, tableScope(table, row));
13627
+ const v = evalExpr(env.ctx, scope, k.expr);
13628
+ value = v.v;
13629
+ type = v.t === "unknown" ? "text" : v.t;
13630
+ } else {
13631
+ value = row[k.colIdx] ?? null;
13632
+ type = table.columns[k.colIdx].type.id;
13633
+ }
13634
+ if (value === null) {
13635
+ hasNull = true;
13636
+ parts.push("\0N");
13637
+ } else {
13638
+ parts.push(datumKey(type, value));
13639
+ }
13640
+ }
13641
+ if (hasNull && !spec.nullsNotDistinct) return null;
13642
+ return parts.join("");
13643
+ }
13644
+ function checkUnique(env, table, row, selfIdx) {
13645
+ for (const spec of uniqueSpecsFor(env, table)) {
13646
+ const key = uniqueKeyOf(env, table, spec, row);
13647
+ if (key === null) continue;
13648
+ indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
13649
+ }
13650
+ }
13651
+ function findConflict(env, table, spec, row) {
13652
+ const key = uniqueKeyOf(env, table, spec, row);
13653
+ if (key === null) return null;
13654
+ const hits = indexStoreFor(env, table, spec).lookup(key);
13655
+ return hits.length > 0 ? hits[0] : null;
13656
+ }
13657
+ function checkForeignKeys(env, table, row) {
13658
+ const state = env.ctx.state;
13659
+ for (const con of table.constraints) {
13660
+ if (con.kind !== "foreign_key") continue;
13661
+ const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
13662
+ const nulls = values.filter((v) => v === null).length;
13663
+ if (con.match === "simple" && nulls > 0) continue;
13664
+ if (con.match === "full") {
13665
+ if (nulls === values.length) continue;
13666
+ if (nulls > 0) {
13667
+ throw pgError(
13668
+ "constraint_foreign_key",
13669
+ `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
13670
+ "23503"
13671
+ );
13672
+ }
13673
+ }
13674
+ const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
13675
+ if (!refTable) {
13676
+ throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
13677
+ }
13678
+ const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
13679
+ const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
13680
+ const wanted = values.map((v, i) => {
13681
+ const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
13682
+ const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
13683
+ return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
13684
+ }).join("");
13685
+ let found = false;
13686
+ for (let i = 0; i < refTable.rowCount(); i++) {
13687
+ const r = refTable.rowAt(i);
13688
+ const key = refIdxs.map((ri, j) => {
13689
+ const v = r[ri] ?? null;
13690
+ return v === null ? "\0N" : datumKey(keyTypes[j], v);
13691
+ }).join("");
13692
+ if (key === wanted) {
13693
+ found = true;
13694
+ break;
13695
+ }
13696
+ }
13697
+ if (!found) {
13698
+ throw pgError(
13699
+ "constraint_foreign_key",
13700
+ `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
13701
+ "23503"
13702
+ );
13487
13703
  }
13488
13704
  }
13489
- table.indexStores = stores;
13490
- return stores;
13491
13705
  }
13492
- function indexStoreFor(env, table, spec) {
13493
- const stores = ensureIndexStores(env, table);
13494
- let store = stores.get(spec.name);
13495
- if (!store) {
13496
- store = new IndexStore(spec.name, true);
13497
- for (let i = 0; i < table.rowCount(); i++) {
13498
- const key = uniqueKeyOf(env, table, spec, table.rowAt(i));
13499
- if (key !== null) store.insert(key, i);
13706
+ function referencingConstraints(env, table) {
13707
+ const out = [];
13708
+ for (const schema of env.ctx.state.schemas.values()) {
13709
+ for (const t of schema.tables.values()) {
13710
+ for (const con of t.constraints) {
13711
+ if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
13712
+ out.push({ table: t, constraint: con });
13713
+ }
13714
+ }
13500
13715
  }
13501
- stores.set(spec.name, store);
13502
- }
13503
- return store;
13504
- }
13505
- function indexInsertRow(env, table, rowIdx, row) {
13506
- for (const spec of uniqueSpecsFor(env, table)) {
13507
- const key = uniqueKeyOf(env, table, spec, row);
13508
- if (key === null) continue;
13509
- indexStoreFor(env, table, spec).insert(key, rowIdx);
13510
- }
13511
- }
13512
- function indexRemoveRow(env, table, rowIdx, row) {
13513
- if (!table.indexStores) return;
13514
- for (const spec of uniqueSpecsFor(env, table)) {
13515
- const key = uniqueKeyOf(env, table, spec, row);
13516
- if (key === null) continue;
13517
- table.indexStores.get(spec.name)?.remove(key, rowIdx);
13518
13716
  }
13519
- }
13520
- function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
13521
- indexRemoveRow(env, table, rowIdx, oldRow);
13522
- indexInsertRow(env, table, rowIdx, newRow);
13523
- }
13524
- function rebuildTableIndexes(env, table) {
13525
- table.indexStores = null;
13526
- ensureIndexStores(env, table);
13717
+ return out;
13527
13718
  }
13528
13719
 
13529
13720
  // src/planner/access.ts
@@ -15409,10 +15600,9 @@ var Parser = class {
15409
15600
  return this.parseCreateFunction(orReplace);
15410
15601
  }
15411
15602
  if (this.atKw("trigger") || this.atKw("constraint") && this.atKw("trigger", 1)) {
15412
- if (this.eatKw("constraint")) {
15413
- }
15603
+ const constraint = this.eatKw("constraint");
15414
15604
  this.expectKw("trigger");
15415
- return this.parseCreateTrigger(orReplace);
15605
+ return this.parseCreateTrigger(orReplace, constraint);
15416
15606
  }
15417
15607
  if (this.atKw("extension")) {
15418
15608
  this.pos++;
@@ -16233,7 +16423,7 @@ var Parser = class {
16233
16423
  strict: strict2
16234
16424
  };
16235
16425
  }
16236
- parseCreateTrigger(orReplace) {
16426
+ parseCreateTrigger(orReplace, constraint) {
16237
16427
  const name = this.ident();
16238
16428
  let timing;
16239
16429
  if (this.eatKw("before")) timing = "before";
@@ -16263,9 +16453,18 @@ var Parser = class {
16263
16453
  this.expectKw("on");
16264
16454
  const table = this.qualifiedName();
16265
16455
  if (this.eatKw("referencing")) throw unsupported("trigger transition tables");
16266
- while (this.atKw("not") || this.atKw("deferrable") || this.atKw("initially")) {
16267
- this.pos++;
16268
- }
16456
+ let deferrable = false;
16457
+ let initiallyDeferred = false;
16458
+ if (this.eatKw("not")) this.expectKw("deferrable");
16459
+ else if (this.eatKw("deferrable")) deferrable = true;
16460
+ if (this.eatKw("initially")) {
16461
+ if (this.eatKw("deferred")) initiallyDeferred = true;
16462
+ else this.expectKw("immediate");
16463
+ }
16464
+ if ((deferrable || initiallyDeferred) && !constraint)
16465
+ this.errorAt(this.peek(), "only constraint triggers can be deferred");
16466
+ if (initiallyDeferred && !deferrable)
16467
+ this.errorAt(this.peek(), "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
16269
16468
  let forEachRow = false;
16270
16469
  if (this.eatKw("for")) {
16271
16470
  this.eatKw("each");
@@ -16290,7 +16489,21 @@ var Parser = class {
16290
16489
  } while (this.eatPunct(","));
16291
16490
  }
16292
16491
  this.expectPunct(")");
16293
- return { type: "create_trigger", orReplace, name, timing, events, table, forEachRow, when, funcName, funcArgs };
16492
+ return {
16493
+ type: "create_trigger",
16494
+ constraint,
16495
+ deferrable,
16496
+ initiallyDeferred,
16497
+ orReplace,
16498
+ name,
16499
+ timing,
16500
+ events,
16501
+ table,
16502
+ forEachRow,
16503
+ when,
16504
+ funcName,
16505
+ funcArgs
16506
+ };
16294
16507
  }
16295
16508
  // --- ALTER --------------------------------------------------------------------
16296
16509
  parseAlter() {
@@ -16635,9 +16848,15 @@ var Parser = class {
16635
16848
  this.skipToStatementEnd();
16636
16849
  return { type: "no_op", what: "SET TRANSACTION" };
16637
16850
  }
16638
- if (this.atKw("constraints")) {
16639
- this.skipToStatementEnd();
16640
- return { type: "no_op", what: "SET CONSTRAINTS" };
16851
+ if (this.eatKw("constraints")) {
16852
+ const names = this.eatKw("all") ? null : [];
16853
+ if (names)
16854
+ do {
16855
+ names.push(this.qualifiedName());
16856
+ } while (this.eatPunct(","));
16857
+ const deferred = this.eatKw("deferred");
16858
+ if (!deferred) this.expectKw("immediate");
16859
+ return { type: "set_constraints", names, deferred };
16641
16860
  }
16642
16861
  if (this.atKw("role") || this.atKw("session", 0) && this.atKw("authorization", 1)) {
16643
16862
  this.skipToStatementEnd();
@@ -19012,12 +19231,12 @@ function executeRecursiveCte(env, cte, query) {
19012
19231
  table: null
19013
19232
  }));
19014
19233
  const seen = /* @__PURE__ */ new Set();
19015
- const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
19234
+ const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(columns[i].type, v)).join("");
19016
19235
  let result = [];
19017
19236
  let working = [];
19018
19237
  for (const row of base.rows) {
19019
19238
  if (!body.all) {
19020
- const k = keyOf(row);
19239
+ const k = keyOf2(row);
19021
19240
  if (seen.has(k)) continue;
19022
19241
  seen.add(k);
19023
19242
  }
@@ -19050,7 +19269,7 @@ function executeRecursiveCte(env, cte, query) {
19050
19269
  return castTo(env.ctx, tv(srcT === UNKNOWN ? "text" : srcT, v), columns[i].type, {}).v;
19051
19270
  });
19052
19271
  if (!body.all) {
19053
- const k = keyOf(row);
19272
+ const k = keyOf2(row);
19054
19273
  if (seen.has(k)) continue;
19055
19274
  seen.add(k);
19056
19275
  }
@@ -20230,7 +20449,7 @@ function setOpRelation(env, op) {
20230
20449
  );
20231
20450
  const lrows = normalize(left);
20232
20451
  const rrows = normalize(right);
20233
- const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
20452
+ const keyOf2 = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
20234
20453
  let rows;
20235
20454
  switch (op.op) {
20236
20455
  case "union": {
@@ -20238,7 +20457,7 @@ function setOpRelation(env, op) {
20238
20457
  if (!op.all) {
20239
20458
  const seen = /* @__PURE__ */ new Set();
20240
20459
  rows = rows.filter((r) => {
20241
- const k = keyOf(r);
20460
+ const k = keyOf2(r);
20242
20461
  if (seen.has(k)) return false;
20243
20462
  seen.add(k);
20244
20463
  return true;
@@ -20249,13 +20468,13 @@ function setOpRelation(env, op) {
20249
20468
  case "intersect": {
20250
20469
  const rightCounts = /* @__PURE__ */ new Map();
20251
20470
  for (const r of rrows) {
20252
- const k = keyOf(r);
20471
+ const k = keyOf2(r);
20253
20472
  rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
20254
20473
  }
20255
20474
  rows = [];
20256
20475
  const emitted = /* @__PURE__ */ new Set();
20257
20476
  for (const r of lrows) {
20258
- const k = keyOf(r);
20477
+ const k = keyOf2(r);
20259
20478
  const avail = rightCounts.get(k) ?? 0;
20260
20479
  if (avail <= 0) continue;
20261
20480
  if (op.all) {
@@ -20271,13 +20490,13 @@ function setOpRelation(env, op) {
20271
20490
  case "except": {
20272
20491
  const rightCounts = /* @__PURE__ */ new Map();
20273
20492
  for (const r of rrows) {
20274
- const k = keyOf(r);
20493
+ const k = keyOf2(r);
20275
20494
  rightCounts.set(k, (rightCounts.get(k) ?? 0) + 1);
20276
20495
  }
20277
20496
  rows = [];
20278
20497
  const emitted = /* @__PURE__ */ new Set();
20279
20498
  for (const r of lrows) {
20280
- const k = keyOf(r);
20499
+ const k = keyOf2(r);
20281
20500
  if (op.all) {
20282
20501
  const avail = rightCounts.get(k) ?? 0;
20283
20502
  if (avail > 0) {
@@ -20725,207 +20944,54 @@ function executeCore(env0, core, orderBy, opts) {
20725
20944
  }).join("");
20726
20945
  if (seen.has(k)) return false;
20727
20946
  seen.add(k);
20728
- return true;
20729
- });
20730
- }
20731
- }
20732
- rel3 = stripHidden(rel3);
20733
- return rel3;
20734
- }
20735
- function executeSelectStmt(env0, stmt) {
20736
- const env = applyWith(env0, stmt.with);
20737
- let rel3;
20738
- if (stmt.body.type === "select_core") {
20739
- rel3 = executeCore(env, stmt.body, stmt.orderBy, { hasLimit: stmt.limit !== null });
20740
- } else {
20741
- rel3 = executeBody(env, stmt.body);
20742
- const specs = outputOrderSpecs(env, rel3, stmt.orderBy);
20743
- if (specs.length > 0) sortRows(env, rel3, specs);
20744
- rel3 = stripHidden(rel3);
20745
- }
20746
- if (stmt.limitWithTies) {
20747
- if (stmt.orderBy.length === 0) {
20748
- throw pgError("syntax", "WITH TIES cannot be specified without ORDER BY clause", "42601");
20749
- }
20750
- const tieSpecs = outputOrderSpecs(env, rel3, stmt.orderBy);
20751
- return stripHidden(applyLimitOffset(env, rel3, stmt.limit, stmt.offset, tieSpecs));
20752
- }
20753
- return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
20754
- }
20755
-
20756
- // src/constraints/enforce.ts
20757
- function tableScope(table, row) {
20758
- const cols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: table.name }));
20759
- return new RowScope(cols, row, null, /* @__PURE__ */ new Set([table.name]));
20760
- }
20761
- function checkNotNull(env, table, row) {
20762
- void env;
20763
- for (let i = 0; i < table.columns.length; i++) {
20764
- const c = table.columns[i];
20765
- if (c.notNull && (row[i] ?? null) === null) {
20766
- throw pgError(
20767
- "not_null_violation",
20768
- `null value in column "${c.name}" of relation "${table.name}" violates not-null constraint`,
20769
- "23502"
20770
- );
20771
- }
20772
- }
20773
- }
20774
- function checkChecks(env, table, row) {
20775
- for (const con of table.constraints) {
20776
- if (con.kind !== "check") continue;
20777
- const scope = makeEvalScope(env, tableScope(table, row));
20778
- const v = evalExpr(env.ctx, scope, con.expr);
20779
- if (v.v === null) continue;
20780
- const b = castTo(env.ctx, v, "bool", {});
20781
- if (b.v !== true) {
20782
- throw pgError(
20783
- "check_violation",
20784
- `new row for relation "${table.name}" violates check constraint "${con.name}"`,
20785
- "23514"
20786
- );
20787
- }
20788
- }
20789
- }
20790
- function uniqueSpecsFor(env, table) {
20791
- const specs = [];
20792
- for (const con of table.constraints) {
20793
- if (con.kind !== "primary_key" && con.kind !== "unique") continue;
20794
- specs.push({
20795
- name: con.name,
20796
- keys: con.columns.map((c) => ({ colIdx: table.columnIndex(c), expr: null })),
20797
- columnNames: con.columns,
20798
- nullsNotDistinct: con.kind === "unique" ? con.nullsNotDistinct : false,
20799
- where: null,
20800
- isPrimary: con.kind === "primary_key"
20801
- });
20802
- }
20803
- const schema = env.ctx.state.schemas.get(table.schema);
20804
- if (schema) {
20805
- for (const idx of schema.indexes.values()) {
20806
- if (idx.table !== table.name || !idx.unique || idx.isConstraint || idx.valid === false) continue;
20807
- specs.push(uniqueSpecOfIndex(table, idx));
20808
- }
20809
- }
20810
- return specs;
20811
- }
20812
- function uniqueSpecOfIndex(table, idx) {
20813
- return {
20814
- name: idx.name,
20815
- keys: idx.columns.map((c) => ({
20816
- colIdx: c.column !== null ? table.columnIndex(c.column) : -1,
20817
- expr: c.expr
20818
- })),
20819
- columnNames: idx.columns.map((c) => c.column ?? "expr"),
20820
- nullsNotDistinct: idx.nullsNotDistinct,
20821
- where: idx.where,
20822
- isPrimary: false
20823
- };
20824
- }
20825
- function uniqueKeyOf(env, table, spec, row) {
20826
- if (spec.where) {
20827
- const scope = makeEvalScope(env, tableScope(table, row));
20828
- const v = evalExpr(env.ctx, scope, spec.where);
20829
- if (v.v !== true) return null;
20830
- }
20831
- const parts = [];
20832
- let hasNull = false;
20833
- for (const k of spec.keys) {
20834
- let value;
20835
- let type;
20836
- if (k.expr) {
20837
- const scope = makeEvalScope(env, tableScope(table, row));
20838
- const v = evalExpr(env.ctx, scope, k.expr);
20839
- value = v.v;
20840
- type = v.t === "unknown" ? "text" : v.t;
20841
- } else {
20842
- value = row[k.colIdx] ?? null;
20843
- type = table.columns[k.colIdx].type.id;
20844
- }
20845
- if (value === null) {
20846
- hasNull = true;
20847
- parts.push("\0N");
20848
- } else {
20849
- parts.push(datumKey(type, value));
20850
- }
20851
- }
20852
- if (hasNull && !spec.nullsNotDistinct) return null;
20853
- return parts.join("");
20854
- }
20855
- function checkUnique(env, table, row, selfIdx) {
20856
- for (const spec of uniqueSpecsFor(env, table)) {
20857
- const key = uniqueKeyOf(env, table, spec, row);
20858
- if (key === null) continue;
20859
- indexStoreFor(env, table, spec).checkUnique(key, selfIdx);
20860
- }
20861
- }
20862
- function findConflict(env, table, spec, row) {
20863
- const key = uniqueKeyOf(env, table, spec, row);
20864
- if (key === null) return null;
20865
- const hits = indexStoreFor(env, table, spec).lookup(key);
20866
- return hits.length > 0 ? hits[0] : null;
20867
- }
20868
- function checkForeignKeys(env, table, row) {
20869
- const state = env.ctx.state;
20870
- for (const con of table.constraints) {
20871
- if (con.kind !== "foreign_key") continue;
20872
- const values = con.columns.map((c) => row[table.columnIndex(c)] ?? null);
20873
- const nulls = values.filter((v) => v === null).length;
20874
- if (con.match === "simple" && nulls > 0) continue;
20875
- if (con.match === "full") {
20876
- if (nulls === values.length) continue;
20877
- if (nulls > 0) {
20878
- throw pgError(
20879
- "constraint_foreign_key",
20880
- `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
20881
- "23503"
20882
- );
20883
- }
20884
- }
20885
- const refTable = state.schemas.get(con.refSchema)?.tables.get(con.refTable);
20886
- if (!refTable) {
20887
- throw pgError("undefined_table", `relation "${con.refSchema}.${con.refTable}" does not exist`, "42P01");
20888
- }
20889
- const refIdxs = con.refColumns.map((c) => refTable.columnIndex(c));
20890
- const keyTypes = con.refColumns.map((_c, i) => refTable.columns[refIdxs[i]].type.id);
20891
- const wanted = values.map((v, i) => {
20892
- const localT = table.columns[table.columnIndex(con.columns[i])].type.id;
20893
- const cast = castTo(env.ctx, tv(localT, v), keyTypes[i], {});
20894
- return cast.v === null ? "\0N" : datumKey(keyTypes[i], cast.v);
20895
- }).join("");
20896
- let found = false;
20897
- for (let i = 0; i < refTable.rowCount(); i++) {
20898
- const r = refTable.rowAt(i);
20899
- const key = refIdxs.map((ri, j) => {
20900
- const v = r[ri] ?? null;
20901
- return v === null ? "\0N" : datumKey(keyTypes[j], v);
20902
- }).join("");
20903
- if (key === wanted) {
20904
- found = true;
20905
- break;
20906
- }
20907
- }
20908
- if (!found) {
20909
- throw pgError(
20910
- "constraint_foreign_key",
20911
- `insert or update on table "${table.name}" violates foreign key constraint "${con.name}"`,
20912
- "23503"
20913
- );
20947
+ return true;
20948
+ });
20914
20949
  }
20915
20950
  }
20951
+ rel3 = stripHidden(rel3);
20952
+ return rel3;
20916
20953
  }
20917
- function referencingConstraints(env, table) {
20918
- const out = [];
20919
- for (const schema of env.ctx.state.schemas.values()) {
20920
- for (const t of schema.tables.values()) {
20921
- for (const con of t.constraints) {
20922
- if (con.kind === "foreign_key" && con.refSchema === table.schema && con.refTable === table.name) {
20923
- out.push({ table: t, constraint: con });
20924
- }
20925
- }
20954
+ function executeSelectStmt(env0, stmt) {
20955
+ const env = applyWith(env0, stmt.with);
20956
+ let rel3;
20957
+ if (stmt.body.type === "select_core") {
20958
+ rel3 = executeCore(env, stmt.body, stmt.orderBy, { hasLimit: stmt.limit !== null });
20959
+ } else {
20960
+ rel3 = executeBody(env, stmt.body);
20961
+ const specs = outputOrderSpecs(env, rel3, stmt.orderBy);
20962
+ if (specs.length > 0) sortRows(env, rel3, specs);
20963
+ rel3 = stripHidden(rel3);
20964
+ }
20965
+ if (stmt.limitWithTies) {
20966
+ if (stmt.orderBy.length === 0) {
20967
+ throw pgError("syntax", "WITH TIES cannot be specified without ORDER BY clause", "42601");
20926
20968
  }
20969
+ const tieSpecs = outputOrderSpecs(env, rel3, stmt.orderBy);
20970
+ return stripHidden(applyLimitOffset(env, rel3, stmt.limit, stmt.offset, tieSpecs));
20927
20971
  }
20928
- return out;
20972
+ return applyLimitOffset(env, rel3, stmt.limit, stmt.offset);
20973
+ }
20974
+
20975
+ // src/executor/triggers-scope.ts
20976
+ function triggerScope(table, newRow, oldRow) {
20977
+ const cols = [];
20978
+ const row = [];
20979
+ const rangeVars = /* @__PURE__ */ new Set();
20980
+ if (newRow) {
20981
+ rangeVars.add("new");
20982
+ for (let i = 0; i < table.columns.length; i++) {
20983
+ cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
20984
+ row.push(newRow[i] ?? null);
20985
+ }
20986
+ }
20987
+ if (oldRow) {
20988
+ rangeVars.add("old");
20989
+ for (let i = 0; i < table.columns.length; i++) {
20990
+ cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
20991
+ row.push(oldRow[i] ?? null);
20992
+ }
20993
+ }
20994
+ return new RowScope(cols, row, null, rangeVars);
20929
20995
  }
20930
20996
 
20931
20997
  // src/executor/triggers.ts
@@ -20941,6 +21007,18 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
20941
21007
  for (const t of table.triggers) {
20942
21008
  if (t.timing !== timing || !matches(t, event) || !t.forEachRow) continue;
20943
21009
  if (!executor) throw unsupported(`trigger "${t.name}" execution`);
21010
+ if (t.constraint && timing === "after") {
21011
+ if (t.when && !evalPredicate(env, triggerScope(table, newRow, oldRow), t.when)) continue;
21012
+ env.ctx.state.pendingTriggers.push({
21013
+ schema: table.schema,
21014
+ table: table.name,
21015
+ trigger: t.name,
21016
+ event,
21017
+ oldRow: oldRow?.slice() ?? null,
21018
+ newRow: newRow?.slice() ?? null
21019
+ });
21020
+ continue;
21021
+ }
20944
21022
  const result = executor(env, table, t, event, oldRow, timing === "before" ? row : newRow);
20945
21023
  if (timing === "before") {
20946
21024
  if (result === null) return { row: null };
@@ -20949,6 +21027,167 @@ function fireRowTriggers(env, table, timing, event, oldRow, newRow) {
20949
21027
  }
20950
21028
  return { row };
20951
21029
  }
21030
+ var keyOf = (schema, name) => JSON.stringify([schema, name]);
21031
+ function flushConstraintTriggers(state, all = false) {
21032
+ const env = { ctx: new EngineCtx(state), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
21033
+ for (let i = 0; i < state.pendingTriggers.length; ) {
21034
+ const pending = state.pendingTriggers[i];
21035
+ const table = state.schemas.get(pending.schema)?.tables.get(pending.table);
21036
+ const trigger = table?.triggers.find((t) => t.name === pending.trigger);
21037
+ if (!table || !trigger) {
21038
+ state.pendingTriggers.splice(i, 1);
21039
+ continue;
21040
+ }
21041
+ const deferred = trigger.deferrable && (state.constraintModes.get(keyOf(table.schema, trigger.name)) ?? trigger.initiallyDeferred);
21042
+ if (!all && state.inTransaction && deferred) {
21043
+ i++;
21044
+ continue;
21045
+ }
21046
+ if (!executor) throw unsupported(`trigger "${trigger.name}" execution`);
21047
+ state.pendingTriggers.splice(i, 1);
21048
+ const changes = state.changes;
21049
+ try {
21050
+ executor(env, table, { ...trigger, when: null }, pending.event, pending.oldRow, pending.newRow);
21051
+ } catch (error) {
21052
+ state.pendingTriggers.splice(i, 0, pending);
21053
+ throw error;
21054
+ } finally {
21055
+ state.changes = changes;
21056
+ }
21057
+ }
21058
+ }
21059
+ function setConstraints(state, stmt) {
21060
+ if (!state.inTransaction) return;
21061
+ const matches2 = [];
21062
+ if (stmt.names) {
21063
+ for (const name of stmt.names) {
21064
+ const schemas = name.length > 1 ? [name[name.length - 2]] : state.effectiveSearchPath();
21065
+ let found = false;
21066
+ for (const schema of schemas) {
21067
+ const items = [...state.schemas.get(schema)?.tables.values() ?? []].flatMap(
21068
+ (table) => table.triggers.filter((t) => t.constraint && t.name === name[name.length - 1])
21069
+ );
21070
+ if (items.length) {
21071
+ for (const trigger of items) matches2.push({ schema, trigger });
21072
+ found = true;
21073
+ break;
21074
+ }
21075
+ }
21076
+ if (!found) throw pgError("undefined_object", `constraint "${name.join(".")}" does not exist`, "42704");
21077
+ }
21078
+ } else {
21079
+ for (const schema of state.schemas.values())
21080
+ for (const table of schema.tables.values())
21081
+ for (const trigger of table.triggers)
21082
+ if (trigger.constraint && trigger.deferrable) matches2.push({ schema: schema.name, trigger });
21083
+ }
21084
+ for (const { trigger } of matches2)
21085
+ if (stmt.deferred && !trigger.deferrable)
21086
+ throw pgError("misuse", `constraint "${trigger.name}" is not deferrable`, "55000");
21087
+ const previous = new Map(state.constraintModes);
21088
+ for (const { schema, trigger } of matches2) state.constraintModes.set(keyOf(schema, trigger.name), stmt.deferred);
21089
+ try {
21090
+ if (!stmt.deferred) flushConstraintTriggers(state);
21091
+ } catch (error) {
21092
+ state.constraintModes = previous;
21093
+ throw error;
21094
+ }
21095
+ }
21096
+
21097
+ // src/transactions/manager.ts
21098
+ var TransactionManager = class {
21099
+ constructor(state) {
21100
+ this.state = state;
21101
+ }
21102
+ state;
21103
+ base = null;
21104
+ savepoints = [];
21105
+ freezeDepth = 0;
21106
+ get inTransaction() {
21107
+ return this.base !== null;
21108
+ }
21109
+ takeSnapshot() {
21110
+ this.state.freezeShared();
21111
+ this.freezeDepth++;
21112
+ return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
21113
+ }
21114
+ restore(snap) {
21115
+ this.state.restoreFrom(snap.state);
21116
+ this.state.prng.setState(snap.prngState);
21117
+ }
21118
+ thawOnce() {
21119
+ if (this.freezeDepth > 0) {
21120
+ this.state.thawShared();
21121
+ this.freezeDepth--;
21122
+ }
21123
+ }
21124
+ begin() {
21125
+ if (this.base !== null) {
21126
+ return;
21127
+ }
21128
+ this.base = this.takeSnapshot();
21129
+ this.state.inTransaction = true;
21130
+ assert(this.freezeDepth === 1, "BEGIN freeze depth");
21131
+ }
21132
+ commit() {
21133
+ try {
21134
+ flushConstraintTriggers(this.state, true);
21135
+ } catch (error) {
21136
+ this.rollback();
21137
+ throw error;
21138
+ }
21139
+ this.base = null;
21140
+ this.savepoints = [];
21141
+ this.state.inTransaction = false;
21142
+ this.state.localSettings.clear();
21143
+ this.state.pendingTriggers = [];
21144
+ this.state.constraintModes.clear();
21145
+ while (this.freezeDepth > 0) this.thawOnce();
21146
+ }
21147
+ rollback() {
21148
+ if (this.base !== null) {
21149
+ this.restore(this.base);
21150
+ }
21151
+ this.base = null;
21152
+ this.savepoints = [];
21153
+ this.state.inTransaction = false;
21154
+ this.state.localSettings.clear();
21155
+ this.state.pendingTriggers = [];
21156
+ this.state.constraintModes.clear();
21157
+ while (this.freezeDepth > 0) this.thawOnce();
21158
+ }
21159
+ savepoint(name) {
21160
+ if (this.base === null) {
21161
+ throw pgError("transaction_state", "SAVEPOINT can only be used in transaction blocks", "25P01");
21162
+ }
21163
+ this.savepoints.push({ name, snap: this.takeSnapshot() });
21164
+ }
21165
+ releaseSavepoint(name) {
21166
+ const idx = this.findSavepoint(name);
21167
+ this.savepoints.splice(idx);
21168
+ }
21169
+ rollbackToSavepoint(name) {
21170
+ const idx = this.findSavepoint(name);
21171
+ const sp = this.savepoints[idx];
21172
+ this.restore(sp.snap);
21173
+ this.savepoints.splice(idx + 1);
21174
+ }
21175
+ findSavepoint(name) {
21176
+ if (this.base === null) {
21177
+ throw pgError("transaction_state", `SAVEPOINT can only be used in transaction blocks`, "25P01");
21178
+ }
21179
+ for (let i = this.savepoints.length - 1; i >= 0; i--) {
21180
+ if (this.savepoints[i].name === name) return i;
21181
+ }
21182
+ throw pgError("invalid_savepoint_specification", `savepoint "${name}" does not exist`, "3B001");
21183
+ }
21184
+ reset() {
21185
+ this.base = null;
21186
+ this.savepoints = [];
21187
+ this.state.inTransaction = false;
21188
+ this.freezeDepth = 0;
21189
+ }
21190
+ };
20952
21191
 
20953
21192
  // src/executor/dml.ts
20954
21193
  function withStatementRollback(env, fn) {
@@ -21638,6 +21877,7 @@ function executeSet(env, stmt) {
21638
21877
  else target.delete(name);
21639
21878
  return commandResult("SET", 0);
21640
21879
  }
21880
+ validateStorageSetting(name, stmt.value);
21641
21881
  target.set(name, stmt.value);
21642
21882
  return commandResult("SET", 0);
21643
21883
  }
@@ -21943,64 +22183,6 @@ function executeCopyFromData(env, stmt, data) {
21943
22183
  };
21944
22184
  }
21945
22185
 
21946
- // src/expressions/context.ts
21947
- var EngineCtx = class {
21948
- state;
21949
- /** transaction timestamp (now(), current_timestamp) — UTC micros since PG epoch */
21950
- txNow;
21951
- /** statement timestamp */
21952
- stmtNow;
21953
- constructor(state, txNow) {
21954
- this.state = state;
21955
- const nowMs = state.clock().getTime();
21956
- const nowMicros = BigInt(Math.round(nowMs)) * 1000n + UNIX_EPOCH_MICROS_FROM_PG;
21957
- this.txNow = txNow ?? nowMicros;
21958
- this.stmtNow = nowMicros;
21959
- }
21960
- timezone() {
21961
- return this.state.getSetting("timezone") ?? "UTC";
21962
- }
21963
- namespaceOid(name) {
21964
- const oid = builtinNamespaceOid(name) ?? this.state.schemas.get(name)?.oid;
21965
- if (oid === void 0) throw pgError("undefined_object", `schema "${name}" does not exist`, "3F000");
21966
- return oid;
21967
- }
21968
- namespaceName(oid) {
21969
- const builtin = builtinNamespaceName(oid);
21970
- if (builtin !== void 0) return builtin;
21971
- for (const schema of this.state.schemas.values()) {
21972
- if (schema.oid === oid) return schema.name;
21973
- }
21974
- return null;
21975
- }
21976
- zoneOffsetAt(utcMicros) {
21977
- return zoneOffsetAtUtc(this.timezone(), utcMicros);
21978
- }
21979
- zoneOffsetForNaive(naiveMicros) {
21980
- return zoneOffsetForNaive(this.timezone(), naiveMicros);
21981
- }
21982
- enumLabels(enumType) {
21983
- if (!isEnumType(enumType)) return null;
21984
- const e = this.state.findEnumByKey(enumType.slice(5));
21985
- return e?.labels ?? null;
21986
- }
21987
- enumHasLabel(enumType, label) {
21988
- const labels = this.enumLabels(enumType);
21989
- return labels === null ? true : labels.includes(label);
21990
- }
21991
- enumOrder(enumType, label) {
21992
- const labels = this.enumLabels(enumType);
21993
- if (!labels) return 0;
21994
- const idx = labels.indexOf(label);
21995
- return idx === -1 ? labels.length : idx;
21996
- }
21997
- requireEnum(enumType) {
21998
- const labels = this.enumLabels(enumType);
21999
- if (!labels) throw pgError("undefined_object", `type "${enumType.slice(5)}" does not exist`);
22000
- return labels;
22001
- }
22002
- };
22003
-
22004
22186
  // src/runtime/clock.ts
22005
22187
  var DEFAULT_NOW = /* @__PURE__ */ new Date("2000-01-01T00:00:00.000Z");
22006
22188
  function fixedClock(instant = DEFAULT_NOW) {
@@ -22968,7 +23150,7 @@ function pgClass(ctx) {
22968
23150
  v.materialized ? "m" : "v",
22969
23151
  v.columns?.length ?? 0,
22970
23152
  v.matRows?.length ?? 0,
22971
- false,
23153
+ v.materialized && [...schemaData.indexes.values()].some((i) => i.tableOid === v.oid),
22972
23154
  !v.materialized || v.matRows !== null,
22973
23155
  relPersistence(v)
22974
23156
  );
@@ -23234,7 +23416,7 @@ function pgIndex(ctx) {
23234
23416
  const valid = i.valid !== false;
23235
23417
  rows.push([
23236
23418
  i.oid ?? 0,
23237
- table?.oid ?? 0,
23419
+ table?.oid ?? i.tableOid ?? 0,
23238
23420
  indexAttributeCount(i),
23239
23421
  i.columns.length,
23240
23422
  i.unique,
@@ -23406,7 +23588,7 @@ function pgStatUserIndexes(ctx) {
23406
23588
  for (const index of schema.indexes.values()) {
23407
23589
  const table = schema.tables.get(index.table);
23408
23590
  rows.push([
23409
- table?.oid ?? 0,
23591
+ table?.oid ?? index.tableOid ?? 0,
23410
23592
  index.oid ?? 0,
23411
23593
  schema.name,
23412
23594
  index.table,
@@ -24001,26 +24183,6 @@ function runTriggerBody(env, table, stmts, vars) {
24001
24183
  }
24002
24184
  }
24003
24185
  }
24004
- function triggerScope(table, newRow, oldRow) {
24005
- const cols = [];
24006
- const row = [];
24007
- const rangeVars = /* @__PURE__ */ new Set();
24008
- if (newRow) {
24009
- rangeVars.add("new");
24010
- for (let i = 0; i < table.columns.length; i++) {
24011
- cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "new" });
24012
- row.push(newRow[i] ?? null);
24013
- }
24014
- }
24015
- if (oldRow) {
24016
- rangeVars.add("old");
24017
- for (let i = 0; i < table.columns.length; i++) {
24018
- cols.push({ name: table.columns[i].name, type: table.columns[i].type.id, table: "old" });
24019
- row.push(oldRow[i] ?? null);
24020
- }
24021
- }
24022
- return new RowScope(cols, row, null, rangeVars);
24023
- }
24024
24186
  function executeTrigger(env, table, trigger, _event, oldRow, newRow) {
24025
24187
  const state = env.ctx.state;
24026
24188
  if (trigger.when !== null && !evalPredicate(env, triggerScope(table, newRow, oldRow), trigger.when)) {
@@ -24987,7 +25149,8 @@ function executeCreateTableAs(env, stmt) {
24987
25149
  }
24988
25150
  function executeCreateIndex(env, stmt) {
24989
25151
  const state = env.ctx.state;
24990
- const table = state.findTable(stmt.table);
25152
+ const view = state.findView(stmt.table);
25153
+ const table = state.findTable(stmt.table) ?? (view?.materialized ? materializedTable(view) : void 0);
24991
25154
  if (!table) {
24992
25155
  throw pgError("undefined_table", `relation "${stmt.table.join(".")}" does not exist`, "42P01");
24993
25156
  }
@@ -25099,13 +25262,22 @@ function executeRefreshMatView(env, stmt) {
25099
25262
  if (!found?.materialized) {
25100
25263
  throw pgError("undefined_table", `materialized view "${stmt.name.join(".")}" does not exist`, "42P01");
25101
25264
  }
25265
+ const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), found.query);
25266
+ const next = {
25267
+ ...found,
25268
+ matColumns: rel3.columns.map((c, i) => ({
25269
+ name: found.columns?.[i] ?? c.name,
25270
+ type: c.type === UNKNOWN ? "text" : c.type
25271
+ })),
25272
+ matRows: stmt.withData ? rel3.rows : null
25273
+ };
25274
+ const table = materializedTable(next);
25275
+ for (const index of env.ctx.state.getSchema(found.schema).indexes.values()) {
25276
+ if (index.tableOid === found.oid && index.valid !== false) assertUniqueIndexBuildable(env, table, index);
25277
+ }
25102
25278
  const view = env.ctx.state.ensureWritableView(found);
25103
- const rel3 = executeSelectStmt(matviewQueryEnv(env, !stmt.withData), view.query);
25104
- view.matColumns = rel3.columns.map((c, i) => ({
25105
- name: view.columns?.[i] ?? c.name,
25106
- type: c.type === UNKNOWN ? "text" : c.type
25107
- }));
25108
- view.matRows = stmt.withData ? rel3.rows : null;
25279
+ view.matColumns = next.matColumns;
25280
+ view.matRows = next.matRows;
25109
25281
  return commandResult("REFRESH MATERIALIZED VIEW", 0);
25110
25282
  }
25111
25283
  function executeCreateSchema(env, stmt) {
@@ -25247,6 +25419,8 @@ function executeCreateFunction(env, stmt) {
25247
25419
  return commandResult("CREATE FUNCTION", 0);
25248
25420
  }
25249
25421
  function executeCreateTrigger(env, stmt) {
25422
+ if (stmt.constraint && (stmt.timing !== "after" || !stmt.forEachRow))
25423
+ throw pgError("syntax", "constraint triggers must be AFTER ROW triggers", "42601");
25250
25424
  const state = env.ctx.state;
25251
25425
  const found = state.findTable(stmt.table);
25252
25426
  if (!found) {
@@ -25265,6 +25439,9 @@ function executeCreateTrigger(env, stmt) {
25265
25439
  }
25266
25440
  const meta = {
25267
25441
  name: stmt.name,
25442
+ constraint: stmt.constraint,
25443
+ deferrable: stmt.deferrable,
25444
+ initiallyDeferred: stmt.initiallyDeferred,
25268
25445
  timing: stmt.timing,
25269
25446
  events: stmt.events,
25270
25447
  forEachRow: stmt.forEachRow,
@@ -26033,7 +26210,16 @@ function alterTableIsSelfContained(stmt) {
26033
26210
  return stmt.actions.length === 1 && action !== void 0 && SELF_CONTAINED_ALTER_ACTIONS.has(action.kind);
26034
26211
  }
26035
26212
  function executeStatement(env, stmt) {
26036
- if (!RESHAPES_TABLES.has(stmt.type)) return dispatchStatement(env, stmt);
26213
+ if (!RESHAPES_TABLES.has(stmt.type)) {
26214
+ 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))) {
26215
+ return withStatementRollback(env, () => {
26216
+ const result = dispatchStatement(env, stmt);
26217
+ flushConstraintTriggers(env.ctx.state);
26218
+ return result;
26219
+ });
26220
+ }
26221
+ return dispatchStatement(env, stmt);
26222
+ }
26037
26223
  try {
26038
26224
  return dispatchStatement(env, stmt);
26039
26225
  } finally {
@@ -26092,6 +26278,9 @@ function dispatchStatement(env, stmt) {
26092
26278
  return executeRefreshMatView(env, stmt);
26093
26279
  case "transaction":
26094
26280
  return executeTransaction(env, stmt);
26281
+ case "set_constraints":
26282
+ setConstraints(env.ctx.state, stmt);
26283
+ return commandResult("SET CONSTRAINTS", 0);
26095
26284
  case "set":
26096
26285
  return executeSet(env, stmt);
26097
26286
  case "show":