@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/CHANGELOG.md +13 -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/index.js
CHANGED
|
@@ -1,3 +1,1015 @@
|
|
|
1
|
+
// ../../core/dist/durability.js
|
|
2
|
+
function withDurability(api, durable) {
|
|
3
|
+
let queue = Promise.resolve();
|
|
4
|
+
return {
|
|
5
|
+
fetch(request) {
|
|
6
|
+
const next = queue.then(async () => {
|
|
7
|
+
const response = await api.fetch(request);
|
|
8
|
+
await durable.flush();
|
|
9
|
+
return response;
|
|
10
|
+
});
|
|
11
|
+
queue = next.catch(() => {
|
|
12
|
+
});
|
|
13
|
+
return next;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
var DurabilityConflictError = class extends Error {
|
|
18
|
+
constructor() {
|
|
19
|
+
super("Durable image changed in another writer; reopen before saving");
|
|
20
|
+
this.name = "DurabilityConflictError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
async function openDurable(options) {
|
|
24
|
+
const { backend, key: key2, codec: codec3 } = options;
|
|
25
|
+
const pageSize = options.pageSize ?? 16384;
|
|
26
|
+
if (!Number.isSafeInteger(pageSize) || pageSize < 1)
|
|
27
|
+
throw new RangeError("Invalid page size");
|
|
28
|
+
const image = await backend.load(key2);
|
|
29
|
+
let previous = image ? new Uint8Array(image.bytes) : new Uint8Array();
|
|
30
|
+
const value = image ? codec3.decode(image.bytes) : codec3.create();
|
|
31
|
+
let revision = image?.revision ?? 0;
|
|
32
|
+
let storedPageSize = image?.pageSize ?? pageSize;
|
|
33
|
+
let queue = Promise.resolve();
|
|
34
|
+
let closing = false;
|
|
35
|
+
let closed = false;
|
|
36
|
+
function enqueue(run) {
|
|
37
|
+
if (closed || closing)
|
|
38
|
+
return Promise.reject(new Error("Durable handle is closed"));
|
|
39
|
+
const next = queue.then(run);
|
|
40
|
+
queue = next.catch(() => {
|
|
41
|
+
});
|
|
42
|
+
return next;
|
|
43
|
+
}
|
|
44
|
+
async function persist() {
|
|
45
|
+
const bytes = codec3.encode(value);
|
|
46
|
+
const byteLength = bytes.length;
|
|
47
|
+
const changedPages = [];
|
|
48
|
+
let changedBytes = 0;
|
|
49
|
+
const words = bytes.byteOffset % 4 === 0 ? new Uint32Array(bytes.buffer, bytes.byteOffset, Math.floor(byteLength / 4)) : null;
|
|
50
|
+
const previousWords = new Uint32Array(previous.buffer, previous.byteOffset, Math.floor(previous.length / 4));
|
|
51
|
+
for (let offset = 0; offset < byteLength; offset += pageSize) {
|
|
52
|
+
const end = Math.min(offset + pageSize, byteLength);
|
|
53
|
+
let changed = storedPageSize !== pageSize || Math.min(pageSize, previous.length - offset) !== end - offset;
|
|
54
|
+
if (!changed)
|
|
55
|
+
changed = !equalPage(bytes, previous, words, previousWords, offset, end);
|
|
56
|
+
if (changed) {
|
|
57
|
+
changedPages.push(offset / pageSize);
|
|
58
|
+
changedBytes += end - offset;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (revision !== 0 && changedPages.length === 0 && byteLength === previous.length && storedPageSize === pageSize)
|
|
62
|
+
return;
|
|
63
|
+
const replacement = previous.length !== byteLength || changedBytes >= byteLength / 2 ? new Uint8Array(bytes) : void 0;
|
|
64
|
+
const pages = changedPages.map((index) => {
|
|
65
|
+
const offset = index * pageSize;
|
|
66
|
+
const end = Math.min(offset + pageSize, byteLength);
|
|
67
|
+
return {
|
|
68
|
+
index,
|
|
69
|
+
// Buffer.slice returns a borrowed view; the TypedArray operation owns its result.
|
|
70
|
+
bytes: replacement ? replacement.subarray(offset, end) : Uint8Array.prototype.slice.call(bytes, offset, end)
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
revision = await backend.commit(key2, revision, { byteLength, pageSize, pages });
|
|
74
|
+
if (replacement)
|
|
75
|
+
previous = replacement;
|
|
76
|
+
else
|
|
77
|
+
for (const page of pages)
|
|
78
|
+
previous.set(page.bytes, page.index * pageSize);
|
|
79
|
+
storedPageSize = pageSize;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
value,
|
|
83
|
+
flush: () => enqueue(persist),
|
|
84
|
+
write: (run) => enqueue(async () => {
|
|
85
|
+
const result = run(value);
|
|
86
|
+
if (result && typeof result.then === "function") {
|
|
87
|
+
throw new TypeError("Durable write callbacks must be synchronous");
|
|
88
|
+
}
|
|
89
|
+
await persist();
|
|
90
|
+
return result;
|
|
91
|
+
}),
|
|
92
|
+
close() {
|
|
93
|
+
if (closed)
|
|
94
|
+
return Promise.resolve();
|
|
95
|
+
if (closing)
|
|
96
|
+
return queue;
|
|
97
|
+
const next = enqueue(async () => {
|
|
98
|
+
await persist();
|
|
99
|
+
await codec3.close?.(value);
|
|
100
|
+
closed = true;
|
|
101
|
+
});
|
|
102
|
+
closing = true;
|
|
103
|
+
const completion = next.catch((error) => {
|
|
104
|
+
closing = false;
|
|
105
|
+
queue = Promise.resolve();
|
|
106
|
+
throw error;
|
|
107
|
+
});
|
|
108
|
+
queue = completion;
|
|
109
|
+
return completion;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function equalPage(left, right, leftWords, rightWords, start, end) {
|
|
114
|
+
let offset = start;
|
|
115
|
+
if (!leftWords) {
|
|
116
|
+
for (; offset < end; offset++)
|
|
117
|
+
if (left[offset] !== right[offset])
|
|
118
|
+
return false;
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
while (offset < end && offset % 4 !== 0) {
|
|
122
|
+
if (left[offset] !== right[offset])
|
|
123
|
+
return false;
|
|
124
|
+
offset++;
|
|
125
|
+
}
|
|
126
|
+
const wordEnd = Math.floor(end / 4);
|
|
127
|
+
let word2 = offset / 4;
|
|
128
|
+
for (; word2 + 4 <= wordEnd; word2 += 4) {
|
|
129
|
+
if (leftWords[word2] !== rightWords[word2] || leftWords[word2 + 1] !== rightWords[word2 + 1] || leftWords[word2 + 2] !== rightWords[word2 + 2] || leftWords[word2 + 3] !== rightWords[word2 + 3])
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
for (; word2 < wordEnd; word2++) {
|
|
133
|
+
if (leftWords[word2] !== rightWords[word2])
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
offset = word2 * 4;
|
|
137
|
+
while (offset < end) {
|
|
138
|
+
if (left[offset] !== right[offset])
|
|
139
|
+
return false;
|
|
140
|
+
offset++;
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
function applyDurablePatch(previous, patch) {
|
|
145
|
+
validateDurablePatch(patch);
|
|
146
|
+
const bytes = new Uint8Array(patch.byteLength);
|
|
147
|
+
bytes.set(previous.subarray(0, bytes.length));
|
|
148
|
+
for (const page of patch.pages)
|
|
149
|
+
bytes.set(page.bytes, page.index * patch.pageSize);
|
|
150
|
+
return bytes;
|
|
151
|
+
}
|
|
152
|
+
function validateDurablePatch(patch) {
|
|
153
|
+
if (!Number.isSafeInteger(patch.byteLength) || patch.byteLength < 0 || !Number.isSafeInteger(patch.pageSize) || patch.pageSize < 1) {
|
|
154
|
+
throw new RangeError("Invalid durable patch dimensions");
|
|
155
|
+
}
|
|
156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
157
|
+
for (const page of patch.pages) {
|
|
158
|
+
const offset = page.index * patch.pageSize;
|
|
159
|
+
if (!Number.isSafeInteger(page.index) || page.index < 0 || offset >= patch.byteLength || page.bytes.length !== Math.min(patch.pageSize, patch.byteLength - offset) || seen.has(page.index)) {
|
|
160
|
+
throw new RangeError("Invalid durable page");
|
|
161
|
+
}
|
|
162
|
+
seen.add(page.index);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function validateDurableCommit(patch, previous) {
|
|
166
|
+
validateDurablePatch(patch);
|
|
167
|
+
const supplied = new Set(patch.pages.map((page) => page.index));
|
|
168
|
+
const count = Math.ceil(patch.byteLength / patch.pageSize);
|
|
169
|
+
let start = count;
|
|
170
|
+
if (!previous || previous.pageSize !== patch.pageSize)
|
|
171
|
+
start = 0;
|
|
172
|
+
else if (patch.byteLength > previous.byteLength)
|
|
173
|
+
start = Math.floor(previous.byteLength / patch.pageSize);
|
|
174
|
+
else if (patch.byteLength < previous.byteLength && patch.byteLength % patch.pageSize !== 0)
|
|
175
|
+
start = count - 1;
|
|
176
|
+
for (let index = start; index < count; index++) {
|
|
177
|
+
if (!supplied.has(index))
|
|
178
|
+
throw new RangeError("Missing durable page");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
var MemoryDurabilityBackend = class {
|
|
182
|
+
images = /* @__PURE__ */ new Map();
|
|
183
|
+
async load(key2) {
|
|
184
|
+
const image = this.images.get(key2);
|
|
185
|
+
return image && { ...image, bytes: image.bytes.slice() };
|
|
186
|
+
}
|
|
187
|
+
async commit(key2, expectedRevision, patch) {
|
|
188
|
+
const previous = this.images.get(key2);
|
|
189
|
+
if ((previous?.revision ?? 0) !== expectedRevision)
|
|
190
|
+
throw new DurabilityConflictError();
|
|
191
|
+
validateDurableCommit(patch, previous && { byteLength: previous.bytes.length, pageSize: previous.pageSize });
|
|
192
|
+
const revision = expectedRevision + 1;
|
|
193
|
+
this.images.set(key2, {
|
|
194
|
+
revision,
|
|
195
|
+
pageSize: patch.pageSize,
|
|
196
|
+
bytes: applyDurablePatch(previous?.bytes ?? new Uint8Array(), patch)
|
|
197
|
+
});
|
|
198
|
+
return revision;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// ../../core/dist/durability-browser.js
|
|
203
|
+
var browserDurabilityLifecycle = {
|
|
204
|
+
subscribe(onSuspend) {
|
|
205
|
+
if (typeof globalThis.addEventListener !== "function")
|
|
206
|
+
return () => {
|
|
207
|
+
};
|
|
208
|
+
globalThis.addEventListener("pagehide", onSuspend);
|
|
209
|
+
return () => globalThis.removeEventListener("pagehide", onSuspend);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var IndexedDBDurabilityBackend = class {
|
|
213
|
+
database;
|
|
214
|
+
factory;
|
|
215
|
+
name;
|
|
216
|
+
durability;
|
|
217
|
+
constructor(options = {}) {
|
|
218
|
+
this.factory = options.indexedDB ?? globalThis.indexedDB;
|
|
219
|
+
if (!this.factory)
|
|
220
|
+
throw new Error("IndexedDB is unavailable; inject a durability backend");
|
|
221
|
+
this.name = options.name ?? "mockingbird";
|
|
222
|
+
this.durability = options.durability ?? "strict";
|
|
223
|
+
}
|
|
224
|
+
open() {
|
|
225
|
+
this.database ??= new Promise((resolve, reject) => {
|
|
226
|
+
const request = this.factory.open(this.name, 1);
|
|
227
|
+
request.onupgradeneeded = () => {
|
|
228
|
+
const db = request.result;
|
|
229
|
+
db.createObjectStore("images");
|
|
230
|
+
const pages = db.createObjectStore("pages", { keyPath: ["key", "index"] });
|
|
231
|
+
pages.createIndex("image", "key");
|
|
232
|
+
};
|
|
233
|
+
request.onerror = () => {
|
|
234
|
+
this.database = void 0;
|
|
235
|
+
reject(request.error);
|
|
236
|
+
};
|
|
237
|
+
request.onblocked = () => {
|
|
238
|
+
};
|
|
239
|
+
request.onsuccess = () => {
|
|
240
|
+
const db = request.result;
|
|
241
|
+
db.onversionchange = () => {
|
|
242
|
+
db.close();
|
|
243
|
+
this.database = void 0;
|
|
244
|
+
};
|
|
245
|
+
resolve(db);
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
return this.database;
|
|
249
|
+
}
|
|
250
|
+
async load(key2) {
|
|
251
|
+
const db = await this.open();
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
const tx = db.transaction(["images", "pages"], "readonly");
|
|
254
|
+
let image;
|
|
255
|
+
let failure;
|
|
256
|
+
tx.onabort = () => reject(failure ?? tx.error);
|
|
257
|
+
tx.oncomplete = () => resolve(image);
|
|
258
|
+
const meta = tx.objectStore("images").get(key2);
|
|
259
|
+
meta.onsuccess = () => {
|
|
260
|
+
const info = meta.result;
|
|
261
|
+
if (!info)
|
|
262
|
+
return;
|
|
263
|
+
const request = tx.objectStore("pages").index("image").getAll(key2);
|
|
264
|
+
request.onsuccess = () => {
|
|
265
|
+
try {
|
|
266
|
+
const bytes = new Uint8Array(info.byteLength);
|
|
267
|
+
const pages = request.result;
|
|
268
|
+
if (pages.length !== Math.ceil(info.byteLength / info.pageSize)) {
|
|
269
|
+
throw new Error("Incomplete durable image");
|
|
270
|
+
}
|
|
271
|
+
const patch = {
|
|
272
|
+
...info,
|
|
273
|
+
pages: pages.map((page) => ({ index: page.index, bytes: page.bytes }))
|
|
274
|
+
};
|
|
275
|
+
validateDurablePatch(patch);
|
|
276
|
+
for (const page of pages)
|
|
277
|
+
bytes.set(page.bytes, page.index * info.pageSize);
|
|
278
|
+
image = { revision: info.revision, pageSize: info.pageSize, bytes };
|
|
279
|
+
} catch (error) {
|
|
280
|
+
failure = error;
|
|
281
|
+
tx.abort();
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
async commit(key2, expectedRevision, patch) {
|
|
288
|
+
validateDurablePatch(patch);
|
|
289
|
+
const db = await this.open();
|
|
290
|
+
return new Promise((resolve, reject) => {
|
|
291
|
+
const tx = db.transaction(["images", "pages"], "readwrite", { durability: this.durability });
|
|
292
|
+
let failure;
|
|
293
|
+
tx.onabort = () => reject(failure ?? tx.error);
|
|
294
|
+
tx.oncomplete = () => resolve(expectedRevision + 1);
|
|
295
|
+
const images = tx.objectStore("images");
|
|
296
|
+
const pages = tx.objectStore("pages");
|
|
297
|
+
const request = images.get(key2);
|
|
298
|
+
request.onsuccess = () => {
|
|
299
|
+
try {
|
|
300
|
+
const previous = request.result;
|
|
301
|
+
if ((previous?.revision ?? 0) !== expectedRevision) {
|
|
302
|
+
failure = new DurabilityConflictError();
|
|
303
|
+
tx.abort();
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
validateDurableCommit(patch, previous);
|
|
307
|
+
const oldCount = previous ? Math.ceil(previous.byteLength / previous.pageSize) : 0;
|
|
308
|
+
const newCount = Math.ceil(patch.byteLength / patch.pageSize);
|
|
309
|
+
for (let index = newCount; index < oldCount; index++)
|
|
310
|
+
pages.delete([key2, index]);
|
|
311
|
+
for (const page of patch.pages) {
|
|
312
|
+
const bytes = page.bytes.byteOffset === 0 && page.bytes.byteLength === page.bytes.buffer.byteLength ? page.bytes : new Uint8Array(page.bytes);
|
|
313
|
+
pages.put({ key: key2, index: page.index, bytes });
|
|
314
|
+
}
|
|
315
|
+
images.put({
|
|
316
|
+
revision: expectedRevision + 1,
|
|
317
|
+
byteLength: patch.byteLength,
|
|
318
|
+
pageSize: patch.pageSize
|
|
319
|
+
}, key2);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
failure = error;
|
|
322
|
+
tx.abort();
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/** Release this adapter's connection; does not delete any saved images. */
|
|
328
|
+
async close() {
|
|
329
|
+
const opening = this.database;
|
|
330
|
+
this.database = void 0;
|
|
331
|
+
if (opening)
|
|
332
|
+
(await opening).close();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var LocalStorageDurabilityBackend = class {
|
|
336
|
+
storage;
|
|
337
|
+
prefix;
|
|
338
|
+
locks;
|
|
339
|
+
constructor(options = {}) {
|
|
340
|
+
this.storage = options.storage ?? globalThis.localStorage;
|
|
341
|
+
if (!this.storage)
|
|
342
|
+
throw new Error("localStorage is unavailable; inject a durability backend");
|
|
343
|
+
this.prefix = options.prefix ?? "mockingbird:";
|
|
344
|
+
this.locks = options.locks ?? globalThis.navigator?.locks;
|
|
345
|
+
}
|
|
346
|
+
async load(key2) {
|
|
347
|
+
return this.read(key2);
|
|
348
|
+
}
|
|
349
|
+
read(key2) {
|
|
350
|
+
const text = this.storage.getItem(this.prefix + key2);
|
|
351
|
+
if (text === null)
|
|
352
|
+
return void 0;
|
|
353
|
+
const image = JSON.parse(text);
|
|
354
|
+
const binary = atob(image.data);
|
|
355
|
+
const bytes = new Uint8Array(binary.length);
|
|
356
|
+
for (let i = 0; i < binary.length; i++)
|
|
357
|
+
bytes[i] = binary.charCodeAt(i);
|
|
358
|
+
return { revision: image.revision, pageSize: image.pageSize, bytes };
|
|
359
|
+
}
|
|
360
|
+
async commit(key2, expectedRevision, patch) {
|
|
361
|
+
const save = () => {
|
|
362
|
+
const previous = this.read(key2);
|
|
363
|
+
if ((previous?.revision ?? 0) !== expectedRevision)
|
|
364
|
+
throw new DurabilityConflictError();
|
|
365
|
+
validateDurableCommit(patch, previous && { byteLength: previous.bytes.length, pageSize: previous.pageSize });
|
|
366
|
+
const bytes = applyDurablePatch(previous?.bytes ?? new Uint8Array(), patch);
|
|
367
|
+
let binary = "";
|
|
368
|
+
for (let offset = 0; offset < bytes.length; offset += 8192) {
|
|
369
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
|
|
370
|
+
}
|
|
371
|
+
const revision = expectedRevision + 1;
|
|
372
|
+
this.storage.setItem(this.prefix + key2, JSON.stringify({ revision, pageSize: patch.pageSize, data: btoa(binary) }));
|
|
373
|
+
return revision;
|
|
374
|
+
};
|
|
375
|
+
return this.locks ? this.locks.request(this.prefix + key2, save) : save();
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// ../../core/dist/durability-buffered.js
|
|
380
|
+
var HEADER = 32;
|
|
381
|
+
var MAGIC = [77, 66, 68, 74];
|
|
382
|
+
function isDurableJournal(bytes) {
|
|
383
|
+
return MAGIC.every((byte, index) => bytes[index] === byte);
|
|
384
|
+
}
|
|
385
|
+
function checksum(bytes) {
|
|
386
|
+
let hash = 2166136261;
|
|
387
|
+
for (const byte of bytes)
|
|
388
|
+
hash = Math.imul(hash ^ byte, 16777619);
|
|
389
|
+
return hash >>> 0;
|
|
390
|
+
}
|
|
391
|
+
function header(checkpointBytes, hash) {
|
|
392
|
+
if (checkpointBytes > 4294967295)
|
|
393
|
+
throw new RangeError("Buffered checkpoint exceeds 4 GiB");
|
|
394
|
+
const bytes = new Uint8Array(HEADER);
|
|
395
|
+
bytes.set(MAGIC);
|
|
396
|
+
const view = new DataView(bytes.buffer);
|
|
397
|
+
view.setUint32(4, 1, true);
|
|
398
|
+
view.setUint32(8, checkpointBytes, true);
|
|
399
|
+
view.setUint32(20, hash, true);
|
|
400
|
+
view.setUint32(24, checksum(bytes.subarray(0, 24)), true);
|
|
401
|
+
return bytes;
|
|
402
|
+
}
|
|
403
|
+
function encodeDurableCheckpoint(record) {
|
|
404
|
+
const bytes = new Uint8Array(HEADER + record.length);
|
|
405
|
+
bytes.set(header(record.length, checksum(record)));
|
|
406
|
+
bytes.set(record, HEADER);
|
|
407
|
+
return bytes;
|
|
408
|
+
}
|
|
409
|
+
function readDurableJournal(bytes) {
|
|
410
|
+
if (!isDurableJournal(bytes))
|
|
411
|
+
return void 0;
|
|
412
|
+
const bad2 = () => {
|
|
413
|
+
throw new Error("Invalid buffered durability image");
|
|
414
|
+
};
|
|
415
|
+
if (bytes.length < HEADER)
|
|
416
|
+
return bad2();
|
|
417
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
418
|
+
const checkpointBytes = view.getUint32(8, true);
|
|
419
|
+
if (view.getUint32(4, true) !== 1 || view.getUint32(12, true) !== 0 || view.getUint32(16, true) !== 0 || view.getUint32(24, true) !== checksum(bytes.subarray(0, 24)) || HEADER + checkpointBytes > bytes.length)
|
|
420
|
+
return bad2();
|
|
421
|
+
const checkpoint = bytes.subarray(HEADER, HEADER + checkpointBytes);
|
|
422
|
+
if (checksum(checkpoint) !== view.getUint32(20, true))
|
|
423
|
+
return bad2();
|
|
424
|
+
const records = [checkpoint];
|
|
425
|
+
let offset = HEADER + checkpointBytes;
|
|
426
|
+
while (offset < bytes.length) {
|
|
427
|
+
if (offset + 8 > bytes.length)
|
|
428
|
+
return bad2();
|
|
429
|
+
const length = view.getUint32(offset, true);
|
|
430
|
+
const hash = view.getUint32(offset + 4, true);
|
|
431
|
+
offset += 8;
|
|
432
|
+
if (offset + length > bytes.length)
|
|
433
|
+
return bad2();
|
|
434
|
+
const record = bytes.subarray(offset, offset + length);
|
|
435
|
+
if (checksum(record) !== hash)
|
|
436
|
+
return bad2();
|
|
437
|
+
records.push(record);
|
|
438
|
+
offset += length;
|
|
439
|
+
}
|
|
440
|
+
if (offset !== bytes.length)
|
|
441
|
+
return bad2();
|
|
442
|
+
return records;
|
|
443
|
+
}
|
|
444
|
+
var timers = {
|
|
445
|
+
schedule(run, delay) {
|
|
446
|
+
const timer = setTimeout(run, delay);
|
|
447
|
+
timer.unref?.();
|
|
448
|
+
return timer;
|
|
449
|
+
},
|
|
450
|
+
cancel(timer) {
|
|
451
|
+
clearTimeout(timer);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
async function openBufferedDurable(options) {
|
|
455
|
+
const { backend, key: key2, codec: codec3 } = options;
|
|
456
|
+
const pageSize = options.pageSize ?? 16384;
|
|
457
|
+
const interval = options.flushIntervalMs ?? 1e3;
|
|
458
|
+
const maxBatches = options.compactAfterBatches ?? 256;
|
|
459
|
+
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || !Number.isSafeInteger(interval) || interval < 1 || !Number.isSafeInteger(maxBatches) || maxBatches < 1 || options.compactAfterBytes !== void 0 && (!Number.isSafeInteger(options.compactAfterBytes) || options.compactAfterBytes < 1))
|
|
460
|
+
throw new RangeError("Invalid buffered durability options");
|
|
461
|
+
const scheduler = options.scheduler ?? timers;
|
|
462
|
+
const image = await backend.load(key2);
|
|
463
|
+
const records = image && readDurableJournal(image.bytes);
|
|
464
|
+
const value = image ? records ? codec3.decodeJournal(records) : codec3.decode(image.bytes) : codec3.create();
|
|
465
|
+
let revision = image?.revision ?? 0;
|
|
466
|
+
let length = records && image ? image.bytes.length : 0;
|
|
467
|
+
let baseLength = records?.[0]?.length ?? 0;
|
|
468
|
+
let baseHash = records?.[0] ? checksum(records[0]) : 0;
|
|
469
|
+
let batches = records ? records.length - 1 : 0;
|
|
470
|
+
let tail = records && image ? new Uint8Array(image.bytes.subarray(Math.floor((length - 1) / pageSize) * pageSize)) : new Uint8Array();
|
|
471
|
+
let checkpointRequired = !records || image?.pageSize !== pageSize;
|
|
472
|
+
let previous;
|
|
473
|
+
if (records)
|
|
474
|
+
previous = codec3.baseline ? codec3.baseline(value) : codec3.capture(value, void 0, true).token;
|
|
475
|
+
let generation = 0;
|
|
476
|
+
let saved = 0;
|
|
477
|
+
let queued = 0;
|
|
478
|
+
let inWrite = false;
|
|
479
|
+
let closing = false;
|
|
480
|
+
let closed = false;
|
|
481
|
+
let conflict;
|
|
482
|
+
let lastError;
|
|
483
|
+
let exposed = false;
|
|
484
|
+
let timer;
|
|
485
|
+
let timerActive = false;
|
|
486
|
+
let retryDelay = interval;
|
|
487
|
+
let queue = Promise.resolve();
|
|
488
|
+
function cancel() {
|
|
489
|
+
if (timerActive)
|
|
490
|
+
scheduler.cancel(timer);
|
|
491
|
+
timerActive = false;
|
|
492
|
+
}
|
|
493
|
+
function schedule(delay = interval) {
|
|
494
|
+
if (timerActive || closing || closed || conflict)
|
|
495
|
+
return;
|
|
496
|
+
timerActive = true;
|
|
497
|
+
timer = scheduler.schedule(() => {
|
|
498
|
+
timerActive = false;
|
|
499
|
+
if (!codec3.canCapture(value)) {
|
|
500
|
+
schedule();
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
void enqueue(() => persist()).catch(() => {
|
|
504
|
+
});
|
|
505
|
+
}, delay);
|
|
506
|
+
}
|
|
507
|
+
function failure(error) {
|
|
508
|
+
lastError = error;
|
|
509
|
+
if (error instanceof DurabilityConflictError || typeof error === "object" && error !== null && "name" in error && error.name === "DurabilityConflictError") {
|
|
510
|
+
conflict = error;
|
|
511
|
+
cancel();
|
|
512
|
+
stopLifecycle?.();
|
|
513
|
+
} else {
|
|
514
|
+
schedule(retryDelay);
|
|
515
|
+
retryDelay = Math.min(1e4, Math.max(1e3, retryDelay * 2));
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
options.onError?.(error);
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function enqueue(run) {
|
|
523
|
+
if (closed || closing)
|
|
524
|
+
return Promise.reject(new Error("Durable handle is closed"));
|
|
525
|
+
if (conflict)
|
|
526
|
+
return Promise.reject(conflict);
|
|
527
|
+
queued++;
|
|
528
|
+
const next = queue.then(run).finally(() => {
|
|
529
|
+
queued--;
|
|
530
|
+
});
|
|
531
|
+
queue = next.catch(() => {
|
|
532
|
+
});
|
|
533
|
+
return next;
|
|
534
|
+
}
|
|
535
|
+
async function persist(force = false) {
|
|
536
|
+
if (conflict)
|
|
537
|
+
throw conflict;
|
|
538
|
+
cancel();
|
|
539
|
+
if (!checkpointRequired && generation === saved && !exposed && !force)
|
|
540
|
+
return;
|
|
541
|
+
const at = generation;
|
|
542
|
+
try {
|
|
543
|
+
const checkpoint = checkpointRequired || batches >= maxBatches || length - HEADER - baseLength >= (options.compactAfterBytes ?? Math.max(4 * 1024 * 1024, baseLength));
|
|
544
|
+
const capture = codec3.capture(value, previous, checkpoint || exposed);
|
|
545
|
+
if (!capture.bytes) {
|
|
546
|
+
previous = capture.token;
|
|
547
|
+
saved = at;
|
|
548
|
+
codec3.acknowledge(value, capture.token);
|
|
549
|
+
lastError = void 0;
|
|
550
|
+
retryDelay = interval;
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const payload = capture.bytes;
|
|
554
|
+
let patch;
|
|
555
|
+
let nextLength;
|
|
556
|
+
let nextBase = baseLength;
|
|
557
|
+
let nextHash = baseHash;
|
|
558
|
+
let nextBatches = batches;
|
|
559
|
+
if (capture.checkpoint) {
|
|
560
|
+
nextBase = payload.length;
|
|
561
|
+
nextHash = checksum(payload);
|
|
562
|
+
nextBatches = 0;
|
|
563
|
+
nextLength = HEADER + payload.length;
|
|
564
|
+
const bytes = new Uint8Array(nextLength);
|
|
565
|
+
bytes.set(header(nextBase, nextHash));
|
|
566
|
+
bytes.set(payload, HEADER);
|
|
567
|
+
const pages = [];
|
|
568
|
+
for (let offset = 0; offset < bytes.length; offset += pageSize)
|
|
569
|
+
pages.push({ index: offset / pageSize, bytes: bytes.subarray(offset, offset + pageSize) });
|
|
570
|
+
patch = { byteLength: nextLength, pageSize, pages };
|
|
571
|
+
} else {
|
|
572
|
+
const frame = new Uint8Array(8 + payload.length);
|
|
573
|
+
const view = new DataView(frame.buffer);
|
|
574
|
+
view.setUint32(0, payload.length, true);
|
|
575
|
+
view.setUint32(4, checksum(payload), true);
|
|
576
|
+
frame.set(payload, 8);
|
|
577
|
+
nextLength = length + frame.length;
|
|
578
|
+
nextBatches++;
|
|
579
|
+
const pages = /* @__PURE__ */ new Map();
|
|
580
|
+
let offset = length;
|
|
581
|
+
while (offset < nextLength) {
|
|
582
|
+
const index = Math.floor(offset / pageSize);
|
|
583
|
+
const start = index * pageSize;
|
|
584
|
+
let page = pages.get(index);
|
|
585
|
+
if (!page) {
|
|
586
|
+
page = new Uint8Array(Math.min(pageSize, nextLength - start));
|
|
587
|
+
if (index === Math.floor((length - 1) / pageSize))
|
|
588
|
+
page.set(tail);
|
|
589
|
+
pages.set(index, page);
|
|
590
|
+
}
|
|
591
|
+
const count = Math.min(page.length - (offset - start), nextLength - offset);
|
|
592
|
+
page.set(frame.subarray(offset - length, offset - length + count), offset - start);
|
|
593
|
+
offset += count;
|
|
594
|
+
}
|
|
595
|
+
patch = {
|
|
596
|
+
byteLength: nextLength,
|
|
597
|
+
pageSize,
|
|
598
|
+
pages: [...pages].map(([index, bytes]) => ({ index, bytes }))
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
const lastPage = patch.pages.find((page) => page.index === Math.floor((nextLength - 1) / pageSize));
|
|
602
|
+
if (!lastPage)
|
|
603
|
+
throw new Error("Missing buffered tail page");
|
|
604
|
+
const nextTail = lastPage.bytes.byteOffset === 0 && lastPage.bytes.byteLength === lastPage.bytes.buffer.byteLength ? lastPage.bytes : new Uint8Array(lastPage.bytes);
|
|
605
|
+
revision = await backend.commit(key2, revision, patch);
|
|
606
|
+
tail = nextTail;
|
|
607
|
+
length = nextLength;
|
|
608
|
+
baseLength = nextBase;
|
|
609
|
+
baseHash = nextHash;
|
|
610
|
+
batches = nextBatches;
|
|
611
|
+
previous = capture.token;
|
|
612
|
+
checkpointRequired = false;
|
|
613
|
+
codec3.acknowledge(value, capture.token);
|
|
614
|
+
saved = at;
|
|
615
|
+
lastError = void 0;
|
|
616
|
+
retryDelay = interval;
|
|
617
|
+
} catch (error) {
|
|
618
|
+
failure(error);
|
|
619
|
+
throw error;
|
|
620
|
+
} finally {
|
|
621
|
+
if (generation !== saved || exposed)
|
|
622
|
+
schedule();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
let unsubscribe = () => {
|
|
626
|
+
};
|
|
627
|
+
let stopLifecycle;
|
|
628
|
+
unsubscribe = codec3.subscribe(value, {
|
|
629
|
+
changed() {
|
|
630
|
+
generation++;
|
|
631
|
+
schedule();
|
|
632
|
+
},
|
|
633
|
+
exposed() {
|
|
634
|
+
exposed = true;
|
|
635
|
+
generation++;
|
|
636
|
+
schedule();
|
|
637
|
+
},
|
|
638
|
+
assertWritable() {
|
|
639
|
+
if (closed || closing && !inWrite)
|
|
640
|
+
throw new Error("Durable handle is closed");
|
|
641
|
+
},
|
|
642
|
+
closed() {
|
|
643
|
+
if (closing || closed)
|
|
644
|
+
return;
|
|
645
|
+
closed = true;
|
|
646
|
+
cancel();
|
|
647
|
+
unsubscribe();
|
|
648
|
+
stopLifecycle?.();
|
|
649
|
+
if (checkpointRequired || generation !== saved)
|
|
650
|
+
failure(new Error("Database closed directly; pending buffered changes were not acknowledged"));
|
|
651
|
+
void Promise.resolve().then(() => codec3.close?.(value)).catch(failure);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
stopLifecycle = !closed ? options.lifecycle?.subscribe(() => {
|
|
655
|
+
if (!closed && !closing && !conflict && codec3.canCapture(value))
|
|
656
|
+
void enqueue(persist).catch(() => {
|
|
657
|
+
});
|
|
658
|
+
}) : void 0;
|
|
659
|
+
if (checkpointRequired)
|
|
660
|
+
schedule();
|
|
661
|
+
return {
|
|
662
|
+
value,
|
|
663
|
+
get pending() {
|
|
664
|
+
return checkpointRequired || generation !== saved || queued > 0;
|
|
665
|
+
},
|
|
666
|
+
get lastError() {
|
|
667
|
+
return lastError;
|
|
668
|
+
},
|
|
669
|
+
flush: () => enqueue(() => persist(true)),
|
|
670
|
+
write: (run) => enqueue(async () => {
|
|
671
|
+
let result;
|
|
672
|
+
inWrite = true;
|
|
673
|
+
try {
|
|
674
|
+
result = run(value);
|
|
675
|
+
} finally {
|
|
676
|
+
inWrite = false;
|
|
677
|
+
}
|
|
678
|
+
if (result && typeof result.then === "function")
|
|
679
|
+
throw new TypeError("Durable write callbacks must be synchronous");
|
|
680
|
+
generation++;
|
|
681
|
+
await persist();
|
|
682
|
+
return result;
|
|
683
|
+
}),
|
|
684
|
+
close() {
|
|
685
|
+
if (closed)
|
|
686
|
+
return lastError ? Promise.reject(lastError) : Promise.resolve();
|
|
687
|
+
if (closing)
|
|
688
|
+
return queue;
|
|
689
|
+
const next = enqueue(async () => {
|
|
690
|
+
await persist(true);
|
|
691
|
+
await codec3.close?.(value);
|
|
692
|
+
cancel();
|
|
693
|
+
unsubscribe();
|
|
694
|
+
stopLifecycle?.();
|
|
695
|
+
closed = true;
|
|
696
|
+
});
|
|
697
|
+
closing = true;
|
|
698
|
+
const completion = next.catch((error) => {
|
|
699
|
+
closing = false;
|
|
700
|
+
queue = Promise.resolve();
|
|
701
|
+
if (!conflict)
|
|
702
|
+
schedule();
|
|
703
|
+
throw error;
|
|
704
|
+
});
|
|
705
|
+
queue = completion;
|
|
706
|
+
return completion;
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
function packDurableRecord(metadata, snapshot) {
|
|
711
|
+
const meta = new TextEncoder().encode(JSON.stringify(packValue(metadata)));
|
|
712
|
+
const bytes = new Uint8Array(4 + meta.length + snapshot.length);
|
|
713
|
+
new DataView(bytes.buffer).setUint32(0, meta.length, true);
|
|
714
|
+
bytes.set(meta, 4);
|
|
715
|
+
bytes.set(snapshot, 4 + meta.length);
|
|
716
|
+
return bytes;
|
|
717
|
+
}
|
|
718
|
+
function unpackDurableRecord(bytes) {
|
|
719
|
+
if (bytes.length < 4)
|
|
720
|
+
throw new Error("Invalid buffered record");
|
|
721
|
+
const length = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0, true);
|
|
722
|
+
if (length > bytes.length - 4)
|
|
723
|
+
throw new Error("Invalid buffered record");
|
|
724
|
+
return {
|
|
725
|
+
metadata: unpackValue(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(4, 4 + length)))),
|
|
726
|
+
snapshot: bytes.subarray(4 + length)
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function packValue(value) {
|
|
730
|
+
if (typeof value === "bigint")
|
|
731
|
+
return [2, String(value)];
|
|
732
|
+
if (typeof value === "number" && !Number.isFinite(value))
|
|
733
|
+
return [3, String(value)];
|
|
734
|
+
if (value instanceof Uint8Array)
|
|
735
|
+
return [1, Array.from(value)];
|
|
736
|
+
if (Array.isArray(value))
|
|
737
|
+
return [0, value.map(packValue)];
|
|
738
|
+
if (value !== null && typeof value === "object")
|
|
739
|
+
return [
|
|
740
|
+
4,
|
|
741
|
+
Object.entries(value).filter(([, value2]) => value2 !== void 0).map(([key2, value2]) => [key2, packValue(value2)])
|
|
742
|
+
];
|
|
743
|
+
if (value === void 0)
|
|
744
|
+
throw new TypeError("Undefined durable metadata");
|
|
745
|
+
return value;
|
|
746
|
+
}
|
|
747
|
+
function unpackValue(value) {
|
|
748
|
+
if (!Array.isArray(value))
|
|
749
|
+
return value;
|
|
750
|
+
const [tag, payload] = value;
|
|
751
|
+
switch (tag) {
|
|
752
|
+
case 0:
|
|
753
|
+
return payload.map(unpackValue);
|
|
754
|
+
case 1:
|
|
755
|
+
return new Uint8Array(payload);
|
|
756
|
+
case 2:
|
|
757
|
+
return BigInt(payload);
|
|
758
|
+
case 3:
|
|
759
|
+
return Number(payload);
|
|
760
|
+
case 4:
|
|
761
|
+
return Object.fromEntries(payload.map(([key2, value2]) => [key2, unpackValue(value2)]));
|
|
762
|
+
default:
|
|
763
|
+
throw new Error("Invalid durable metadata tag");
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// ../../core/dist/fork-map.js
|
|
768
|
+
var ForkMap = class _ForkMap {
|
|
769
|
+
owner = {};
|
|
770
|
+
index = { owner: this.owner, groups: [] };
|
|
771
|
+
order = { owner: this.owner, cells: [] };
|
|
772
|
+
shift = 0;
|
|
773
|
+
length = 0;
|
|
774
|
+
count = 0;
|
|
775
|
+
nextSequence = 0;
|
|
776
|
+
layout = 0;
|
|
777
|
+
cleared = 0;
|
|
778
|
+
version = 0;
|
|
779
|
+
constructor(entries) {
|
|
780
|
+
if (entries)
|
|
781
|
+
for (const [key2, value] of entries)
|
|
782
|
+
this.set(key2, value);
|
|
783
|
+
}
|
|
784
|
+
get size() {
|
|
785
|
+
return this.count;
|
|
786
|
+
}
|
|
787
|
+
get [Symbol.toStringTag]() {
|
|
788
|
+
return "Map";
|
|
789
|
+
}
|
|
790
|
+
fork() {
|
|
791
|
+
const copy = new _ForkMap();
|
|
792
|
+
copy.index = this.index;
|
|
793
|
+
copy.order = this.order;
|
|
794
|
+
copy.shift = this.shift;
|
|
795
|
+
copy.length = this.length;
|
|
796
|
+
copy.count = this.count;
|
|
797
|
+
copy.nextSequence = this.nextSequence;
|
|
798
|
+
this.owner = {};
|
|
799
|
+
return copy;
|
|
800
|
+
}
|
|
801
|
+
/** Visit changed hash buckets only. Values must be replaced, rather than mutated in place. */
|
|
802
|
+
*changes(previous) {
|
|
803
|
+
if (previous?.index === this.index)
|
|
804
|
+
return;
|
|
805
|
+
for (let group = 0; group < 32; group++) {
|
|
806
|
+
const currentGroup = this.index.groups[group];
|
|
807
|
+
const oldGroup = previous?.index.groups[group];
|
|
808
|
+
if (currentGroup === oldGroup)
|
|
809
|
+
continue;
|
|
810
|
+
for (let bucket = 0; bucket < 32; bucket++) {
|
|
811
|
+
const current = currentGroup?.buckets[bucket]?.data;
|
|
812
|
+
const old = oldGroup?.buckets[bucket]?.data;
|
|
813
|
+
if (current === old)
|
|
814
|
+
continue;
|
|
815
|
+
if (current)
|
|
816
|
+
for (const [key2, entry] of current) {
|
|
817
|
+
const before = old?.get(key2);
|
|
818
|
+
if (!before || !Object.is(before.value, entry.value) || before.sequence !== entry.sequence)
|
|
819
|
+
yield { key: key2, value: entry.value, deleted: false };
|
|
820
|
+
}
|
|
821
|
+
if (old) {
|
|
822
|
+
for (const key2 of old.keys())
|
|
823
|
+
if (!current?.has(key2))
|
|
824
|
+
yield { key: key2, deleted: true };
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
get(key2) {
|
|
830
|
+
return this.find(key2)?.value;
|
|
831
|
+
}
|
|
832
|
+
has(key2) {
|
|
833
|
+
return this.find(key2) !== void 0;
|
|
834
|
+
}
|
|
835
|
+
set(key2, value) {
|
|
836
|
+
if (typeof key2 === "number" && key2 === 0)
|
|
837
|
+
key2 = 0;
|
|
838
|
+
const hash = hashKey(key2);
|
|
839
|
+
const existing = this.bucket(hash)?.get(key2);
|
|
840
|
+
if (existing && Object.is(existing.value, value))
|
|
841
|
+
return this;
|
|
842
|
+
const entry = existing ? { ...existing, value } : { key: key2, value, position: this.length++, sequence: this.nextSequence++ };
|
|
843
|
+
this.writableBucket(hash).set(key2, entry);
|
|
844
|
+
this.writeOrder(entry.position, entry);
|
|
845
|
+
if (!existing)
|
|
846
|
+
this.count++;
|
|
847
|
+
this.version++;
|
|
848
|
+
return this;
|
|
849
|
+
}
|
|
850
|
+
delete(key2) {
|
|
851
|
+
const hash = hashKey(key2);
|
|
852
|
+
const entry = this.bucket(hash)?.get(key2);
|
|
853
|
+
if (!entry)
|
|
854
|
+
return false;
|
|
855
|
+
this.writableBucket(hash).delete(key2);
|
|
856
|
+
this.writeOrder(entry.position, void 0);
|
|
857
|
+
this.count--;
|
|
858
|
+
this.version++;
|
|
859
|
+
if (this.length > 1024 && this.length > this.count * 2)
|
|
860
|
+
this.compact();
|
|
861
|
+
return true;
|
|
862
|
+
}
|
|
863
|
+
clear() {
|
|
864
|
+
this.index = { owner: this.owner, groups: [] };
|
|
865
|
+
this.order = { owner: this.owner, cells: [] };
|
|
866
|
+
this.shift = this.length = this.count = this.nextSequence = 0;
|
|
867
|
+
this.layout++;
|
|
868
|
+
this.cleared++;
|
|
869
|
+
this.version++;
|
|
870
|
+
}
|
|
871
|
+
entries() {
|
|
872
|
+
return this.iterate(0);
|
|
873
|
+
}
|
|
874
|
+
keys() {
|
|
875
|
+
return this.iterate(1);
|
|
876
|
+
}
|
|
877
|
+
values() {
|
|
878
|
+
return this.iterate(2);
|
|
879
|
+
}
|
|
880
|
+
[Symbol.iterator]() {
|
|
881
|
+
return this.entries();
|
|
882
|
+
}
|
|
883
|
+
forEach(callback, thisArg) {
|
|
884
|
+
for (const [key2, value] of this)
|
|
885
|
+
callback.call(thisArg, value, key2, this);
|
|
886
|
+
}
|
|
887
|
+
find(key2) {
|
|
888
|
+
return this.bucket(hashKey(key2))?.get(key2);
|
|
889
|
+
}
|
|
890
|
+
bucket(hash) {
|
|
891
|
+
return this.index.groups[hash >>> 5 & 31]?.buckets[hash & 31]?.data;
|
|
892
|
+
}
|
|
893
|
+
writableBucket(hash) {
|
|
894
|
+
if (this.index.owner !== this.owner)
|
|
895
|
+
this.index = { owner: this.owner, groups: this.index.groups.slice() };
|
|
896
|
+
const groupIndex = hash >>> 5 & 31;
|
|
897
|
+
let group = this.index.groups[groupIndex];
|
|
898
|
+
if (!group || group.owner !== this.owner) {
|
|
899
|
+
group = { owner: this.owner, buckets: group?.buckets.slice() ?? [] };
|
|
900
|
+
this.index.groups[groupIndex] = group;
|
|
901
|
+
}
|
|
902
|
+
const at = hash & 31;
|
|
903
|
+
let bucket = group.buckets[at];
|
|
904
|
+
if (!bucket || bucket.owner !== this.owner) {
|
|
905
|
+
bucket = { owner: this.owner, data: bucket ? this.copyBucket(bucket.data) : /* @__PURE__ */ new Map() };
|
|
906
|
+
group.buckets[at] = bucket;
|
|
907
|
+
}
|
|
908
|
+
return bucket.data;
|
|
909
|
+
}
|
|
910
|
+
copyBucket(bucket) {
|
|
911
|
+
return new Map(bucket);
|
|
912
|
+
}
|
|
913
|
+
leaf(position) {
|
|
914
|
+
let node = this.order;
|
|
915
|
+
for (let shift = this.shift; shift > 0; shift -= 5)
|
|
916
|
+
node = node.cells[Math.floor(position / 2 ** shift) & 31];
|
|
917
|
+
return node;
|
|
918
|
+
}
|
|
919
|
+
writeOrder(position, entry) {
|
|
920
|
+
while (position >= 2 ** (this.shift + 5)) {
|
|
921
|
+
this.order = { owner: this.owner, cells: [this.order] };
|
|
922
|
+
this.shift += 5;
|
|
923
|
+
}
|
|
924
|
+
if (this.order.owner !== this.owner)
|
|
925
|
+
this.order = { owner: this.owner, cells: this.order.cells.slice() };
|
|
926
|
+
let node = this.order;
|
|
927
|
+
for (let shift = this.shift; shift > 0; shift -= 5) {
|
|
928
|
+
const at = Math.floor(position / 2 ** shift) & 31;
|
|
929
|
+
let child = node.cells[at];
|
|
930
|
+
if (!child || child.owner !== this.owner) {
|
|
931
|
+
child = { owner: this.owner, cells: child?.cells.slice() ?? [] };
|
|
932
|
+
node.cells[at] = child;
|
|
933
|
+
}
|
|
934
|
+
node = child;
|
|
935
|
+
}
|
|
936
|
+
node.cells[position & 31] = entry;
|
|
937
|
+
}
|
|
938
|
+
compact() {
|
|
939
|
+
const entries = [];
|
|
940
|
+
for (let position = 0; position < this.length; position++) {
|
|
941
|
+
const entry = this.leaf(position).cells[position & 31];
|
|
942
|
+
if (entry)
|
|
943
|
+
entries.push(entry);
|
|
944
|
+
}
|
|
945
|
+
this.index = { owner: this.owner, groups: [] };
|
|
946
|
+
this.order = { owner: this.owner, cells: [] };
|
|
947
|
+
this.shift = this.length = this.count = 0;
|
|
948
|
+
for (const original of entries) {
|
|
949
|
+
const entry = { ...original, position: this.length++ };
|
|
950
|
+
this.writableBucket(hashKey(entry.key)).set(entry.key, entry);
|
|
951
|
+
this.writeOrder(entry.position, entry);
|
|
952
|
+
this.count++;
|
|
953
|
+
}
|
|
954
|
+
this.layout++;
|
|
955
|
+
}
|
|
956
|
+
*iterate(kind) {
|
|
957
|
+
let position = 0;
|
|
958
|
+
let layout = this.layout;
|
|
959
|
+
let cleared = this.cleared;
|
|
960
|
+
let lastSequence = -1;
|
|
961
|
+
let version = -1;
|
|
962
|
+
let leaf;
|
|
963
|
+
let page = -1;
|
|
964
|
+
for (; ; ) {
|
|
965
|
+
if (cleared !== this.cleared) {
|
|
966
|
+
position = 0;
|
|
967
|
+
lastSequence = -1;
|
|
968
|
+
cleared = this.cleared;
|
|
969
|
+
}
|
|
970
|
+
if (layout !== this.layout) {
|
|
971
|
+
position = 0;
|
|
972
|
+
while (position < this.length) {
|
|
973
|
+
const entry2 = this.leaf(position).cells[position & 31];
|
|
974
|
+
if (entry2 && entry2.sequence > lastSequence)
|
|
975
|
+
break;
|
|
976
|
+
position++;
|
|
977
|
+
}
|
|
978
|
+
layout = this.layout;
|
|
979
|
+
}
|
|
980
|
+
if (position >= this.length)
|
|
981
|
+
return;
|
|
982
|
+
const nextPage = Math.floor(position / 32);
|
|
983
|
+
if (page !== nextPage || version !== this.version) {
|
|
984
|
+
leaf = this.leaf(position);
|
|
985
|
+
page = nextPage;
|
|
986
|
+
version = this.version;
|
|
987
|
+
}
|
|
988
|
+
const entry = leaf?.cells[position++ & 31];
|
|
989
|
+
if (!entry)
|
|
990
|
+
continue;
|
|
991
|
+
lastSequence = entry.sequence;
|
|
992
|
+
yield kind === 0 ? [entry.key, entry.value] : kind === 1 ? entry.key : entry.value;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
function hashKey(key2) {
|
|
997
|
+
if (typeof key2 === "number" && Number.isInteger(key2))
|
|
998
|
+
return mix((key2 | 0) ^ (key2 / 4294967296 | 0));
|
|
999
|
+
if (typeof key2 === "bigint")
|
|
1000
|
+
return mix(Number(BigInt.asIntN(32, key2 ^ key2 >> 32n)));
|
|
1001
|
+
const text = String(key2);
|
|
1002
|
+
let hash = 2166136261;
|
|
1003
|
+
for (let i = 0; i < text.length; i++)
|
|
1004
|
+
hash = Math.imul(hash ^ text.charCodeAt(i), 16777619);
|
|
1005
|
+
return hash;
|
|
1006
|
+
}
|
|
1007
|
+
function mix(value) {
|
|
1008
|
+
value = Math.imul(value ^ value >>> 16, 73244475);
|
|
1009
|
+
value = Math.imul(value ^ value >>> 16, 73244475);
|
|
1010
|
+
return value ^ value >>> 16;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1
1013
|
// ../../core/dist/timeline.js
|
|
2
1014
|
var BRANCH_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
3
1015
|
var Timeline = class {
|
|
@@ -921,11 +1933,11 @@ function tsHeadline(config, document, query, options) {
|
|
|
921
1933
|
for (const part of options.split(",")) {
|
|
922
1934
|
const eq = part.indexOf("=");
|
|
923
1935
|
if (eq === -1) continue;
|
|
924
|
-
const
|
|
1936
|
+
const key2 = part.slice(0, eq).trim().toLowerCase();
|
|
925
1937
|
let val = part.slice(eq + 1).trim();
|
|
926
1938
|
if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
|
|
927
|
-
if (
|
|
928
|
-
else if (
|
|
1939
|
+
if (key2 === "startsel") startSel = val;
|
|
1940
|
+
else if (key2 === "stopsel") stopSel = val;
|
|
929
1941
|
}
|
|
930
1942
|
}
|
|
931
1943
|
const node = parseTsqueryText(query);
|
|
@@ -1028,8 +2040,8 @@ function weightIndex(w) {
|
|
|
1028
2040
|
function collectQueryOperands(node, out) {
|
|
1029
2041
|
switch (node.kind) {
|
|
1030
2042
|
case "lexeme": {
|
|
1031
|
-
const
|
|
1032
|
-
if (!out.has(
|
|
2043
|
+
const key2 = `${node.word}\0${node.prefix ? "*" : ""}`;
|
|
2044
|
+
if (!out.has(key2)) out.set(key2, node);
|
|
1033
2045
|
break;
|
|
1034
2046
|
}
|
|
1035
2047
|
case "not":
|
|
@@ -2256,13 +3268,13 @@ var JsonParser = class {
|
|
|
2256
3268
|
for (; ; ) {
|
|
2257
3269
|
this.skipWs();
|
|
2258
3270
|
if (this.text[this.pos] !== '"') this.fail("expected string key");
|
|
2259
|
-
const
|
|
3271
|
+
const key2 = this.parseString();
|
|
2260
3272
|
this.skipWs();
|
|
2261
3273
|
if (this.text[this.pos] !== ":") this.fail("expected :");
|
|
2262
3274
|
this.pos++;
|
|
2263
3275
|
this.skipWs();
|
|
2264
3276
|
const value = this.parseValue(depth);
|
|
2265
|
-
m.set(
|
|
3277
|
+
m.set(key2, value);
|
|
2266
3278
|
this.skipWs();
|
|
2267
3279
|
const c = this.text[this.pos];
|
|
2268
3280
|
if (c === ",") {
|
|
@@ -4790,10 +5802,81 @@ function parseArrayText(t, text, ctx) {
|
|
|
4790
5802
|
|
|
4791
5803
|
// src/storage/database-state.ts
|
|
4792
5804
|
var TableData = class _TableData {
|
|
5805
|
+
/** @internal Optional physical row journal; IDs remain stable across heap splices. */
|
|
5806
|
+
durableChanges = null;
|
|
5807
|
+
durableNextId = 0;
|
|
5808
|
+
durableEpoch;
|
|
5809
|
+
durableUnsafe = false;
|
|
5810
|
+
durableIds;
|
|
5811
|
+
enableDurabilityTracking(ids, nextId) {
|
|
5812
|
+
if (this.durableChanges && !this.durableUnsafe && !ids) return;
|
|
5813
|
+
this.materializeSlab();
|
|
5814
|
+
this.durableIds = /* @__PURE__ */ new WeakMap();
|
|
5815
|
+
this.durableChanges = new ForkMap();
|
|
5816
|
+
this.durableNextId = nextId ?? this.#rows.length;
|
|
5817
|
+
for (let i = 0; i < this.#rows.length; i++) this.durableIds.set(this.#rows[i], ids?.[i] ?? i);
|
|
5818
|
+
this.durableEpoch = {};
|
|
5819
|
+
this.durableUnsafe = false;
|
|
5820
|
+
}
|
|
5821
|
+
durableRowId(row) {
|
|
5822
|
+
const id = this.durableIds?.get(row);
|
|
5823
|
+
if (id === void 0) throw new Error("Untracked durable row");
|
|
5824
|
+
return id;
|
|
5825
|
+
}
|
|
5826
|
+
disableDurabilityTracking() {
|
|
5827
|
+
this.durableChanges = null;
|
|
5828
|
+
this.durableIds = void 0;
|
|
5829
|
+
this.durableEpoch = void 0;
|
|
5830
|
+
this.durableUnsafe = false;
|
|
5831
|
+
}
|
|
5832
|
+
recordDurableRow(row, previous) {
|
|
5833
|
+
if (!this.durableChanges) return;
|
|
5834
|
+
const id = (previous && this.durableIds?.get(previous)) ?? this.durableIds?.get(row) ?? this.durableNextId++;
|
|
5835
|
+
this.durableIds.set(row, id);
|
|
5836
|
+
this.durableChanges.set(id, { row, xmin: this.rowXmins.get(row) ?? 2 });
|
|
5837
|
+
}
|
|
5838
|
+
recordDurableDelete(row) {
|
|
5839
|
+
if (this.durableChanges) this.durableChanges.set(this.durableRowId(row), { row: null, xmin: 0 });
|
|
5840
|
+
}
|
|
5841
|
+
/** @internal Metadata twin with no visits/copies of existing rows or index entries. */
|
|
5842
|
+
emptyForDurability() {
|
|
5843
|
+
const copy = new _TableData(this.schema, this.name, this.columns, this.oid, this.temp);
|
|
5844
|
+
copy.constraints = this.constraints;
|
|
5845
|
+
copy.triggers = this.triggers;
|
|
5846
|
+
copy.unlogged = this.unlogged;
|
|
5847
|
+
return copy;
|
|
5848
|
+
}
|
|
5849
|
+
/** @internal Serialization/SQL scans must not count as public native-array exposure. */
|
|
5850
|
+
serializationRows() {
|
|
5851
|
+
this.materializeSlab();
|
|
5852
|
+
return this.materializedRows();
|
|
5853
|
+
}
|
|
5854
|
+
/** @internal Internal mutable array access, with ordinary public mutableRows kept intact. */
|
|
5855
|
+
writeRows() {
|
|
5856
|
+
return this.serializationRows();
|
|
5857
|
+
}
|
|
5858
|
+
/** @internal Ordinary snapshot branches must not inherit a persistence journal. */
|
|
5859
|
+
untrackedFork() {
|
|
5860
|
+
const copy = this.emptyForDurability();
|
|
5861
|
+
copy.rowXmins = this.rowXmins;
|
|
5862
|
+
copy.slabXmins = this.slabXmins;
|
|
5863
|
+
if (this.slab) copy.slab = this.slab;
|
|
5864
|
+
else {
|
|
5865
|
+
copy.#rows = this.#rows;
|
|
5866
|
+
copy.#rowPatches = this.#rowPatches?.fork() ?? null;
|
|
5867
|
+
copy.#rowsShared = this.#rowsShared = true;
|
|
5868
|
+
copy.tuplesShared = this.tuplesShared = true;
|
|
5869
|
+
}
|
|
5870
|
+
copy.indexStores = this.indexStores ? new Map([...this.indexStores].map(([name, store]) => [name, store.clone()])) : null;
|
|
5871
|
+
return copy;
|
|
5872
|
+
}
|
|
4793
5873
|
name;
|
|
4794
5874
|
schema;
|
|
4795
5875
|
columns;
|
|
4796
|
-
rows;
|
|
5876
|
+
#rows = [];
|
|
5877
|
+
#rowPatches = null;
|
|
5878
|
+
#rowsShared = false;
|
|
5879
|
+
#rowsExposed = false;
|
|
4797
5880
|
rowXmins = /* @__PURE__ */ new WeakMap();
|
|
4798
5881
|
slabXmins = null;
|
|
4799
5882
|
/** Frozen columnar storage after snapshot hydrate; `rows` stays empty until materialized. */
|
|
@@ -4809,22 +5892,54 @@ var TableData = class _TableData {
|
|
|
4809
5892
|
shareCount = 0;
|
|
4810
5893
|
/** Derived unique/btree index maps; null until built or invalidated. */
|
|
4811
5894
|
indexStores = null;
|
|
5895
|
+
/** DML replaces tuples; DDL must detach them before changing cells in place. */
|
|
5896
|
+
tuplesShared = false;
|
|
4812
5897
|
constructor(schema, name, columns, oid, temp = false) {
|
|
4813
5898
|
this.schema = schema;
|
|
4814
5899
|
this.name = name;
|
|
4815
5900
|
this.columns = columns;
|
|
4816
|
-
this
|
|
5901
|
+
Object.defineProperty(this, "rows", {
|
|
5902
|
+
enumerable: true,
|
|
5903
|
+
configurable: true,
|
|
5904
|
+
get: this.nativeRows,
|
|
5905
|
+
set: this.setNativeRows
|
|
5906
|
+
});
|
|
4817
5907
|
this.constraints = [];
|
|
4818
5908
|
this.triggers = [];
|
|
4819
5909
|
this.temp = temp;
|
|
4820
5910
|
this.oid = oid;
|
|
4821
5911
|
}
|
|
4822
5912
|
rowCount() {
|
|
4823
|
-
return this.slab ? this.slab.rowCount : this
|
|
5913
|
+
return this.slab ? this.slab.rowCount : this.#rows.length;
|
|
4824
5914
|
}
|
|
4825
5915
|
rowAt(index) {
|
|
4826
5916
|
if (this.slab) return this.slab.rowAt(index);
|
|
4827
|
-
return this
|
|
5917
|
+
return this.#rowPatches?.get(index) ?? this.#rows[index] ?? [];
|
|
5918
|
+
}
|
|
5919
|
+
/** Replace an existing tuple without copying the array of unchanged row references. */
|
|
5920
|
+
replaceRow(index, row) {
|
|
5921
|
+
if (this.durableChanges) this.recordDurableRow(row, this.rowAt(index));
|
|
5922
|
+
if (this.#rowPatches) this.#rowPatches.set(index, row);
|
|
5923
|
+
else this.#rows[index] = row;
|
|
5924
|
+
}
|
|
5925
|
+
materializedRows() {
|
|
5926
|
+
if (this.#rowsShared) this.#rows = this.#rows.slice();
|
|
5927
|
+
if (this.#rowPatches) for (const [index, row] of this.#rowPatches) this.#rows[index] = row;
|
|
5928
|
+
this.#rowPatches = null;
|
|
5929
|
+
this.#rowsShared = false;
|
|
5930
|
+
return this.#rows;
|
|
5931
|
+
}
|
|
5932
|
+
nativeRows() {
|
|
5933
|
+
if (this.durableChanges) this.durableUnsafe = true;
|
|
5934
|
+
this.#rowsExposed = true;
|
|
5935
|
+
return this.materializedRows();
|
|
5936
|
+
}
|
|
5937
|
+
setNativeRows(rows) {
|
|
5938
|
+
if (this.durableChanges) this.durableUnsafe = true;
|
|
5939
|
+
this.#rows = rows;
|
|
5940
|
+
this.#rowPatches = null;
|
|
5941
|
+
this.#rowsShared = false;
|
|
5942
|
+
this.#rowsExposed = true;
|
|
4828
5943
|
}
|
|
4829
5944
|
/** All rows for scans; materializes slab once into `rows` when needed. */
|
|
4830
5945
|
allRows() {
|
|
@@ -4833,13 +5948,15 @@ var TableData = class _TableData {
|
|
|
4833
5948
|
}
|
|
4834
5949
|
attachSlab(slab) {
|
|
4835
5950
|
this.slab = slab;
|
|
4836
|
-
this
|
|
5951
|
+
this.#rows = [];
|
|
5952
|
+
this.#rowPatches = null;
|
|
5953
|
+
this.#rowsShared = false;
|
|
4837
5954
|
}
|
|
4838
5955
|
materializeSlab() {
|
|
4839
5956
|
if (!this.slab) return;
|
|
4840
|
-
this
|
|
5957
|
+
this.#rows = this.slab.materialize();
|
|
4841
5958
|
if (this.slabXmins)
|
|
4842
|
-
this
|
|
5959
|
+
this.#rows.forEach((row, index) => {
|
|
4843
5960
|
this.rowXmins.set(row, this.slabXmins[index] ?? 2);
|
|
4844
5961
|
});
|
|
4845
5962
|
this.slabXmins = null;
|
|
@@ -4863,7 +5980,7 @@ var TableData = class _TableData {
|
|
|
4863
5980
|
columnIndex(name) {
|
|
4864
5981
|
return this.columns.findIndex((c) => c.name === name);
|
|
4865
5982
|
}
|
|
4866
|
-
|
|
5983
|
+
cloneMetadata() {
|
|
4867
5984
|
const t = new _TableData(
|
|
4868
5985
|
this.schema,
|
|
4869
5986
|
this.name,
|
|
@@ -4871,21 +5988,68 @@ var TableData = class _TableData {
|
|
|
4871
5988
|
this.oid,
|
|
4872
5989
|
this.temp
|
|
4873
5990
|
);
|
|
5991
|
+
t.unlogged = this.unlogged;
|
|
5992
|
+
t.durableChanges = this.durableChanges?.fork() ?? null;
|
|
5993
|
+
t.durableIds = this.durableIds;
|
|
5994
|
+
t.durableNextId = this.durableNextId;
|
|
5995
|
+
t.durableEpoch = this.durableEpoch;
|
|
5996
|
+
t.durableUnsafe = this.durableUnsafe;
|
|
5997
|
+
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
5998
|
+
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
5999
|
+
t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
|
|
6000
|
+
return t;
|
|
6001
|
+
}
|
|
6002
|
+
clone() {
|
|
6003
|
+
const t = this.cloneMetadata();
|
|
6004
|
+
if (t.durableChanges) t.durableUnsafe = true;
|
|
4874
6005
|
if (this.slab) {
|
|
4875
|
-
t
|
|
6006
|
+
t.#rows = this.slab.materialize().map((r) => r.slice());
|
|
4876
6007
|
} else {
|
|
4877
|
-
t
|
|
6008
|
+
t.#rows = this.materializedRows().map((r) => r.slice());
|
|
4878
6009
|
}
|
|
4879
|
-
t
|
|
4880
|
-
const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this
|
|
6010
|
+
t.#rows.forEach((row, index) => {
|
|
6011
|
+
const xmin = this.slabXmins?.[index] ?? this.rowXmins.get(this.#rows[index]);
|
|
4881
6012
|
if (xmin !== void 0) t.rowXmins.set(row, xmin);
|
|
4882
6013
|
});
|
|
4883
|
-
t.unlogged = this.unlogged;
|
|
4884
|
-
t.constraints = this.constraints.map((c) => ({ ...c }));
|
|
4885
|
-
t.triggers = this.triggers.map((tr) => ({ ...tr }));
|
|
4886
|
-
t.indexStores = this.indexStores ? new Map([...this.indexStores].map(([k, v]) => [k, v.clone()])) : null;
|
|
4887
6014
|
return t;
|
|
4888
6015
|
}
|
|
6016
|
+
/** DML creates new tuple arrays; retain existing tuples and xmin bindings until DDL. */
|
|
6017
|
+
cloneForDml() {
|
|
6018
|
+
if (this.triggers.length > 0) return this.cloneForWrite();
|
|
6019
|
+
const t = this.cloneMetadata();
|
|
6020
|
+
t.rowXmins = this.rowXmins;
|
|
6021
|
+
if (this.slab) {
|
|
6022
|
+
t.#rows = this.slab.materialize();
|
|
6023
|
+
if (this.slabXmins) {
|
|
6024
|
+
for (let index = 0; index < t.#rows.length; index++)
|
|
6025
|
+
t.rowXmins.set(t.#rows[index], this.slabXmins[index] ?? 2);
|
|
6026
|
+
}
|
|
6027
|
+
} else {
|
|
6028
|
+
if (this.#rows.length >= 4096 && !this.#rowsExposed) {
|
|
6029
|
+
this.#rowPatches ??= new ForkMap();
|
|
6030
|
+
t.#rowPatches = this.#rowPatches.fork();
|
|
6031
|
+
t.#rows = this.#rows;
|
|
6032
|
+
t.#rowsShared = this.#rowsShared = true;
|
|
6033
|
+
} else t.#rows = this.materializedRows().slice();
|
|
6034
|
+
this.tuplesShared = true;
|
|
6035
|
+
}
|
|
6036
|
+
t.tuplesShared = true;
|
|
6037
|
+
return t;
|
|
6038
|
+
}
|
|
6039
|
+
/** A live DML fork can be unfrozen while its unchanged tuples still belong to history. */
|
|
6040
|
+
ownTuples() {
|
|
6041
|
+
if (this.durableChanges) this.durableUnsafe = true;
|
|
6042
|
+
if (!this.tuplesShared) return;
|
|
6043
|
+
const xmins = /* @__PURE__ */ new WeakMap();
|
|
6044
|
+
this.rows = this.rows.map((row) => {
|
|
6045
|
+
const copy = row.slice();
|
|
6046
|
+
const xmin = this.rowXmins.get(row);
|
|
6047
|
+
if (xmin !== void 0) xmins.set(copy, xmin);
|
|
6048
|
+
return copy;
|
|
6049
|
+
});
|
|
6050
|
+
this.rowXmins = xmins;
|
|
6051
|
+
this.tuplesShared = false;
|
|
6052
|
+
}
|
|
4889
6053
|
/** Independent writable copy (row tuples copied so ALTER COLUMN is isolated). */
|
|
4890
6054
|
cloneForWrite() {
|
|
4891
6055
|
const t = this.clone();
|
|
@@ -5153,10 +6317,10 @@ var DatabaseState = class _DatabaseState {
|
|
|
5153
6317
|
}
|
|
5154
6318
|
return out;
|
|
5155
6319
|
}
|
|
5156
|
-
findEnumByKey(
|
|
5157
|
-
const dot =
|
|
5158
|
-
if (dot === -1) return this.findEnum([
|
|
5159
|
-
return this.schemas.get(
|
|
6320
|
+
findEnumByKey(key2) {
|
|
6321
|
+
const dot = key2.indexOf(".");
|
|
6322
|
+
if (dot === -1) return this.findEnum([key2]);
|
|
6323
|
+
return this.schemas.get(key2.slice(0, dot))?.enums.get(key2.slice(dot + 1)) ?? null;
|
|
5160
6324
|
}
|
|
5161
6325
|
/** format_type()-style display name for a type OID (builtin or enum). */
|
|
5162
6326
|
typeNameForOid(oid) {
|
|
@@ -5239,12 +6403,21 @@ var DatabaseState = class _DatabaseState {
|
|
|
5239
6403
|
}
|
|
5240
6404
|
}
|
|
5241
6405
|
ensureWritableTable(table) {
|
|
5242
|
-
if (!table.frozen)
|
|
6406
|
+
if (!table.frozen) {
|
|
6407
|
+
table.ownTuples();
|
|
6408
|
+
return table;
|
|
6409
|
+
}
|
|
5243
6410
|
const schema = this.schemas.get(table.schema);
|
|
5244
6411
|
const copy = table.cloneForWrite();
|
|
5245
6412
|
schema?.tables.set(table.name, copy);
|
|
5246
6413
|
return copy;
|
|
5247
6414
|
}
|
|
6415
|
+
ensureWritableTableForDml(table) {
|
|
6416
|
+
if (!table.frozen) return table;
|
|
6417
|
+
const copy = table.cloneForDml();
|
|
6418
|
+
this.schemas.get(table.schema)?.tables.set(table.name, copy);
|
|
6419
|
+
return copy;
|
|
6420
|
+
}
|
|
5248
6421
|
ensureWritableSequence(seq) {
|
|
5249
6422
|
if ((seq.shareCount ?? 0) === 0) return seq;
|
|
5250
6423
|
const schema = this.schemas.get(seq.schema);
|
|
@@ -5634,8 +6807,8 @@ function canImplicitCast(from, to) {
|
|
|
5634
6807
|
if (from === to) return true;
|
|
5635
6808
|
if (from === "unknown") return true;
|
|
5636
6809
|
if (isArrayType(from) && isArrayType(to)) return canImplicitCast(arrayElemType(from), arrayElemType(to));
|
|
5637
|
-
const
|
|
5638
|
-
return IMPLICIT_CASTS.has(
|
|
6810
|
+
const key2 = `${from}->${to}`;
|
|
6811
|
+
return IMPLICIT_CASTS.has(key2);
|
|
5639
6812
|
}
|
|
5640
6813
|
var IMPLICIT_CASTS = /* @__PURE__ */ new Set([
|
|
5641
6814
|
"int2->int4",
|
|
@@ -5946,8 +7119,8 @@ function numericFamilyCast(from, to, v) {
|
|
|
5946
7119
|
}
|
|
5947
7120
|
}
|
|
5948
7121
|
function datetimeCast(env, from, to, v) {
|
|
5949
|
-
const
|
|
5950
|
-
switch (
|
|
7122
|
+
const key2 = `${from}->${to}`;
|
|
7123
|
+
switch (key2) {
|
|
5951
7124
|
case "date->timestamp": {
|
|
5952
7125
|
const d = v;
|
|
5953
7126
|
if (d === DATE_POS_INF) return TS_POS_INF;
|
|
@@ -8354,7 +9527,7 @@ function evalPgcryptoFunction(ctx, fn, args) {
|
|
|
8354
9527
|
// src/functions/hash-fns.ts
|
|
8355
9528
|
var utf83 = new TextEncoder();
|
|
8356
9529
|
var rot = (x, k) => (x << k | x >>> 32 - k) >>> 0;
|
|
8357
|
-
function
|
|
9530
|
+
function mix2(s) {
|
|
8358
9531
|
s[0] = s[0] - s[2] >>> 0;
|
|
8359
9532
|
s[0] = (s[0] ^ rot(s[2], 4)) >>> 0;
|
|
8360
9533
|
s[2] = s[2] + s[1] >>> 0;
|
|
@@ -8398,7 +9571,7 @@ function hashAny(k, seed) {
|
|
|
8398
9571
|
const bits = BigInt.asUintN(64, seed);
|
|
8399
9572
|
s[0] = s[0] + Number(bits >> 32n) >>> 0;
|
|
8400
9573
|
s[1] = s[1] + Number(bits & 0xffffffffn) >>> 0;
|
|
8401
|
-
|
|
9574
|
+
mix2(s);
|
|
8402
9575
|
}
|
|
8403
9576
|
let at = 0;
|
|
8404
9577
|
let len = k.length;
|
|
@@ -8406,7 +9579,7 @@ function hashAny(k, seed) {
|
|
|
8406
9579
|
s[0] = s[0] + word(k, at) >>> 0;
|
|
8407
9580
|
s[1] = s[1] + word(k, at + 4) >>> 0;
|
|
8408
9581
|
s[2] = s[2] + word(k, at + 8) >>> 0;
|
|
8409
|
-
|
|
9582
|
+
mix2(s);
|
|
8410
9583
|
}
|
|
8411
9584
|
for (let i = len - 1; i >= 0; i--) {
|
|
8412
9585
|
const byte = k[at + i];
|
|
@@ -8596,7 +9769,7 @@ function getJsonFunctions() {
|
|
|
8596
9769
|
m.set("json_typeof", typeofFn);
|
|
8597
9770
|
m.set("jsonb_typeof", typeofFn);
|
|
8598
9771
|
for (const name of ["jsonb_path_query_first", "jsonb_path_exists", "jsonb_path_match", "jsonb_path_query_array"]) {
|
|
8599
|
-
for (const
|
|
9772
|
+
for (const key2 of [name, `${name}_tz`]) m.set(key2, jsonpathFunction(name));
|
|
8600
9773
|
}
|
|
8601
9774
|
m.set(
|
|
8602
9775
|
"json_array_length",
|
|
@@ -8620,11 +9793,11 @@ function getJsonFunctions() {
|
|
|
8620
9793
|
let current = jsonArg(ctx, args[0]);
|
|
8621
9794
|
for (const pathArg of args.slice(1)) {
|
|
8622
9795
|
if (current === null) break;
|
|
8623
|
-
const
|
|
9796
|
+
const key2 = argText(ctx, pathArg);
|
|
8624
9797
|
if (current.j === "obj") {
|
|
8625
|
-
current = current.v.get(
|
|
9798
|
+
current = current.v.get(key2) ?? null;
|
|
8626
9799
|
} else if (current.j === "arr") {
|
|
8627
|
-
const idx = Number.parseInt(
|
|
9800
|
+
const idx = Number.parseInt(key2, 10);
|
|
8628
9801
|
if (Number.isNaN(idx)) {
|
|
8629
9802
|
current = null;
|
|
8630
9803
|
} else {
|
|
@@ -8733,8 +9906,8 @@ function buildObject(ctx, args) {
|
|
|
8733
9906
|
if (k.v === null) {
|
|
8734
9907
|
throw pgError("invalid_parameter_value", "argument 1: key must not be null", "22004");
|
|
8735
9908
|
}
|
|
8736
|
-
const
|
|
8737
|
-
entries.push([
|
|
9909
|
+
const key2 = castTo(ctx, k, "text", { explicit: true }).v;
|
|
9910
|
+
entries.push([key2, datumToJsonb(ctx, args[i + 1].t, args[i + 1].v)]);
|
|
8738
9911
|
}
|
|
8739
9912
|
return jsonbObj(entries);
|
|
8740
9913
|
}
|
|
@@ -9430,14 +10603,14 @@ function nameTaken(schema, name, self) {
|
|
|
9430
10603
|
return other !== void 0 && other !== self;
|
|
9431
10604
|
}
|
|
9432
10605
|
function syncIndexCatalog(state) {
|
|
9433
|
-
const
|
|
10606
|
+
const tables2 = /* @__PURE__ */ new Map();
|
|
9434
10607
|
for (const schema of state.schemas.values()) {
|
|
9435
|
-
for (const table of schema.tables.values())
|
|
10608
|
+
for (const table of schema.tables.values()) tables2.set(table.oid, { schema, table });
|
|
9436
10609
|
for (const view of schema.views.values())
|
|
9437
|
-
if (view.materialized)
|
|
10610
|
+
if (view.materialized) tables2.set(view.oid, { schema, table: materializedTable(view) });
|
|
9438
10611
|
}
|
|
9439
10612
|
const backing = /* @__PURE__ */ new Map();
|
|
9440
|
-
for (const { table } of
|
|
10613
|
+
for (const { table } of tables2.values()) {
|
|
9441
10614
|
for (const constraint of table.constraints) {
|
|
9442
10615
|
if (isKeyConstraint(constraint) && constraint.indexOid !== void 0) {
|
|
9443
10616
|
backing.set(constraint.indexOid, { table, constraint });
|
|
@@ -9446,20 +10619,20 @@ function syncIndexCatalog(state) {
|
|
|
9446
10619
|
}
|
|
9447
10620
|
const linked = /* @__PURE__ */ new Set();
|
|
9448
10621
|
for (const schema of state.schemas.values()) {
|
|
9449
|
-
for (const [
|
|
9450
|
-
const home = index.tableOid !== void 0 ?
|
|
10622
|
+
for (const [key2, index] of [...schema.indexes]) {
|
|
10623
|
+
const home = index.tableOid !== void 0 ? tables2.get(index.tableOid) : schema.tables.has(index.table) ? { schema, table: schema.tables.get(index.table) } : void 0;
|
|
9451
10624
|
const owner = index.isConstraint && index.oid !== void 0 ? backing.get(index.oid) : void 0;
|
|
9452
10625
|
if (!home || index.isConstraint && (!owner || owner.table !== home.table)) {
|
|
9453
|
-
schema.indexes.delete(
|
|
10626
|
+
schema.indexes.delete(key2);
|
|
9454
10627
|
continue;
|
|
9455
10628
|
}
|
|
9456
10629
|
const name = owner ? owner.constraint.name : index.name;
|
|
9457
|
-
const stale = index.oid === void 0 || index.tableOid !== home.table.oid || index.schema !== home.schema.name || index.table !== home.table.name || index.name !== name ||
|
|
10630
|
+
const stale = index.oid === void 0 || index.tableOid !== home.table.oid || index.schema !== home.schema.name || index.table !== home.table.name || index.name !== name || key2 !== name || owner !== void 0 && !sameKeyColumns(index, owner.constraint);
|
|
9458
10631
|
if (!stale) {
|
|
9459
10632
|
if (owner) linked.add(index.oid);
|
|
9460
10633
|
continue;
|
|
9461
10634
|
}
|
|
9462
|
-
schema.indexes.delete(
|
|
10635
|
+
schema.indexes.delete(key2);
|
|
9463
10636
|
if (nameTaken(home.schema, name)) continue;
|
|
9464
10637
|
const next = {
|
|
9465
10638
|
...index,
|
|
@@ -9477,7 +10650,7 @@ function syncIndexCatalog(state) {
|
|
|
9477
10650
|
home.schema.indexes.set(name, next);
|
|
9478
10651
|
}
|
|
9479
10652
|
}
|
|
9480
|
-
for (const { schema, table } of [...
|
|
10653
|
+
for (const { schema, table } of [...tables2.values()]) {
|
|
9481
10654
|
if (!table.constraints.some((c) => isKeyConstraint(c) && (c.indexOid === void 0 || !linked.has(c.indexOid)))) {
|
|
9482
10655
|
continue;
|
|
9483
10656
|
}
|
|
@@ -10264,8 +11437,8 @@ function splitCollation(column) {
|
|
|
10264
11437
|
return { column: null, expr: inner, collation };
|
|
10265
11438
|
}
|
|
10266
11439
|
function keyText(scope, column, pretty) {
|
|
10267
|
-
const
|
|
10268
|
-
return
|
|
11440
|
+
const key2 = splitCollation(column);
|
|
11441
|
+
return key2.column !== null ? quoteIdentifier(key2.column) : deparseIndexExpr(scope, key2.expr, pretty);
|
|
10269
11442
|
}
|
|
10270
11443
|
function defaultOpclass(type) {
|
|
10271
11444
|
if (type === null) return null;
|
|
@@ -10274,15 +11447,15 @@ function defaultOpclass(type) {
|
|
|
10274
11447
|
return type === "varchar" ? "text_ops" : `${type}_ops`;
|
|
10275
11448
|
}
|
|
10276
11449
|
function columnType(table, column) {
|
|
10277
|
-
const
|
|
10278
|
-
return
|
|
11450
|
+
const key2 = splitCollation(column);
|
|
11451
|
+
return key2.column === null ? null : table?.columns.find((c) => c.name === key2.column)?.type.id ?? null;
|
|
10279
11452
|
}
|
|
10280
11453
|
function keyDefinition(scope, index, column, pretty) {
|
|
10281
11454
|
let out = keyText(scope, column, pretty);
|
|
10282
|
-
const
|
|
10283
|
-
const declared =
|
|
10284
|
-
if (
|
|
10285
|
-
out += ` COLLATE ${quoteIdentifier(
|
|
11455
|
+
const key2 = splitCollation(column);
|
|
11456
|
+
const declared = key2.column === null ? null : scope.table?.columns.find((c) => c.name === key2.column)?.collate;
|
|
11457
|
+
if (key2.collation !== null && key2.collation !== (declared ?? null)) {
|
|
11458
|
+
out += ` COLLATE ${quoteIdentifier(key2.collation)}`;
|
|
10286
11459
|
}
|
|
10287
11460
|
const opclass = column.opclass;
|
|
10288
11461
|
if (opclass && opclass[opclass.length - 1] !== defaultOpclass(columnType(scope.table, column))) {
|
|
@@ -10309,7 +11482,7 @@ function deparseIndexPredicate(ctx, index, pretty = false) {
|
|
|
10309
11482
|
}
|
|
10310
11483
|
function deparseIndexExpressions(ctx, index) {
|
|
10311
11484
|
const scope = scopeOf(ctx, index);
|
|
10312
|
-
const expressions = index.columns.map(splitCollation).filter((
|
|
11485
|
+
const expressions = index.columns.map(splitCollation).filter((key2) => key2.expr !== null).map((key2) => deparseRuleExpr(scope, key2.expr));
|
|
10313
11486
|
return expressions.length > 0 ? expressions.join(", ") : null;
|
|
10314
11487
|
}
|
|
10315
11488
|
function deparseIndex(ctx, index, pretty = false) {
|
|
@@ -10324,8 +11497,8 @@ function deparseIndex(ctx, index, pretty = false) {
|
|
|
10324
11497
|
return predicate === null ? out : `${out} WHERE ${predicate}`;
|
|
10325
11498
|
}
|
|
10326
11499
|
function deparseIndexColumn(ctx, index, position, pretty = false) {
|
|
10327
|
-
const
|
|
10328
|
-
if (
|
|
11500
|
+
const key2 = index.columns[position - 1];
|
|
11501
|
+
if (key2) return keyText(scopeOf(ctx, index), key2, pretty);
|
|
10329
11502
|
const included = index.include?.[position - 1 - index.columns.length];
|
|
10330
11503
|
return included === void 0 ? "" : quoteIdentifier(included);
|
|
10331
11504
|
}
|
|
@@ -12380,9 +13553,9 @@ function tryDatetimeArith(ctx, op, l, r) {
|
|
|
12380
13553
|
const rt = r.t;
|
|
12381
13554
|
const isDT = (t) => ["date", "time", "timestamp", "timestamptz", "interval"].includes(t);
|
|
12382
13555
|
if (!isDT(lt) && !isDT(rt)) return null;
|
|
12383
|
-
const
|
|
13556
|
+
const key2 = `${lt}${op}${rt}`;
|
|
12384
13557
|
const nullOf = (t) => tv(t, null);
|
|
12385
|
-
switch (
|
|
13558
|
+
switch (key2) {
|
|
12386
13559
|
case "date+int4":
|
|
12387
13560
|
case "date+int2": {
|
|
12388
13561
|
if (l.v === null || r.v === null) return nullOf("date");
|
|
@@ -12849,11 +14022,11 @@ function evalJsonExtract(_ctx, op, l, r) {
|
|
|
12849
14022
|
result = null;
|
|
12850
14023
|
break;
|
|
12851
14024
|
}
|
|
12852
|
-
const
|
|
12853
|
-
if (result.j === "arr" && /^-?\d+$/.test(
|
|
12854
|
-
result = jsonGetIndex(result, Number(
|
|
14025
|
+
const key2 = String(p);
|
|
14026
|
+
if (result.j === "arr" && /^-?\d+$/.test(key2)) {
|
|
14027
|
+
result = jsonGetIndex(result, Number(key2));
|
|
12855
14028
|
} else {
|
|
12856
|
-
result = jsonGetKey(result,
|
|
14029
|
+
result = jsonGetKey(result, key2);
|
|
12857
14030
|
}
|
|
12858
14031
|
}
|
|
12859
14032
|
}
|
|
@@ -12871,18 +14044,18 @@ function jsonGetIndex(v, idx) {
|
|
|
12871
14044
|
const i = idx < 0 ? v.v.length + idx : idx;
|
|
12872
14045
|
return v.v[i] ?? null;
|
|
12873
14046
|
}
|
|
12874
|
-
function jsonGetKey(v,
|
|
14047
|
+
function jsonGetKey(v, key2) {
|
|
12875
14048
|
if (v.j !== "obj") return null;
|
|
12876
|
-
return v.v.get(
|
|
14049
|
+
return v.v.get(key2) ?? null;
|
|
12877
14050
|
}
|
|
12878
14051
|
function evalJsonbExists(_ctx, op, l, r) {
|
|
12879
14052
|
if (l.t !== "jsonb") opNotExist(op, l.t, r.t);
|
|
12880
14053
|
if (l.v === null || r.v === null) return tv("bool", null);
|
|
12881
14054
|
const value = l.v.value;
|
|
12882
|
-
const has = (
|
|
12883
|
-
if (value.j === "obj") return value.v.has(
|
|
12884
|
-
if (value.j === "arr") return value.v.some((x) => x.j === "str" && x.v ===
|
|
12885
|
-
if (value.j === "str") return value.v ===
|
|
14055
|
+
const has = (key2) => {
|
|
14056
|
+
if (value.j === "obj") return value.v.has(key2);
|
|
14057
|
+
if (value.j === "arr") return value.v.some((x) => x.j === "str" && x.v === key2);
|
|
14058
|
+
if (value.j === "str") return value.v === key2;
|
|
12886
14059
|
return false;
|
|
12887
14060
|
};
|
|
12888
14061
|
if (op === "?") {
|
|
@@ -12896,14 +14069,14 @@ function evalJsonbDelete(_ctx, l, r) {
|
|
|
12896
14069
|
if (l.v === null || r.v === null) return tv("jsonb", null);
|
|
12897
14070
|
const value = l.v.value;
|
|
12898
14071
|
if (isTextType(r.t) || r.t === "unknown") {
|
|
12899
|
-
const
|
|
14072
|
+
const key2 = r.v;
|
|
12900
14073
|
if (value.j === "obj") {
|
|
12901
14074
|
const m = new Map(value.v);
|
|
12902
|
-
m.delete(
|
|
14075
|
+
m.delete(key2);
|
|
12903
14076
|
return tv("jsonb", wrapJsonb({ j: "obj", v: m }));
|
|
12904
14077
|
}
|
|
12905
14078
|
if (value.j === "arr") {
|
|
12906
|
-
return tv("jsonb", wrapJsonb(jsonbArr(value.v.filter((x) => !(x.j === "str" && x.v ===
|
|
14079
|
+
return tv("jsonb", wrapJsonb(jsonbArr(value.v.filter((x) => !(x.j === "str" && x.v === key2)))));
|
|
12907
14080
|
}
|
|
12908
14081
|
throw pgError("invalid_parameter_value", "cannot delete from scalar", "22023");
|
|
12909
14082
|
}
|
|
@@ -12940,22 +14113,22 @@ function evalJsonbDeletePath(ctx, l, r0) {
|
|
|
12940
14113
|
throw pgError("invalid_parameter_value", "path element cannot be null", "22004");
|
|
12941
14114
|
}
|
|
12942
14115
|
const del = (v, depth) => {
|
|
12943
|
-
const
|
|
14116
|
+
const key2 = path[depth];
|
|
12944
14117
|
if (v.j === "obj") {
|
|
12945
|
-
if (!v.v.has(
|
|
14118
|
+
if (!v.v.has(key2)) return v;
|
|
12946
14119
|
const m = new Map(v.v);
|
|
12947
14120
|
if (depth === path.length - 1) {
|
|
12948
|
-
m.delete(
|
|
14121
|
+
m.delete(key2);
|
|
12949
14122
|
} else {
|
|
12950
|
-
m.set(
|
|
14123
|
+
m.set(key2, del(m.get(key2), depth + 1));
|
|
12951
14124
|
}
|
|
12952
14125
|
return { j: "obj", v: m };
|
|
12953
14126
|
}
|
|
12954
14127
|
if (v.j === "arr") {
|
|
12955
|
-
if (!/^-?\d+$/.test(
|
|
14128
|
+
if (!/^-?\d+$/.test(key2)) {
|
|
12956
14129
|
return v;
|
|
12957
14130
|
}
|
|
12958
|
-
let idx = Number(
|
|
14131
|
+
let idx = Number(key2);
|
|
12959
14132
|
if (idx < 0) idx = v.v.length + idx;
|
|
12960
14133
|
if (idx < 0 || idx >= v.v.length) return v;
|
|
12961
14134
|
if (depth === path.length - 1) {
|
|
@@ -13728,8 +14901,8 @@ function evalJsonbSubscript(ctx, scope, base, e) {
|
|
|
13728
14901
|
const k = evalExpr(ctx, scope, sub.lower ?? sub.upper);
|
|
13729
14902
|
if (k.v === null) return tv("jsonb", null);
|
|
13730
14903
|
if (current.j === "obj") {
|
|
13731
|
-
const
|
|
13732
|
-
current = current.v.get(
|
|
14904
|
+
const key2 = String(castTo(ctx, k, "text", { explicit: true }).v);
|
|
14905
|
+
current = current.v.get(key2) ?? null;
|
|
13733
14906
|
} else if (current.j === "arr") {
|
|
13734
14907
|
const cast = castTo(ctx, k, "int4", { explicit: true });
|
|
13735
14908
|
let i = cast.v;
|
|
@@ -14182,8 +15355,8 @@ var JsonObjectAggAcc = class {
|
|
|
14182
15355
|
if (k.v === null) {
|
|
14183
15356
|
throw pgError("null_value_not_allowed", "field name must not be null", "22004");
|
|
14184
15357
|
}
|
|
14185
|
-
const
|
|
14186
|
-
this.entries.push([
|
|
15358
|
+
const key2 = castTo(this.ctx, k, "text", { explicit: true }).v;
|
|
15359
|
+
this.entries.push([key2, v.v === null ? JSONB_NULL : datumToJsonb(this.ctx, v.t, v.v)]);
|
|
14187
15360
|
}
|
|
14188
15361
|
result() {
|
|
14189
15362
|
if (!this.any) return tv(this.jsonb ? "jsonb" : "json", null);
|
|
@@ -14978,6 +16151,21 @@ var IndexStore = class _IndexStore {
|
|
|
14978
16151
|
name;
|
|
14979
16152
|
unique;
|
|
14980
16153
|
entries = /* @__PURE__ */ new Map();
|
|
16154
|
+
integerKeys = null;
|
|
16155
|
+
shared = false;
|
|
16156
|
+
ownedBuckets = /* @__PURE__ */ new WeakSet();
|
|
16157
|
+
writableBucket(key2, bucket) {
|
|
16158
|
+
if (this.ownedBuckets.has(bucket)) return bucket;
|
|
16159
|
+
const copy = bucket.slice();
|
|
16160
|
+
this.entries.set(key2, copy);
|
|
16161
|
+
this.ownedBuckets.add(copy);
|
|
16162
|
+
return copy;
|
|
16163
|
+
}
|
|
16164
|
+
detach() {
|
|
16165
|
+
if (!this.shared) return;
|
|
16166
|
+
if (!(this.entries instanceof ForkMap)) this.entries = new Map(this.entries);
|
|
16167
|
+
this.shared = false;
|
|
16168
|
+
}
|
|
14981
16169
|
constructor(name, unique = false) {
|
|
14982
16170
|
this.name = name;
|
|
14983
16171
|
this.unique = unique;
|
|
@@ -14985,8 +16173,8 @@ var IndexStore = class _IndexStore {
|
|
|
14985
16173
|
get size() {
|
|
14986
16174
|
return this.entries.size;
|
|
14987
16175
|
}
|
|
14988
|
-
checkUnique(
|
|
14989
|
-
const existing = this.entries.get(
|
|
16176
|
+
checkUnique(key2, rowIdx) {
|
|
16177
|
+
const existing = this.entries.get(key2);
|
|
14990
16178
|
if (!existing) return;
|
|
14991
16179
|
for (const i of existing) {
|
|
14992
16180
|
if (rowIdx === void 0 || i !== rowIdx) {
|
|
@@ -14994,46 +16182,88 @@ var IndexStore = class _IndexStore {
|
|
|
14994
16182
|
}
|
|
14995
16183
|
}
|
|
14996
16184
|
}
|
|
14997
|
-
insert(
|
|
14998
|
-
if (this.unique) this.checkUnique(
|
|
14999
|
-
|
|
16185
|
+
insert(key2, rowIdx) {
|
|
16186
|
+
if (this.unique) this.checkUnique(key2, rowIdx);
|
|
16187
|
+
this.detach();
|
|
16188
|
+
this.integerKeys = null;
|
|
16189
|
+
const existing = this.entries.get(key2);
|
|
15000
16190
|
if (!existing) {
|
|
15001
|
-
|
|
16191
|
+
const bucket = [rowIdx];
|
|
16192
|
+
this.ownedBuckets.add(bucket);
|
|
16193
|
+
this.entries.set(key2, bucket);
|
|
15002
16194
|
return;
|
|
15003
16195
|
}
|
|
15004
16196
|
if (existing.includes(rowIdx)) return;
|
|
15005
|
-
existing.push(rowIdx);
|
|
16197
|
+
this.writableBucket(key2, existing).push(rowIdx);
|
|
15006
16198
|
}
|
|
15007
|
-
remove(
|
|
15008
|
-
const existing = this.entries.get(
|
|
16199
|
+
remove(key2, rowIdx) {
|
|
16200
|
+
const existing = this.entries.get(key2);
|
|
15009
16201
|
if (!existing) return;
|
|
15010
16202
|
const at = existing.indexOf(rowIdx);
|
|
15011
16203
|
if (at < 0) return;
|
|
15012
|
-
|
|
15013
|
-
|
|
16204
|
+
this.detach();
|
|
16205
|
+
this.integerKeys = null;
|
|
16206
|
+
if (existing.length === 1) this.entries.delete(key2);
|
|
16207
|
+
else {
|
|
16208
|
+
const remaining = this.writableBucket(key2, existing);
|
|
16209
|
+
remaining.splice(at, 1);
|
|
16210
|
+
this.entries.set(key2, remaining);
|
|
16211
|
+
}
|
|
16212
|
+
}
|
|
16213
|
+
lookup(key2) {
|
|
16214
|
+
return this.entries.get(key2) ?? [];
|
|
15014
16215
|
}
|
|
15015
|
-
|
|
15016
|
-
|
|
16216
|
+
/** Ordered access for a single int2/int4 key (the planner validates the key type). */
|
|
16217
|
+
integerRange(lower, upper, includeLower, includeUpper) {
|
|
16218
|
+
const keys = this.integerKeys ??= [...this.entries].map(([key2, rows]) => ({ value: Number(key2.slice(1)), rows })).sort((a, b) => a.value - b.value);
|
|
16219
|
+
let lo = 0;
|
|
16220
|
+
let hi = keys.length;
|
|
16221
|
+
while (lo < hi) {
|
|
16222
|
+
const mid = lo + hi >>> 1;
|
|
16223
|
+
const value = keys[mid].value;
|
|
16224
|
+
if (value < lower || !includeLower && value === lower) lo = mid + 1;
|
|
16225
|
+
else hi = mid;
|
|
16226
|
+
}
|
|
16227
|
+
const result = [];
|
|
16228
|
+
for (let i = lo; i < keys.length; i++) {
|
|
16229
|
+
const key2 = keys[i];
|
|
16230
|
+
if (key2.value > upper || !includeUpper && key2.value === upper) break;
|
|
16231
|
+
result.push(...key2.rows);
|
|
16232
|
+
}
|
|
16233
|
+
return result.sort((a, b) => a - b);
|
|
15017
16234
|
}
|
|
15018
16235
|
clear() {
|
|
15019
|
-
this.entries
|
|
16236
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
16237
|
+
this.shared = false;
|
|
16238
|
+
this.integerKeys = null;
|
|
15020
16239
|
}
|
|
15021
16240
|
clone() {
|
|
15022
16241
|
const copy = new _IndexStore(this.name, this.unique);
|
|
15023
|
-
|
|
16242
|
+
if (this.entries.size >= 4096 && !(this.entries instanceof ForkMap)) this.entries = new ForkMap(this.entries);
|
|
16243
|
+
copy.entries = this.entries instanceof ForkMap ? this.entries.fork() : this.entries;
|
|
16244
|
+
copy.integerKeys = this.integerKeys;
|
|
16245
|
+
copy.shared = true;
|
|
16246
|
+
this.shared = true;
|
|
16247
|
+
this.ownedBuckets = /* @__PURE__ */ new WeakSet();
|
|
15024
16248
|
return copy;
|
|
15025
16249
|
}
|
|
15026
16250
|
};
|
|
15027
16251
|
|
|
15028
16252
|
// src/indexes/maintain.ts
|
|
16253
|
+
function prepareDmlIndexStores(env, table) {
|
|
16254
|
+
if (table.indexStores || !table.frozen || table.triggers.length > 0) return;
|
|
16255
|
+
const specs = uniqueSpecsFor(env, table);
|
|
16256
|
+
if (specs.some((spec) => spec.where || spec.keys.some((key2) => key2.expr))) return;
|
|
16257
|
+
ensureIndexStores(env, table);
|
|
16258
|
+
}
|
|
15029
16259
|
function ensureIndexStores(env, table) {
|
|
15030
16260
|
if (table.indexStores) return table.indexStores;
|
|
15031
16261
|
const stores = /* @__PURE__ */ new Map();
|
|
15032
16262
|
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15033
16263
|
const store = new IndexStore(spec.name, true);
|
|
15034
16264
|
for (let i = 0; i < table.rowCount(); i++) {
|
|
15035
|
-
const
|
|
15036
|
-
if (
|
|
16265
|
+
const key2 = uniqueKeyOf(env, table, spec, table.rowAt(i));
|
|
16266
|
+
if (key2 !== null) store.insert(key2, i);
|
|
15037
16267
|
}
|
|
15038
16268
|
stores.set(spec.name, store);
|
|
15039
16269
|
}
|
|
@@ -15053,8 +16283,8 @@ function indexStoreFor(env, table, spec) {
|
|
|
15053
16283
|
if (!store) {
|
|
15054
16284
|
store = new IndexStore(spec.name, true);
|
|
15055
16285
|
for (let i = 0; i < table.rowCount(); i++) {
|
|
15056
|
-
const
|
|
15057
|
-
if (
|
|
16286
|
+
const key2 = uniqueKeyOf(env, table, spec, table.rowAt(i));
|
|
16287
|
+
if (key2 !== null) store.insert(key2, i);
|
|
15058
16288
|
}
|
|
15059
16289
|
stores.set(spec.name, store);
|
|
15060
16290
|
}
|
|
@@ -15062,22 +16292,33 @@ function indexStoreFor(env, table, spec) {
|
|
|
15062
16292
|
}
|
|
15063
16293
|
function indexInsertRow(env, table, rowIdx, row) {
|
|
15064
16294
|
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15065
|
-
const
|
|
15066
|
-
if (
|
|
15067
|
-
indexStoreFor(env, table, spec).insert(
|
|
16295
|
+
const key2 = uniqueKeyOf(env, table, spec, row);
|
|
16296
|
+
if (key2 === null) continue;
|
|
16297
|
+
indexStoreFor(env, table, spec).insert(key2, rowIdx);
|
|
15068
16298
|
}
|
|
15069
16299
|
}
|
|
15070
16300
|
function indexRemoveRow(env, table, rowIdx, row) {
|
|
15071
16301
|
if (!table.indexStores) return;
|
|
15072
16302
|
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15073
|
-
const
|
|
15074
|
-
if (
|
|
15075
|
-
table.indexStores.get(spec.name)?.remove(
|
|
16303
|
+
const key2 = uniqueKeyOf(env, table, spec, row);
|
|
16304
|
+
if (key2 === null) continue;
|
|
16305
|
+
table.indexStores.get(spec.name)?.remove(key2, rowIdx);
|
|
15076
16306
|
}
|
|
15077
16307
|
}
|
|
15078
16308
|
function indexUpdateRow(env, table, rowIdx, oldRow, newRow) {
|
|
15079
|
-
|
|
15080
|
-
|
|
16309
|
+
const specs = uniqueSpecsFor(env, table);
|
|
16310
|
+
if (specs.some((spec) => spec.where || spec.keys.some((key2) => key2.expr))) {
|
|
16311
|
+
indexRemoveRow(env, table, rowIdx, oldRow);
|
|
16312
|
+
indexInsertRow(env, table, rowIdx, newRow);
|
|
16313
|
+
return;
|
|
16314
|
+
}
|
|
16315
|
+
for (const spec of specs) {
|
|
16316
|
+
const before = uniqueKeyOf(env, table, spec, oldRow);
|
|
16317
|
+
const after = uniqueKeyOf(env, table, spec, newRow);
|
|
16318
|
+
if (before === after) continue;
|
|
16319
|
+
if (before !== null) table.indexStores?.get(spec.name)?.remove(before, rowIdx);
|
|
16320
|
+
if (after !== null) indexStoreFor(env, table, spec).insert(after, rowIdx);
|
|
16321
|
+
}
|
|
15081
16322
|
}
|
|
15082
16323
|
function rebuildTableIndexes(env, table) {
|
|
15083
16324
|
table.indexStores = null;
|
|
@@ -15196,15 +16437,15 @@ function uniqueKeyOf(env, table, spec, row) {
|
|
|
15196
16437
|
}
|
|
15197
16438
|
function checkUnique(env, table, row, selfIdx) {
|
|
15198
16439
|
for (const spec of uniqueSpecsFor(env, table)) {
|
|
15199
|
-
const
|
|
15200
|
-
if (
|
|
15201
|
-
indexStoreFor(env, table, spec).checkUnique(
|
|
16440
|
+
const key2 = uniqueKeyOf(env, table, spec, row);
|
|
16441
|
+
if (key2 === null) continue;
|
|
16442
|
+
indexStoreFor(env, table, spec).checkUnique(key2, selfIdx);
|
|
15202
16443
|
}
|
|
15203
16444
|
}
|
|
15204
16445
|
function findConflict(env, table, spec, row) {
|
|
15205
|
-
const
|
|
15206
|
-
if (
|
|
15207
|
-
const hits = indexStoreFor(env, table, spec).lookup(
|
|
16446
|
+
const key2 = uniqueKeyOf(env, table, spec, row);
|
|
16447
|
+
if (key2 === null) return null;
|
|
16448
|
+
const hits = indexStoreFor(env, table, spec).lookup(key2);
|
|
15208
16449
|
return hits.length > 0 ? hits[0] : null;
|
|
15209
16450
|
}
|
|
15210
16451
|
function checkForeignKeys(env, table, row) {
|
|
@@ -15238,11 +16479,11 @@ function checkForeignKeys(env, table, row) {
|
|
|
15238
16479
|
let found = false;
|
|
15239
16480
|
for (let i = 0; i < refTable.rowCount(); i++) {
|
|
15240
16481
|
const r = refTable.rowAt(i);
|
|
15241
|
-
const
|
|
16482
|
+
const key2 = refIdxs.map((ri, j) => {
|
|
15242
16483
|
const v = r[ri] ?? null;
|
|
15243
16484
|
return v === null ? "\0N" : datumKey(keyTypes[j], v);
|
|
15244
16485
|
}).join("");
|
|
15245
|
-
if (
|
|
16486
|
+
if (key2 === wanted) {
|
|
15246
16487
|
found = true;
|
|
15247
16488
|
break;
|
|
15248
16489
|
}
|
|
@@ -15294,6 +16535,82 @@ function conjunctions(expr) {
|
|
|
15294
16535
|
}
|
|
15295
16536
|
return [expr];
|
|
15296
16537
|
}
|
|
16538
|
+
function tryPointRowIndices(env, table, alias, where) {
|
|
16539
|
+
const eqs = [];
|
|
16540
|
+
for (const part of conjunctions(where)) {
|
|
16541
|
+
const eq = equalityAgainstConst(part);
|
|
16542
|
+
if (!eq || !["param", "number_lit", "string_lit", "bool_lit", "null_lit"].includes(eq.valueExpr.type)) return null;
|
|
16543
|
+
if (part.type !== "binop") return null;
|
|
16544
|
+
const column = part.left.type === "colref" ? part.left : part.right;
|
|
16545
|
+
if (column.type !== "colref") return null;
|
|
16546
|
+
if (column.parts.length > 1 && column.parts[column.parts.length - 2] !== alias) return null;
|
|
16547
|
+
eqs.push(eq);
|
|
16548
|
+
}
|
|
16549
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
16550
|
+
if (spec.where || spec.keys.length !== eqs.length || spec.keys.some((key3) => key3.expr) || !eqs.every((eq, index) => eq.column === spec.columnNames[index]))
|
|
16551
|
+
continue;
|
|
16552
|
+
const values = table.columns.map(() => null);
|
|
16553
|
+
let matches2 = true;
|
|
16554
|
+
for (let index = 0; index < spec.keys.length; index++) {
|
|
16555
|
+
const key3 = spec.keys[index];
|
|
16556
|
+
const column = table.columns[key3.colIdx];
|
|
16557
|
+
const eq = eqs[index];
|
|
16558
|
+
if (!column || !eq || column.domain) {
|
|
16559
|
+
matches2 = false;
|
|
16560
|
+
break;
|
|
16561
|
+
}
|
|
16562
|
+
const value = evalExpr(env.ctx, makeEvalScope(env, null), eq.valueExpr);
|
|
16563
|
+
if (value.t !== column.type.id && !(column.type.id === "text" && value.t === "unknown" && typeof value.v === "string")) {
|
|
16564
|
+
matches2 = false;
|
|
16565
|
+
break;
|
|
16566
|
+
}
|
|
16567
|
+
values[key3.colIdx] = value.v;
|
|
16568
|
+
}
|
|
16569
|
+
if (!matches2) continue;
|
|
16570
|
+
const key2 = uniqueKeyOf(env, table, spec, values);
|
|
16571
|
+
return { indices: key2 === null ? [] : indexStoreFor(env, table, spec).lookup(key2), indexName: spec.name };
|
|
16572
|
+
}
|
|
16573
|
+
return null;
|
|
16574
|
+
}
|
|
16575
|
+
function recordIndexScan(env, table, name) {
|
|
16576
|
+
const schema = env.ctx.state.schemas.get(table.schema);
|
|
16577
|
+
const index = schema?.indexes.get(name);
|
|
16578
|
+
if (schema && index) {
|
|
16579
|
+
const lastScan = env.ctx.state.clock().getTime();
|
|
16580
|
+
schema.indexes.set(name, { ...index, scans: (index.scans ?? 0n) + 1n, lastScan });
|
|
16581
|
+
}
|
|
16582
|
+
}
|
|
16583
|
+
function tryRangeRowIndices(env, table, alias, where) {
|
|
16584
|
+
const parts = conjunctions(where);
|
|
16585
|
+
if (parts.length !== 2) return null;
|
|
16586
|
+
const bounds = [];
|
|
16587
|
+
for (const part of parts) {
|
|
16588
|
+
if (part.type !== "binop" || ![">", ">=", "<", "<="].includes(part.op)) return null;
|
|
16589
|
+
const column2 = part.left.type === "colref" ? part.left : part.right;
|
|
16590
|
+
const value = part.left.type === "colref" ? part.right : part.left;
|
|
16591
|
+
if (column2.type !== "colref" || column2.parts.length > 2 || column2.parts.length === 2 && column2.parts[0] !== alias || !["param", "number_lit"].includes(value.type))
|
|
16592
|
+
return null;
|
|
16593
|
+
const op = part.left === column2 ? part.op : { ">": "<", ">=": "<=", "<": ">", "<=": ">=" }[part.op];
|
|
16594
|
+
bounds.push({ column: column2.parts.at(-1), op, value });
|
|
16595
|
+
}
|
|
16596
|
+
const lower = bounds.find((bound) => bound.op === ">" || bound.op === ">=");
|
|
16597
|
+
const upper = bounds.find((bound) => bound.op === "<" || bound.op === "<=");
|
|
16598
|
+
if (!lower || !upper || lower.column !== upper.column) return null;
|
|
16599
|
+
const column = table.columns.find((column2) => column2.name === lower.column);
|
|
16600
|
+
if (!column || column.domain || !["int2", "int4"].includes(column.type.id)) return null;
|
|
16601
|
+
for (const spec of uniqueSpecsFor(env, table)) {
|
|
16602
|
+
if (spec.where || spec.keys.length !== 1 || spec.keys[0].expr || spec.columnNames[0] !== lower.column) continue;
|
|
16603
|
+
const low = evalExpr(env.ctx, makeEvalScope(env, null), lower.value);
|
|
16604
|
+
const high = evalExpr(env.ctx, makeEvalScope(env, null), upper.value);
|
|
16605
|
+
if (low.t !== column.type.id || high.t !== column.type.id || typeof low.v !== "number" || typeof high.v !== "number")
|
|
16606
|
+
return null;
|
|
16607
|
+
return {
|
|
16608
|
+
indices: indexStoreFor(env, table, spec).integerRange(low.v, high.v, lower.op === ">=", upper.op === "<="),
|
|
16609
|
+
indexName: spec.name
|
|
16610
|
+
};
|
|
16611
|
+
}
|
|
16612
|
+
return null;
|
|
16613
|
+
}
|
|
15297
16614
|
function tryIndexedTableRows(env, table, alias, where) {
|
|
15298
16615
|
const parts = conjunctions(where);
|
|
15299
16616
|
const eqs = [];
|
|
@@ -15327,15 +16644,10 @@ function tryIndexedTableRows(env, table, alias, where) {
|
|
|
15327
16644
|
const v = evalExpr(env.ctx, makeEvalScope(env, scope), eqs[i].valueExpr);
|
|
15328
16645
|
probeRow[colIdx] = castTo(env.ctx, v, table.columns[colIdx].type.id, {}).v;
|
|
15329
16646
|
}
|
|
15330
|
-
const
|
|
15331
|
-
if (
|
|
15332
|
-
const hits = indexStoreFor(env, table, spec).lookup(
|
|
15333
|
-
|
|
15334
|
-
const index = schema?.indexes.get(spec.name);
|
|
15335
|
-
if (schema && index) {
|
|
15336
|
-
const lastScan = env.ctx.state.clock().getTime();
|
|
15337
|
-
schema.indexes.set(spec.name, { ...index, scans: (index.scans ?? 0n) + 1n, lastScan });
|
|
15338
|
-
}
|
|
16647
|
+
const key2 = uniqueKeyOf(env, table, spec, probeRow);
|
|
16648
|
+
if (key2 === null) return [];
|
|
16649
|
+
const hits = indexStoreFor(env, table, spec).lookup(key2);
|
|
16650
|
+
recordIndexScan(env, table, spec.name);
|
|
15339
16651
|
return hits.map((i) => table.rowAt(i));
|
|
15340
16652
|
}
|
|
15341
16653
|
function tryIndexedFromItem(env, item, where) {
|
|
@@ -16155,17 +17467,17 @@ var Parser = class {
|
|
|
16155
17467
|
else this.expectKw("null");
|
|
16156
17468
|
return { type: "comment", objectKind: kind, objectName: objectName2, comment: comment2 };
|
|
16157
17469
|
}
|
|
16158
|
-
let
|
|
17470
|
+
let extra2 = "";
|
|
16159
17471
|
if (kind === "materialized") {
|
|
16160
17472
|
this.expectKw("view");
|
|
16161
|
-
|
|
17473
|
+
extra2 = " view";
|
|
16162
17474
|
}
|
|
16163
17475
|
const objectName = this.qualifiedName();
|
|
16164
17476
|
this.expectKw("is");
|
|
16165
17477
|
let comment = null;
|
|
16166
17478
|
if (this.peek().type === "string") comment = this.next().value;
|
|
16167
17479
|
else this.expectKw("null");
|
|
16168
|
-
return { type: "comment", objectKind: kind +
|
|
17480
|
+
return { type: "comment", objectKind: kind + extra2, objectName, comment };
|
|
16169
17481
|
}
|
|
16170
17482
|
case "grant":
|
|
16171
17483
|
case "revoke": {
|
|
@@ -17925,10 +19237,10 @@ var Parser = class {
|
|
|
17925
19237
|
continue;
|
|
17926
19238
|
}
|
|
17927
19239
|
if (this.eatKw("set")) {
|
|
17928
|
-
const
|
|
19240
|
+
const key2 = this.ident(true).toLowerCase();
|
|
17929
19241
|
if (this.eatKw("from")) {
|
|
17930
19242
|
this.expectKw("current");
|
|
17931
|
-
settings[
|
|
19243
|
+
settings[key2] = "__from_current__";
|
|
17932
19244
|
} else {
|
|
17933
19245
|
if (!this.eatOp("=")) this.expectKw("to");
|
|
17934
19246
|
const values = [];
|
|
@@ -17937,10 +19249,10 @@ var Parser = class {
|
|
|
17937
19249
|
if (!["string", "ident", "quoted_ident", "number"].includes(value.type))
|
|
17938
19250
|
this.errorAt(value, "expected setting value");
|
|
17939
19251
|
values.push(
|
|
17940
|
-
|
|
19252
|
+
key2 === "search_path" && /[^a-z0-9_]/.test(value.value) ? `"${value.value.replaceAll('"', '""')}"` : value.value
|
|
17941
19253
|
);
|
|
17942
19254
|
} while (this.eatPunct(","));
|
|
17943
|
-
settings[
|
|
19255
|
+
settings[key2] = values.join(", ");
|
|
17944
19256
|
}
|
|
17945
19257
|
continue;
|
|
17946
19258
|
}
|
|
@@ -18442,10 +19754,10 @@ var Parser = class {
|
|
|
18442
19754
|
parseTruncate() {
|
|
18443
19755
|
this.expectKw("truncate");
|
|
18444
19756
|
this.eatKw("table");
|
|
18445
|
-
const
|
|
19757
|
+
const tables2 = [];
|
|
18446
19758
|
do {
|
|
18447
19759
|
this.eatKw("only");
|
|
18448
|
-
|
|
19760
|
+
tables2.push(this.qualifiedName());
|
|
18449
19761
|
this.eatOp("*");
|
|
18450
19762
|
} while (this.eatPunct(","));
|
|
18451
19763
|
let restartIdentity = false;
|
|
@@ -18458,7 +19770,7 @@ var Parser = class {
|
|
|
18458
19770
|
let cascade = false;
|
|
18459
19771
|
if (this.eatKw("cascade")) cascade = true;
|
|
18460
19772
|
else this.eatKw("restrict");
|
|
18461
|
-
return { type: "truncate", tables, restartIdentity, cascade };
|
|
19773
|
+
return { type: "truncate", tables: tables2, restartIdentity, cascade };
|
|
18462
19774
|
}
|
|
18463
19775
|
// --- SET / EXPLAIN / COPY -----------------------------------------------------
|
|
18464
19776
|
parseSet() {
|
|
@@ -18581,12 +19893,12 @@ var Parser = class {
|
|
|
18581
19893
|
this.pos++;
|
|
18582
19894
|
if (!this.atPunct(")")) {
|
|
18583
19895
|
do {
|
|
18584
|
-
const
|
|
19896
|
+
const key2 = this.ident(true);
|
|
18585
19897
|
if (this.atPunct(",") || this.atPunct(")")) {
|
|
18586
|
-
options[
|
|
19898
|
+
options[key2] = true;
|
|
18587
19899
|
} else {
|
|
18588
19900
|
const v = this.next();
|
|
18589
|
-
options[
|
|
19901
|
+
options[key2] = v.value;
|
|
18590
19902
|
}
|
|
18591
19903
|
} while (this.eatPunct(","));
|
|
18592
19904
|
}
|
|
@@ -20588,11 +21900,11 @@ function computeOneWindow(ctx, call, spec, rowCount, evalAt) {
|
|
|
20588
21900
|
const v = evalAt(i, p);
|
|
20589
21901
|
keyParts.push(v.v === null ? "\0N" : datumKey(v.t === UNKNOWN ? "text" : v.t, v.v));
|
|
20590
21902
|
}
|
|
20591
|
-
const
|
|
20592
|
-
partitionOf.push(
|
|
20593
|
-
const list = partitions.get(
|
|
21903
|
+
const key2 = keyParts.join("");
|
|
21904
|
+
partitionOf.push(key2);
|
|
21905
|
+
const list = partitions.get(key2) ?? [];
|
|
20594
21906
|
list.push(i);
|
|
20595
|
-
partitions.set(
|
|
21907
|
+
partitions.set(key2, list);
|
|
20596
21908
|
}
|
|
20597
21909
|
const results = new Array(rowCount);
|
|
20598
21910
|
for (const rowIdxs of partitions.values()) {
|
|
@@ -20613,8 +21925,8 @@ function computePartition(ctx, call, spec, rowIdxs, evalAt, results) {
|
|
|
20613
21925
|
let group = 0;
|
|
20614
21926
|
for (let pos = 0; pos < n; pos++) {
|
|
20615
21927
|
if (pos > 0) {
|
|
20616
|
-
const
|
|
20617
|
-
if (!
|
|
21928
|
+
const same2 = spec.orderBy.length === 0 || compareKeys(ctx, keys.get(rowIdxs[pos - 1]), keys.get(rowIdxs[pos])) === 0;
|
|
21929
|
+
if (!same2) group++;
|
|
20618
21930
|
}
|
|
20619
21931
|
peerGroup[pos] = group;
|
|
20620
21932
|
}
|
|
@@ -20869,10 +22181,10 @@ function computeRankingFunction(ctx, name, call, rowIdxs, peerGroup, _groupCount
|
|
|
20869
22181
|
if (buckets <= 0)
|
|
20870
22182
|
throw pgError("invalid_parameter_value", "argument of ntile must be greater than zero", "22014");
|
|
20871
22183
|
const base = Math.floor(n / buckets);
|
|
20872
|
-
const
|
|
22184
|
+
const extra2 = n % buckets;
|
|
20873
22185
|
let pos = 0;
|
|
20874
22186
|
for (let b = 1; b <= buckets && pos < n; b++) {
|
|
20875
|
-
const size = base + (b <=
|
|
22187
|
+
const size = base + (b <= extra2 ? 1 : 0);
|
|
20876
22188
|
for (let k = 0; k < size && pos < n; k++, pos++) {
|
|
20877
22189
|
results[rowIdxs[pos]] = tv("int4", b);
|
|
20878
22190
|
}
|
|
@@ -21311,7 +22623,7 @@ function renameWithAliases(rel4, label, colAliases, what) {
|
|
|
21311
22623
|
}));
|
|
21312
22624
|
return { columns, rows: rel4.rows };
|
|
21313
22625
|
}
|
|
21314
|
-
function materializeItem(env, item, scope) {
|
|
22626
|
+
function materializeItem(env, item, scope, shapeOnly = false) {
|
|
21315
22627
|
switch (item.type) {
|
|
21316
22628
|
case "from_table": {
|
|
21317
22629
|
const label = item.alias ?? item.name[item.name.length - 1];
|
|
@@ -21330,7 +22642,7 @@ function materializeItem(env, item, scope) {
|
|
|
21330
22642
|
...table.columns.map((c) => ({ name: c.name, type: c.type.id, mod: c.type.mod, table: label })),
|
|
21331
22643
|
{ name: "xmin", type: "xid", table: label, hidden: true, system: true }
|
|
21332
22644
|
],
|
|
21333
|
-
rows: table.
|
|
22645
|
+
rows: shapeOnly ? [] : table.serializationRows().map((row) => [...row, table.rowXmins.get(row) ?? 2])
|
|
21334
22646
|
};
|
|
21335
22647
|
return { rel: renameWithAliases(rel4, label, item.colAliases, label), rangeVars: /* @__PURE__ */ new Set([label]) };
|
|
21336
22648
|
}
|
|
@@ -21742,16 +23054,16 @@ function combineJoin(env, kind, left, right, on, using, usingAlias, rangeVars, e
|
|
|
21742
23054
|
const rightTypes = hashRightIdxs.map((i) => right.columns[i].type);
|
|
21743
23055
|
const buckets = /* @__PURE__ */ new Map();
|
|
21744
23056
|
for (let ri = 0; ri < right.rows.length; ri++) {
|
|
21745
|
-
const
|
|
21746
|
-
if (
|
|
21747
|
-
const bucket = buckets.get(
|
|
23057
|
+
const key2 = joinKeyFromRow(right.rows[ri], hashRightIdxs, rightTypes, hashKeyTypes, ctx);
|
|
23058
|
+
if (key2 === null) continue;
|
|
23059
|
+
const bucket = buckets.get(key2);
|
|
21748
23060
|
if (bucket) bucket.push(ri);
|
|
21749
|
-
else buckets.set(
|
|
23061
|
+
else buckets.set(key2, [ri]);
|
|
21750
23062
|
}
|
|
21751
23063
|
for (const lrow of left.rows) {
|
|
21752
23064
|
let matched = false;
|
|
21753
|
-
const
|
|
21754
|
-
const candidates =
|
|
23065
|
+
const key2 = joinKeyFromRow(lrow, hashLeftIdxs, leftTypes, hashKeyTypes, ctx);
|
|
23066
|
+
const candidates = key2 === null ? [] : buckets.get(key2) ?? [];
|
|
21755
23067
|
for (const ri of candidates) {
|
|
21756
23068
|
const rrow = right.rows[ri];
|
|
21757
23069
|
if (!matchRows(lrow, rrow)) continue;
|
|
@@ -22252,9 +23564,9 @@ function computeAggregate(env, call, rows, probeScope) {
|
|
|
22252
23564
|
const seen = /* @__PURE__ */ new Set();
|
|
22253
23565
|
effective = [];
|
|
22254
23566
|
for (const t of tuples) {
|
|
22255
|
-
const
|
|
22256
|
-
if (seen.has(
|
|
22257
|
-
seen.add(
|
|
23567
|
+
const key2 = t.args.map((a) => a.v === null ? "\0N" : datumKey(a.t === UNKNOWN ? "text" : a.t, a.v)).join("");
|
|
23568
|
+
if (seen.has(key2)) continue;
|
|
23569
|
+
seen.add(key2);
|
|
22258
23570
|
effective.push(t);
|
|
22259
23571
|
}
|
|
22260
23572
|
}
|
|
@@ -22603,14 +23915,30 @@ function checkPredicateType(env, columns, rangeVars, e, kind, extras) {
|
|
|
22603
23915
|
function executeCore(env0, core, orderBy, opts) {
|
|
22604
23916
|
const env = env0;
|
|
22605
23917
|
const ctx = env.ctx;
|
|
22606
|
-
const
|
|
23918
|
+
const item = core.from.length === 1 ? core.from[0] : null;
|
|
23919
|
+
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;
|
|
23920
|
+
const point = pointTable && core.where && item?.type === "from_table" ? tryPointRowIndices(env, pointTable, item.alias ?? item.name[item.name.length - 1], core.where) : null;
|
|
23921
|
+
const range = !point && pointTable && core.where && item?.type === "from_table" ? tryRangeRowIndices(env, pointTable, item.alias ?? item.name[item.name.length - 1], core.where) : null;
|
|
23922
|
+
const access = point ?? range;
|
|
23923
|
+
const source = access && item ? materializeItem(env, item, env.outer, true) : buildFrom(env, core.from, core.where);
|
|
22607
23924
|
const srcCols = source.rel.columns;
|
|
22608
23925
|
const rangeVars = source.rangeVars;
|
|
22609
23926
|
const rowScope = (row) => new RowScope(srcCols, row, env.outer, rangeVars);
|
|
22610
23927
|
let srcRows = source.rel.rows;
|
|
22611
23928
|
if (core.where) {
|
|
22612
23929
|
checkPredicateType(env, srcCols, rangeVars, core.where, "WHERE");
|
|
22613
|
-
if (
|
|
23930
|
+
if (access && pointTable) {
|
|
23931
|
+
pointTable.materializeSlab();
|
|
23932
|
+
srcRows = access.indices.map((index) => {
|
|
23933
|
+
const row = pointTable.rowAt(index);
|
|
23934
|
+
return [...row, pointTable.rowXmins.get(row) ?? 2];
|
|
23935
|
+
});
|
|
23936
|
+
if (range) {
|
|
23937
|
+
const where = core.where;
|
|
23938
|
+
srcRows = srcRows.filter((row) => evalPredicate2(env, rowScope(row), where));
|
|
23939
|
+
}
|
|
23940
|
+
recordIndexScan(env, pointTable, access.indexName);
|
|
23941
|
+
} else if (core.from.length === 1) {
|
|
22614
23942
|
const indexed = tryIndexedFromItem(env, core.from[0], core.where);
|
|
22615
23943
|
if (indexed !== null) {
|
|
22616
23944
|
srcRows = indexed;
|
|
@@ -22673,13 +24001,13 @@ function executeCore(env0, core, orderBy, opts) {
|
|
|
22673
24001
|
} else {
|
|
22674
24002
|
for (const row of srcRows) {
|
|
22675
24003
|
const scope = rowScope(row);
|
|
22676
|
-
const
|
|
24004
|
+
const key2 = set.map((e) => {
|
|
22677
24005
|
const v = evalScalar(env, scope, e);
|
|
22678
24006
|
return v.v === null ? "\0N" : datumKey(v.t === UNKNOWN ? "text" : v.t, v.v);
|
|
22679
24007
|
}).join("");
|
|
22680
|
-
const list = groups.get(
|
|
24008
|
+
const list = groups.get(key2);
|
|
22681
24009
|
if (list) list.push(row);
|
|
22682
|
-
else groups.set(
|
|
24010
|
+
else groups.set(key2, [row]);
|
|
22683
24011
|
}
|
|
22684
24012
|
}
|
|
22685
24013
|
for (const rows of groups.values()) {
|
|
@@ -22791,11 +24119,11 @@ function executeCore(env0, core, orderBy, opts) {
|
|
|
22791
24119
|
}
|
|
22792
24120
|
}
|
|
22793
24121
|
}
|
|
22794
|
-
const
|
|
24122
|
+
const item2 = { name: `__ord${orderExprs.length}`, kind: "expr", colIdx: -1, expr: ob.expr, hidden: true };
|
|
22795
24123
|
const spec = { colIdx: projItems.length, dir, nullsFirst };
|
|
22796
|
-
projItems.push(
|
|
24124
|
+
projItems.push(item2);
|
|
22797
24125
|
sortSpecs.push(spec);
|
|
22798
|
-
orderExprs.push({ item, spec });
|
|
24126
|
+
orderExprs.push({ item: item2, spec });
|
|
22799
24127
|
}
|
|
22800
24128
|
if (core.distinct && !core.distinct.on && sortSpecs.length > 0 && !opts?.hasLimit) {
|
|
22801
24129
|
const used = new Set(sortSpecs.map((s) => s.colIdx));
|
|
@@ -23169,7 +24497,7 @@ var TransactionManager = class {
|
|
|
23169
24497
|
return { state: this.state.cloneShallow(), prngState: this.state.prng.getState() };
|
|
23170
24498
|
}
|
|
23171
24499
|
restore(snap) {
|
|
23172
|
-
this.state.restoreFrom(snap.state);
|
|
24500
|
+
this.state.restoreFrom(snap.state.cloneShallow());
|
|
23173
24501
|
this.state.prng.setState(snap.prngState);
|
|
23174
24502
|
}
|
|
23175
24503
|
thawOnce() {
|
|
@@ -23269,7 +24597,10 @@ function withStatementRollback(env, fn) {
|
|
|
23269
24597
|
function requireTargetTable(env, parts, verb) {
|
|
23270
24598
|
const state = env.ctx.state;
|
|
23271
24599
|
const table = state.findTable(parts);
|
|
23272
|
-
if (table)
|
|
24600
|
+
if (table) {
|
|
24601
|
+
prepareDmlIndexStores(env, table);
|
|
24602
|
+
return state.ensureWritableTableForDml(table);
|
|
24603
|
+
}
|
|
23273
24604
|
const view = state.findView(parts);
|
|
23274
24605
|
if (view) {
|
|
23275
24606
|
throw pgError("wrong_object_type", `cannot ${verb} view "${view.name}"`, "42809");
|
|
@@ -23437,7 +24768,7 @@ function resolveArbiters(env, table, clause) {
|
|
|
23437
24768
|
return matches2.map((spec) => ({ spec }));
|
|
23438
24769
|
}
|
|
23439
24770
|
function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, where) {
|
|
23440
|
-
const rows = table.
|
|
24771
|
+
const rows = table.writeRows();
|
|
23441
24772
|
const existing = rows[existingIdx];
|
|
23442
24773
|
const cols = [
|
|
23443
24774
|
...table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label })),
|
|
@@ -23449,7 +24780,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
23449
24780
|
applyUpdateSets(env, table, sets, scope, updated);
|
|
23450
24781
|
computeGeneratedColumns(env, table, updated);
|
|
23451
24782
|
table.rowXmins.set(updated, env.ctx.rowVersionXid());
|
|
23452
|
-
|
|
24783
|
+
table.replaceRow(existingIdx, updated);
|
|
23453
24784
|
indexUpdateRow(env, table, existingIdx, existing, updated);
|
|
23454
24785
|
checkNotNull(env, table, updated);
|
|
23455
24786
|
checkChecks(env, table, updated);
|
|
@@ -23461,7 +24792,7 @@ function applyOnConflictUpdate(env, table, label, existingIdx, newRow, sets, whe
|
|
|
23461
24792
|
function executeInsert(env0, stmt) {
|
|
23462
24793
|
const env = applyWith(env0, stmt.with);
|
|
23463
24794
|
const table = requireTargetTable(env, stmt.table, "insert into");
|
|
23464
|
-
const rows = table.
|
|
24795
|
+
const rows = table.writeRows();
|
|
23465
24796
|
const label = stmt.alias ?? table.name;
|
|
23466
24797
|
const colIdxs = insertTargetColumns(env, table, stmt);
|
|
23467
24798
|
const sourceRows = insertSourceRows(env, stmt, colIdxs);
|
|
@@ -23545,6 +24876,7 @@ function executeInsert(env0, stmt) {
|
|
|
23545
24876
|
checkForeignKeys(env, table, newRow);
|
|
23546
24877
|
table.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23547
24878
|
rows.push(newRow);
|
|
24879
|
+
table.recordDurableRow(newRow);
|
|
23548
24880
|
indexInsertRow(env, table, rows.length - 1, newRow);
|
|
23549
24881
|
insertedCount++;
|
|
23550
24882
|
insertedRows.push(newRow);
|
|
@@ -23637,13 +24969,17 @@ function executeUpdate(env0, stmt) {
|
|
|
23637
24969
|
const label = stmt.alias ?? table.name;
|
|
23638
24970
|
fireStatementTriggers(env, table, "before", "update");
|
|
23639
24971
|
const fromSource = stmt.from.length > 0 ? buildFrom(env, stmt.from) : null;
|
|
23640
|
-
const rows = table.mutableRows();
|
|
23641
24972
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
23642
24973
|
const updatedRows = [];
|
|
23643
24974
|
const replacedRows = [];
|
|
23644
24975
|
let updateCount = 0;
|
|
23645
|
-
|
|
23646
|
-
|
|
24976
|
+
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(
|
|
24977
|
+
(set) => set.columns.every((column) => column.subscripts === null && column.fields.length === 0) && "type" in set.value && isPureUpdateValue(set.value)
|
|
24978
|
+
) && referencingConstraints(env, table).length === 0 ? tryPointRowIndices(env, table, label, stmt.where) : null;
|
|
24979
|
+
const rows = point ? null : table.writeRows();
|
|
24980
|
+
for (let candidate = 0; candidate < (point ? point.indices.length : rows.length); candidate++) {
|
|
24981
|
+
const ri = point ? point.indices[candidate] : candidate;
|
|
24982
|
+
const oldRow = point ? table.rowAt(ri) : rows[ri];
|
|
23647
24983
|
let matchScope = null;
|
|
23648
24984
|
if (fromSource) {
|
|
23649
24985
|
let found = false;
|
|
@@ -23669,14 +25005,14 @@ function executeUpdate(env0, stmt) {
|
|
|
23669
25005
|
const finalRow = fired.row;
|
|
23670
25006
|
computeGeneratedColumns(env, table, finalRow);
|
|
23671
25007
|
table.rowXmins.set(finalRow, env.ctx.rowVersionXid());
|
|
23672
|
-
|
|
25008
|
+
table.replaceRow(ri, finalRow);
|
|
23673
25009
|
try {
|
|
23674
25010
|
checkNotNull(env, table, finalRow);
|
|
23675
25011
|
checkChecks(env, table, finalRow);
|
|
23676
25012
|
checkUnique(env, table, finalRow, ri);
|
|
23677
25013
|
checkForeignKeys(env, table, finalRow);
|
|
23678
25014
|
} catch (err) {
|
|
23679
|
-
|
|
25015
|
+
table.replaceRow(ri, oldRow);
|
|
23680
25016
|
throw err;
|
|
23681
25017
|
}
|
|
23682
25018
|
indexUpdateRow(env, table, ri, oldRow, finalRow);
|
|
@@ -23694,12 +25030,29 @@ function executeUpdate(env0, stmt) {
|
|
|
23694
25030
|
}
|
|
23695
25031
|
return commandResult("UPDATE", updateCount);
|
|
23696
25032
|
}
|
|
25033
|
+
function isPureUpdateValue(expr) {
|
|
25034
|
+
switch (expr.type) {
|
|
25035
|
+
case "param":
|
|
25036
|
+
case "number_lit":
|
|
25037
|
+
case "string_lit":
|
|
25038
|
+
case "bool_lit":
|
|
25039
|
+
case "null_lit":
|
|
25040
|
+
case "colref":
|
|
25041
|
+
return true;
|
|
25042
|
+
case "binop":
|
|
25043
|
+
return isPureUpdateValue(expr.left) && isPureUpdateValue(expr.right);
|
|
25044
|
+
case "unop":
|
|
25045
|
+
return isPureUpdateValue(expr.operand);
|
|
25046
|
+
default:
|
|
25047
|
+
return false;
|
|
25048
|
+
}
|
|
25049
|
+
}
|
|
23697
25050
|
function executeDelete(env0, stmt) {
|
|
23698
25051
|
const env = applyWith(env0, stmt.with);
|
|
23699
25052
|
const table = requireTargetTable(env, stmt.table, "delete from");
|
|
23700
25053
|
const label = stmt.alias ?? table.name;
|
|
23701
25054
|
fireStatementTriggers(env, table, "before", "delete");
|
|
23702
|
-
const rows = table.
|
|
25055
|
+
const rows = table.writeRows();
|
|
23703
25056
|
const usingSource = stmt.using.length > 0 ? buildFrom(env, stmt.using) : null;
|
|
23704
25057
|
const targetCols = table.columns.map((c) => ({ name: c.name, type: c.type.id, table: label }));
|
|
23705
25058
|
const toDelete = [];
|
|
@@ -23729,6 +25082,7 @@ function executeDelete(env0, stmt) {
|
|
|
23729
25082
|
if (skipped.has(ri)) continue;
|
|
23730
25083
|
const row = rows[ri];
|
|
23731
25084
|
handleReferencedDelete(env, table, row, 0);
|
|
25085
|
+
table.recordDurableDelete(row);
|
|
23732
25086
|
rows.splice(ri, 1);
|
|
23733
25087
|
deletedRows.unshift(row);
|
|
23734
25088
|
}
|
|
@@ -23755,12 +25109,12 @@ function rowKeyFor(table, row, columns) {
|
|
|
23755
25109
|
}
|
|
23756
25110
|
return parts.join("");
|
|
23757
25111
|
}
|
|
23758
|
-
function referencingRowIdxs(ref,
|
|
25112
|
+
function referencingRowIdxs(ref, key2) {
|
|
23759
25113
|
const { table: rt, constraint: con } = ref;
|
|
23760
25114
|
const out = [];
|
|
23761
25115
|
for (let i = 0; i < rt.rowCount(); i++) {
|
|
23762
25116
|
const k = rowKeyFor(rt, rt.rowAt(i), con.columns);
|
|
23763
|
-
if (k ===
|
|
25117
|
+
if (k === key2) out.push(i);
|
|
23764
25118
|
}
|
|
23765
25119
|
return out;
|
|
23766
25120
|
}
|
|
@@ -23770,18 +25124,19 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
23770
25124
|
}
|
|
23771
25125
|
for (const ref of referencingConstraints(env, table)) {
|
|
23772
25126
|
const con = ref.constraint;
|
|
23773
|
-
const
|
|
23774
|
-
if (
|
|
23775
|
-
const idxs = referencingRowIdxs(ref,
|
|
25127
|
+
const key2 = rowKeyFor(table, row, con.refColumns);
|
|
25128
|
+
if (key2 === null) continue;
|
|
25129
|
+
const idxs = referencingRowIdxs(ref, key2);
|
|
23776
25130
|
if (idxs.length === 0) continue;
|
|
23777
25131
|
const action = con.onDelete ?? "no_action";
|
|
23778
|
-
const child = env.ctx.state.
|
|
23779
|
-
const childRows = child.
|
|
25132
|
+
const child = env.ctx.state.ensureWritableTableForDml(ref.table);
|
|
25133
|
+
const childRows = child.writeRows();
|
|
23780
25134
|
switch (action) {
|
|
23781
25135
|
case "cascade": {
|
|
23782
25136
|
for (let k = idxs.length - 1; k >= 0; k--) {
|
|
23783
25137
|
const childRow = childRows[idxs[k]];
|
|
23784
25138
|
handleReferencedDelete(env, child, childRow, depth + 1);
|
|
25139
|
+
child.recordDurableDelete(childRow);
|
|
23785
25140
|
childRows.splice(idxs[k], 1);
|
|
23786
25141
|
}
|
|
23787
25142
|
break;
|
|
@@ -23793,7 +25148,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
23793
25148
|
newRow[child.columnIndex(c)] = null;
|
|
23794
25149
|
}
|
|
23795
25150
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23796
|
-
|
|
25151
|
+
child.replaceRow(i, newRow);
|
|
23797
25152
|
checkNotNull(env, child, newRow);
|
|
23798
25153
|
checkChecks(env, child, newRow);
|
|
23799
25154
|
}
|
|
@@ -23807,7 +25162,7 @@ function handleReferencedDelete(env, table, row, depth) {
|
|
|
23807
25162
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
23808
25163
|
}
|
|
23809
25164
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23810
|
-
|
|
25165
|
+
child.replaceRow(i, newRow);
|
|
23811
25166
|
checkNotNull(env, child, newRow);
|
|
23812
25167
|
checkChecks(env, child, newRow);
|
|
23813
25168
|
checkForeignKeys(env, child, newRow);
|
|
@@ -23833,8 +25188,8 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23833
25188
|
const idxs = referencingRowIdxs(ref, oldKey);
|
|
23834
25189
|
if (idxs.length === 0) continue;
|
|
23835
25190
|
const action = con.onUpdate ?? "no_action";
|
|
23836
|
-
const child = env.ctx.state.
|
|
23837
|
-
const childRows = child.
|
|
25191
|
+
const child = env.ctx.state.ensureWritableTableForDml(ref.table);
|
|
25192
|
+
const childRows = child.writeRows();
|
|
23838
25193
|
switch (action) {
|
|
23839
25194
|
case "cascade": {
|
|
23840
25195
|
for (const i of idxs) {
|
|
@@ -23845,7 +25200,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23845
25200
|
newRow[localIdx] = newRows[r][refIdx] ?? null;
|
|
23846
25201
|
}
|
|
23847
25202
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23848
|
-
|
|
25203
|
+
child.replaceRow(i, newRow);
|
|
23849
25204
|
checkChecks(env, child, newRow);
|
|
23850
25205
|
}
|
|
23851
25206
|
break;
|
|
@@ -23855,7 +25210,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23855
25210
|
const newRow = childRows[i].slice();
|
|
23856
25211
|
for (const c of con.columns) newRow[child.columnIndex(c)] = null;
|
|
23857
25212
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23858
|
-
|
|
25213
|
+
child.replaceRow(i, newRow);
|
|
23859
25214
|
checkNotNull(env, child, newRow);
|
|
23860
25215
|
}
|
|
23861
25216
|
break;
|
|
@@ -23868,7 +25223,7 @@ function handleReferencedUpdate(env, table, oldRows, newRows) {
|
|
|
23868
25223
|
newRow[ci] = columnDefault(env, child, child.columns[ci]);
|
|
23869
25224
|
}
|
|
23870
25225
|
child.rowXmins.set(newRow, env.ctx.rowVersionXid());
|
|
23871
|
-
|
|
25226
|
+
child.replaceRow(i, newRow);
|
|
23872
25227
|
checkForeignKeys(env, child, newRow);
|
|
23873
25228
|
}
|
|
23874
25229
|
break;
|
|
@@ -24220,12 +25575,12 @@ function executeCopyFromData(env, stmt, data) {
|
|
|
24220
25575
|
const delim = String(stmt.options.delimiter ?? (format === "csv" ? "," : " "));
|
|
24221
25576
|
const nullStr = String(stmt.options.null ?? (format === "csv" ? "" : "\\N"));
|
|
24222
25577
|
const quote2 = String(stmt.options.quote ?? '"');
|
|
24223
|
-
const
|
|
25578
|
+
const header2 = stmt.options.header === true || String(stmt.options.header ?? "").toLowerCase() === "true";
|
|
24224
25579
|
let lines = format === "csv" ? splitCsvRecords(data, quote2) : data.split(/\r?\n/);
|
|
24225
25580
|
const endMarker = lines.indexOf("\\.");
|
|
24226
25581
|
if (endMarker !== -1) lines = lines.slice(0, endMarker);
|
|
24227
25582
|
if (lines.length > 0 && lines[lines.length - 1] === "") lines = lines.slice(0, -1);
|
|
24228
|
-
if (
|
|
25583
|
+
if (header2 && lines.length > 0) lines = lines.slice(1);
|
|
24229
25584
|
const valueRows = lines.map((line) => {
|
|
24230
25585
|
let cells;
|
|
24231
25586
|
if (format === "csv") {
|
|
@@ -24391,6 +25746,8 @@ function openSnapshot(snapshot, options = {}) {
|
|
|
24391
25746
|
now = () => new Date(ms);
|
|
24392
25747
|
}
|
|
24393
25748
|
const state = snapshot.state.cloneShallow();
|
|
25749
|
+
for (const schema of state.schemas.values())
|
|
25750
|
+
for (const [name, table] of schema.tables) if (table.durableChanges) schema.tables.set(name, table.untrackedFork());
|
|
24394
25751
|
state.prng = prng;
|
|
24395
25752
|
state.clock = now;
|
|
24396
25753
|
return createAdoptedDatabase({
|
|
@@ -25044,8 +26401,8 @@ function pgDepend(ctx) {
|
|
|
25044
26401
|
for (const index of schema.indexes.values()) {
|
|
25045
26402
|
const table = schema.tables.get(index.table);
|
|
25046
26403
|
if (!table || index.isConstraint) continue;
|
|
25047
|
-
for (const
|
|
25048
|
-
const attnum =
|
|
26404
|
+
for (const key2 of index.columns) {
|
|
26405
|
+
const attnum = key2.column === null ? 0 : table.columns.findIndex((c) => c.name === key2.column) + 1;
|
|
25049
26406
|
if (attnum > 0) rows.push([PG_CLASS_OID, index.oid ?? 0, 0, PG_CLASS_OID, table.oid, attnum, "a"]);
|
|
25050
26407
|
}
|
|
25051
26408
|
}
|
|
@@ -26659,10 +28016,10 @@ function assertUniqueIndexBuildable(env, table, index, name = index.name) {
|
|
|
26659
28016
|
const spec = uniqueSpecOfIndex(table, index);
|
|
26660
28017
|
const seen = /* @__PURE__ */ new Set();
|
|
26661
28018
|
for (let i = 0; i < table.rowCount(); i++) {
|
|
26662
|
-
const
|
|
26663
|
-
if (
|
|
26664
|
-
if (seen.has(
|
|
26665
|
-
seen.add(
|
|
28019
|
+
const key2 = uniqueKeyOf(env, table, spec, table.rowAt(i));
|
|
28020
|
+
if (key2 === null) continue;
|
|
28021
|
+
if (seen.has(key2)) throw pgError("constraint_unique", `could not create unique index "${name}"`, "23505");
|
|
28022
|
+
seen.add(key2);
|
|
26666
28023
|
}
|
|
26667
28024
|
}
|
|
26668
28025
|
function relationMissing(state, parts) {
|
|
@@ -27282,8 +28639,8 @@ function executeRenameEnum(env, enumData, newName) {
|
|
|
27282
28639
|
const baseType = { ...domain.baseType, id: retype(domain.baseType.id) };
|
|
27283
28640
|
const defaultExpr = domain.defaultExpr ? rewrite(domain.defaultExpr) : domain.defaultExpr;
|
|
27284
28641
|
const checks = domain.checks.map((c) => ({ ...c, expr: rewrite(c.expr) }));
|
|
27285
|
-
const
|
|
27286
|
-
if (!
|
|
28642
|
+
const same2 = baseType.id === domain.baseType.id && defaultExpr === domain.defaultExpr && checks.every((c, i) => c.expr === domain.checks[i]?.expr);
|
|
28643
|
+
if (!same2) sch.domains.set(name, { ...domain, baseType, defaultExpr, checks, shareCount: 0 });
|
|
27287
28644
|
}
|
|
27288
28645
|
for (const overloads of sch.functions.values()) {
|
|
27289
28646
|
overloads.forEach((fn, index) => {
|
|
@@ -27291,15 +28648,15 @@ function executeRenameEnum(env, enumData, newName) {
|
|
|
27291
28648
|
const returns = fn.returns === null ? null : retype(fn.returns);
|
|
27292
28649
|
const returnsTable = fn.returnsTable?.map((c) => ({ ...c, type: retype(c.type) })) ?? null;
|
|
27293
28650
|
const argDefaults = fn.argDefaults.map((d) => d ? rewrite(d) : d);
|
|
27294
|
-
const
|
|
27295
|
-
if (!
|
|
28651
|
+
const same2 = argTypes.every((t, i) => t === fn.argTypes[i]) && returns === fn.returns && returnsTable?.every((c, i) => c.type === fn.returnsTable?.[i]?.type) !== false && argDefaults.every((d, i) => d === fn.argDefaults[i]);
|
|
28652
|
+
if (!same2) overloads[index] = { ...fn, argTypes, returns, returnsTable, argDefaults };
|
|
27296
28653
|
});
|
|
27297
28654
|
}
|
|
27298
28655
|
for (const [name, index] of [...sch.indexes]) {
|
|
27299
28656
|
const columns = index.columns.map((c) => c.expr ? { ...c, expr: rewrite(c.expr) } : c);
|
|
27300
28657
|
const where = index.where ? rewrite(index.where) : index.where;
|
|
27301
|
-
const
|
|
27302
|
-
if (!
|
|
28658
|
+
const same2 = where === index.where && columns.every((c, i) => c === index.columns[i]);
|
|
28659
|
+
if (!same2) sch.indexes.set(name, { ...index, columns, where });
|
|
27303
28660
|
}
|
|
27304
28661
|
}
|
|
27305
28662
|
const renamed = state.ensureWritableEnum(enumData);
|
|
@@ -27333,9 +28690,9 @@ function typeNameRewriter(isTarget, newName) {
|
|
|
27333
28690
|
return { ...record, parts: [...record.parts.slice(0, -1), newName] };
|
|
27334
28691
|
}
|
|
27335
28692
|
let out = null;
|
|
27336
|
-
for (const [
|
|
28693
|
+
for (const [key2, value] of Object.entries(record)) {
|
|
27337
28694
|
const next = walk(value);
|
|
27338
|
-
if (next !== value) (out ??= { ...record })[
|
|
28695
|
+
if (next !== value) (out ??= { ...record })[key2] = next;
|
|
27339
28696
|
}
|
|
27340
28697
|
return out ?? node;
|
|
27341
28698
|
};
|
|
@@ -27455,7 +28812,7 @@ function renameSequenceReferences(env, seq, newName) {
|
|
|
27455
28812
|
if (Array.isArray(value)) return value.map(rewrite);
|
|
27456
28813
|
if (value === null || typeof value !== "object") return value;
|
|
27457
28814
|
const node = value;
|
|
27458
|
-
const updated = Object.fromEntries(Object.entries(node).map(([
|
|
28815
|
+
const updated = Object.fromEntries(Object.entries(node).map(([key2, child]) => [key2, rewrite(child)]));
|
|
27459
28816
|
const rewriteLiteral = (literal2) => {
|
|
27460
28817
|
if (literal2 && typeof literal2 === "object" && "type" in literal2 && literal2.type === "string_lit" && "value" in literal2 && typeof literal2.value === "string" && matches2(literal2.value)) {
|
|
27461
28818
|
return { ...literal2, value: [seq.schema, newName].map(quoteIdent).join(".") };
|
|
@@ -28097,9 +29454,9 @@ function executeCreateFunction(env, stmt) {
|
|
|
28097
29454
|
body,
|
|
28098
29455
|
rawBody: stmt.body,
|
|
28099
29456
|
settings: Object.fromEntries(
|
|
28100
|
-
Object.entries(stmt.settings ?? {}).map(([
|
|
28101
|
-
|
|
28102
|
-
value === "__from_current__" ? state.getSetting(
|
|
29457
|
+
Object.entries(stmt.settings ?? {}).map(([key2, value]) => [
|
|
29458
|
+
key2,
|
|
29459
|
+
value === "__from_current__" ? state.getSetting(key2) ?? "" : value
|
|
28103
29460
|
])
|
|
28104
29461
|
),
|
|
28105
29462
|
strict: stmt.strict,
|
|
@@ -28873,12 +30230,12 @@ function executeDrop(env, stmt) {
|
|
|
28873
30230
|
}
|
|
28874
30231
|
function executeTruncate(env, stmt) {
|
|
28875
30232
|
const state = env.ctx.state;
|
|
28876
|
-
const
|
|
30233
|
+
const tables2 = stmt.tables.map((parts) => {
|
|
28877
30234
|
const t = state.findTable(parts);
|
|
28878
30235
|
if (!t) throw pgError("undefined_table", `relation "${parts.join(".")}" does not exist`, "42P01");
|
|
28879
30236
|
return state.ensureWritableTable(t);
|
|
28880
30237
|
});
|
|
28881
|
-
const set = new Set(
|
|
30238
|
+
const set = new Set(tables2);
|
|
28882
30239
|
if (stmt.cascade) {
|
|
28883
30240
|
let grew = true;
|
|
28884
30241
|
while (grew) {
|
|
@@ -29073,6 +30430,24 @@ var Statement = class _Statement {
|
|
|
29073
30430
|
this.database = database;
|
|
29074
30431
|
this.sql = sql;
|
|
29075
30432
|
this.statements = statements;
|
|
30433
|
+
const observer = database.durabilityObserver;
|
|
30434
|
+
if (observer) {
|
|
30435
|
+
const execute = this.execute;
|
|
30436
|
+
this.execute = (params) => {
|
|
30437
|
+
observer.assertWritable();
|
|
30438
|
+
if (params.some((value) => value instanceof Uint8Array)) observer.exposed();
|
|
30439
|
+
try {
|
|
30440
|
+
const result = execute.call(this, params);
|
|
30441
|
+
if (result.columns.some(
|
|
30442
|
+
(column, index) => column.type === "bytea" && result.rows.some((row) => row[index] !== null && row[index] !== void 0)
|
|
30443
|
+
))
|
|
30444
|
+
observer.exposed();
|
|
30445
|
+
return result;
|
|
30446
|
+
} finally {
|
|
30447
|
+
observer.changed();
|
|
30448
|
+
}
|
|
30449
|
+
};
|
|
30450
|
+
}
|
|
29076
30451
|
}
|
|
29077
30452
|
database;
|
|
29078
30453
|
statements;
|
|
@@ -29105,7 +30480,7 @@ var Statement = class _Statement {
|
|
|
29105
30480
|
*/
|
|
29106
30481
|
textResult(...params) {
|
|
29107
30482
|
const res = this.execute(params);
|
|
29108
|
-
const ctx = new EngineCtx(this.database.
|
|
30483
|
+
const ctx = new EngineCtx(this.database.engineState);
|
|
29109
30484
|
return {
|
|
29110
30485
|
columns: res.columns.map((c) => c.name),
|
|
29111
30486
|
columnTypes: res.columns.map((c) => c.type === UNKNOWN ? "text" : c.type),
|
|
@@ -29142,7 +30517,7 @@ var Statement = class _Statement {
|
|
|
29142
30517
|
const refused = this.database.transactions.inTransaction ? refusedInTransactionBlock(stmt) : null;
|
|
29143
30518
|
if (refused) throw pgError("misuse", `${refused} cannot run inside a transaction block`, "25001");
|
|
29144
30519
|
const env = {
|
|
29145
|
-
ctx: new EngineCtx(this.database.
|
|
30520
|
+
ctx: new EngineCtx(this.database.engineState),
|
|
29146
30521
|
params: typed,
|
|
29147
30522
|
ctes: /* @__PURE__ */ new Map(),
|
|
29148
30523
|
outer: null
|
|
@@ -29152,12 +30527,13 @@ var Statement = class _Statement {
|
|
|
29152
30527
|
return last;
|
|
29153
30528
|
}
|
|
29154
30529
|
toRows(res) {
|
|
29155
|
-
const ctx = new EngineCtx(this.database.
|
|
30530
|
+
const ctx = new EngineCtx(this.database.engineState);
|
|
29156
30531
|
return res.rows.map((row) => {
|
|
29157
30532
|
const obj = {};
|
|
29158
30533
|
for (let i = 0; i < res.columns.length; i++) {
|
|
29159
30534
|
const c = res.columns[i];
|
|
29160
|
-
|
|
30535
|
+
const value = datumToJs(c.type, row[i] ?? null, ctx, this.database.int8Mode);
|
|
30536
|
+
obj[c.name] = value;
|
|
29161
30537
|
}
|
|
29162
30538
|
return obj;
|
|
29163
30539
|
});
|
|
@@ -29175,6 +30551,10 @@ function project(point) {
|
|
|
29175
30551
|
var Database = class {
|
|
29176
30552
|
/** @internal Engine catalog, tables, session settings. */
|
|
29177
30553
|
state;
|
|
30554
|
+
/** @internal Engine-only access; buffered mode observes public state exposure. */
|
|
30555
|
+
engineState;
|
|
30556
|
+
/** @internal Installed only by buffered durability. */
|
|
30557
|
+
durabilityObserver;
|
|
29178
30558
|
/** Seed used to construct the PRNG. Ignored when {@link randomMode} is `"os"`. */
|
|
29179
30559
|
seed;
|
|
29180
30560
|
/** Entropy mode for `random()` / `gen_random_uuid()`. */
|
|
@@ -29204,10 +30584,10 @@ var Database = class {
|
|
|
29204
30584
|
this.int8Mode = options.int8Mode;
|
|
29205
30585
|
this.prng = options.prng;
|
|
29206
30586
|
this.now = options.now;
|
|
29207
|
-
this.
|
|
29208
|
-
this.
|
|
29209
|
-
this.
|
|
29210
|
-
this.transactions = txManagerFor(this.
|
|
30587
|
+
this.engineState = options.state;
|
|
30588
|
+
this.engineState.prng = this.prng;
|
|
30589
|
+
this.engineState.clock = () => this.now();
|
|
30590
|
+
this.transactions = txManagerFor(this.engineState);
|
|
29211
30591
|
} else {
|
|
29212
30592
|
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
29213
30593
|
this.randomMode = options.random ?? "deterministic";
|
|
@@ -29215,9 +30595,10 @@ var Database = class {
|
|
|
29215
30595
|
this.int8Mode = options.int8 ?? "bigint";
|
|
29216
30596
|
this.prng = this.randomMode === "os" ? new OsEntropy() : new Prng(this.seed);
|
|
29217
30597
|
this.now = resolveClock(options.now);
|
|
29218
|
-
this.
|
|
29219
|
-
this.transactions = txManagerFor(this.
|
|
30598
|
+
this.engineState = new DatabaseState(this.prng, () => this.now());
|
|
30599
|
+
this.transactions = txManagerFor(this.engineState);
|
|
29220
30600
|
}
|
|
30601
|
+
this.state = this.engineState;
|
|
29221
30602
|
this.timeline = new Timeline({
|
|
29222
30603
|
now: () => this.now().getTime(),
|
|
29223
30604
|
...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
|
|
@@ -29244,14 +30625,15 @@ var Database = class {
|
|
|
29244
30625
|
* a live in-memory snapshot.
|
|
29245
30626
|
*/
|
|
29246
30627
|
registerFunction(spec) {
|
|
30628
|
+
this.durabilityObserver?.assertWritable();
|
|
29247
30629
|
this.assertOpen();
|
|
29248
30630
|
const parts = spec.name.split(".").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
29249
30631
|
if (parts.length === 0) throw pgError("syntax", "function name is required", "42601");
|
|
29250
|
-
const schemaName = (parts.length >= 2 ? parts[0] : this.
|
|
30632
|
+
const schemaName = (parts.length >= 2 ? parts[0] : this.engineState.currentSchema()).toLowerCase();
|
|
29251
30633
|
const name = parts[parts.length - 1].toLowerCase();
|
|
29252
|
-
const functions = schemaName === "pg_catalog" ? this.
|
|
29253
|
-
const argTypes = spec.args.map((a) => resolveTypeName(this.
|
|
29254
|
-
const returns = resolveTypeName(this.
|
|
30634
|
+
const functions = schemaName === "pg_catalog" ? this.engineState.systemFunctions : this.engineState.getSchema(schemaName).functions;
|
|
30635
|
+
const argTypes = spec.args.map((a) => resolveTypeName(this.engineState, parseTypeName(a)).column.id);
|
|
30636
|
+
const returns = resolveTypeName(this.engineState, parseTypeName(spec.returns)).column.id;
|
|
29255
30637
|
const fn = {
|
|
29256
30638
|
name,
|
|
29257
30639
|
schema: schemaName,
|
|
@@ -29265,7 +30647,7 @@ var Database = class {
|
|
|
29265
30647
|
body: null,
|
|
29266
30648
|
rawBody: null,
|
|
29267
30649
|
strict: spec.strict ?? true,
|
|
29268
|
-
oid: this.
|
|
30650
|
+
oid: this.engineState.nextOid(),
|
|
29269
30651
|
jsImpl: spec.fn
|
|
29270
30652
|
};
|
|
29271
30653
|
const existing = functions.get(name) ?? [];
|
|
@@ -29275,6 +30657,7 @@ var Database = class {
|
|
|
29275
30657
|
if (sameSig !== -1) existing[sameSig] = fn;
|
|
29276
30658
|
else existing.push(fn);
|
|
29277
30659
|
functions.set(name, existing);
|
|
30660
|
+
this.durabilityObserver?.exposed();
|
|
29278
30661
|
}
|
|
29279
30662
|
/** Execute a single-statement query and return all rows keyed by column name. */
|
|
29280
30663
|
query(sql, params = [], options = {}) {
|
|
@@ -29301,6 +30684,7 @@ var Database = class {
|
|
|
29301
30684
|
*/
|
|
29302
30685
|
transaction(fn) {
|
|
29303
30686
|
this.assertOpen();
|
|
30687
|
+
this.durabilityObserver?.assertWritable();
|
|
29304
30688
|
this.apiTransactionDepth++;
|
|
29305
30689
|
try {
|
|
29306
30690
|
if (!this.transactions.inTransaction) {
|
|
@@ -29327,6 +30711,7 @@ var Database = class {
|
|
|
29327
30711
|
}
|
|
29328
30712
|
} finally {
|
|
29329
30713
|
this.apiTransactionDepth--;
|
|
30714
|
+
this.durabilityObserver?.changed();
|
|
29330
30715
|
}
|
|
29331
30716
|
}
|
|
29332
30717
|
/**
|
|
@@ -29421,7 +30806,7 @@ var Database = class {
|
|
|
29421
30806
|
*/
|
|
29422
30807
|
fault(faults) {
|
|
29423
30808
|
this.assertOpen();
|
|
29424
|
-
Object.assign(this.
|
|
30809
|
+
Object.assign(this.engineState.faults, faults);
|
|
29425
30810
|
}
|
|
29426
30811
|
/** Close the database. Further SQL throws. Idempotent. */
|
|
29427
30812
|
close() {
|
|
@@ -29431,6 +30816,7 @@ var Database = class {
|
|
|
29431
30816
|
}
|
|
29432
30817
|
if (this.transactions.inTransaction) this.transactions.rollback();
|
|
29433
30818
|
this.closed = true;
|
|
30819
|
+
this.durabilityObserver?.closed?.();
|
|
29434
30820
|
}
|
|
29435
30821
|
/**
|
|
29436
30822
|
* Execute `COPY table [(cols)] FROM STDIN` with the given text/csv payload
|
|
@@ -29449,13 +30835,18 @@ var Database = class {
|
|
|
29449
30835
|
if (statements.length !== 1 || !stmt || stmt.type !== "copy" || stmt.direction !== "from") {
|
|
29450
30836
|
throw pgError("misuse", "copyFrom() requires a single COPY ... FROM STDIN statement", "XX000");
|
|
29451
30837
|
}
|
|
29452
|
-
|
|
29453
|
-
|
|
30838
|
+
this.durabilityObserver?.assertWritable();
|
|
30839
|
+
try {
|
|
30840
|
+
const env = { ctx: new EngineCtx(this.engineState), params: null, ctes: /* @__PURE__ */ new Map(), outer: null };
|
|
30841
|
+
return executeCopyFromData(env, stmt, data).rowCount;
|
|
30842
|
+
} finally {
|
|
30843
|
+
this.durabilityObserver?.changed();
|
|
30844
|
+
}
|
|
29454
30845
|
}
|
|
29455
30846
|
/** Rows affected by the most recent INSERT / UPDATE / DELETE. */
|
|
29456
30847
|
get changes() {
|
|
29457
30848
|
this.assertOpen();
|
|
29458
|
-
return this.
|
|
30849
|
+
return this.engineState.changes;
|
|
29459
30850
|
}
|
|
29460
30851
|
/** @internal */
|
|
29461
30852
|
assertOpen() {
|
|
@@ -29467,7 +30858,7 @@ var Database = class {
|
|
|
29467
30858
|
}
|
|
29468
30859
|
captureLive() {
|
|
29469
30860
|
return captureSnapshot(
|
|
29470
|
-
this.
|
|
30861
|
+
this.engineState,
|
|
29471
30862
|
this.prng,
|
|
29472
30863
|
this.now,
|
|
29473
30864
|
this.seed,
|
|
@@ -29477,7 +30868,8 @@ var Database = class {
|
|
|
29477
30868
|
);
|
|
29478
30869
|
}
|
|
29479
30870
|
install(snapshot) {
|
|
29480
|
-
this.
|
|
30871
|
+
this.durabilityObserver?.exposed();
|
|
30872
|
+
this.engineState.restoreFrom(snapshot.state.cloneShallow());
|
|
29481
30873
|
this.prng.setState(snapshot.prngState);
|
|
29482
30874
|
if (!this.systemClock) this.now = fixedClock(new Date(snapshot.nowMs));
|
|
29483
30875
|
}
|
|
@@ -29719,6 +31111,13 @@ var Writer = class {
|
|
|
29719
31111
|
this.view.setBigInt64(this.len, value, true);
|
|
29720
31112
|
this.len += 8;
|
|
29721
31113
|
}
|
|
31114
|
+
/** Finite integer Number, encoded identically to i64(BigInt(value)) without allocation. */
|
|
31115
|
+
i64Number(value) {
|
|
31116
|
+
this.ensure(8);
|
|
31117
|
+
this.view.setUint32(this.len, value >>> 0, true);
|
|
31118
|
+
this.view.setUint32(this.len + 4, Math.floor(value / 4294967296) >>> 0, true);
|
|
31119
|
+
this.len += 8;
|
|
31120
|
+
}
|
|
29722
31121
|
f64(value) {
|
|
29723
31122
|
this.ensure(8);
|
|
29724
31123
|
this.view.setFloat64(this.len, value, true);
|
|
@@ -29729,6 +31128,13 @@ var Writer = class {
|
|
|
29729
31128
|
this.buf.set(value, this.len);
|
|
29730
31129
|
this.len += value.length;
|
|
29731
31130
|
}
|
|
31131
|
+
/** Reserve one contiguous region. Fill it before any subsequent Writer call can grow the buffer. */
|
|
31132
|
+
block(byteLength) {
|
|
31133
|
+
this.ensure(byteLength);
|
|
31134
|
+
const block = new DataView(this.buf.buffer, this.len, byteLength);
|
|
31135
|
+
this.len += byteLength;
|
|
31136
|
+
return block;
|
|
31137
|
+
}
|
|
29732
31138
|
/** Length-prefixed UTF-8. */
|
|
29733
31139
|
text(value) {
|
|
29734
31140
|
const encoded = utf8Encode(value);
|
|
@@ -29741,7 +31147,7 @@ var Writer = class {
|
|
|
29741
31147
|
}
|
|
29742
31148
|
finish() {
|
|
29743
31149
|
if (this.len === this.buf.length) return this.buf;
|
|
29744
|
-
return this.buf.
|
|
31150
|
+
return this.buf.slice(0, this.len);
|
|
29745
31151
|
}
|
|
29746
31152
|
fail() {
|
|
29747
31153
|
throw new PostgresError("snapshot_format", "invalid or truncated postgres-mem snapshot", "XX000");
|
|
@@ -29909,8 +31315,8 @@ function readBjv(r, intern) {
|
|
|
29909
31315
|
const count = readVarintU32(r);
|
|
29910
31316
|
const out = {};
|
|
29911
31317
|
for (let i = 0; i < count; i++) {
|
|
29912
|
-
const
|
|
29913
|
-
out[
|
|
31318
|
+
const key2 = readBjv(r, intern);
|
|
31319
|
+
out[key2] = readBjv(r, intern);
|
|
29914
31320
|
}
|
|
29915
31321
|
return out;
|
|
29916
31322
|
}
|
|
@@ -29958,15 +31364,18 @@ function writeInternTable(w, strings) {
|
|
|
29958
31364
|
writeVarintU32(w, strings.length);
|
|
29959
31365
|
if (strings.length === 0) return;
|
|
29960
31366
|
const offsets = [];
|
|
31367
|
+
const encoded = [];
|
|
29961
31368
|
let total = 0;
|
|
29962
31369
|
for (const s of strings) {
|
|
29963
31370
|
offsets.push(total);
|
|
29964
|
-
|
|
31371
|
+
const bytes = utf8Encode(s);
|
|
31372
|
+
encoded.push(bytes);
|
|
31373
|
+
total += bytes.length;
|
|
29965
31374
|
}
|
|
29966
31375
|
writeVarintU32(w, total);
|
|
29967
31376
|
const base = w.position + offsets.length * 4;
|
|
29968
31377
|
for (const off of offsets) w.u32(base + off);
|
|
29969
|
-
for (const
|
|
31378
|
+
for (const bytes of encoded) w.raw(bytes);
|
|
29970
31379
|
}
|
|
29971
31380
|
function readInternTable(r) {
|
|
29972
31381
|
const count = readVarintU32(r);
|
|
@@ -29986,12 +31395,15 @@ function readInternTable(r) {
|
|
|
29986
31395
|
}
|
|
29987
31396
|
|
|
29988
31397
|
// src/serialization/codec.ts
|
|
29989
|
-
var
|
|
31398
|
+
var MAGIC2 = utf8Encode("PGMM");
|
|
29990
31399
|
var VERSION = 3;
|
|
29991
31400
|
function encodeDatabaseState(state, runtime) {
|
|
29992
31401
|
const pool = new InternPool();
|
|
29993
31402
|
const schemas = [...state.schemas.values()].sort((a, b) => compareNames(a.name, b.name));
|
|
31403
|
+
const internValues = /* @__PURE__ */ new Map();
|
|
29994
31404
|
for (const schema of schemas) {
|
|
31405
|
+
const values = [];
|
|
31406
|
+
internValues.set(schema, values);
|
|
29995
31407
|
pool.count(schema.name);
|
|
29996
31408
|
for (const table of sortedValues(schema.tables)) {
|
|
29997
31409
|
pool.count(table.name);
|
|
@@ -30002,8 +31414,13 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30002
31414
|
pool.count(column.domain ?? "");
|
|
30003
31415
|
if (column.identity) pool.count(column.identity.sequence);
|
|
30004
31416
|
}
|
|
30005
|
-
for (const row of table.
|
|
30006
|
-
for (const d of row)
|
|
31417
|
+
for (const row of table.serializationRows()) {
|
|
31418
|
+
for (const d of row) {
|
|
31419
|
+
if (typeof d === "string") {
|
|
31420
|
+
pool.count(d);
|
|
31421
|
+
values.push(d);
|
|
31422
|
+
} else if (d !== null && typeof d === "object" && !(d instanceof Uint8Array)) values.push(d);
|
|
31423
|
+
}
|
|
30007
31424
|
}
|
|
30008
31425
|
}
|
|
30009
31426
|
for (const view of sortedValues(schema.views)) {
|
|
@@ -30011,7 +31428,12 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30011
31428
|
pool.count(view.schema);
|
|
30012
31429
|
if (view.matRows) {
|
|
30013
31430
|
for (const row of view.matRows) {
|
|
30014
|
-
for (const d of row)
|
|
31431
|
+
for (const d of row) {
|
|
31432
|
+
if (typeof d === "string") {
|
|
31433
|
+
pool.count(d);
|
|
31434
|
+
values.push(d);
|
|
31435
|
+
} else if (d !== null && typeof d === "object" && !(d instanceof Uint8Array)) values.push(d);
|
|
31436
|
+
}
|
|
30015
31437
|
}
|
|
30016
31438
|
}
|
|
30017
31439
|
}
|
|
@@ -30044,7 +31466,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30044
31466
|
}
|
|
30045
31467
|
}
|
|
30046
31468
|
pool.finalize();
|
|
30047
|
-
forceAllEncodeIntern(pool, state, schemas);
|
|
31469
|
+
forceAllEncodeIntern(pool, state, schemas, internValues);
|
|
30048
31470
|
const internId = (s) => pool.id(s);
|
|
30049
31471
|
const forceId = (s) => {
|
|
30050
31472
|
const id = pool.id(s);
|
|
@@ -30052,7 +31474,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30052
31474
|
return id;
|
|
30053
31475
|
};
|
|
30054
31476
|
const w = new Writer(64 * 1024);
|
|
30055
|
-
w.raw(
|
|
31477
|
+
w.raw(MAGIC2);
|
|
30056
31478
|
w.u32(VERSION);
|
|
30057
31479
|
w.u32(state.snapshotOidCounter());
|
|
30058
31480
|
w.u32(state.changes);
|
|
@@ -30067,13 +31489,13 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30067
31489
|
for (const schema of schemas) {
|
|
30068
31490
|
writeVarintU32(w, forceId(schema.name));
|
|
30069
31491
|
w.u32(schema.oid);
|
|
30070
|
-
const
|
|
30071
|
-
writeVarintU32(w,
|
|
30072
|
-
for (const table of
|
|
31492
|
+
const tables2 = sortedValues(schema.tables);
|
|
31493
|
+
writeVarintU32(w, tables2.length);
|
|
31494
|
+
for (const table of tables2) {
|
|
30073
31495
|
writeBjv(
|
|
30074
31496
|
w,
|
|
30075
31497
|
{
|
|
30076
|
-
xmins: table.
|
|
31498
|
+
xmins: table.serializationRows().map((row) => table.rowXmins.get(row) ?? 2),
|
|
30077
31499
|
name: table.name,
|
|
30078
31500
|
schema: table.schema,
|
|
30079
31501
|
columns: table.columns,
|
|
@@ -30086,7 +31508,7 @@ function encodeDatabaseState(state, runtime) {
|
|
|
30086
31508
|
},
|
|
30087
31509
|
forceId
|
|
30088
31510
|
);
|
|
30089
|
-
const rows = table.
|
|
31511
|
+
const rows = table.serializationRows();
|
|
30090
31512
|
writeVarintU32(w, rows.length);
|
|
30091
31513
|
for (let c = 0; c < table.columns.length; c++) writePackedColumn(w, rows, c, internId, forceId);
|
|
30092
31514
|
}
|
|
@@ -30142,7 +31564,7 @@ function decodeDatabaseState(snapshot, prng, clock) {
|
|
|
30142
31564
|
function decodeInner(snapshot, prng, clock) {
|
|
30143
31565
|
const r = new Reader(snapshot);
|
|
30144
31566
|
const magic = r.raw(4);
|
|
30145
|
-
if (!magic.every((byte, index) => byte ===
|
|
31567
|
+
if (!magic.every((byte, index) => byte === MAGIC2[index])) {
|
|
30146
31568
|
throw new PostgresError("snapshot_format", "invalid postgres-mem snapshot magic", "XX000");
|
|
30147
31569
|
}
|
|
30148
31570
|
const version = r.u32();
|
|
@@ -30412,44 +31834,57 @@ function writePackedColumn(w, rows, col, internId, forceId) {
|
|
|
30412
31834
|
}
|
|
30413
31835
|
if (pack === PACK_FLOAT) {
|
|
30414
31836
|
w.align4();
|
|
31837
|
+
const block = w.block((n - nulls) * 8);
|
|
31838
|
+
let offset = 0;
|
|
30415
31839
|
for (let i = 0; i < n; i++) {
|
|
30416
31840
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
30417
|
-
|
|
31841
|
+
block.setFloat64(offset, rows[i][col], true);
|
|
31842
|
+
offset += 8;
|
|
30418
31843
|
}
|
|
30419
31844
|
return;
|
|
30420
31845
|
}
|
|
30421
31846
|
if (pack === PACK_INT) {
|
|
30422
31847
|
w.align4();
|
|
31848
|
+
const block = w.block((n - nulls) * 8);
|
|
31849
|
+
let offset = 0;
|
|
30423
31850
|
for (let i = 0; i < n; i++) {
|
|
30424
31851
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
30425
31852
|
const v = rows[i][col];
|
|
30426
|
-
|
|
31853
|
+
if (typeof v === "bigint") block.setBigInt64(offset, v, true);
|
|
31854
|
+
else {
|
|
31855
|
+
block.setUint32(offset, v >>> 0, true);
|
|
31856
|
+
block.setUint32(offset + 4, Math.floor(v / 4294967296) >>> 0, true);
|
|
31857
|
+
}
|
|
31858
|
+
offset += 8;
|
|
30427
31859
|
}
|
|
30428
31860
|
return;
|
|
30429
31861
|
}
|
|
30430
31862
|
if (pack === PACK_TEXT_INTERN) {
|
|
30431
31863
|
w.align4();
|
|
31864
|
+
const block = w.block((n - nulls) * 4);
|
|
31865
|
+
let offset = 0;
|
|
30432
31866
|
for (let i = 0; i < n; i++) {
|
|
30433
31867
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
30434
|
-
|
|
31868
|
+
block.setUint32(offset, internId(rows[i][col]), true);
|
|
31869
|
+
offset += 4;
|
|
30435
31870
|
}
|
|
30436
31871
|
return;
|
|
30437
31872
|
}
|
|
30438
31873
|
if (pack === PACK_TEXT_INLINE) {
|
|
30439
31874
|
const offsets = [];
|
|
31875
|
+
const encoded = [];
|
|
30440
31876
|
let blobLen = 0;
|
|
30441
31877
|
for (let i = 0; i < n; i++) {
|
|
30442
31878
|
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
30443
31879
|
offsets.push(blobLen);
|
|
30444
|
-
|
|
31880
|
+
const bytes = utf8Encode(rows[i][col]);
|
|
31881
|
+
encoded.push(bytes);
|
|
31882
|
+
blobLen += bytes.length;
|
|
30445
31883
|
}
|
|
30446
31884
|
writeVarintU32(w, offsets.length);
|
|
30447
31885
|
for (const off of offsets) writeVarintU32(w, off);
|
|
30448
31886
|
writeVarintU32(w, blobLen);
|
|
30449
|
-
for (
|
|
30450
|
-
if (bits[i >> 3] & 1 << (i & 7)) continue;
|
|
30451
|
-
w.textBytes(rows[i][col]);
|
|
30452
|
-
}
|
|
31887
|
+
for (const bytes of encoded) w.raw(bytes);
|
|
30453
31888
|
return;
|
|
30454
31889
|
}
|
|
30455
31890
|
if (pack === PACK_BLOB) {
|
|
@@ -30530,10 +31965,10 @@ function writeSchemaCatalog(w, schema, forceId) {
|
|
|
30530
31965
|
const domains = sortedValues(schema.domains);
|
|
30531
31966
|
writeVarintU32(w, domains.length);
|
|
30532
31967
|
for (const d of domains) writeBjv(w, stripRuntimeFields(d), forceId);
|
|
30533
|
-
const functionEntries = [...schema.functions.entries()].map(([
|
|
31968
|
+
const functionEntries = [...schema.functions.entries()].map(([key2, overloads]) => [key2, overloads.filter((f) => f.language !== "js")]).filter((entry) => entry[1].length > 0).sort((a, b) => compareNames(a[0], b[0]));
|
|
30534
31969
|
writeVarintU32(w, functionEntries.length);
|
|
30535
|
-
for (const [
|
|
30536
|
-
writeVarintU32(w, forceId(
|
|
31970
|
+
for (const [key2, overloads] of functionEntries) {
|
|
31971
|
+
writeVarintU32(w, forceId(key2));
|
|
30537
31972
|
writeVarintU32(w, overloads.length);
|
|
30538
31973
|
for (const f of overloads) {
|
|
30539
31974
|
const { jsImpl: _js, ...meta } = f;
|
|
@@ -30596,11 +32031,11 @@ function readSchemaCatalog(r, schema, intern, str) {
|
|
|
30596
32031
|
}
|
|
30597
32032
|
const fnCount = readVarintU32(r);
|
|
30598
32033
|
for (let i = 0; i < fnCount; i++) {
|
|
30599
|
-
const
|
|
32034
|
+
const key2 = str(readVarintU32(r));
|
|
30600
32035
|
const overloadCount = readVarintU32(r);
|
|
30601
32036
|
const overloads = [];
|
|
30602
32037
|
for (let oi = 0; oi < overloadCount; oi++) overloads.push(readBjv(r, intern));
|
|
30603
|
-
schema.functions.set(
|
|
32038
|
+
schema.functions.set(key2, overloads);
|
|
30604
32039
|
}
|
|
30605
32040
|
const indexCount = readVarintU32(r);
|
|
30606
32041
|
for (let i = 0; i < indexCount; i++) {
|
|
@@ -30649,7 +32084,7 @@ function compareNames(a, b) {
|
|
|
30649
32084
|
function sortedValues(map) {
|
|
30650
32085
|
return [...map.values()].sort((a, b) => compareNames(a.name, b.name));
|
|
30651
32086
|
}
|
|
30652
|
-
function forceAllEncodeIntern(pool, state, schemas) {
|
|
32087
|
+
function forceAllEncodeIntern(pool, state, schemas, internValues) {
|
|
30653
32088
|
forceSchemaIntern(pool, state, schemas);
|
|
30654
32089
|
for (const [k, v] of state.settings) {
|
|
30655
32090
|
pool.forceId(k);
|
|
@@ -30661,18 +32096,7 @@ function forceAllEncodeIntern(pool, state, schemas) {
|
|
|
30661
32096
|
}
|
|
30662
32097
|
for (const schema of schemas) {
|
|
30663
32098
|
pool.forceId(schema.name);
|
|
30664
|
-
for (const
|
|
30665
|
-
for (const row of table.allRows()) {
|
|
30666
|
-
for (const d of row) forceDatumIntern(pool, d);
|
|
30667
|
-
}
|
|
30668
|
-
}
|
|
30669
|
-
for (const view of sortedValues(schema.views)) {
|
|
30670
|
-
if (view.matRows) {
|
|
30671
|
-
for (const row of view.matRows) {
|
|
30672
|
-
for (const d of row) forceDatumIntern(pool, d);
|
|
30673
|
-
}
|
|
30674
|
-
}
|
|
30675
|
-
}
|
|
32099
|
+
for (const value of internValues.get(schema) ?? []) forceDatumIntern(pool, value);
|
|
30676
32100
|
}
|
|
30677
32101
|
}
|
|
30678
32102
|
function forceDatumIntern(pool, value) {
|
|
@@ -30717,7 +32141,8 @@ function forceSchemaIntern(pool, state, schemas) {
|
|
|
30717
32141
|
pool.forceId(table.name);
|
|
30718
32142
|
pool.forceId(table.schema);
|
|
30719
32143
|
forceBjvStrings(pool, {
|
|
30720
|
-
|
|
32144
|
+
// Only strings are interned here. Traversing every numeric xmin cannot add an id.
|
|
32145
|
+
xmins: [],
|
|
30721
32146
|
name: table.name,
|
|
30722
32147
|
schema: table.schema,
|
|
30723
32148
|
columns: table.columns,
|
|
@@ -30765,8 +32190,8 @@ function forceSchemaIntern(pool, state, schemas) {
|
|
|
30765
32190
|
pool.forceId(idx.table);
|
|
30766
32191
|
forceBjvStrings(pool, indexBjv(idx));
|
|
30767
32192
|
}
|
|
30768
|
-
for (const [
|
|
30769
|
-
pool.forceId(
|
|
32193
|
+
for (const [key2, overloads] of schema.functions) {
|
|
32194
|
+
pool.forceId(key2);
|
|
30770
32195
|
for (const fn of overloads) {
|
|
30771
32196
|
if (fn.language === "js") continue;
|
|
30772
32197
|
const { jsImpl: _js, ...meta } = fn;
|
|
@@ -30796,6 +32221,265 @@ function forceBjvStrings(pool, value) {
|
|
|
30796
32221
|
}
|
|
30797
32222
|
registerCodec({ encodeDatabaseState, decodeDatabaseState });
|
|
30798
32223
|
|
|
32224
|
+
// src/api/buffered.ts
|
|
32225
|
+
var key = (schema, name) => JSON.stringify([schema, name]);
|
|
32226
|
+
var same = (a, b) => a.length === b.length && a.every((byte, index) => byte === b[index]);
|
|
32227
|
+
function tables(state) {
|
|
32228
|
+
return new Map(
|
|
32229
|
+
[...state.schemas.values()].flatMap(
|
|
32230
|
+
(schema) => [...schema.tables.values()].map((table) => [key(schema.name, table.name), table])
|
|
32231
|
+
)
|
|
32232
|
+
);
|
|
32233
|
+
}
|
|
32234
|
+
function extra(state) {
|
|
32235
|
+
return { xidCounter: state.xidCounter, sequenceCurrval: [...state.sequenceCurrval], prepared: [...state.prepared] };
|
|
32236
|
+
}
|
|
32237
|
+
function encodeBufferedCheckpoint(db) {
|
|
32238
|
+
const snap = captureSnapshot(db.engineState, db.prng, db.now, db.seed, db.randomMode, db.systemClock, db.int8Mode);
|
|
32239
|
+
const info = [...tables(snap.state).values()].map((table) => ({
|
|
32240
|
+
schema: table.schema,
|
|
32241
|
+
name: table.name,
|
|
32242
|
+
ids: Array.from({ length: table.rowCount() }, (_, id) => id),
|
|
32243
|
+
deleted: [],
|
|
32244
|
+
nextId: table.rowCount()
|
|
32245
|
+
}));
|
|
32246
|
+
return encodeDurableCheckpoint(
|
|
32247
|
+
packDurableRecord({ ...extra(snap.state), checkpoint: true, tables: info }, snap.encode())
|
|
32248
|
+
);
|
|
32249
|
+
}
|
|
32250
|
+
function decodeBufferedDatabase(bytes, options) {
|
|
32251
|
+
const records = readDurableJournal(bytes);
|
|
32252
|
+
const db = records ? recover(records, options) : Snapshot.decode(bytes).open(options);
|
|
32253
|
+
for (const table of tables(db.engineState).values()) table.disableDurabilityTracking();
|
|
32254
|
+
return db;
|
|
32255
|
+
}
|
|
32256
|
+
function recover(records, options) {
|
|
32257
|
+
let db;
|
|
32258
|
+
const rows = /* @__PURE__ */ new Map();
|
|
32259
|
+
try {
|
|
32260
|
+
for (const bytes of records) {
|
|
32261
|
+
const { metadata, snapshot } = unpackDurableRecord(bytes);
|
|
32262
|
+
const next = Snapshot.decode(snapshot).open(options);
|
|
32263
|
+
next.engineState.xidCounter = metadata.xidCounter;
|
|
32264
|
+
next.engineState.sequenceCurrval = new Map(metadata.sequenceCurrval);
|
|
32265
|
+
next.engineState.prepared = new Map(metadata.prepared);
|
|
32266
|
+
if (metadata.checkpoint) rows.clear();
|
|
32267
|
+
else if (!db) throw new Error("Missing buffered checkpoint");
|
|
32268
|
+
const changes = tables(next.engineState);
|
|
32269
|
+
for (const info of metadata.tables) {
|
|
32270
|
+
const name = key(info.schema, info.name);
|
|
32271
|
+
const table = changes.get(name);
|
|
32272
|
+
if (!table || table.rowCount() !== info.ids.length) throw new Error("Invalid incremental row IDs");
|
|
32273
|
+
let target = rows.get(name);
|
|
32274
|
+
if (!target) {
|
|
32275
|
+
target = { rows: /* @__PURE__ */ new Map(), nextId: 0 };
|
|
32276
|
+
rows.set(name, target);
|
|
32277
|
+
}
|
|
32278
|
+
target.nextId = info.nextId;
|
|
32279
|
+
for (const id of info.deleted) target.rows.delete(id);
|
|
32280
|
+
table.materializeSlab();
|
|
32281
|
+
for (let i = 0; i < info.ids.length; i++) {
|
|
32282
|
+
const row = table.rowAt(i);
|
|
32283
|
+
target.rows.set(info.ids[i], { row, xmin: table.rowXmins.get(row) ?? 2 });
|
|
32284
|
+
}
|
|
32285
|
+
}
|
|
32286
|
+
db?.close();
|
|
32287
|
+
db = next;
|
|
32288
|
+
}
|
|
32289
|
+
if (!db) throw new Error("Missing buffered checkpoint");
|
|
32290
|
+
for (const [name, table] of tables(db.engineState)) {
|
|
32291
|
+
const stored = rows.get(name);
|
|
32292
|
+
if (!stored) throw new Error("Missing buffered table");
|
|
32293
|
+
const ordered = [...stored.rows].sort((a, b) => a[0] - b[0]);
|
|
32294
|
+
table.rows = ordered.map(([, value]) => value.row);
|
|
32295
|
+
table.rowXmins = new WeakMap(ordered.map(([, value]) => [value.row, value.xmin]));
|
|
32296
|
+
table.indexStores = null;
|
|
32297
|
+
table.enableDurabilityTracking(
|
|
32298
|
+
ordered.map(([id]) => id),
|
|
32299
|
+
stored.nextId
|
|
32300
|
+
);
|
|
32301
|
+
}
|
|
32302
|
+
const snap = captureSnapshot(db.engineState, db.prng, db.now, db.seed, db.randomMode, db.systemClock, db.int8Mode);
|
|
32303
|
+
const result = snap.open(options);
|
|
32304
|
+
for (const [name, table] of tables(result.engineState)) {
|
|
32305
|
+
const stored = rows.get(name);
|
|
32306
|
+
const ids = [...stored.rows.keys()].sort((a, b) => a - b);
|
|
32307
|
+
table.enableDurabilityTracking(ids, stored.nextId);
|
|
32308
|
+
}
|
|
32309
|
+
db.close();
|
|
32310
|
+
return result;
|
|
32311
|
+
} catch (error) {
|
|
32312
|
+
db?.close();
|
|
32313
|
+
throw error;
|
|
32314
|
+
}
|
|
32315
|
+
}
|
|
32316
|
+
function codec2(options, closeBackend) {
|
|
32317
|
+
let exposed = false;
|
|
32318
|
+
function capture(db, previous, checkpoint, baselineOnly = false) {
|
|
32319
|
+
db.assertOpen();
|
|
32320
|
+
if (db.transactions.inTransaction)
|
|
32321
|
+
throw pgError("transaction_state", "cannot persist during a transaction", "25P01");
|
|
32322
|
+
const currentTables = tables(db.engineState);
|
|
32323
|
+
const shape = packDurableRecord(
|
|
32324
|
+
[...currentTables].map(([name, table]) => [name, table.oid, table.columns, table.constraints, table.triggers]),
|
|
32325
|
+
new Uint8Array()
|
|
32326
|
+
);
|
|
32327
|
+
checkpoint ||= exposed || !previous || !same(shape, previous.shape) || [...currentTables].some(
|
|
32328
|
+
([name, table]) => table.durableUnsafe || !table.durableChanges || table.durableEpoch !== previous?.tables.get(name)?.durableEpoch
|
|
32329
|
+
);
|
|
32330
|
+
for (const table of currentTables.values()) table.enableDurabilityTracking();
|
|
32331
|
+
const snap = captureSnapshot(db.engineState, db.prng, db.now, db.seed, db.randomMode, db.systemClock, db.int8Mode);
|
|
32332
|
+
const capturedTables = tables(snap.state);
|
|
32333
|
+
const metadataState = snap.state.cloneShallow();
|
|
32334
|
+
for (const schema of metadataState.schemas.values())
|
|
32335
|
+
schema.tables = new Map([...schema.tables].map(([name, table]) => [name, table.emptyForDurability()]));
|
|
32336
|
+
const runtime = { prngState: snap.prngState, nowMs: snap.nowMs };
|
|
32337
|
+
const extras = extra(snap.state);
|
|
32338
|
+
const metadata = packDurableRecord(extras, encodeDatabaseState(metadataState, runtime));
|
|
32339
|
+
const token = {
|
|
32340
|
+
metadata,
|
|
32341
|
+
shape,
|
|
32342
|
+
tables: new Map(
|
|
32343
|
+
[...capturedTables].map(([name, table]) => [
|
|
32344
|
+
name,
|
|
32345
|
+
{
|
|
32346
|
+
durableChanges: table.durableChanges.fork(),
|
|
32347
|
+
durableEpoch: table.durableEpoch
|
|
32348
|
+
}
|
|
32349
|
+
])
|
|
32350
|
+
)
|
|
32351
|
+
};
|
|
32352
|
+
if (baselineOnly) return { token, checkpoint: true };
|
|
32353
|
+
const info = [];
|
|
32354
|
+
let changed = false;
|
|
32355
|
+
for (const [name, table] of capturedTables) {
|
|
32356
|
+
const ids = [];
|
|
32357
|
+
const deleted = [];
|
|
32358
|
+
if (checkpoint) {
|
|
32359
|
+
for (let i = 0; i < table.rowCount(); i++) ids.push(table.durableRowId(table.rowAt(i)));
|
|
32360
|
+
} else {
|
|
32361
|
+
const destination = metadataState.schemas.get(table.schema).tables.get(table.name);
|
|
32362
|
+
const destinationRows = destination.writeRows();
|
|
32363
|
+
for (const change of table.durableChanges.changes(previous?.tables.get(name)?.durableChanges ?? void 0)) {
|
|
32364
|
+
if (change.deleted) continue;
|
|
32365
|
+
changed = true;
|
|
32366
|
+
if (change.value.row) {
|
|
32367
|
+
ids.push(change.key);
|
|
32368
|
+
destinationRows.push(change.value.row);
|
|
32369
|
+
destination.rowXmins.set(change.value.row, change.value.xmin);
|
|
32370
|
+
} else deleted.push(change.key);
|
|
32371
|
+
}
|
|
32372
|
+
}
|
|
32373
|
+
info.push({ schema: table.schema, name: table.name, ids, deleted, nextId: table.durableNextId });
|
|
32374
|
+
}
|
|
32375
|
+
if (!checkpoint && !changed && previous && same(metadata, previous.metadata)) return { token, checkpoint: false };
|
|
32376
|
+
return {
|
|
32377
|
+
token,
|
|
32378
|
+
checkpoint,
|
|
32379
|
+
bytes: packDurableRecord(
|
|
32380
|
+
{ ...extras, checkpoint, tables: info },
|
|
32381
|
+
checkpoint ? snap.encode() : encodeDatabaseState(metadataState, runtime)
|
|
32382
|
+
)
|
|
32383
|
+
};
|
|
32384
|
+
}
|
|
32385
|
+
return {
|
|
32386
|
+
create: () => new Database(options.databaseOptions),
|
|
32387
|
+
decode: (bytes) => decodeBufferedDatabase(bytes, options.databaseOptions),
|
|
32388
|
+
decodeJournal: (records) => recover(records, options.databaseOptions),
|
|
32389
|
+
encode: (db) => captureSnapshot(db.engineState, db.prng, db.now, db.seed, db.randomMode, db.systemClock, db.int8Mode).encode(),
|
|
32390
|
+
canCapture: (db) => !db.transactions.inTransaction,
|
|
32391
|
+
capture,
|
|
32392
|
+
baseline: (db) => capture(db, void 0, true, true).token,
|
|
32393
|
+
acknowledge(db, token) {
|
|
32394
|
+
const liveTables = tables(db.engineState);
|
|
32395
|
+
for (const [name, table] of token.tables) {
|
|
32396
|
+
const live = liveTables.get(name);
|
|
32397
|
+
if (!live?.durableChanges || !table.durableChanges || live.durableEpoch !== table.durableEpoch) continue;
|
|
32398
|
+
for (const [id, row] of table.durableChanges)
|
|
32399
|
+
if (live.durableChanges.get(id) === row) live.durableChanges.delete(id);
|
|
32400
|
+
}
|
|
32401
|
+
},
|
|
32402
|
+
subscribe(db, observer) {
|
|
32403
|
+
const descriptor = Object.getOwnPropertyDescriptor(db, "state");
|
|
32404
|
+
db.durabilityObserver = observer;
|
|
32405
|
+
Object.defineProperty(db, "state", {
|
|
32406
|
+
enumerable: true,
|
|
32407
|
+
configurable: true,
|
|
32408
|
+
get() {
|
|
32409
|
+
exposed = true;
|
|
32410
|
+
observer.exposed();
|
|
32411
|
+
return db.engineState;
|
|
32412
|
+
}
|
|
32413
|
+
});
|
|
32414
|
+
return () => {
|
|
32415
|
+
db.durabilityObserver = void 0;
|
|
32416
|
+
Object.defineProperty(db, "state", descriptor);
|
|
32417
|
+
};
|
|
32418
|
+
},
|
|
32419
|
+
async close(db) {
|
|
32420
|
+
db.close();
|
|
32421
|
+
await closeBackend();
|
|
32422
|
+
}
|
|
32423
|
+
};
|
|
32424
|
+
}
|
|
32425
|
+
async function openBufferedDurableDatabase(options) {
|
|
32426
|
+
const backend = options.backend ?? new IndexedDBDurabilityBackend({ durability: "relaxed" });
|
|
32427
|
+
const closeBackend = async () => {
|
|
32428
|
+
if (!options.backend && backend instanceof IndexedDBDurabilityBackend) await backend.close();
|
|
32429
|
+
};
|
|
32430
|
+
try {
|
|
32431
|
+
return await openBufferedDurable({
|
|
32432
|
+
...options,
|
|
32433
|
+
backend,
|
|
32434
|
+
lifecycle: options.lifecycle ?? browserDurabilityLifecycle,
|
|
32435
|
+
codec: codec2(options, closeBackend)
|
|
32436
|
+
});
|
|
32437
|
+
} catch (error) {
|
|
32438
|
+
await closeBackend();
|
|
32439
|
+
throw error;
|
|
32440
|
+
}
|
|
32441
|
+
}
|
|
32442
|
+
|
|
32443
|
+
// src/api/durable.ts
|
|
32444
|
+
function openDurableDatabase(options) {
|
|
32445
|
+
const backend = options.backend ?? new IndexedDBDurabilityBackend();
|
|
32446
|
+
let journal = false;
|
|
32447
|
+
return openDurable({
|
|
32448
|
+
key: options.key,
|
|
32449
|
+
backend,
|
|
32450
|
+
pageSize: options.pageSize,
|
|
32451
|
+
codec: {
|
|
32452
|
+
create: () => new Database(options.databaseOptions),
|
|
32453
|
+
decode: (bytes) => {
|
|
32454
|
+
journal = isDurableJournal(bytes);
|
|
32455
|
+
return decodeBufferedDatabase(bytes, options.databaseOptions);
|
|
32456
|
+
},
|
|
32457
|
+
encode: (db) => {
|
|
32458
|
+
db.assertOpen();
|
|
32459
|
+
if (db.transactions.inTransaction)
|
|
32460
|
+
throw pgError("transaction_state", "cannot persist during a transaction", "25P01");
|
|
32461
|
+
if (journal) return encodeBufferedCheckpoint(db);
|
|
32462
|
+
return captureSnapshot(
|
|
32463
|
+
db.engineState,
|
|
32464
|
+
db.prng,
|
|
32465
|
+
db.now,
|
|
32466
|
+
db.seed,
|
|
32467
|
+
db.randomMode,
|
|
32468
|
+
db.systemClock,
|
|
32469
|
+
db.int8Mode
|
|
32470
|
+
).encode();
|
|
32471
|
+
},
|
|
32472
|
+
close: async (db) => {
|
|
32473
|
+
db.close();
|
|
32474
|
+
if (!options.backend && backend instanceof IndexedDBDurabilityBackend) await backend.close();
|
|
32475
|
+
}
|
|
32476
|
+
}
|
|
32477
|
+
}).catch(async (error) => {
|
|
32478
|
+
if (!options.backend && backend instanceof IndexedDBDurabilityBackend) await backend.close();
|
|
32479
|
+
throw error;
|
|
32480
|
+
});
|
|
32481
|
+
}
|
|
32482
|
+
|
|
30799
32483
|
// src/api/schema-report.ts
|
|
30800
32484
|
var bounded = (value, fallback2, max) => {
|
|
30801
32485
|
if (value === void 0) return fallback2;
|
|
@@ -30809,7 +32493,7 @@ function analyzeSchema(sql, options = {}) {
|
|
|
30809
32493
|
const diagnosticLimit = bounded(options.maxDiagnostics, 100, 1e3);
|
|
30810
32494
|
const report = {
|
|
30811
32495
|
engine: "mockingbird-postgres",
|
|
30812
|
-
version: true ? "3.
|
|
32496
|
+
version: true ? "3.12.1" : "source-tree",
|
|
30813
32497
|
statements: 0,
|
|
30814
32498
|
accepted: 0,
|
|
30815
32499
|
compatible: null,
|
|
@@ -30974,9 +32658,23 @@ function analyzeSchema(sql, options = {}) {
|
|
|
30974
32658
|
}
|
|
30975
32659
|
export {
|
|
30976
32660
|
Database,
|
|
32661
|
+
DurabilityConflictError,
|
|
32662
|
+
IndexedDBDurabilityBackend,
|
|
32663
|
+
LocalStorageDurabilityBackend,
|
|
32664
|
+
MemoryDurabilityBackend,
|
|
30977
32665
|
PostgresError,
|
|
30978
32666
|
Snapshot,
|
|
30979
32667
|
Statement,
|
|
30980
|
-
analyzeSchema
|
|
32668
|
+
analyzeSchema,
|
|
32669
|
+
browserDurabilityLifecycle,
|
|
32670
|
+
encodeDurableCheckpoint,
|
|
32671
|
+
openBufferedDurable,
|
|
32672
|
+
openBufferedDurableDatabase,
|
|
32673
|
+
openDurable,
|
|
32674
|
+
openDurableDatabase,
|
|
32675
|
+
packDurableRecord,
|
|
32676
|
+
readDurableJournal,
|
|
32677
|
+
unpackDurableRecord,
|
|
32678
|
+
withDurability
|
|
30981
32679
|
};
|
|
30982
32680
|
//# sourceMappingURL=index.js.map
|