@minnowdb/core 0.7.1 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/database.d.ts +6 -0
- package/dist/engine/database.js +391 -93
- package/dist/engine/optimizer.js +168 -87
- package/dist/engine/query.d.ts +98 -0
- package/dist/engine/query.js +1457 -162
- package/dist/engine/sql-domains.d.ts +11 -0
- package/dist/engine/sql-domains.js +56 -0
- package/dist/engine/sql-functions.js +129 -3
- package/dist/engine/sql-json.d.ts +2 -0
- package/dist/engine/sql-json.js +4 -0
- package/dist/engine/sql-semantics.d.ts +9 -1
- package/dist/engine/sql-semantics.js +19 -3
- package/dist/engine/vector.js +29 -13
- package/dist/plan/model.d.ts +14 -2
- package/dist/storage/indexeddb.js +9 -1
- package/dist/storage/memory.d.ts +2 -1
- package/dist/storage/memory.js +29 -4
- package/dist/storage/toolkit/record-core.js +15 -3
- package/dist/testing/sqllogictest.js +3 -1
- package/package.json +1 -1
- package/postgres-feature-profile.json +9 -4
- package/sql-feature-matrix.json +543 -6
package/dist/engine/database.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { crossJoinPlan } from "../plan/model.js";
|
|
1
2
|
import { toColumnarBatch } from "./batch.js";
|
|
2
3
|
import { ArtifactCache } from "./artifact-cache.js";
|
|
3
4
|
import { estimateBatchBytes, estimateRowBytes, estimateValuesBytes } from "./byte-estimates.js";
|
|
@@ -15,13 +16,13 @@ import { cachedQueryTerms, FTS_TOKENIZER_VERSION, renderDocumentValue, tokenize
|
|
|
15
16
|
import { boundedMaintenanceBatchItems, simpleDataTypes, floorWholeNumberProduct, BlockReadBatchTooLargeError, validateColumnDefault, validateEnumValues, secondaryIndexColumnIds, secondaryIndexDirections, secondaryUniqueKeyNamespace, CompactionJobConflictError, CompactionBacklogError, GarbageCollectionJobConflictError, MAX_LEVEL_ZERO_SEGMENTS, MAX_FTS_CANDIDATE_ROW_IDS, MAX_FTS_POSTING_TERM_CHARACTERS, MAX_FTS_POSTINGS_PER_CHUNK, MAX_FTS_POSTING_ROW_IDS_PER_CHUNK, MAX_INDEXED_STRING_CHARACTERS, MAX_CATALOG_NAME_CHARACTERS, MAX_MANIFEST_BLOCK_PRESENCE_IDS, MAX_MAINTENANCE_BATCH_ITEMS, MAX_STORAGE_BULK_READ_ITEMS, MAX_TRANSACTION_STAGE_BLOCKS, MAX_TRANSACTION_STAGE_BYTES, MAX_SNAPSHOT_SESSION_TTL_MS, MAX_POSTING_BUILD_TTL_MS, MAX_TEMP_OWNER_TTL_MS, MAX_SNAPSHOT_FRAME_BATCH_BYTES, MAX_SNAPSHOT_FRAME_BATCH_ITEMS, MAX_SNAPSHOT_METADATA_BATCH_BYTES, SNAPSHOT_FRAME_KINDS, validateStorageId, SnapshotManifestMissingError, SchemaConflictError, TableInUseError, TableRecordConflictError, TransactionRecordConflictError, UniqueKeyConflictError, UniqueIndexCoverageError, WriteConflictError } from "../storage/types.js";
|
|
16
17
|
import { decodeSnapshotFrameStream, encodeSnapshotFrameStreamFooter, encodeSnapshotFrameStreamHeader, extendSnapshotFrameStreamChecksum, snapshotFrameEnvelopeParts, snapshotFrameStreamHeaderIdentity } from "../storage/snapshot.js";
|
|
17
18
|
import { Snapshot, TransactionManager } from "../transactions/index.js";
|
|
18
|
-
import { applyWindowFunctions, bindPendingSelectShapes, bindPlanParameters, bindStatementParameters, DUAL_TABLE, dualTableRows, blockHasSubqueries, combineUnionResults, compileCheckExpression, compileQuery, hasAggregate, createRecursiveCteState, annotateAvgArgumentScales, compileStatement, comparisonHolds, createPreparedColumnarQuery, evaluateJoinedRowExpression, evaluateRowExpression, externalizeQueryResult, expressionColumnNames, inferBlockSchema, inferResultColumnDomains, isDeferredInsertExpression, isDefaultInsertValue, referencedColumns, childExpressions, expandFtsColumns, expandNaturalJoins, expandSourceColumnAliases, expandViewSources, forEachBlockExpression, forEachNestedBlock, planContainsFts, planHasNaturalJoins, planHasSourceColumnAliases, planReadsTable, planReadsViews, planReadsBeyondSingleScan, projectResultColumns, queryResultNeedsExternalization, resolveStatementDatetimes, subqueryResolutionSteps, topLevelFtsMatchConjuncts, transparentProjectionSource, unknownColumnDomains, validateDefaultExpression, windowOutputDomain, windowOutputType, volatileScalarFunctionNames } from "./query.js";
|
|
19
|
+
import { applyWindowFunctions, bindPendingSelectShapes, bindPlanParameters, bindStatementParameters, DUAL_TABLE, dualTableRows, blockHasSubqueries, combineUnionResults, compileCheckExpression, compileQuery, hasAggregate, createRecursiveCteState, annotateAvgArgumentScales, compileStatement, comparisonHolds, createPreparedColumnarQuery, evaluateJoinedRowExpression, evaluateRowExpression, externalizeQueryResult, expressionColumnNames, inferBlockSchema, inferResultColumnDomains, isDeferredInsertExpression, isDefaultInsertValue, referencedColumns, childExpressions, expandFtsColumns, expandNaturalJoins, expandSourceColumnAliases, expandViewSources, forEachBlockExpression, forEachNestedBlock, planContainsFts, planHasNaturalJoins, planHasSourceColumnAliases, planReadsTable, planReadsViews, planReadsBeyondSingleScan, projectResultColumns, queryResultNeedsExternalization, resolveStatementDatetimes, subqueryResolutionSteps, topLevelFtsMatchConjuncts, transparentProjectionSource, unknownColumnDomains, validateDefaultExpression, windowOutputDomain, windowOutputType, volatileScalarFunctionNames, annotatePlanIntegerDivision, clonePlanTree, planMayHaveIntegerDivision, foldIdentifierCase, extendGroupByWithKeyDependents, expandRowReferences } from "./query.js";
|
|
19
20
|
import { copyQueryResult, planMemoKey, queryResultMemoKey, queryResultRetainedBytes, RESULT_MEMO_MAX_BYTES } from "./query-cache.js";
|
|
20
21
|
import { QueryMemoryBudgetError, QueryMemoryContext, DEFAULT_QUERY_MEMORY_BUDGET_BYTES } from "./memory.js";
|
|
21
22
|
import { LiveQueryLimitError, LiveQuerySet, MAX_LIVE_QUERY_SETS_PER_DATABASE } from "./live.js";
|
|
22
23
|
import { chooseJoinOrder, optimizePlan, qualifyCorrelatedReferences, renderPlan } from "./optimizer.js";
|
|
23
|
-
import { encodeSqlEqualityValue,
|
|
24
|
-
import { exactNumericAsNumber, externalSqlDomainValue, externalSqlTextValue, isDateDomainValue, isExactNumeric, isSqlDomainValue, normalizeSqlDomainValue, protectedSqlTextValue } from "./sql-domains.js";
|
|
24
|
+
import { encodeSqlEqualityValue, readUntypedText } from "./sql-semantics.js";
|
|
25
|
+
import { exactNumericAsNumber, exactNumericValue, externalSqlDomainValue, externalSqlTextValue, isDateDomainValue, isExactNumeric, isSqlDomainValue, normalizeSqlDomainValue, protectedSqlTextValue } from "./sql-domains.js";
|
|
25
26
|
import { toCatalog } from "./catalog.js";
|
|
26
27
|
import { applyColumnSteps, assertColumnDroppable, compileGeneratedColumnExpression, declaredForeignKeys, isDestructiveStep, planMigration } from "./schema.js";
|
|
27
28
|
import { columnarTableFromRows, createColumnarTable, vectorValue } from "./vector.js";
|
|
@@ -77,6 +78,9 @@ const AUTO_COLLECT_RETRY_MIN_MS = 1e3;
|
|
|
77
78
|
const AUTO_COLLECT_RETRY_MAX_MS = 6e4;
|
|
78
79
|
const AUTO_COMPACT_STEP_BLOCKS = 4;
|
|
79
80
|
const AUTO_COMPACT_MAX_LEVEL_ZERO_SEGMENTS = 256;
|
|
81
|
+
const AUTO_COMPACT_RETRY_MIN_MS = 250;
|
|
82
|
+
const AUTO_COMPACT_RETRY_MAX_MS = 6e4;
|
|
83
|
+
const MAX_COMPACTION_PUBLICATION_CONFLICTS = 64;
|
|
80
84
|
const ZONE_DESCRIPTION_CACHE_BYTES = 160;
|
|
81
85
|
const STAGED_OVERLAY_ORDER_BASE = 2 ** 52;
|
|
82
86
|
class StaleTriggerDerivationsError extends Error {
|
|
@@ -202,6 +206,7 @@ function projectReturningItems(table, alias, items, rows, facts) {
|
|
|
202
206
|
having: [],
|
|
203
207
|
orderBy: []
|
|
204
208
|
}, facts.domains, facts.types);
|
|
209
|
+
annotatePlanIntegerDivision(plan, (name) => facts.integers.get(name));
|
|
205
210
|
const visible = visibleTableColumns(table);
|
|
206
211
|
const columns = new Map(visible.map((column) => [
|
|
207
212
|
column.name,
|
|
@@ -338,6 +343,18 @@ function resolveMutationStatementDatetimes(statement, now) {
|
|
|
338
343
|
}
|
|
339
344
|
};
|
|
340
345
|
}
|
|
346
|
+
const sources = (from) => from === void 0 ? {} : {
|
|
347
|
+
from: {
|
|
348
|
+
...from,
|
|
349
|
+
predicates: predicates(from.predicates),
|
|
350
|
+
joins: from.joins.map((join) => ({
|
|
351
|
+
...join,
|
|
352
|
+
left: expression(join.left),
|
|
353
|
+
right: expression(join.right),
|
|
354
|
+
...join.on === void 0 ? {} : { on: expression(join.on) }
|
|
355
|
+
}))
|
|
356
|
+
}
|
|
357
|
+
};
|
|
341
358
|
if (statement.kind === "update") {
|
|
342
359
|
return {
|
|
343
360
|
...statement,
|
|
@@ -345,11 +362,16 @@ function resolveMutationStatementDatetimes(statement, now) {
|
|
|
345
362
|
...assignment,
|
|
346
363
|
expression: expression(assignment.expression)
|
|
347
364
|
})),
|
|
348
|
-
predicates: predicates(statement.predicates)
|
|
365
|
+
predicates: predicates(statement.predicates),
|
|
366
|
+
...sources(statement.from)
|
|
349
367
|
};
|
|
350
368
|
}
|
|
351
369
|
if (statement.kind === "delete") {
|
|
352
|
-
return {
|
|
370
|
+
return {
|
|
371
|
+
...statement,
|
|
372
|
+
predicates: predicates(statement.predicates),
|
|
373
|
+
...sources(statement.from)
|
|
374
|
+
};
|
|
353
375
|
}
|
|
354
376
|
if (statement.kind === "merge") {
|
|
355
377
|
return {
|
|
@@ -580,6 +602,9 @@ class MinnowDatabase {
|
|
|
580
602
|
#commitsSinceCompactionCheck = /* @__PURE__ */ new Map();
|
|
581
603
|
#compactionSteps = /* @__PURE__ */ new Map();
|
|
582
604
|
#writeChain = Promise.resolve();
|
|
605
|
+
#pendingCompactionPublications = /* @__PURE__ */ new Set();
|
|
606
|
+
#commitSignal;
|
|
607
|
+
#pendingEventLoopTurn;
|
|
583
608
|
#pendingWriteReservations = 0;
|
|
584
609
|
#activeReadReservations = 0;
|
|
585
610
|
#autoCollectionTask;
|
|
@@ -695,6 +720,10 @@ class MinnowDatabase {
|
|
|
695
720
|
this.#gzipVerdicts.clear();
|
|
696
721
|
this.#autoCompactionsRequested.clear();
|
|
697
722
|
this.#idleCompactionTableIds.clear();
|
|
723
|
+
for (const backoff of this.#autoCompactionBackoff.values()) {
|
|
724
|
+
if (backoff.retryTimer !== void 0)
|
|
725
|
+
clearTimeout(backoff.retryTimer);
|
|
726
|
+
}
|
|
698
727
|
this.#autoCompactionBackoff.clear();
|
|
699
728
|
this.#autoCompactionHints.clear();
|
|
700
729
|
this.#commitsSinceCompactionCheck.clear();
|
|
@@ -980,6 +1009,7 @@ class MinnowDatabase {
|
|
|
980
1009
|
}
|
|
981
1010
|
async #runWrite(run) {
|
|
982
1011
|
const restarting = async () => {
|
|
1012
|
+
await this.#publishPendingCompactions();
|
|
983
1013
|
await this.#assistAutomaticCollection();
|
|
984
1014
|
for (let attempt = 0; ; attempt += 1) {
|
|
985
1015
|
try {
|
|
@@ -1012,6 +1042,57 @@ class MinnowDatabase {
|
|
|
1012
1042
|
this.#writeChain = Promise.resolve();
|
|
1013
1043
|
}
|
|
1014
1044
|
}
|
|
1045
|
+
async #withCompactionPublicationSlot(attempt) {
|
|
1046
|
+
return new Promise((resolve, reject) => {
|
|
1047
|
+
let settled;
|
|
1048
|
+
const claim = () => {
|
|
1049
|
+
settled ??= (async () => {
|
|
1050
|
+
try {
|
|
1051
|
+
resolve(await attempt());
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1054
|
+
} finally {
|
|
1055
|
+
this.#pendingCompactionPublications.delete(claim);
|
|
1056
|
+
}
|
|
1057
|
+
})();
|
|
1058
|
+
return settled;
|
|
1059
|
+
};
|
|
1060
|
+
this.#pendingCompactionPublications.add(claim);
|
|
1061
|
+
void this.#eventLoopTurn().then(claim);
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
async #publishPendingCompactions() {
|
|
1065
|
+
while (this.#pendingCompactionPublications.size > 0) {
|
|
1066
|
+
const [claim] = this.#pendingCompactionPublications;
|
|
1067
|
+
if (claim === void 0)
|
|
1068
|
+
return;
|
|
1069
|
+
await claim();
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
#eventLoopTurn() {
|
|
1073
|
+
const pending = this.#pendingEventLoopTurn;
|
|
1074
|
+
if (pending !== void 0)
|
|
1075
|
+
return pending;
|
|
1076
|
+
const turn = yieldToEventLoop().then(() => {
|
|
1077
|
+
if (this.#pendingEventLoopTurn === turn)
|
|
1078
|
+
this.#pendingEventLoopTurn = void 0;
|
|
1079
|
+
});
|
|
1080
|
+
this.#pendingEventLoopTurn = turn;
|
|
1081
|
+
return turn;
|
|
1082
|
+
}
|
|
1083
|
+
#nextLocalCommit() {
|
|
1084
|
+
if (this.#commitSignal === void 0) {
|
|
1085
|
+
let resolve = () => void 0;
|
|
1086
|
+
const promise = new Promise((settle) => {
|
|
1087
|
+
resolve = settle;
|
|
1088
|
+
});
|
|
1089
|
+
this.#commitSignal = { promise, resolve };
|
|
1090
|
+
}
|
|
1091
|
+
return this.#commitSignal.promise;
|
|
1092
|
+
}
|
|
1093
|
+
async #yieldMaintenance() {
|
|
1094
|
+
await Promise.race([this.#eventLoopTurn(), this.#nextLocalCommit()]);
|
|
1095
|
+
}
|
|
1015
1096
|
async #withWriteReservation(run) {
|
|
1016
1097
|
if (this.#pendingWriteReservations >= MAX_DATABASE_PENDING_WRITES) {
|
|
1017
1098
|
throw new RangeError(`A database cannot queue more than ${String(MAX_DATABASE_PENDING_WRITES)} writes; await a write for backpressure`);
|
|
@@ -2492,7 +2573,7 @@ class MinnowDatabase {
|
|
|
2492
2573
|
catalogEpoch: after.catalogEpoch
|
|
2493
2574
|
};
|
|
2494
2575
|
}
|
|
2495
|
-
await
|
|
2576
|
+
await this.#yieldMaintenance();
|
|
2496
2577
|
}
|
|
2497
2578
|
}
|
|
2498
2579
|
async #acquireSharedLease(version) {
|
|
@@ -2667,7 +2748,15 @@ class MinnowDatabase {
|
|
|
2667
2748
|
}
|
|
2668
2749
|
memberSchema.forEach((column, index) => {
|
|
2669
2750
|
const expected = schema2?.[index];
|
|
2670
|
-
if (expected
|
|
2751
|
+
if (expected === void 0)
|
|
2752
|
+
return;
|
|
2753
|
+
if (column.type === expected.type && column.sqlDomain?.kind === "numeric" && expected.sqlDomain?.kind === "numeric") {
|
|
2754
|
+
if (JSON.stringify(column.sqlDomain) !== JSON.stringify(expected.sqlDomain)) {
|
|
2755
|
+
schema2 = schema2?.map((entry, position) => position === index ? { ...entry, sqlDomain: { kind: "numeric" } } : entry);
|
|
2756
|
+
}
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
if (column.type !== expected.type || JSON.stringify(column.sqlDomain ?? null) !== JSON.stringify(expected.sqlDomain ?? null)) {
|
|
2671
2760
|
throw new TypeError(`UNION member column types must match: ${expected.name}`);
|
|
2672
2761
|
}
|
|
2673
2762
|
});
|
|
@@ -2782,7 +2871,13 @@ class MinnowDatabase {
|
|
|
2782
2871
|
if (template !== null) {
|
|
2783
2872
|
const shape = resolvePointReadShape(template, options.params ?? []);
|
|
2784
2873
|
if (shape !== void 0) {
|
|
2785
|
-
|
|
2874
|
+
let point;
|
|
2875
|
+
try {
|
|
2876
|
+
point = await this.#pointReadResult(shape, options);
|
|
2877
|
+
} catch (error) {
|
|
2878
|
+
if (!(error instanceof UnknownTableError))
|
|
2879
|
+
throw error;
|
|
2880
|
+
}
|
|
2786
2881
|
throwIfAborted(options.signal);
|
|
2787
2882
|
if (point !== void 0)
|
|
2788
2883
|
return externalizeQueryResult(point);
|
|
@@ -2905,9 +3000,11 @@ class MinnowDatabase {
|
|
|
2905
3000
|
async #applyCatalogRewrites(plan, probe) {
|
|
2906
3001
|
const aliased = planHasSourceColumnAliases(plan);
|
|
2907
3002
|
const natural = planHasNaturalJoins(plan);
|
|
2908
|
-
const { views, domains, types, columns: catalogColumns } = await this.#catalogFacts(probe);
|
|
3003
|
+
const { views, domains, types, integers, keys, columns: catalogColumns } = await this.#catalogFacts(probe);
|
|
2909
3004
|
let rewritten = plan.usesSequenceCalls === true ? await this.#resolveSequenceCalls(plan) : plan;
|
|
2910
|
-
|
|
3005
|
+
rewritten = foldIdentifierCase(rewritten, catalogColumns);
|
|
3006
|
+
rewritten = expandRowReferences(rewritten, (name) => catalogColumns.get(name));
|
|
3007
|
+
if (views.size > 0 && planReadsViews(rewritten, (name) => views.has(name))) {
|
|
2911
3008
|
const bodies = /* @__PURE__ */ new Map();
|
|
2912
3009
|
rewritten = expandViewSources(rewritten, (name) => {
|
|
2913
3010
|
const sql = views.get(name);
|
|
@@ -2924,6 +3021,13 @@ class MinnowDatabase {
|
|
|
2924
3021
|
if (planReadsTable(rewritten, (name) => domains.has(name) || types.has(name))) {
|
|
2925
3022
|
rewritten = normalizePlanDomainLiterals(rewritten, domains, types);
|
|
2926
3023
|
}
|
|
3024
|
+
if (integers.size > 0 && planMayHaveIntegerDivision(rewritten)) {
|
|
3025
|
+
rewritten = clonePlanTree(rewritten);
|
|
3026
|
+
annotatePlanIntegerDivision(rewritten, (name) => integers.get(name));
|
|
3027
|
+
}
|
|
3028
|
+
if (keys.size > 0) {
|
|
3029
|
+
rewritten = extendGroupByWithKeyDependents(rewritten, (name) => keys.get(name), (name) => catalogColumns.get(name));
|
|
3030
|
+
}
|
|
2927
3031
|
const columnsOf = (name) => {
|
|
2928
3032
|
const columns = catalogColumns.get(name);
|
|
2929
3033
|
if (columns === void 0)
|
|
@@ -3034,6 +3138,14 @@ class MinnowDatabase {
|
|
|
3034
3138
|
item.expression = await rewrite(item.expression);
|
|
3035
3139
|
return resolved;
|
|
3036
3140
|
}
|
|
3141
|
+
async #foldTableName(name) {
|
|
3142
|
+
const { columns } = await this.#catalogFacts();
|
|
3143
|
+
if (columns.has(name))
|
|
3144
|
+
return name;
|
|
3145
|
+
const lowered = name.toLowerCase();
|
|
3146
|
+
const matches = [...columns.keys()].filter((candidate) => candidate.toLowerCase() === lowered);
|
|
3147
|
+
return matches.length === 1 ? matches[0] ?? name : name;
|
|
3148
|
+
}
|
|
3037
3149
|
async #catalogFacts(probe) {
|
|
3038
3150
|
probe ??= await this.store.getCatalogProbe();
|
|
3039
3151
|
const epoch = probe.catalogEpoch;
|
|
@@ -3045,10 +3157,23 @@ class MinnowDatabase {
|
|
|
3045
3157
|
const domains = /* @__PURE__ */ new Map();
|
|
3046
3158
|
const columns = /* @__PURE__ */ new Map();
|
|
3047
3159
|
const types = /* @__PURE__ */ new Map();
|
|
3160
|
+
const integers = /* @__PURE__ */ new Map();
|
|
3161
|
+
const keys = /* @__PURE__ */ new Map();
|
|
3048
3162
|
for (const table of await this.store.listTables()) {
|
|
3049
3163
|
const visible = table.columns.filter(({ hidden }) => hidden !== true);
|
|
3164
|
+
const keyColumn = getUniqueKeyColumn(table);
|
|
3165
|
+
const keyNames = keyColumn !== void 0 && !keyColumn.hidden ? [keyColumn.name] : (table.primaryKeyColumnIds ?? []).flatMap((columnId) => {
|
|
3166
|
+
const column = table.columns.find((candidate) => candidate.id === columnId);
|
|
3167
|
+
return column === void 0 ? [] : [column.name];
|
|
3168
|
+
});
|
|
3169
|
+
if (keyNames.length > 0)
|
|
3170
|
+
keys.set(table.name, keyNames);
|
|
3050
3171
|
columns.set(table.name, visible.map(({ name }) => name));
|
|
3051
3172
|
types.set(table.name, new Map(visible.map((column) => [column.name, column.type])));
|
|
3173
|
+
const integerColumns = visible.filter((column) => column.integer === true);
|
|
3174
|
+
if (integerColumns.length > 0) {
|
|
3175
|
+
integers.set(table.name, new Set(integerColumns.map(({ name }) => name)));
|
|
3176
|
+
}
|
|
3052
3177
|
const tableDomains = new Map(table.columns.flatMap((column) => column.sqlDomain === void 0 ? [] : [[column.name, column.sqlDomain]]));
|
|
3053
3178
|
if (tableDomains.size > 0)
|
|
3054
3179
|
domains.set(table.name, tableDomains);
|
|
@@ -3062,7 +3187,7 @@ class MinnowDatabase {
|
|
|
3062
3187
|
existing.push({ table, key });
|
|
3063
3188
|
}
|
|
3064
3189
|
}
|
|
3065
|
-
const facts = { views, childKeys, domains, columns, types };
|
|
3190
|
+
const facts = { views, childKeys, domains, columns, types, integers, keys };
|
|
3066
3191
|
this.#catalogCache = { epoch, facts };
|
|
3067
3192
|
return facts;
|
|
3068
3193
|
}
|
|
@@ -3558,7 +3683,7 @@ class MinnowDatabase {
|
|
|
3558
3683
|
if (page.nextCursor === null)
|
|
3559
3684
|
break;
|
|
3560
3685
|
cursor = page.nextCursor;
|
|
3561
|
-
await
|
|
3686
|
+
await this.#yieldMaintenance();
|
|
3562
3687
|
}
|
|
3563
3688
|
return result;
|
|
3564
3689
|
}
|
|
@@ -3583,7 +3708,7 @@ class MinnowDatabase {
|
|
|
3583
3708
|
if (page.nextCursor === null)
|
|
3584
3709
|
break;
|
|
3585
3710
|
cursor = page.nextCursor;
|
|
3586
|
-
await
|
|
3711
|
+
await this.#yieldMaintenance();
|
|
3587
3712
|
}
|
|
3588
3713
|
return result;
|
|
3589
3714
|
}
|
|
@@ -3793,8 +3918,11 @@ class MinnowDatabase {
|
|
|
3793
3918
|
return;
|
|
3794
3919
|
if (!autoCompactionDueHint(hint))
|
|
3795
3920
|
return;
|
|
3796
|
-
|
|
3797
|
-
|
|
3921
|
+
const backoff = this.#autoCompactionBackoff.get(table.id);
|
|
3922
|
+
if (backoff !== void 0) {
|
|
3923
|
+
if (backoff.retryTimer !== void 0 || hint.visible < backoff.minimumSegments)
|
|
3924
|
+
return;
|
|
3925
|
+
}
|
|
3798
3926
|
if (this.#autoCompactionsInFlight.has(table.id)) {
|
|
3799
3927
|
this.#autoCompactionsRequested.add(table.id);
|
|
3800
3928
|
return;
|
|
@@ -3802,22 +3930,43 @@ class MinnowDatabase {
|
|
|
3802
3930
|
const run = this.#runAutoCompaction(table).then((folded) => {
|
|
3803
3931
|
if (folded)
|
|
3804
3932
|
this.#autoCompactionBackoff.delete(table.id);
|
|
3805
|
-
else
|
|
3806
|
-
this.#
|
|
3807
|
-
}
|
|
3933
|
+
else
|
|
3934
|
+
this.#backOffAutoCompaction(table.id, hint.visible);
|
|
3808
3935
|
}).catch(() => {
|
|
3809
|
-
this.#
|
|
3936
|
+
this.#backOffAutoCompaction(table.id, hint.visible);
|
|
3810
3937
|
}).finally(() => {
|
|
3811
3938
|
if (this.#autoCompactionsInFlight.get(table.id) === run) {
|
|
3812
3939
|
this.#autoCompactionsInFlight.delete(table.id);
|
|
3813
3940
|
}
|
|
3814
3941
|
if (this.#autoCompactionsRequested.delete(table.id)) {
|
|
3815
|
-
void
|
|
3942
|
+
void this.#yieldMaintenance().then(() => this.#checkAutoCompaction(table.id));
|
|
3816
3943
|
}
|
|
3817
3944
|
});
|
|
3818
3945
|
this.#autoCompactionsInFlight.set(table.id, run);
|
|
3819
3946
|
void run;
|
|
3820
3947
|
}
|
|
3948
|
+
#backOffAutoCompaction(tableId, visible) {
|
|
3949
|
+
if (this.#closed)
|
|
3950
|
+
return;
|
|
3951
|
+
const previous = this.#autoCompactionBackoff.get(tableId);
|
|
3952
|
+
if (previous?.retryTimer !== void 0)
|
|
3953
|
+
clearTimeout(previous.retryTimer);
|
|
3954
|
+
const failures = (previous?.failures ?? 0) + 1;
|
|
3955
|
+
const delay = Math.min(AUTO_COMPACT_RETRY_MAX_MS, AUTO_COMPACT_RETRY_MIN_MS * 2 ** Math.min(16, failures - 1));
|
|
3956
|
+
const timer = setTimeout(() => {
|
|
3957
|
+
const current = this.#autoCompactionBackoff.get(tableId);
|
|
3958
|
+
if (current?.retryTimer === timer) {
|
|
3959
|
+
this.#autoCompactionBackoff.set(tableId, { ...current, retryTimer: void 0 });
|
|
3960
|
+
}
|
|
3961
|
+
void this.#checkAutoCompaction(tableId);
|
|
3962
|
+
}, delay);
|
|
3963
|
+
timer.unref?.();
|
|
3964
|
+
this.#autoCompactionBackoff.set(tableId, {
|
|
3965
|
+
minimumSegments: Math.min(AUTO_COMPACT_MAX_LEVEL_ZERO_SEGMENTS, Math.max(2, visible * 2)),
|
|
3966
|
+
failures,
|
|
3967
|
+
retryTimer: timer
|
|
3968
|
+
});
|
|
3969
|
+
}
|
|
3821
3970
|
async #assertCompactionCapacity(table, transaction) {
|
|
3822
3971
|
transaction?.limitLevelZeroSegments(table.id, MAX_LEVEL_ZERO_SEGMENTS);
|
|
3823
3972
|
if (!this.#autoCompact)
|
|
@@ -3834,6 +3983,7 @@ class MinnowDatabase {
|
|
|
3834
3983
|
}
|
|
3835
3984
|
if (levelZero < MAX_LEVEL_ZERO_SEGMENTS)
|
|
3836
3985
|
return;
|
|
3986
|
+
await this.#publishPendingCompactions();
|
|
3837
3987
|
await this.compactTableStep(table.name, {
|
|
3838
3988
|
maxBlocks: AUTO_COMPACT_STEP_BLOCKS,
|
|
3839
3989
|
maxLevel0Segments: AUTO_COMPACT_MAX_LEVEL_ZERO_SEGMENTS
|
|
@@ -3859,14 +4009,14 @@ class MinnowDatabase {
|
|
|
3859
4009
|
while (progress.result === null) {
|
|
3860
4010
|
if (progress.jobId === null)
|
|
3861
4011
|
throw new Error("Compaction progress lost its job ID");
|
|
3862
|
-
await
|
|
4012
|
+
await this.#yieldMaintenance();
|
|
3863
4013
|
progress = await this.resumeCompactionJob(progress.jobId, options);
|
|
3864
4014
|
}
|
|
3865
4015
|
if (!progress.result.compacted)
|
|
3866
4016
|
return folded;
|
|
3867
4017
|
folded = true;
|
|
3868
4018
|
this.#maybeScheduleAutoCollection();
|
|
3869
|
-
await
|
|
4019
|
+
await this.#yieldMaintenance();
|
|
3870
4020
|
const current = await this.store.getTable(table.id);
|
|
3871
4021
|
if (current === void 0 || !autoCompactionDue(await this.#currentVisibleSegments(current))) {
|
|
3872
4022
|
return folded;
|
|
@@ -3887,6 +4037,9 @@ class MinnowDatabase {
|
|
|
3887
4037
|
return segments;
|
|
3888
4038
|
}
|
|
3889
4039
|
#afterCommit(manifest, transaction) {
|
|
4040
|
+
const signal = this.#commitSignal;
|
|
4041
|
+
this.#commitSignal = void 0;
|
|
4042
|
+
signal?.resolve();
|
|
3890
4043
|
if (this.#closed)
|
|
3891
4044
|
return;
|
|
3892
4045
|
const contribution = transaction?.committedCatalogContribution;
|
|
@@ -4265,6 +4418,7 @@ class MinnowDatabase {
|
|
|
4265
4418
|
const statementNow = this.#now();
|
|
4266
4419
|
const materialized = await this.#materializeInsertExpressions({ ...compiled, rows: derivedRows }, statementNow);
|
|
4267
4420
|
const input = insertStatementBatch(materialized);
|
|
4421
|
+
coerceSqlWriteBatch(target, input);
|
|
4268
4422
|
const filled = await fillColumnDefaults(target, input, (sql) => this.#evaluateDefaultExpression(sql, statementNow), derivedRows.length);
|
|
4269
4423
|
normalizeDomainBatch(target, filled.batch);
|
|
4270
4424
|
fillStoredGeneratedColumns(target, filled.batch, derivedRows.length, filled.generated);
|
|
@@ -4293,6 +4447,9 @@ class MinnowDatabase {
|
|
|
4293
4447
|
const bound = bindStatementParameters(compiled, params);
|
|
4294
4448
|
if (bound.kind !== "update" && bound.kind !== "delete")
|
|
4295
4449
|
continue;
|
|
4450
|
+
if (bound.from !== void 0) {
|
|
4451
|
+
throw new TypeError(`Trigger body ${bound.kind.toUpperCase()}s cannot join other sources`);
|
|
4452
|
+
}
|
|
4296
4453
|
const referenced = /* @__PURE__ */ new Set([keyColumn.name]);
|
|
4297
4454
|
if (bound.kind === "update") {
|
|
4298
4455
|
for (const assignment of bound.assignments) {
|
|
@@ -4417,6 +4574,7 @@ class MinnowDatabase {
|
|
|
4417
4574
|
return this.#openWriteScope((session) => action(session));
|
|
4418
4575
|
}
|
|
4419
4576
|
async #openWriteScope(action, options = {}) {
|
|
4577
|
+
await this.#publishPendingCompactions();
|
|
4420
4578
|
await this.#assistAutomaticCollection();
|
|
4421
4579
|
const transaction = await this.#transactions.beginDeferred({
|
|
4422
4580
|
durableSnapshot: options.durableSnapshot ?? true
|
|
@@ -4458,6 +4616,9 @@ class MinnowDatabase {
|
|
|
4458
4616
|
}));
|
|
4459
4617
|
}
|
|
4460
4618
|
const statement = bindStatementParameters(compiled, params);
|
|
4619
|
+
if (statement.kind === "set" || statement.kind === "show") {
|
|
4620
|
+
return externalizeExecuteResult(await this.runStatement(statement));
|
|
4621
|
+
}
|
|
4461
4622
|
if (!isTransactionalStatement(statement)) {
|
|
4462
4623
|
throw new TypeError(`${statement.kind.toUpperCase().replace("-", " ")} is not allowed inside a write scope`);
|
|
4463
4624
|
}
|
|
@@ -5371,15 +5532,19 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
5371
5532
|
})
|
|
5372
5533
|
};
|
|
5373
5534
|
}
|
|
5535
|
+
if (statement.kind === "set" || statement.kind === "show") {
|
|
5536
|
+
return externalizeExecuteResult(await this.runStatement(statement));
|
|
5537
|
+
}
|
|
5374
5538
|
const open = this.#openTransaction;
|
|
5375
5539
|
if (open !== void 0) {
|
|
5376
|
-
const
|
|
5377
|
-
if (!isTransactionalStatement(
|
|
5378
|
-
throw new TypeError(`${
|
|
5540
|
+
const boundStatement2 = bindStatementParameters(statement, params);
|
|
5541
|
+
if (!isTransactionalStatement(boundStatement2)) {
|
|
5542
|
+
throw new TypeError(`${boundStatement2.kind.toUpperCase().replace("-", " ")} is not allowed inside a transaction`);
|
|
5379
5543
|
}
|
|
5380
|
-
return externalizeExecuteResult(await this.#projectReturningItems(
|
|
5544
|
+
return externalizeExecuteResult(await this.#projectReturningItems(boundStatement2, await this.#duringTransaction(open, () => open.session.executeStatement(boundStatement2))));
|
|
5381
5545
|
}
|
|
5382
|
-
|
|
5546
|
+
const boundStatement = bindStatementParameters(statement, params);
|
|
5547
|
+
return externalizeExecuteResult(await this.#projectReturningItems(boundStatement, await this.runStatement(boundStatement)));
|
|
5383
5548
|
}
|
|
5384
5549
|
async #assertForeignKeysPresent(table, rowsByColumn, read, transaction) {
|
|
5385
5550
|
for (const key of table.foreignKeys ?? []) {
|
|
@@ -5691,12 +5856,14 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
5691
5856
|
if (keyColumn === void 0 || conflictColumns.length !== addressColumns.length || conflictColumns.some((name, index) => name !== addressColumns[index])) {
|
|
5692
5857
|
throw new TypeError(`ON CONFLICT targets the table's primary or unique key columns: ${addressColumns.join(", ") || "(none)"}`);
|
|
5693
5858
|
}
|
|
5859
|
+
statement = foldInsertColumns(table, statement);
|
|
5694
5860
|
for (const name of statement.columns) {
|
|
5695
5861
|
if (!table.columns.some((column) => column.name === name)) {
|
|
5696
5862
|
throw new TypeError(`INSERT column does not exist: ${name}`);
|
|
5697
5863
|
}
|
|
5698
5864
|
}
|
|
5699
5865
|
const proposedBatch = insertStatementBatch(statement);
|
|
5866
|
+
coerceSqlWriteBatch(table, proposedBatch);
|
|
5700
5867
|
const statementNow = this.#now();
|
|
5701
5868
|
const filledProposed = await fillColumnDefaults(table, proposedBatch, (sql) => this.#evaluateDefaultExpression(sql, statementNow), statement.rows.length);
|
|
5702
5869
|
normalizeDomainBatch(table, filledProposed.batch);
|
|
@@ -5892,6 +6059,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
5892
6059
|
throw new TypeError(`ON CONFLICT targets the table's primary or unique key columns: ${addressColumns.join(", ") || "(none)"}`);
|
|
5893
6060
|
}
|
|
5894
6061
|
const proposed = insertStatementBatch(statement);
|
|
6062
|
+
coerceSqlWriteBatch(table, proposed);
|
|
5895
6063
|
const statementNow = this.#now();
|
|
5896
6064
|
const filled = await fillColumnDefaults(table, proposed, (sql) => this.#evaluateDefaultExpression(sql, statementNow), statement.rows.length);
|
|
5897
6065
|
normalizeDomainBatch(table, filled.batch);
|
|
@@ -6064,6 +6232,38 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6064
6232
|
if (statement.kind === "select") {
|
|
6065
6233
|
return { kind: "rows", result: await this.query(statement.sql) };
|
|
6066
6234
|
}
|
|
6235
|
+
if (statement.kind === "set") {
|
|
6236
|
+
return { kind: "set", action: statement.action, name: statement.name };
|
|
6237
|
+
}
|
|
6238
|
+
if (statement.kind === "show") {
|
|
6239
|
+
const settings = {
|
|
6240
|
+
search_path: "public",
|
|
6241
|
+
server_version: "16.0",
|
|
6242
|
+
server_version_num: "160000",
|
|
6243
|
+
timezone: "UTC",
|
|
6244
|
+
time_zone: "UTC",
|
|
6245
|
+
transaction_isolation: "repeatable read",
|
|
6246
|
+
default_transaction_isolation: "repeatable read",
|
|
6247
|
+
client_encoding: "UTF8",
|
|
6248
|
+
server_encoding: "UTF8",
|
|
6249
|
+
standard_conforming_strings: "on",
|
|
6250
|
+
integer_datetimes: "on",
|
|
6251
|
+
datestyle: "ISO, MDY",
|
|
6252
|
+
intervalstyle: "postgres",
|
|
6253
|
+
application_name: "",
|
|
6254
|
+
is_superuser: "on",
|
|
6255
|
+
max_identifier_length: "63"
|
|
6256
|
+
};
|
|
6257
|
+
const name = statement.name.toLowerCase();
|
|
6258
|
+
const value = settings[name];
|
|
6259
|
+
if (value === void 0) {
|
|
6260
|
+
throw new TypeError(`unrecognized configuration parameter "${statement.name}"`);
|
|
6261
|
+
}
|
|
6262
|
+
return {
|
|
6263
|
+
kind: "rows",
|
|
6264
|
+
result: await this.query(`SELECT $1 AS ${quoteIdentifier(name)}`, { params: [value] })
|
|
6265
|
+
};
|
|
6266
|
+
}
|
|
6067
6267
|
if (statement.kind === "create-enum") {
|
|
6068
6268
|
const values = validateEnumValues(statement.values, statement.name);
|
|
6069
6269
|
const storageName = `${ENUM_TYPE_PREFIX}${statement.name}`;
|
|
@@ -6495,12 +6695,14 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6495
6695
|
throw new TypeError(`ON CONFLICT targets the table's primary or unique key columns: ${addressColumns.join(", ") || "(none)"}`);
|
|
6496
6696
|
}
|
|
6497
6697
|
}
|
|
6498
|
-
|
|
6698
|
+
const spelledColumns = foldInsertColumns(table2, statement).columns;
|
|
6699
|
+
for (const name of spelledColumns) {
|
|
6499
6700
|
if (!table2.columns.some((column) => column.name === name)) {
|
|
6500
6701
|
throw new TypeError(`INSERT column does not exist: ${name}`);
|
|
6501
6702
|
}
|
|
6502
6703
|
}
|
|
6503
|
-
const input = insertStatementBatch(statement);
|
|
6704
|
+
const input = insertStatementBatch({ ...statement, columns: spelledColumns });
|
|
6705
|
+
coerceSqlWriteBatch(table2, input);
|
|
6504
6706
|
normalizeDomainBatch(table2, input);
|
|
6505
6707
|
let returningColumns2;
|
|
6506
6708
|
if (options.returning !== void 0) {
|
|
@@ -6514,7 +6716,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6514
6716
|
const writer = options.writer;
|
|
6515
6717
|
const stagedKeyColumn = writer === void 0 ? void 0 : getUniqueKeyColumn(table2);
|
|
6516
6718
|
if (writer !== void 0 && !viaUpsert && stagedKeyColumn !== void 0) {
|
|
6517
|
-
const assignedGenerated = table2.columns.find((column) => column.generatedValue !== void 0 &&
|
|
6719
|
+
const assignedGenerated = table2.columns.find((column) => column.generatedValue !== void 0 && spelledColumns.includes(column.name));
|
|
6518
6720
|
if (assignedGenerated !== void 0) {
|
|
6519
6721
|
throw new TypeError(`Generated column cannot be assigned: ${assignedGenerated.name}`);
|
|
6520
6722
|
}
|
|
@@ -6530,7 +6732,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6530
6732
|
...returningColumns2 === void 0 ? {} : {
|
|
6531
6733
|
...returningExecuteFields(table2, returningColumns2, statement.rows.map((row, rowIndex) => Object.fromEntries(returningColumns2.map((name) => {
|
|
6532
6734
|
const column = table2.columns.find((candidate) => candidate.name === name);
|
|
6533
|
-
const position =
|
|
6735
|
+
const position = spelledColumns.indexOf(name);
|
|
6534
6736
|
const inserted = position < 0 ? void 0 : row[position];
|
|
6535
6737
|
const value = generated[name]?.[rowIndex] ?? input.columns[name]?.[rowIndex] ?? (inserted === void 0 || isDefaultInsertValue(inserted) ? null : boundInsertValue(inserted));
|
|
6536
6738
|
return [name, executionSqlValueFromInput(column, value)];
|
|
@@ -6545,8 +6747,9 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6545
6747
|
}
|
|
6546
6748
|
const returningColumns = options.returning === void 0 ? void 0 : options.returning === "*" ? visibleTableColumns(table).map(({ name }) => name) : [...options.returning];
|
|
6547
6749
|
const updateAssignments = statement.kind !== "update" ? [] : await (async () => {
|
|
6750
|
+
const spelled = foldAssignmentColumns(table, statement.assignments);
|
|
6548
6751
|
if (!table.columns.some((column) => column.sqlDomain !== void 0)) {
|
|
6549
|
-
return
|
|
6752
|
+
return spelled;
|
|
6550
6753
|
}
|
|
6551
6754
|
const { domains } = await this.#catalogFacts();
|
|
6552
6755
|
if (!domains.has(table.name))
|
|
@@ -6555,7 +6758,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6555
6758
|
sql: `UPDATE ${table.name}`,
|
|
6556
6759
|
base: { table: table.name, alias: table.name },
|
|
6557
6760
|
joins: [],
|
|
6558
|
-
select:
|
|
6761
|
+
select: spelled.map((assignment) => ({
|
|
6559
6762
|
expression: assignment.expression,
|
|
6560
6763
|
alias: assignment.column
|
|
6561
6764
|
})),
|
|
@@ -6564,20 +6767,25 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6564
6767
|
having: [],
|
|
6565
6768
|
orderBy: []
|
|
6566
6769
|
}, domains);
|
|
6567
|
-
return
|
|
6770
|
+
return spelled.map((assignment, index) => ({
|
|
6568
6771
|
...assignment,
|
|
6569
6772
|
expression: normalized.select[index]?.expression ?? assignment.expression
|
|
6570
6773
|
}));
|
|
6571
6774
|
})();
|
|
6572
6775
|
const referenced = /* @__PURE__ */ new Set([keyColumn.name, ...returningColumns ?? []]);
|
|
6573
6776
|
const assignmentAlias = (column) => `\0set:${column}`;
|
|
6777
|
+
const targetAlias = statement.alias ?? table.name;
|
|
6778
|
+
const from = statement.from;
|
|
6574
6779
|
const plan = optimizePlan({
|
|
6575
6780
|
sql: `(${statement.kind})`,
|
|
6576
|
-
base: { table: table.name, alias:
|
|
6577
|
-
joins: [],
|
|
6781
|
+
base: { table: table.name, alias: targetAlias },
|
|
6782
|
+
joins: from === void 0 ? [] : [crossJoinPlan(from.base), ...from.joins],
|
|
6578
6783
|
select: [
|
|
6579
6784
|
...[...referenced].map((name) => ({
|
|
6580
|
-
expression: {
|
|
6785
|
+
expression: {
|
|
6786
|
+
kind: "column",
|
|
6787
|
+
reference: from === void 0 ? name : `${targetAlias}.${name}`
|
|
6788
|
+
},
|
|
6581
6789
|
alias: name
|
|
6582
6790
|
})),
|
|
6583
6791
|
...statement.kind === "update" ? updateAssignments.map((assignment) => ({
|
|
@@ -6585,12 +6793,12 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6585
6793
|
alias: assignmentAlias(assignment.column)
|
|
6586
6794
|
})) : []
|
|
6587
6795
|
],
|
|
6588
|
-
predicates: statement.predicates,
|
|
6796
|
+
predicates: from === void 0 ? statement.predicates : [...statement.predicates, ...from.predicates],
|
|
6589
6797
|
groupBy: [],
|
|
6590
6798
|
having: [],
|
|
6591
6799
|
orderBy: []
|
|
6592
6800
|
});
|
|
6593
|
-
if (statement.kind === "delete" && returningColumns === void 0) {
|
|
6801
|
+
if (statement.kind === "delete" && returningColumns === void 0 && from === void 0) {
|
|
6594
6802
|
const internalWriter = options.writer;
|
|
6595
6803
|
if (internalWriter.queryFirstColumn !== void 0) {
|
|
6596
6804
|
const selectedKeys = await internalWriter.queryFirstColumn(plan);
|
|
@@ -6622,6 +6830,16 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6622
6830
|
} else {
|
|
6623
6831
|
rows = (await this.#queryCompiled(plan)).rows;
|
|
6624
6832
|
}
|
|
6833
|
+
if (from !== void 0) {
|
|
6834
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6835
|
+
rows = rows.filter((row) => {
|
|
6836
|
+
const key = JSON.stringify(row[keyColumn.name] ?? null);
|
|
6837
|
+
if (seen.has(key))
|
|
6838
|
+
return false;
|
|
6839
|
+
seen.add(key);
|
|
6840
|
+
return true;
|
|
6841
|
+
});
|
|
6842
|
+
}
|
|
6625
6843
|
const keys = keyColumn.type === "string" && keyColumn.sqlDomain === void 0 ? rows.map((row) => storedSqlValueFromExecution(keyColumn, row[keyColumn.name] ?? null)) : rows.map((row) => row[keyColumn.name] ?? null);
|
|
6626
6844
|
if (keys.some((key) => key === null)) {
|
|
6627
6845
|
throw new TypeError(`Unique key values must not be null: ${keyColumn.name}`);
|
|
@@ -6665,8 +6883,9 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6665
6883
|
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
6666
6884
|
throw new TypeError(`UPDATE assignment produced a non-finite number: ${assignment.column}`);
|
|
6667
6885
|
}
|
|
6668
|
-
|
|
6669
|
-
|
|
6886
|
+
const stored = storedSqlValueFromExecution(column, value);
|
|
6887
|
+
executionValues?.push(column.type === "string" && column.sqlDomain === void 0 ? value : stored);
|
|
6888
|
+
return stored;
|
|
6670
6889
|
});
|
|
6671
6890
|
if (executionValues !== void 0 && returnedChanges !== void 0) {
|
|
6672
6891
|
returnedChanges[assignment.column] = executionValues;
|
|
@@ -8050,7 +8269,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8050
8269
|
return active;
|
|
8051
8270
|
cursor = page.nextCursor;
|
|
8052
8271
|
if (cursor !== null)
|
|
8053
|
-
await
|
|
8272
|
+
await this.#yieldMaintenance();
|
|
8054
8273
|
} while (cursor !== null);
|
|
8055
8274
|
return void 0;
|
|
8056
8275
|
}
|
|
@@ -8088,11 +8307,15 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8088
8307
|
}
|
|
8089
8308
|
cursor = page.nextCursor;
|
|
8090
8309
|
if (cursor !== null)
|
|
8091
|
-
await
|
|
8310
|
+
await this.#yieldMaintenance();
|
|
8092
8311
|
} while (cursor !== null);
|
|
8093
8312
|
}
|
|
8094
8313
|
async collectGarbage(options = {}) {
|
|
8095
8314
|
const maxItems = boundedMaintenanceBatchItems(options.maxItemsPerStep ?? 64, "Garbage collection items per step");
|
|
8315
|
+
let reconciliationCursor = null;
|
|
8316
|
+
do {
|
|
8317
|
+
reconciliationCursor = (await this.#reconcileCompactionPage(reconciliationCursor)).nextCursor;
|
|
8318
|
+
} while (reconciliationCursor !== null);
|
|
8096
8319
|
let progress = await this.collectGarbageStep({
|
|
8097
8320
|
maxItems,
|
|
8098
8321
|
...options.maxPlanningItems === void 0 ? {} : { maxPlanningItems: options.maxPlanningItems },
|
|
@@ -8129,7 +8352,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8129
8352
|
return active;
|
|
8130
8353
|
cursor = page.nextCursor;
|
|
8131
8354
|
if (cursor !== null)
|
|
8132
|
-
await
|
|
8355
|
+
await this.#yieldMaintenance();
|
|
8133
8356
|
} while (cursor !== null);
|
|
8134
8357
|
return void 0;
|
|
8135
8358
|
}
|
|
@@ -8170,14 +8393,14 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8170
8393
|
this.#autoCollectionRequested = false;
|
|
8171
8394
|
this.#commitsSinceCollection = 0;
|
|
8172
8395
|
this.#lastCollectionAt = dateMilliseconds(this.#now());
|
|
8173
|
-
const run = this.#runAutoCollection().then((moreWork) => {
|
|
8396
|
+
const run = this.#runAutoCollection().then(({ moreWork, reclaimed }) => {
|
|
8174
8397
|
this.#autoCollectionConsecutiveFailures = 0;
|
|
8175
8398
|
this.#autoCollectionLastError = void 0;
|
|
8176
8399
|
this.#lastCollectionCompletedAt = dateMilliseconds(this.#now());
|
|
8177
8400
|
this.#autoCollectionRetryAt = void 0;
|
|
8178
8401
|
if (moreWork)
|
|
8179
8402
|
this.#autoCollectionRequested = true;
|
|
8180
|
-
|
|
8403
|
+
if (reclaimed || !moreWork)
|
|
8181
8404
|
this.#autoCollectionDebtCommits = 0;
|
|
8182
8405
|
}).catch((error) => {
|
|
8183
8406
|
this.#autoCollectionConsecutiveFailures += 1;
|
|
@@ -8193,7 +8416,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8193
8416
|
this.#autoCollectionTask = void 0;
|
|
8194
8417
|
this.#autoCollectionInFlight = false;
|
|
8195
8418
|
if (this.#autoCollectionRequested) {
|
|
8196
|
-
void
|
|
8419
|
+
void this.#yieldMaintenance().then(() => {
|
|
8197
8420
|
if (this.#closed) {
|
|
8198
8421
|
this.#autoCollectionRequested = false;
|
|
8199
8422
|
return;
|
|
@@ -8238,6 +8461,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8238
8461
|
async #runAutoCollection() {
|
|
8239
8462
|
const reconciliationMoreWork = await this.#reconcileCompactionMaintenance();
|
|
8240
8463
|
let moreWork = false;
|
|
8464
|
+
let reclaimed = false;
|
|
8241
8465
|
for (let pass = 0; pass < AUTO_COLLECT_MAX_PASSES; pass += 1) {
|
|
8242
8466
|
if (this.#closed)
|
|
8243
8467
|
break;
|
|
@@ -8248,7 +8472,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8248
8472
|
retainRecentVersions: AUTO_COLLECT_RETAINED_VERSIONS
|
|
8249
8473
|
}, AUTO_COLLECT_RETAINED_VERSION_MS);
|
|
8250
8474
|
while (progress.result === null) {
|
|
8251
|
-
await
|
|
8475
|
+
await this.#yieldMaintenance();
|
|
8252
8476
|
progress = await this.resumeGarbageCollectionJob(progress.jobId, {
|
|
8253
8477
|
maxItems: AUTO_COLLECT_STEP_ITEMS
|
|
8254
8478
|
});
|
|
@@ -8258,14 +8482,19 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8258
8482
|
if (!moreWork) {
|
|
8259
8483
|
break;
|
|
8260
8484
|
}
|
|
8261
|
-
|
|
8485
|
+
reclaimed = true;
|
|
8486
|
+
await this.#yieldMaintenance();
|
|
8262
8487
|
}
|
|
8263
8488
|
await this.#pruneFinishedJobRecords();
|
|
8264
|
-
return moreWork || reconciliationMoreWork;
|
|
8489
|
+
return { moreWork: moreWork || reconciliationMoreWork, reclaimed };
|
|
8265
8490
|
}
|
|
8266
8491
|
async #reconcileCompactionMaintenance() {
|
|
8267
|
-
const
|
|
8268
|
-
this.#compactionReconciliationCursor =
|
|
8492
|
+
const { nextCursor, moreWork } = await this.#reconcileCompactionPage(this.#compactionReconciliationCursor);
|
|
8493
|
+
this.#compactionReconciliationCursor = nextCursor;
|
|
8494
|
+
return moreWork;
|
|
8495
|
+
}
|
|
8496
|
+
async #reconcileCompactionPage(cursor) {
|
|
8497
|
+
const page = await this.store.listCompactionJobPage(cursor, AUTO_COMPACTION_RECONCILE_RECORDS);
|
|
8269
8498
|
let moreWork = page.nextCursor !== null;
|
|
8270
8499
|
const cutoff = dateIsoString(this.#now());
|
|
8271
8500
|
for (const listed of page.records) {
|
|
@@ -8309,14 +8538,22 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8309
8538
|
this.#maybeScheduleAutoCompaction(table, await this.#currentVisibleSegments(table));
|
|
8310
8539
|
continue;
|
|
8311
8540
|
}
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8541
|
+
try {
|
|
8542
|
+
const progress = await this.resumeCompactionJob(job.id, { maxBlocks: 1 });
|
|
8543
|
+
if (progress.result !== null) {
|
|
8544
|
+
job = await this.store.getCompactionJob(job.id) ?? job;
|
|
8545
|
+
if (!isActiveCompactionState(job.state))
|
|
8546
|
+
continue;
|
|
8547
|
+
}
|
|
8548
|
+
} catch (error) {
|
|
8549
|
+
if (error instanceof CompactionJobCancelledError)
|
|
8316
8550
|
continue;
|
|
8551
|
+
if (this.#closed)
|
|
8552
|
+
throw error;
|
|
8553
|
+
this.#maybeScheduleAutoCompaction(table, await this.#currentVisibleSegments(table));
|
|
8317
8554
|
}
|
|
8318
8555
|
}
|
|
8319
|
-
return moreWork;
|
|
8556
|
+
return { nextCursor: page.nextCursor, moreWork };
|
|
8320
8557
|
}
|
|
8321
8558
|
async #pruneFinishedJobRecords() {
|
|
8322
8559
|
const newestFirst = (left, right) => right.updatedAt.localeCompare(left.updatedAt) || right.id.localeCompare(left.id);
|
|
@@ -8337,7 +8574,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8337
8574
|
}
|
|
8338
8575
|
cursor = page.nextCursor;
|
|
8339
8576
|
if (cursor !== null)
|
|
8340
|
-
await
|
|
8577
|
+
await this.#yieldMaintenance();
|
|
8341
8578
|
} while (cursor !== null);
|
|
8342
8579
|
const retainedCompactionIds = new Set(newestCompactions.map((job) => job.id));
|
|
8343
8580
|
cursor = null;
|
|
@@ -8387,7 +8624,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8387
8624
|
}
|
|
8388
8625
|
cursor = page.nextCursor;
|
|
8389
8626
|
if (cursor !== null)
|
|
8390
|
-
await
|
|
8627
|
+
await this.#yieldMaintenance();
|
|
8391
8628
|
} while (cursor !== null);
|
|
8392
8629
|
const newestCollections = [];
|
|
8393
8630
|
cursor = null;
|
|
@@ -8399,7 +8636,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8399
8636
|
}
|
|
8400
8637
|
cursor = page.nextCursor;
|
|
8401
8638
|
if (cursor !== null)
|
|
8402
|
-
await
|
|
8639
|
+
await this.#yieldMaintenance();
|
|
8403
8640
|
} while (cursor !== null);
|
|
8404
8641
|
const retainedCollectionIds = new Set(newestCollections.map((job) => job.id));
|
|
8405
8642
|
cursor = null;
|
|
@@ -8412,7 +8649,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8412
8649
|
}
|
|
8413
8650
|
cursor = page.nextCursor;
|
|
8414
8651
|
if (cursor !== null)
|
|
8415
|
-
await
|
|
8652
|
+
await this.#yieldMaintenance();
|
|
8416
8653
|
} while (cursor !== null);
|
|
8417
8654
|
}
|
|
8418
8655
|
async listGarbageCollectionJobs() {
|
|
@@ -8479,7 +8716,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8479
8716
|
}
|
|
8480
8717
|
cursor = page.nextCursor;
|
|
8481
8718
|
if (cursor !== null)
|
|
8482
|
-
await
|
|
8719
|
+
await this.#yieldMaintenance();
|
|
8483
8720
|
} while (cursor !== null);
|
|
8484
8721
|
return newest?.discovery === void 0 ? void 0 : { jobId: newest.id, discovery: newest.discovery };
|
|
8485
8722
|
}
|
|
@@ -9137,7 +9374,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9137
9374
|
}
|
|
9138
9375
|
cursor = page.nextCursor;
|
|
9139
9376
|
if (cursor !== null)
|
|
9140
|
-
await
|
|
9377
|
+
await this.#yieldMaintenance();
|
|
9141
9378
|
} while (cursor !== null);
|
|
9142
9379
|
return largest;
|
|
9143
9380
|
}
|
|
@@ -9716,7 +9953,17 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9716
9953
|
if (job.state === "published") {
|
|
9717
9954
|
return this.#publishedCompactionProgress(table, job);
|
|
9718
9955
|
}
|
|
9719
|
-
|
|
9956
|
+
let linkedTransaction = job.transactionId === null ? void 0 : await this.store.getTransaction(job.transactionId);
|
|
9957
|
+
if (job.transactionId !== null && linkedTransaction?.status === "active" && Date.parse(linkedTransaction.expiresAt) <= dateMilliseconds(this.#now())) {
|
|
9958
|
+
const cutoff = dateIsoString(this.#now());
|
|
9959
|
+
await this.store.abortTransactionIfExpired({
|
|
9960
|
+
transactionId: linkedTransaction.id,
|
|
9961
|
+
expectedOwnerId: linkedTransaction.ownerId,
|
|
9962
|
+
expiresAtCutoff: cutoff,
|
|
9963
|
+
updatedAt: cutoff
|
|
9964
|
+
});
|
|
9965
|
+
linkedTransaction = await this.store.getTransaction(job.transactionId);
|
|
9966
|
+
}
|
|
9720
9967
|
if (linkedTransaction?.status === "committed" && linkedTransaction.committedVersion !== null) {
|
|
9721
9968
|
job = await this.#markCompactionPublished(job, linkedTransaction.committedVersion);
|
|
9722
9969
|
const recoveredManifest = await this.store.getManifest(linkedTransaction.committedVersion);
|
|
@@ -9881,18 +10128,20 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9881
10128
|
}
|
|
9882
10129
|
transaction.setCompactionIntent(job.id, job.sourceBlockIds);
|
|
9883
10130
|
transaction.markLogicallyUnchanged();
|
|
9884
|
-
let
|
|
9885
|
-
|
|
9886
|
-
let publicationConflict;
|
|
10131
|
+
let publicationConflict;
|
|
10132
|
+
const publish = async () => {
|
|
9887
10133
|
try {
|
|
9888
|
-
|
|
9889
|
-
this.#afterCompactionCommit(
|
|
9890
|
-
|
|
10134
|
+
const manifest2 = await transaction.commit();
|
|
10135
|
+
this.#afterCompactionCommit(manifest2, table.id);
|
|
10136
|
+
return manifest2;
|
|
9891
10137
|
} catch (error) {
|
|
9892
10138
|
if (!(error instanceof WriteConflictError))
|
|
9893
10139
|
throw error;
|
|
9894
10140
|
publicationConflict = error;
|
|
10141
|
+
return void 0;
|
|
9895
10142
|
}
|
|
10143
|
+
};
|
|
10144
|
+
const rebaseOverConcurrentCommits = async () => {
|
|
9896
10145
|
const currentVersion = await this.store.getCurrentManifestVersion();
|
|
9897
10146
|
const sourcePresence = await this.#manifestBlockPresence(currentVersion, job.sourceBlockIds);
|
|
9898
10147
|
if (sourcePresence.some((present) => !present)) {
|
|
@@ -9919,7 +10168,24 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9919
10168
|
}
|
|
9920
10169
|
transaction.setCompactionIntent(job.id, job.sourceBlockIds);
|
|
9921
10170
|
transaction.markLogicallyUnchanged();
|
|
9922
|
-
|
|
10171
|
+
};
|
|
10172
|
+
let manifest = await publish();
|
|
10173
|
+
if (manifest === void 0) {
|
|
10174
|
+
manifest = await this.#withCompactionPublicationSlot(async () => {
|
|
10175
|
+
for (let conflicts = 1; conflicts <= MAX_COMPACTION_PUBLICATION_CONFLICTS; conflicts += 1) {
|
|
10176
|
+
await rebaseOverConcurrentCommits();
|
|
10177
|
+
const published = await publish();
|
|
10178
|
+
if (published !== void 0)
|
|
10179
|
+
return published;
|
|
10180
|
+
}
|
|
10181
|
+
return void 0;
|
|
10182
|
+
});
|
|
10183
|
+
if (manifest === void 0) {
|
|
10184
|
+
if (transaction.status === "active")
|
|
10185
|
+
await transaction.abort();
|
|
10186
|
+
job = await this.#abortCompactionJob(job, `Compaction publication lost ${String(MAX_COMPACTION_PUBLICATION_CONFLICTS + 1)} manifest races`);
|
|
10187
|
+
throw new Error(job.error, { cause: publicationConflict });
|
|
10188
|
+
}
|
|
9923
10189
|
}
|
|
9924
10190
|
job = await this.#markCompactionPublished(job, manifest.version);
|
|
9925
10191
|
return compactionProgress(table.name, job, this.#compactionResult(table, job, manifest.version));
|
|
@@ -10079,7 +10345,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
10079
10345
|
}
|
|
10080
10346
|
segmentCursor = page.nextCursor;
|
|
10081
10347
|
if (segmentCursor !== null)
|
|
10082
|
-
await
|
|
10348
|
+
await this.#yieldMaintenance();
|
|
10083
10349
|
} while (segmentCursor !== null);
|
|
10084
10350
|
if (job.outputPartitionOrdinal === void 0 && job.targetLevel === 1 && plan.kind !== "copy-v1" && plan.partitions !== void 0) {
|
|
10085
10351
|
const table = await this.store.getTable(job.tableId);
|
|
@@ -12633,7 +12899,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
12633
12899
|
}
|
|
12634
12900
|
async #findTable(name) {
|
|
12635
12901
|
validateName(name, "Table");
|
|
12636
|
-
const table = await this.store.getTableByName(name);
|
|
12902
|
+
const table = await this.store.getTableByName(name) ?? await this.store.getTableByName(await this.#foldTableName(name));
|
|
12637
12903
|
if (table === void 0)
|
|
12638
12904
|
throw new UnknownTableError(name);
|
|
12639
12905
|
if (table.view !== void 0)
|
|
@@ -13328,7 +13594,26 @@ function postingFrequencyTotal(postings) {
|
|
|
13328
13594
|
}
|
|
13329
13595
|
const secondaryNumberBits = new DataView(new ArrayBuffer(8));
|
|
13330
13596
|
function storedSqlValueFromExecution(column, value) {
|
|
13331
|
-
|
|
13597
|
+
if (column?.type === "string" && column.sqlDomain === void 0 && typeof value === "string") {
|
|
13598
|
+
return externalSqlTextValue(value);
|
|
13599
|
+
}
|
|
13600
|
+
return coerceSqlWriteValue(column, value);
|
|
13601
|
+
}
|
|
13602
|
+
function coerceSqlWriteValue(column, value) {
|
|
13603
|
+
if (column === void 0 || column.sqlDomain !== void 0 || column.type === "string" || typeof value !== "string" || isSqlDomainValue(value)) {
|
|
13604
|
+
return value;
|
|
13605
|
+
}
|
|
13606
|
+
return readUntypedText(column.type, value);
|
|
13607
|
+
}
|
|
13608
|
+
function coerceSqlWriteBatch(table, input) {
|
|
13609
|
+
for (const column of table.columns) {
|
|
13610
|
+
if (column.type === "string" || column.sqlDomain !== void 0)
|
|
13611
|
+
continue;
|
|
13612
|
+
const values = input.columns[column.name];
|
|
13613
|
+
if (!values?.some((value) => typeof value === "string"))
|
|
13614
|
+
continue;
|
|
13615
|
+
input.columns[column.name] = values.map((value) => coerceSqlWriteValue(column, value));
|
|
13616
|
+
}
|
|
13332
13617
|
}
|
|
13333
13618
|
function executionSqlValueFromStorage(column, value) {
|
|
13334
13619
|
return column?.type === "string" && column.sqlDomain === void 0 && typeof value === "string" ? protectedSqlTextValue(value) : value;
|
|
@@ -13815,6 +14100,28 @@ function compositeKeyValue(columns, values) {
|
|
|
13815
14100
|
}
|
|
13816
14101
|
return columns.map((column, index) => secondaryTupleComponent(column.type, values[index] ?? null)).join("");
|
|
13817
14102
|
}
|
|
14103
|
+
function foldColumnName(table, name) {
|
|
14104
|
+
const columns = visibleTableColumns(table);
|
|
14105
|
+
if (columns.some((column) => column.name === name))
|
|
14106
|
+
return name;
|
|
14107
|
+
const lowered = name.toLowerCase();
|
|
14108
|
+
const matches = columns.filter((column) => column.name.toLowerCase() === lowered);
|
|
14109
|
+
return matches.length === 1 ? matches[0]?.name ?? name : name;
|
|
14110
|
+
}
|
|
14111
|
+
function foldInsertColumns(table, statement) {
|
|
14112
|
+
const columns = statement.columns.map((name) => foldColumnName(table, name));
|
|
14113
|
+
return columns.some((name, index) => name !== statement.columns[index]) ? { ...statement, columns } : statement;
|
|
14114
|
+
}
|
|
14115
|
+
function foldAssignmentColumns(table, assignments) {
|
|
14116
|
+
const folded = assignments.map((assignment) => {
|
|
14117
|
+
const column = foldColumnName(table, assignment.column);
|
|
14118
|
+
return column === assignment.column ? assignment : { ...assignment, column };
|
|
14119
|
+
});
|
|
14120
|
+
return folded.some((assignment, index) => assignment !== assignments[index]) ? folded : assignments;
|
|
14121
|
+
}
|
|
14122
|
+
function quoteIdentifier(name) {
|
|
14123
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
14124
|
+
}
|
|
13818
14125
|
function normalizeDomainBatch(table, input) {
|
|
13819
14126
|
for (const column of table.columns) {
|
|
13820
14127
|
const values = input.columns[column.name];
|
|
@@ -13899,23 +14206,8 @@ function normalizeDomainUpdate(table, input) {
|
|
|
13899
14206
|
function normalizePlanDomainLiterals(input, domains, types = /* @__PURE__ */ new Map()) {
|
|
13900
14207
|
const pass = { probing: true, needed: false };
|
|
13901
14208
|
const coerceText = (type, text) => {
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
if (type === "number") {
|
|
13905
|
-
const trimmed = text.trim();
|
|
13906
|
-
if (trimmed === "")
|
|
13907
|
-
return void 0;
|
|
13908
|
-
const parsed = Number(trimmed);
|
|
13909
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
13910
|
-
}
|
|
13911
|
-
if (type === "boolean") {
|
|
13912
|
-
const lowered = text.trim().toLowerCase();
|
|
13913
|
-
if (lowered === "t" || lowered === "true" || lowered === "1")
|
|
13914
|
-
return true;
|
|
13915
|
-
if (lowered === "f" || lowered === "false" || lowered === "0")
|
|
13916
|
-
return false;
|
|
13917
|
-
}
|
|
13918
|
-
return void 0;
|
|
14209
|
+
const read = readUntypedText(type, text);
|
|
14210
|
+
return read === text ? void 0 : read;
|
|
13919
14211
|
};
|
|
13920
14212
|
const rewriteBlock = (block) => {
|
|
13921
14213
|
if (pass.probing && pass.needed)
|
|
@@ -14493,9 +14785,15 @@ function installStreamedWindow(vector, resident, windowStart, windowRows, memory
|
|
|
14493
14785
|
mutable.window = { start: windowStart, length: windowRows };
|
|
14494
14786
|
}
|
|
14495
14787
|
function derivedColumnarTable(name, result, schema) {
|
|
14496
|
-
const columns = new Map(schema.map(({ name: columnName, type }) => [
|
|
14788
|
+
const columns = new Map(schema.map(({ name: columnName, type, sqlDomain }) => [
|
|
14497
14789
|
columnName,
|
|
14498
|
-
{
|
|
14790
|
+
{
|
|
14791
|
+
type,
|
|
14792
|
+
values: sqlDomain?.kind === "numeric" ? result.rows.map((row) => {
|
|
14793
|
+
const value = row[columnName] ?? null;
|
|
14794
|
+
return typeof value === "number" ? exactNumericValue(value) : value;
|
|
14795
|
+
}) : result.rows.map((row) => row[columnName] ?? null)
|
|
14796
|
+
}
|
|
14499
14797
|
]));
|
|
14500
14798
|
return createColumnarTable(name, columns);
|
|
14501
14799
|
}
|