@crvouga/mockingbird-service-postgres 3.11.0 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/unstable.js CHANGED
@@ -8566,6 +8566,252 @@ function validateStorageSetting(name, value) {
8566
8566
  }
8567
8567
  }
8568
8568
 
8569
+ // ../../core/dist/fork-map.js
8570
+ var ForkMap = class _ForkMap {
8571
+ owner = {};
8572
+ index = { owner: this.owner, groups: [] };
8573
+ order = { owner: this.owner, cells: [] };
8574
+ shift = 0;
8575
+ length = 0;
8576
+ count = 0;
8577
+ nextSequence = 0;
8578
+ layout = 0;
8579
+ cleared = 0;
8580
+ version = 0;
8581
+ constructor(entries) {
8582
+ if (entries)
8583
+ for (const [key, value] of entries)
8584
+ this.set(key, value);
8585
+ }
8586
+ get size() {
8587
+ return this.count;
8588
+ }
8589
+ get [Symbol.toStringTag]() {
8590
+ return "Map";
8591
+ }
8592
+ fork() {
8593
+ const copy = new _ForkMap();
8594
+ copy.index = this.index;
8595
+ copy.order = this.order;
8596
+ copy.shift = this.shift;
8597
+ copy.length = this.length;
8598
+ copy.count = this.count;
8599
+ copy.nextSequence = this.nextSequence;
8600
+ this.owner = {};
8601
+ return copy;
8602
+ }
8603
+ /** Visit changed hash buckets only. Values must be replaced, rather than mutated in place. */
8604
+ *changes(previous) {
8605
+ if (previous?.index === this.index)
8606
+ return;
8607
+ for (let group = 0; group < 32; group++) {
8608
+ const currentGroup = this.index.groups[group];
8609
+ const oldGroup = previous?.index.groups[group];
8610
+ if (currentGroup === oldGroup)
8611
+ continue;
8612
+ for (let bucket = 0; bucket < 32; bucket++) {
8613
+ const current = currentGroup?.buckets[bucket]?.data;
8614
+ const old = oldGroup?.buckets[bucket]?.data;
8615
+ if (current === old)
8616
+ continue;
8617
+ if (current)
8618
+ for (const [key, entry] of current) {
8619
+ const before = old?.get(key);
8620
+ if (!before || !Object.is(before.value, entry.value) || before.sequence !== entry.sequence)
8621
+ yield { key, value: entry.value, deleted: false };
8622
+ }
8623
+ if (old) {
8624
+ for (const key of old.keys())
8625
+ if (!current?.has(key))
8626
+ yield { key, deleted: true };
8627
+ }
8628
+ }
8629
+ }
8630
+ }
8631
+ get(key) {
8632
+ return this.find(key)?.value;
8633
+ }
8634
+ has(key) {
8635
+ return this.find(key) !== void 0;
8636
+ }
8637
+ set(key, value) {
8638
+ if (typeof key === "number" && key === 0)
8639
+ key = 0;
8640
+ const hash = hashKey(key);
8641
+ const existing = this.bucket(hash)?.get(key);
8642
+ if (existing && Object.is(existing.value, value))
8643
+ return this;
8644
+ const entry = existing ? { ...existing, value } : { key, value, position: this.length++, sequence: this.nextSequence++ };
8645
+ this.writableBucket(hash).set(key, entry);
8646
+ this.writeOrder(entry.position, entry);
8647
+ if (!existing)
8648
+ this.count++;
8649
+ this.version++;
8650
+ return this;
8651
+ }
8652
+ delete(key) {
8653
+ const hash = hashKey(key);
8654
+ const entry = this.bucket(hash)?.get(key);
8655
+ if (!entry)
8656
+ return false;
8657
+ this.writableBucket(hash).delete(key);
8658
+ this.writeOrder(entry.position, void 0);
8659
+ this.count--;
8660
+ this.version++;
8661
+ if (this.length > 1024 && this.length > this.count * 2)
8662
+ this.compact();
8663
+ return true;
8664
+ }
8665
+ clear() {
8666
+ this.index = { owner: this.owner, groups: [] };
8667
+ this.order = { owner: this.owner, cells: [] };
8668
+ this.shift = this.length = this.count = this.nextSequence = 0;
8669
+ this.layout++;
8670
+ this.cleared++;
8671
+ this.version++;
8672
+ }
8673
+ entries() {
8674
+ return this.iterate(0);
8675
+ }
8676
+ keys() {
8677
+ return this.iterate(1);
8678
+ }
8679
+ values() {
8680
+ return this.iterate(2);
8681
+ }
8682
+ [Symbol.iterator]() {
8683
+ return this.entries();
8684
+ }
8685
+ forEach(callback, thisArg) {
8686
+ for (const [key, value] of this)
8687
+ callback.call(thisArg, value, key, this);
8688
+ }
8689
+ find(key) {
8690
+ return this.bucket(hashKey(key))?.get(key);
8691
+ }
8692
+ bucket(hash) {
8693
+ return this.index.groups[hash >>> 5 & 31]?.buckets[hash & 31]?.data;
8694
+ }
8695
+ writableBucket(hash) {
8696
+ if (this.index.owner !== this.owner)
8697
+ this.index = { owner: this.owner, groups: this.index.groups.slice() };
8698
+ const groupIndex = hash >>> 5 & 31;
8699
+ let group = this.index.groups[groupIndex];
8700
+ if (!group || group.owner !== this.owner) {
8701
+ group = { owner: this.owner, buckets: group?.buckets.slice() ?? [] };
8702
+ this.index.groups[groupIndex] = group;
8703
+ }
8704
+ const at = hash & 31;
8705
+ let bucket = group.buckets[at];
8706
+ if (!bucket || bucket.owner !== this.owner) {
8707
+ bucket = { owner: this.owner, data: bucket ? this.copyBucket(bucket.data) : /* @__PURE__ */ new Map() };
8708
+ group.buckets[at] = bucket;
8709
+ }
8710
+ return bucket.data;
8711
+ }
8712
+ copyBucket(bucket) {
8713
+ return new Map(bucket);
8714
+ }
8715
+ leaf(position) {
8716
+ let node = this.order;
8717
+ for (let shift = this.shift; shift > 0; shift -= 5)
8718
+ node = node.cells[Math.floor(position / 2 ** shift) & 31];
8719
+ return node;
8720
+ }
8721
+ writeOrder(position, entry) {
8722
+ while (position >= 2 ** (this.shift + 5)) {
8723
+ this.order = { owner: this.owner, cells: [this.order] };
8724
+ this.shift += 5;
8725
+ }
8726
+ if (this.order.owner !== this.owner)
8727
+ this.order = { owner: this.owner, cells: this.order.cells.slice() };
8728
+ let node = this.order;
8729
+ for (let shift = this.shift; shift > 0; shift -= 5) {
8730
+ const at = Math.floor(position / 2 ** shift) & 31;
8731
+ let child = node.cells[at];
8732
+ if (!child || child.owner !== this.owner) {
8733
+ child = { owner: this.owner, cells: child?.cells.slice() ?? [] };
8734
+ node.cells[at] = child;
8735
+ }
8736
+ node = child;
8737
+ }
8738
+ node.cells[position & 31] = entry;
8739
+ }
8740
+ compact() {
8741
+ const entries = [];
8742
+ for (let position = 0; position < this.length; position++) {
8743
+ const entry = this.leaf(position).cells[position & 31];
8744
+ if (entry)
8745
+ entries.push(entry);
8746
+ }
8747
+ this.index = { owner: this.owner, groups: [] };
8748
+ this.order = { owner: this.owner, cells: [] };
8749
+ this.shift = this.length = this.count = 0;
8750
+ for (const original of entries) {
8751
+ const entry = { ...original, position: this.length++ };
8752
+ this.writableBucket(hashKey(entry.key)).set(entry.key, entry);
8753
+ this.writeOrder(entry.position, entry);
8754
+ this.count++;
8755
+ }
8756
+ this.layout++;
8757
+ }
8758
+ *iterate(kind) {
8759
+ let position = 0;
8760
+ let layout = this.layout;
8761
+ let cleared = this.cleared;
8762
+ let lastSequence = -1;
8763
+ let version = -1;
8764
+ let leaf;
8765
+ let page = -1;
8766
+ for (; ; ) {
8767
+ if (cleared !== this.cleared) {
8768
+ position = 0;
8769
+ lastSequence = -1;
8770
+ cleared = this.cleared;
8771
+ }
8772
+ if (layout !== this.layout) {
8773
+ position = 0;
8774
+ while (position < this.length) {
8775
+ const entry2 = this.leaf(position).cells[position & 31];
8776
+ if (entry2 && entry2.sequence > lastSequence)
8777
+ break;
8778
+ position++;
8779
+ }
8780
+ layout = this.layout;
8781
+ }
8782
+ if (position >= this.length)
8783
+ return;
8784
+ const nextPage = Math.floor(position / 32);
8785
+ if (page !== nextPage || version !== this.version) {
8786
+ leaf = this.leaf(position);
8787
+ page = nextPage;
8788
+ version = this.version;
8789
+ }
8790
+ const entry = leaf?.cells[position++ & 31];
8791
+ if (!entry)
8792
+ continue;
8793
+ lastSequence = entry.sequence;
8794
+ yield kind === 0 ? [entry.key, entry.value] : kind === 1 ? entry.key : entry.value;
8795
+ }
8796
+ }
8797
+ };
8798
+ function hashKey(key) {
8799
+ if (typeof key === "number" && Number.isInteger(key))
8800
+ return mix2((key | 0) ^ (key / 4294967296 | 0));
8801
+ if (typeof key === "bigint")
8802
+ return mix2(Number(BigInt.asIntN(32, key ^ key >> 32n)));
8803
+ const text = String(key);
8804
+ let hash = 2166136261;
8805
+ for (let i = 0; i < text.length; i++)
8806
+ hash = Math.imul(hash ^ text.charCodeAt(i), 16777619);
8807
+ return hash;
8808
+ }
8809
+ function mix2(value) {
8810
+ value = Math.imul(value ^ value >>> 16, 73244475);
8811
+ value = Math.imul(value ^ value >>> 16, 73244475);
8812
+ return value ^ value >>> 16;
8813
+ }
8814
+
8569
8815
  // src/schema/catalog.ts
8570
8816
  var builder = null;
8571
8817
  function setCatalogBuilder(b) {
@@ -8614,10 +8860,81 @@ var PG_CATALOG_RELATIONS = /* @__PURE__ */ new Set([
8614
8860
 
8615
8861
  // src/storage/database-state.ts
8616
8862
  var TableData = class _TableData {
8863
+ /** @internal Optional physical row journal; IDs remain stable across heap splices. */
8864
+ durableChanges = null;
8865
+ durableNextId = 0;
8866
+ durableEpoch;
8867
+ durableUnsafe = false;
8868
+ durableIds;
8869
+ enableDurabilityTracking(ids, nextId) {
8870
+ if (this.durableChanges && !this.durableUnsafe && !ids) return;
8871
+ this.materializeSlab();
8872
+ this.durableIds = /* @__PURE__ */ new WeakMap();
8873
+ this.durableChanges = new ForkMap();
8874
+ this.durableNextId = nextId ?? this.#rows.length;
8875
+ for (let i = 0; i < this.#rows.length; i++) this.durableIds.set(this.#rows[i], ids?.[i] ?? i);
8876
+ this.durableEpoch = {};
8877
+ this.durableUnsafe = false;
8878
+ }
8879
+ durableRowId(row) {
8880
+ const id = this.durableIds?.get(row);
8881
+ if (id === void 0) throw new Error("Untracked durable row");
8882
+ return id;
8883
+ }
8884
+ disableDurabilityTracking() {
8885
+ this.durableChanges = null;
8886
+ this.durableIds = void 0;
8887
+ this.durableEpoch = void 0;
8888
+ this.durableUnsafe = false;
8889
+ }
8890
+ recordDurableRow(row, previous) {
8891
+ if (!this.durableChanges) return;
8892
+ const id = (previous && this.durableIds?.get(previous)) ?? this.durableIds?.get(row) ?? this.durableNextId++;
8893
+ this.durableIds.set(row, id);
8894
+ this.durableChanges.set(id, { row, xmin: this.rowXmins.get(row) ?? 2 });
8895
+ }
8896
+ recordDurableDelete(row) {
8897
+ if (this.durableChanges) this.durableChanges.set(this.durableRowId(row), { row: null, xmin: 0 });
8898
+ }
8899
+ /** @internal Metadata twin with no visits/copies of existing rows or index entries. */
8900
+ emptyForDurability() {
8901
+ const copy = new _TableData(this.schema, this.name, this.columns, this.oid, this.temp);
8902
+ copy.constraints = this.constraints;
8903
+ copy.triggers = this.triggers;
8904
+ copy.unlogged = this.unlogged;
8905
+ return copy;
8906
+ }
8907
+ /** @internal Serialization/SQL scans must not count as public native-array exposure. */
8908
+ serializationRows() {
8909
+ this.materializeSlab();
8910
+ return this.materializedRows();
8911
+ }
8912
+ /** @internal Internal mutable array access, with ordinary public mutableRows kept intact. */
8913
+ writeRows() {
8914
+ return this.serializationRows();
8915
+ }
8916
+ /** @internal Ordinary snapshot branches must not inherit a persistence journal. */
8917
+ untrackedFork() {
8918
+ const copy = this.emptyForDurability();
8919
+ copy.rowXmins = this.rowXmins;
8920
+ copy.slabXmins = this.slabXmins;
8921
+ if (this.slab) copy.slab = this.slab;
8922
+ else {
8923
+ copy.#rows = this.#rows;
8924
+ copy.#rowPatches = this.#rowPatches?.fork() ?? null;
8925
+ copy.#rowsShared = this.#rowsShared = true;
8926
+ copy.tuplesShared = this.tuplesShared = true;
8927
+ }
8928
+ copy.indexStores = this.indexStores ? new Map([...this.indexStores].map(([name, store]) => [name, store.clone()])) : null;
8929
+ return copy;
8930
+ }
8617
8931
  name;
8618
8932
  schema;
8619
8933
  columns;
8620
- rows;
8934
+ #rows = [];
8935
+ #rowPatches = null;
8936
+ #rowsShared = false;
8937
+ #rowsExposed = false;
8621
8938
  rowXmins = /* @__PURE__ */ new WeakMap();
8622
8939
  slabXmins = null;
8623
8940
  /** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
@@ -8633,22 +8950,54 @@ var TableData = class _TableData {
8633
8950
  shareCount = 0;
8634
8951
  /** Derived unique/btree index maps; null until built or invalidated. */
8635
8952
  indexStores = null;
8953
+ /** DML replaces tuples; DDL must detach them before changing cells in place. */
8954
+ tuplesShared = false;
8636
8955
  constructor(schema, name, columns, oid, temp = false) {
8637
8956
  this.schema = schema;
8638
8957
  this.name = name;
8639
8958
  this.columns = columns;
8640
- this.rows = [];
8959
+ Object.defineProperty(this, "rows", {
8960
+ enumerable: true,
8961
+ configurable: true,
8962
+ get: this.nativeRows,
8963
+ set: this.setNativeRows
8964
+ });
8641
8965
  this.constraints = [];
8642
8966
  this.triggers = [];
8643
8967
  this.temp = temp;
8644
8968
  this.oid = oid;
8645
8969
  }
8646
8970
  rowCount() {
8647
- return this.slab ? this.slab.rowCount : this.rows.length;
8971
+ return this.slab ? this.slab.rowCount : this.#rows.length;
8648
8972
  }
8649
8973
  rowAt(index) {
8650
8974
  if (this.slab) return this.slab.rowAt(index);
8651
- return this.rows[index] ?? [];
8975
+ return this.#rowPatches?.get(index) ?? this.#rows[index] ?? [];
8976
+ }
8977
+ /** Replace an existing tuple without copying the array of unchanged row references. */
8978
+ replaceRow(index, row) {
8979
+ if (this.durableChanges) this.recordDurableRow(row, this.rowAt(index));
8980
+ if (this.#rowPatches) this.#rowPatches.set(index, row);
8981
+ else this.#rows[index] = row;
8982
+ }
8983
+ materializedRows() {
8984
+ if (this.#rowsShared) this.#rows = this.#rows.slice();
8985
+ if (this.#rowPatches) for (const [index, row] of this.#rowPatches) this.#rows[index] = row;
8986
+ this.#rowPatches = null;
8987
+ this.#rowsShared = false;
8988
+ return this.#rows;
8989
+ }
8990
+ nativeRows() {
8991
+ if (this.durableChanges) this.durableUnsafe = true;
8992
+ this.#rowsExposed = true;
8993
+ return this.materializedRows();
8994
+ }
8995
+ setNativeRows(rows) {
8996
+ if (this.durableChanges) this.durableUnsafe = true;
8997
+ this.#rows = rows;
8998
+ this.#rowPatches = null;
8999
+ this.#rowsShared = false;
9000
+ this.#rowsExposed = true;
8652
9001
  }
8653
9002
  /** All rows for scans; materializes slab once into `rows` when needed. */
8654
9003
  allRows() {
@@ -8657,13 +9006,15 @@ var TableData = class _TableData {
8657
9006
  }
8658
9007
  attachSlab(slab) {
8659
9008
  this.slab = slab;
8660
- this.rows = [];
9009
+ this.#rows = [];
9010
+ this.#rowPatches = null;
9011
+ this.#rowsShared = false;
8661
9012
  }
8662
9013
  materializeSlab() {
8663
9014
  if (!this.slab) return;
8664
- this.rows = this.slab.materialize();
9015
+ this.#rows = this.slab.materialize();
8665
9016
  if (this.slabXmins)
8666
- this.rows.forEach((row, index) => {
9017
+ this.#rows.forEach((row, index) => {
8667
9018
  this.rowXmins.set(row, this.slabXmins[index] ?? 2);
8668
9019
  });
8669
9020
  this.slabXmins = null;
@@ -8687,7 +9038,7 @@ var TableData = class _TableData {
8687
9038
  columnIndex(name) {
8688
9039
  return this.columns.findIndex((c) => c.name === name);
8689
9040
  }
8690
- clone() {
9041
+ cloneMetadata() {
8691
9042
  const t = new _TableData(
8692
9043
  this.schema,
8693
9044
  this.name,
@@ -8695,21 +9046,68 @@ var TableData = class _TableData {
8695
9046
  this.oid,
8696
9047
  this.temp
8697
9048
  );
9049
+ t.unlogged = this.unlogged;
9050
+ t.durableChanges = this.durableChanges?.fork() ?? null;
9051
+ t.durableIds = this.durableIds;
9052
+ t.durableNextId = this.durableNextId;
9053
+ t.durableEpoch = this.durableEpoch;
9054
+ t.durableUnsafe = this.durableUnsafe;
9055
+ t.constraints = this.constraints.map((c) => ({ ...c }));
9056
+ t.triggers = this.triggers.map((tr) => ({ ...tr }));
9057
+ t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
9058
+ return t;
9059
+ }
9060
+ clone() {
9061
+ const t = this.cloneMetadata();
9062
+ if (t.durableChanges) t.durableUnsafe = true;
8698
9063
  if (this.slab) {
8699
- t.rows = this.slab.materialize().map((r) => r.slice());
9064
+ t.#rows = this.slab.materialize().map((r) => r.slice());
8700
9065
  } else {
8701
- t.rows = this.rows.map((r) => r.slice());
9066
+ t.#rows = this.materializedRows().map((r) => r.slice());
8702
9067
  }
8703
- t.rows.forEach((row, index) => {
8704
- const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this.rows[index]);
9068
+ t.#rows.forEach((row, index) => {
9069
+ const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this.#rows[index]);
8705
9070
  if (xmin !== void 0) t.rowXmins.set(row, xmin);
8706
9071
  });
8707
- t.unlogged = this.unlogged;
8708
- t.constraints = this.constraints.map((c) => ({ ...c }));
8709
- t.triggers = this.triggers.map((tr) => ({ ...tr }));
8710
- t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
8711
9072
  return t;
8712
9073
  }
9074
+ /** DML creates new tuple arrays; retain existing tuples and xmin bindings until DDL. */
9075
+ cloneForDml() {
9076
+ if (this.triggers.length > 0) return this.cloneForWrite();
9077
+ const t = this.cloneMetadata();
9078
+ t.rowXmins = this.rowXmins;
9079
+ if (this.slab) {
9080
+ t.#rows = this.slab.materialize();
9081
+ if (this.slabXmins) {
9082
+ for (let index = 0; index < t.#rows.length; index++)
9083
+ t.rowXmins.set(t.#rows[index], this.slabXmins[index] ?? 2);
9084
+ }
9085
+ } else {
9086
+ if (this.#rows.length >= 4096 && !this.#rowsExposed) {
9087
+ this.#rowPatches ??= new ForkMap();
9088
+ t.#rowPatches = this.#rowPatches.fork();
9089
+ t.#rows = this.#rows;
9090
+ t.#rowsShared = this.#rowsShared = true;
9091
+ } else t.#rows = this.materializedRows().slice();
9092
+ this.tuplesShared = true;
9093
+ }
9094
+ t.tuplesShared = true;
9095
+ return t;
9096
+ }
9097
+ /** A live DML fork can be unfrozen while its unchanged tuples still belong to history. */
9098
+ ownTuples() {
9099
+ if (this.durableChanges) this.durableUnsafe = true;
9100
+ if (!this.tuplesShared) return;
9101
+ const xmins = /* @__PURE__ */ new WeakMap();
9102
+ this.rows = this.rows.map((row) => {
9103
+ const copy = row.slice();
9104
+ const xmin = this.rowXmins.get(row);
9105
+ if (xmin !== void 0) xmins.set(copy, xmin);
9106
+ return copy;
9107
+ });
9108
+ this.rowXmins = xmins;
9109
+ this.tuplesShared = false;
9110
+ }
8713
9111
  /** Independent writable copy (row tuples copied so ALTER COLUMN is isolated). */
8714
9112
  cloneForWrite() {
8715
9113
  const t = this.clone();
@@ -9063,12 +9461,21 @@ var DatabaseState = class _DatabaseState {
9063
9461
  }
9064
9462
  }
9065
9463
  ensureWritableTable(table) {
9066
- if (!table.frozen) return table;
9464
+ if (!table.frozen) {
9465
+ table.ownTuples();
9466
+ return table;
9467
+ }
9067
9468
  const schema = this.schemas.get(table.schema);
9068
9469
  const copy = table.cloneForWrite();
9069
9470
  schema?.tables.set(table.name, copy);
9070
9471
  return copy;
9071
9472
  }
9473
+ ensureWritableTableForDml(table) {
9474
+ if (!table.frozen) return table;
9475
+ const copy = table.cloneForDml();
9476
+ this.schemas.get(table.schema)?.tables.set(table.name, copy);
9477
+ return copy;
9478
+ }
9072
9479
  ensureWritableSequence(seq) {
9073
9480
  if ((seq.shareCount ?? 0) === 0) return seq;
9074
9481
  const schema = this.schemas.get(seq.schema);
@@ -14716,6 +15123,21 @@ var IndexStore = class _IndexStore {
14716
15123
  name;
14717
15124
  unique;
14718
15125
  entries = /* @__PURE__ */ new Map();
15126
+ integerKeys = null;
15127
+ shared = false;
15128
+ ownedBuckets = /* @__PURE__ */ new WeakSet();
15129
+ writableBucket(key, bucket) {
15130
+ if (this.ownedBuckets.has(bucket)) return bucket;
15131
+ const copy = bucket.slice();
15132
+ this.entries.set(key, copy);
15133
+ this.ownedBuckets.add(copy);
15134
+ return copy;
15135
+ }
15136
+ detach() {
15137
+ if (!this.shared) return;
15138
+ if (!(this.entries instanceof ForkMap)) this.entries = new Map(this.entries);
15139
+ this.shared = false;
15140
+ }
14719
15141
  constructor(name, unique = false) {
14720
15142
  this.name = name;
14721
15143
  this.unique = unique;
@@ -14734,36 +15156,78 @@ var IndexStore = class _IndexStore {
14734
15156
  }
14735
15157
  insert(key, rowIdx) {
14736
15158
  if (this.unique) this.checkUnique(key, rowIdx);
15159
+ this.detach();
15160
+ this.integerKeys = null;
14737
15161
  const existing = this.entries.get(key);
14738
15162
  if (!existing) {
14739
- this.entries.set(key, [rowIdx]);
15163
+ const bucket = [rowIdx];
15164
+ this.ownedBuckets.add(bucket);
15165
+ this.entries.set(key, bucket);
14740
15166
  return;
14741
15167
  }
14742
15168
  if (existing.includes(rowIdx)) return;
14743
- existing.push(rowIdx);
15169
+ this.writableBucket(key, existing).push(rowIdx);
14744
15170
  }
14745
15171
  remove(key, rowIdx) {
14746
15172
  const existing = this.entries.get(key);
14747
15173
  if (!existing) return;
14748
15174
  const at = existing.indexOf(rowIdx);
14749
15175
  if (at < 0) return;
14750
- existing.splice(at, 1);
14751
- if (existing.length === 0) this.entries.delete(key);
15176
+ this.detach();
15177
+ this.integerKeys = null;
15178
+ if (existing.length === 1) this.entries.delete(key);
15179
+ else {
15180
+ const remaining = this.writableBucket(key, existing);
15181
+ remaining.splice(at, 1);
15182
+ this.entries.set(key, remaining);
15183
+ }
14752
15184
  }
14753
15185
  lookup(key) {
14754
15186
  return this.entries.get(key) ?? [];
14755
15187
  }
15188
+ /** Ordered access for a single int2/int4 key (the planner validates the key type). */
15189
+ integerRange(lower, upper, includeLower, includeUpper) {
15190
+ const keys = this.integerKeys ??= [...this.entries].map(([key, rows]) => ({ value: Number(key.slice(1)), rows })).sort((a, b) => a.value - b.value);
15191
+ let lo = 0;
15192
+ let hi = keys.length;
15193
+ while (lo < hi) {
15194
+ const mid = lo + hi >>> 1;
15195
+ const value = keys[mid].value;
15196
+ if (value < lower || !includeLower && value === lower) lo = mid + 1;
15197
+ else hi = mid;
15198
+ }
15199
+ const result = [];
15200
+ for (let i = lo; i < keys.length; i++) {
15201
+ const key = keys[i];
15202
+ if (key.value > upper || !includeUpper && key.value === upper) break;
15203
+ result.push(...key.rows);
15204
+ }
15205
+ return result.sort((a, b) => a - b);
15206
+ }
14756
15207
  clear() {
14757
- this.entries.clear();
15208
+ this.entries = /* @__PURE__ */ new Map();
15209
+ this.shared = false;
15210
+ this.integerKeys = null;
14758
15211
  }
14759
15212
  clone() {
14760
15213
  const copy = new _IndexStore(this.name, this.unique);
14761
- for (const [key, rowids] of this.entries) copy.entries.set(key, [...rowids]);
15214
+ if (this.entries.size >= 4096 && !(this.entries instanceof ForkMap)) this.entries = new ForkMap(this.entries);
15215
+ copy.entries = this.entries instanceof ForkMap ? this.entries.fork() : this.entries;
15216
+ copy.integerKeys = this.integerKeys;
15217
+ copy.shared = true;
15218
+ this.shared = true;
15219
+ this.ownedBuckets = /* @__PURE__ */ new WeakSet();
14762
15220
  return copy;
14763
15221
  }
14764
15222
  };
14765
15223
 
14766
15224
  // src/indexes/maintain.ts
15225
+ function prepareDmlIndexStores(env, table) {
15226
+ if (table.indexStores || !table.frozen || table.triggers.length > 0) return;
15227
+ const specs = uniqueSpecsFor(env, table);
15228
+ if (specs.some((spec) => spec.where || spec.keys.some((key) => key.expr))) return;
15229
+ ensureIndexStores(env, table);
15230
+ }
14767
15231
  function ensureIndexStores(env, table) {
14768
15232
  if (table.indexStores) return table.indexStores;
14769
15233
  const stores = /* @__PURE__ */ new Map();
@@ -14814,8 +15278,19 @@ function indexRemoveRow(env, table, rowIdx, row) {
14814
15278
  }
14815
15279
  }
14816
15280
  function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
14817
- indexRemoveRow(env, table, rowIdx, oldRow);
14818
- indexInsertRow(env, table, rowIdx, newRow);
15281
+ const specs = uniqueSpecsFor(env, table);
15282
+ if (specs.some((spec) => spec.where || spec.keys.some((key) => key.expr))) {
15283
+ indexRemoveRow(env, table, rowIdx, oldRow);
15284
+ indexInsertRow(env, table, rowIdx, newRow);
15285
+ return;
15286
+ }
15287
+ for (const spec of specs) {
15288
+ const before = uniqueKeyOf(env, table, spec, oldRow);
15289
+ const after = uniqueKeyOf(env, table, spec, newRow);
15290
+ if (before === after) continue;
15291
+ if (before !== null) table.indexStores?.get(spec.name)?.remove(before, rowIdx);
15292
+ if (after !== null) indexStoreFor(env, table, spec).insert(after, rowIdx);
15293
+ }
14819
15294
  }
14820
15295
  function rebuildTableIndexes(env, table) {
14821
15296
  table.indexStores = null;
@@ -15032,6 +15507,82 @@ function conjunctions(expr) {
15032
15507
  }
15033
15508
  return [expr];
15034
15509
  }
15510
+ function tryPointRowIndices(env, table, alias, where) {
15511
+ const eqs = [];
15512
+ for (const part of conjunctions(where)) {
15513
+ const eq = equalityAgainstConst(part);
15514
+ if (!eq || !["param", "number_lit", "string_lit", "bool_lit", "null_lit"].includes(eq.valueExpr.type)) return null;
15515
+ if (part.type !== "binop") return null;
15516
+ const column = part.left.type === "colref" ? part.left : part.right;
15517
+ if (column.type !== "colref") return null;
15518
+ if (column.parts.length > 1 && column.parts[column.parts.length - 2] !== alias) return null;
15519
+ eqs.push(eq);
15520
+ }
15521
+ for (const spec of uniqueSpecsFor(env, table)) {
15522
+ if (spec.where || spec.keys.length !== eqs.length || spec.keys.some((key2) => key2.expr) || !eqs.every((eq, index) => eq.column === spec.columnNames[index]))
15523
+ continue;
15524
+ const values = table.columns.map(() => null);
15525
+ let matches2 = true;
15526
+ for (let index = 0; index < spec.keys.length; index++) {
15527
+ const key2 = spec.keys[index];
15528
+ const column = table.columns[key2.colIdx];
15529
+ const eq = eqs[index];
15530
+ if (!column || !eq || column.domain) {
15531
+ matches2 = false;
15532
+ break;
15533
+ }
15534
+ const value = evalExpr(env.ctx, makeEvalScope(env, null), eq.valueExpr);
15535
+ if (value.t !== column.type.id && !(column.type.id === "text" && value.t === "unknown" && typeof value.v === "string")) {
15536
+ matches2 = false;
15537
+ break;
15538
+ }
15539
+ values[key2.colIdx] = value.v;
15540
+ }
15541
+ if (!matches2) continue;
15542
+ const key = uniqueKeyOf(env, table, spec, values);
15543
+ return { indices: key === null ? [] : indexStoreFor(env, table, spec).lookup(key), indexName: spec.name };
15544
+ }
15545
+ return null;
15546
+ }
15547
+ function recordIndexScan(env, table, name) {
15548
+ const schema = env.ctx.state.schemas.get(table.schema);
15549
+ const index = schema?.indexes.get(name);
15550
+ if (schema && index) {
15551
+ const lastScan = env.ctx.state.clock().getTime();
15552
+ schema.indexes.set(name, { ...index, scans: (index.scans ?? 0n) + 1n, lastScan });
15553
+ }
15554
+ }
15555
+ function tryRangeRowIndices(env, table, alias, where) {
15556
+ const parts = conjunctions(where);
15557
+ if (parts.length !== 2) return null;
15558
+ const bounds = [];
15559
+ for (const part of parts) {
15560
+ if (part.type !== "binop" || ![">", ">=", "<", "<="].includes(part.op)) return null;
15561
+ const column2 = part.left.type === "colref" ? part.left : part.right;
15562
+ const value = part.left.type === "colref" ? part.right : part.left;
15563
+ if (column2.type !== "colref" || column2.parts.length > 2 || column2.parts.length === 2 && column2.parts[0] !== alias || !["param", "number_lit"].includes(value.type))
15564
+ return null;
15565
+ const op = part.left === column2 ? part.op : { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[part.op];
15566
+ bounds.push({ column: column2.parts.at(-1), op, value });
15567
+ }
15568
+ const lower = bounds.find((bound) => bound.op === ">" || bound.op === ">=");
15569
+ const upper = bounds.find((bound) => bound.op === "<" || bound.op === "<=");
15570
+ if (!lower || !upper || lower.column !== upper.column) return null;
15571
+ const column = table.columns.find((column2) => column2.name === lower.column);
15572
+ if (!column || column.domain || !["int2", "int4"].includes(column.type.id)) return null;
15573
+ for (const spec of uniqueSpecsFor(env, table)) {
15574
+ if (spec.where || spec.keys.length !== 1 || spec.keys[0].expr || spec.columnNames[0] !== lower.column) continue;
15575
+ const low = evalExpr(env.ctx, makeEvalScope(env, null), lower.value);
15576
+ const high = evalExpr(env.ctx, makeEvalScope(env, null), upper.value);
15577
+ if (low.t !== column.type.id || high.t !== column.type.id || typeof low.v !== "number" || typeof high.v !== "number")
15578
+ return null;
15579
+ return {
15580
+ indices: indexStoreFor(env, table, spec).integerRange(low.v, high.v, lower.op === ">=", upper.op === "<="),
15581
+ indexName: spec.name
15582
+ };
15583
+ }
15584
+ return null;
15585
+ }
15035
15586
  function tryIndexedTableRows(env, table, alias, where) {
15036
15587
  const parts = conjunctions(where);
15037
15588
  const eqs = [];
@@ -15068,12 +15619,7 @@ function tryIndexedTableRows(env, table, alias, where) {
15068
15619
  const key = uniqueKeyOf(env, table, spec, probeRow);
15069
15620
  if (key === null) return [];
15070
15621
  const hits = indexStoreFor(env, table, spec).lookup(key);
15071
- const schema = env.ctx.state.schemas.get(table.schema);
15072
- const index = schema?.indexes.get(spec.name);
15073
- if (schema && index) {
15074
- const lastScan = env.ctx.state.clock().getTime();
15075
- schema.indexes.set(spec.name, { ...index, scans: (index.scans ?? 0n) + 1n, lastScan });
15076
- }
15622
+ recordIndexScan(env, table, spec.name);
15077
15623
  return hits.map((i) => table.rowAt(i));
15078
15624
  }
15079
15625
  function tryIndexedFromItem(env, item, where) {
@@ -21059,7 +21605,7 @@ function renameWithAliases(rel4, label, colAliases, what) {
21059
21605
  }));
21060
21606
  return { columns, rows: rel4.rows };
21061
21607
  }
21062
- function materializeItem(env, item, scope) {
21608
+ function materializeItem(env, item, scope, shapeOnly = false) {
21063
21609
  switch (item.type) {
21064
21610
  case "from_table": {
21065
21611
  const label = item.alias ?? item.name[item.name.length - 1];
@@ -21078,7 +21624,7 @@ function materializeItem(env, item, scope) {
21078
21624
  ...table.columns.map((c) => ({ name: c.name, type: c.type.id, mod: c.type.mod, table: label })),
21079
21625
  { name: "xmin", type: "xid", table: label, hidden: true, system: true }
21080
21626
  ],
21081
- rows: table.rows.map((row) => [...row, table.rowXmins.get(row) ?? 2])
21627
+ rows: shapeOnly ? [] : table.serializationRows().map((row) => [...row, table.rowXmins.get(row) ?? 2])
21082
21628
  };
21083
21629
  return { rel: renameWithAliases(rel4, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
21084
21630
  }
@@ -22351,14 +22897,30 @@ function checkPredicateType(env, columns, rangeVars, e, kind, extras) {
22351
22897
  function executeCore(env0, core, orderBy, opts) {
22352
22898
  const env = env0;
22353
22899
  const ctx = env.ctx;
22354
- const source = buildFrom(env, core.from, core.where);
22900
+ const item = core.from.length === 1 ? core.from[0] : null;
22901
+ const pointTable = item?.type === "from_table" && !item.colAliases?.length && !(item.name.length === 1 && env.ctes.has(item.name[0])) ? env.ctx.state.findTable(item.name) : null;
22902
+ const point = pointTable && core.where && item?.type === "from_table" ? tryPointRowIndices(env, pointTable, item.alias ?? item.name[item.name.length - 1], core.where) : null;
22903
+ const range = !point && pointTable && core.where && item?.type === "from_table" ? tryRangeRowIndices(env, pointTable, item.alias ?? item.name[item.name.length - 1], core.where) : null;
22904
+ const access = point ?? range;
22905
+ const source = access && item ? materializeItem(env, item, env.outer, true) : buildFrom(env, core.from, core.where);
22355
22906
  const srcCols = source.rel.columns;
22356
22907
  const rangeVars = source.rangeVars;
22357
22908
  const rowScope = (row) => new RowScope(srcCols, row, env.outer, rangeVars);
22358
22909
  let srcRows = source.rel.rows;
22359
22910
  if (core.where) {
22360
22911
  checkPredicateType(env, srcCols, rangeVars, core.where, "WHERE");
22361
- if (core.from.length === 1) {
22912
+ if (access && pointTable) {
22913
+ pointTable.materializeSlab();
22914
+ srcRows = access.indices.map((index) => {
22915
+ const row = pointTable.rowAt(index);
22916
+ return [...row, pointTable.rowXmins.get(row) ?? 2];
22917
+ });
22918
+ if (range) {
22919
+ const where = core.where;
22920
+ srcRows = srcRows.filter((row) => evalPredicate2(env, rowScope(row), where));
22921
+ }
22922
+ recordIndexScan(env, pointTable, access.indexName);
22923
+ } else if (core.from.length === 1) {
22362
22924
  const indexed = tryIndexedFromItem(env, core.from[0], core.where);
22363
22925
  if (indexed !== null) {
22364
22926
  srcRows = indexed;
@@ -22539,11 +23101,11 @@ function executeCore(env0, core, orderBy, opts) {
22539
23101
  }
22540
23102
  }
22541
23103
  }
22542
- const item = { name: `__ord${orderExprs.length}`, kind: "expr", colIdx: -1, expr: ob.expr, hidden: true };
23104
+ const item2 = { name: `__ord${orderExprs.length}`, kind: "expr", colIdx: -1, expr: ob.expr, hidden: true };
22543
23105
  const spec = { colIdx: projItems.length, dir, nullsFirst };
22544
- projItems.push(item);
23106
+ projItems.push(item2);
22545
23107
  sortSpecs.push(spec);
22546
- orderExprs.push({ item, spec });
23108
+ orderExprs.push({ item: item2, spec });
22547
23109
  }
22548
23110
  if (core.distinct && !core.distinct.on && sortSpecs.length > 0 && !opts?.hasLimit) {
22549
23111
  const used = new Set(sortSpecs.map((s) => s.colIdx));
@@ -27437,7 +27999,10 @@ function withStatementRollback(env, fn) {
27437
27999
  function requireTargetTable(env, parts, verb) {
27438
28000
  const state = env.ctx.state;
27439
28001
  const table = state.findTable(parts);
27440
- if (table) return state.ensureWritableTable(table);
28002
+ if (table) {
28003
+ prepareDmlIndexStores(env, table);
28004
+ return state.ensureWritableTableForDml(table);
28005
+ }
27441
28006
  const view = state.findView(parts);
27442
28007
  if (view) {
27443
28008
  throw pgError("wrong_object_type", `cannot ${verb} view "${view.name}"`, "42809");
@@ -27605,7 +28170,7 @@ function resolveArbiters(env, table, clause) {
27605
28170
  return matches2.map((spec) => ({ spec }));
27606
28171
  }
27607
28172
  function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, where) {
27608
- const rows = table.mutableRows();
28173
+ const rows = table.writeRows();
27609
28174
  const existing = rows[existingIdx];
27610
28175
  const cols = [
27611
28176
  ...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
@@ -27617,7 +28182,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
27617
28182
  applyUpdateSets(env, table, sets, scope, updated);
27618
28183
  computeGeneratedColumns(env, table, updated);
27619
28184
  table.rowXmins.set(updated, env.ctx.rowVersionXid());
27620
- rows[existingIdx] = updated;
28185
+ table.replaceRow(existingIdx, updated);
27621
28186
  indexUpdateRow(env, table, existingIdx, existing, updated);
27622
28187
  checkNotNull(env, table, updated);
27623
28188
  checkChecks(env, table, updated);
@@ -27629,7 +28194,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
27629
28194
  function executeInsert(env0, stmt) {
27630
28195
  const env = applyWith(env0, stmt.with);
27631
28196
  const table = requireTargetTable(env, stmt.table, "insert into");
27632
- const rows = table.mutableRows();
28197
+ const rows = table.writeRows();
27633
28198
  const label = stmt.alias ?? table.name;
27634
28199
  const colIdxs = insertTargetColumns(env, table, stmt);
27635
28200
  const sourceRows = insertSourceRows(env, stmt, colIdxs);
@@ -27713,6 +28278,7 @@ function executeInsert(env0, stmt) {
27713
28278
  checkForeignKeys(env, table, newRow);
27714
28279
  table.rowXmins.set(newRow, env.ctx.rowVersionXid());
27715
28280
  rows.push(newRow);
28281
+ table.recordDurableRow(newRow);
27716
28282
  indexInsertRow(env, table, rows.length - 1, newRow);
27717
28283
  insertedCount++;
27718
28284
  insertedRows.push(newRow);
@@ -27805,13 +28371,17 @@ function executeUpdate(env0, stmt) {
27805
28371
  const label = stmt.alias ?? table.name;
27806
28372
  fireStatementTriggers(env, table, "before", "update");
27807
28373
  const fromSource = stmt.from.length > 0 ? buildFrom(env, stmt.from) : null;
27808
- const rows = table.mutableRows();
27809
28374
  const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
27810
28375
  const updatedRows = [];
27811
28376
  const replacedRows = [];
27812
28377
  let updateCount = 0;
27813
- for (let ri = 0; ri < rows.length; ri++) {
27814
- const oldRow = rows[ri];
28378
+ const point = !fromSource && stmt.where && table.triggers.length === 0 && !table.columns.some((column) => column.generated || column.domain) && !table.constraints.some((constraint) => constraint.kind === "check") && stmt.sets.every(
28379
+ (set) => set.columns.every((column) => column.subscripts === null && column.fields.length === 0) && "type" in set.value && isPureUpdateValue(set.value)
28380
+ ) && referencingConstraints(env, table).length === 0 ? tryPointRowIndices(env, table, label, stmt.where) : null;
28381
+ const rows = point ? null : table.writeRows();
28382
+ for (let candidate = 0; candidate < (point ? point.indices.length : rows.length); candidate++) {
28383
+ const ri = point ? point.indices[candidate] : candidate;
28384
+ const oldRow = point ? table.rowAt(ri) : rows[ri];
27815
28385
  let matchScope = null;
27816
28386
  if (fromSource) {
27817
28387
  let found = false;
@@ -27837,14 +28407,14 @@ function executeUpdate(env0, stmt) {
27837
28407
  const finalRow = fired.row;
27838
28408
  computeGeneratedColumns(env, table, finalRow);
27839
28409
  table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
27840
- rows[ri] = finalRow;
28410
+ table.replaceRow(ri, finalRow);
27841
28411
  try {
27842
28412
  checkNotNull(env, table, finalRow);
27843
28413
  checkChecks(env, table, finalRow);
27844
28414
  checkUnique(env, table, finalRow, ri);
27845
28415
  checkForeignKeys(env, table, finalRow);
27846
28416
  } catch (err) {
27847
- rows[ri] = oldRow;
28417
+ table.replaceRow(ri, oldRow);
27848
28418
  throw err;
27849
28419
  }
27850
28420
  indexUpdateRow(env, table, ri, oldRow, finalRow);
@@ -27862,12 +28432,29 @@ function executeUpdate(env0, stmt) {
27862
28432
  }
27863
28433
  return commandResult("UPDATE", updateCount);
27864
28434
  }
28435
+ function isPureUpdateValue(expr) {
28436
+ switch (expr.type) {
28437
+ case "param":
28438
+ case "number_lit":
28439
+ case "string_lit":
28440
+ case "bool_lit":
28441
+ case "null_lit":
28442
+ case "colref":
28443
+ return true;
28444
+ case "binop":
28445
+ return isPureUpdateValue(expr.left) && isPureUpdateValue(expr.right);
28446
+ case "unop":
28447
+ return isPureUpdateValue(expr.operand);
28448
+ default:
28449
+ return false;
28450
+ }
28451
+ }
27865
28452
  function executeDelete(env0, stmt) {
27866
28453
  const env = applyWith(env0, stmt.with);
27867
28454
  const table = requireTargetTable(env, stmt.table, "delete from");
27868
28455
  const label = stmt.alias ?? table.name;
27869
28456
  fireStatementTriggers(env, table, "before", "delete");
27870
- const rows = table.mutableRows();
28457
+ const rows = table.writeRows();
27871
28458
  const usingSource = stmt.using.length > 0 ? buildFrom(env, stmt.using) : null;
27872
28459
  const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
27873
28460
  const toDelete = [];
@@ -27897,6 +28484,7 @@ function executeDelete(env0, stmt) {
27897
28484
  if (skipped.has(ri)) continue;
27898
28485
  const row = rows[ri];
27899
28486
  handleReferencedDelete(env, table, row, 0);
28487
+ table.recordDurableDelete(row);
27900
28488
  rows.splice(ri, 1);
27901
28489
  deletedRows.unshift(row);
27902
28490
  }
@@ -27943,13 +28531,14 @@ function handleReferencedDelete(env, table, row, depth) {
27943
28531
  const idxs = referencingRowIdxs(ref, key);
27944
28532
  if (idxs.length === 0) continue;
27945
28533
  const action = con.onDelete ?? "no_action";
27946
- const child = env.ctx.state.ensureWritableTable(ref.table);
27947
- const childRows = child.mutableRows();
28534
+ const child = env.ctx.state.ensureWritableTableForDml(ref.table);
28535
+ const childRows = child.writeRows();
27948
28536
  switch (action) {
27949
28537
  case "cascade": {
27950
28538
  for (let k = idxs.length - 1; k >= 0; k--) {
27951
28539
  const childRow = childRows[idxs[k]];
27952
28540
  handleReferencedDelete(env, child, childRow, depth + 1);
28541
+ child.recordDurableDelete(childRow);
27953
28542
  childRows.splice(idxs[k], 1);
27954
28543
  }
27955
28544
  break;
@@ -27961,7 +28550,7 @@ function handleReferencedDelete(env, table, row, depth) {
27961
28550
  newRow[child.columnIndex(c)] = null;
27962
28551
  }
27963
28552
  child.rowXmins.set(newRow, env.ctx.rowVersionXid());
27964
- childRows[i] = newRow;
28553
+ child.replaceRow(i, newRow);
27965
28554
  checkNotNull(env, child, newRow);
27966
28555
  checkChecks(env, child, newRow);
27967
28556
  }
@@ -27975,7 +28564,7 @@ function handleReferencedDelete(env, table, row, depth) {
27975
28564
  newRow[ci] = columnDefault(env, child, child.columns[ci]);
27976
28565
  }
27977
28566
  child.rowXmins.set(newRow, env.ctx.rowVersionXid());
27978
- childRows[i] = newRow;
28567
+ child.replaceRow(i, newRow);
27979
28568
  checkNotNull(env, child, newRow);
27980
28569
  checkChecks(env, child, newRow);
27981
28570
  checkForeignKeys(env, child, newRow);
@@ -28001,8 +28590,8 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
28001
28590
  const idxs = referencingRowIdxs(ref, oldKey);
28002
28591
  if (idxs.length === 0) continue;
28003
28592
  const action = con.onUpdate ?? "no_action";
28004
- const child = env.ctx.state.ensureWritableTable(ref.table);
28005
- const childRows = child.mutableRows();
28593
+ const child = env.ctx.state.ensureWritableTableForDml(ref.table);
28594
+ const childRows = child.writeRows();
28006
28595
  switch (action) {
28007
28596
  case "cascade": {
28008
28597
  for (const i of idxs) {
@@ -28013,7 +28602,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
28013
28602
  newRow[localIdx] = newRows[r][refIdx] ?? null;
28014
28603
  }
28015
28604
  child.rowXmins.set(newRow, env.ctx.rowVersionXid());
28016
- childRows[i] = newRow;
28605
+ child.replaceRow(i, newRow);
28017
28606
  checkChecks(env, child, newRow);
28018
28607
  }
28019
28608
  break;
@@ -28023,7 +28612,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
28023
28612
  const newRow = childRows[i].slice();
28024
28613
  for (const c of con.columns) newRow[child.columnIndex(c)] = null;
28025
28614
  child.rowXmins.set(newRow, env.ctx.rowVersionXid());
28026
- childRows[i] = newRow;
28615
+ child.replaceRow(i, newRow);
28027
28616
  checkNotNull(env, child, newRow);
28028
28617
  }
28029
28618
  break;
@@ -28036,7 +28625,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
28036
28625
  newRow[ci] = columnDefault(env, child, child.columns[ci]);
28037
28626
  }
28038
28627
  child.rowXmins.set(newRow, env.ctx.rowVersionXid());
28039
- childRows[i] = newRow;
28628
+ child.replaceRow(i, newRow);
28040
28629
  checkForeignKeys(env, child, newRow);
28041
28630
  }
28042
28631
  break;
@@ -28199,7 +28788,7 @@ var TransactionManager = class {
28199
28788
  return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
28200
28789
  }
28201
28790
  restore(snap) {
28202
- this.state.restoreFrom(snap.state);
28791
+ this.state.restoreFrom(snap.state.cloneShallow());
28203
28792
  this.state.prng.setState(snap.prngState);
28204
28793
  }
28205
28794
  thawOnce() {