@minnowdb/core 0.7.7 → 0.7.10
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 +44 -27
- package/dist/engine/client.js +22 -10
- package/dist/engine/database.d.ts +40 -22
- package/dist/engine/database.js +489 -38
- package/dist/engine/keyed-live.js +55 -18
- package/dist/engine/live.d.ts +95 -8
- package/dist/engine/live.js +311 -148
- package/dist/engine/schema.d.ts +85 -2
- package/dist/engine/typed-live.d.ts +12 -1
- package/dist/engine/typed-live.js +89 -12
- package/dist/engine/worker-server.js +12 -5
- 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";
|
|
@@ -21,7 +21,7 @@ import { copyQueryResult, planMemoKey, queryResultMemoKey, queryResultRetainedBy
|
|
|
21
21
|
import { QueryMemoryBudgetError, QueryMemoryContext, DEFAULT_QUERY_MEMORY_BUDGET_BYTES } from "./memory.js";
|
|
22
22
|
import { LiveQueryLimitError, LiveQuerySet, MAX_LIVE_QUERY_SETS_PER_DATABASE } from "./live.js";
|
|
23
23
|
import { chooseJoinOrder, optimizePlan, qualifyCorrelatedReferences, renderPlan } from "./optimizer.js";
|
|
24
|
-
import { encodeSqlEqualityValue, readUntypedText } from "./sql-semantics.js";
|
|
24
|
+
import { encodeSqlEqualityValue, readUntypedText, compareSqlValues } from "./sql-semantics.js";
|
|
25
25
|
import { exactNumericAsNumber, exactNumericValue, externalSqlDomainValue, externalSqlTextValue, isDateDomainValue, isExactNumeric, isSqlDomainValue, normalizeSqlDomainValue, protectedSqlTextValue } from "./sql-domains.js";
|
|
26
26
|
import { toCatalog } from "./catalog.js";
|
|
27
27
|
import { applyColumnSteps, assertColumnDroppable, compileGeneratedColumnExpression, declaredForeignKeys, isDestructiveStep, planMigration } from "./schema.js";
|
|
@@ -182,6 +182,9 @@ function migrationResult(steps) {
|
|
|
182
182
|
steps: [...steps]
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
|
+
function compactUpdateBatchInput(input) {
|
|
186
|
+
return Object.values(input.changes).includes(void 0) ? { keys: input.keys, changes: definedVectors(input.changes) } : input;
|
|
187
|
+
}
|
|
185
188
|
const MAX_GARBAGE_COLLECTION_RETAIN_RECENT_VERSIONS = 1024;
|
|
186
189
|
const REFERENTIAL_CASCADES = 8;
|
|
187
190
|
class TransactionRollback extends Error {
|
|
@@ -548,6 +551,10 @@ async function* singleSnapshotChunk(bytes) {
|
|
|
548
551
|
}
|
|
549
552
|
class MinnowDatabase {
|
|
550
553
|
store;
|
|
554
|
+
#schema;
|
|
555
|
+
get #erased() {
|
|
556
|
+
return this;
|
|
557
|
+
}
|
|
551
558
|
#closed = false;
|
|
552
559
|
#closePromise;
|
|
553
560
|
#transactions;
|
|
@@ -625,6 +632,7 @@ class MinnowDatabase {
|
|
|
625
632
|
#preparedCatalogStates = /* @__PURE__ */ new WeakMap();
|
|
626
633
|
constructor(store, options = {}) {
|
|
627
634
|
this.store = store;
|
|
635
|
+
this.#schema = options.schema;
|
|
628
636
|
this.#compression = options.compression ?? "gzip";
|
|
629
637
|
this.#rowsPerBlock = options.rowsPerBlock ?? 65536;
|
|
630
638
|
if (!Number.isSafeInteger(this.#rowsPerBlock) || this.#rowsPerBlock <= 0 || this.#rowsPerBlock > MAX_BLOCK_ROW_COUNT) {
|
|
@@ -1116,11 +1124,11 @@ class MinnowDatabase {
|
|
|
1116
1124
|
this.#activeReadReservations -= 1;
|
|
1117
1125
|
}
|
|
1118
1126
|
}
|
|
1119
|
-
async #queryWithReadReservation(sql, options) {
|
|
1127
|
+
async #queryWithReadReservation(sql, options, probe, storeMemo = true) {
|
|
1120
1128
|
throwIfAborted(options.signal);
|
|
1121
1129
|
await this.#settleExpiredStatementTransaction();
|
|
1122
1130
|
throwIfAborted(options.signal);
|
|
1123
|
-
return this.#openTransaction === void 0 ? this.#withReadReservation(() => this.#queryUnreserved(sql, options)) : this.#queryUnreserved(sql, options);
|
|
1131
|
+
return this.#openTransaction === void 0 ? this.#withReadReservation(() => this.#queryUnreserved(sql, options, probe, storeMemo)) : this.#queryUnreserved(sql, options, probe, storeMemo);
|
|
1124
1132
|
}
|
|
1125
1133
|
async #assistAutomaticCollection() {
|
|
1126
1134
|
if (!this.#autoCollect && !this.#manualCollectionDebtInitialized) {
|
|
@@ -1589,7 +1597,7 @@ class MinnowDatabase {
|
|
|
1589
1597
|
return { ...statement, rows };
|
|
1590
1598
|
}
|
|
1591
1599
|
async insert(tableName, row) {
|
|
1592
|
-
return this.insertBatch(tableName, [row]);
|
|
1600
|
+
return this.#erased.insertBatch(tableName, [row]);
|
|
1593
1601
|
}
|
|
1594
1602
|
upsertBatch(tableName, input, options = {}) {
|
|
1595
1603
|
return this.#withWriteReservation(() => this.#upsertBatchReserved(tableName, input, options));
|
|
@@ -1631,10 +1639,10 @@ class MinnowDatabase {
|
|
|
1631
1639
|
};
|
|
1632
1640
|
}
|
|
1633
1641
|
async upsert(tableName, row, options = {}) {
|
|
1634
|
-
return this.upsertBatch(tableName, [row], options);
|
|
1642
|
+
return this.#erased.upsertBatch(tableName, [row], options);
|
|
1635
1643
|
}
|
|
1636
1644
|
updateBatch(tableName, input) {
|
|
1637
|
-
return this.#withWriteReservation(() => this.#updateBatchReserved(tableName, input));
|
|
1645
|
+
return this.#withWriteReservation(() => this.#updateBatchReserved(tableName, compactUpdateBatchInput(input)));
|
|
1638
1646
|
}
|
|
1639
1647
|
async #updateBatchReserved(tableName, input) {
|
|
1640
1648
|
return this.#runWrite(async () => {
|
|
@@ -1657,13 +1665,13 @@ class MinnowDatabase {
|
|
|
1657
1665
|
});
|
|
1658
1666
|
}
|
|
1659
1667
|
async update(tableName, key, changes) {
|
|
1660
|
-
return this.updateBatch(tableName, {
|
|
1668
|
+
return this.#erased.updateBatch(tableName, {
|
|
1661
1669
|
keys: [key],
|
|
1662
|
-
changes: Object.fromEntries(Object.entries(changes).
|
|
1670
|
+
changes: Object.fromEntries(Object.entries(changes).flatMap(([name, value]) => value === void 0 ? [] : [[name, [value]]]))
|
|
1663
1671
|
});
|
|
1664
1672
|
}
|
|
1665
1673
|
async delete(tableName, key) {
|
|
1666
|
-
return this.deleteBatch(tableName, { keys: [key] });
|
|
1674
|
+
return this.#erased.deleteBatch(tableName, { keys: [key] });
|
|
1667
1675
|
}
|
|
1668
1676
|
deleteBatch(tableName, input) {
|
|
1669
1677
|
return this.#withWriteReservation(() => this.#deleteBatchReserved(tableName, input));
|
|
@@ -1678,7 +1686,7 @@ class MinnowDatabase {
|
|
|
1678
1686
|
if (dependents.length === 0)
|
|
1679
1687
|
return this.#deleteBatchOnce(tableName, normalizedInput);
|
|
1680
1688
|
const started = performance.now();
|
|
1681
|
-
const { result, version } = await this.write(async (session) => {
|
|
1689
|
+
const { result, version } = await this.#erased.write(async (session) => {
|
|
1682
1690
|
await this.#applyReferentialActions(table, [...normalizedInput.keys], session, REFERENTIAL_CASCADES);
|
|
1683
1691
|
return session.deleteBatch(tableName, normalizedInput);
|
|
1684
1692
|
});
|
|
@@ -1882,7 +1890,7 @@ class MinnowDatabase {
|
|
|
1882
1890
|
}
|
|
1883
1891
|
}
|
|
1884
1892
|
bufferedWriter(tableName, options = {}) {
|
|
1885
|
-
return new BufferedTableWriter(this, tableName, options);
|
|
1893
|
+
return new BufferedTableWriter(this.#erased, tableName, options);
|
|
1886
1894
|
}
|
|
1887
1895
|
async #writeUpdateBatch(table, keyColumn, input, keys) {
|
|
1888
1896
|
await this.#assertCompactionCapacity(table);
|
|
@@ -2849,7 +2857,7 @@ class MinnowDatabase {
|
|
|
2849
2857
|
async query(sql, options = {}) {
|
|
2850
2858
|
return this.#queryWithReadReservation(sql, options);
|
|
2851
2859
|
}
|
|
2852
|
-
async #queryUnreserved(sql, options) {
|
|
2860
|
+
async #queryUnreserved(sql, options, probe, storeMemo = true) {
|
|
2853
2861
|
throwIfAborted(options.signal);
|
|
2854
2862
|
const open = this.#openTransaction;
|
|
2855
2863
|
if (open !== void 0) {
|
|
@@ -2875,9 +2883,9 @@ class MinnowDatabase {
|
|
|
2875
2883
|
}
|
|
2876
2884
|
}
|
|
2877
2885
|
const plan = bindPlanParameters(compiled, options.params);
|
|
2878
|
-
const
|
|
2886
|
+
const readProbe = this.store.getCatalogProbe.bind(this.store);
|
|
2879
2887
|
const memoizable = options.memoize !== false && cacheableQueryInput(sql, options.params ?? []) && plan.usesStatementDatetime !== true && plan.usesVolatileFunctions !== true && plan.usesSequenceCalls !== true && options.version === void 0 && options.executionMemoryBudgetBytes === void 0 && options.spillToStorage === void 0 && options.spillPageRows === void 0;
|
|
2880
|
-
const result = !memoizable ? await this.#queryCompiled(plan, options) : await this.#memoizedQuery(plan, `res ${queryResultMemoKey(sql, options.params ?? [])}`, options, probe);
|
|
2888
|
+
const result = !memoizable ? await this.#queryCompiled(plan, options, probe) : await this.#memoizedQuery(plan, `res ${queryResultMemoKey(sql, options.params ?? [])}`, options, readProbe, probe, storeMemo);
|
|
2881
2889
|
throwIfAborted(options.signal);
|
|
2882
2890
|
return externalizeQueryResult(result);
|
|
2883
2891
|
}
|
|
@@ -2966,9 +2974,9 @@ class MinnowDatabase {
|
|
|
2966
2974
|
});
|
|
2967
2975
|
}
|
|
2968
2976
|
}
|
|
2969
|
-
async #memoizedQuery(plan, key, options, probe) {
|
|
2977
|
+
async #memoizedQuery(plan, key, options, probe, before, store = true) {
|
|
2970
2978
|
throwIfAborted(options.signal);
|
|
2971
|
-
|
|
2979
|
+
before ??= await probe();
|
|
2972
2980
|
throwIfAborted(options.signal);
|
|
2973
2981
|
const cached = this.#cacheGet(`${key}${String(before.catalogEpoch)}`);
|
|
2974
2982
|
if (cached !== void 0) {
|
|
@@ -2977,6 +2985,8 @@ class MinnowDatabase {
|
|
|
2977
2985
|
}
|
|
2978
2986
|
const result = await this.#queryCompiled(plan, options, before);
|
|
2979
2987
|
throwIfAborted(options.signal);
|
|
2988
|
+
if (!store)
|
|
2989
|
+
return result;
|
|
2980
2990
|
const bytes = queryResultRetainedBytes(result);
|
|
2981
2991
|
if (bytes <= RESULT_MEMO_MAX_BYTES) {
|
|
2982
2992
|
const after = await probe();
|
|
@@ -3721,19 +3731,15 @@ class MinnowDatabase {
|
|
|
3721
3731
|
return this.store.getCatalogProbe();
|
|
3722
3732
|
},
|
|
3723
3733
|
manifestPage: (afterVersion, limit) => this.store.listManifestPage(afterVersion, limit),
|
|
3724
|
-
dependencyTableIds: async (query) => {
|
|
3734
|
+
dependencyTableIds: async (query, probe) => {
|
|
3725
3735
|
const compiled = compileLiveQuery(query);
|
|
3726
|
-
const plan = await this.#applyCatalogRewrites(compiled);
|
|
3736
|
+
const plan = await this.#applyCatalogRewrites(compiled, probe);
|
|
3727
3737
|
const tables = await this.#findRealBlockTables(plan);
|
|
3728
3738
|
return new Set([...tables.values()].map((record) => record.id));
|
|
3729
3739
|
},
|
|
3730
|
-
execute:
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
if (query.kind === "typed-query")
|
|
3734
|
-
return this.#queryCompiled(query.plan);
|
|
3735
|
-
return this.query(query.sql, { params: query.params });
|
|
3736
|
-
},
|
|
3740
|
+
execute: (query, context) => this.#liveExecute(query, context),
|
|
3741
|
+
executeMaintainable: (query, context) => this.#liveExecuteMaintainable(compileLiveQuery(query), context),
|
|
3742
|
+
maintain: (_query, result, state, tableIds, after, until, probe) => this.#liveMaintain(result, state, tableIds, after, until, probe),
|
|
3737
3743
|
changeCanAffect: (query, tableIds, after, until) => this.#liveChangeCanAffect(query, tableIds, after, until)
|
|
3738
3744
|
}, {
|
|
3739
3745
|
...options,
|
|
@@ -3746,6 +3752,205 @@ class MinnowDatabase {
|
|
|
3746
3752
|
this.#liveSets.add(set);
|
|
3747
3753
|
return set;
|
|
3748
3754
|
}
|
|
3755
|
+
async #liveExecute(query, context) {
|
|
3756
|
+
if (context === void 0) {
|
|
3757
|
+
if (typeof query === "string")
|
|
3758
|
+
return this.query(query);
|
|
3759
|
+
if (query.kind === "typed-query")
|
|
3760
|
+
return this.#queryCompiled(query.plan);
|
|
3761
|
+
return this.query(query.sql, { params: query.params });
|
|
3762
|
+
}
|
|
3763
|
+
const { probe, memoize } = context;
|
|
3764
|
+
if (typeof query !== "string" && query.kind === "typed-query") {
|
|
3765
|
+
const plan = query.plan;
|
|
3766
|
+
const memoizable = plan.usesStatementDatetime !== true && plan.usesVolatileFunctions !== true && plan.usesSequenceCalls !== true;
|
|
3767
|
+
return this.#withReadReservation(() => memoizable ? this.#memoizedQuery(plan, `typed ${planMemoKey(plan)}`, {}, this.store.getCatalogProbe.bind(this.store), probe, memoize) : this.#queryCompiled(plan, {}, probe));
|
|
3768
|
+
}
|
|
3769
|
+
if (typeof query === "string")
|
|
3770
|
+
return this.#queryWithReadReservation(query, {}, probe, memoize);
|
|
3771
|
+
return this.#queryWithReadReservation(query.sql, { params: query.params }, probe, memoize);
|
|
3772
|
+
}
|
|
3773
|
+
async #liveMaintenancePlan(compiled, probe) {
|
|
3774
|
+
if (compiled.usesStatementDatetime === true || compiled.usesVolatileFunctions === true || compiled.usesSequenceCalls === true) {
|
|
3775
|
+
return void 0;
|
|
3776
|
+
}
|
|
3777
|
+
const plan = await this.#applyCatalogRewrites(compiled, probe);
|
|
3778
|
+
const base = plan.base;
|
|
3779
|
+
if (plan.joins.length > 0 || base.derived !== void 0 || base.union !== void 0 || base.recursive !== void 0 || base.windowed !== void 0 || plan.pendingSelectShape !== void 0 || plan.distinctWildcard === true || plan.groupBy.length > 0 || plan.having.length > 0 || plan.limitParameter !== void 0 || plan.offsetParameter !== void 0 || planReadsBeyondSingleScan(plan) || planContainsFts(plan)) {
|
|
3780
|
+
return void 0;
|
|
3781
|
+
}
|
|
3782
|
+
const rowLocal = (expression) => {
|
|
3783
|
+
if (expression.kind === "subquery" || expression.kind === "exists" || expression.kind === "window" || expression.kind === "wildcard" || expression.kind === "parameter" || hasAggregate(expression)) {
|
|
3784
|
+
return false;
|
|
3785
|
+
}
|
|
3786
|
+
return childExpressions(expression).every(rowLocal);
|
|
3787
|
+
};
|
|
3788
|
+
if (!plan.select.every((item) => rowLocal(item.expression)))
|
|
3789
|
+
return void 0;
|
|
3790
|
+
if (!plan.predicates.every(({ left, right }) => rowLocal(left) && rowLocal(right))) {
|
|
3791
|
+
return void 0;
|
|
3792
|
+
}
|
|
3793
|
+
if (!plan.orderBy.every((term) => rowLocal(term.expression)))
|
|
3794
|
+
return void 0;
|
|
3795
|
+
const table = await this.#findTable(base.table);
|
|
3796
|
+
const keyColumn = getUniqueKeyColumn(table);
|
|
3797
|
+
if (keyColumn === void 0 || keyColumn.hidden === true || keyColumn.sqlDomain !== void 0) {
|
|
3798
|
+
return void 0;
|
|
3799
|
+
}
|
|
3800
|
+
const publicColumns = plan.select.map((item) => item.alias);
|
|
3801
|
+
if (publicColumns.some((name) => name.startsWith(LIVE_HIDDEN_PREFIX)))
|
|
3802
|
+
return void 0;
|
|
3803
|
+
const fullPlan = clonePlanTree(plan);
|
|
3804
|
+
const qualifiedKey = `${base.alias}.${keyColumn.name}`;
|
|
3805
|
+
fullPlan.select.push({
|
|
3806
|
+
expression: { kind: "column", reference: qualifiedKey },
|
|
3807
|
+
alias: LIVE_KEY_ALIAS
|
|
3808
|
+
});
|
|
3809
|
+
const orderTerms = [];
|
|
3810
|
+
for (const [index, term] of plan.orderBy.entries()) {
|
|
3811
|
+
let alias;
|
|
3812
|
+
if (term.expression.kind === "column" && !term.expression.reference.includes(".")) {
|
|
3813
|
+
const output = term.expression.reference;
|
|
3814
|
+
if (publicColumns.includes(output))
|
|
3815
|
+
alias = output;
|
|
3816
|
+
} else if (term.expression.kind === "literal" && typeof term.expression.value === "number") {
|
|
3817
|
+
const position = term.expression.value;
|
|
3818
|
+
if (!Number.isInteger(position) || position < 1 || position > publicColumns.length) {
|
|
3819
|
+
return void 0;
|
|
3820
|
+
}
|
|
3821
|
+
alias = publicColumns[position - 1];
|
|
3822
|
+
}
|
|
3823
|
+
if (alias === void 0) {
|
|
3824
|
+
alias = `${LIVE_ORDER_ALIAS}${String(index)}`;
|
|
3825
|
+
fullPlan.select.push({ expression: clonePlanTree(term.expression), alias });
|
|
3826
|
+
}
|
|
3827
|
+
orderTerms.push({ alias, descending: term.direction === "desc", nulls: term.nulls });
|
|
3828
|
+
}
|
|
3829
|
+
const deltaPlan = clonePlanTree(fullPlan);
|
|
3830
|
+
deltaPlan.orderBy = [];
|
|
3831
|
+
delete deltaPlan.limit;
|
|
3832
|
+
delete deltaPlan.offset;
|
|
3833
|
+
const margin = plan.limit === void 0 || orderTerms.length === 0 ? 0 : Math.max(LIVE_WINDOW_MARGIN_MIN, Math.min(plan.limit, LIVE_WINDOW_MARGIN_MAX));
|
|
3834
|
+
if (margin > 0)
|
|
3835
|
+
fullPlan.limit = (plan.limit ?? 0) + margin;
|
|
3836
|
+
return {
|
|
3837
|
+
tableId: table.id,
|
|
3838
|
+
keyColumnId: keyColumn.id,
|
|
3839
|
+
qualifiedKey,
|
|
3840
|
+
fullPlan,
|
|
3841
|
+
deltaPlan,
|
|
3842
|
+
publicColumns,
|
|
3843
|
+
columnDomains: [],
|
|
3844
|
+
orderTerms,
|
|
3845
|
+
limit: plan.limit,
|
|
3846
|
+
offset: plan.offset ?? 0,
|
|
3847
|
+
margin,
|
|
3848
|
+
complete: true,
|
|
3849
|
+
rows: [],
|
|
3850
|
+
keys: [],
|
|
3851
|
+
order: []
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
async #liveExecuteMaintainable(compiled, context) {
|
|
3855
|
+
const probe = context?.probe ?? await this.store.getCatalogProbe();
|
|
3856
|
+
const state = await this.#liveMaintenancePlan(compiled, probe);
|
|
3857
|
+
if (state === void 0)
|
|
3858
|
+
return void 0;
|
|
3859
|
+
let executed;
|
|
3860
|
+
try {
|
|
3861
|
+
executed = externalizeQueryResult(await this.#withReadReservation(() => this.#queryCompiled(state.fullPlan, {}, probe)));
|
|
3862
|
+
} catch {
|
|
3863
|
+
return void 0;
|
|
3864
|
+
}
|
|
3865
|
+
const split = splitLiveHiddenColumns(executed, state);
|
|
3866
|
+
return liveMaintainedOutcome({
|
|
3867
|
+
...state,
|
|
3868
|
+
columnDomains: split.result.columnDomains,
|
|
3869
|
+
complete: state.fullPlan.limit === void 0 || split.result.rows.length < state.fullPlan.limit,
|
|
3870
|
+
rows: split.result.rows,
|
|
3871
|
+
keys: split.keys,
|
|
3872
|
+
order: split.order
|
|
3873
|
+
}, void 0);
|
|
3874
|
+
}
|
|
3875
|
+
async #liveMaintain(retained, state, tableIds, after, until, probe) {
|
|
3876
|
+
if (tableIds.length !== 1 || tableIds[0] !== state.tableId)
|
|
3877
|
+
return void 0;
|
|
3878
|
+
const context = await this.#liveProofContext(after, until);
|
|
3879
|
+
const entry = await this.#liveProofTable(context, state.tableId);
|
|
3880
|
+
if (entry === void 0)
|
|
3881
|
+
return void 0;
|
|
3882
|
+
for (const version of context.changedVersions.get(state.tableId) ?? []) {
|
|
3883
|
+
if (!entry.coveredVersions.has(version))
|
|
3884
|
+
return void 0;
|
|
3885
|
+
}
|
|
3886
|
+
const changed = await this.#liveChangedKeys(entry, state.keyColumnId);
|
|
3887
|
+
if (changed === void 0)
|
|
3888
|
+
return void 0;
|
|
3889
|
+
const { changedKeys, existingKeys } = changed;
|
|
3890
|
+
if (changedKeys.size === 0)
|
|
3891
|
+
return { result: retained, state, changed: false };
|
|
3892
|
+
const affected = /* @__PURE__ */ new Set();
|
|
3893
|
+
const retainedTokens = /* @__PURE__ */ new Set();
|
|
3894
|
+
for (const [index, key] of state.keys.entries()) {
|
|
3895
|
+
const token = liveKeyToken(key);
|
|
3896
|
+
retainedTokens.add(token);
|
|
3897
|
+
if (changedKeys.has(token))
|
|
3898
|
+
affected.add(index);
|
|
3899
|
+
}
|
|
3900
|
+
const fullLimit = state.limit === void 0 ? void 0 : state.limit + state.margin;
|
|
3901
|
+
const truncated = !state.complete;
|
|
3902
|
+
if (truncated && affected.size > 0 && state.orderTerms.length === 0)
|
|
3903
|
+
return void 0;
|
|
3904
|
+
if (state.offset > 0) {
|
|
3905
|
+
for (const token of existingKeys)
|
|
3906
|
+
if (!retainedTokens.has(token))
|
|
3907
|
+
return void 0;
|
|
3908
|
+
}
|
|
3909
|
+
const deltaPlan = {
|
|
3910
|
+
...state.deltaPlan,
|
|
3911
|
+
predicates: [
|
|
3912
|
+
...state.deltaPlan.predicates,
|
|
3913
|
+
{
|
|
3914
|
+
left: { kind: "column", reference: state.qualifiedKey },
|
|
3915
|
+
operator: "IN",
|
|
3916
|
+
right: {
|
|
3917
|
+
kind: "list",
|
|
3918
|
+
items: [...changedKeys.values()].map((value) => ({ kind: "literal", value }))
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
]
|
|
3922
|
+
};
|
|
3923
|
+
const delta = splitLiveHiddenColumns(externalizeQueryResult(await this.#withReadReservation(() => this.#queryCompiled(deltaPlan, {}, probe))), state);
|
|
3924
|
+
const compare = liveOrderComparator(state.orderTerms);
|
|
3925
|
+
const added = {
|
|
3926
|
+
rows: delta.result.rows,
|
|
3927
|
+
keys: delta.keys,
|
|
3928
|
+
order: delta.order,
|
|
3929
|
+
previousIndex: new Int32Array(delta.result.rows.length).fill(-1)
|
|
3930
|
+
};
|
|
3931
|
+
let merged;
|
|
3932
|
+
try {
|
|
3933
|
+
const kept = filterLiveRows(state, (index) => !affected.has(index));
|
|
3934
|
+
if (state.offset > 0 && added.rows.length > 0) {
|
|
3935
|
+
if (kept.rows.length === 0 || added.rows.some((_, index) => compare(added.order, index, kept.order, 0) < 0)) {
|
|
3936
|
+
return void 0;
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
merged = mergeLiveRows(kept, added, compare, state.orderTerms.length > 0);
|
|
3940
|
+
} catch {
|
|
3941
|
+
return void 0;
|
|
3942
|
+
}
|
|
3943
|
+
if (fullLimit !== void 0 && merged.rows.length > fullLimit)
|
|
3944
|
+
merged = trimLiveRows(merged, fullLimit);
|
|
3945
|
+
if (truncated && affected.size > 0 && state.limit !== void 0) {
|
|
3946
|
+
const oldEdge = state.rows.length - 1;
|
|
3947
|
+
const visibleEdge = Math.min(merged.rows.length, state.limit) - 1;
|
|
3948
|
+
if (merged.rows.length < state.limit || oldEdge < 0 || visibleEdge < 0 || compare(merged.order, visibleEdge, state.order, oldEdge) > 0) {
|
|
3949
|
+
return void 0;
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
return liveMaintainedOutcome({ ...state, rows: merged.rows, keys: merged.keys, order: merged.order }, retained, merged.previousIndex);
|
|
3953
|
+
}
|
|
3749
3954
|
async #liveChangeCanAffect(query, tableIds, after, until) {
|
|
3750
3955
|
const plan = typeof query === "string" ? this.#compileCached(query) : query.kind === "typed-query" ? query.plan : bindPlanParameters(this.#compileCached(query.sql), query.params);
|
|
3751
3956
|
if (planContainsFts(plan))
|
|
@@ -3847,6 +4052,51 @@ class MinnowDatabase {
|
|
|
3847
4052
|
}
|
|
3848
4053
|
return context;
|
|
3849
4054
|
}
|
|
4055
|
+
#liveChangedKeys(entry, keyColumnId) {
|
|
4056
|
+
const cached = entry.changedKeys.get(keyColumnId);
|
|
4057
|
+
if (cached !== void 0)
|
|
4058
|
+
return cached;
|
|
4059
|
+
const pending = (async () => {
|
|
4060
|
+
const blockIds = [];
|
|
4061
|
+
const existingBlockIds = /* @__PURE__ */ new Set();
|
|
4062
|
+
let deltaRows = 0;
|
|
4063
|
+
for (const segment of entry.windowSegments) {
|
|
4064
|
+
if (segment.kind === "base")
|
|
4065
|
+
continue;
|
|
4066
|
+
const ids = segment.columnBlockIds[keyColumnId];
|
|
4067
|
+
if (ids === void 0)
|
|
4068
|
+
return void 0;
|
|
4069
|
+
deltaRows += segment.rowCount;
|
|
4070
|
+
if (deltaRows > LIVE_MAINTENANCE_MAX_DELTA_ROWS)
|
|
4071
|
+
return void 0;
|
|
4072
|
+
blockIds.push(...ids);
|
|
4073
|
+
if (segment.kind !== "insert")
|
|
4074
|
+
for (const id of ids)
|
|
4075
|
+
existingBlockIds.add(id);
|
|
4076
|
+
}
|
|
4077
|
+
const changedKeys = /* @__PURE__ */ new Map();
|
|
4078
|
+
const existingKeys = /* @__PURE__ */ new Set();
|
|
4079
|
+
if (blockIds.length > 0) {
|
|
4080
|
+
const decoded = await this.#loadDecodedBlocks([...new Set(blockIds)]);
|
|
4081
|
+
for (const blockId of blockIds) {
|
|
4082
|
+
const column = decoded.get(blockId);
|
|
4083
|
+
if (column === void 0)
|
|
4084
|
+
return void 0;
|
|
4085
|
+
for (const value of column.values) {
|
|
4086
|
+
if (value === null)
|
|
4087
|
+
continue;
|
|
4088
|
+
const token = liveKeyToken(value);
|
|
4089
|
+
changedKeys.set(token, value);
|
|
4090
|
+
if (existingBlockIds.has(blockId))
|
|
4091
|
+
existingKeys.add(token);
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
return { changedKeys, existingKeys };
|
|
4096
|
+
})();
|
|
4097
|
+
entry.changedKeys.set(keyColumnId, pending);
|
|
4098
|
+
return pending;
|
|
4099
|
+
}
|
|
3850
4100
|
#liveProofTable(context, tableId) {
|
|
3851
4101
|
const cached = context.tables.get(tableId);
|
|
3852
4102
|
if (cached !== void 0)
|
|
@@ -3867,7 +4117,7 @@ class MinnowDatabase {
|
|
|
3867
4117
|
coveredVersions.add(committed);
|
|
3868
4118
|
windowSegments.push(segment);
|
|
3869
4119
|
}
|
|
3870
|
-
return { table, windowSegments, coveredVersions };
|
|
4120
|
+
return { table, windowSegments, coveredVersions, changedKeys: /* @__PURE__ */ new Map() };
|
|
3871
4121
|
})();
|
|
3872
4122
|
context.tables.set(tableId, entry);
|
|
3873
4123
|
return entry;
|
|
@@ -4880,6 +5130,11 @@ class MinnowDatabase {
|
|
|
4880
5130
|
}
|
|
4881
5131
|
async #sessionInsert(transaction, tableName, input, kind, options, cascadeBudget = 1) {
|
|
4882
5132
|
const table = await this.#findTable(tableName);
|
|
5133
|
+
const sessionUpsertKeyColumn = kind === "upsert" ? getUniqueKeyColumn(table) : void 0;
|
|
5134
|
+
const normalizedConflictWhere = kind === "upsert" && options?.conflictWhere !== void 0 ? normalizeUpsertConflictWhere(table, options.conflictWhere) : void 0;
|
|
5135
|
+
if (normalizedConflictWhere !== void 0 && sessionUpsertKeyColumn === void 0) {
|
|
5136
|
+
throw new TypeError(`Table needs a unique key before it can be upserted: ${table.name}`);
|
|
5137
|
+
}
|
|
4883
5138
|
const filled = await this.#fillDefaults(table, input);
|
|
4884
5139
|
const { generated, autoIncrement } = filled;
|
|
4885
5140
|
let batch = filled.batch;
|
|
@@ -4892,11 +5147,6 @@ class MinnowDatabase {
|
|
|
4892
5147
|
validateValue(autoIncrement.column, patched[rowIndex] ?? null, rowIndex);
|
|
4893
5148
|
}
|
|
4894
5149
|
}
|
|
4895
|
-
const sessionUpsertKeyColumn = kind === "upsert" ? getUniqueKeyColumn(table) : void 0;
|
|
4896
|
-
const normalizedConflictWhere = kind === "upsert" && options?.conflictWhere !== void 0 ? normalizeUpsertConflictWhere(table, options.conflictWhere) : void 0;
|
|
4897
|
-
if (normalizedConflictWhere !== void 0 && sessionUpsertKeyColumn === void 0) {
|
|
4898
|
-
throw new TypeError(`Table needs a unique key before it can be upserted: ${table.name}`);
|
|
4899
|
-
}
|
|
4900
5150
|
let sessionUpsertFirings = sessionUpsertKeyColumn === void 0 ? void 0 : await this.#upsertTriggerFirings(table, sessionUpsertKeyColumn, batch, rowCount, (sql, params) => this.#sessionQuery(transaction, sql, { params }), normalizedConflictWhere);
|
|
4901
5151
|
let skippedRowCount = 0;
|
|
4902
5152
|
if (normalizedConflictWhere !== void 0) {
|
|
@@ -4912,6 +5162,7 @@ class MinnowDatabase {
|
|
|
4912
5162
|
rowCount = filtered.rowCount;
|
|
4913
5163
|
sessionUpsertFirings = filtered.firings;
|
|
4914
5164
|
if (rowCount === 0) {
|
|
5165
|
+
collectAutoIncrementGenerated(batch, generated, autoIncrement);
|
|
4915
5166
|
return {
|
|
4916
5167
|
tableName: table.name,
|
|
4917
5168
|
segmentId: null,
|
|
@@ -5178,7 +5429,10 @@ class MinnowDatabase {
|
|
|
5178
5429
|
}
|
|
5179
5430
|
return (await this.#memoizedQuery(query.plan, `typed ${planMemoKey(query.plan)}`, {}, probe)).rows;
|
|
5180
5431
|
}
|
|
5181
|
-
async migrate(definition, options = {}) {
|
|
5432
|
+
async migrate(definition = this.#schema, options = {}) {
|
|
5433
|
+
if (definition === void 0) {
|
|
5434
|
+
throw new TypeError("migrate() needs a schema: pass a definition, or construct the database with { schema }");
|
|
5435
|
+
}
|
|
5182
5436
|
let originalSteps;
|
|
5183
5437
|
for (let attempt = 0; ; attempt += 1) {
|
|
5184
5438
|
try {
|
|
@@ -6079,7 +6333,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6079
6333
|
let outcome;
|
|
6080
6334
|
let version;
|
|
6081
6335
|
if (options.writer === void 0) {
|
|
6082
|
-
const completed = await this.write(apply);
|
|
6336
|
+
const completed = await this.#erased.write(apply);
|
|
6083
6337
|
outcome = completed.result;
|
|
6084
6338
|
version = completed.version;
|
|
6085
6339
|
} else {
|
|
@@ -6184,7 +6438,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6184
6438
|
}
|
|
6185
6439
|
const sourceKey = mergeSourceKeyExpression(statement, keyColumn.name);
|
|
6186
6440
|
const sourceSql = mergeSourceSql(statement);
|
|
6187
|
-
const { result: applied, version } = await this.write(async (session) => {
|
|
6441
|
+
const { result: applied, version } = await this.#erased.write(async (session) => {
|
|
6188
6442
|
const sourceRows = (await session.query(sourceSql)).rows;
|
|
6189
6443
|
const keys = sourceRows.map((row) => storedSqlValueFromExecution(keyColumn, evaluateJoinedRowExpression(sourceKey, { [statement.source.alias]: row })));
|
|
6190
6444
|
const present = /* @__PURE__ */ new Map();
|
|
@@ -6765,7 +7019,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6765
7019
|
}
|
|
6766
7020
|
await this.#assertStagedInsertKeysFree(table2, stagedKeyColumn, input, writer);
|
|
6767
7021
|
}
|
|
6768
|
-
const result = await (viaUpsert ? writer?.upsertBatch(statement.table, input) ?? this.upsertBatch(statement.table, input) : writer?.insertBatch(statement.table, input) ?? this.insertBatch(statement.table, input));
|
|
7022
|
+
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));
|
|
6769
7023
|
const generated = "generatedColumns" in result ? result.generatedColumns ?? {} : {};
|
|
6770
7024
|
return {
|
|
6771
7025
|
kind: "insert",
|
|
@@ -6897,7 +7151,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6897
7151
|
...returnedRows2 === void 0 || returningColumns === void 0 ? {} : returningExecuteFields(table, returningColumns, returnedRows2)
|
|
6898
7152
|
};
|
|
6899
7153
|
}
|
|
6900
|
-
const deleted = options.writer === void 0 ? await this.deleteBatch(table.name, { keys }) : await options.writer.deleteBatch(table.name, { keys });
|
|
7154
|
+
const deleted = options.writer === void 0 ? await this.#erased.deleteBatch(table.name, { keys }) : await options.writer.deleteBatch(table.name, { keys });
|
|
6901
7155
|
return {
|
|
6902
7156
|
kind: "delete",
|
|
6903
7157
|
table: table.name,
|
|
@@ -6934,7 +7188,7 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
|
|
|
6934
7188
|
returnedChanges[assignment.column] = executionValues;
|
|
6935
7189
|
}
|
|
6936
7190
|
}
|
|
6937
|
-
const updated = options.writer === void 0 ? await this.updateBatch(table.name, { keys, changes }) : await options.writer.updateBatch(table.name, { keys, changes });
|
|
7191
|
+
const updated = options.writer === void 0 ? await this.#erased.updateBatch(table.name, { keys, changes }) : await options.writer.updateBatch(table.name, { keys, changes });
|
|
6938
7192
|
const returnedRows = returningColumns === void 0 ? void 0 : rows.map((row, index) => Object.fromEntries(returningColumns.map((name) => [
|
|
6939
7193
|
name,
|
|
6940
7194
|
returnedChanges !== void 0 && name in returnedChanges ? returnedChanges[name]?.[index] ?? null : updated.generatedColumns?.[name] !== void 0 ? updated.generatedColumns[name][index] ?? null : row[name] ?? null
|
|
@@ -14177,6 +14431,203 @@ function searchableFtsColumns(table) {
|
|
|
14177
14431
|
return void 0;
|
|
14178
14432
|
return visibleTableColumns(table).filter((column) => column.type !== "boolean").map((column) => column.name);
|
|
14179
14433
|
}
|
|
14434
|
+
const LIVE_HIDDEN_PREFIX = "__minnow_live_";
|
|
14435
|
+
const LIVE_KEY_ALIAS = `${LIVE_HIDDEN_PREFIX}key`;
|
|
14436
|
+
const LIVE_ORDER_ALIAS = `${LIVE_HIDDEN_PREFIX}order_`;
|
|
14437
|
+
const LIVE_MAINTENANCE_MAX_DELTA_ROWS = 2048;
|
|
14438
|
+
const LIVE_WINDOW_MARGIN_MIN = 16;
|
|
14439
|
+
const LIVE_WINDOW_MARGIN_MAX = 64;
|
|
14440
|
+
function sameLiveRow(left, right, columns) {
|
|
14441
|
+
for (const column of columns) {
|
|
14442
|
+
const a = left[column] ?? null;
|
|
14443
|
+
const b = right[column] ?? null;
|
|
14444
|
+
if (a instanceof Date || b instanceof Date) {
|
|
14445
|
+
if (!(a instanceof Date && b instanceof Date) || !Object.is(dateMilliseconds(a), dateMilliseconds(b))) {
|
|
14446
|
+
return false;
|
|
14447
|
+
}
|
|
14448
|
+
} else if (!Object.is(a, b))
|
|
14449
|
+
return false;
|
|
14450
|
+
}
|
|
14451
|
+
return true;
|
|
14452
|
+
}
|
|
14453
|
+
function liveKeyToken(value) {
|
|
14454
|
+
if (typeof value === "number")
|
|
14455
|
+
return `n:${String(value)}`;
|
|
14456
|
+
if (typeof value === "string")
|
|
14457
|
+
return `s:${value}`;
|
|
14458
|
+
if (typeof value === "boolean")
|
|
14459
|
+
return value ? "b:1" : "b:0";
|
|
14460
|
+
if (value instanceof Date)
|
|
14461
|
+
return `d:${String(dateMilliseconds(value))}`;
|
|
14462
|
+
return "z";
|
|
14463
|
+
}
|
|
14464
|
+
function splitLiveHiddenColumns(executed, state) {
|
|
14465
|
+
const publicCount = state.publicColumns.length;
|
|
14466
|
+
const hidden = executed.columns.slice(publicCount);
|
|
14467
|
+
const count = executed.rows.length;
|
|
14468
|
+
const keys = new Array(count);
|
|
14469
|
+
const order = state.orderTerms.map(() => new Array(count));
|
|
14470
|
+
for (let index = 0; index < count; index += 1) {
|
|
14471
|
+
const row = executed.rows[index] ?? {};
|
|
14472
|
+
keys[index] = row[LIVE_KEY_ALIAS] ?? null;
|
|
14473
|
+
for (const [term, { alias }] of state.orderTerms.entries()) {
|
|
14474
|
+
const values = order[term];
|
|
14475
|
+
if (values !== void 0)
|
|
14476
|
+
values[index] = row[alias] ?? null;
|
|
14477
|
+
}
|
|
14478
|
+
for (const column of hidden)
|
|
14479
|
+
Reflect.deleteProperty(row, column);
|
|
14480
|
+
}
|
|
14481
|
+
return {
|
|
14482
|
+
result: {
|
|
14483
|
+
columns: executed.columns.slice(0, publicCount),
|
|
14484
|
+
columnDomains: executed.columnDomains.slice(0, publicCount),
|
|
14485
|
+
rows: executed.rows
|
|
14486
|
+
},
|
|
14487
|
+
keys,
|
|
14488
|
+
order
|
|
14489
|
+
};
|
|
14490
|
+
}
|
|
14491
|
+
function liveMaintainedOutcome(state, previous, previousIndex) {
|
|
14492
|
+
const visibleCount = state.limit === void 0 ? state.rows.length : Math.min(state.rows.length, state.limit);
|
|
14493
|
+
const visible = state.rows.slice(0, visibleCount);
|
|
14494
|
+
if (previous === void 0) {
|
|
14495
|
+
return {
|
|
14496
|
+
result: {
|
|
14497
|
+
columns: [...state.publicColumns],
|
|
14498
|
+
columnDomains: [...state.columnDomains],
|
|
14499
|
+
rows: visible
|
|
14500
|
+
},
|
|
14501
|
+
state,
|
|
14502
|
+
changed: true
|
|
14503
|
+
};
|
|
14504
|
+
}
|
|
14505
|
+
const previousCount = previous.rows.length;
|
|
14506
|
+
const retained = new Int32Array(visibleCount);
|
|
14507
|
+
let same = visibleCount === previousCount;
|
|
14508
|
+
let kept = 0;
|
|
14509
|
+
for (let index = 0; index < visibleCount; index += 1) {
|
|
14510
|
+
const was = previousIndex?.[index] ?? -1;
|
|
14511
|
+
if (was >= 0 && was < previousCount) {
|
|
14512
|
+
retained[index] = was;
|
|
14513
|
+
kept += 1;
|
|
14514
|
+
if (was !== index)
|
|
14515
|
+
same = false;
|
|
14516
|
+
continue;
|
|
14517
|
+
}
|
|
14518
|
+
retained[index] = -1;
|
|
14519
|
+
const before = previous.rows[index];
|
|
14520
|
+
const now = visible[index];
|
|
14521
|
+
if (before !== void 0 && now !== void 0 && sameLiveRow(before, now, previous.columns)) {
|
|
14522
|
+
visible[index] = before;
|
|
14523
|
+
retained[index] = index;
|
|
14524
|
+
kept += 1;
|
|
14525
|
+
} else
|
|
14526
|
+
same = false;
|
|
14527
|
+
}
|
|
14528
|
+
if (same)
|
|
14529
|
+
return { result: previous, state, changed: false };
|
|
14530
|
+
return {
|
|
14531
|
+
result: {
|
|
14532
|
+
columns: [...state.publicColumns],
|
|
14533
|
+
columnDomains: [...state.columnDomains],
|
|
14534
|
+
rows: visible
|
|
14535
|
+
},
|
|
14536
|
+
state,
|
|
14537
|
+
changed: true,
|
|
14538
|
+
...kept > 0 ? { retained } : {}
|
|
14539
|
+
};
|
|
14540
|
+
}
|
|
14541
|
+
function filterLiveRows(state, keep) {
|
|
14542
|
+
const rows = [];
|
|
14543
|
+
const keys = [];
|
|
14544
|
+
const order = state.order.map(() => new Array());
|
|
14545
|
+
const previous = [];
|
|
14546
|
+
for (let index = 0; index < state.rows.length; index += 1) {
|
|
14547
|
+
const row = state.rows[index];
|
|
14548
|
+
if (row === void 0 || !keep(index))
|
|
14549
|
+
continue;
|
|
14550
|
+
rows.push(row);
|
|
14551
|
+
keys.push(state.keys[index] ?? null);
|
|
14552
|
+
for (const [term, values] of state.order.entries()) {
|
|
14553
|
+
order[term]?.push(values[index] ?? null);
|
|
14554
|
+
}
|
|
14555
|
+
previous.push(index);
|
|
14556
|
+
}
|
|
14557
|
+
return { rows, keys, order, previousIndex: Int32Array.from(previous) };
|
|
14558
|
+
}
|
|
14559
|
+
function trimLiveRows(rows, count) {
|
|
14560
|
+
return {
|
|
14561
|
+
rows: rows.rows.slice(0, count),
|
|
14562
|
+
keys: rows.keys.slice(0, count),
|
|
14563
|
+
order: rows.order.map((values) => values.slice(0, count)),
|
|
14564
|
+
previousIndex: rows.previousIndex.slice(0, count)
|
|
14565
|
+
};
|
|
14566
|
+
}
|
|
14567
|
+
function liveOrderComparator(terms) {
|
|
14568
|
+
return (leftOrder, leftIndex, rightOrder, rightIndex) => {
|
|
14569
|
+
for (const [term, { descending, nulls }] of terms.entries()) {
|
|
14570
|
+
const a = leftOrder[term]?.[leftIndex] ?? null;
|
|
14571
|
+
const b = rightOrder[term]?.[rightIndex] ?? null;
|
|
14572
|
+
if (a === null || b === null) {
|
|
14573
|
+
if (a === null && b === null)
|
|
14574
|
+
continue;
|
|
14575
|
+
const nullsFirst = nulls === "first" || nulls === void 0 && descending;
|
|
14576
|
+
return a === null ? nullsFirst ? -1 : 1 : nullsFirst ? 1 : -1;
|
|
14577
|
+
}
|
|
14578
|
+
let comparison = compareSqlValues(a, b);
|
|
14579
|
+
if (descending)
|
|
14580
|
+
comparison = -comparison;
|
|
14581
|
+
if (comparison !== 0)
|
|
14582
|
+
return comparison;
|
|
14583
|
+
}
|
|
14584
|
+
return 0;
|
|
14585
|
+
};
|
|
14586
|
+
}
|
|
14587
|
+
function mergeLiveRows(kept, added, compare, ordered) {
|
|
14588
|
+
if (added.rows.length === 0)
|
|
14589
|
+
return kept;
|
|
14590
|
+
const terms = kept.order.length;
|
|
14591
|
+
const total = kept.rows.length + added.rows.length;
|
|
14592
|
+
const rows = new Array(total);
|
|
14593
|
+
const keys = new Array(total);
|
|
14594
|
+
const order = Array.from({ length: terms }, () => new Array(total));
|
|
14595
|
+
const previousIndex = new Int32Array(total);
|
|
14596
|
+
const take = (source, from, to) => {
|
|
14597
|
+
rows[to] = source.rows[from] ?? {};
|
|
14598
|
+
keys[to] = source.keys[from] ?? null;
|
|
14599
|
+
for (let term = 0; term < terms; term += 1) {
|
|
14600
|
+
const values = order[term];
|
|
14601
|
+
if (values !== void 0)
|
|
14602
|
+
values[to] = source.order[term]?.[from] ?? null;
|
|
14603
|
+
}
|
|
14604
|
+
previousIndex[to] = source.previousIndex[from] ?? -1;
|
|
14605
|
+
};
|
|
14606
|
+
if (!ordered) {
|
|
14607
|
+
for (let index = 0; index < kept.rows.length; index += 1)
|
|
14608
|
+
take(kept, index, index);
|
|
14609
|
+
for (let index = 0; index < added.rows.length; index += 1) {
|
|
14610
|
+
take(added, index, kept.rows.length + index);
|
|
14611
|
+
}
|
|
14612
|
+
return { rows, keys, order, previousIndex };
|
|
14613
|
+
}
|
|
14614
|
+
const addedIndexes = added.rows.map((_, index) => index);
|
|
14615
|
+
addedIndexes.sort((left, right) => compare(added.order, left, added.order, right));
|
|
14616
|
+
let keptIndex = 0;
|
|
14617
|
+
let addedPosition = 0;
|
|
14618
|
+
for (let to = 0; to < total; to += 1) {
|
|
14619
|
+
const addedIndex = addedIndexes[addedPosition];
|
|
14620
|
+
const takeAdded = addedIndex !== void 0 && (keptIndex >= kept.rows.length || compare(added.order, addedIndex, kept.order, keptIndex) < 0);
|
|
14621
|
+
if (takeAdded) {
|
|
14622
|
+
take(added, addedIndex, to);
|
|
14623
|
+
addedPosition += 1;
|
|
14624
|
+
} else {
|
|
14625
|
+
take(kept, keptIndex, to);
|
|
14626
|
+
keptIndex += 1;
|
|
14627
|
+
}
|
|
14628
|
+
}
|
|
14629
|
+
return { rows, keys, order, previousIndex };
|
|
14630
|
+
}
|
|
14180
14631
|
function getUniqueKeyColumn(table) {
|
|
14181
14632
|
if (table.uniqueKeyColumnId === void 0)
|
|
14182
14633
|
return void 0;
|