@minnowdb/core 0.10.1 → 0.10.2
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/client.js +2 -1
- package/dist/engine/database.js +140 -822
- package/dist/engine/index-terms.js +627 -0
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +2 -1
- package/dist/engine/live-maintenance.js +220 -0
- package/dist/engine/schema.js +2 -1
- package/dist/engine/sql-functions.js +3 -2
- package/dist/engine/sql-quote.js +6 -0
- package/dist/engine/vector.js +1 -3
- package/dist/storage/indexeddb.d.ts +18 -0
- package/dist/storage/indexeddb.js +186 -42
- package/dist/storage/opfs/rpc.js +2 -1
- package/dist/storage/types.d.ts +33 -2
- package/dist/storage/types.js +17 -4
- package/dist/testing/block-store-conformance.js +40 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +9 -0
- package/dist/testing/interaction-simulator.d.ts +319 -0
- package/dist/testing/interaction-simulator.js +1631 -0
- package/package.json +4 -1
- package/dist/engine/client-audit-harness.js +0 -123
- package/dist/storage/indexeddb-audit-helpers.js +0 -269
- package/dist/storage/opfs/power-loss-model.js +0 -62
package/dist/engine/database.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { coordinateWrite } from "./write-coordinator.js";
|
|
2
|
+
import { quoteSqlIdentifier } from "./sql-quote.js";
|
|
2
3
|
import { LiveAggregate } from "./live-aggregate.js";
|
|
3
4
|
import { stageLiveExecution } from "./live-accept.js";
|
|
4
5
|
import { QueryGenerations } from "./query-generations.js";
|
|
@@ -18,8 +19,8 @@ import { dateIsoString, dateMilliseconds } from "../date-value.js";
|
|
|
18
19
|
import { estimateCompactionRowsPerOutput, planAlignedWriteBlockRanges as writeBlockRanges } from "./write-block-planner.js";
|
|
19
20
|
import { MAX_CACHEABLE_TEXT_CHARACTERS, MAX_SQL_PARAMETERS } from "./cache-limits.js";
|
|
20
21
|
import { fillColumnDefaults, patchAutoIncrementValues } from "./defaults.js";
|
|
21
|
-
import { cachedQueryTerms, FTS_TOKENIZER_VERSION
|
|
22
|
-
import { boundedMaintenanceBatchItems, simpleDataTypes, floorWholeNumberProduct, BlockReadBatchTooLargeError, validateColumnDefault, validateEnumValues, secondaryIndexColumnIds, secondaryIndexDirections, secondaryUniqueKeyNamespace, CompactionJobConflictError, CompactionBacklogError, GarbageCollectionJobConflictError, MAX_LEVEL_ZERO_SEGMENTS, MAX_FTS_CANDIDATE_ROW_IDS,
|
|
22
|
+
import { cachedQueryTerms, FTS_TOKENIZER_VERSION } from "./fts.js";
|
|
23
|
+
import { boundedMaintenanceBatchItems, simpleDataTypes, floorWholeNumberProduct, BlockReadBatchTooLargeError, validateColumnDefault, validateEnumValues, secondaryIndexColumnIds, secondaryIndexDirections, secondaryUniqueKeyNamespace, CompactionJobConflictError, CompactionBacklogError, GarbageCollectionJobConflictError, MAX_LEVEL_ZERO_SEGMENTS, MAX_FTS_CANDIDATE_ROW_IDS, MAX_CATALOG_NAME_CHARACTERS, MAX_MANIFEST_BLOCK_PRESENCE_IDS, MAX_MAINTENANCE_BATCH_ITEMS, MAX_STORAGE_BULK_READ_ITEMS, MAX_BLOCK_READ_BATCH_BYTES, 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";
|
|
23
24
|
import { decodeSnapshotFrameStream, encodeSnapshotFrameStreamFooter, encodeSnapshotFrameStreamHeader, extendSnapshotFrameStreamChecksum, snapshotFrameEnvelopeParts, snapshotFrameStreamHeaderIdentity } from "../storage/snapshot.js";
|
|
24
25
|
import { Snapshot, TransactionManager } from "../transactions/index.js";
|
|
25
26
|
import { bindPendingSelectShapes, bindPlanParameters, bindStatementParameters, DUAL_TABLE, dualTableRows, blockHasSubqueries, combineUnionResults, compileCheckExpression, compileQuery, hasAggregate, createRecursiveCteState, annotateAvgArgumentScales, compileStatement, comparisonHolds, createPreparedColumnarQuery, evaluateJoinedRowExpression, evaluateRowExpression, externalizeQueryResult, markQueryResultExternal, expressionColumnNames, inferBlockSchema, inferResultColumnDomains, isDeferredInsertExpression, isDefaultInsertValue, referencedColumns, childExpressions, mapChildExpressions, 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";
|
|
@@ -27,11 +28,13 @@ import { copyQueryResult, planMemoKey, queryResultMemoKey, queryResultRetainedBy
|
|
|
27
28
|
import { QueryMemoryBudgetError, QueryMemoryContext, DEFAULT_QUERY_MEMORY_BUDGET_BYTES } from "./memory.js";
|
|
28
29
|
import { LiveQueryLimitError, LiveQuerySet, MAX_LIVE_QUERY_SETS_PER_DATABASE } from "./live.js";
|
|
29
30
|
import { chooseJoinOrder, optimizePlan, qualifyCorrelatedReferences, renderPlan } from "./optimizer.js";
|
|
30
|
-
import { encodeSqlEqualityValue, readUntypedText
|
|
31
|
+
import { encodeSqlEqualityValue, readUntypedText } from "./sql-semantics.js";
|
|
31
32
|
import { exactNumericAsNumber, exactNumericValue, externalSqlDomainValue, externalSqlTextValue, isDateDomainValue, isExactNumeric, isSqlDomainValue, normalizeSqlDomainValue, protectedSqlTextValue } from "./sql-domains.js";
|
|
32
33
|
import { toCatalog } from "./catalog.js";
|
|
33
34
|
import { applyColumnSteps, assertColumnDroppable, compileGeneratedColumnExpression, declaredForeignKeys, isDestructiveStep, planMigration } from "./schema.js";
|
|
34
35
|
import { columnarTableFromRows, createColumnarTable, createColumnVector, vectorValue } from "./vector.js";
|
|
36
|
+
import { ASCENDING_NULL_COMPONENT, LOWEST_SECONDARY_COMPONENT, addFtsDocument, addSecondaryPosting, appendRowForLocator, appendRowIdLocator, assertBatchSecondaryTermsDistinct, assertNoDuplicateUniqueTerms, buildFtsColumnDeltas, buildSecondaryDeleteCoverage, buildSecondaryInsertDeltas, buildSecondaryUpdateDeltas, chunkFtsPostings, decodeSecondaryTupleTerm, getUniqueKeyColumn, keyToken, mergeSourceRowIdSpans, postingFrequencyTotal, readyUniqueSecondaryIndexes, rowIdSpanEnvelope, secondaryIndexComponentTerm, secondaryIndexTerm, secondaryIndexTermsCoverNulls, secondaryIndexUpdateNeedsPreImages, secondaryKeyLocator, secondaryTupleComponent, secondaryUniqueTerm, sortedFtsPostings, sortedSecondaryPostings, stageSecondaryUniqueInsertChanges, stageSecondaryUniqueMutationChanges } from "./index-terms.js";
|
|
37
|
+
import { LIVE_HIDDEN_PREFIX, LIVE_KEY_ALIAS, LIVE_MAINTENANCE_MAX_DELTA_ROWS, LIVE_ORDER_ALIAS, LIVE_WINDOW_MARGIN_MAX, LIVE_WINDOW_MARGIN_MIN, filterLiveRows, liveKeyToken, liveMaintainedOutcome, liveOrderComparator, liveRowStateBytes, mergeLiveRows, splitLiveHiddenColumns, trimLiveRows } from "./live-maintenance.js";
|
|
35
38
|
import { cachedPointReadTemplate, equalRunRange, pointReadTestHooks, resolvePointReadShape, valuesAreAscending } from "./point-read.js";
|
|
36
39
|
const PLATFORM_LITTLE_ENDIAN = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
|
|
37
40
|
const vectorTextDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
@@ -418,9 +421,6 @@ function resolveMutationStatementDatetimes(statement, now) {
|
|
|
418
421
|
}
|
|
419
422
|
return statement;
|
|
420
423
|
}
|
|
421
|
-
function quoteSqlIdentifier(identifier) {
|
|
422
|
-
return `"${identifier.replaceAll('"', '""')}"`;
|
|
423
|
-
}
|
|
424
424
|
const MAX_VISIBLE_SEGMENT_PAGE_ITEMS = 64;
|
|
425
425
|
function streamingSnapshotExportStore(store) {
|
|
426
426
|
const beginSnapshotFrameExport = store.beginSnapshotFrameExport?.bind(store);
|
|
@@ -1003,7 +1003,7 @@ class MinnowDatabase {
|
|
|
1003
1003
|
directions: indexedColumns.map(() => "asc"),
|
|
1004
1004
|
unique: true,
|
|
1005
1005
|
uniqueEnforced: true,
|
|
1006
|
-
termEncoding: "tuple-
|
|
1006
|
+
termEncoding: "tuple-v2",
|
|
1007
1007
|
storage: "postings-v1",
|
|
1008
1008
|
storageColumnId: this.#createId(),
|
|
1009
1009
|
locator: uniqueKeyColumn === void 0 ? "row-id" : "key-hash-v1",
|
|
@@ -4891,7 +4891,7 @@ class MinnowDatabase {
|
|
|
4891
4891
|
if (!fires && conflictWhere === void 0 && !retiresUniqueTerms || rowCount === 0) {
|
|
4892
4892
|
return void 0;
|
|
4893
4893
|
}
|
|
4894
|
-
const quote =
|
|
4894
|
+
const quote = quoteSqlIdentifier;
|
|
4895
4895
|
const keyValues = batch.columns[keyColumn.name] ?? [];
|
|
4896
4896
|
const distinct = /* @__PURE__ */ new Map();
|
|
4897
4897
|
for (const value of keyValues) {
|
|
@@ -4972,7 +4972,7 @@ class MinnowDatabase {
|
|
|
4972
4972
|
if (lookup !== void 0) {
|
|
4973
4973
|
rows = await lookup(keys, "*");
|
|
4974
4974
|
} else {
|
|
4975
|
-
const quote =
|
|
4975
|
+
const quote = quoteSqlIdentifier;
|
|
4976
4976
|
const placeholders = keys.map(() => "?").join(", ");
|
|
4977
4977
|
const preImageSql = `SELECT * FROM ${quote(table.name)} WHERE ${quote(keyColumn.name)} IN (${placeholders})`;
|
|
4978
4978
|
const result = readRows === void 0 ? await this.#readStoredRows(preImageSql, [...keys]) : await readRows(preImageSql, [...keys]);
|
|
@@ -6055,7 +6055,7 @@ class MinnowDatabase {
|
|
|
6055
6055
|
const direct = await this.#scopeRowsByKey(transaction, table, keyColumn, keys, projection);
|
|
6056
6056
|
if (direct !== void 0)
|
|
6057
6057
|
return direct;
|
|
6058
|
-
const quote =
|
|
6058
|
+
const quote = quoteSqlIdentifier;
|
|
6059
6059
|
const selected = projection === "*" ? "*" : projection.map(quote).join(", ");
|
|
6060
6060
|
const rows = [];
|
|
6061
6061
|
for (let start = 0; start < keys.length; start += SCOPE_KEY_LOOKUP_WINDOW) {
|
|
@@ -7803,7 +7803,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
7803
7803
|
}
|
|
7804
7804
|
return {
|
|
7805
7805
|
kind: "rows",
|
|
7806
|
-
result: await this.query(`SELECT $1 AS ${
|
|
7806
|
+
result: await this.query(`SELECT $1 AS ${quoteSqlIdentifier(name)}`, { params: [value] })
|
|
7807
7807
|
};
|
|
7808
7808
|
}
|
|
7809
7809
|
if (statement.kind === "create-enum") {
|
|
@@ -8170,7 +8170,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8170
8170
|
}
|
|
8171
8171
|
if (statement.kind === "transaction") {
|
|
8172
8172
|
const action = statement.action === "rollback-to" ? "ROLLBACK TO" : statement.action;
|
|
8173
|
-
return this.execute(`${action}${statement.name === void 0 ? "" : ` ${
|
|
8173
|
+
return this.execute(`${action}${statement.name === void 0 ? "" : ` ${quoteSqlIdentifier(statement.name)}`}`);
|
|
8174
8174
|
}
|
|
8175
8175
|
if (options.writer === void 0) {
|
|
8176
8176
|
return this.#coordinateWrite(async () => {
|
|
@@ -8727,7 +8727,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
8727
8727
|
return true;
|
|
8728
8728
|
return keyPredicates.every((predicate) => zoneMapCanMatch(description, predicate));
|
|
8729
8729
|
});
|
|
8730
|
-
if (canAffect)
|
|
8730
|
+
if (segment.kind === "delete" || canAffect)
|
|
8731
8731
|
prunedSegments.push(segment);
|
|
8732
8732
|
continue;
|
|
8733
8733
|
}
|
|
@@ -9936,13 +9936,21 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9936
9936
|
async compactTable(tableName, options = {}) {
|
|
9937
9937
|
return this.#foreground(async () => {
|
|
9938
9938
|
const maxBlocks = boundedMaintenanceBatchItems(options.maxBlocksPerStep ?? 16, "Compaction blocks per step");
|
|
9939
|
-
let
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
9943
|
-
|
|
9939
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
9940
|
+
try {
|
|
9941
|
+
let progress = await this.compactTableStep(tableName, { ...options, maxBlocks });
|
|
9942
|
+
while (progress.result === null) {
|
|
9943
|
+
if (progress.jobId === null)
|
|
9944
|
+
throw new Error("Compaction progress lost its job ID");
|
|
9945
|
+
progress = await this.resumeCompactionJob(progress.jobId, { maxBlocks });
|
|
9946
|
+
}
|
|
9947
|
+
return progress.result;
|
|
9948
|
+
} catch (error) {
|
|
9949
|
+
if (!isTransientCompactionConflict(error) || attempt >= this.#maxCommitRetries) {
|
|
9950
|
+
throw error;
|
|
9951
|
+
}
|
|
9952
|
+
}
|
|
9944
9953
|
}
|
|
9945
|
-
return progress.result;
|
|
9946
9954
|
});
|
|
9947
9955
|
}
|
|
9948
9956
|
async compactTableStep(tableName, options = {}) {
|
|
@@ -10046,13 +10054,21 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
10046
10054
|
do {
|
|
10047
10055
|
reconciliationCursor = (await this.#reconcileCompactionPage(reconciliationCursor)).nextCursor;
|
|
10048
10056
|
} while (reconciliationCursor !== null);
|
|
10049
|
-
let progress
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10057
|
+
let progress;
|
|
10058
|
+
for (let attempt = 0; progress?.result == null; attempt += 1) {
|
|
10059
|
+
try {
|
|
10060
|
+
progress = progress === void 0 ? await this.collectGarbageStep({
|
|
10061
|
+
maxItems,
|
|
10062
|
+
...options.maxPlanningItems === void 0 ? {} : { maxPlanningItems: options.maxPlanningItems },
|
|
10063
|
+
...options.retainRecentVersions === void 0 ? {} : { retainRecentVersions: options.retainRecentVersions }
|
|
10064
|
+
}) : await this.resumeGarbageCollectionJob(progress.jobId, { maxItems });
|
|
10065
|
+
} catch (error) {
|
|
10066
|
+
if (!(error instanceof GarbageCollectionJobConflictError || error instanceof TransactionRecordConflictError || error instanceof CompactionJobConflictError) || attempt >= 64) {
|
|
10067
|
+
throw error;
|
|
10068
|
+
}
|
|
10069
|
+
progress = void 0;
|
|
10070
|
+
await this.#yieldMaintenance();
|
|
10071
|
+
}
|
|
10056
10072
|
}
|
|
10057
10073
|
await this.#pruneFinishedJobRecords();
|
|
10058
10074
|
this.#autoCollectionDebtCommits = 0;
|
|
@@ -10138,6 +10154,12 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
10138
10154
|
if (reclaimed || !moreWork)
|
|
10139
10155
|
this.#autoCollectionDebtCommits = 0;
|
|
10140
10156
|
}).catch((error) => {
|
|
10157
|
+
if (this.#closed)
|
|
10158
|
+
return;
|
|
10159
|
+
if (error instanceof GarbageCollectionJobConflictError) {
|
|
10160
|
+
this.#autoCollectionRequested = true;
|
|
10161
|
+
return;
|
|
10162
|
+
}
|
|
10141
10163
|
this.#autoCollectionConsecutiveFailures += 1;
|
|
10142
10164
|
const at = dateMilliseconds(this.#now());
|
|
10143
10165
|
this.#autoCollectionLastError = {
|
|
@@ -10431,15 +10453,19 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
10431
10453
|
}
|
|
10432
10454
|
};
|
|
10433
10455
|
let discovered;
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10456
|
+
for (let attempt = 0; discovered === void 0; attempt += 1) {
|
|
10457
|
+
try {
|
|
10458
|
+
discovered = await this.store.createGarbageCollectionJob(input);
|
|
10459
|
+
} catch (error) {
|
|
10460
|
+
if (!(error instanceof GarbageCollectionJobConflictError))
|
|
10461
|
+
throw error;
|
|
10462
|
+
const raced = await this.#findActiveGarbageCollectionJob();
|
|
10463
|
+
if (raced !== void 0)
|
|
10464
|
+
return raced;
|
|
10465
|
+
if (attempt >= 3)
|
|
10466
|
+
throw error;
|
|
10467
|
+
await this.#yieldMaintenance();
|
|
10468
|
+
}
|
|
10443
10469
|
}
|
|
10444
10470
|
if (continuation !== void 0) {
|
|
10445
10471
|
await this.store.removeGarbageCollectionJob(continuation.jobId);
|
|
@@ -11421,7 +11447,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
11421
11447
|
columns: columns2
|
|
11422
11448
|
});
|
|
11423
11449
|
}
|
|
11424
|
-
assertCanonicalMergeSourceOrder(
|
|
11450
|
+
assertCanonicalMergeSourceOrder(sourceSegments.map((segment) => sourceOrderTuple(segment, transactions, "Compaction source")));
|
|
11425
11451
|
const resolved = await this.#resolveMergeOutput(table, describedSegments, keyColumn, memoryBudgetBytes, snapshot);
|
|
11426
11452
|
const { columns, rowIdSpans, totalRows } = resolved;
|
|
11427
11453
|
const rowIdEnvelope = rowIdSpanEnvelope(rowIdSpans);
|
|
@@ -11900,6 +11926,12 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
11900
11926
|
this.#afterCompactionCommit(manifest2, table.id);
|
|
11901
11927
|
return manifest2;
|
|
11902
11928
|
} catch (error) {
|
|
11929
|
+
if (error instanceof SchemaConflictError) {
|
|
11930
|
+
if (transaction.status === "active")
|
|
11931
|
+
await transaction.abort();
|
|
11932
|
+
job = await this.#abortCompactionJob(job, "Schema changed during compaction");
|
|
11933
|
+
throw error;
|
|
11934
|
+
}
|
|
11903
11935
|
if (!(error instanceof WriteConflictError))
|
|
11904
11936
|
throw error;
|
|
11905
11937
|
publicationConflict = error;
|
|
@@ -12704,25 +12736,42 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
12704
12736
|
const indexId = this.#createId();
|
|
12705
12737
|
const storageColumnId = `secondary-index:${indexId}`;
|
|
12706
12738
|
const buildId = this.#createId();
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12739
|
+
let current = table;
|
|
12740
|
+
let marked;
|
|
12741
|
+
for (let attempt = 0; marked === void 0; attempt += 1) {
|
|
12742
|
+
try {
|
|
12743
|
+
marked = await this.store.updateTable(current.id, current.revision, {
|
|
12744
|
+
secondaryIndexes: {
|
|
12745
|
+
...current.secondaryIndexes,
|
|
12746
|
+
[indexId]: {
|
|
12747
|
+
name: indexName,
|
|
12748
|
+
columnId: columns[0]?.id ?? "",
|
|
12749
|
+
columnIds: columns.map((column) => column.id),
|
|
12750
|
+
directions: indexColumns.map((column) => column.direction),
|
|
12751
|
+
...options.unique === true ? { unique: true } : {},
|
|
12752
|
+
termEncoding: "tuple-v2",
|
|
12753
|
+
storage: "postings-v1",
|
|
12754
|
+
storageColumnId,
|
|
12755
|
+
locator: current.uniqueKeyColumnId === void 0 ? "row-id" : "key-hash-v1",
|
|
12756
|
+
state: "building",
|
|
12757
|
+
buildId,
|
|
12758
|
+
buildFromVersion: -1
|
|
12759
|
+
}
|
|
12760
|
+
}
|
|
12761
|
+
});
|
|
12762
|
+
} catch (error) {
|
|
12763
|
+
if (!(error instanceof TableRecordConflictError) || attempt >= this.#maxCommitRetries) {
|
|
12764
|
+
throw error;
|
|
12765
|
+
}
|
|
12766
|
+
const fresh = await this.store.getTable(current.id);
|
|
12767
|
+
if (fresh === void 0)
|
|
12768
|
+
throw new UnknownTableError(tableName);
|
|
12769
|
+
if (Object.values(fresh.secondaryIndexes ?? {}).some((index) => index.name === indexName)) {
|
|
12770
|
+
throw new TypeError(`Index already exists: ${indexName}`, { cause: error });
|
|
12723
12771
|
}
|
|
12772
|
+
current = fresh;
|
|
12724
12773
|
}
|
|
12725
|
-
}
|
|
12774
|
+
}
|
|
12726
12775
|
try {
|
|
12727
12776
|
await this.#buildSecondaryIndex(marked, indexId);
|
|
12728
12777
|
} catch (error) {
|
|
@@ -12970,8 +13019,6 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
12970
13019
|
const byTerm = /* @__PURE__ */ new Map();
|
|
12971
13020
|
for (let row = start; row < end; row += 1) {
|
|
12972
13021
|
const values = valueVectors.map((vector, position) => vector === void 0 ? null : storedSqlValueFromExecution(columns[position], vectorValue(vector, row)));
|
|
12973
|
-
if (values.some((value) => value === null))
|
|
12974
|
-
continue;
|
|
12975
13022
|
const locator = secondaryKeyLocator(keyColumn.type, storedSqlValueFromExecution(keyColumn, vectorValue(keyVector, row)));
|
|
12976
13023
|
addSecondaryPosting(byTerm, index, columns, values, locator, uniqueTerms);
|
|
12977
13024
|
}
|
|
@@ -13022,8 +13069,6 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
13022
13069
|
const byTerm = /* @__PURE__ */ new Map();
|
|
13023
13070
|
for (let row = start; row < end; row += 1) {
|
|
13024
13071
|
const values = valueVectors.map((vector, position) => vector === void 0 ? null : storedSqlValueFromExecution(columns[position], vectorValue(vector, row)));
|
|
13025
|
-
if (values.some((value) => value === null))
|
|
13026
|
-
continue;
|
|
13027
13072
|
const locator = keyColumn === void 0 ? rowIdAt?.(row) ?? 0n : secondaryKeyLocator(keyColumn.type, keyVector === void 0 ? null : storedSqlValueFromExecution(keyColumn, vectorValue(keyVector, row)));
|
|
13028
13073
|
addSecondaryPosting(byTerm, index, columns, values, locator, uniqueTerms);
|
|
13029
13074
|
}
|
|
@@ -13112,8 +13157,6 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
13112
13157
|
const vector = vectors[position];
|
|
13113
13158
|
return vector === void 0 ? column.backfill ?? null : vectorValue(vector, row);
|
|
13114
13159
|
});
|
|
13115
|
-
if (values.some((value) => value === null))
|
|
13116
|
-
continue;
|
|
13117
13160
|
const locator = keyColumn === void 0 ? rowIdAt?.(segmentRow + row) ?? 0n : secondaryKeyLocator(keyColumn.type, keys === void 0 ? null : vectorValue(keys, row));
|
|
13118
13161
|
addSecondaryPosting(byTerm, index, columns, values, locator);
|
|
13119
13162
|
}
|
|
@@ -13523,6 +13566,10 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
13523
13566
|
let selectedRowStart = 0;
|
|
13524
13567
|
const selected = [];
|
|
13525
13568
|
for (const segment of segments) {
|
|
13569
|
+
if (segment.kind === "delete") {
|
|
13570
|
+
selected.push(segment);
|
|
13571
|
+
continue;
|
|
13572
|
+
}
|
|
13526
13573
|
const anchorColumn2 = keyColumn ?? table.columns[0];
|
|
13527
13574
|
if (anchorColumn2 === void 0)
|
|
13528
13575
|
return { segments, pruned: false };
|
|
@@ -13606,7 +13653,10 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
13606
13653
|
]))
|
|
13607
13654
|
});
|
|
13608
13655
|
}
|
|
13609
|
-
|
|
13656
|
+
if (rowPositions === void 0) {
|
|
13657
|
+
return { segments: withEveryDelta(segments, selected), pruned: true };
|
|
13658
|
+
}
|
|
13659
|
+
return { segments: selected, pruned: true, rows: rowPositions };
|
|
13610
13660
|
}
|
|
13611
13661
|
#scheduleSecondaryIndexBuild(table, indexId) {
|
|
13612
13662
|
if (this.#closed)
|
|
@@ -13785,7 +13835,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
13785
13835
|
const kind = segment.kind;
|
|
13786
13836
|
return kind === "insert" || kind === "base" || kind === "delete" || kind === "update";
|
|
13787
13837
|
})) {
|
|
13788
|
-
const overlay = await this.#materializeOverlayTable(table, snapshot, projectedColumns, segments, keyColumn, plan, indexed.pruned);
|
|
13838
|
+
const overlay = await this.#materializeOverlayTable(table, snapshot, projectedColumns, indexed.rows === void 0 ? segments : withEveryDelta(visibleSegments, segments), keyColumn, plan, indexed.pruned);
|
|
13789
13839
|
if (overlay !== void 0)
|
|
13790
13840
|
return overlay;
|
|
13791
13841
|
}
|
|
@@ -15429,35 +15479,6 @@ function sortedRowIdsIntersectRange(sorted, start, endExclusive) {
|
|
|
15429
15479
|
}
|
|
15430
15480
|
return low < sorted.length && (sorted[low] ?? 0n) < endExclusive;
|
|
15431
15481
|
}
|
|
15432
|
-
function addFtsDocument(byTerm, value, rowId) {
|
|
15433
|
-
const rendered = renderDocumentValue(value);
|
|
15434
|
-
if (rendered === void 0)
|
|
15435
|
-
return 0;
|
|
15436
|
-
const tokens = ftsTokenize(rendered);
|
|
15437
|
-
const counts = /* @__PURE__ */ new Map();
|
|
15438
|
-
for (const token of tokens)
|
|
15439
|
-
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
15440
|
-
for (const [term, tf] of counts) {
|
|
15441
|
-
const posting = byTerm.get(term) ?? { rowIds: [], tf: [] };
|
|
15442
|
-
posting.rowIds.push(rowId);
|
|
15443
|
-
posting.tf.push(tf);
|
|
15444
|
-
byTerm.set(term, posting);
|
|
15445
|
-
}
|
|
15446
|
-
return tokens.length;
|
|
15447
|
-
}
|
|
15448
|
-
function sortedFtsPostings(byTerm) {
|
|
15449
|
-
return [...byTerm.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([term, posting]) => ({ term, rowIds: posting.rowIds, tf: posting.tf }));
|
|
15450
|
-
}
|
|
15451
|
-
function postingFrequencyTotal(postings) {
|
|
15452
|
-
let total = 0;
|
|
15453
|
-
for (const posting of postings) {
|
|
15454
|
-
for (const frequency of posting.tf) {
|
|
15455
|
-
total = safeWholeNumberSum([total, frequency], "Posting term-frequency total");
|
|
15456
|
-
}
|
|
15457
|
-
}
|
|
15458
|
-
return total;
|
|
15459
|
-
}
|
|
15460
|
-
const secondaryNumberBits = new DataView(new ArrayBuffer(8));
|
|
15461
15482
|
function storedSqlValueFromExecution(column, value) {
|
|
15462
15483
|
if (column?.type === "string" && column.sqlDomain === void 0 && typeof value === "string") {
|
|
15463
15484
|
return externalSqlTextValue(value);
|
|
@@ -15487,661 +15508,11 @@ function executionSqlValueFromInput(column, value) {
|
|
|
15487
15508
|
const stored = column?.sqlDomain === void 0 ? value : normalizeSqlDomainValue(column.sqlDomain, value);
|
|
15488
15509
|
return executionSqlValueFromStorage(column, stored);
|
|
15489
15510
|
}
|
|
15490
|
-
function secondaryIndexTerm(type, value) {
|
|
15491
|
-
if (value === null)
|
|
15492
|
-
throw new TypeError("A NULL has no secondary-index comparison term");
|
|
15493
|
-
if (type === "string") {
|
|
15494
|
-
if (typeof value !== "string")
|
|
15495
|
-
throw new TypeError("Invalid string index value");
|
|
15496
|
-
assertIndexedStringLength(value);
|
|
15497
|
-
return value;
|
|
15498
|
-
}
|
|
15499
|
-
if (type === "boolean") {
|
|
15500
|
-
if (typeof value !== "boolean")
|
|
15501
|
-
throw new TypeError("Invalid boolean index value");
|
|
15502
|
-
return value ? "1" : "0";
|
|
15503
|
-
}
|
|
15504
|
-
const numeric = type === "datetime" && value instanceof Date ? dateMilliseconds(value) : value;
|
|
15505
|
-
if (typeof numeric !== "number" || !Number.isFinite(numeric)) {
|
|
15506
|
-
throw new TypeError(`Invalid ${type} index value`);
|
|
15507
|
-
}
|
|
15508
|
-
secondaryNumberBits.setFloat64(0, numeric === 0 ? 0 : numeric, false);
|
|
15509
|
-
const bits = secondaryNumberBits.getBigUint64(0, false);
|
|
15510
|
-
const sortable = (bits & 0x8000000000000000n) === 0n ? bits ^ 0x8000000000000000n : ~bits & 0xffffffffffffffffn;
|
|
15511
|
-
return sortable.toString(16).padStart(16, "0");
|
|
15512
|
-
}
|
|
15513
|
-
function secondaryTupleComponent(type, value) {
|
|
15514
|
-
if (value === null)
|
|
15515
|
-
throw new TypeError("A NULL has no secondary-index comparison term");
|
|
15516
|
-
if (type === "string") {
|
|
15517
|
-
if (typeof value !== "string")
|
|
15518
|
-
throw new TypeError("Invalid string index value");
|
|
15519
|
-
assertIndexedStringLength(value);
|
|
15520
|
-
if (value.length * 5 + 5 > MAX_FTS_POSTING_TERM_CHARACTERS) {
|
|
15521
|
-
throw new RangeError("Composite index term exceeds the persisted term limit");
|
|
15522
|
-
}
|
|
15523
|
-
let encoded = "";
|
|
15524
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
15525
|
-
encoded += (value.charCodeAt(index) + 1).toString(16).padStart(5, "0");
|
|
15526
|
-
}
|
|
15527
|
-
return `${encoded}00000`;
|
|
15528
|
-
}
|
|
15529
|
-
if (type === "boolean") {
|
|
15530
|
-
if (typeof value !== "boolean")
|
|
15531
|
-
throw new TypeError("Invalid boolean index value");
|
|
15532
|
-
return value ? "1" : "0";
|
|
15533
|
-
}
|
|
15534
|
-
return secondaryIndexTerm(type, value);
|
|
15535
|
-
}
|
|
15536
|
-
const reversedHex = new Map(Array.from("0123456789abcdef", (character, index) => [
|
|
15537
|
-
character,
|
|
15538
|
-
"fedcba9876543210"[index] ?? ""
|
|
15539
|
-
]));
|
|
15540
|
-
function reverseSecondaryHex(input) {
|
|
15541
|
-
let output = "";
|
|
15542
|
-
for (let index = 0; index < input.length; index += 1) {
|
|
15543
|
-
const character = input.charAt(index);
|
|
15544
|
-
const reversed = reversedHex.get(character);
|
|
15545
|
-
if (reversed === void 0)
|
|
15546
|
-
throw new Error("Secondary index has a non-hexadecimal term");
|
|
15547
|
-
output += reversed;
|
|
15548
|
-
}
|
|
15549
|
-
return output;
|
|
15550
|
-
}
|
|
15551
|
-
function secondaryTupleIndexTerm(index, columns, values) {
|
|
15552
|
-
if (columns.length !== values.length)
|
|
15553
|
-
throw new TypeError("Index key has the wrong arity");
|
|
15554
|
-
const directions = secondaryIndexDirections(index);
|
|
15555
|
-
let term = "";
|
|
15556
|
-
for (const [position, column] of columns.entries()) {
|
|
15557
|
-
const component = secondaryTupleComponent(column.type, values[position] ?? null);
|
|
15558
|
-
if (term.length + component.length > MAX_FTS_POSTING_TERM_CHARACTERS) {
|
|
15559
|
-
throw new RangeError("Composite index term exceeds the persisted term limit");
|
|
15560
|
-
}
|
|
15561
|
-
term += directions[position] === "desc" ? reverseSecondaryHex(component) : component;
|
|
15562
|
-
}
|
|
15563
|
-
return term;
|
|
15564
|
-
}
|
|
15565
|
-
function secondaryIndexComponentTerm(index, column, position, value) {
|
|
15566
|
-
const component = secondaryTupleComponent(column.type, value);
|
|
15567
|
-
if (secondaryIndexDirections(index)[position] !== "desc")
|
|
15568
|
-
return component;
|
|
15569
|
-
return reverseSecondaryHex(component);
|
|
15570
|
-
}
|
|
15571
|
-
function decodeSecondaryTupleTerm(index, columns, term) {
|
|
15572
|
-
const directions = secondaryIndexDirections(index);
|
|
15573
|
-
let offset = 0;
|
|
15574
|
-
const values = columns.map((column, position) => {
|
|
15575
|
-
const descending = directions[position] === "desc";
|
|
15576
|
-
const restore = (encoded2) => descending ? reverseSecondaryHex(encoded2) : encoded2;
|
|
15577
|
-
if (column.type === "string") {
|
|
15578
|
-
let value2 = "";
|
|
15579
|
-
for (; ; ) {
|
|
15580
|
-
const group = restore(term.slice(offset, offset + 5));
|
|
15581
|
-
if (group.length !== 5)
|
|
15582
|
-
throw new Error(`Secondary index ${index.name} has a bad term`);
|
|
15583
|
-
offset += 5;
|
|
15584
|
-
if (group === "00000")
|
|
15585
|
-
return value2;
|
|
15586
|
-
const code = Number.parseInt(group, 16) - 1;
|
|
15587
|
-
if (!Number.isInteger(code) || code < 0 || code > 65535) {
|
|
15588
|
-
throw new Error(`Secondary index ${index.name} has a bad string term`);
|
|
15589
|
-
}
|
|
15590
|
-
value2 += String.fromCharCode(code);
|
|
15591
|
-
}
|
|
15592
|
-
}
|
|
15593
|
-
if (column.type === "boolean") {
|
|
15594
|
-
const encoded2 = restore(term.slice(offset, offset + 1));
|
|
15595
|
-
offset += 1;
|
|
15596
|
-
if (encoded2 !== "0" && encoded2 !== "1") {
|
|
15597
|
-
throw new Error(`Secondary index ${index.name} has a bad boolean term`);
|
|
15598
|
-
}
|
|
15599
|
-
return encoded2 === "1";
|
|
15600
|
-
}
|
|
15601
|
-
const encoded = restore(term.slice(offset, offset + 16));
|
|
15602
|
-
if (encoded.length !== 16)
|
|
15603
|
-
throw new Error(`Secondary index ${index.name} has a bad term`);
|
|
15604
|
-
offset += 16;
|
|
15605
|
-
const sortable = BigInt(`0x${encoded}`);
|
|
15606
|
-
const bits = (sortable & 0x8000000000000000n) === 0n ? ~sortable & 0xffffffffffffffffn : sortable ^ 0x8000000000000000n;
|
|
15607
|
-
secondaryNumberBits.setBigUint64(0, bits, false);
|
|
15608
|
-
const value = secondaryNumberBits.getFloat64(0, false);
|
|
15609
|
-
return column.type === "datetime" ? new Date(value) : value;
|
|
15610
|
-
});
|
|
15611
|
-
if (offset !== term.length)
|
|
15612
|
-
throw new Error(`Secondary index ${index.name} has a bad term`);
|
|
15613
|
-
return values;
|
|
15614
|
-
}
|
|
15615
|
-
function secondaryKeyLocator(type, value) {
|
|
15616
|
-
const token = keyToken(type, value);
|
|
15617
|
-
let hash = 2166136261;
|
|
15618
|
-
for (let index = 0; index < token.length; index += 1) {
|
|
15619
|
-
const code = token.charCodeAt(index);
|
|
15620
|
-
hash = Math.imul(hash ^ code & 255, 16777619) >>> 0;
|
|
15621
|
-
hash = Math.imul(hash ^ code >>> 8, 16777619) >>> 0;
|
|
15622
|
-
}
|
|
15623
|
-
return BigInt(hash >>> 0);
|
|
15624
|
-
}
|
|
15625
|
-
function addSecondaryPosting(byTerm, index, columns, values, locator, uniqueTerms) {
|
|
15626
|
-
if (values.some((value) => value === null))
|
|
15627
|
-
return;
|
|
15628
|
-
const first = columns[0];
|
|
15629
|
-
if (first === void 0)
|
|
15630
|
-
return;
|
|
15631
|
-
const term = secondaryTupleIndexTerm(index, columns, values);
|
|
15632
|
-
if (uniqueTerms?.has(term) === true) {
|
|
15633
|
-
throw new TypeError(`UNIQUE index ${index.name} has a duplicate key`);
|
|
15634
|
-
}
|
|
15635
|
-
uniqueTerms?.add(term);
|
|
15636
|
-
const posting = byTerm.get(term) ?? { rowIds: [], tf: [] };
|
|
15637
|
-
posting.rowIds.push(locator);
|
|
15638
|
-
posting.tf.push(1);
|
|
15639
|
-
byTerm.set(term, posting);
|
|
15640
|
-
}
|
|
15641
|
-
function sortedSecondaryPostings(byTerm) {
|
|
15642
|
-
return [...byTerm.entries()].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([term, posting]) => {
|
|
15643
|
-
const rowIds = [...new Set(posting.rowIds)].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
15644
|
-
return { term, rowIds, tf: rowIds.map(() => 1) };
|
|
15645
|
-
});
|
|
15646
|
-
}
|
|
15647
|
-
function appendRowIdLocator(segments, expectedRows) {
|
|
15648
|
-
const spans = appendRowIdSpans(segments, expectedRows);
|
|
15649
|
-
let spanIndex = 0;
|
|
15650
|
-
return (row) => {
|
|
15651
|
-
while (spanIndex < spans.length && row >= (spans[spanIndex]?.rowStart ?? 0) + (spans[spanIndex]?.rowCount ?? 0)) {
|
|
15652
|
-
spanIndex += 1;
|
|
15653
|
-
}
|
|
15654
|
-
const span = spans[spanIndex];
|
|
15655
|
-
if (span === void 0 || row < span.rowStart) {
|
|
15656
|
-
throw new Error(`Secondary-index row ID is missing: ${String(row)}`);
|
|
15657
|
-
}
|
|
15658
|
-
return span.rowIdStart + BigInt(row - span.rowStart);
|
|
15659
|
-
};
|
|
15660
|
-
}
|
|
15661
|
-
function appendRowIdSpans(segments, expectedRows) {
|
|
15662
|
-
const spans = [];
|
|
15663
|
-
let outputStart = 0;
|
|
15664
|
-
for (const segment of segments) {
|
|
15665
|
-
const kind = segment.kind;
|
|
15666
|
-
if (kind !== "insert" && kind !== "base")
|
|
15667
|
-
continue;
|
|
15668
|
-
for (const span of mergeSourceRowIdSpans(segment, kind)) {
|
|
15669
|
-
spans.push({ ...span, rowStart: outputStart + span.rowStart });
|
|
15670
|
-
}
|
|
15671
|
-
outputStart += segment.rowCount;
|
|
15672
|
-
}
|
|
15673
|
-
const rows = spans.reduce((total, span) => total + span.rowCount, 0);
|
|
15674
|
-
if (rows !== expectedRows)
|
|
15675
|
-
throw new Error("Secondary-index row IDs differ from table rows");
|
|
15676
|
-
return spans;
|
|
15677
|
-
}
|
|
15678
|
-
function appendRowForLocator(segments, expectedRows) {
|
|
15679
|
-
const spans = appendRowIdSpans(segments, expectedRows).sort((left, right) => left.rowIdStart < right.rowIdStart ? -1 : left.rowIdStart > right.rowIdStart ? 1 : 0);
|
|
15680
|
-
return (locator) => {
|
|
15681
|
-
let low = 0;
|
|
15682
|
-
let high = spans.length;
|
|
15683
|
-
while (low < high) {
|
|
15684
|
-
const middle = low + high >>> 1;
|
|
15685
|
-
if ((spans[middle]?.rowIdStart ?? 0n) <= locator)
|
|
15686
|
-
low = middle + 1;
|
|
15687
|
-
else
|
|
15688
|
-
high = middle;
|
|
15689
|
-
}
|
|
15690
|
-
const span = spans[low - 1];
|
|
15691
|
-
if (span === void 0)
|
|
15692
|
-
return void 0;
|
|
15693
|
-
const offset = locator - span.rowIdStart;
|
|
15694
|
-
if (offset < 0n || offset >= BigInt(span.rowCount))
|
|
15695
|
-
return void 0;
|
|
15696
|
-
return span.rowStart + Number(offset);
|
|
15697
|
-
};
|
|
15698
|
-
}
|
|
15699
|
-
function chunkFtsPostings(postings, size = 128) {
|
|
15700
|
-
const chunks = [];
|
|
15701
|
-
let chunk = [];
|
|
15702
|
-
let rowIds = 0;
|
|
15703
|
-
const flush = () => {
|
|
15704
|
-
if (chunk.length === 0)
|
|
15705
|
-
return;
|
|
15706
|
-
chunks.push(chunk);
|
|
15707
|
-
chunk = [];
|
|
15708
|
-
rowIds = 0;
|
|
15709
|
-
};
|
|
15710
|
-
for (const posting of postings) {
|
|
15711
|
-
if (posting.rowIds.length > MAX_FTS_POSTING_ROW_IDS_PER_CHUNK) {
|
|
15712
|
-
throw new RangeError("One posting exceeds the persisted row-id chunk limit");
|
|
15713
|
-
}
|
|
15714
|
-
if (chunk.length >= Math.min(size, MAX_FTS_POSTINGS_PER_CHUNK) || rowIds + posting.rowIds.length > MAX_FTS_POSTING_ROW_IDS_PER_CHUNK) {
|
|
15715
|
-
flush();
|
|
15716
|
-
}
|
|
15717
|
-
chunk.push(posting);
|
|
15718
|
-
rowIds += posting.rowIds.length;
|
|
15719
|
-
}
|
|
15720
|
-
flush();
|
|
15721
|
-
return chunks;
|
|
15722
|
-
}
|
|
15723
|
-
function buildFtsColumnDeltas(table, input, rowIdStart) {
|
|
15724
|
-
const active = Object.entries(table.ftsColumns ?? {}).filter(([, record]) => record.state !== "invalid");
|
|
15725
|
-
if (active.length === 0)
|
|
15726
|
-
return [];
|
|
15727
|
-
const columnsById = new Map(table.columns.map((column) => [column.id, column]));
|
|
15728
|
-
return active.flatMap(([columnId]) => {
|
|
15729
|
-
const column = columnsById.get(columnId);
|
|
15730
|
-
if (column === void 0)
|
|
15731
|
-
return [];
|
|
15732
|
-
const byTerm = /* @__PURE__ */ new Map();
|
|
15733
|
-
let totalTokens = 0;
|
|
15734
|
-
(input.columns[column.name] ?? []).forEach((value, index) => {
|
|
15735
|
-
const documentValue = column.type === "string" && column.sqlDomain === void 0 && typeof value === "string" ? protectedSqlTextValue(value) : value;
|
|
15736
|
-
totalTokens += addFtsDocument(byTerm, documentValue, rowIdStart + BigInt(index));
|
|
15737
|
-
});
|
|
15738
|
-
return [{ columnId, postings: sortedFtsPostings(byTerm), totalTokens }];
|
|
15739
|
-
});
|
|
15740
|
-
}
|
|
15741
|
-
function buildSecondaryInsertDeltas(table, input, rowIdStart) {
|
|
15742
|
-
const active = Object.values(table.secondaryIndexes ?? {}).filter((index) => index.state !== "invalid");
|
|
15743
|
-
if (active.length === 0)
|
|
15744
|
-
return [];
|
|
15745
|
-
const columnsById = new Map(table.columns.map((column) => [column.id, column]));
|
|
15746
|
-
const keyColumn = getUniqueKeyColumn(table);
|
|
15747
|
-
return active.flatMap((index) => {
|
|
15748
|
-
const columns = secondaryIndexColumnIds(index).map((columnId) => columnsById.get(columnId));
|
|
15749
|
-
if (columns.some((column) => column === void 0))
|
|
15750
|
-
return [];
|
|
15751
|
-
const indexedColumns = columns;
|
|
15752
|
-
const byTerm = /* @__PURE__ */ new Map();
|
|
15753
|
-
const keys = keyColumn === void 0 ? void 0 : input.columns[keyColumn.name] ?? [];
|
|
15754
|
-
const rowCount = input.rowCount ?? Object.values(input.columns)[0]?.length ?? 0;
|
|
15755
|
-
try {
|
|
15756
|
-
for (let row = 0; row < rowCount; row += 1) {
|
|
15757
|
-
const values = indexedColumns.map((column) => input.columns[column.name]?.[row] ?? null);
|
|
15758
|
-
if (values.some((value) => value === null))
|
|
15759
|
-
continue;
|
|
15760
|
-
const locator = keyColumn === void 0 ? rowIdStart + BigInt(row) : secondaryKeyLocator(keyColumn.type, keys?.[row] ?? null);
|
|
15761
|
-
addSecondaryPosting(byTerm, index, indexedColumns, values, locator);
|
|
15762
|
-
}
|
|
15763
|
-
} catch (error) {
|
|
15764
|
-
if (error instanceof RangeError && index.unique !== true)
|
|
15765
|
-
return [];
|
|
15766
|
-
throw error;
|
|
15767
|
-
}
|
|
15768
|
-
const postings = sortedSecondaryPostings(byTerm);
|
|
15769
|
-
return [
|
|
15770
|
-
{
|
|
15771
|
-
columnId: index.storageColumnId,
|
|
15772
|
-
postings,
|
|
15773
|
-
totalTokens: postingFrequencyTotal(postings)
|
|
15774
|
-
}
|
|
15775
|
-
];
|
|
15776
|
-
});
|
|
15777
|
-
}
|
|
15778
|
-
function buildSecondaryUpdateDeltas(table, input, preImages = []) {
|
|
15779
|
-
const active = Object.values(table.secondaryIndexes ?? {}).filter((index) => index.state !== "invalid");
|
|
15780
|
-
if (active.length === 0)
|
|
15781
|
-
return [];
|
|
15782
|
-
const keyColumn = getUniqueKeyColumn(table);
|
|
15783
|
-
if (keyColumn === void 0)
|
|
15784
|
-
return [];
|
|
15785
|
-
const columnsById = new Map(table.columns.map((column) => [column.id, column]));
|
|
15786
|
-
return active.flatMap((index) => {
|
|
15787
|
-
const columns = secondaryIndexColumnIds(index).map((columnId) => columnsById.get(columnId));
|
|
15788
|
-
if (columns.some((column) => column === void 0))
|
|
15789
|
-
return [];
|
|
15790
|
-
const indexedColumns = columns;
|
|
15791
|
-
const byTerm = /* @__PURE__ */ new Map();
|
|
15792
|
-
const affected = indexedColumns.some((column) => input.changes[column.name] !== void 0);
|
|
15793
|
-
if (affected) {
|
|
15794
|
-
try {
|
|
15795
|
-
for (let row = 0; row < input.keys.length; row += 1) {
|
|
15796
|
-
const values = indexedColumns.map((column) => input.changes[column.name] === void 0 ? preImages[row]?.[column.name] ?? null : input.changes[column.name]?.[row] ?? null);
|
|
15797
|
-
if (values.some((value) => value === null))
|
|
15798
|
-
continue;
|
|
15799
|
-
addSecondaryPosting(byTerm, index, indexedColumns, values, secondaryKeyLocator(keyColumn.type, input.keys[row] ?? null));
|
|
15800
|
-
}
|
|
15801
|
-
} catch (error) {
|
|
15802
|
-
if (error instanceof RangeError && index.unique !== true)
|
|
15803
|
-
return [];
|
|
15804
|
-
throw error;
|
|
15805
|
-
}
|
|
15806
|
-
}
|
|
15807
|
-
const postings = sortedSecondaryPostings(byTerm);
|
|
15808
|
-
return [
|
|
15809
|
-
{
|
|
15810
|
-
columnId: index.storageColumnId,
|
|
15811
|
-
postings,
|
|
15812
|
-
totalTokens: postingFrequencyTotal(postings)
|
|
15813
|
-
}
|
|
15814
|
-
];
|
|
15815
|
-
});
|
|
15816
|
-
}
|
|
15817
|
-
function secondaryIndexUpdateNeedsPreImages(table, input) {
|
|
15818
|
-
const changed = new Set(Object.keys(input.changes));
|
|
15819
|
-
const columnsById = new Map(table.columns.map((column) => [column.id, column.name]));
|
|
15820
|
-
return Object.values(table.secondaryIndexes ?? {}).some((index) => {
|
|
15821
|
-
if (index.state === "invalid")
|
|
15822
|
-
return false;
|
|
15823
|
-
const names = secondaryIndexColumnIds(index).map((columnId) => columnsById.get(columnId));
|
|
15824
|
-
return names.some((name) => name !== void 0 && changed.has(name)) && names.some((name) => name === void 0 || !changed.has(name));
|
|
15825
|
-
});
|
|
15826
|
-
}
|
|
15827
|
-
function buildSecondaryDeleteCoverage(table) {
|
|
15828
|
-
return Object.values(table.secondaryIndexes ?? {}).flatMap((index) => index.state === "invalid" ? [] : [
|
|
15829
|
-
{
|
|
15830
|
-
columnId: index.storageColumnId,
|
|
15831
|
-
postings: [],
|
|
15832
|
-
totalTokens: 0
|
|
15833
|
-
}
|
|
15834
|
-
]);
|
|
15835
|
-
}
|
|
15836
|
-
function readyUniqueSecondaryIndexes(table) {
|
|
15837
|
-
const columnsById = new Map(table.columns.map((column) => [column.id, column]));
|
|
15838
|
-
return Object.entries(table.secondaryIndexes ?? {}).flatMap(([indexId, index]) => {
|
|
15839
|
-
if (index.unique !== true || index.uniqueEnforced !== true)
|
|
15840
|
-
return [];
|
|
15841
|
-
const columns = secondaryIndexColumnIds(index).map((columnId) => columnsById.get(columnId));
|
|
15842
|
-
return columns.some((column) => column === void 0) ? [] : [{ indexId, index, columns }];
|
|
15843
|
-
});
|
|
15844
|
-
}
|
|
15845
|
-
function secondaryUniqueTerm(index, columns, values) {
|
|
15846
|
-
if (values.some((value) => value === null))
|
|
15847
|
-
return void 0;
|
|
15848
|
-
return secondaryTupleIndexTerm(index, columns, values);
|
|
15849
|
-
}
|
|
15850
|
-
function assertNoDuplicateUniqueTerms(index, terms) {
|
|
15851
|
-
const seen = /* @__PURE__ */ new Set();
|
|
15852
|
-
for (const term of terms) {
|
|
15853
|
-
if (seen.has(term))
|
|
15854
|
-
throw new TypeError(`UNIQUE index ${index.name} has a duplicate key`);
|
|
15855
|
-
seen.add(term);
|
|
15856
|
-
}
|
|
15857
|
-
}
|
|
15858
|
-
function assertBatchSecondaryTermsDistinct(table, input) {
|
|
15859
|
-
const rowCount = input.rowCount ?? Object.values(input.columns)[0]?.length ?? 0;
|
|
15860
|
-
for (const { index, columns } of readyUniqueSecondaryIndexes(table)) {
|
|
15861
|
-
const terms = [];
|
|
15862
|
-
for (let row = 0; row < rowCount; row += 1) {
|
|
15863
|
-
const term = secondaryUniqueTerm(index, columns, columns.map((column) => input.columns[column.name]?.[row] ?? null));
|
|
15864
|
-
if (term !== void 0)
|
|
15865
|
-
terms.push(term);
|
|
15866
|
-
}
|
|
15867
|
-
assertNoDuplicateUniqueTerms(index, terms);
|
|
15868
|
-
}
|
|
15869
|
-
}
|
|
15870
|
-
function stageSecondaryUniqueInsertChanges(transaction, table, input, oldImages) {
|
|
15871
|
-
const rowCount = input.rowCount ?? Object.values(input.columns)[0]?.length ?? 0;
|
|
15872
|
-
for (const { indexId, index, columns } of readyUniqueSecondaryIndexes(table)) {
|
|
15873
|
-
const namespaceId = secondaryUniqueKeyNamespace(table.id, indexId);
|
|
15874
|
-
const removed = (oldImages ?? []).flatMap((old) => {
|
|
15875
|
-
if (old === void 0)
|
|
15876
|
-
return [];
|
|
15877
|
-
const term = secondaryUniqueTerm(index, columns, columns.map((column) => old[column.name] ?? null));
|
|
15878
|
-
return term === void 0 ? [] : [term];
|
|
15879
|
-
});
|
|
15880
|
-
if (removed.length > 0) {
|
|
15881
|
-
transaction.setUniqueKeyChanges({
|
|
15882
|
-
tableId: namespaceId,
|
|
15883
|
-
keyTokens: removed,
|
|
15884
|
-
requireAbsent: false,
|
|
15885
|
-
remove: true
|
|
15886
|
-
});
|
|
15887
|
-
}
|
|
15888
|
-
const added = [];
|
|
15889
|
-
for (let row = 0; row < rowCount; row += 1) {
|
|
15890
|
-
const term = secondaryUniqueTerm(index, columns, columns.map((column) => input.columns[column.name]?.[row] ?? null));
|
|
15891
|
-
if (term !== void 0)
|
|
15892
|
-
added.push(term);
|
|
15893
|
-
}
|
|
15894
|
-
assertNoDuplicateUniqueTerms(index, added);
|
|
15895
|
-
transaction.setUniqueKeyChanges({
|
|
15896
|
-
tableId: namespaceId,
|
|
15897
|
-
keyTokens: added,
|
|
15898
|
-
requireAbsent: true
|
|
15899
|
-
});
|
|
15900
|
-
}
|
|
15901
|
-
}
|
|
15902
|
-
function stageSecondaryUniqueMutationChanges(transaction, table, input, oldImages) {
|
|
15903
|
-
for (const { indexId, index, columns } of readyUniqueSecondaryIndexes(table)) {
|
|
15904
|
-
const namespaceId = secondaryUniqueKeyNamespace(table.id, indexId);
|
|
15905
|
-
const removed = oldImages.flatMap((old) => {
|
|
15906
|
-
if (old === void 0)
|
|
15907
|
-
return [];
|
|
15908
|
-
const term = secondaryUniqueTerm(index, columns, columns.map((column) => old[column.name] ?? null));
|
|
15909
|
-
return term === void 0 ? [] : [term];
|
|
15910
|
-
});
|
|
15911
|
-
transaction.setUniqueKeyChanges({
|
|
15912
|
-
tableId: namespaceId,
|
|
15913
|
-
keyTokens: removed,
|
|
15914
|
-
requireAbsent: false,
|
|
15915
|
-
remove: true
|
|
15916
|
-
});
|
|
15917
|
-
if (input === void 0)
|
|
15918
|
-
continue;
|
|
15919
|
-
const added = oldImages.flatMap((old, row) => {
|
|
15920
|
-
if (old === void 0)
|
|
15921
|
-
return [];
|
|
15922
|
-
const term = secondaryUniqueTerm(index, columns, columns.map((column) => {
|
|
15923
|
-
const assigned = input.changes[column.name];
|
|
15924
|
-
return assigned === void 0 ? old[column.name] ?? null : assigned[row] ?? null;
|
|
15925
|
-
}));
|
|
15926
|
-
return term === void 0 ? [] : [term];
|
|
15927
|
-
});
|
|
15928
|
-
assertNoDuplicateUniqueTerms(index, added);
|
|
15929
|
-
transaction.setUniqueKeyChanges({
|
|
15930
|
-
tableId: namespaceId,
|
|
15931
|
-
keyTokens: added,
|
|
15932
|
-
requireAbsent: true
|
|
15933
|
-
});
|
|
15934
|
-
}
|
|
15935
|
-
}
|
|
15936
15511
|
function searchableFtsColumns(table) {
|
|
15937
15512
|
if (table === void 0)
|
|
15938
15513
|
return void 0;
|
|
15939
15514
|
return visibleTableColumns(table).filter((column) => column.type !== "boolean").map((column) => column.name);
|
|
15940
15515
|
}
|
|
15941
|
-
const LIVE_HIDDEN_PREFIX = "__minnow_live_";
|
|
15942
|
-
const LIVE_KEY_ALIAS = `${LIVE_HIDDEN_PREFIX}key`;
|
|
15943
|
-
const LIVE_ORDER_ALIAS = `${LIVE_HIDDEN_PREFIX}order_`;
|
|
15944
|
-
const LIVE_MAINTENANCE_MAX_DELTA_ROWS = 2048;
|
|
15945
|
-
const LIVE_WINDOW_MARGIN_MIN = 16;
|
|
15946
|
-
const LIVE_WINDOW_MARGIN_MAX = 64;
|
|
15947
|
-
function liveKeyToken(value) {
|
|
15948
|
-
if (typeof value === "number")
|
|
15949
|
-
return `n:${String(value)}`;
|
|
15950
|
-
if (typeof value === "string")
|
|
15951
|
-
return `s:${value}`;
|
|
15952
|
-
if (typeof value === "boolean")
|
|
15953
|
-
return value ? "b:1" : "b:0";
|
|
15954
|
-
if (value instanceof Date)
|
|
15955
|
-
return `d:${String(dateMilliseconds(value))}`;
|
|
15956
|
-
return "z";
|
|
15957
|
-
}
|
|
15958
|
-
function splitLiveHiddenColumns(executed, state) {
|
|
15959
|
-
const publicCount = state.publicColumns.length;
|
|
15960
|
-
const hidden = executed.columns.slice(publicCount);
|
|
15961
|
-
const count = executed.rows.length;
|
|
15962
|
-
const keys = new Array(count);
|
|
15963
|
-
const order = state.orderTerms.map(() => new Array(count));
|
|
15964
|
-
for (let index = 0; index < count; index += 1) {
|
|
15965
|
-
const row = executed.rows[index] ?? {};
|
|
15966
|
-
keys[index] = liveKeyToken(row[LIVE_KEY_ALIAS] ?? null);
|
|
15967
|
-
for (const [term, { alias }] of state.orderTerms.entries()) {
|
|
15968
|
-
const values = order[term];
|
|
15969
|
-
if (values !== void 0)
|
|
15970
|
-
values[index] = row[alias] ?? null;
|
|
15971
|
-
}
|
|
15972
|
-
for (const column of hidden)
|
|
15973
|
-
Reflect.deleteProperty(row, column);
|
|
15974
|
-
}
|
|
15975
|
-
return {
|
|
15976
|
-
result: externalizeQueryResult({
|
|
15977
|
-
columns: executed.columns.slice(0, publicCount),
|
|
15978
|
-
columnDomains: executed.columnDomains.slice(0, publicCount),
|
|
15979
|
-
rows: executed.rows
|
|
15980
|
-
}),
|
|
15981
|
-
keys,
|
|
15982
|
-
order
|
|
15983
|
-
};
|
|
15984
|
-
}
|
|
15985
|
-
function liveMaintainedOutcome(state, previous, previousIndex) {
|
|
15986
|
-
const positions = /* @__PURE__ */ new Map();
|
|
15987
|
-
let retainedBytes = state.retainedBytes ?? 128 + planMemoKey(state.fullPlan).length * 2 + planMemoKey(state.deltaPlan).length * 2;
|
|
15988
|
-
for (const [index, key] of state.keys.entries()) {
|
|
15989
|
-
if (positions.has(key))
|
|
15990
|
-
throw new TypeError("Duplicate live input key");
|
|
15991
|
-
positions.set(key, index);
|
|
15992
|
-
if (state.retainedBytes === void 0)
|
|
15993
|
-
retainedBytes += liveRowStateBytes(state, index);
|
|
15994
|
-
}
|
|
15995
|
-
state = { ...state, positions, retainedBytes };
|
|
15996
|
-
const visibleCount = state.limit === void 0 ? state.rows.length : Math.min(state.rows.length, state.limit);
|
|
15997
|
-
const visible = state.rows.slice(0, visibleCount);
|
|
15998
|
-
if (previous === void 0) {
|
|
15999
|
-
return {
|
|
16000
|
-
result: {
|
|
16001
|
-
columns: [...state.publicColumns],
|
|
16002
|
-
columnDomains: [...state.columnDomains],
|
|
16003
|
-
rows: visible
|
|
16004
|
-
},
|
|
16005
|
-
state,
|
|
16006
|
-
retainedBytes,
|
|
16007
|
-
changed: true
|
|
16008
|
-
};
|
|
16009
|
-
}
|
|
16010
|
-
const previousCount = previous.rows.length;
|
|
16011
|
-
const retained = new Int32Array(visibleCount);
|
|
16012
|
-
let same = visibleCount === previousCount;
|
|
16013
|
-
for (let index = 0; index < visibleCount; index += 1) {
|
|
16014
|
-
const was = previousIndex?.[index] ?? -1;
|
|
16015
|
-
if (was >= 0 && was < previousCount) {
|
|
16016
|
-
retained[index] = was;
|
|
16017
|
-
if (was !== index)
|
|
16018
|
-
same = false;
|
|
16019
|
-
continue;
|
|
16020
|
-
}
|
|
16021
|
-
retained[index] = -1;
|
|
16022
|
-
const before = previous.rows[index];
|
|
16023
|
-
const now = visible[index];
|
|
16024
|
-
if (before !== void 0 && now !== void 0 && sameLiveRow(before, now, previous.columns)) {
|
|
16025
|
-
visible[index] = before;
|
|
16026
|
-
retained[index] = index;
|
|
16027
|
-
} else
|
|
16028
|
-
same = false;
|
|
16029
|
-
}
|
|
16030
|
-
if (same)
|
|
16031
|
-
return { result: previous, state, retainedBytes, changed: false };
|
|
16032
|
-
return {
|
|
16033
|
-
result: {
|
|
16034
|
-
columns: [...state.publicColumns],
|
|
16035
|
-
columnDomains: [...state.columnDomains],
|
|
16036
|
-
rows: visible
|
|
16037
|
-
},
|
|
16038
|
-
state,
|
|
16039
|
-
retainedBytes,
|
|
16040
|
-
changed: true,
|
|
16041
|
-
retained
|
|
16042
|
-
};
|
|
16043
|
-
}
|
|
16044
|
-
function liveRowStateBytes(state, index) {
|
|
16045
|
-
let bytes = 112 + (state.keys[index]?.length ?? 0) * 2 + estimateValuesBytes(Object.values(state.rows[index] ?? {})) * 2;
|
|
16046
|
-
for (const values of state.order)
|
|
16047
|
-
bytes += 8 + estimateValuesBytes([values[index]]) * 2;
|
|
16048
|
-
return bytes;
|
|
16049
|
-
}
|
|
16050
|
-
function filterLiveRows(state, keep) {
|
|
16051
|
-
const rows = [];
|
|
16052
|
-
const keys = [];
|
|
16053
|
-
const order = state.order.map(() => new Array());
|
|
16054
|
-
const previous = [];
|
|
16055
|
-
for (let index = 0; index < state.rows.length; index += 1) {
|
|
16056
|
-
const row = state.rows[index];
|
|
16057
|
-
if (row === void 0 || !keep(index))
|
|
16058
|
-
continue;
|
|
16059
|
-
rows.push(row);
|
|
16060
|
-
keys.push(state.keys[index] ?? "z");
|
|
16061
|
-
for (const [term, values] of state.order.entries()) {
|
|
16062
|
-
order[term]?.push(values[index] ?? null);
|
|
16063
|
-
}
|
|
16064
|
-
previous.push(index);
|
|
16065
|
-
}
|
|
16066
|
-
return { rows, keys, order, previousIndex: Int32Array.from(previous) };
|
|
16067
|
-
}
|
|
16068
|
-
function trimLiveRows(rows, count) {
|
|
16069
|
-
return {
|
|
16070
|
-
rows: rows.rows.slice(0, count),
|
|
16071
|
-
keys: rows.keys.slice(0, count),
|
|
16072
|
-
order: rows.order.map((values) => values.slice(0, count)),
|
|
16073
|
-
previousIndex: rows.previousIndex.slice(0, count)
|
|
16074
|
-
};
|
|
16075
|
-
}
|
|
16076
|
-
function liveOrderComparator(terms) {
|
|
16077
|
-
return (leftOrder, leftIndex, rightOrder, rightIndex) => {
|
|
16078
|
-
for (const [term, { descending, nulls }] of terms.entries()) {
|
|
16079
|
-
const a = leftOrder[term]?.[leftIndex] ?? null;
|
|
16080
|
-
const b = rightOrder[term]?.[rightIndex] ?? null;
|
|
16081
|
-
if (a === null || b === null) {
|
|
16082
|
-
if (a === null && b === null)
|
|
16083
|
-
continue;
|
|
16084
|
-
const nullsFirst = nulls === "first" || nulls === void 0 && descending;
|
|
16085
|
-
return a === null ? nullsFirst ? -1 : 1 : nullsFirst ? 1 : -1;
|
|
16086
|
-
}
|
|
16087
|
-
let comparison = compareSqlValues(a, b);
|
|
16088
|
-
if (descending)
|
|
16089
|
-
comparison = -comparison;
|
|
16090
|
-
if (comparison !== 0)
|
|
16091
|
-
return comparison;
|
|
16092
|
-
}
|
|
16093
|
-
return 0;
|
|
16094
|
-
};
|
|
16095
|
-
}
|
|
16096
|
-
function mergeLiveRows(kept, added, compare, ordered) {
|
|
16097
|
-
if (added.rows.length === 0)
|
|
16098
|
-
return kept;
|
|
16099
|
-
const terms = kept.order.length;
|
|
16100
|
-
const total = kept.rows.length + added.rows.length;
|
|
16101
|
-
const rows = new Array(total);
|
|
16102
|
-
const keys = new Array(total);
|
|
16103
|
-
const order = Array.from({ length: terms }, () => new Array(total));
|
|
16104
|
-
const previousIndex = new Int32Array(total);
|
|
16105
|
-
const take = (source, from, to) => {
|
|
16106
|
-
rows[to] = source.rows[from] ?? {};
|
|
16107
|
-
keys[to] = source.keys[from] ?? "z";
|
|
16108
|
-
for (let term = 0; term < terms; term += 1) {
|
|
16109
|
-
const values = order[term];
|
|
16110
|
-
if (values !== void 0)
|
|
16111
|
-
values[to] = source.order[term]?.[from] ?? null;
|
|
16112
|
-
}
|
|
16113
|
-
previousIndex[to] = source.previousIndex[from] ?? -1;
|
|
16114
|
-
};
|
|
16115
|
-
if (!ordered) {
|
|
16116
|
-
for (let index = 0; index < kept.rows.length; index += 1)
|
|
16117
|
-
take(kept, index, index);
|
|
16118
|
-
for (let index = 0; index < added.rows.length; index += 1) {
|
|
16119
|
-
take(added, index, kept.rows.length + index);
|
|
16120
|
-
}
|
|
16121
|
-
return { rows, keys, order, previousIndex };
|
|
16122
|
-
}
|
|
16123
|
-
const addedIndexes = added.rows.map((_, index) => index);
|
|
16124
|
-
addedIndexes.sort((left, right) => compare(added.order, left, added.order, right));
|
|
16125
|
-
let keptIndex = 0;
|
|
16126
|
-
let addedPosition = 0;
|
|
16127
|
-
for (let to = 0; to < total; to += 1) {
|
|
16128
|
-
const addedIndex = addedIndexes[addedPosition];
|
|
16129
|
-
const takeAdded = addedIndex !== void 0 && (keptIndex >= kept.rows.length || compare(added.order, addedIndex, kept.order, keptIndex) < 0);
|
|
16130
|
-
if (takeAdded) {
|
|
16131
|
-
take(added, addedIndex, to);
|
|
16132
|
-
addedPosition += 1;
|
|
16133
|
-
} else {
|
|
16134
|
-
take(kept, keptIndex, to);
|
|
16135
|
-
keptIndex += 1;
|
|
16136
|
-
}
|
|
16137
|
-
}
|
|
16138
|
-
return { rows, keys, order, previousIndex };
|
|
16139
|
-
}
|
|
16140
|
-
function getUniqueKeyColumn(table) {
|
|
16141
|
-
if (table.uniqueKeyColumnId === void 0)
|
|
16142
|
-
return void 0;
|
|
16143
|
-
return table.columns.find((column) => column.id === table.uniqueKeyColumnId);
|
|
16144
|
-
}
|
|
16145
15516
|
function visibleTableColumns(table) {
|
|
16146
15517
|
return table.columns.filter((column) => column.hidden !== true);
|
|
16147
15518
|
}
|
|
@@ -16198,9 +15569,6 @@ function foldAssignmentColumns(table, assignments) {
|
|
|
16198
15569
|
});
|
|
16199
15570
|
return folded.some((assignment, index) => assignment !== assignments[index]) ? folded : assignments;
|
|
16200
15571
|
}
|
|
16201
|
-
function quoteIdentifier(name) {
|
|
16202
|
-
return `"${name.replace(/"/g, '""')}"`;
|
|
16203
|
-
}
|
|
16204
15572
|
function normalizeDomainBatch(table, input) {
|
|
16205
15573
|
for (const column of table.columns) {
|
|
16206
15574
|
const values = input.columns[column.name];
|
|
@@ -16606,36 +15974,6 @@ function valueMatchesColumnType(column, value) {
|
|
|
16606
15974
|
return value instanceof Date;
|
|
16607
15975
|
}
|
|
16608
15976
|
}
|
|
16609
|
-
function keyToken(type, value) {
|
|
16610
|
-
if (value === null)
|
|
16611
|
-
throw new TypeError("Unique key cannot be null");
|
|
16612
|
-
switch (type) {
|
|
16613
|
-
case "boolean":
|
|
16614
|
-
if (typeof value !== "boolean")
|
|
16615
|
-
throw new TypeError("Invalid boolean unique key");
|
|
16616
|
-
return value ? "boolean:true" : "boolean:false";
|
|
16617
|
-
case "number":
|
|
16618
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
16619
|
-
throw new TypeError("Invalid number unique key");
|
|
16620
|
-
}
|
|
16621
|
-
return `number:${String(value)}`;
|
|
16622
|
-
case "string":
|
|
16623
|
-
if (typeof value !== "string")
|
|
16624
|
-
throw new TypeError("Invalid string unique key");
|
|
16625
|
-
assertIndexedStringLength(value);
|
|
16626
|
-
return `string:${value}`;
|
|
16627
|
-
case "datetime":
|
|
16628
|
-
if (!(value instanceof Date) || !Number.isFinite(dateMilliseconds(value))) {
|
|
16629
|
-
throw new TypeError("Invalid datetime unique key");
|
|
16630
|
-
}
|
|
16631
|
-
return `datetime:${String(dateMilliseconds(value))}`;
|
|
16632
|
-
}
|
|
16633
|
-
}
|
|
16634
|
-
function assertIndexedStringLength(value) {
|
|
16635
|
-
if (value.length > MAX_INDEXED_STRING_CHARACTERS) {
|
|
16636
|
-
throw new RangeError(`Indexed strings cannot exceed ${String(MAX_INDEXED_STRING_CHARACTERS)} characters`);
|
|
16637
|
-
}
|
|
16638
|
-
}
|
|
16639
15977
|
function formatValue(value) {
|
|
16640
15978
|
return value instanceof Date ? dateIsoString(value) : String(value);
|
|
16641
15979
|
}
|
|
@@ -17619,13 +16957,14 @@ function secondaryIndexPredicates(plan, table) {
|
|
|
17619
16957
|
break;
|
|
17620
16958
|
if (constraint.lower === void 0 && constraint.upper === void 0)
|
|
17621
16959
|
break;
|
|
17622
|
-
const
|
|
16960
|
+
const direction = secondaryIndexDirections(index)[position] ?? "asc";
|
|
16961
|
+
const descending = direction === "desc";
|
|
17623
16962
|
queries = prefixes.map((prefix) => {
|
|
17624
16963
|
const query = {};
|
|
17625
16964
|
const logicalLower = descending ? constraint.upper : constraint.lower;
|
|
17626
16965
|
const logicalUpper = descending ? constraint.lower : constraint.upper;
|
|
17627
16966
|
if (logicalLower === void 0) {
|
|
17628
|
-
query.lower = prefix;
|
|
16967
|
+
query.lower = descending ? prefix + LOWEST_SECONDARY_COMPONENT : prefix;
|
|
17629
16968
|
query.lowerInclusive = true;
|
|
17630
16969
|
} else {
|
|
17631
16970
|
const term = prefix + secondaryIndexComponentTerm(index, column, position, logicalLower.value);
|
|
@@ -17633,7 +16972,7 @@ function secondaryIndexPredicates(plan, table) {
|
|
|
17633
16972
|
query.lowerInclusive = logicalLower.inclusive;
|
|
17634
16973
|
}
|
|
17635
16974
|
if (logicalUpper === void 0) {
|
|
17636
|
-
query.upper = `${prefix}\uFFFF
|
|
16975
|
+
query.upper = descending ? `${prefix}\uFFFF` : prefix + ASCENDING_NULL_COMPONENT;
|
|
17637
16976
|
query.upperInclusive = false;
|
|
17638
16977
|
} else {
|
|
17639
16978
|
const term = prefix + secondaryIndexComponentTerm(index, column, position, logicalUpper.value);
|
|
@@ -17647,6 +16986,9 @@ function secondaryIndexPredicates(plan, table) {
|
|
|
17647
16986
|
}
|
|
17648
16987
|
if (matchedColumns === 0)
|
|
17649
16988
|
continue;
|
|
16989
|
+
if (matchedColumns < indexedColumns.length && !secondaryIndexTermsCoverNulls(index) && indexedColumns.slice(matchedColumns).some((column) => column.nullable)) {
|
|
16990
|
+
continue;
|
|
16991
|
+
}
|
|
17650
16992
|
if (queries === void 0) {
|
|
17651
16993
|
const complete = matchedColumns === indexedColumns.length;
|
|
17652
16994
|
queries = prefixes.map((term) => ({ term, prefix: !complete }));
|
|
@@ -17972,41 +17314,30 @@ function validateMergeSegmentShape(table, segmentId, kind, columns, keyColumnId)
|
|
|
17972
17314
|
throw new Error(`Update compaction source has invalid columns: ${segmentId}`);
|
|
17973
17315
|
}
|
|
17974
17316
|
}
|
|
17975
|
-
function
|
|
17976
|
-
if (
|
|
17977
|
-
|
|
17978
|
-
throw new Error(`Mutation marker unexpectedly owns row IDs: ${segment.id}`);
|
|
17979
|
-
}
|
|
17980
|
-
return [];
|
|
17981
|
-
}
|
|
17982
|
-
const spans = segment.rowIdSpans.length === 0 ? [{ rowStart: 0, rowCount: segment.rowCount, rowIdStart: segment.rowIdStart }] : segment.rowIdSpans.map((span) => ({ ...span }));
|
|
17983
|
-
const envelope = rowIdSpanEnvelope(spans);
|
|
17984
|
-
let rowStart = 0;
|
|
17985
|
-
for (const [index, span] of spans.entries()) {
|
|
17986
|
-
if (span.rowStart !== rowStart || span.rowCount <= 0) {
|
|
17987
|
-
throw new Error(`Segment row ID spans are not contiguous: ${segment.id}`);
|
|
17988
|
-
}
|
|
17989
|
-
const previous = spans[index - 1];
|
|
17990
|
-
if (previous !== void 0 && previous.rowIdStart + BigInt(previous.rowCount) === span.rowIdStart) {
|
|
17991
|
-
throw new Error(`Segment row ID spans are not coalesced: ${segment.id}`);
|
|
17992
|
-
}
|
|
17993
|
-
rowStart = safeWholeNumberSum([rowStart, span.rowCount], "Segment row ID span rows");
|
|
17317
|
+
function isTransientCompactionConflict(error) {
|
|
17318
|
+
if (error instanceof SchemaConflictError || error instanceof TransactionRecordConflictError || error instanceof CompactionJobConflictError) {
|
|
17319
|
+
return true;
|
|
17994
17320
|
}
|
|
17995
|
-
if (
|
|
17996
|
-
|
|
17321
|
+
if (!(error instanceof Error))
|
|
17322
|
+
return false;
|
|
17323
|
+
if (error.cause instanceof WriteConflictError || error.cause instanceof SchemaConflictError) {
|
|
17324
|
+
return true;
|
|
17997
17325
|
}
|
|
17998
|
-
|
|
17999
|
-
|
|
18000
|
-
|
|
18001
|
-
|
|
18002
|
-
|
|
18003
|
-
|
|
18004
|
-
|
|
18005
|
-
|
|
18006
|
-
|
|
17326
|
+
return /^Compaction (source|sources|publication|planned layout)/u.test(error.message);
|
|
17327
|
+
}
|
|
17328
|
+
function withEveryDelta(visible, pruned) {
|
|
17329
|
+
const keptAppends = new Map(pruned.filter((segment) => segment.kind !== "update" && segment.kind !== "delete").map((segment) => [segment.id, segment]));
|
|
17330
|
+
const result = [];
|
|
17331
|
+
for (const segment of visible) {
|
|
17332
|
+
if (segment.kind === "update" || segment.kind === "delete")
|
|
17333
|
+
result.push(segment);
|
|
17334
|
+
else {
|
|
17335
|
+
const kept = keptAppends.get(segment.id);
|
|
17336
|
+
if (kept !== void 0)
|
|
17337
|
+
result.push(kept);
|
|
18007
17338
|
}
|
|
18008
17339
|
}
|
|
18009
|
-
return
|
|
17340
|
+
return result;
|
|
18010
17341
|
}
|
|
18011
17342
|
function assertCanonicalMergeSourceOrder(segments) {
|
|
18012
17343
|
for (let index = 1; index < segments.length; index += 1) {
|
|
@@ -18018,7 +17349,7 @@ function assertCanonicalMergeSourceOrder(segments) {
|
|
|
18018
17349
|
}
|
|
18019
17350
|
}
|
|
18020
17351
|
function compareMergeSourceOrder(left, right) {
|
|
18021
|
-
return left.logicalOrder - right.logicalOrder || left.committedVersion - right.committedVersion || left.segmentId.localeCompare(right.segmentId);
|
|
17352
|
+
return left.logicalOrder - right.logicalOrder || left.committedVersion - right.committedVersion || (left.commitOrdinal ?? 0) - (right.commitOrdinal ?? 0) || left.segmentId.localeCompare(right.segmentId);
|
|
18022
17353
|
}
|
|
18023
17354
|
function sourceOrderTuple(segment, transactions, label) {
|
|
18024
17355
|
if (segment === void 0)
|
|
@@ -18030,6 +17361,7 @@ function sourceOrderTuple(segment, transactions, label) {
|
|
|
18030
17361
|
return {
|
|
18031
17362
|
logicalOrder: segment.logicalOrder,
|
|
18032
17363
|
committedVersion: owner.committedVersion,
|
|
17364
|
+
commitOrdinal: segment.commitOrdinal,
|
|
18033
17365
|
segmentId: segment.id
|
|
18034
17366
|
};
|
|
18035
17367
|
}
|
|
@@ -18352,20 +17684,6 @@ function rowRangeAt(ranges, rowIndex) {
|
|
|
18352
17684
|
}
|
|
18353
17685
|
return void 0;
|
|
18354
17686
|
}
|
|
18355
|
-
function rowIdSpanEnvelope(spans) {
|
|
18356
|
-
if (spans.length === 0)
|
|
18357
|
-
return { start: 0n, endExclusive: 0n };
|
|
18358
|
-
let start = spans[0]?.rowIdStart ?? 0n;
|
|
18359
|
-
let endExclusive = start + BigInt(spans[0]?.rowCount ?? 0);
|
|
18360
|
-
for (const span of spans.slice(1)) {
|
|
18361
|
-
if (span.rowIdStart < start)
|
|
18362
|
-
start = span.rowIdStart;
|
|
18363
|
-
const spanEnd = span.rowIdStart + BigInt(span.rowCount);
|
|
18364
|
-
if (spanEnd > endExclusive)
|
|
18365
|
-
endExclusive = spanEnd;
|
|
18366
|
-
}
|
|
18367
|
-
return { start, endExclusive };
|
|
18368
|
-
}
|
|
18369
17687
|
function validatePhysicalTablePlan(table, plan) {
|
|
18370
17688
|
if (table.columns.length !== plan.columns.length || table.columns.some((column, index) => {
|
|
18371
17689
|
const planned = plan.columns[index];
|