@minnowdb/core 0.7.8 → 0.8.0

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.
@@ -1,3 +1,6 @@
1
+ import { LiveAggregate } from "./live-aggregate.js";
2
+ import { QueryGenerations } from "./query-generations.js";
3
+ import { applyWindowFunctionsAsync } from "./windows.js";
1
4
  import { crossJoinPlan } from "../plan/model.js";
2
5
  import { definedVectors, toColumnarBatch } from "./batch.js";
3
6
  import { ArtifactCache } from "./artifact-cache.js";
@@ -16,12 +19,12 @@ import { cachedQueryTerms, FTS_TOKENIZER_VERSION, renderDocumentValue, tokenize
16
19
  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
20
  import { decodeSnapshotFrameStream, encodeSnapshotFrameStreamFooter, encodeSnapshotFrameStreamHeader, extendSnapshotFrameStreamChecksum, snapshotFrameEnvelopeParts, snapshotFrameStreamHeaderIdentity } from "../storage/snapshot.js";
18
21
  import { Snapshot, TransactionManager } from "../transactions/index.js";
19
- import { applyWindowFunctions, bindPendingSelectShapes, bindPlanParameters, bindStatementParameters, DUAL_TABLE, dualTableRows, blockHasSubqueries, combineUnionResults, compileCheckExpression, compileQuery, hasAggregate, createRecursiveCteState, annotateAvgArgumentScales, compileStatement, comparisonHolds, createPreparedColumnarQuery, evaluateJoinedRowExpression, evaluateRowExpression, externalizeQueryResult, expressionColumnNames, inferBlockSchema, inferResultColumnDomains, isDeferredInsertExpression, isDefaultInsertValue, referencedColumns, childExpressions, expandFtsColumns, expandNaturalJoins, expandSourceColumnAliases, expandViewSources, forEachBlockExpression, forEachNestedBlock, planContainsFts, planHasNaturalJoins, planHasSourceColumnAliases, planReadsTable, planReadsViews, planReadsBeyondSingleScan, projectResultColumns, queryResultNeedsExternalization, resolveStatementDatetimes, subqueryResolutionSteps, topLevelFtsMatchConjuncts, transparentProjectionSource, unknownColumnDomains, validateDefaultExpression, windowOutputDomain, windowOutputType, volatileScalarFunctionNames, annotatePlanIntegerDivision, clonePlanTree, planMayHaveIntegerDivision, foldIdentifierCase, extendGroupByWithKeyDependents, expandRowReferences } from "./query.js";
22
+ import { 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, 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";
20
23
  import { copyQueryResult, planMemoKey, queryResultMemoKey, queryResultRetainedBytes, RESULT_MEMO_MAX_BYTES } from "./query-cache.js";
21
24
  import { QueryMemoryBudgetError, QueryMemoryContext, DEFAULT_QUERY_MEMORY_BUDGET_BYTES } from "./memory.js";
22
25
  import { LiveQueryLimitError, LiveQuerySet, MAX_LIVE_QUERY_SETS_PER_DATABASE } from "./live.js";
23
26
  import { chooseJoinOrder, optimizePlan, qualifyCorrelatedReferences, renderPlan } from "./optimizer.js";
24
- import { encodeSqlEqualityValue, readUntypedText } from "./sql-semantics.js";
27
+ import { encodeSqlEqualityValue, readUntypedText, compareSqlValues } from "./sql-semantics.js";
25
28
  import { exactNumericAsNumber, exactNumericValue, externalSqlDomainValue, externalSqlTextValue, isDateDomainValue, isExactNumeric, isSqlDomainValue, normalizeSqlDomainValue, protectedSqlTextValue } from "./sql-domains.js";
26
29
  import { toCatalog } from "./catalog.js";
27
30
  import { applyColumnSteps, assertColumnDroppable, compileGeneratedColumnExpression, declaredForeignKeys, isDestructiveStep, planMigration } from "./schema.js";
@@ -576,6 +579,7 @@ class MinnowDatabase {
576
579
  #liveSets = /* @__PURE__ */ new Set();
577
580
  #liveProofContexts = /* @__PURE__ */ new Map();
578
581
  #artifactCache;
582
+ #queryGenerations;
579
583
  #queryExecutionMemoryBudgetBytes;
580
584
  #ftsAutoIndexRows;
581
585
  #autoCompact;
@@ -657,6 +661,7 @@ class MinnowDatabase {
657
661
  if (!Number.isSafeInteger(this.#transactionOwnerLeaseMs) || this.#transactionOwnerLeaseMs <= 0) {
658
662
  throw new RangeError("Transaction owner lease lifetime must be a positive whole number");
659
663
  }
664
+ this.#queryGenerations = new QueryGenerations((after, limit) => this.store.listManifestPage(after, limit));
660
665
  this.#artifactCache = new ArtifactCache(options.bufferPoolBytes ?? 64 * 1024 * 1024);
661
666
  const queryBudget = options.executionMemoryBudgetBytes;
662
667
  this.#queryExecutionMemoryBudgetBytes = queryBudget === null ? Number.MAX_SAFE_INTEGER : positiveWholeNumber(queryBudget ?? DEFAULT_QUERY_MEMORY_BUDGET_BYTES, "Query execution memory budget");
@@ -1124,11 +1129,11 @@ class MinnowDatabase {
1124
1129
  this.#activeReadReservations -= 1;
1125
1130
  }
1126
1131
  }
1127
- async #queryWithReadReservation(sql, options) {
1132
+ async #queryWithReadReservation(sql, options, probe, storeMemo = true) {
1128
1133
  throwIfAborted(options.signal);
1129
1134
  await this.#settleExpiredStatementTransaction();
1130
1135
  throwIfAborted(options.signal);
1131
- return this.#openTransaction === void 0 ? this.#withReadReservation(() => this.#queryUnreserved(sql, options)) : this.#queryUnreserved(sql, options);
1136
+ return this.#openTransaction === void 0 ? this.#withReadReservation(() => this.#queryUnreserved(sql, options, probe, storeMemo)) : this.#queryUnreserved(sql, options, probe, storeMemo);
1132
1137
  }
1133
1138
  async #assistAutomaticCollection() {
1134
1139
  if (!this.#autoCollect && !this.#manualCollectionDebtInitialized) {
@@ -2748,6 +2753,12 @@ class MinnowDatabase {
2748
2753
  const expected = schema2?.[index];
2749
2754
  if (expected === void 0)
2750
2755
  return;
2756
+ if (column.unknown === true)
2757
+ return;
2758
+ if (expected.unknown === true) {
2759
+ schema2 = schema2?.map((entry, position) => position === index ? { ...column, name: entry.name } : entry);
2760
+ return;
2761
+ }
2751
2762
  if (column.type === expected.type && column.sqlDomain?.kind === "numeric" && expected.sqlDomain?.kind === "numeric") {
2752
2763
  if (JSON.stringify(column.sqlDomain) !== JSON.stringify(expected.sqlDomain)) {
2753
2764
  schema2 = schema2?.map((entry, position) => position === index ? { ...entry, sqlDomain: { kind: "numeric" } } : entry);
@@ -2769,7 +2780,7 @@ class MinnowDatabase {
2769
2780
  if (source.windowed !== void 0) {
2770
2781
  const innerKey = await this.#blockResultCacheKey(source.windowed.block, snapshot, visibility, realTables, cacheResults);
2771
2782
  throwIfAborted(signal);
2772
- const columnarKey2 = innerKey === void 0 ? void 0 : `ctw|${JSON.stringify(source.windowed.windows)}|${innerKey}`;
2783
+ const columnarKey2 = innerKey === void 0 ? void 0 : `ctw|${planMemoKey(source.windowed.windows)}|${innerKey}`;
2773
2784
  if (columnarKey2 !== void 0) {
2774
2785
  const hit = this.#cacheGet(columnarKey2);
2775
2786
  if (hit !== void 0) {
@@ -2779,8 +2790,10 @@ class MinnowDatabase {
2779
2790
  }
2780
2791
  }
2781
2792
  const { result: inner, schema: innerSchema } = await this.#executeBlockWithSchemaCached(source.windowed.block, snapshot, visibility, memory, realTables, typedSchemas, cacheResults, allowSpill, forceSpill, spillPageRows, signal);
2782
- const windowed = applyWindowFunctions(inner, source.windowed.windows, {
2783
- copyRows: cacheResults
2793
+ const windowed = await applyWindowFunctionsAsync(inner, source.windowed.windows, {
2794
+ copyRows: cacheResults,
2795
+ memoryContext: memory,
2796
+ ...signal === void 0 ? {} : { signal }
2784
2797
  });
2785
2798
  const schema2 = [
2786
2799
  ...innerSchema,
@@ -2857,7 +2870,7 @@ class MinnowDatabase {
2857
2870
  async query(sql, options = {}) {
2858
2871
  return this.#queryWithReadReservation(sql, options);
2859
2872
  }
2860
- async #queryUnreserved(sql, options) {
2873
+ async #queryUnreserved(sql, options, probe, storeMemo = true) {
2861
2874
  throwIfAborted(options.signal);
2862
2875
  const open = this.#openTransaction;
2863
2876
  if (open !== void 0) {
@@ -2883,9 +2896,9 @@ class MinnowDatabase {
2883
2896
  }
2884
2897
  }
2885
2898
  const plan = bindPlanParameters(compiled, options.params);
2886
- const probe = this.store.getCatalogProbe.bind(this.store);
2899
+ const readProbe = this.store.getCatalogProbe.bind(this.store);
2887
2900
  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;
2888
- const result = !memoizable ? await this.#queryCompiled(plan, options) : await this.#memoizedQuery(plan, `res ${queryResultMemoKey(sql, options.params ?? [])}`, options, probe);
2901
+ const result = !memoizable ? await this.#queryCompiled(plan, options, probe) : await this.#memoizedQuery(plan, `res ${queryResultMemoKey(sql, options.params ?? [])}`, options, readProbe, probe, storeMemo);
2889
2902
  throwIfAborted(options.signal);
2890
2903
  return externalizeQueryResult(result);
2891
2904
  }
@@ -2974,23 +2987,34 @@ class MinnowDatabase {
2974
2987
  });
2975
2988
  }
2976
2989
  }
2977
- async #memoizedQuery(plan, key, options, probe) {
2990
+ async #memoizedQuery(plan, key, options, probe, before, store = true) {
2978
2991
  throwIfAborted(options.signal);
2979
- const before = await probe();
2992
+ before ??= await probe();
2980
2993
  throwIfAborted(options.signal);
2981
- const cached = this.#cacheGet(`${key}${String(before.catalogEpoch)}`);
2994
+ const dependencyKey = `deps ${String(before.schemaEpoch)} ${key}`;
2995
+ let dependencies = this.#cacheGet(dependencyKey);
2996
+ if (dependencies === void 0) {
2997
+ const rewritten = await this.#applyCatalogRewrites(plan, before);
2998
+ dependencies = [...(await this.#findRealBlockTables(rewritten)).values()].map((table) => table.id).sort();
2999
+ this.#cachePut(dependencyKey, dependencies, 64 + dependencies.reduce((bytes2, id) => bytes2 + id.length * 2 + 16, 0));
3000
+ }
3001
+ const generation = await this.#queryGenerations.key(dependencies, before.manifestVersion);
3002
+ const resultKey = `${key}${String(before.schemaEpoch)}${generation}`;
3003
+ const cached = this.#cacheGet(resultKey);
2982
3004
  if (cached !== void 0) {
2983
3005
  options.onStats?.({ peakMemoryBytes: 0 });
2984
3006
  return copyQueryResult(cached);
2985
3007
  }
2986
3008
  const result = await this.#queryCompiled(plan, options, before);
2987
3009
  throwIfAborted(options.signal);
3010
+ if (!store)
3011
+ return result;
2988
3012
  const bytes = queryResultRetainedBytes(result);
2989
3013
  if (bytes <= RESULT_MEMO_MAX_BYTES) {
2990
3014
  const after = await probe();
2991
3015
  throwIfAborted(options.signal);
2992
3016
  if (after.catalogEpoch === before.catalogEpoch) {
2993
- this.#cachePut(`${key}${String(before.catalogEpoch)}`, copyQueryResult(result), bytes);
3017
+ this.#cachePut(resultKey, copyQueryResult(result), bytes);
2994
3018
  }
2995
3019
  }
2996
3020
  return result;
@@ -3081,7 +3105,34 @@ class MinnowDatabase {
3081
3105
  if (plan.base.table !== DUAL_TABLE || plan.joins.length > 0) {
3082
3106
  throw new TypeError("NEXTVAL and CURRVAL currently require a SELECT without FROM");
3083
3107
  }
3084
- const resolved = structuredClone(plan);
3108
+ const resolved = resolveStatementDatetimes(structuredClone(plan), this.#now());
3109
+ const evaluate = (expression) => evaluateRowExpression(expression, DUAL_TABLE, {});
3110
+ const literal = (value) => ({
3111
+ kind: "literal",
3112
+ value,
3113
+ internalSqlValue: true
3114
+ });
3115
+ const omitted = resolved.limit === 0;
3116
+ const matches = !omitted && resolved.predicates.every((predicate) => evaluate({ kind: "condition", ...predicate }) === true);
3117
+ if (!omitted)
3118
+ resolved.predicates = matches ? [] : [
3119
+ {
3120
+ left: literal(false),
3121
+ operator: "=",
3122
+ right: literal(true)
3123
+ }
3124
+ ];
3125
+ const discarded = !matches;
3126
+ if (discarded) {
3127
+ const omit = (expression) => {
3128
+ if (expression.kind === "call" && sequenceFunctionNames.has(expression.name))
3129
+ return literal(0);
3130
+ return mapChildExpressions(expression, omit);
3131
+ };
3132
+ for (const item of resolved.select)
3133
+ item.expression = omit(item.expression);
3134
+ return resolved;
3135
+ }
3085
3136
  const rewrite = async (expression) => {
3086
3137
  if (expression.kind === "call" && (expression.name === "NEXTVAL" || expression.name === "CURRVAL")) {
3087
3138
  const argument = expression.arguments[0];
@@ -3105,6 +3156,21 @@ class MinnowDatabase {
3105
3156
  this.#sequenceCurrValues.set(name, value);
3106
3157
  return { kind: "literal", value };
3107
3158
  }
3159
+ if (expression.kind === "case") {
3160
+ for (const branch of expression.branches) {
3161
+ if (evaluate(await rewrite(branch.when)) === true)
3162
+ return rewrite(branch.then);
3163
+ }
3164
+ return expression.otherwise === void 0 ? literal(null) : rewrite(expression.otherwise);
3165
+ }
3166
+ if (expression.kind === "call" && expression.name === "COALESCE") {
3167
+ for (const argument of expression.arguments) {
3168
+ const value = evaluate(await rewrite(argument));
3169
+ if (value !== null)
3170
+ return literal(value);
3171
+ }
3172
+ return literal(null);
3173
+ }
3108
3174
  if (expression.kind === "binary" || expression.kind === "condition" || expression.kind === "logical") {
3109
3175
  expression.left = await rewrite(expression.left);
3110
3176
  expression.right = await rewrite(expression.right);
@@ -3122,13 +3188,6 @@ class MinnowDatabase {
3122
3188
  }
3123
3189
  } else if (expression.kind === "not") {
3124
3190
  expression.operand = await rewrite(expression.operand);
3125
- } else if (expression.kind === "case") {
3126
- for (const branch of expression.branches) {
3127
- branch.when = await rewrite(branch.when);
3128
- branch.then = await rewrite(branch.then);
3129
- }
3130
- if (expression.otherwise !== void 0)
3131
- expression.otherwise = await rewrite(expression.otherwise);
3132
3191
  }
3133
3192
  return expression;
3134
3193
  };
@@ -3729,19 +3788,15 @@ class MinnowDatabase {
3729
3788
  return this.store.getCatalogProbe();
3730
3789
  },
3731
3790
  manifestPage: (afterVersion, limit) => this.store.listManifestPage(afterVersion, limit),
3732
- dependencyTableIds: async (query) => {
3791
+ dependencyTableIds: async (query, probe) => {
3733
3792
  const compiled = compileLiveQuery(query);
3734
- const plan = await this.#applyCatalogRewrites(compiled);
3793
+ const plan = await this.#applyCatalogRewrites(compiled, probe);
3735
3794
  const tables = await this.#findRealBlockTables(plan);
3736
3795
  return new Set([...tables.values()].map((record) => record.id));
3737
3796
  },
3738
- execute: async (query) => {
3739
- if (typeof query === "string")
3740
- return this.query(query);
3741
- if (query.kind === "typed-query")
3742
- return this.#queryCompiled(query.plan);
3743
- return this.query(query.sql, { params: query.params });
3744
- },
3797
+ execute: (query, context) => this.#liveExecute(query, context),
3798
+ executeMaintainable: (query, context) => this.#liveExecuteMaintainable(compileLiveQuery(query), context),
3799
+ maintain: (_query, result, state, tableIds, after, until, probe) => this.#liveMaintain(result, state, tableIds, after, until, probe),
3745
3800
  changeCanAffect: (query, tableIds, after, until) => this.#liveChangeCanAffect(query, tableIds, after, until)
3746
3801
  }, {
3747
3802
  ...options,
@@ -3754,6 +3809,282 @@ class MinnowDatabase {
3754
3809
  this.#liveSets.add(set);
3755
3810
  return set;
3756
3811
  }
3812
+ async #liveExecute(query, context) {
3813
+ if (context === void 0) {
3814
+ if (typeof query === "string")
3815
+ return this.query(query);
3816
+ if (query.kind === "typed-query")
3817
+ return externalizeQueryResult(await this.#withReadReservation(() => this.#queryCompiled(query.plan)));
3818
+ return this.query(query.sql, { params: query.params });
3819
+ }
3820
+ const { probe, memoize } = context;
3821
+ if (typeof query !== "string" && query.kind === "typed-query") {
3822
+ const plan = query.plan;
3823
+ const memoizable = plan.usesStatementDatetime !== true && plan.usesVolatileFunctions !== true && plan.usesSequenceCalls !== true;
3824
+ return externalizeQueryResult(await this.#withReadReservation(() => memoizable ? this.#memoizedQuery(plan, `typed ${planMemoKey(plan)}`, {}, this.store.getCatalogProbe.bind(this.store), probe, memoize) : this.#queryCompiled(plan, {}, probe)));
3825
+ }
3826
+ if (typeof query === "string")
3827
+ return this.#queryWithReadReservation(query, {}, probe, memoize);
3828
+ return this.#queryWithReadReservation(query.sql, { params: query.params }, probe, memoize);
3829
+ }
3830
+ async #liveMaintenancePlan(compiled, probe) {
3831
+ if (compiled.usesStatementDatetime === true || compiled.usesVolatileFunctions === true || compiled.usesSequenceCalls === true) {
3832
+ return void 0;
3833
+ }
3834
+ const plan = await this.#applyCatalogRewrites(compiled, probe);
3835
+ const base = plan.base;
3836
+ if (base.table === DUAL_TABLE || base.derived !== void 0 || base.union !== void 0 || base.recursive !== void 0 || base.windowed !== void 0 || plan.pendingSelectShape !== void 0 || plan.distinctWildcard === true || plan.limitParameter !== void 0 || plan.offsetParameter !== void 0 || planReadsBeyondSingleScan(plan) || planContainsFts(plan)) {
3837
+ return void 0;
3838
+ }
3839
+ if (plan.joins.length > 0) {
3840
+ const join = plan.joins[0];
3841
+ if (plan.joins.length !== 1 || join === void 0 || !["inner", "left"].includes(join.kind) || join.on !== void 0 || join.derived !== void 0 || join.union !== void 0 || join.windowed !== void 0 || join.recursive !== void 0)
3842
+ return void 0;
3843
+ const lookup = await this.#findTable(join.table);
3844
+ const lookupKey = getUniqueKeyColumn(lookup);
3845
+ if (lookupKey === void 0 || lookupKey.sqlDomain !== void 0)
3846
+ return void 0;
3847
+ const lookupReference = `${join.alias}.${lookupKey.name}`;
3848
+ const joinedKey = [join.left, join.right].find((expression) => expression.kind === "column" && expression.reference === lookupReference);
3849
+ if (joinedKey === void 0)
3850
+ return void 0;
3851
+ }
3852
+ const table = await this.#findTable(base.table);
3853
+ const keyColumn = getUniqueKeyColumn(table);
3854
+ if (keyColumn === void 0 || keyColumn.hidden === true || keyColumn.sqlDomain !== void 0)
3855
+ return void 0;
3856
+ const aggregate = LiveAggregate.plan(plan, `${base.alias}.${keyColumn.name}`);
3857
+ if (aggregate !== void 0)
3858
+ return {
3859
+ tableId: table.id,
3860
+ keyColumnId: keyColumn.id,
3861
+ qualifiedKey: `${base.alias}.${keyColumn.name}`,
3862
+ fullPlan: aggregate.inputPlan,
3863
+ deltaPlan: aggregate.inputPlan,
3864
+ publicColumns: plan.select.map((item) => item.alias),
3865
+ columnDomains: [],
3866
+ orderTerms: [],
3867
+ limit: void 0,
3868
+ offset: 0,
3869
+ margin: 0,
3870
+ complete: true,
3871
+ rows: [],
3872
+ keys: [],
3873
+ positions: /* @__PURE__ */ new Map(),
3874
+ order: [],
3875
+ aggregate
3876
+ };
3877
+ if (plan.groupBy.length > 0 || plan.having.length > 0)
3878
+ return void 0;
3879
+ const rowLocal = (expression) => {
3880
+ if (expression.kind === "subquery" || expression.kind === "exists" || expression.kind === "window" || expression.kind === "wildcard" || expression.kind === "parameter" || hasAggregate(expression)) {
3881
+ return false;
3882
+ }
3883
+ return childExpressions(expression).every(rowLocal);
3884
+ };
3885
+ if (!plan.select.every((item) => rowLocal(item.expression)))
3886
+ return void 0;
3887
+ if (!plan.predicates.every(({ left, right }) => rowLocal(left) && rowLocal(right))) {
3888
+ return void 0;
3889
+ }
3890
+ if (!plan.orderBy.every((term) => rowLocal(term.expression)))
3891
+ return void 0;
3892
+ const publicColumns = plan.select.map((item) => item.alias);
3893
+ if (publicColumns.some((name) => name.startsWith(LIVE_HIDDEN_PREFIX)))
3894
+ return void 0;
3895
+ const fullPlan = clonePlanTree(plan);
3896
+ const qualifiedKey = `${base.alias}.${keyColumn.name}`;
3897
+ fullPlan.select.push({
3898
+ expression: { kind: "column", reference: qualifiedKey },
3899
+ alias: LIVE_KEY_ALIAS
3900
+ });
3901
+ const orderTerms = [];
3902
+ for (const [index, term] of plan.orderBy.entries()) {
3903
+ let alias;
3904
+ if (term.expression.kind === "column" && !term.expression.reference.includes(".")) {
3905
+ const output = term.expression.reference;
3906
+ if (publicColumns.includes(output))
3907
+ alias = output;
3908
+ } else if (term.expression.kind === "literal" && typeof term.expression.value === "number") {
3909
+ const position = term.expression.value;
3910
+ if (!Number.isInteger(position) || position < 1 || position > publicColumns.length) {
3911
+ return void 0;
3912
+ }
3913
+ alias = publicColumns[position - 1];
3914
+ }
3915
+ if (alias === void 0) {
3916
+ alias = `${LIVE_ORDER_ALIAS}${String(index)}`;
3917
+ fullPlan.select.push({ expression: clonePlanTree(term.expression), alias });
3918
+ }
3919
+ orderTerms.push({ alias, descending: term.direction === "desc", nulls: term.nulls });
3920
+ }
3921
+ const deltaPlan = clonePlanTree(fullPlan);
3922
+ deltaPlan.orderBy = [];
3923
+ delete deltaPlan.limit;
3924
+ delete deltaPlan.offset;
3925
+ const margin = plan.limit === void 0 || orderTerms.length === 0 ? 0 : Math.max(LIVE_WINDOW_MARGIN_MIN, Math.min(plan.limit, LIVE_WINDOW_MARGIN_MAX));
3926
+ if (margin > 0)
3927
+ fullPlan.limit = (plan.limit ?? 0) + margin;
3928
+ return {
3929
+ tableId: table.id,
3930
+ keyColumnId: keyColumn.id,
3931
+ qualifiedKey,
3932
+ fullPlan,
3933
+ deltaPlan,
3934
+ publicColumns,
3935
+ columnDomains: [],
3936
+ orderTerms,
3937
+ limit: plan.limit,
3938
+ offset: plan.offset ?? 0,
3939
+ margin,
3940
+ complete: true,
3941
+ rows: [],
3942
+ keys: [],
3943
+ positions: /* @__PURE__ */ new Map(),
3944
+ order: []
3945
+ };
3946
+ }
3947
+ async #liveExecuteMaintainable(compiled, context) {
3948
+ const probe = context?.probe ?? await this.store.getCatalogProbe();
3949
+ const state = await this.#liveMaintenancePlan(compiled, probe);
3950
+ if (state === void 0)
3951
+ return void 0;
3952
+ if (state.aggregate !== void 0) {
3953
+ try {
3954
+ const input = await this.#withReadReservation(() => this.#queryCompiled(state.fullPlan, {}, probe));
3955
+ const aggregate = state.aggregate.patch(input, /* @__PURE__ */ new Set(), liveKeyToken);
3956
+ return {
3957
+ result: aggregate.result(),
3958
+ state: { ...state, aggregate },
3959
+ retainedBytes: aggregate.retainedBytes
3960
+ };
3961
+ } catch {
3962
+ return void 0;
3963
+ }
3964
+ }
3965
+ let executed;
3966
+ try {
3967
+ executed = externalizeQueryResult(await this.#withReadReservation(() => this.#queryCompiled(state.fullPlan, {}, probe)));
3968
+ } catch {
3969
+ return void 0;
3970
+ }
3971
+ const split = splitLiveHiddenColumns(executed, state);
3972
+ return liveMaintainedOutcome({
3973
+ ...state,
3974
+ columnDomains: split.result.columnDomains,
3975
+ complete: state.fullPlan.limit === void 0 || split.result.rows.length < state.fullPlan.limit,
3976
+ rows: split.result.rows,
3977
+ keys: split.keys,
3978
+ order: split.order
3979
+ }, void 0);
3980
+ }
3981
+ async #liveMaintain(retained, state, tableIds, after, until, probe) {
3982
+ if (tableIds.length !== 1 || tableIds[0] !== state.tableId)
3983
+ return void 0;
3984
+ const context = await this.#liveProofContext(after, until);
3985
+ const entry = await this.#liveProofTable(context, state.tableId);
3986
+ if (entry === void 0)
3987
+ return void 0;
3988
+ for (const version of context.changedVersions.get(state.tableId) ?? []) {
3989
+ if (!entry.coveredVersions.has(version))
3990
+ return void 0;
3991
+ }
3992
+ const changed = await this.#liveChangedKeys(entry, state.keyColumnId);
3993
+ if (changed === void 0)
3994
+ return void 0;
3995
+ const { changedKeys, existingKeys } = changed;
3996
+ if (changedKeys.size === 0)
3997
+ return { result: retained, state, changed: false };
3998
+ if (state.aggregate !== void 0) {
3999
+ const deltaPlan2 = {
4000
+ ...state.deltaPlan,
4001
+ predicates: [
4002
+ ...state.deltaPlan.predicates,
4003
+ {
4004
+ left: { kind: "column", reference: state.qualifiedKey },
4005
+ operator: "IN",
4006
+ right: {
4007
+ kind: "list",
4008
+ items: [...changedKeys.values()].map((value) => ({ kind: "literal", value }))
4009
+ }
4010
+ }
4011
+ ]
4012
+ };
4013
+ const input = await this.#withReadReservation(() => this.#queryCompiled(deltaPlan2, {}, probe));
4014
+ const aggregate = state.aggregate.patch(input, new Set(changedKeys.keys()), liveKeyToken);
4015
+ const result = aggregate.result();
4016
+ const changed2 = result.rows.length !== retained.rows.length || result.rows.some((row, index) => {
4017
+ const previous = retained.rows[index];
4018
+ return previous === void 0 || !sameLiveRow(previous, row, result.columns);
4019
+ });
4020
+ return {
4021
+ result,
4022
+ state: { ...state, aggregate },
4023
+ retainedBytes: aggregate.retainedBytes,
4024
+ changed: changed2
4025
+ };
4026
+ }
4027
+ const affected = /* @__PURE__ */ new Set();
4028
+ const retainedTokens = state.positions;
4029
+ for (const token of changedKeys.keys()) {
4030
+ const index = retainedTokens.get(token);
4031
+ if (index !== void 0)
4032
+ affected.add(index);
4033
+ }
4034
+ const fullLimit = state.limit === void 0 ? void 0 : state.limit + state.margin;
4035
+ const truncated = !state.complete;
4036
+ if (truncated && affected.size > 0 && state.orderTerms.length === 0)
4037
+ return void 0;
4038
+ if (state.offset > 0) {
4039
+ for (const token of existingKeys)
4040
+ if (!retainedTokens.has(token))
4041
+ return void 0;
4042
+ }
4043
+ const deltaPlan = {
4044
+ ...state.deltaPlan,
4045
+ predicates: [
4046
+ ...state.deltaPlan.predicates,
4047
+ {
4048
+ left: { kind: "column", reference: state.qualifiedKey },
4049
+ operator: "IN",
4050
+ right: {
4051
+ kind: "list",
4052
+ items: [...changedKeys.values()].map((value) => ({ kind: "literal", value }))
4053
+ }
4054
+ }
4055
+ ]
4056
+ };
4057
+ const delta = splitLiveHiddenColumns(externalizeQueryResult(await this.#withReadReservation(() => this.#queryCompiled(deltaPlan, {}, probe))), state);
4058
+ const compare = liveOrderComparator(state.orderTerms);
4059
+ const added = {
4060
+ rows: delta.result.rows,
4061
+ keys: delta.keys,
4062
+ order: delta.order,
4063
+ previousIndex: new Int32Array(delta.result.rows.length).fill(-1)
4064
+ };
4065
+ let merged;
4066
+ try {
4067
+ const kept = filterLiveRows(state, (index) => !affected.has(index));
4068
+ if (state.offset > 0 && added.rows.length > 0) {
4069
+ if (kept.rows.length === 0 || added.rows.some((_, index) => compare(added.order, index, kept.order, 0) < 0)) {
4070
+ return void 0;
4071
+ }
4072
+ }
4073
+ merged = mergeLiveRows(kept, added, compare, state.orderTerms.length > 0);
4074
+ } catch {
4075
+ return void 0;
4076
+ }
4077
+ if (fullLimit !== void 0 && merged.rows.length > fullLimit)
4078
+ merged = trimLiveRows(merged, fullLimit);
4079
+ if (truncated && affected.size > 0 && state.limit !== void 0) {
4080
+ const oldEdge = state.rows.length - 1;
4081
+ const visibleEdge = Math.min(merged.rows.length, state.limit) - 1;
4082
+ if (merged.rows.length < state.limit || oldEdge < 0 || visibleEdge < 0 || compare(merged.order, visibleEdge, state.order, oldEdge) > 0) {
4083
+ return void 0;
4084
+ }
4085
+ }
4086
+ return liveMaintainedOutcome({ ...state, rows: merged.rows, keys: merged.keys, order: merged.order }, retained, merged.previousIndex);
4087
+ }
3757
4088
  async #liveChangeCanAffect(query, tableIds, after, until) {
3758
4089
  const plan = typeof query === "string" ? this.#compileCached(query) : query.kind === "typed-query" ? query.plan : bindPlanParameters(this.#compileCached(query.sql), query.params);
3759
4090
  if (planContainsFts(plan))
@@ -3855,6 +4186,51 @@ class MinnowDatabase {
3855
4186
  }
3856
4187
  return context;
3857
4188
  }
4189
+ #liveChangedKeys(entry, keyColumnId) {
4190
+ const cached = entry.changedKeys.get(keyColumnId);
4191
+ if (cached !== void 0)
4192
+ return cached;
4193
+ const pending = (async () => {
4194
+ const blockIds = [];
4195
+ const existingBlockIds = /* @__PURE__ */ new Set();
4196
+ let deltaRows = 0;
4197
+ for (const segment of entry.windowSegments) {
4198
+ if (segment.kind === "base")
4199
+ continue;
4200
+ const ids = segment.columnBlockIds[keyColumnId];
4201
+ if (ids === void 0)
4202
+ return void 0;
4203
+ deltaRows += segment.rowCount;
4204
+ if (deltaRows > LIVE_MAINTENANCE_MAX_DELTA_ROWS)
4205
+ return void 0;
4206
+ blockIds.push(...ids);
4207
+ if (segment.kind !== "insert")
4208
+ for (const id of ids)
4209
+ existingBlockIds.add(id);
4210
+ }
4211
+ const changedKeys = /* @__PURE__ */ new Map();
4212
+ const existingKeys = /* @__PURE__ */ new Set();
4213
+ if (blockIds.length > 0) {
4214
+ const decoded = await this.#loadDecodedBlocks([...new Set(blockIds)]);
4215
+ for (const blockId of blockIds) {
4216
+ const column = decoded.get(blockId);
4217
+ if (column === void 0)
4218
+ return void 0;
4219
+ for (const value of column.values) {
4220
+ if (value === null)
4221
+ continue;
4222
+ const token = liveKeyToken(value);
4223
+ changedKeys.set(token, value);
4224
+ if (existingBlockIds.has(blockId))
4225
+ existingKeys.add(token);
4226
+ }
4227
+ }
4228
+ }
4229
+ return { changedKeys, existingKeys };
4230
+ })();
4231
+ entry.changedKeys.set(keyColumnId, pending);
4232
+ return pending;
4233
+ }
3858
4234
  #liveProofTable(context, tableId) {
3859
4235
  const cached = context.tables.get(tableId);
3860
4236
  if (cached !== void 0)
@@ -3875,7 +4251,7 @@ class MinnowDatabase {
3875
4251
  coveredVersions.add(committed);
3876
4252
  windowSegments.push(segment);
3877
4253
  }
3878
- return { table, windowSegments, coveredVersions };
4254
+ return { table, windowSegments, coveredVersions, changedKeys: /* @__PURE__ */ new Map() };
3879
4255
  })();
3880
4256
  context.tables.set(tableId, entry);
3881
4257
  return entry;
@@ -5181,11 +5557,15 @@ class MinnowDatabase {
5181
5557
  set.notifyLocalCommit();
5182
5558
  }
5183
5559
  async run(query) {
5184
- const probe = this.store.getCatalogProbe.bind(this.store);
5185
- if (query.plan.usesStatementDatetime === true || query.plan.usesVolatileFunctions === true) {
5186
- return (await this.#queryCompiled(query.plan)).rows;
5187
- }
5188
- return (await this.#memoizedQuery(query.plan, `typed ${planMemoKey(query.plan)}`, {}, probe)).rows;
5560
+ await this.#settleExpiredStatementTransaction();
5561
+ const open = this.#openTransaction;
5562
+ const plan = query.plan;
5563
+ const execute = async () => {
5564
+ const probe = this.store.getCatalogProbe.bind(this.store);
5565
+ return plan.usesStatementDatetime === true || plan.usesVolatileFunctions === true || plan.usesSequenceCalls === true ? this.#queryCompiled(plan) : this.#memoizedQuery(plan, `typed ${planMemoKey(plan)}`, {}, probe);
5566
+ };
5567
+ const result = open === void 0 ? await this.#withReadReservation(execute) : await this.#duringTransaction(open, () => open.session.queryPlan(plan));
5568
+ return externalizeQueryResult(result).rows;
5189
5569
  }
5190
5570
  async migrate(definition = this.#schema, options = {}) {
5191
5571
  if (definition === void 0) {
@@ -7968,10 +8348,12 @@ ${notes.map((note) => `-- ${note}`).join("\n")}`;
7968
8348
  const table = realTables.get(name);
7969
8349
  if (table === void 0)
7970
8350
  return void 0;
7971
- parts.push(`${table.id}=${String(snapshot.version ?? -1)}`);
8351
+ parts.push(`${table.id}:${String(table.revision)}`);
7972
8352
  }
7973
8353
  try {
7974
- return `blk\0${parts.join(";")}\0${JSON.stringify(block)}`;
8354
+ const ids = collectRealTableNames(block).sort().map((name) => realTables.get(name)?.id ?? name);
8355
+ const generation = await this.#queryGenerations.key(ids, snapshot.version);
8356
+ return `blk\0${parts.join(";")}\0${generation}\0${planMemoKey(block)}`;
7975
8357
  } catch {
7976
8358
  return void 0;
7977
8359
  }
@@ -14189,6 +14571,217 @@ function searchableFtsColumns(table) {
14189
14571
  return void 0;
14190
14572
  return visibleTableColumns(table).filter((column) => column.type !== "boolean").map((column) => column.name);
14191
14573
  }
14574
+ const LIVE_HIDDEN_PREFIX = "__minnow_live_";
14575
+ const LIVE_KEY_ALIAS = `${LIVE_HIDDEN_PREFIX}key`;
14576
+ const LIVE_ORDER_ALIAS = `${LIVE_HIDDEN_PREFIX}order_`;
14577
+ const LIVE_MAINTENANCE_MAX_DELTA_ROWS = 2048;
14578
+ const LIVE_WINDOW_MARGIN_MIN = 16;
14579
+ const LIVE_WINDOW_MARGIN_MAX = 64;
14580
+ function sameLiveRow(left, right, columns) {
14581
+ for (const column of columns) {
14582
+ const a = left[column] ?? null;
14583
+ const b = right[column] ?? null;
14584
+ if (a instanceof Date || b instanceof Date) {
14585
+ if (!(a instanceof Date && b instanceof Date) || !Object.is(dateMilliseconds(a), dateMilliseconds(b))) {
14586
+ return false;
14587
+ }
14588
+ } else if (!Object.is(a, b))
14589
+ return false;
14590
+ }
14591
+ return true;
14592
+ }
14593
+ function liveKeyToken(value) {
14594
+ if (typeof value === "number")
14595
+ return `n:${String(value)}`;
14596
+ if (typeof value === "string")
14597
+ return `s:${value}`;
14598
+ if (typeof value === "boolean")
14599
+ return value ? "b:1" : "b:0";
14600
+ if (value instanceof Date)
14601
+ return `d:${String(dateMilliseconds(value))}`;
14602
+ return "z";
14603
+ }
14604
+ function splitLiveHiddenColumns(executed, state) {
14605
+ const publicCount = state.publicColumns.length;
14606
+ const hidden = executed.columns.slice(publicCount);
14607
+ const count = executed.rows.length;
14608
+ const keys = new Array(count);
14609
+ const order = state.orderTerms.map(() => new Array(count));
14610
+ for (let index = 0; index < count; index += 1) {
14611
+ const row = executed.rows[index] ?? {};
14612
+ keys[index] = row[LIVE_KEY_ALIAS] ?? null;
14613
+ for (const [term, { alias }] of state.orderTerms.entries()) {
14614
+ const values = order[term];
14615
+ if (values !== void 0)
14616
+ values[index] = row[alias] ?? null;
14617
+ }
14618
+ for (const column of hidden)
14619
+ Reflect.deleteProperty(row, column);
14620
+ }
14621
+ return {
14622
+ result: {
14623
+ columns: executed.columns.slice(0, publicCount),
14624
+ columnDomains: executed.columnDomains.slice(0, publicCount),
14625
+ rows: executed.rows
14626
+ },
14627
+ keys,
14628
+ order
14629
+ };
14630
+ }
14631
+ function liveMaintainedOutcome(state, previous, previousIndex) {
14632
+ const positions = /* @__PURE__ */ new Map();
14633
+ let retainedBytes = 128 + planMemoKey(state.fullPlan).length * 2 + planMemoKey(state.deltaPlan).length * 2;
14634
+ for (const [index, key] of state.keys.entries()) {
14635
+ const token = liveKeyToken(key);
14636
+ positions.set(token, index);
14637
+ retainedBytes += 64 + token.length * 2;
14638
+ }
14639
+ for (const row of state.rows)
14640
+ retainedBytes += 48 + estimateValuesBytes(Object.values(row)) * 2;
14641
+ for (const values of state.order)
14642
+ retainedBytes += values.length * 8 + estimateValuesBytes(values) * 2;
14643
+ state = { ...state, positions };
14644
+ const visibleCount = state.limit === void 0 ? state.rows.length : Math.min(state.rows.length, state.limit);
14645
+ const visible = state.rows.slice(0, visibleCount);
14646
+ if (previous === void 0) {
14647
+ return {
14648
+ result: {
14649
+ columns: [...state.publicColumns],
14650
+ columnDomains: [...state.columnDomains],
14651
+ rows: visible
14652
+ },
14653
+ state,
14654
+ retainedBytes,
14655
+ changed: true
14656
+ };
14657
+ }
14658
+ const previousCount = previous.rows.length;
14659
+ const retained = new Int32Array(visibleCount);
14660
+ let same = visibleCount === previousCount;
14661
+ let kept = 0;
14662
+ for (let index = 0; index < visibleCount; index += 1) {
14663
+ const was = previousIndex?.[index] ?? -1;
14664
+ if (was >= 0 && was < previousCount) {
14665
+ retained[index] = was;
14666
+ kept += 1;
14667
+ if (was !== index)
14668
+ same = false;
14669
+ continue;
14670
+ }
14671
+ retained[index] = -1;
14672
+ const before = previous.rows[index];
14673
+ const now = visible[index];
14674
+ if (before !== void 0 && now !== void 0 && sameLiveRow(before, now, previous.columns)) {
14675
+ visible[index] = before;
14676
+ retained[index] = index;
14677
+ kept += 1;
14678
+ } else
14679
+ same = false;
14680
+ }
14681
+ if (same)
14682
+ return { result: previous, state, retainedBytes, changed: false };
14683
+ return {
14684
+ result: {
14685
+ columns: [...state.publicColumns],
14686
+ columnDomains: [...state.columnDomains],
14687
+ rows: visible
14688
+ },
14689
+ state,
14690
+ retainedBytes,
14691
+ changed: true,
14692
+ ...kept > 0 ? { retained } : {}
14693
+ };
14694
+ }
14695
+ function filterLiveRows(state, keep) {
14696
+ const rows = [];
14697
+ const keys = [];
14698
+ const order = state.order.map(() => new Array());
14699
+ const previous = [];
14700
+ for (let index = 0; index < state.rows.length; index += 1) {
14701
+ const row = state.rows[index];
14702
+ if (row === void 0 || !keep(index))
14703
+ continue;
14704
+ rows.push(row);
14705
+ keys.push(state.keys[index] ?? null);
14706
+ for (const [term, values] of state.order.entries()) {
14707
+ order[term]?.push(values[index] ?? null);
14708
+ }
14709
+ previous.push(index);
14710
+ }
14711
+ return { rows, keys, order, previousIndex: Int32Array.from(previous) };
14712
+ }
14713
+ function trimLiveRows(rows, count) {
14714
+ return {
14715
+ rows: rows.rows.slice(0, count),
14716
+ keys: rows.keys.slice(0, count),
14717
+ order: rows.order.map((values) => values.slice(0, count)),
14718
+ previousIndex: rows.previousIndex.slice(0, count)
14719
+ };
14720
+ }
14721
+ function liveOrderComparator(terms) {
14722
+ return (leftOrder, leftIndex, rightOrder, rightIndex) => {
14723
+ for (const [term, { descending, nulls }] of terms.entries()) {
14724
+ const a = leftOrder[term]?.[leftIndex] ?? null;
14725
+ const b = rightOrder[term]?.[rightIndex] ?? null;
14726
+ if (a === null || b === null) {
14727
+ if (a === null && b === null)
14728
+ continue;
14729
+ const nullsFirst = nulls === "first" || nulls === void 0 && descending;
14730
+ return a === null ? nullsFirst ? -1 : 1 : nullsFirst ? 1 : -1;
14731
+ }
14732
+ let comparison = compareSqlValues(a, b);
14733
+ if (descending)
14734
+ comparison = -comparison;
14735
+ if (comparison !== 0)
14736
+ return comparison;
14737
+ }
14738
+ return 0;
14739
+ };
14740
+ }
14741
+ function mergeLiveRows(kept, added, compare, ordered) {
14742
+ if (added.rows.length === 0)
14743
+ return kept;
14744
+ const terms = kept.order.length;
14745
+ const total = kept.rows.length + added.rows.length;
14746
+ const rows = new Array(total);
14747
+ const keys = new Array(total);
14748
+ const order = Array.from({ length: terms }, () => new Array(total));
14749
+ const previousIndex = new Int32Array(total);
14750
+ const take = (source, from, to) => {
14751
+ rows[to] = source.rows[from] ?? {};
14752
+ keys[to] = source.keys[from] ?? null;
14753
+ for (let term = 0; term < terms; term += 1) {
14754
+ const values = order[term];
14755
+ if (values !== void 0)
14756
+ values[to] = source.order[term]?.[from] ?? null;
14757
+ }
14758
+ previousIndex[to] = source.previousIndex[from] ?? -1;
14759
+ };
14760
+ if (!ordered) {
14761
+ for (let index = 0; index < kept.rows.length; index += 1)
14762
+ take(kept, index, index);
14763
+ for (let index = 0; index < added.rows.length; index += 1) {
14764
+ take(added, index, kept.rows.length + index);
14765
+ }
14766
+ return { rows, keys, order, previousIndex };
14767
+ }
14768
+ const addedIndexes = added.rows.map((_, index) => index);
14769
+ addedIndexes.sort((left, right) => compare(added.order, left, added.order, right));
14770
+ let keptIndex = 0;
14771
+ let addedPosition = 0;
14772
+ for (let to = 0; to < total; to += 1) {
14773
+ const addedIndex = addedIndexes[addedPosition];
14774
+ const takeAdded = addedIndex !== void 0 && (keptIndex >= kept.rows.length || compare(added.order, addedIndex, kept.order, keptIndex) < 0);
14775
+ if (takeAdded) {
14776
+ take(added, addedIndex, to);
14777
+ addedPosition += 1;
14778
+ } else {
14779
+ take(kept, keptIndex, to);
14780
+ keptIndex += 1;
14781
+ }
14782
+ }
14783
+ return { rows, keys, order, previousIndex };
14784
+ }
14192
14785
  function getUniqueKeyColumn(table) {
14193
14786
  if (table.uniqueKeyColumnId === void 0)
14194
14787
  return void 0;