@minnowdb/core 0.7.5 → 0.7.8
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/batch.d.ts +14 -1
- package/dist/engine/batch.js +23 -2
- package/dist/engine/buffered-writer.d.ts +7 -4
- package/dist/engine/client.d.ts +42 -25
- package/dist/engine/client.js +23 -8
- package/dist/engine/database.d.ts +44 -22
- package/dist/engine/database.js +285 -103
- package/dist/engine/schema.d.ts +85 -2
- package/dist/engine/worker-server.js +4 -1
- package/dist/storage/indexeddb.js +13 -8
- package/dist/storage/toolkit/record-core.js +11 -6
- package/dist/storage/types.d.ts +11 -0
- package/dist/worker-protocol/index.d.ts +1 -1
- package/dist/worker-protocol/index.js +1 -1
- package/package.json +1 -1
package/dist/engine/database.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { crossJoinPlan } from "../plan/model.js";
|
|
2
|
-
import { toColumnarBatch } from "./batch.js";
|
|
2
|
+
import { definedVectors, toColumnarBatch } from "./batch.js";
|
|
3
3
|
import { ArtifactCache } from "./artifact-cache.js";
|
|
4
4
|
import { estimateBatchBytes, estimateRowBytes, estimateValuesBytes } from "./byte-estimates.js";
|
|
5
5
|
import { throwIfAborted } from "./cancellation.js";
|
|
@@ -13,7 +13,7 @@ import { estimateCompactionRowsPerOutput, planAlignedWriteBlockRanges as writeBl
|
|
|
13
13
|
import { MAX_CACHEABLE_TEXT_CHARACTERS, MAX_SQL_PARAMETERS } from "./cache-limits.js";
|
|
14
14
|
import { fillColumnDefaults, patchAutoIncrementValues } from "./defaults.js";
|
|
15
15
|
import { cachedQueryTerms, FTS_TOKENIZER_VERSION, renderDocumentValue, tokenize as ftsTokenize } from "./fts.js";
|
|
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
|
+
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_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";
|
|
17
17
|
import { decodeSnapshotFrameStream, encodeSnapshotFrameStreamFooter, encodeSnapshotFrameStreamHeader, extendSnapshotFrameStreamChecksum, snapshotFrameEnvelopeParts, snapshotFrameStreamHeaderIdentity } from "../storage/snapshot.js";
|
|
18
18
|
import { Snapshot, TransactionManager } from "../transactions/index.js";
|
|
19
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";
|
|
@@ -78,6 +78,7 @@ const AUTO_COLLECT_RETRY_MIN_MS = 1e3;
|
|
|
78
78
|
const AUTO_COLLECT_RETRY_MAX_MS = 6e4;
|
|
79
79
|
const AUTO_COMPACT_STEP_BLOCKS = 4;
|
|
80
80
|
const AUTO_COMPACT_MAX_LEVEL_ZERO_SEGMENTS = 256;
|
|
81
|
+
const AUTO_COMPACT_BACKPRESSURE_LEVEL_ZERO_SEGMENTS = 2 * AUTO_COMPACT_MAX_LEVEL_ZERO_SEGMENTS;
|
|
81
82
|
const AUTO_COMPACT_RETRY_MIN_MS = 250;
|
|
82
83
|
const AUTO_COMPACT_RETRY_MAX_MS = 6e4;
|
|
83
84
|
const MAX_COMPACTION_PUBLICATION_CONFLICTS = 64;
|
|
@@ -181,6 +182,9 @@ function migrationResult(steps) {
|
|
|
181
182
|
steps: [...steps]
|
|
182
183
|
};
|
|
183
184
|
}
|
|
185
|
+
function compactUpdateBatchInput(input) {
|
|
186
|
+
return Object.values(input.changes).includes(void 0) ? { keys: input.keys, changes: definedVectors(input.changes) } : input;
|
|
187
|
+
}
|
|
184
188
|
const MAX_GARBAGE_COLLECTION_RETAIN_RECENT_VERSIONS = 1024;
|
|
185
189
|
const REFERENTIAL_CASCADES = 8;
|
|
186
190
|
class TransactionRollback extends Error {
|
|
@@ -547,6 +551,10 @@ async function* singleSnapshotChunk(bytes) {
|
|
|
547
551
|
}
|
|
548
552
|
class MinnowDatabase {
|
|
549
553
|
store;
|
|
554
|
+
#schema;
|
|
555
|
+
get #erased() {
|
|
556
|
+
return this;
|
|
557
|
+
}
|
|
550
558
|
#closed = false;
|
|
551
559
|
#closePromise;
|
|
552
560
|
#transactions;
|
|
@@ -624,6 +632,7 @@ class MinnowDatabase {
|
|
|
624
632
|
#preparedCatalogStates = /* @__PURE__ */ new WeakMap();
|
|
625
633
|
constructor(store, options = {}) {
|
|
626
634
|
this.store = store;
|
|
635
|
+
this.#schema = options.schema;
|
|
627
636
|
this.#compression = options.compression ?? "gzip";
|
|
628
637
|
this.#rowsPerBlock = options.rowsPerBlock ?? 65536;
|
|
629
638
|
if (!Number.isSafeInteger(this.#rowsPerBlock) || this.#rowsPerBlock <= 0 || this.#rowsPerBlock > MAX_BLOCK_ROW_COUNT) {
|
|
@@ -1588,7 +1597,7 @@ class MinnowDatabase {
|
|
|
1588
1597
|
return { ...statement, rows };
|
|
1589
1598
|
}
|
|
1590
1599
|
async insert(tableName, row) {
|
|
1591
|
-
return this.insertBatch(tableName, [row]);
|
|
1600
|
+
return this.#erased.insertBatch(tableName, [row]);
|
|
1592
1601
|
}
|
|
1593
1602
|
upsertBatch(tableName, input, options = {}) {
|
|
1594
1603
|
return this.#withWriteReservation(() => this.#upsertBatchReserved(tableName, input, options));
|
|
@@ -1612,9 +1621,7 @@ class MinnowDatabase {
|
|
|
1612
1621
|
const { result, batch, generated, autoIncrement } = completed;
|
|
1613
1622
|
collectAutoIncrementGenerated(batch, generated, autoIncrement);
|
|
1614
1623
|
if (result.acceptedRowIndexes.length !== result.requestedRowCount) {
|
|
1615
|
-
|
|
1616
|
-
generated.set(name, result.acceptedRowIndexes.map((index) => values[index] ?? null));
|
|
1617
|
-
}
|
|
1624
|
+
remapGeneratedColumns(generated, result.acceptedRowIndexes);
|
|
1618
1625
|
}
|
|
1619
1626
|
return {
|
|
1620
1627
|
tableName: result.tableName,
|
|
@@ -1632,10 +1639,10 @@ class MinnowDatabase {
|
|
|
1632
1639
|
};
|
|
1633
1640
|
}
|
|
1634
1641
|
async upsert(tableName, row, options = {}) {
|
|
1635
|
-
return this.upsertBatch(tableName, [row], options);
|
|
1642
|
+
return this.#erased.upsertBatch(tableName, [row], options);
|
|
1636
1643
|
}
|
|
1637
1644
|
updateBatch(tableName, input) {
|
|
1638
|
-
return this.#withWriteReservation(() => this.#updateBatchReserved(tableName, input));
|
|
1645
|
+
return this.#withWriteReservation(() => this.#updateBatchReserved(tableName, compactUpdateBatchInput(input)));
|
|
1639
1646
|
}
|
|
1640
1647
|
async #updateBatchReserved(tableName, input) {
|
|
1641
1648
|
return this.#runWrite(async () => {
|
|
@@ -1658,13 +1665,13 @@ class MinnowDatabase {
|
|
|
1658
1665
|
});
|
|
1659
1666
|
}
|
|
1660
1667
|
async update(tableName, key, changes) {
|
|
1661
|
-
return this.updateBatch(tableName, {
|
|
1668
|
+
return this.#erased.updateBatch(tableName, {
|
|
1662
1669
|
keys: [key],
|
|
1663
|
-
changes: Object.fromEntries(Object.entries(changes).
|
|
1670
|
+
changes: Object.fromEntries(Object.entries(changes).flatMap(([name, value]) => value === void 0 ? [] : [[name, [value]]]))
|
|
1664
1671
|
});
|
|
1665
1672
|
}
|
|
1666
1673
|
async delete(tableName, key) {
|
|
1667
|
-
return this.deleteBatch(tableName, { keys: [key] });
|
|
1674
|
+
return this.#erased.deleteBatch(tableName, { keys: [key] });
|
|
1668
1675
|
}
|
|
1669
1676
|
deleteBatch(tableName, input) {
|
|
1670
1677
|
return this.#withWriteReservation(() => this.#deleteBatchReserved(tableName, input));
|
|
@@ -1679,7 +1686,7 @@ class MinnowDatabase {
|
|
|
1679
1686
|
if (dependents.length === 0)
|
|
1680
1687
|
return this.#deleteBatchOnce(tableName, normalizedInput);
|
|
1681
1688
|
const started = performance.now();
|
|
1682
|
-
const { result, version } = await this.write(async (session) => {
|
|
1689
|
+
const { result, version } = await this.#erased.write(async (session) => {
|
|
1683
1690
|
await this.#applyReferentialActions(table, [...normalizedInput.keys], session, REFERENTIAL_CASCADES);
|
|
1684
1691
|
return session.deleteBatch(tableName, normalizedInput);
|
|
1685
1692
|
});
|
|
@@ -1883,7 +1890,7 @@ class MinnowDatabase {
|
|
|
1883
1890
|
}
|
|
1884
1891
|
}
|
|
1885
1892
|
bufferedWriter(tableName, options = {}) {
|
|
1886
|
-
return new BufferedTableWriter(this, tableName, options);
|
|
1893
|
+
return new BufferedTableWriter(this.#erased, tableName, options);
|
|
1887
1894
|
}
|
|
1888
1895
|
async #writeUpdateBatch(table, keyColumn, input, keys) {
|
|
1889
1896
|
await this.#assertCompactionCapacity(table);
|
|
@@ -2092,30 +2099,21 @@ class MinnowDatabase {
|
|
|
2092
2099
|
const normalizedConflictWhere = kind === "upsert" && conflictWhere !== void 0 ? normalizeUpsertConflictWhere(table, conflictWhere) : void 0;
|
|
2093
2100
|
const upsertKeyColumn = kind === "upsert" ? getUniqueKeyColumn(table) : void 0;
|
|
2094
2101
|
if (upsertKeyColumn !== void 0) {
|
|
2095
|
-
upsertFirings = await this.#upsertTriggerFirings(table, upsertKeyColumn, input, rowCount, (sql, params) => this.#sessionQuery(transaction, sql, { params }), normalizedConflictWhere
|
|
2102
|
+
upsertFirings = await this.#upsertTriggerFirings(table, upsertKeyColumn, input, rowCount, (sql, params) => this.#sessionQuery(transaction, sql, { params }), normalizedConflictWhere);
|
|
2096
2103
|
}
|
|
2097
2104
|
if (normalizedConflictWhere !== void 0) {
|
|
2098
2105
|
if (upsertFirings === void 0) {
|
|
2099
2106
|
throw new Error("Upsert conflict classification is missing");
|
|
2100
2107
|
}
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2108
|
+
counts = { inserted: upsertFirings.inserts.length, updated: upsertFirings.updates.length };
|
|
2109
|
+
const filtered = applyUpsertConflictFilter(input, rowCount, upsertFirings);
|
|
2110
|
+
acceptedRowIndexes = filtered.acceptedRowIndexes;
|
|
2111
|
+
skippedRowCount = filtered.skippedRowCount;
|
|
2112
|
+
input = filtered.batch;
|
|
2113
|
+
rowCount = filtered.rowCount;
|
|
2114
|
+
upsertFirings = filtered.firings;
|
|
2106
2115
|
logicalBytes = estimateBatchBytes(input);
|
|
2107
2116
|
resolvedKeys = batchKeys(table, input);
|
|
2108
|
-
counts = {
|
|
2109
|
-
inserted: classifiedFirings.inserts.length,
|
|
2110
|
-
updated: classifiedFirings.updates.length
|
|
2111
|
-
};
|
|
2112
|
-
const acceptedPosition = new Map(acceptedRowIndexes.map((original, position) => [original, position]));
|
|
2113
|
-
upsertFirings = {
|
|
2114
|
-
inserts: classifiedFirings.inserts.map((index) => acceptedPosition.get(index) ?? -1),
|
|
2115
|
-
updates: classifiedFirings.updates.map((index) => acceptedPosition.get(index) ?? -1),
|
|
2116
|
-
skipped: [],
|
|
2117
|
-
oldImages: acceptedRowIndexes.map((index) => classifiedFirings.oldImages[index])
|
|
2118
|
-
};
|
|
2119
2117
|
if (rowCount === 0) {
|
|
2120
2118
|
await transaction.abort();
|
|
2121
2119
|
const version = transaction.snapshotVersion;
|
|
@@ -3973,23 +3971,38 @@ class MinnowDatabase {
|
|
|
3973
3971
|
return;
|
|
3974
3972
|
const hintVersion = transaction?.snapshotVersion ?? await this.store.getCurrentManifestVersion();
|
|
3975
3973
|
const hint = this.#autoCompactionHints.get(table.id);
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3974
|
+
let visible;
|
|
3975
|
+
let levelZero;
|
|
3976
|
+
if (hint?.version === hintVersion) {
|
|
3977
|
+
({ visible, levelZero } = hint);
|
|
3978
|
+
} else {
|
|
3979
|
+
const segments = await this.#currentVisibleSegments(table);
|
|
3980
|
+
visible = segments.length;
|
|
3981
|
+
levelZero = countLevelZeroSegments(segments);
|
|
3983
3982
|
}
|
|
3984
|
-
if (levelZero <
|
|
3983
|
+
if (levelZero < AUTO_COMPACT_BACKPRESSURE_LEVEL_ZERO_SEGMENTS)
|
|
3984
|
+
return;
|
|
3985
|
+
const atCeiling = levelZero >= MAX_LEVEL_ZERO_SEGMENTS;
|
|
3986
|
+
if (!atCeiling && this.#autoCompactionBackoff.get(table.id)?.retryTimer !== void 0)
|
|
3985
3987
|
return;
|
|
3986
3988
|
await this.#publishPendingCompactions();
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3989
|
+
try {
|
|
3990
|
+
const progress = await this.compactTableStep(table.name, {
|
|
3991
|
+
maxBlocks: AUTO_COMPACT_STEP_BLOCKS,
|
|
3992
|
+
maxLevel0Segments: AUTO_COMPACT_MAX_LEVEL_ZERO_SEGMENTS
|
|
3993
|
+
});
|
|
3994
|
+
if (!atCeiling && progress.result !== null && !progress.result.compacted) {
|
|
3995
|
+
this.#backOffAutoCompaction(table.id, visible);
|
|
3996
|
+
}
|
|
3997
|
+
} catch (error) {
|
|
3998
|
+
if (atCeiling)
|
|
3999
|
+
throw error;
|
|
4000
|
+
this.#backOffAutoCompaction(table.id, visible);
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
if (!atCeiling)
|
|
4004
|
+
return;
|
|
4005
|
+
levelZero = countLevelZeroSegments(await this.#currentVisibleSegments(table));
|
|
3993
4006
|
if (levelZero >= MAX_LEVEL_ZERO_SEGMENTS) {
|
|
3994
4007
|
throw new CompactionBacklogError(table.name, levelZero, MAX_LEVEL_ZERO_SEGMENTS);
|
|
3995
4008
|
}
|
|
@@ -4299,9 +4312,9 @@ class MinnowDatabase {
|
|
|
4299
4312
|
}
|
|
4300
4313
|
}
|
|
4301
4314
|
}
|
|
4302
|
-
async #upsertTriggerFirings(table, keyColumn, batch, rowCount, readRows, conflictWhere
|
|
4315
|
+
async #upsertTriggerFirings(table, keyColumn, batch, rowCount, readRows, conflictWhere) {
|
|
4303
4316
|
const fires = (table.triggers ?? []).some((trigger) => trigger.event === "insert" || trigger.event === "update");
|
|
4304
|
-
if (!fires &&
|
|
4317
|
+
if (!fires && conflictWhere === void 0 || rowCount === 0)
|
|
4305
4318
|
return void 0;
|
|
4306
4319
|
const quote = (name) => `"${name.replaceAll('"', '""')}"`;
|
|
4307
4320
|
const keyValues = batch.columns[keyColumn.name] ?? [];
|
|
@@ -4415,8 +4428,15 @@ class MinnowDatabase {
|
|
|
4415
4428
|
if (target.uniqueKeyColumnId !== void 0) {
|
|
4416
4429
|
throw new TypeError(`Trigger bodies insert into keyless tables only: ${target.name}`);
|
|
4417
4430
|
}
|
|
4431
|
+
let columns = compiled.columns;
|
|
4432
|
+
if (columns.length === 0 && compiled.defaultValues !== true) {
|
|
4433
|
+
columns = visibleTableColumns(target).map((column) => column.name);
|
|
4434
|
+
if (derivedRows.some((row) => row.length !== columns.length)) {
|
|
4435
|
+
throw new TypeError(`Trigger body INSERT must match the ${String(columns.length)} visible columns of ${target.name}`);
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4418
4438
|
const statementNow = this.#now();
|
|
4419
|
-
const materialized = await this.#materializeInsertExpressions({ ...compiled, rows: derivedRows }, statementNow);
|
|
4439
|
+
const materialized = await this.#materializeInsertExpressions({ ...compiled, columns, rows: derivedRows }, statementNow);
|
|
4420
4440
|
const input = insertStatementBatch(materialized);
|
|
4421
4441
|
coerceSqlWriteBatch(target, input);
|
|
4422
4442
|
const filled = await fillColumnDefaults(target, input, (sql) => this.#evaluateDefaultExpression(sql, statementNow), derivedRows.length);
|
|
@@ -4629,10 +4649,10 @@ class MinnowDatabase {
|
|
|
4629
4649
|
staged += 1;
|
|
4630
4650
|
return guarded(() => this.#sessionInsert(transaction, tableName, input, "insert"));
|
|
4631
4651
|
},
|
|
4632
|
-
upsertBatch: async (tableName, input) => {
|
|
4652
|
+
upsertBatch: async (tableName, input, options2) => {
|
|
4633
4653
|
open();
|
|
4634
4654
|
staged += 1;
|
|
4635
|
-
return guarded(() => this.#sessionInsert(transaction, tableName, input, "upsert"));
|
|
4655
|
+
return guarded(() => this.#sessionInsert(transaction, tableName, input, "upsert", options2));
|
|
4636
4656
|
},
|
|
4637
4657
|
updateBatch: async (tableName, input) => {
|
|
4638
4658
|
open();
|
|
@@ -4866,10 +4886,17 @@ class MinnowDatabase {
|
|
|
4866
4886
|
}
|
|
4867
4887
|
return { added, removed };
|
|
4868
4888
|
}
|
|
4869
|
-
async #sessionInsert(transaction, tableName, input, kind, cascadeBudget = 1) {
|
|
4889
|
+
async #sessionInsert(transaction, tableName, input, kind, options, cascadeBudget = 1) {
|
|
4870
4890
|
const table = await this.#findTable(tableName);
|
|
4871
|
-
|
|
4872
|
-
const
|
|
4891
|
+
const sessionUpsertKeyColumn = kind === "upsert" ? getUniqueKeyColumn(table) : void 0;
|
|
4892
|
+
const normalizedConflictWhere = kind === "upsert" && options?.conflictWhere !== void 0 ? normalizeUpsertConflictWhere(table, options.conflictWhere) : void 0;
|
|
4893
|
+
if (normalizedConflictWhere !== void 0 && sessionUpsertKeyColumn === void 0) {
|
|
4894
|
+
throw new TypeError(`Table needs a unique key before it can be upserted: ${table.name}`);
|
|
4895
|
+
}
|
|
4896
|
+
const filled = await this.#fillDefaults(table, input);
|
|
4897
|
+
const { generated, autoIncrement } = filled;
|
|
4898
|
+
let batch = filled.batch;
|
|
4899
|
+
let rowCount = filled.rowCount;
|
|
4873
4900
|
if (autoIncrement !== void 0 && autoIncrement.missingIndexes.length > 0) {
|
|
4874
4901
|
const values = await this.store.reserveAutoIncrement(table.id, autoIncrement.column.id, autoIncrement.missingIndexes.length, autoIncrement.atLeast);
|
|
4875
4902
|
patchAutoIncrementValues(batch, autoIncrement, values);
|
|
@@ -4878,6 +4905,32 @@ class MinnowDatabase {
|
|
|
4878
4905
|
validateValue(autoIncrement.column, patched[rowIndex] ?? null, rowIndex);
|
|
4879
4906
|
}
|
|
4880
4907
|
}
|
|
4908
|
+
let sessionUpsertFirings = sessionUpsertKeyColumn === void 0 ? void 0 : await this.#upsertTriggerFirings(table, sessionUpsertKeyColumn, batch, rowCount, (sql, params) => this.#sessionQuery(transaction, sql, { params }), normalizedConflictWhere);
|
|
4909
|
+
let skippedRowCount = 0;
|
|
4910
|
+
if (normalizedConflictWhere !== void 0) {
|
|
4911
|
+
if (sessionUpsertFirings === void 0) {
|
|
4912
|
+
throw new Error("Upsert conflict classification is missing");
|
|
4913
|
+
}
|
|
4914
|
+
const filtered = applyUpsertConflictFilter(batch, rowCount, sessionUpsertFirings);
|
|
4915
|
+
skippedRowCount = filtered.skippedRowCount;
|
|
4916
|
+
if (filtered.rowCount !== rowCount) {
|
|
4917
|
+
remapGeneratedColumns(generated, filtered.acceptedRowIndexes);
|
|
4918
|
+
}
|
|
4919
|
+
batch = filtered.batch;
|
|
4920
|
+
rowCount = filtered.rowCount;
|
|
4921
|
+
sessionUpsertFirings = filtered.firings;
|
|
4922
|
+
if (rowCount === 0) {
|
|
4923
|
+
collectAutoIncrementGenerated(batch, generated, autoIncrement);
|
|
4924
|
+
return {
|
|
4925
|
+
tableName: table.name,
|
|
4926
|
+
segmentId: null,
|
|
4927
|
+
rowCount: 0,
|
|
4928
|
+
skippedRowCount,
|
|
4929
|
+
...generated.size === 0 ? {} : { generatedColumns: Object.fromEntries(generated) }
|
|
4930
|
+
};
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
await this.#assertCompactionCapacity(table, transaction);
|
|
4881
4934
|
const keys = batchKeys(table, batch);
|
|
4882
4935
|
if (keys !== void 0) {
|
|
4883
4936
|
transaction.setUniqueKeyChanges({
|
|
@@ -4889,8 +4942,6 @@ class MinnowDatabase {
|
|
|
4889
4942
|
await this.#assertForeignKeysPresent(table, (column) => batch.columns[column] ?? [], (sql, params) => this.#sessionQuery(transaction, sql, { params }), transaction);
|
|
4890
4943
|
const rowIds = await this.store.reserveRowIds(table.id, rowCount);
|
|
4891
4944
|
const insertValueAt = (source, column, rowIndex) => source === "new" ? batch.columns[column]?.[rowIndex] ?? null : null;
|
|
4892
|
-
const sessionUpsertKeyColumn = kind === "upsert" ? getUniqueKeyColumn(table) : void 0;
|
|
4893
|
-
const sessionUpsertFirings = sessionUpsertKeyColumn === void 0 ? void 0 : await this.#upsertTriggerFirings(table, sessionUpsertKeyColumn, batch, rowCount, (sql, params) => this.#sessionQuery(transaction, sql, { params }));
|
|
4894
4945
|
stageSecondaryUniqueInsertChanges(transaction, table, batch, kind === "upsert" ? sessionUpsertFirings?.oldImages : void 0);
|
|
4895
4946
|
if (kind === "insert") {
|
|
4896
4947
|
await this.#stageTriggerDerivedInserts(transaction, table, "insert", rowCount, insertValueAt, "before", cascadeBudget);
|
|
@@ -4908,6 +4959,7 @@ class MinnowDatabase {
|
|
|
4908
4959
|
tableName: table.name,
|
|
4909
4960
|
segmentId,
|
|
4910
4961
|
rowCount,
|
|
4962
|
+
skippedRowCount,
|
|
4911
4963
|
...generated.size === 0 ? {} : { generatedColumns: Object.fromEntries(generated) }
|
|
4912
4964
|
};
|
|
4913
4965
|
}
|
|
@@ -5135,7 +5187,10 @@ class MinnowDatabase {
|
|
|
5135
5187
|
}
|
|
5136
5188
|
return (await this.#memoizedQuery(query.plan, `typed ${planMemoKey(query.plan)}`, {}, probe)).rows;
|
|
5137
5189
|
}
|
|
5138
|
-
async migrate(definition, options = {}) {
|
|
5190
|
+
async migrate(definition = this.#schema, options = {}) {
|
|
5191
|
+
if (definition === void 0) {
|
|
5192
|
+
throw new TypeError("migrate() needs a schema: pass a definition, or construct the database with { schema }");
|
|
5193
|
+
}
|
|
5139
5194
|
let originalSteps;
|
|
5140
5195
|
for (let attempt = 0; ; attempt += 1) {
|
|
5141
5196
|
try {
|
|
@@ -6036,7 +6091,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6036
6091
|
let outcome;
|
|
6037
6092
|
let version;
|
|
6038
6093
|
if (options.writer === void 0) {
|
|
6039
|
-
const completed = await this.write(apply);
|
|
6094
|
+
const completed = await this.#erased.write(apply);
|
|
6040
6095
|
outcome = completed.result;
|
|
6041
6096
|
version = completed.version;
|
|
6042
6097
|
} else {
|
|
@@ -6141,7 +6196,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6141
6196
|
}
|
|
6142
6197
|
const sourceKey = mergeSourceKeyExpression(statement, keyColumn.name);
|
|
6143
6198
|
const sourceSql = mergeSourceSql(statement);
|
|
6144
|
-
const { result: applied, version } = await this.write(async (session) => {
|
|
6199
|
+
const { result: applied, version } = await this.#erased.write(async (session) => {
|
|
6145
6200
|
const sourceRows = (await session.query(sourceSql)).rows;
|
|
6146
6201
|
const keys = sourceRows.map((row) => storedSqlValueFromExecution(keyColumn, evaluateJoinedRowExpression(sourceKey, { [statement.source.alias]: row })));
|
|
6147
6202
|
const present = /* @__PURE__ */ new Map();
|
|
@@ -6722,7 +6777,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6722
6777
|
}
|
|
6723
6778
|
await this.#assertStagedInsertKeysFree(table2, stagedKeyColumn, input, writer);
|
|
6724
6779
|
}
|
|
6725
|
-
const result = await (viaUpsert ? writer?.upsertBatch(statement.table, input) ?? this.upsertBatch(statement.table, input) : writer?.insertBatch(statement.table, input) ?? this.insertBatch(statement.table, input));
|
|
6780
|
+
const result = await (viaUpsert ? writer?.upsertBatch(statement.table, input) ?? this.#erased.upsertBatch(statement.table, input) : writer?.insertBatch(statement.table, input) ?? this.#erased.insertBatch(statement.table, input));
|
|
6726
6781
|
const generated = "generatedColumns" in result ? result.generatedColumns ?? {} : {};
|
|
6727
6782
|
return {
|
|
6728
6783
|
kind: "insert",
|
|
@@ -6854,7 +6909,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6854
6909
|
...returnedRows2 === void 0 || returningColumns === void 0 ? {} : returningExecuteFields(table, returningColumns, returnedRows2)
|
|
6855
6910
|
};
|
|
6856
6911
|
}
|
|
6857
|
-
const deleted = options.writer === void 0 ? await this.deleteBatch(table.name, { keys }) : await options.writer.deleteBatch(table.name, { keys });
|
|
6912
|
+
const deleted = options.writer === void 0 ? await this.#erased.deleteBatch(table.name, { keys }) : await options.writer.deleteBatch(table.name, { keys });
|
|
6858
6913
|
return {
|
|
6859
6914
|
kind: "delete",
|
|
6860
6915
|
table: table.name,
|
|
@@ -6891,7 +6946,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6891
6946
|
returnedChanges[assignment.column] = executionValues;
|
|
6892
6947
|
}
|
|
6893
6948
|
}
|
|
6894
|
-
const updated = options.writer === void 0 ? await this.updateBatch(table.name, { keys, changes }) : await options.writer.updateBatch(table.name, { keys, changes });
|
|
6949
|
+
const updated = options.writer === void 0 ? await this.#erased.updateBatch(table.name, { keys, changes }) : await options.writer.updateBatch(table.name, { keys, changes });
|
|
6895
6950
|
const returnedRows = returningColumns === void 0 ? void 0 : rows.map((row, index) => Object.fromEntries(returningColumns.map((name) => [
|
|
6896
6951
|
name,
|
|
6897
6952
|
returnedChanges !== void 0 && name in returnedChanges ? returnedChanges[name]?.[index] ?? null : updated.generatedColumns?.[name] !== void 0 ? updated.generatedColumns[name][index] ?? null : row[name] ?? null
|
|
@@ -9275,7 +9330,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9275
9330
|
}
|
|
9276
9331
|
const priorAttemptOutputStoredBytes = await this.#priorCompactionAttemptOutputStoredBytes(table.id, baseJobId);
|
|
9277
9332
|
const maximumOutputStoredBytes = Math.max(0, floorWholeNumberProduct(selection.level0SourceStoredBytes, maxWriteAmplification, "Compaction maximum output stored bytes") - priorAttemptOutputStoredBytes);
|
|
9278
|
-
const plannedOutputStoredBytesUpperBound = await this.#plannedPhysicalOutputStoredBytesUpperBound(rewritePlan, snapshot);
|
|
9333
|
+
const plannedOutputStoredBytesUpperBound = await this.#plannedPhysicalOutputStoredBytesUpperBound(rewritePlan, memoryBudgetBytes, snapshot);
|
|
9279
9334
|
levelTwoBudget = {
|
|
9280
9335
|
outputPartitionOrdinal,
|
|
9281
9336
|
maxWriteAmplification,
|
|
@@ -9731,17 +9786,15 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9731
9786
|
const columnIndexById = new Map(table.columns.map((column, index) => [column.id, index]));
|
|
9732
9787
|
const touched = /* @__PURE__ */ new Set();
|
|
9733
9788
|
const deltaKeys = /* @__PURE__ */ new Map();
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
deltaKeys.set(segment.segmentId, keys);
|
|
9744
|
-
}
|
|
9789
|
+
const deltas = segments.filter((segment) => mergeSourceReferencesKeys(segment.kind));
|
|
9790
|
+
for (const segment of deltas)
|
|
9791
|
+
deltaKeys.set(segment.segmentId, []);
|
|
9792
|
+
const unusedBudgetBytes = memoryBudgetBytes - plannerMemoryBytes;
|
|
9793
|
+
await this.#forEachMergeSourceKey(deltas, keyColumn, unusedBudgetBytes, snapshot, (segment, value) => {
|
|
9794
|
+
const key = overlayKeyOf(keyColumn.type, value);
|
|
9795
|
+
deltaKeys.get(segment.segmentId)?.push(key);
|
|
9796
|
+
touched.add(key);
|
|
9797
|
+
});
|
|
9745
9798
|
let slotCount = 0;
|
|
9746
9799
|
for (const segment of segments) {
|
|
9747
9800
|
if (mergeSourceBearsRows(segment.kind))
|
|
@@ -9813,7 +9866,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9813
9866
|
if (keys !== void 0) {
|
|
9814
9867
|
keys.forEach(visit);
|
|
9815
9868
|
} else if (touched.size > 0) {
|
|
9816
|
-
await this.#forEachMergeSourceKey(segment, keyColumn, snapshot, (value, rowIndex) => {
|
|
9869
|
+
await this.#forEachMergeSourceKey([segment], keyColumn, unusedBudgetBytes, snapshot, (_, value, rowIndex) => {
|
|
9817
9870
|
visit(overlayKeyOf(keyColumn.type, value), rowIndex);
|
|
9818
9871
|
});
|
|
9819
9872
|
}
|
|
@@ -9850,28 +9903,48 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9850
9903
|
}
|
|
9851
9904
|
return { ...output.finish(), sourceOutputRowStarts };
|
|
9852
9905
|
}
|
|
9853
|
-
async #forEachMergeSourceKey(
|
|
9854
|
-
const
|
|
9855
|
-
|
|
9856
|
-
|
|
9906
|
+
async #forEachMergeSourceKey(segments, column, unusedBudgetBytes, snapshot, action) {
|
|
9907
|
+
const blocks = [];
|
|
9908
|
+
let largestEncodedBytes = 0;
|
|
9909
|
+
for (const segment of segments) {
|
|
9910
|
+
const planned = segment.columns.find((candidate) => candidate.columnId === column.id);
|
|
9911
|
+
if (planned === void 0) {
|
|
9912
|
+
throw new Error(`Mutation segment has no key column: ${segment.segmentId}`);
|
|
9913
|
+
}
|
|
9914
|
+
for (const source of planned.sourceBlocks) {
|
|
9915
|
+
blocks.push({
|
|
9916
|
+
blockId: source.blockId,
|
|
9917
|
+
storedBytes: source.storedBytes,
|
|
9918
|
+
encodedBytes: source.encodedBytes,
|
|
9919
|
+
segment,
|
|
9920
|
+
source
|
|
9921
|
+
});
|
|
9922
|
+
largestEncodedBytes = Math.max(largestEncodedBytes, source.encodedBytes);
|
|
9923
|
+
}
|
|
9857
9924
|
}
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9925
|
+
const limitBytes = safeWholeNumberSum([
|
|
9926
|
+
Math.max(0, unusedBudgetBytes),
|
|
9927
|
+
safeWholeNumberProduct(largestEncodedBytes, MERGE_PLANNER_DECODED_KEY_BLOCK_FACTOR, "Mutation compaction key decode memory")
|
|
9928
|
+
], "Mutation compaction key decode memory");
|
|
9929
|
+
const rowIndexes = /* @__PURE__ */ new Map();
|
|
9930
|
+
await this.#decodeCompactionBlocks(blocks, { limitBytes, retainsDecoded: false }, snapshot, async (bytes, { source }) => {
|
|
9864
9931
|
const description = inspectBlock(bytes);
|
|
9865
9932
|
if (bytes.byteLength !== source.storedBytes || description.encodedLength !== source.encodedBytes || description.checksum !== source.checksum || description.rowCount !== source.rowCount || description.type !== column.type) {
|
|
9866
9933
|
throw new Error(`Compaction source block differs from its plan: ${source.blockId}`);
|
|
9867
9934
|
}
|
|
9868
|
-
|
|
9869
|
-
|
|
9935
|
+
return (await decodeBlock(bytes)).column.values;
|
|
9936
|
+
}, (values, { segment }) => {
|
|
9937
|
+
let rowIndex = rowIndexes.get(segment.segmentId) ?? 0;
|
|
9938
|
+
for (const value of values) {
|
|
9939
|
+
action(segment, value, rowIndex);
|
|
9870
9940
|
rowIndex += 1;
|
|
9871
9941
|
}
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9942
|
+
rowIndexes.set(segment.segmentId, rowIndex);
|
|
9943
|
+
});
|
|
9944
|
+
for (const segment of segments) {
|
|
9945
|
+
if ((rowIndexes.get(segment.segmentId) ?? 0) !== segment.rowCount) {
|
|
9946
|
+
throw new Error(`Mutation segment key rows differ: ${segment.segmentId}`);
|
|
9947
|
+
}
|
|
9875
9948
|
}
|
|
9876
9949
|
}
|
|
9877
9950
|
async #refinePhysicalOutputWindows(columns, estimatedOutputs, targetBlockBytes, outputCompression, memoryBudgetBytes, snapshot) {
|
|
@@ -9930,12 +10003,12 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
9930
10003
|
const loaded = await this.#loadPhysicalCompactionRanges(column, output, outputCompression, memoryBudgetBytes, snapshot);
|
|
9931
10004
|
return measurePhysicalColumnRanges(column.type, loaded.ranges);
|
|
9932
10005
|
}
|
|
9933
|
-
async #plannedPhysicalOutputStoredBytesUpperBound(plan, snapshot) {
|
|
10006
|
+
async #plannedPhysicalOutputStoredBytesUpperBound(plan, memoryBudgetBytes, snapshot) {
|
|
9934
10007
|
const layout = physicalRewriteLayout(plan);
|
|
9935
10008
|
let total = 0;
|
|
9936
10009
|
for (const output of layout.outputs) {
|
|
9937
10010
|
for (const column of layout.columns) {
|
|
9938
|
-
const measurement = await this.#measurePhysicalCompactionOutput(column, output, plan.outputCompression,
|
|
10011
|
+
const measurement = await this.#measurePhysicalCompactionOutput(column, output, plan.outputCompression, memoryBudgetBytes, snapshot);
|
|
9939
10012
|
total = safeWholeNumberSum([
|
|
9940
10013
|
total,
|
|
9941
10014
|
maximumPhysicalBlockByteLength(measurement.encodedByteLength, measurement.metadata, plan.outputCompression)
|
|
@@ -10296,26 +10369,84 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
10296
10369
|
throw new CompactionMemoryBudgetError(memoryBudgetBytes, memoryBound);
|
|
10297
10370
|
}
|
|
10298
10371
|
const outputEnd = safeWholeNumberSum([output.rowStart, output.rowCount], "Compaction output row range");
|
|
10299
|
-
const
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10372
|
+
const sourceBlocks = overlappingPhysicalSourceRanges(column, output);
|
|
10373
|
+
const distinct = [];
|
|
10374
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10375
|
+
for (const sourceBlock of sourceBlocks) {
|
|
10376
|
+
if (seen.has(sourceBlock.blockId))
|
|
10377
|
+
continue;
|
|
10378
|
+
seen.add(sourceBlock.blockId);
|
|
10379
|
+
distinct.push(sourceBlock);
|
|
10380
|
+
}
|
|
10381
|
+
const decoded = /* @__PURE__ */ new Map();
|
|
10382
|
+
const peakWorkingBytes = await this.#decodeCompactionBlocks(distinct, { limitBytes: memoryBudgetBytes, retainsDecoded: true }, snapshot, async (bytes, sourceBlock) => {
|
|
10307
10383
|
const description = inspectBlock(bytes);
|
|
10308
10384
|
if (bytes.byteLength !== sourceBlock.storedBytes || description.encodedLength !== sourceBlock.encodedBytes || description.checksum !== sourceBlock.checksum || description.rowCount !== sourceBlock.sourceBlockRowCount || description.type !== column.type) {
|
|
10309
10385
|
throw new Error(`A compaction source block differs from its plan: ${sourceBlock.blockId}`);
|
|
10310
10386
|
}
|
|
10311
|
-
|
|
10387
|
+
return decodePhysicalBlock(bytes);
|
|
10388
|
+
}, (block, sourceBlock) => {
|
|
10389
|
+
decoded.set(sourceBlock.blockId, block);
|
|
10390
|
+
});
|
|
10391
|
+
const ranges = [];
|
|
10392
|
+
for (const sourceBlock of sourceBlocks) {
|
|
10393
|
+
const block = decoded.get(sourceBlock.blockId);
|
|
10394
|
+
if (block === void 0) {
|
|
10395
|
+
throw new Error(`A compaction source block is missing: ${sourceBlock.blockId}`);
|
|
10396
|
+
}
|
|
10312
10397
|
const sourceEnd = safeWholeNumberSum([sourceBlock.outputRowStart, sourceBlock.rowCount], "Compaction source row range");
|
|
10313
10398
|
const start = sourceBlock.sourceRowStart + Math.max(output.rowStart, sourceBlock.outputRowStart) - sourceBlock.outputRowStart;
|
|
10314
10399
|
const end = sourceBlock.sourceRowStart + Math.min(outputEnd, sourceEnd) - sourceBlock.outputRowStart;
|
|
10315
|
-
const slice = slicePhysicalColumn(
|
|
10400
|
+
const slice = slicePhysicalColumn(block.column, start, end);
|
|
10316
10401
|
ranges.push({ column: slice, start: 0, end: slice.rowCount });
|
|
10317
10402
|
}
|
|
10318
|
-
return { ranges, peakWorkingBytes: memoryBound };
|
|
10403
|
+
return { ranges, peakWorkingBytes: Math.max(memoryBound, peakWorkingBytes) };
|
|
10404
|
+
}
|
|
10405
|
+
async #decodeCompactionBlocks(blocks, budget, snapshot, decode, consume) {
|
|
10406
|
+
let retainedBytes = 0;
|
|
10407
|
+
let peakBytes = 0;
|
|
10408
|
+
let start = 0;
|
|
10409
|
+
while (start < blocks.length) {
|
|
10410
|
+
let end = start;
|
|
10411
|
+
let transientBytes = 0;
|
|
10412
|
+
let storedBytes = 0;
|
|
10413
|
+
for (; end < blocks.length && end - start < MAX_STORAGE_BULK_READ_ITEMS; end += 1) {
|
|
10414
|
+
const block = blocks[end];
|
|
10415
|
+
if (block === void 0)
|
|
10416
|
+
break;
|
|
10417
|
+
const blockTransientBytes = safeWholeNumberSum([
|
|
10418
|
+
safeWholeNumberProduct(block.storedBytes, 2, "Compaction decode memory"),
|
|
10419
|
+
safeWholeNumberProduct(block.encodedBytes, 2, "Compaction decode memory")
|
|
10420
|
+
], "Compaction decode memory");
|
|
10421
|
+
if (end > start && (transientBytes + blockTransientBytes > budget.limitBytes - retainedBytes || storedBytes + block.storedBytes > MAX_BLOCK_READ_BATCH_BYTES)) {
|
|
10422
|
+
break;
|
|
10423
|
+
}
|
|
10424
|
+
transientBytes += blockTransientBytes;
|
|
10425
|
+
storedBytes += block.storedBytes;
|
|
10426
|
+
}
|
|
10427
|
+
const group = blocks.slice(start, end);
|
|
10428
|
+
if (snapshot !== void 0)
|
|
10429
|
+
await this.#renewInternalLeaseIfNeeded(snapshot);
|
|
10430
|
+
const bytes = await this.#readBlockWindow(group.map((block) => block.blockId));
|
|
10431
|
+
const decoded = await Promise.all(group.map((block, index) => {
|
|
10432
|
+
const stored = bytes[index];
|
|
10433
|
+
if (stored === void 0) {
|
|
10434
|
+
throw new Error(`A compaction source block is missing: ${block.blockId}`);
|
|
10435
|
+
}
|
|
10436
|
+
return decode(stored, block);
|
|
10437
|
+
}));
|
|
10438
|
+
peakBytes = Math.max(peakBytes, retainedBytes + transientBytes);
|
|
10439
|
+
for (const [index, block] of group.entries()) {
|
|
10440
|
+
const value = decoded[index];
|
|
10441
|
+
if (value === void 0)
|
|
10442
|
+
throw new Error("Compaction decode lost a block");
|
|
10443
|
+
consume(value, block);
|
|
10444
|
+
if (budget.retainsDecoded)
|
|
10445
|
+
retainedBytes += block.encodedBytes;
|
|
10446
|
+
}
|
|
10447
|
+
start = end;
|
|
10448
|
+
}
|
|
10449
|
+
return peakBytes;
|
|
10319
10450
|
}
|
|
10320
10451
|
async #loadCompactionSources(job) {
|
|
10321
10452
|
const segments = await Promise.all(job.sourceSegmentIds.map((id) => this.store.getSegment(id)));
|
|
@@ -14152,6 +14283,49 @@ function normalizeColumnLogicalValue(column, value) {
|
|
|
14152
14283
|
}
|
|
14153
14284
|
return value;
|
|
14154
14285
|
}
|
|
14286
|
+
function mergeAscendingIndexes(left, right) {
|
|
14287
|
+
const merged = [];
|
|
14288
|
+
let leftIndex = 0;
|
|
14289
|
+
let rightIndex = 0;
|
|
14290
|
+
while (leftIndex < left.length || rightIndex < right.length) {
|
|
14291
|
+
const a = left[leftIndex];
|
|
14292
|
+
const b = right[rightIndex];
|
|
14293
|
+
if (b === void 0 || a !== void 0 && a <= b) {
|
|
14294
|
+
if (a !== void 0)
|
|
14295
|
+
merged.push(a);
|
|
14296
|
+
leftIndex += 1;
|
|
14297
|
+
} else {
|
|
14298
|
+
merged.push(b);
|
|
14299
|
+
rightIndex += 1;
|
|
14300
|
+
}
|
|
14301
|
+
}
|
|
14302
|
+
return merged;
|
|
14303
|
+
}
|
|
14304
|
+
function applyUpsertConflictFilter(batch, rowCount, classified) {
|
|
14305
|
+
const acceptedRowIndexes = mergeAscendingIndexes(classified.inserts, classified.updates);
|
|
14306
|
+
const skippedRowCount = classified.skipped.length;
|
|
14307
|
+
if (acceptedRowIndexes.length === rowCount) {
|
|
14308
|
+
return { batch, rowCount, skippedRowCount, acceptedRowIndexes, firings: classified };
|
|
14309
|
+
}
|
|
14310
|
+
const acceptedPosition = new Map(acceptedRowIndexes.map((original, position) => [original, position]));
|
|
14311
|
+
return {
|
|
14312
|
+
batch: selectBatchRows(batch, acceptedRowIndexes),
|
|
14313
|
+
rowCount: acceptedRowIndexes.length,
|
|
14314
|
+
skippedRowCount,
|
|
14315
|
+
acceptedRowIndexes,
|
|
14316
|
+
firings: {
|
|
14317
|
+
inserts: classified.inserts.map((index) => acceptedPosition.get(index) ?? -1),
|
|
14318
|
+
updates: classified.updates.map((index) => acceptedPosition.get(index) ?? -1),
|
|
14319
|
+
skipped: [],
|
|
14320
|
+
oldImages: acceptedRowIndexes.map((index) => classified.oldImages[index])
|
|
14321
|
+
}
|
|
14322
|
+
};
|
|
14323
|
+
}
|
|
14324
|
+
function remapGeneratedColumns(generated, acceptedRowIndexes) {
|
|
14325
|
+
for (const [name, values] of generated) {
|
|
14326
|
+
generated.set(name, acceptedRowIndexes.map((index) => values[index] ?? null));
|
|
14327
|
+
}
|
|
14328
|
+
}
|
|
14155
14329
|
function selectBatchRows(input, indexes) {
|
|
14156
14330
|
return {
|
|
14157
14331
|
columns: Object.fromEntries(Object.entries(input.columns).map(([name, values]) => [
|
|
@@ -14431,6 +14605,14 @@ function autoCompactionHint(version, segments) {
|
|
|
14431
14605
|
deltas
|
|
14432
14606
|
};
|
|
14433
14607
|
}
|
|
14608
|
+
function countLevelZeroSegments(segments) {
|
|
14609
|
+
let levelZero = 0;
|
|
14610
|
+
for (const segment of segments) {
|
|
14611
|
+
if (segment.level === 0)
|
|
14612
|
+
levelZero += 1;
|
|
14613
|
+
}
|
|
14614
|
+
return levelZero;
|
|
14615
|
+
}
|
|
14434
14616
|
function autoCompactionDueHint(hint) {
|
|
14435
14617
|
return hint.levelZero >= AUTO_COMPACT_SCAN_SEGMENTS || hint.deltas >= AUTO_COMPACT_DELTA_SEGMENTS;
|
|
14436
14618
|
}
|
|
@@ -15881,6 +16063,7 @@ function sourceOrderTuple(segment, transactions, label) {
|
|
|
15881
16063
|
};
|
|
15882
16064
|
}
|
|
15883
16065
|
const MERGE_PLANNER_KEY_BYTES = 96;
|
|
16066
|
+
const MERGE_PLANNER_DECODED_KEY_BLOCK_FACTOR = 4;
|
|
15884
16067
|
function planLinearOutputPartitions(totalRows, partitionRows, firstOrder, nextOrder) {
|
|
15885
16068
|
const count = Math.max(1, Math.ceil(totalRows / partitionRows));
|
|
15886
16069
|
const orders = fractionalLogicalOrders(firstOrder, nextOrder, count);
|
|
@@ -15996,7 +16179,6 @@ function mergePlannerMemoryBound(table, segments, keyColumnId) {
|
|
|
15996
16179
|
const PATCH_ROW_BYTES = 64;
|
|
15997
16180
|
const PATCH_CELL_BYTES = 48;
|
|
15998
16181
|
const RANGE_BYTES = 80;
|
|
15999
|
-
const DECODED_KEY_BLOCK_FACTOR = 4;
|
|
16000
16182
|
let slotRows = 0;
|
|
16001
16183
|
let deltaKeys = 0;
|
|
16002
16184
|
let patchRows = 0;
|
|
@@ -16027,7 +16209,7 @@ function mergePlannerMemoryBound(table, segments, keyColumnId) {
|
|
|
16027
16209
|
safeWholeNumberProduct(columns, PATCH_CELL_BYTES, "Mutation patch cells")
|
|
16028
16210
|
], "Mutation compaction patch row"), "Mutation compaction patches"),
|
|
16029
16211
|
safeWholeNumberProduct(safeWholeNumberSum([sourceBlocks, safeWholeNumberProduct(patchRows, columns, "Mutation patched cells")], "Mutation compaction ranges"), RANGE_BYTES, "Mutation compaction range bytes"),
|
|
16030
|
-
safeWholeNumberProduct(largestKeyBlockBytes,
|
|
16212
|
+
safeWholeNumberProduct(largestKeyBlockBytes, MERGE_PLANNER_DECODED_KEY_BLOCK_FACTOR, "Mutation compaction decoded key block")
|
|
16031
16213
|
], "Mutation compaction planner memory");
|
|
16032
16214
|
}
|
|
16033
16215
|
function mergeSourceBearsRows(kind) {
|