@crvouga/mockingbird-service-postgres 3.11.0 → 3.12.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/CHANGELOG.md +7 -0
- package/README.md +89 -1
- package/dist/admin.js +708 -82
- package/dist/admin.js.map +4 -4
- package/dist/api/buffered.d.ts +7 -0
- package/dist/api/durable.d.ts +12 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1985 -287
- package/dist/index.js.map +4 -4
- package/dist/indexes/index.d.ts +7 -0
- package/dist/indexes/maintain.d.ts +2 -0
- package/dist/planner/access.d.ts +15 -0
- package/dist/serialization/wire.d.ts +4 -0
- package/dist/server.js +772 -113
- package/dist/server.js.map +4 -4
- package/dist/shared/core/durability-browser.d.ts +52 -0
- package/dist/shared/core/durability-buffered.d.ts +65 -0
- package/dist/shared/core/durability.d.ts +78 -0
- package/dist/shared/core/fork-map.d.ts +51 -0
- package/dist/shared/core/index.d.ts +4 -0
- package/dist/shared/core/mount.d.ts +17 -0
- package/dist/shared/core/timeline.d.ts +80 -0
- package/dist/storage/database-state.d.ts +23 -0
- package/dist/unstable.js +647 -58
- package/dist/unstable.js.map +4 -4
- package/dist/wire/cli.js +773 -114
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/index.js +772 -113
- package/dist/wire/index.js.map +4 -4
- package/package.json +1 -1
package/dist/wire/index.js
CHANGED
|
@@ -1,6 +1,252 @@
|
|
|
1
1
|
// src/wire/index.ts
|
|
2
2
|
import { createServer } from "node:net";
|
|
3
3
|
|
|
4
|
+
// ../../core/dist/fork-map.js
|
|
5
|
+
var ForkMap = class _ForkMap {
|
|
6
|
+
owner = {};
|
|
7
|
+
index = { owner: this.owner, groups: [] };
|
|
8
|
+
order = { owner: this.owner, cells: [] };
|
|
9
|
+
shift = 0;
|
|
10
|
+
length = 0;
|
|
11
|
+
count = 0;
|
|
12
|
+
nextSequence = 0;
|
|
13
|
+
layout = 0;
|
|
14
|
+
cleared = 0;
|
|
15
|
+
version = 0;
|
|
16
|
+
constructor(entries) {
|
|
17
|
+
if (entries)
|
|
18
|
+
for (const [key, value] of entries)
|
|
19
|
+
this.set(key, value);
|
|
20
|
+
}
|
|
21
|
+
get size() {
|
|
22
|
+
return this.count;
|
|
23
|
+
}
|
|
24
|
+
get [Symbol.toStringTag]() {
|
|
25
|
+
return "Map";
|
|
26
|
+
}
|
|
27
|
+
fork() {
|
|
28
|
+
const copy = new _ForkMap();
|
|
29
|
+
copy.index = this.index;
|
|
30
|
+
copy.order = this.order;
|
|
31
|
+
copy.shift = this.shift;
|
|
32
|
+
copy.length = this.length;
|
|
33
|
+
copy.count = this.count;
|
|
34
|
+
copy.nextSequence = this.nextSequence;
|
|
35
|
+
this.owner = {};
|
|
36
|
+
return copy;
|
|
37
|
+
}
|
|
38
|
+
/** Visit changed hash buckets only. Values must be replaced, rather than mutated in place. */
|
|
39
|
+
*changes(previous) {
|
|
40
|
+
if (previous?.index === this.index)
|
|
41
|
+
return;
|
|
42
|
+
for (let group = 0; group < 32; group++) {
|
|
43
|
+
const currentGroup = this.index.groups[group];
|
|
44
|
+
const oldGroup = previous?.index.groups[group];
|
|
45
|
+
if (currentGroup === oldGroup)
|
|
46
|
+
continue;
|
|
47
|
+
for (let bucket = 0; bucket < 32; bucket++) {
|
|
48
|
+
const current = currentGroup?.buckets[bucket]?.data;
|
|
49
|
+
const old = oldGroup?.buckets[bucket]?.data;
|
|
50
|
+
if (current === old)
|
|
51
|
+
continue;
|
|
52
|
+
if (current)
|
|
53
|
+
for (const [key, entry] of current) {
|
|
54
|
+
const before = old?.get(key);
|
|
55
|
+
if (!before || !Object.is(before.value, entry.value) || before.sequence !== entry.sequence)
|
|
56
|
+
yield { key, value: entry.value, deleted: false };
|
|
57
|
+
}
|
|
58
|
+
if (old) {
|
|
59
|
+
for (const key of old.keys())
|
|
60
|
+
if (!current?.has(key))
|
|
61
|
+
yield { key, deleted: true };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
get(key) {
|
|
67
|
+
return this.find(key)?.value;
|
|
68
|
+
}
|
|
69
|
+
has(key) {
|
|
70
|
+
return this.find(key) !== void 0;
|
|
71
|
+
}
|
|
72
|
+
set(key, value) {
|
|
73
|
+
if (typeof key === "number" && key === 0)
|
|
74
|
+
key = 0;
|
|
75
|
+
const hash = hashKey(key);
|
|
76
|
+
const existing = this.bucket(hash)?.get(key);
|
|
77
|
+
if (existing && Object.is(existing.value, value))
|
|
78
|
+
return this;
|
|
79
|
+
const entry = existing ? { ...existing, value } : { key, value, position: this.length++, sequence: this.nextSequence++ };
|
|
80
|
+
this.writableBucket(hash).set(key, entry);
|
|
81
|
+
this.writeOrder(entry.position, entry);
|
|
82
|
+
if (!existing)
|
|
83
|
+
this.count++;
|
|
84
|
+
this.version++;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
delete(key) {
|
|
88
|
+
const hash = hashKey(key);
|
|
89
|
+
const entry = this.bucket(hash)?.get(key);
|
|
90
|
+
if (!entry)
|
|
91
|
+
return false;
|
|
92
|
+
this.writableBucket(hash).delete(key);
|
|
93
|
+
this.writeOrder(entry.position, void 0);
|
|
94
|
+
this.count--;
|
|
95
|
+
this.version++;
|
|
96
|
+
if (this.length > 1024 && this.length > this.count * 2)
|
|
97
|
+
this.compact();
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
clear() {
|
|
101
|
+
this.index = { owner: this.owner, groups: [] };
|
|
102
|
+
this.order = { owner: this.owner, cells: [] };
|
|
103
|
+
this.shift = this.length = this.count = this.nextSequence = 0;
|
|
104
|
+
this.layout++;
|
|
105
|
+
this.cleared++;
|
|
106
|
+
this.version++;
|
|
107
|
+
}
|
|
108
|
+
entries() {
|
|
109
|
+
return this.iterate(0);
|
|
110
|
+
}
|
|
111
|
+
keys() {
|
|
112
|
+
return this.iterate(1);
|
|
113
|
+
}
|
|
114
|
+
values() {
|
|
115
|
+
return this.iterate(2);
|
|
116
|
+
}
|
|
117
|
+
[Symbol.iterator]() {
|
|
118
|
+
return this.entries();
|
|
119
|
+
}
|
|
120
|
+
forEach(callback, thisArg) {
|
|
121
|
+
for (const [key, value] of this)
|
|
122
|
+
callback.call(thisArg, value, key, this);
|
|
123
|
+
}
|
|
124
|
+
find(key) {
|
|
125
|
+
return this.bucket(hashKey(key))?.get(key);
|
|
126
|
+
}
|
|
127
|
+
bucket(hash) {
|
|
128
|
+
return this.index.groups[hash >>> 5 & 31]?.buckets[hash & 31]?.data;
|
|
129
|
+
}
|
|
130
|
+
writableBucket(hash) {
|
|
131
|
+
if (this.index.owner !== this.owner)
|
|
132
|
+
this.index = { owner: this.owner, groups: this.index.groups.slice() };
|
|
133
|
+
const groupIndex = hash >>> 5 & 31;
|
|
134
|
+
let group = this.index.groups[groupIndex];
|
|
135
|
+
if (!group || group.owner !== this.owner) {
|
|
136
|
+
group = { owner: this.owner, buckets: group?.buckets.slice() ?? [] };
|
|
137
|
+
this.index.groups[groupIndex] = group;
|
|
138
|
+
}
|
|
139
|
+
const at = hash & 31;
|
|
140
|
+
let bucket = group.buckets[at];
|
|
141
|
+
if (!bucket || bucket.owner !== this.owner) {
|
|
142
|
+
bucket = { owner: this.owner, data: bucket ? this.copyBucket(bucket.data) : /* @__PURE__ */ new Map() };
|
|
143
|
+
group.buckets[at] = bucket;
|
|
144
|
+
}
|
|
145
|
+
return bucket.data;
|
|
146
|
+
}
|
|
147
|
+
copyBucket(bucket) {
|
|
148
|
+
return new Map(bucket);
|
|
149
|
+
}
|
|
150
|
+
leaf(position) {
|
|
151
|
+
let node = this.order;
|
|
152
|
+
for (let shift = this.shift; shift > 0; shift -= 5)
|
|
153
|
+
node = node.cells[Math.floor(position / 2 ** shift) & 31];
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
writeOrder(position, entry) {
|
|
157
|
+
while (position >= 2 ** (this.shift + 5)) {
|
|
158
|
+
this.order = { owner: this.owner, cells: [this.order] };
|
|
159
|
+
this.shift += 5;
|
|
160
|
+
}
|
|
161
|
+
if (this.order.owner !== this.owner)
|
|
162
|
+
this.order = { owner: this.owner, cells: this.order.cells.slice() };
|
|
163
|
+
let node = this.order;
|
|
164
|
+
for (let shift = this.shift; shift > 0; shift -= 5) {
|
|
165
|
+
const at = Math.floor(position / 2 ** shift) & 31;
|
|
166
|
+
let child = node.cells[at];
|
|
167
|
+
if (!child || child.owner !== this.owner) {
|
|
168
|
+
child = { owner: this.owner, cells: child?.cells.slice() ?? [] };
|
|
169
|
+
node.cells[at] = child;
|
|
170
|
+
}
|
|
171
|
+
node = child;
|
|
172
|
+
}
|
|
173
|
+
node.cells[position & 31] = entry;
|
|
174
|
+
}
|
|
175
|
+
compact() {
|
|
176
|
+
const entries = [];
|
|
177
|
+
for (let position = 0; position < this.length; position++) {
|
|
178
|
+
const entry = this.leaf(position).cells[position & 31];
|
|
179
|
+
if (entry)
|
|
180
|
+
entries.push(entry);
|
|
181
|
+
}
|
|
182
|
+
this.index = { owner: this.owner, groups: [] };
|
|
183
|
+
this.order = { owner: this.owner, cells: [] };
|
|
184
|
+
this.shift = this.length = this.count = 0;
|
|
185
|
+
for (const original of entries) {
|
|
186
|
+
const entry = { ...original, position: this.length++ };
|
|
187
|
+
this.writableBucket(hashKey(entry.key)).set(entry.key, entry);
|
|
188
|
+
this.writeOrder(entry.position, entry);
|
|
189
|
+
this.count++;
|
|
190
|
+
}
|
|
191
|
+
this.layout++;
|
|
192
|
+
}
|
|
193
|
+
*iterate(kind) {
|
|
194
|
+
let position = 0;
|
|
195
|
+
let layout = this.layout;
|
|
196
|
+
let cleared = this.cleared;
|
|
197
|
+
let lastSequence = -1;
|
|
198
|
+
let version = -1;
|
|
199
|
+
let leaf;
|
|
200
|
+
let page = -1;
|
|
201
|
+
for (; ; ) {
|
|
202
|
+
if (cleared !== this.cleared) {
|
|
203
|
+
position = 0;
|
|
204
|
+
lastSequence = -1;
|
|
205
|
+
cleared = this.cleared;
|
|
206
|
+
}
|
|
207
|
+
if (layout !== this.layout) {
|
|
208
|
+
position = 0;
|
|
209
|
+
while (position < this.length) {
|
|
210
|
+
const entry2 = this.leaf(position).cells[position & 31];
|
|
211
|
+
if (entry2 && entry2.sequence > lastSequence)
|
|
212
|
+
break;
|
|
213
|
+
position++;
|
|
214
|
+
}
|
|
215
|
+
layout = this.layout;
|
|
216
|
+
}
|
|
217
|
+
if (position >= this.length)
|
|
218
|
+
return;
|
|
219
|
+
const nextPage = Math.floor(position / 32);
|
|
220
|
+
if (page !== nextPage || version !== this.version) {
|
|
221
|
+
leaf = this.leaf(position);
|
|
222
|
+
page = nextPage;
|
|
223
|
+
version = this.version;
|
|
224
|
+
}
|
|
225
|
+
const entry = leaf?.cells[position++ & 31];
|
|
226
|
+
if (!entry)
|
|
227
|
+
continue;
|
|
228
|
+
lastSequence = entry.sequence;
|
|
229
|
+
yield kind === 0 ? [entry.key, entry.value] : kind === 1 ? entry.key : entry.value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
function hashKey(key) {
|
|
234
|
+
if (typeof key === "number" && Number.isInteger(key))
|
|
235
|
+
return mix((key | 0) ^ (key / 4294967296 | 0));
|
|
236
|
+
if (typeof key === "bigint")
|
|
237
|
+
return mix(Number(BigInt.asIntN(32, key ^ key >> 32n)));
|
|
238
|
+
const text = String(key);
|
|
239
|
+
let hash = 2166136261;
|
|
240
|
+
for (let i = 0; i < text.length; i++)
|
|
241
|
+
hash = Math.imul(hash ^ text.charCodeAt(i), 16777619);
|
|
242
|
+
return hash;
|
|
243
|
+
}
|
|
244
|
+
function mix(value) {
|
|
245
|
+
value = Math.imul(value ^ value >>> 16, 73244475);
|
|
246
|
+
value = Math.imul(value ^ value >>> 16, 73244475);
|
|
247
|
+
return value ^ value >>> 16;
|
|
248
|
+
}
|
|
249
|
+
|
|
4
250
|
// ../../core/dist/timeline.js
|
|
5
251
|
var BRANCH_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
6
252
|
var Timeline = class {
|
|
@@ -4794,10 +5040,81 @@ function parseArrayText(t, text, ctx) {
|
|
|
4794
5040
|
|
|
4795
5041
|
// src/storage/database-state.ts
|
|
4796
5042
|
var TableData = class _TableData {
|
|
5043
|
+
/** @internal Optional physical row journal; IDs remain stable across heap splices. */
|
|
5044
|
+
durableChanges = null;
|
|
5045
|
+
durableNextId = 0;
|
|
5046
|
+
durableEpoch;
|
|
5047
|
+
durableUnsafe = false;
|
|
5048
|
+
durableIds;
|
|
5049
|
+
enableDurabilityTracking(ids, nextId) {
|
|
5050
|
+
if (this.durableChanges && !this.durableUnsafe && !ids) return;
|
|
5051
|
+
this.materializeSlab();
|
|
5052
|
+
this.durableIds = /* @__PURE__ */ new WeakMap();
|
|
5053
|
+
this.durableChanges = new ForkMap();
|
|
5054
|
+
this.durableNextId = nextId ?? this.#rows.length;
|
|
5055
|
+
for (let i = 0; i < this.#rows.length; i++) this.durableIds.set(this.#rows[i], ids?.[i] ?? i);
|
|
5056
|
+
this.durableEpoch = {};
|
|
5057
|
+
this.durableUnsafe = false;
|
|
5058
|
+
}
|
|
5059
|
+
durableRowId(row) {
|
|
5060
|
+
const id = this.durableIds?.get(row);
|
|
5061
|
+
if (id === void 0) throw new Error("Untracked durable row");
|
|
5062
|
+
return id;
|
|
5063
|
+
}
|
|
5064
|
+
disableDurabilityTracking() {
|
|
5065
|
+
this.durableChanges = null;
|
|
5066
|
+
this.durableIds = void 0;
|
|
5067
|
+
this.durableEpoch = void 0;
|
|
5068
|
+
this.durableUnsafe = false;
|
|
5069
|
+
}
|
|
5070
|
+
recordDurableRow(row, previous) {
|
|
5071
|
+
if (!this.durableChanges) return;
|
|
5072
|
+
const id = (previous && this.durableIds?.get(previous)) ?? this.durableIds?.get(row) ?? this.durableNextId++;
|
|
5073
|
+
this.durableIds.set(row, id);
|
|
5074
|
+
this.durableChanges.set(id, { row, xmin: this.rowXmins.get(row) ?? 2 });
|
|
5075
|
+
}
|
|
5076
|
+
recordDurableDelete(row) {
|
|
5077
|
+
if (this.durableChanges) this.durableChanges.set(this.durableRowId(row), { row: null, xmin: 0 });
|
|
5078
|
+
}
|
|
5079
|
+
/** @internal Metadata twin with no visits/copies of existing rows or index entries. */
|
|
5080
|
+
emptyForDurability() {
|
|
5081
|
+
const copy = new _TableData(this.schema, this.name, this.columns, this.oid, this.temp);
|
|
5082
|
+
copy.constraints = this.constraints;
|
|
5083
|
+
copy.triggers = this.triggers;
|
|
5084
|
+
copy.unlogged = this.unlogged;
|
|
5085
|
+
return copy;
|
|
5086
|
+
}
|
|
5087
|
+
/** @internal Serialization/SQL scans must not count as public native-array exposure. */
|
|
5088
|
+
serializationRows() {
|
|
5089
|
+
this.materializeSlab();
|
|
5090
|
+
return this.materializedRows();
|
|
5091
|
+
}
|
|
5092
|
+
/** @internal Internal mutable array access, with ordinary public mutableRows kept intact. */
|
|
5093
|
+
writeRows() {
|
|
5094
|
+
return this.serializationRows();
|
|
5095
|
+
}
|
|
5096
|
+
/** @internal Ordinary snapshot branches must not inherit a persistence journal. */
|
|
5097
|
+
untrackedFork() {
|
|
5098
|
+
const copy = this.emptyForDurability();
|
|
5099
|
+
copy.rowXmins = this.rowXmins;
|
|
5100
|
+
copy.slabXmins = this.slabXmins;
|
|
5101
|
+
if (this.slab) copy.slab = this.slab;
|
|
5102
|
+
else {
|
|
5103
|
+
copy.#rows = this.#rows;
|
|
5104
|
+
copy.#rowPatches = this.#rowPatches?.fork() ?? null;
|
|
5105
|
+
copy.#rowsShared = this.#rowsShared = true;
|
|
5106
|
+
copy.tuplesShared = this.tuplesShared = true;
|
|
5107
|
+
}
|
|
5108
|
+
copy.indexStores = this.indexStores ? new Map([...this.indexStores].map(([name, store]) => [name, store.clone()])) : null;
|
|
5109
|
+
return copy;
|
|
5110
|
+
}
|
|
4797
5111
|
name;
|
|
4798
5112
|
schema;
|
|
4799
5113
|
columns;
|
|
4800
|
-
rows;
|
|
5114
|
+
#rows = [];
|
|
5115
|
+
#rowPatches = null;
|
|
5116
|
+
#rowsShared = false;
|
|
5117
|
+
#rowsExposed = false;
|
|
4801
5118
|
rowXmins = /* @__PURE__ */ new WeakMap();
|
|
4802
5119
|
slabXmins = null;
|
|
4803
5120
|
/** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
|
|
@@ -4813,22 +5130,54 @@ var TableData = class _TableData {
|
|
|
4813
5130
|
shareCount = 0;
|
|
4814
5131
|
/** Derived unique/btree index maps; null until built or invalidated. */
|
|
4815
5132
|
indexStores = null;
|
|
5133
|
+
/** DML replaces tuples; DDL must detach them before changing cells in place. */
|
|
5134
|
+
tuplesShared = false;
|
|
4816
5135
|
constructor(schema, name, columns, oid, temp = false) {
|
|
4817
5136
|
this.schema = schema;
|
|
4818
5137
|
this.name = name;
|
|
4819
5138
|
this.columns = columns;
|
|
4820
|
-
this
|
|
5139
|
+
Object.defineProperty(this, "rows", {
|
|
5140
|
+
enumerable: true,
|
|
5141
|
+
configurable: true,
|
|
5142
|
+
get: this.nativeRows,
|
|
5143
|
+
set: this.setNativeRows
|
|
5144
|
+
});
|
|
4821
5145
|
this.constraints = [];
|
|
4822
5146
|
this.triggers = [];
|
|
4823
5147
|
this.temp = temp;
|
|
4824
5148
|
this.oid = oid;
|
|
4825
5149
|
}
|
|
4826
5150
|
rowCount() {
|
|
4827
|
-
return this.slab ? this.slab.rowCount : this
|
|
5151
|
+
return this.slab ? this.slab.rowCount : this.#rows.length;
|
|
4828
5152
|
}
|
|
4829
5153
|
rowAt(index) {
|
|
4830
5154
|
if (this.slab) return this.slab.rowAt(index);
|
|
4831
|
-
return this
|
|
5155
|
+
return this.#rowPatches?.get(index) ?? this.#rows[index] ?? [];
|
|
5156
|
+
}
|
|
5157
|
+
/** Replace an existing tuple without copying the array of unchanged row references. */
|
|
5158
|
+
replaceRow(index, row) {
|
|
5159
|
+
if (this.durableChanges) this.recordDurableRow(row, this.rowAt(index));
|
|
5160
|
+
if (this.#rowPatches) this.#rowPatches.set(index, row);
|
|
5161
|
+
else this.#rows[index] = row;
|
|
5162
|
+
}
|
|
5163
|
+
materializedRows() {
|
|
5164
|
+
if (this.#rowsShared) this.#rows = this.#rows.slice();
|
|
5165
|
+
if (this.#rowPatches) for (const [index, row] of this.#rowPatches) this.#rows[index] = row;
|
|
5166
|
+
this.#rowPatches = null;
|
|
5167
|
+
this.#rowsShared = false;
|
|
5168
|
+
return this.#rows;
|
|
5169
|
+
}
|
|
5170
|
+
nativeRows() {
|
|
5171
|
+
if (this.durableChanges) this.durableUnsafe = true;
|
|
5172
|
+
this.#rowsExposed = true;
|
|
5173
|
+
return this.materializedRows();
|
|
5174
|
+
}
|
|
5175
|
+
setNativeRows(rows) {
|
|
5176
|
+
if (this.durableChanges) this.durableUnsafe = true;
|
|
5177
|
+
this.#rows = rows;
|
|
5178
|
+
this.#rowPatches = null;
|
|
5179
|
+
this.#rowsShared = false;
|
|
5180
|
+
this.#rowsExposed = true;
|
|
4832
5181
|
}
|
|
4833
5182
|
/** All rows for scans; materializes slab once into `rows` when needed. */
|
|
4834
5183
|
allRows() {
|
|
@@ -4837,13 +5186,15 @@ var TableData = class _TableData {
|
|
|
4837
5186
|
}
|
|
4838
5187
|
attachSlab(slab) {
|
|
4839
5188
|
this.slab = slab;
|
|
4840
|
-
this
|
|
5189
|
+
this.#rows = [];
|
|
5190
|
+
this.#rowPatches = null;
|
|
5191
|
+
this.#rowsShared = false;
|
|
4841
5192
|
}
|
|
4842
5193
|
materializeSlab() {
|
|
4843
5194
|
if (!this.slab) return;
|
|
4844
|
-
this
|
|
5195
|
+
this.#rows = this.slab.materialize();
|
|
4845
5196
|
if (this.slabXmins)
|
|
4846
|
-
this
|
|
5197
|
+
this.#rows.forEach((row, index) => {
|
|
4847
5198
|
this.rowXmins.set(row, this.slabXmins[index] ?? 2);
|
|
4848
5199
|
});
|
|
4849
5200
|
this.slabXmins = null;
|
|
@@ -4867,7 +5218,7 @@ var TableData = class _TableData {
|
|
|
4867
5218
|
columnIndex(name) {
|
|
4868
5219
|
return this.columns.findIndex((c) => c.name === name);
|
|
4869
5220
|
}
|
|
4870
|
-
|
|
5221
|
+
cloneMetadata() {
|
|
4871
5222
|
const t = new _TableData(
|
|
4872
5223
|
this.schema,
|
|
4873
5224
|
this.name,
|
|
@@ -4875,21 +5226,68 @@ var TableData = class _TableData {
|
|
|
4875
5226
|
this.oid,
|
|
4876
5227
|
this.temp
|
|
4877
5228
|
);
|
|
5229
|
+
t.unlogged = this.unlogged;
|
|
5230
|
+
t.durableChanges = this.durableChanges?.fork() ?? null;
|
|
5231
|
+
t.durableIds = this.durableIds;
|
|
5232
|
+
t.durableNextId = this.durableNextId;
|
|
5233
|
+
t.durableEpoch = this.durableEpoch;
|
|
5234
|
+
t.durableUnsafe = this.durableUnsafe;
|
|
5235
|
+
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
5236
|
+
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
5237
|
+
t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
|
|
5238
|
+
return t;
|
|
5239
|
+
}
|
|
5240
|
+
clone() {
|
|
5241
|
+
const t = this.cloneMetadata();
|
|
5242
|
+
if (t.durableChanges) t.durableUnsafe = true;
|
|
4878
5243
|
if (this.slab) {
|
|
4879
|
-
t
|
|
5244
|
+
t.#rows = this.slab.materialize().map((r) => r.slice());
|
|
4880
5245
|
} else {
|
|
4881
|
-
t
|
|
5246
|
+
t.#rows = this.materializedRows().map((r) => r.slice());
|
|
4882
5247
|
}
|
|
4883
|
-
t
|
|
4884
|
-
const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this
|
|
5248
|
+
t.#rows.forEach((row, index) => {
|
|
5249
|
+
const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this.#rows[index]);
|
|
4885
5250
|
if (xmin !== void 0) t.rowXmins.set(row, xmin);
|
|
4886
5251
|
});
|
|
4887
|
-
t.unlogged = this.unlogged;
|
|
4888
|
-
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
4889
|
-
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
4890
|
-
t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
|
|
4891
5252
|
return t;
|
|
4892
5253
|
}
|
|
5254
|
+
/** DML creates new tuple arrays; retain existing tuples and xmin bindings until DDL. */
|
|
5255
|
+
cloneForDml() {
|
|
5256
|
+
if (this.triggers.length > 0) return this.cloneForWrite();
|
|
5257
|
+
const t = this.cloneMetadata();
|
|
5258
|
+
t.rowXmins = this.rowXmins;
|
|
5259
|
+
if (this.slab) {
|
|
5260
|
+
t.#rows = this.slab.materialize();
|
|
5261
|
+
if (this.slabXmins) {
|
|
5262
|
+
for (let index = 0; index < t.#rows.length; index++)
|
|
5263
|
+
t.rowXmins.set(t.#rows[index], this.slabXmins[index] ?? 2);
|
|
5264
|
+
}
|
|
5265
|
+
} else {
|
|
5266
|
+
if (this.#rows.length >= 4096 && !this.#rowsExposed) {
|
|
5267
|
+
this.#rowPatches ??= new ForkMap();
|
|
5268
|
+
t.#rowPatches = this.#rowPatches.fork();
|
|
5269
|
+
t.#rows = this.#rows;
|
|
5270
|
+
t.#rowsShared = this.#rowsShared = true;
|
|
5271
|
+
} else t.#rows = this.materializedRows().slice();
|
|
5272
|
+
this.tuplesShared = true;
|
|
5273
|
+
}
|
|
5274
|
+
t.tuplesShared = true;
|
|
5275
|
+
return t;
|
|
5276
|
+
}
|
|
5277
|
+
/** A live DML fork can be unfrozen while its unchanged tuples still belong to history. */
|
|
5278
|
+
ownTuples() {
|
|
5279
|
+
if (this.durableChanges) this.durableUnsafe = true;
|
|
5280
|
+
if (!this.tuplesShared) return;
|
|
5281
|
+
const xmins = /* @__PURE__ */ new WeakMap();
|
|
5282
|
+
this.rows = this.rows.map((row) => {
|
|
5283
|
+
const copy = row.slice();
|
|
5284
|
+
const xmin = this.rowXmins.get(row);
|
|
5285
|
+
if (xmin !== void 0) xmins.set(copy, xmin);
|
|
5286
|
+
return copy;
|
|
5287
|
+
});
|
|
5288
|
+
this.rowXmins = xmins;
|
|
5289
|
+
this.tuplesShared = false;
|
|
5290
|
+
}
|
|
4893
5291
|
/** Independent writable copy (row tuples copied so ALTER COLUMN is isolated). */
|
|
4894
5292
|
cloneForWrite() {
|
|
4895
5293
|
const t = this.clone();
|
|
@@ -5243,12 +5641,21 @@ var DatabaseState = class _DatabaseState {
|
|
|
5243
5641
|
}
|
|
5244
5642
|
}
|
|
5245
5643
|
ensureWritableTable(table) {
|
|
5246
|
-
if (!table.frozen)
|
|
5644
|
+
if (!table.frozen) {
|
|
5645
|
+
table.ownTuples();
|
|
5646
|
+
return table;
|
|
5647
|
+
}
|
|
5247
5648
|
const schema = this.schemas.get(table.schema);
|
|
5248
5649
|
const copy = table.cloneForWrite();
|
|
5249
5650
|
schema?.tables.set(table.name, copy);
|
|
5250
5651
|
return copy;
|
|
5251
5652
|
}
|
|
5653
|
+
ensureWritableTableForDml(table) {
|
|
5654
|
+
if (!table.frozen) return table;
|
|
5655
|
+
const copy = table.cloneForDml();
|
|
5656
|
+
this.schemas.get(table.schema)?.tables.set(table.name, copy);
|
|
5657
|
+
return copy;
|
|
5658
|
+
}
|
|
5252
5659
|
ensureWritableSequence(seq) {
|
|
5253
5660
|
if ((seq.shareCount ?? 0) === 0) return seq;
|
|
5254
5661
|
const schema = this.schemas.get(seq.schema);
|
|
@@ -8364,7 +8771,7 @@ function evalPgcryptoFunction(ctx, fn, args) {
|
|
|
8364
8771
|
// src/functions/hash-fns.ts
|
|
8365
8772
|
var utf83 = new TextEncoder();
|
|
8366
8773
|
var rot = (x, k) => (x << k | x >>> 32 - k) >>> 0;
|
|
8367
|
-
function
|
|
8774
|
+
function mix2(s) {
|
|
8368
8775
|
s[0] = s[0] - s[2] >>> 0;
|
|
8369
8776
|
s[0] = (s[0] ^ rot(s[2], 4)) >>> 0;
|
|
8370
8777
|
s[2] = s[2] + s[1] >>> 0;
|
|
@@ -8408,7 +8815,7 @@ function hashAny(k, seed) {
|
|
|
8408
8815
|
const bits = BigInt.asUintN(64, seed);
|
|
8409
8816
|
s[0] = s[0] + Number(bits >> 32n) >>> 0;
|
|
8410
8817
|
s[1] = s[1] + Number(bits & 0xffffffffn) >>> 0;
|
|
8411
|
-
|
|
8818
|
+
mix2(s);
|
|
8412
8819
|
}
|
|
8413
8820
|
let at = 0;
|
|
8414
8821
|
let len = k.length;
|
|
@@ -8416,7 +8823,7 @@ function hashAny(k, seed) {
|
|
|
8416
8823
|
s[0] = s[0] + word(k, at) >>> 0;
|
|
8417
8824
|
s[1] = s[1] + word(k, at + 4) >>> 0;
|
|
8418
8825
|
s[2] = s[2] + word(k, at + 8) >>> 0;
|
|
8419
|
-
|
|
8826
|
+
mix2(s);
|
|
8420
8827
|
}
|
|
8421
8828
|
for (let i = len - 1; i >= 0; i--) {
|
|
8422
8829
|
const byte = k[at + i];
|
|
@@ -14988,6 +15395,21 @@ var IndexStore = class _IndexStore {
|
|
|
14988
15395
|
name;
|
|
14989
15396
|
unique;
|
|
14990
15397
|
entries = /* @__PURE__ */ new Map();
|
|
15398
|
+
integerKeys = null;
|
|
15399
|
+
shared = false;
|
|
15400
|
+
ownedBuckets = /* @__PURE__ */ new WeakSet();
|
|
15401
|
+
writableBucket(key, bucket) {
|
|
15402
|
+
if (this.ownedBuckets.has(bucket)) return bucket;
|
|
15403
|
+
const copy = bucket.slice();
|
|
15404
|
+
this.entries.set(key, copy);
|
|
15405
|
+
this.ownedBuckets.add(copy);
|
|
15406
|
+
return copy;
|
|
15407
|
+
}
|
|
15408
|
+
detach() {
|
|
15409
|
+
if (!this.shared) return;
|
|
15410
|
+
if (!(this.entries instanceof ForkMap)) this.entries = new Map(this.entries);
|
|
15411
|
+
this.shared = false;
|
|
15412
|
+
}
|
|
14991
15413
|
constructor(name, unique = false) {
|
|
14992
15414
|
this.name = name;
|
|
14993
15415
|
this.unique = unique;
|
|
@@ -15006,36 +15428,78 @@ var IndexStore = class _IndexStore {
|
|
|
15006
15428
|
}
|
|
15007
15429
|
insert(key, rowIdx) {
|
|
15008
15430
|
if (this.unique) this.checkUnique(key, rowIdx);
|
|
15431
|
+
this.detach();
|
|
15432
|
+
this.integerKeys = null;
|
|
15009
15433
|
const existing = this.entries.get(key);
|
|
15010
15434
|
if (!existing) {
|
|
15011
|
-
|
|
15435
|
+
const bucket = [rowIdx];
|
|
15436
|
+
this.ownedBuckets.add(bucket);
|
|
15437
|
+
this.entries.set(key, bucket);
|
|
15012
15438
|
return;
|
|
15013
15439
|
}
|
|
15014
15440
|
if (existing.includes(rowIdx)) return;
|
|
15015
|
-
existing.push(rowIdx);
|
|
15441
|
+
this.writableBucket(key, existing).push(rowIdx);
|
|
15016
15442
|
}
|
|
15017
15443
|
remove(key, rowIdx) {
|
|
15018
15444
|
const existing = this.entries.get(key);
|
|
15019
15445
|
if (!existing) return;
|
|
15020
15446
|
const at = existing.indexOf(rowIdx);
|
|
15021
15447
|
if (at < 0) return;
|
|
15022
|
-
|
|
15023
|
-
|
|
15448
|
+
this.detach();
|
|
15449
|
+
this.integerKeys = null;
|
|
15450
|
+
if (existing.length === 1) this.entries.delete(key);
|
|
15451
|
+
else {
|
|
15452
|
+
const remaining = this.writableBucket(key, existing);
|
|
15453
|
+
remaining.splice(at, 1);
|
|
15454
|
+
this.entries.set(key, remaining);
|
|
15455
|
+
}
|
|
15024
15456
|
}
|
|
15025
15457
|
lookup(key) {
|
|
15026
15458
|
return this.entries.get(key) ?? [];
|
|
15027
15459
|
}
|
|
15460
|
+
/** Ordered access for a single int2/int4 key (the planner validates the key type). */
|
|
15461
|
+
integerRange(lower, upper, includeLower, includeUpper) {
|
|
15462
|
+
const keys = this.integerKeys ??= [...this.entries].map(([key, rows]) => ({ value: Number(key.slice(1)), rows })).sort((a, b) => a.value - b.value);
|
|
15463
|
+
let lo = 0;
|
|
15464
|
+
let hi = keys.length;
|
|
15465
|
+
while (lo < hi) {
|
|
15466
|
+
const mid = lo + hi >>> 1;
|
|
15467
|
+
const value = keys[mid].value;
|
|
15468
|
+
if (value < lower || !includeLower && value === lower) lo = mid + 1;
|
|
15469
|
+
else hi = mid;
|
|
15470
|
+
}
|
|
15471
|
+
const result = [];
|
|
15472
|
+
for (let i = lo; i < keys.length; i++) {
|
|
15473
|
+
const key = keys[i];
|
|
15474
|
+
if (key.value > upper || !includeUpper && key.value === upper) break;
|
|
15475
|
+
result.push(...key.rows);
|
|
15476
|
+
}
|
|
15477
|
+
return result.sort((a, b) => a - b);
|
|
15478
|
+
}
|
|
15028
15479
|
clear() {
|
|
15029
|
-
this.entries
|
|
15480
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
15481
|
+
this.shared = false;
|
|
15482
|
+
this.integerKeys = null;
|
|
15030
15483
|
}
|
|
15031
15484
|
clone() {
|
|
15032
15485
|
const copy = new _IndexStore(this.name, this.unique);
|
|
15033
|
-
|
|
15486
|
+
if (this.entries.size >= 4096 && !(this.entries instanceof ForkMap)) this.entries = new ForkMap(this.entries);
|
|
15487
|
+
copy.entries = this.entries instanceof ForkMap ? this.entries.fork() : this.entries;
|
|
15488
|
+
copy.integerKeys = this.integerKeys;
|
|
15489
|
+
copy.shared = true;
|
|
15490
|
+
this.shared = true;
|
|
15491
|
+
this.ownedBuckets = /* @__PURE__ */ new WeakSet();
|
|
15034
15492
|
return copy;
|
|
15035
15493
|
}
|
|
15036
15494
|
};
|
|
15037
15495
|
|
|
15038
15496
|
// src/indexes/maintain.ts
|
|
15497
|
+
function prepareDmlIndexStores(env, table) {
|
|
15498
|
+
if (table.indexStores || !table.frozen || table.triggers.length > 0) return;
|
|
15499
|
+
const specs = uniqueSpecsFor(env, table);
|
|
15500
|
+
if (specs.some((spec) => spec.where || spec.keys.some((key) => key.expr))) return;
|
|
15501
|
+
ensureIndexStores(env, table);
|
|
15502
|
+
}
|
|
15039
15503
|
function ensureIndexStores(env, table) {
|
|
15040
15504
|
if (table.indexStores) return table.indexStores;
|
|
15041
15505
|
const stores = /* @__PURE__ */ new Map();
|
|
@@ -15086,8 +15550,19 @@ function indexRemoveRow(env, table, rowIdx, row) {
|
|
|
15086
15550
|
}
|
|
15087
15551
|
}
|
|
15088
15552
|
function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
|
|
15089
|
-
|
|
15090
|
-
|
|
15553
|
+
const specs = uniqueSpecsFor(env, table);
|
|
15554
|
+
if (specs.some((spec) => spec.where || spec.keys.some((key) => key.expr))) {
|
|
15555
|
+
indexRemoveRow(env, table, rowIdx, oldRow);
|
|
15556
|
+
indexInsertRow(env, table, rowIdx, newRow);
|
|
15557
|
+
return;
|
|
15558
|
+
}
|
|
15559
|
+
for (const spec of specs) {
|
|
15560
|
+
const before = uniqueKeyOf(env, table, spec, oldRow);
|
|
15561
|
+
const after = uniqueKeyOf(env, table, spec, newRow);
|
|
15562
|
+
if (before === after) continue;
|
|
15563
|
+
if (before !== null) table.indexStores?.get(spec.name)?.remove(before, rowIdx);
|
|
15564
|
+
if (after !== null) indexStoreFor(env, table, spec).insert(after, rowIdx);
|
|
15565
|
+
}
|
|
15091
15566
|
}
|
|
15092
15567
|
function rebuildTableIndexes(env, table) {
|
|
15093
15568
|
table.indexStores = null;
|
|
@@ -15304,6 +15779,82 @@ function conjunctions(expr) {
|
|
|
15304
15779
|
}
|
|
15305
15780
|
return [expr];
|
|
15306
15781
|
}
|
|
15782
|
+
function tryPointRowIndices(env, table, alias, where) {
|
|
15783
|
+
const eqs = [];
|
|
15784
|
+
for (const part of conjunctions(where)) {
|
|
15785
|
+
const eq = equalityAgainstConst(part);
|
|
15786
|
+
if (!eq || !["param", "number_lit", "string_lit", "bool_lit", "null_lit"].includes(eq.valueExpr.type)) return null;
|
|
15787
|
+
if (part.type !== "binop") return null;
|
|
15788
|
+
const column = part.left.type === "colref" ? part.left : part.right;
|
|
15789
|
+
if (column.type !== "colref") return null;
|
|
15790
|
+
if (column.parts.length > 1 && column.parts[column.parts.length - 2] !== alias) return null;
|
|
15791
|
+
eqs.push(eq);
|
|
15792
|
+
}
|
|
15793
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15794
|
+
if (spec.where || spec.keys.length !== eqs.length || spec.keys.some((key2) => key2.expr) || !eqs.every((eq, index) => eq.column === spec.columnNames[index]))
|
|
15795
|
+
continue;
|
|
15796
|
+
const values = table.columns.map(() => null);
|
|
15797
|
+
let matches2 = true;
|
|
15798
|
+
for (let index = 0; index < spec.keys.length; index++) {
|
|
15799
|
+
const key2 = spec.keys[index];
|
|
15800
|
+
const column = table.columns[key2.colIdx];
|
|
15801
|
+
const eq = eqs[index];
|
|
15802
|
+
if (!column || !eq || column.domain) {
|
|
15803
|
+
matches2 = false;
|
|
15804
|
+
break;
|
|
15805
|
+
}
|
|
15806
|
+
const value = evalExpr(env.ctx, makeEvalScope(env, null), eq.valueExpr);
|
|
15807
|
+
if (value.t !== column.type.id && !(column.type.id === "text" && value.t === "unknown" && typeof value.v === "string")) {
|
|
15808
|
+
matches2 = false;
|
|
15809
|
+
break;
|
|
15810
|
+
}
|
|
15811
|
+
values[key2.colIdx] = value.v;
|
|
15812
|
+
}
|
|
15813
|
+
if (!matches2) continue;
|
|
15814
|
+
const key = uniqueKeyOf(env, table, spec, values);
|
|
15815
|
+
return { indices: key === null ? [] : indexStoreFor(env, table, spec).lookup(key), indexName: spec.name };
|
|
15816
|
+
}
|
|
15817
|
+
return null;
|
|
15818
|
+
}
|
|
15819
|
+
function recordIndexScan(env, table, name) {
|
|
15820
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
15821
|
+
const index = schema?.indexes.get(name);
|
|
15822
|
+
if (schema && index) {
|
|
15823
|
+
const lastScan = env.ctx.state.clock().getTime();
|
|
15824
|
+
schema.indexes.set(name, { ...index, scans: (index.scans ?? 0n) + 1n, lastScan });
|
|
15825
|
+
}
|
|
15826
|
+
}
|
|
15827
|
+
function tryRangeRowIndices(env, table, alias, where) {
|
|
15828
|
+
const parts = conjunctions(where);
|
|
15829
|
+
if (parts.length !== 2) return null;
|
|
15830
|
+
const bounds = [];
|
|
15831
|
+
for (const part of parts) {
|
|
15832
|
+
if (part.type !== "binop" || ![">", ">=", "<", "<="].includes(part.op)) return null;
|
|
15833
|
+
const column2 = part.left.type === "colref" ? part.left : part.right;
|
|
15834
|
+
const value = part.left.type === "colref" ? part.right : part.left;
|
|
15835
|
+
if (column2.type !== "colref" || column2.parts.length > 2 || column2.parts.length === 2 && column2.parts[0] !== alias || !["param", "number_lit"].includes(value.type))
|
|
15836
|
+
return null;
|
|
15837
|
+
const op = part.left === column2 ? part.op : { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[part.op];
|
|
15838
|
+
bounds.push({ column: column2.parts.at(-1), op, value });
|
|
15839
|
+
}
|
|
15840
|
+
const lower = bounds.find((bound) => bound.op === ">" || bound.op === ">=");
|
|
15841
|
+
const upper = bounds.find((bound) => bound.op === "<" || bound.op === "<=");
|
|
15842
|
+
if (!lower || !upper || lower.column !== upper.column) return null;
|
|
15843
|
+
const column = table.columns.find((column2) => column2.name === lower.column);
|
|
15844
|
+
if (!column || column.domain || !["int2", "int4"].includes(column.type.id)) return null;
|
|
15845
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15846
|
+
if (spec.where || spec.keys.length !== 1 || spec.keys[0].expr || spec.columnNames[0] !== lower.column) continue;
|
|
15847
|
+
const low = evalExpr(env.ctx, makeEvalScope(env, null), lower.value);
|
|
15848
|
+
const high = evalExpr(env.ctx, makeEvalScope(env, null), upper.value);
|
|
15849
|
+
if (low.t !== column.type.id || high.t !== column.type.id || typeof low.v !== "number" || typeof high.v !== "number")
|
|
15850
|
+
return null;
|
|
15851
|
+
return {
|
|
15852
|
+
indices: indexStoreFor(env, table, spec).integerRange(low.v, high.v, lower.op === ">=", upper.op === "<="),
|
|
15853
|
+
indexName: spec.name
|
|
15854
|
+
};
|
|
15855
|
+
}
|
|
15856
|
+
return null;
|
|
15857
|
+
}
|
|
15307
15858
|
function tryIndexedTableRows(env, table, alias, where) {
|
|
15308
15859
|
const parts = conjunctions(where);
|
|
15309
15860
|
const eqs = [];
|
|
@@ -15340,12 +15891,7 @@ function tryIndexedTableRows(env, table, alias, where) {
|
|
|
15340
15891
|
const key = uniqueKeyOf(env, table, spec, probeRow);
|
|
15341
15892
|
if (key === null) return [];
|
|
15342
15893
|
const hits = indexStoreFor(env, table, spec).lookup(key);
|
|
15343
|
-
|
|
15344
|
-
const index = schema?.indexes.get(spec.name);
|
|
15345
|
-
if (schema && index) {
|
|
15346
|
-
const lastScan = env.ctx.state.clock().getTime();
|
|
15347
|
-
schema.indexes.set(spec.name, { ...index, scans: (index.scans ?? 0n) + 1n, lastScan });
|
|
15348
|
-
}
|
|
15894
|
+
recordIndexScan(env, table, spec.name);
|
|
15349
15895
|
return hits.map((i) => table.rowAt(i));
|
|
15350
15896
|
}
|
|
15351
15897
|
function tryIndexedFromItem(env, item, where) {
|
|
@@ -21321,7 +21867,7 @@ function renameWithAliases(rel4, label, colAliases, what) {
|
|
|
21321
21867
|
}));
|
|
21322
21868
|
return { columns, rows: rel4.rows };
|
|
21323
21869
|
}
|
|
21324
|
-
function materializeItem(env, item, scope) {
|
|
21870
|
+
function materializeItem(env, item, scope, shapeOnly = false) {
|
|
21325
21871
|
switch (item.type) {
|
|
21326
21872
|
case "from_table": {
|
|
21327
21873
|
const label = item.alias ?? item.name[item.name.length - 1];
|
|
@@ -21340,7 +21886,7 @@ function materializeItem(env, item, scope) {
|
|
|
21340
21886
|
...table.columns.map((c) => ({ name: c.name, type: c.type.id, mod: c.type.mod, table: label })),
|
|
21341
21887
|
{ name: "xmin", type: "xid", table: label, hidden: true, system: true }
|
|
21342
21888
|
],
|
|
21343
|
-
rows: table.
|
|
21889
|
+
rows: shapeOnly ? [] : table.serializationRows().map((row) => [...row, table.rowXmins.get(row) ?? 2])
|
|
21344
21890
|
};
|
|
21345
21891
|
return { rel: renameWithAliases(rel4, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
|
|
21346
21892
|
}
|
|
@@ -22613,14 +23159,30 @@ function checkPredicateType(env, columns, rangeVars, e, kind, extras) {
|
|
|
22613
23159
|
function executeCore(env0, core, orderBy, opts) {
|
|
22614
23160
|
const env = env0;
|
|
22615
23161
|
const ctx = env.ctx;
|
|
22616
|
-
const
|
|
23162
|
+
const item = core.from.length === 1 ? core.from[0] : null;
|
|
23163
|
+
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;
|
|
23164
|
+
const point = pointTable && core.where && item?.type === "from_table" ? tryPointRowIndices(env, pointTable, item.alias ?? item.name[item.name.length - 1], core.where) : null;
|
|
23165
|
+
const range = !point && pointTable && core.where && item?.type === "from_table" ? tryRangeRowIndices(env, pointTable, item.alias ?? item.name[item.name.length - 1], core.where) : null;
|
|
23166
|
+
const access = point ?? range;
|
|
23167
|
+
const source = access && item ? materializeItem(env, item, env.outer, true) : buildFrom(env, core.from, core.where);
|
|
22617
23168
|
const srcCols = source.rel.columns;
|
|
22618
23169
|
const rangeVars = source.rangeVars;
|
|
22619
23170
|
const rowScope = (row) => new RowScope(srcCols, row, env.outer, rangeVars);
|
|
22620
23171
|
let srcRows = source.rel.rows;
|
|
22621
23172
|
if (core.where) {
|
|
22622
23173
|
checkPredicateType(env, srcCols, rangeVars, core.where, "WHERE");
|
|
22623
|
-
if (
|
|
23174
|
+
if (access && pointTable) {
|
|
23175
|
+
pointTable.materializeSlab();
|
|
23176
|
+
srcRows = access.indices.map((index) => {
|
|
23177
|
+
const row = pointTable.rowAt(index);
|
|
23178
|
+
return [...row, pointTable.rowXmins.get(row) ?? 2];
|
|
23179
|
+
});
|
|
23180
|
+
if (range) {
|
|
23181
|
+
const where = core.where;
|
|
23182
|
+
srcRows = srcRows.filter((row) => evalPredicate2(env, rowScope(row), where));
|
|
23183
|
+
}
|
|
23184
|
+
recordIndexScan(env, pointTable, access.indexName);
|
|
23185
|
+
} else if (core.from.length === 1) {
|
|
22624
23186
|
const indexed = tryIndexedFromItem(env, core.from[0], core.where);
|
|
22625
23187
|
if (indexed !== null) {
|
|
22626
23188
|
srcRows = indexed;
|
|
@@ -22801,11 +23363,11 @@ function executeCore(env0, core, orderBy, opts) {
|
|
|
22801
23363
|
}
|
|
22802
23364
|
}
|
|
22803
23365
|
}
|
|
22804
|
-
const
|
|
23366
|
+
const item2 = { name: `__ord${orderExprs.length}`, kind: "expr", colIdx: -1, expr: ob.expr, hidden: true };
|
|
22805
23367
|
const spec = { colIdx: projItems.length, dir, nullsFirst };
|
|
22806
|
-
projItems.push(
|
|
23368
|
+
projItems.push(item2);
|
|
22807
23369
|
sortSpecs.push(spec);
|
|
22808
|
-
orderExprs.push({ item, spec });
|
|
23370
|
+
orderExprs.push({ item: item2, spec });
|
|
22809
23371
|
}
|
|
22810
23372
|
if (core.distinct && !core.distinct.on && sortSpecs.length > 0 && !opts?.hasLimit) {
|
|
22811
23373
|
const used = new Set(sortSpecs.map((s) => s.colIdx));
|
|
@@ -23179,7 +23741,7 @@ var TransactionManager = class {
|
|
|
23179
23741
|
return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
|
|
23180
23742
|
}
|
|
23181
23743
|
restore(snap) {
|
|
23182
|
-
this.state.restoreFrom(snap.state);
|
|
23744
|
+
this.state.restoreFrom(snap.state.cloneShallow());
|
|
23183
23745
|
this.state.prng.setState(snap.prngState);
|
|
23184
23746
|
}
|
|
23185
23747
|
thawOnce() {
|
|
@@ -23279,7 +23841,10 @@ function withStatementRollback(env, fn) {
|
|
|
23279
23841
|
function requireTargetTable(env, parts, verb) {
|
|
23280
23842
|
const state = env.ctx.state;
|
|
23281
23843
|
const table = state.findTable(parts);
|
|
23282
|
-
if (table)
|
|
23844
|
+
if (table) {
|
|
23845
|
+
prepareDmlIndexStores(env, table);
|
|
23846
|
+
return state.ensureWritableTableForDml(table);
|
|
23847
|
+
}
|
|
23283
23848
|
const view = state.findView(parts);
|
|
23284
23849
|
if (view) {
|
|
23285
23850
|
throw pgError("wrong_object_type", `cannot ${verb} view "${view.name}"`, "42809");
|
|
@@ -23447,7 +24012,7 @@ function resolveArbiters(env, table, clause) {
|
|
|
23447
24012
|
return matches2.map((spec) => ({ spec }));
|
|
23448
24013
|
}
|
|
23449
24014
|
function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, where) {
|
|
23450
|
-
const rows = table.
|
|
24015
|
+
const rows = table.writeRows();
|
|
23451
24016
|
const existing = rows[existingIdx];
|
|
23452
24017
|
const cols = [
|
|
23453
24018
|
...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
|
|
@@ -23459,7 +24024,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
23459
24024
|
applyUpdateSets(env, table, sets, scope, updated);
|
|
23460
24025
|
computeGeneratedColumns(env, table, updated);
|
|
23461
24026
|
table.rowXmins.set(updated, env.ctx.rowVersionXid());
|
|
23462
|
-
|
|
24027
|
+
table.replaceRow(existingIdx, updated);
|
|
23463
24028
|
indexUpdateRow(env, table, existingIdx, existing, updated);
|
|
23464
24029
|
checkNotNull(env, table, updated);
|
|
23465
24030
|
checkChecks(env, table, updated);
|
|
@@ -23471,7 +24036,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
23471
24036
|
function executeInsert(env0, stmt) {
|
|
23472
24037
|
const env = applyWith(env0, stmt.with);
|
|
23473
24038
|
const table = requireTargetTable(env, stmt.table, "insert into");
|
|
23474
|
-
const rows = table.
|
|
24039
|
+
const rows = table.writeRows();
|
|
23475
24040
|
const label = stmt.alias ?? table.name;
|
|
23476
24041
|
const colIdxs = insertTargetColumns(env, table, stmt);
|
|
23477
24042
|
const sourceRows = insertSourceRows(env, stmt, colIdxs);
|
|
@@ -23555,6 +24120,7 @@ function executeInsert(env0, stmt) {
|
|
|
23555
24120
|
checkForeignKeys(env, table, newRow);
|
|
23556
24121
|
table.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23557
24122
|
rows.push(newRow);
|
|
24123
|
+
table.recordDurableRow(newRow);
|
|
23558
24124
|
indexInsertRow(env, table, rows.length - 1, newRow);
|
|
23559
24125
|
insertedCount++;
|
|
23560
24126
|
insertedRows.push(newRow);
|
|
@@ -23647,13 +24213,17 @@ function executeUpdate(env0, stmt) {
|
|
|
23647
24213
|
const label = stmt.alias ?? table.name;
|
|
23648
24214
|
fireStatementTriggers(env, table, "before", "update");
|
|
23649
24215
|
const fromSource = stmt.from.length > 0 ? buildFrom(env, stmt.from) : null;
|
|
23650
|
-
const rows = table.mutableRows();
|
|
23651
24216
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
23652
24217
|
const updatedRows = [];
|
|
23653
24218
|
const replacedRows = [];
|
|
23654
24219
|
let updateCount = 0;
|
|
23655
|
-
|
|
23656
|
-
|
|
24220
|
+
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(
|
|
24221
|
+
(set) => set.columns.every((column) => column.subscripts === null && column.fields.length === 0) && "type" in set.value && isPureUpdateValue(set.value)
|
|
24222
|
+
) && referencingConstraints(env, table).length === 0 ? tryPointRowIndices(env, table, label, stmt.where) : null;
|
|
24223
|
+
const rows = point ? null : table.writeRows();
|
|
24224
|
+
for (let candidate = 0; candidate < (point ? point.indices.length : rows.length); candidate++) {
|
|
24225
|
+
const ri = point ? point.indices[candidate] : candidate;
|
|
24226
|
+
const oldRow = point ? table.rowAt(ri) : rows[ri];
|
|
23657
24227
|
let matchScope = null;
|
|
23658
24228
|
if (fromSource) {
|
|
23659
24229
|
let found = false;
|
|
@@ -23679,14 +24249,14 @@ function executeUpdate(env0, stmt) {
|
|
|
23679
24249
|
const finalRow = fired.row;
|
|
23680
24250
|
computeGeneratedColumns(env, table, finalRow);
|
|
23681
24251
|
table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
|
|
23682
|
-
|
|
24252
|
+
table.replaceRow(ri, finalRow);
|
|
23683
24253
|
try {
|
|
23684
24254
|
checkNotNull(env, table, finalRow);
|
|
23685
24255
|
checkChecks(env, table, finalRow);
|
|
23686
24256
|
checkUnique(env, table, finalRow, ri);
|
|
23687
24257
|
checkForeignKeys(env, table, finalRow);
|
|
23688
24258
|
} catch (err) {
|
|
23689
|
-
|
|
24259
|
+
table.replaceRow(ri, oldRow);
|
|
23690
24260
|
throw err;
|
|
23691
24261
|
}
|
|
23692
24262
|
indexUpdateRow(env, table, ri, oldRow, finalRow);
|
|
@@ -23704,12 +24274,29 @@ function executeUpdate(env0, stmt) {
|
|
|
23704
24274
|
}
|
|
23705
24275
|
return commandResult("UPDATE", updateCount);
|
|
23706
24276
|
}
|
|
24277
|
+
function isPureUpdateValue(expr) {
|
|
24278
|
+
switch (expr.type) {
|
|
24279
|
+
case "param":
|
|
24280
|
+
case "number_lit":
|
|
24281
|
+
case "string_lit":
|
|
24282
|
+
case "bool_lit":
|
|
24283
|
+
case "null_lit":
|
|
24284
|
+
case "colref":
|
|
24285
|
+
return true;
|
|
24286
|
+
case "binop":
|
|
24287
|
+
return isPureUpdateValue(expr.left) && isPureUpdateValue(expr.right);
|
|
24288
|
+
case "unop":
|
|
24289
|
+
return isPureUpdateValue(expr.operand);
|
|
24290
|
+
default:
|
|
24291
|
+
return false;
|
|
24292
|
+
}
|
|
24293
|
+
}
|
|
23707
24294
|
function executeDelete(env0, stmt) {
|
|
23708
24295
|
const env = applyWith(env0, stmt.with);
|
|
23709
24296
|
const table = requireTargetTable(env, stmt.table, "delete from");
|
|
23710
24297
|
const label = stmt.alias ?? table.name;
|
|
23711
24298
|
fireStatementTriggers(env, table, "before", "delete");
|
|
23712
|
-
const rows = table.
|
|
24299
|
+
const rows = table.writeRows();
|
|
23713
24300
|
const usingSource = stmt.using.length > 0 ? buildFrom(env, stmt.using) : null;
|
|
23714
24301
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
23715
24302
|
const toDelete = [];
|
|
@@ -23739,6 +24326,7 @@ function executeDelete(env0, stmt) {
|
|
|
23739
24326
|
if (skipped.has(ri)) continue;
|
|
23740
24327
|
const row = rows[ri];
|
|
23741
24328
|
handleReferencedDelete(env, table, row, 0);
|
|
24329
|
+
table.recordDurableDelete(row);
|
|
23742
24330
|
rows.splice(ri, 1);
|
|
23743
24331
|
deletedRows.unshift(row);
|
|
23744
24332
|
}
|
|
@@ -23785,13 +24373,14 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
23785
24373
|
const idxs = referencingRowIdxs(ref, key);
|
|
23786
24374
|
if (idxs.length === 0) continue;
|
|
23787
24375
|
const action = con.onDelete ?? "no_action";
|
|
23788
|
-
const child = env.ctx.state.
|
|
23789
|
-
const childRows = child.
|
|
24376
|
+
const child = env.ctx.state.ensureWritableTableForDml(ref.table);
|
|
24377
|
+
const childRows = child.writeRows();
|
|
23790
24378
|
switch (action) {
|
|
23791
24379
|
case "cascade": {
|
|
23792
24380
|
for (let k = idxs.length - 1; k >= 0; k--) {
|
|
23793
24381
|
const childRow = childRows[idxs[k]];
|
|
23794
24382
|
handleReferencedDelete(env, child, childRow, depth + 1);
|
|
24383
|
+
child.recordDurableDelete(childRow);
|
|
23795
24384
|
childRows.splice(idxs[k], 1);
|
|
23796
24385
|
}
|
|
23797
24386
|
break;
|
|
@@ -23803,7 +24392,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
23803
24392
|
newRow[child.columnIndex(c)] = null;
|
|
23804
24393
|
}
|
|
23805
24394
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23806
|
-
|
|
24395
|
+
child.replaceRow(i, newRow);
|
|
23807
24396
|
checkNotNull(env, child, newRow);
|
|
23808
24397
|
checkChecks(env, child, newRow);
|
|
23809
24398
|
}
|
|
@@ -23817,7 +24406,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
23817
24406
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
23818
24407
|
}
|
|
23819
24408
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23820
|
-
|
|
24409
|
+
child.replaceRow(i, newRow);
|
|
23821
24410
|
checkNotNull(env, child, newRow);
|
|
23822
24411
|
checkChecks(env, child, newRow);
|
|
23823
24412
|
checkForeignKeys(env, child, newRow);
|
|
@@ -23843,8 +24432,8 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23843
24432
|
const idxs = referencingRowIdxs(ref, oldKey);
|
|
23844
24433
|
if (idxs.length === 0) continue;
|
|
23845
24434
|
const action = con.onUpdate ?? "no_action";
|
|
23846
|
-
const child = env.ctx.state.
|
|
23847
|
-
const childRows = child.
|
|
24435
|
+
const child = env.ctx.state.ensureWritableTableForDml(ref.table);
|
|
24436
|
+
const childRows = child.writeRows();
|
|
23848
24437
|
switch (action) {
|
|
23849
24438
|
case "cascade": {
|
|
23850
24439
|
for (const i of idxs) {
|
|
@@ -23855,7 +24444,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23855
24444
|
newRow[localIdx] = newRows[r][refIdx] ?? null;
|
|
23856
24445
|
}
|
|
23857
24446
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23858
|
-
|
|
24447
|
+
child.replaceRow(i, newRow);
|
|
23859
24448
|
checkChecks(env, child, newRow);
|
|
23860
24449
|
}
|
|
23861
24450
|
break;
|
|
@@ -23865,7 +24454,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23865
24454
|
const newRow = childRows[i].slice();
|
|
23866
24455
|
for (const c of con.columns) newRow[child.columnIndex(c)] = null;
|
|
23867
24456
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23868
|
-
|
|
24457
|
+
child.replaceRow(i, newRow);
|
|
23869
24458
|
checkNotNull(env, child, newRow);
|
|
23870
24459
|
}
|
|
23871
24460
|
break;
|
|
@@ -23878,7 +24467,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23878
24467
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
23879
24468
|
}
|
|
23880
24469
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23881
|
-
|
|
24470
|
+
child.replaceRow(i, newRow);
|
|
23882
24471
|
checkForeignKeys(env, child, newRow);
|
|
23883
24472
|
}
|
|
23884
24473
|
break;
|
|
@@ -24401,6 +24990,8 @@ function openSnapshot(snapshot, options = {}) {
|
|
|
24401
24990
|
now = () => new Date(ms);
|
|
24402
24991
|
}
|
|
24403
24992
|
const state = snapshot.state.cloneShallow();
|
|
24993
|
+
for (const schema of state.schemas.values())
|
|
24994
|
+
for (const [name, table] of schema.tables) if (table.durableChanges) schema.tables.set(name, table.untrackedFork());
|
|
24404
24995
|
state.prng = prng;
|
|
24405
24996
|
state.clock = now;
|
|
24406
24997
|
return createAdoptedDatabase({
|
|
@@ -29086,6 +29677,24 @@ var Statement = class _Statement {
|
|
|
29086
29677
|
this.database = database;
|
|
29087
29678
|
this.sql = sql;
|
|
29088
29679
|
this.statements = statements;
|
|
29680
|
+
const observer = database.durabilityObserver;
|
|
29681
|
+
if (observer) {
|
|
29682
|
+
const execute = this.execute;
|
|
29683
|
+
this.execute = (params) => {
|
|
29684
|
+
observer.assertWritable();
|
|
29685
|
+
if (params.some((value) => value instanceof Uint8Array)) observer.exposed();
|
|
29686
|
+
try {
|
|
29687
|
+
const result = execute.call(this, params);
|
|
29688
|
+
if (result.columns.some(
|
|
29689
|
+
(column, index) => column.type === "bytea" && result.rows.some((row) => row[index] !== null && row[index] !== void 0)
|
|
29690
|
+
))
|
|
29691
|
+
observer.exposed();
|
|
29692
|
+
return result;
|
|
29693
|
+
} finally {
|
|
29694
|
+
observer.changed();
|
|
29695
|
+
}
|
|
29696
|
+
};
|
|
29697
|
+
}
|
|
29089
29698
|
}
|
|
29090
29699
|
database;
|
|
29091
29700
|
statements;
|
|
@@ -29118,7 +29727,7 @@ var Statement = class _Statement {
|
|
|
29118
29727
|
*/
|
|
29119
29728
|
textResult(...params) {
|
|
29120
29729
|
const res = this.execute(params);
|
|
29121
|
-
const ctx = new EngineCtx(this.database.
|
|
29730
|
+
const ctx = new EngineCtx(this.database.engineState);
|
|
29122
29731
|
return {
|
|
29123
29732
|
columns: res.columns.map((c) => c.name),
|
|
29124
29733
|
columnTypes: res.columns.map((c) => c.type === UNKNOWN ? "text" : c.type),
|
|
@@ -29155,7 +29764,7 @@ var Statement = class _Statement {
|
|
|
29155
29764
|
const refused = this.database.transactions.inTransaction ? refusedInTransactionBlock(stmt) : null;
|
|
29156
29765
|
if (refused) throw pgError("misuse", `${refused} cannot run inside a transaction block`, "25001");
|
|
29157
29766
|
const env = {
|
|
29158
|
-
ctx: new EngineCtx(this.database.
|
|
29767
|
+
ctx: new EngineCtx(this.database.engineState),
|
|
29159
29768
|
params: typed,
|
|
29160
29769
|
ctes: /* @__PURE__ */ new Map(),
|
|
29161
29770
|
outer: null
|
|
@@ -29165,12 +29774,13 @@ var Statement = class _Statement {
|
|
|
29165
29774
|
return last;
|
|
29166
29775
|
}
|
|
29167
29776
|
toRows(res) {
|
|
29168
|
-
const ctx = new EngineCtx(this.database.
|
|
29777
|
+
const ctx = new EngineCtx(this.database.engineState);
|
|
29169
29778
|
return res.rows.map((row) => {
|
|
29170
29779
|
const obj = {};
|
|
29171
29780
|
for (let i = 0; i < res.columns.length; i++) {
|
|
29172
29781
|
const c = res.columns[i];
|
|
29173
|
-
|
|
29782
|
+
const value = datumToJs(c.type, row[i] ?? null, ctx, this.database.int8Mode);
|
|
29783
|
+
obj[c.name] = value;
|
|
29174
29784
|
}
|
|
29175
29785
|
return obj;
|
|
29176
29786
|
});
|
|
@@ -29188,6 +29798,10 @@ function project(point) {
|
|
|
29188
29798
|
var Database = class {
|
|
29189
29799
|
/** @internal Engine catalog, tables, session settings. */
|
|
29190
29800
|
state;
|
|
29801
|
+
/** @internal Engine-only access; buffered mode observes public state exposure. */
|
|
29802
|
+
engineState;
|
|
29803
|
+
/** @internal Installed only by buffered durability. */
|
|
29804
|
+
durabilityObserver;
|
|
29191
29805
|
/** Seed used to construct the PRNG. Ignored when {@link randomMode} is `"os"`. */
|
|
29192
29806
|
seed;
|
|
29193
29807
|
/** Entropy mode for `random()` / `gen_random_uuid()`. */
|
|
@@ -29217,10 +29831,10 @@ var Database = class {
|
|
|
29217
29831
|
this.int8Mode = options.int8Mode;
|
|
29218
29832
|
this.prng = options.prng;
|
|
29219
29833
|
this.now = options.now;
|
|
29220
|
-
this.
|
|
29221
|
-
this.
|
|
29222
|
-
this.
|
|
29223
|
-
this.transactions = txManagerFor(this.
|
|
29834
|
+
this.engineState = options.state;
|
|
29835
|
+
this.engineState.prng = this.prng;
|
|
29836
|
+
this.engineState.clock = () => this.now();
|
|
29837
|
+
this.transactions = txManagerFor(this.engineState);
|
|
29224
29838
|
} else {
|
|
29225
29839
|
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
29226
29840
|
this.randomMode = options.random ?? "deterministic";
|
|
@@ -29228,9 +29842,10 @@ var Database = class {
|
|
|
29228
29842
|
this.int8Mode = options.int8 ?? "bigint";
|
|
29229
29843
|
this.prng = this.randomMode === "os" ? new OsEntropy() : new Prng(this.seed);
|
|
29230
29844
|
this.now = resolveClock(options.now);
|
|
29231
|
-
this.
|
|
29232
|
-
this.transactions = txManagerFor(this.
|
|
29845
|
+
this.engineState = new DatabaseState(this.prng, () => this.now());
|
|
29846
|
+
this.transactions = txManagerFor(this.engineState);
|
|
29233
29847
|
}
|
|
29848
|
+
this.state = this.engineState;
|
|
29234
29849
|
this.timeline = new Timeline({
|
|
29235
29850
|
now: () => this.now().getTime(),
|
|
29236
29851
|
...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
|
|
@@ -29257,14 +29872,15 @@ var Database = class {
|
|
|
29257
29872
|
* a live in-memory snapshot.
|
|
29258
29873
|
*/
|
|
29259
29874
|
registerFunction(spec) {
|
|
29875
|
+
this.durabilityObserver?.assertWritable();
|
|
29260
29876
|
this.assertOpen();
|
|
29261
29877
|
const parts = spec.name.split(".").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
29262
29878
|
if (parts.length === 0) throw pgError("syntax", "function name is required", "42601");
|
|
29263
|
-
const schemaName = (parts.length >= 2 ? parts[0] : this.
|
|
29879
|
+
const schemaName = (parts.length >= 2 ? parts[0] : this.engineState.currentSchema()).toLowerCase();
|
|
29264
29880
|
const name = parts[parts.length - 1].toLowerCase();
|
|
29265
|
-
const functions = schemaName === "pg_catalog" ? this.
|
|
29266
|
-
const argTypes = spec.args.map((a) => resolveTypeName(this.
|
|
29267
|
-
const returns = resolveTypeName(this.
|
|
29881
|
+
const functions = schemaName === "pg_catalog" ? this.engineState.systemFunctions : this.engineState.getSchema(schemaName).functions;
|
|
29882
|
+
const argTypes = spec.args.map((a) => resolveTypeName(this.engineState, parseTypeName(a)).column.id);
|
|
29883
|
+
const returns = resolveTypeName(this.engineState, parseTypeName(spec.returns)).column.id;
|
|
29268
29884
|
const fn = {
|
|
29269
29885
|
name,
|
|
29270
29886
|
schema: schemaName,
|
|
@@ -29278,7 +29894,7 @@ var Database = class {
|
|
|
29278
29894
|
body: null,
|
|
29279
29895
|
rawBody: null,
|
|
29280
29896
|
strict: spec.strict ?? true,
|
|
29281
|
-
oid: this.
|
|
29897
|
+
oid: this.engineState.nextOid(),
|
|
29282
29898
|
jsImpl: spec.fn
|
|
29283
29899
|
};
|
|
29284
29900
|
const existing = functions.get(name) ?? [];
|
|
@@ -29288,6 +29904,7 @@ var Database = class {
|
|
|
29288
29904
|
if (sameSig !== -1) existing[sameSig] = fn;
|
|
29289
29905
|
else existing.push(fn);
|
|
29290
29906
|
functions.set(name, existing);
|
|
29907
|
+
this.durabilityObserver?.exposed();
|
|
29291
29908
|
}
|
|
29292
29909
|
/** Execute a single-statement query and return all rows keyed by column name. */
|
|
29293
29910
|
query(sql, params = [], options = {}) {
|
|
@@ -29314,6 +29931,7 @@ var Database = class {
|
|
|
29314
29931
|
*/
|
|
29315
29932
|
transaction(fn) {
|
|
29316
29933
|
this.assertOpen();
|
|
29934
|
+
this.durabilityObserver?.assertWritable();
|
|
29317
29935
|
this.apiTransactionDepth++;
|
|
29318
29936
|
try {
|
|
29319
29937
|
if (!this.transactions.inTransaction) {
|
|
@@ -29340,6 +29958,7 @@ var Database = class {
|
|
|
29340
29958
|
}
|
|
29341
29959
|
} finally {
|
|
29342
29960
|
this.apiTransactionDepth--;
|
|
29961
|
+
this.durabilityObserver?.changed();
|
|
29343
29962
|
}
|
|
29344
29963
|
}
|
|
29345
29964
|
/**
|
|
@@ -29434,7 +30053,7 @@ var Database = class {
|
|
|
29434
30053
|
*/
|
|
29435
30054
|
fault(faults) {
|
|
29436
30055
|
this.assertOpen();
|
|
29437
|
-
Object.assign(this.
|
|
30056
|
+
Object.assign(this.engineState.faults, faults);
|
|
29438
30057
|
}
|
|
29439
30058
|
/** Close the database. Further SQL throws. Idempotent. */
|
|
29440
30059
|
close() {
|
|
@@ -29444,6 +30063,7 @@ var Database = class {
|
|
|
29444
30063
|
}
|
|
29445
30064
|
if (this.transactions.inTransaction) this.transactions.rollback();
|
|
29446
30065
|
this.closed = true;
|
|
30066
|
+
this.durabilityObserver?.closed?.();
|
|
29447
30067
|
}
|
|
29448
30068
|
/**
|
|
29449
30069
|
* Execute `COPY table [(cols)] FROM STDIN` with the given text/csv payload
|
|
@@ -29462,13 +30082,18 @@ var Database = class {
|
|
|
29462
30082
|
if (statements.length !== 1 || !stmt || stmt.type !== "copy" || stmt.direction !== "from") {
|
|
29463
30083
|
throw pgError("misuse", "copyFrom() requires a single COPY ... FROM STDIN statement", "XX000");
|
|
29464
30084
|
}
|
|
29465
|
-
|
|
29466
|
-
|
|
30085
|
+
this.durabilityObserver?.assertWritable();
|
|
30086
|
+
try {
|
|
30087
|
+
const env = { ctx: new EngineCtx(this.engineState), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
|
|
30088
|
+
return executeCopyFromData(env, stmt, data).rowCount;
|
|
30089
|
+
} finally {
|
|
30090
|
+
this.durabilityObserver?.changed();
|
|
30091
|
+
}
|
|
29467
30092
|
}
|
|
29468
30093
|
/** Rows affected by the most recent INSERT / UPDATE / DELETE. */
|
|
29469
30094
|
get changes() {
|
|
29470
30095
|
this.assertOpen();
|
|
29471
|
-
return this.
|
|
30096
|
+
return this.engineState.changes;
|
|
29472
30097
|
}
|
|
29473
30098
|
/** @internal */
|
|
29474
30099
|
assertOpen() {
|
|
@@ -29480,7 +30105,7 @@ var Database = class {
|
|
|
29480
30105
|
}
|
|
29481
30106
|
captureLive() {
|
|
29482
30107
|
return captureSnapshot(
|
|
29483
|
-
this.
|
|
30108
|
+
this.engineState,
|
|
29484
30109
|
this.prng,
|
|
29485
30110
|
this.now,
|
|
29486
30111
|
this.seed,
|
|
@@ -29490,7 +30115,8 @@ var Database = class {
|
|
|
29490
30115
|
);
|
|
29491
30116
|
}
|
|
29492
30117
|
install(snapshot) {
|
|
29493
|
-
this.
|
|
30118
|
+
this.durabilityObserver?.exposed();
|
|
30119
|
+
this.engineState.restoreFrom(snapshot.state.cloneShallow());
|
|
29494
30120
|
this.prng.setState(snapshot.prngState);
|
|
29495
30121
|
if (!this.systemClock) this.now = fixedClock(new Date(snapshot.nowMs));
|
|
29496
30122
|
}
|
|
@@ -30686,6 +31312,13 @@ var Writer = class {
|
|
|
30686
31312
|
this.view.setBigInt64(this.len, value, true);
|
|
30687
31313
|
this.len += 8;
|
|
30688
31314
|
}
|
|
31315
|
+
/** Finite integer Number, encoded identically to i64(BigInt(value)) without allocation. */
|
|
31316
|
+
i64Number(value) {
|
|
31317
|
+
this.ensure(8);
|
|
31318
|
+
this.view.setUint32(this.len, value >>> 0, true);
|
|
31319
|
+
this.view.setUint32(this.len + 4, Math.floor(value / 4294967296) >>> 0, true);
|
|
31320
|
+
this.len += 8;
|
|
31321
|
+
}
|
|
30689
31322
|
f64(value) {
|
|
30690
31323
|
this.ensure(8);
|
|
30691
31324
|
this.view.setFloat64(this.len, value, true);
|
|
@@ -30696,6 +31329,13 @@ var Writer = class {
|
|
|
30696
31329
|
this.buf.set(value, this.len);
|
|
30697
31330
|
this.len += value.length;
|
|
30698
31331
|
}
|
|
31332
|
+
/** Reserve one contiguous region. Fill it before any subsequent Writer call can grow the buffer. */
|
|
31333
|
+
block(byteLength) {
|
|
31334
|
+
this.ensure(byteLength);
|
|
31335
|
+
const block = new DataView(this.buf.buffer, this.len, byteLength);
|
|
31336
|
+
this.len += byteLength;
|
|
31337
|
+
return block;
|
|
31338
|
+
}
|
|
30699
31339
|
/** Length-prefixed UTF-8. */
|
|
30700
31340
|
text(value) {
|
|
30701
31341
|
const encoded = utf8Encode(value);
|
|
@@ -30708,7 +31348,7 @@ var Writer = class {
|
|
|
30708
31348
|
}
|
|
30709
31349
|
finish() {
|
|
30710
31350
|
if (this.len === this.buf.length) return this.buf;
|
|
30711
|
-
return this.buf.
|
|
31351
|
+
return this.buf.slice(0, this.len);
|
|
30712
31352
|
}
|
|
30713
31353
|
fail() {
|
|
30714
31354
|
throw new PostgresError("snapshot_format", "invalid or truncated postgres-mem snapshot", "XX000");
|
|
@@ -30925,15 +31565,18 @@ function writeInternTable(w, strings) {
|
|
|
30925
31565
|
writeVarintU32(w, strings.length);
|
|
30926
31566
|
if (strings.length === 0) return;
|
|
30927
31567
|
const offsets = [];
|
|
31568
|
+
const encoded = [];
|
|
30928
31569
|
let total = 0;
|
|
30929
31570
|
for (const s of strings) {
|
|
30930
31571
|
offsets.push(total);
|
|
30931
|
-
|
|
31572
|
+
const bytes = utf8Encode(s);
|
|
31573
|
+
encoded.push(bytes);
|
|
31574
|
+
total += bytes.length;
|
|
30932
31575
|
}
|
|
30933
31576
|
writeVarintU32(w, total);
|
|
30934
31577
|
const base = w.position + offsets.length * 4;
|
|
30935
31578
|
for (const off of offsets) w.u32(base + off);
|
|
30936
|
-
for (const
|
|
31579
|
+
for (const bytes of encoded) w.raw(bytes);
|
|
30937
31580
|
}
|
|
30938
31581
|
function readInternTable(r) {
|
|
30939
31582
|
const count = readVarintU32(r);
|
|
@@ -30958,7 +31601,10 @@ var VERSION = 3;
|
|
|
30958
31601
|
function encodeDatabaseState(state, runtime) {
|
|
30959
31602
|
const pool = new InternPool();
|
|
30960
31603
|
const schemas = [...state.schemas.values()].sort((a, b) => compareNames(a.name, b.name));
|
|
31604
|
+
const internValues = /* @__PURE__ */ new Map();
|
|
30961
31605
|
for (const schema of schemas) {
|
|
31606
|
+
const values = [];
|
|
31607
|
+
internValues.set(schema, values);
|
|
30962
31608
|
pool.count(schema.name);
|
|
30963
31609
|
for (const table of sortedValues(schema.tables)) {
|
|
30964
31610
|
pool.count(table.name);
|
|
@@ -30969,8 +31615,13 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30969
31615
|
pool.count(column.domain ?? "");
|
|
30970
31616
|
if (column.identity) pool.count(column.identity.sequence);
|
|
30971
31617
|
}
|
|
30972
|
-
for (const row of table.
|
|
30973
|
-
for (const d of row)
|
|
31618
|
+
for (const row of table.serializationRows()) {
|
|
31619
|
+
for (const d of row) {
|
|
31620
|
+
if (typeof d === "string") {
|
|
31621
|
+
pool.count(d);
|
|
31622
|
+
values.push(d);
|
|
31623
|
+
} else if (d !== null && typeof d === "object" && !(d instanceof Uint8Array)) values.push(d);
|
|
31624
|
+
}
|
|
30974
31625
|
}
|
|
30975
31626
|
}
|
|
30976
31627
|
for (const view of sortedValues(schema.views)) {
|
|
@@ -30978,7 +31629,12 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30978
31629
|
pool.count(view.schema);
|
|
30979
31630
|
if (view.matRows) {
|
|
30980
31631
|
for (const row of view.matRows) {
|
|
30981
|
-
for (const d of row)
|
|
31632
|
+
for (const d of row) {
|
|
31633
|
+
if (typeof d === "string") {
|
|
31634
|
+
pool.count(d);
|
|
31635
|
+
values.push(d);
|
|
31636
|
+
} else if (d !== null && typeof d === "object" && !(d instanceof Uint8Array)) values.push(d);
|
|
31637
|
+
}
|
|
30982
31638
|
}
|
|
30983
31639
|
}
|
|
30984
31640
|
}
|
|
@@ -31011,7 +31667,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
31011
31667
|
}
|
|
31012
31668
|
}
|
|
31013
31669
|
pool.finalize();
|
|
31014
|
-
forceAllEncodeIntern(pool, state, schemas);
|
|
31670
|
+
forceAllEncodeIntern(pool, state, schemas, internValues);
|
|
31015
31671
|
const internId = (s) => pool.id(s);
|
|
31016
31672
|
const forceId = (s) => {
|
|
31017
31673
|
const id = pool.id(s);
|
|
@@ -31040,7 +31696,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
31040
31696
|
writeBjv(
|
|
31041
31697
|
w,
|
|
31042
31698
|
{
|
|
31043
|
-
xmins: table.
|
|
31699
|
+
xmins: table.serializationRows().map((row) => table.rowXmins.get(row) ?? 2),
|
|
31044
31700
|
name: table.name,
|
|
31045
31701
|
schema: table.schema,
|
|
31046
31702
|
columns: table.columns,
|
|
@@ -31053,7 +31709,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
31053
31709
|
},
|
|
31054
31710
|
forceId
|
|
31055
31711
|
);
|
|
31056
|
-
const rows = table.
|
|
31712
|
+
const rows = table.serializationRows();
|
|
31057
31713
|
writeVarintU32(w, rows.length);
|
|
31058
31714
|
for (let c = 0; c < table.columns.length; c++) writePackedColumn(w, rows, c, internId, forceId);
|
|
31059
31715
|
}
|
|
@@ -31379,44 +32035,57 @@ function writePackedColumn(w, rows, col, internId, forceId) {
|
|
|
31379
32035
|
}
|
|
31380
32036
|
if (pack === PACK_FLOAT) {
|
|
31381
32037
|
w.align4();
|
|
32038
|
+
const block = w.block((n - nulls) * 8);
|
|
32039
|
+
let offset = 0;
|
|
31382
32040
|
for (let i = 0; i < n; i++) {
|
|
31383
32041
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
31384
|
-
|
|
32042
|
+
block.setFloat64(offset, rows[i][col], true);
|
|
32043
|
+
offset += 8;
|
|
31385
32044
|
}
|
|
31386
32045
|
return;
|
|
31387
32046
|
}
|
|
31388
32047
|
if (pack === PACK_INT) {
|
|
31389
32048
|
w.align4();
|
|
32049
|
+
const block = w.block((n - nulls) * 8);
|
|
32050
|
+
let offset = 0;
|
|
31390
32051
|
for (let i = 0; i < n; i++) {
|
|
31391
32052
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
31392
32053
|
const v = rows[i][col];
|
|
31393
|
-
|
|
32054
|
+
if (typeof v === "bigint") block.setBigInt64(offset, v, true);
|
|
32055
|
+
else {
|
|
32056
|
+
block.setUint32(offset, v >>> 0, true);
|
|
32057
|
+
block.setUint32(offset + 4, Math.floor(v / 4294967296) >>> 0, true);
|
|
32058
|
+
}
|
|
32059
|
+
offset += 8;
|
|
31394
32060
|
}
|
|
31395
32061
|
return;
|
|
31396
32062
|
}
|
|
31397
32063
|
if (pack === PACK_TEXT_INTERN) {
|
|
31398
32064
|
w.align4();
|
|
32065
|
+
const block = w.block((n - nulls) * 4);
|
|
32066
|
+
let offset = 0;
|
|
31399
32067
|
for (let i = 0; i < n; i++) {
|
|
31400
32068
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
31401
|
-
|
|
32069
|
+
block.setUint32(offset, internId(rows[i][col]), true);
|
|
32070
|
+
offset += 4;
|
|
31402
32071
|
}
|
|
31403
32072
|
return;
|
|
31404
32073
|
}
|
|
31405
32074
|
if (pack === PACK_TEXT_INLINE) {
|
|
31406
32075
|
const offsets = [];
|
|
32076
|
+
const encoded = [];
|
|
31407
32077
|
let blobLen = 0;
|
|
31408
32078
|
for (let i = 0; i < n; i++) {
|
|
31409
32079
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
31410
32080
|
offsets.push(blobLen);
|
|
31411
|
-
|
|
32081
|
+
const bytes = utf8Encode(rows[i][col]);
|
|
32082
|
+
encoded.push(bytes);
|
|
32083
|
+
blobLen += bytes.length;
|
|
31412
32084
|
}
|
|
31413
32085
|
writeVarintU32(w, offsets.length);
|
|
31414
32086
|
for (const off of offsets) writeVarintU32(w, off);
|
|
31415
32087
|
writeVarintU32(w, blobLen);
|
|
31416
|
-
for (
|
|
31417
|
-
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
31418
|
-
w.textBytes(rows[i][col]);
|
|
31419
|
-
}
|
|
32088
|
+
for (const bytes of encoded) w.raw(bytes);
|
|
31420
32089
|
return;
|
|
31421
32090
|
}
|
|
31422
32091
|
if (pack === PACK_BLOB) {
|
|
@@ -31616,7 +32285,7 @@ function compareNames(a, b) {
|
|
|
31616
32285
|
function sortedValues(map) {
|
|
31617
32286
|
return [...map.values()].sort((a, b) => compareNames(a.name, b.name));
|
|
31618
32287
|
}
|
|
31619
|
-
function forceAllEncodeIntern(pool, state, schemas) {
|
|
32288
|
+
function forceAllEncodeIntern(pool, state, schemas, internValues) {
|
|
31620
32289
|
forceSchemaIntern(pool, state, schemas);
|
|
31621
32290
|
for (const [k, v] of state.settings) {
|
|
31622
32291
|
pool.forceId(k);
|
|
@@ -31628,18 +32297,7 @@ function forceAllEncodeIntern(pool, state, schemas) {
|
|
|
31628
32297
|
}
|
|
31629
32298
|
for (const schema of schemas) {
|
|
31630
32299
|
pool.forceId(schema.name);
|
|
31631
|
-
for (const
|
|
31632
|
-
for (const row of table.allRows()) {
|
|
31633
|
-
for (const d of row) forceDatumIntern(pool, d);
|
|
31634
|
-
}
|
|
31635
|
-
}
|
|
31636
|
-
for (const view of sortedValues(schema.views)) {
|
|
31637
|
-
if (view.matRows) {
|
|
31638
|
-
for (const row of view.matRows) {
|
|
31639
|
-
for (const d of row) forceDatumIntern(pool, d);
|
|
31640
|
-
}
|
|
31641
|
-
}
|
|
31642
|
-
}
|
|
32300
|
+
for (const value of internValues.get(schema) ?? []) forceDatumIntern(pool, value);
|
|
31643
32301
|
}
|
|
31644
32302
|
}
|
|
31645
32303
|
function forceDatumIntern(pool, value) {
|
|
@@ -31684,7 +32342,8 @@ function forceSchemaIntern(pool, state, schemas) {
|
|
|
31684
32342
|
pool.forceId(table.name);
|
|
31685
32343
|
pool.forceId(table.schema);
|
|
31686
32344
|
forceBjvStrings(pool, {
|
|
31687
|
-
|
|
32345
|
+
// Only strings are interned here. Traversing every numeric xmin cannot add an id.
|
|
32346
|
+
xmins: [],
|
|
31688
32347
|
name: table.name,
|
|
31689
32348
|
schema: table.schema,
|
|
31690
32349
|
columns: table.columns,
|