@sixb/ducklake 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,6 +65,18 @@ const lakeStorage = new DuckLakeStorage({
65
65
  Use `close()` during service shutdown so DuckDB can close its runtime and release
66
66
  catalog connections cleanly.
67
67
 
68
+ ### Extensions
69
+
70
+ On first use, the provider installs DuckLake and the extensions its catalog,
71
+ data path and secrets need. A missing extension is downloaded over HTTPS from
72
+ `https://extensions.duckdb.org` into DuckDB's extension directory
73
+ (`duckdb.config.extension_directory`, `~/.duckdb/extensions` by default), so a
74
+ host that allows only that HTTPS domain, such as a restricted Vercel sandbox,
75
+ can open a lake. When that download fails, the provider logs a warning and
76
+ DuckDB's own `INSTALL` downloads the extension over plain HTTP, as before.
77
+ With `custom_extension_repository` set, DuckDB installs from that repository
78
+ alone.
79
+
68
80
  ### Maintenance
69
81
 
70
82
  DuckLake keeps historical snapshots and deleted files until maintenance runs.
@@ -102,6 +114,10 @@ budget and PostgreSQL metadata pool. Metadata and writes remain available betwee
102
114
  Each read stays pinned to one immutable snapshot. Maintenance waits until no reads remain; it
103
115
  blocks new reads only while running, so continuous traffic can postpone it.
104
116
 
117
+ `readChanges` compares two versions in DuckDB. When no version between them changed rows
118
+ (`rowsChanged` in the commit metadata, read from at most 32 snapshots), it returns an empty change
119
+ set without reading the table.
120
+
105
121
  `maxStreamingReads` limits native streaming readers per provider (default: `4`); extra reads use
106
122
  bounded pages on the main connection. It does not limit pipeline inputs. Raise it when memory and
107
123
  catalog capacity allow. Finish the iterator, use `for await` with `break`, or pass `signal` to cancel
@@ -190,8 +206,10 @@ Datasets declaring `sequenceBy` instead compare source revisions: older changes
190
206
  identical ties are unchanged, and conflicting ties abort the merge. Deletes require
191
207
  `change.delete(key, { sequence })`. Source revisions and deletion tombstones are stored in a
192
208
  companion DuckLake table in the same transaction as visible rows, surviving snapshot expiration.
193
- New tombstone state creates a version even when the visible row count stays zero. Sequenced
194
- provider writes use merge sessions; snapshot syncs convert rows to ordered upserts.
209
+ New tombstone state creates a version even when the visible rows stay the same; such a version
210
+ records `rowsChanged: false`. So does a newer upsert that differs from the stored row only in its
211
+ `sequenceBy` column: the row is kept and only its source revision advances. Sequenced provider
212
+ writes use merge sessions; snapshot syncs convert rows to ordered upserts.
195
213
  Snapshot syncs use `commit({ createInitialVersion: true })` to make a first empty result
196
214
  addressable. Later no-op commits reuse the current version, including after a concurrent write.
197
215
  Use `commit({ retryOnConflict: true })` to rebase retained changes, with at most 3 attempts.
package/dist/index.js CHANGED
@@ -647,6 +647,7 @@ function parseCommitMetadata(value) {
647
647
  const producer = parseDatasetProducer(parsed.sixb.producer);
648
648
  const rowCount = parsed.sixb.rowCount;
649
649
  const validatedPrimaryKeyColumns = parsed.sixb.validatedPrimaryKeyColumns;
650
+ const { rowsChanged, lineageChanged } = parsed.sixb;
650
651
  return {
651
652
  kind: "datasetVersion",
652
653
  datasetId: parsed.sixb.datasetId,
@@ -657,7 +658,9 @@ function parseCommitMetadata(value) {
657
658
  ...typeof commitId === "string" && isRowCount(rowCount) ? { rowCount } : {},
658
659
  ...typeof commitId === "string" && isValidatedPrimaryKeyColumns(validatedPrimaryKeyColumns) ? { validatedPrimaryKeyColumns } : {},
659
660
  ...isSchemaChangeMetadata(parsed.sixb.schemaChange) ? { schemaChange: parsed.sixb.schemaChange } : {},
660
- ...parsed.sixb.lineage === undefined ? {} : { lineage: parsed.sixb.lineage }
661
+ ...parsed.sixb.lineage === undefined ? {} : { lineage: parsed.sixb.lineage },
662
+ ...typeof rowsChanged === "boolean" ? { rowsChanged } : {},
663
+ ...typeof lineageChanged === "boolean" ? { lineageChanged } : {}
661
664
  };
662
665
  }
663
666
  function changesTouchTables(changesMade, tableIds) {
@@ -729,6 +732,7 @@ class DuckLakeChangeReader {
729
732
  const columns = resolveDatasetChangeColumns(input, before, after);
730
733
  if (!columns)
731
734
  return null;
735
+ const unchanged = (input.toVersionId === input.fromVersionId || !after.rowsChanged) && await this.snapshots.holdsRowsOf(input.datasetId, input.toVersionId, input.fromVersionId);
732
736
  const lease = await this.connections.acquireAttachedRuntime();
733
737
  const runtime = lease.runtime;
734
738
  const temporary = quoteIdentifier(`sixb_delta_${randomUUID().replaceAll("-", "")}`);
@@ -741,6 +745,9 @@ class DuckLakeChangeReader {
741
745
  const table = qualifiedTableName(this.options, ref.tableName);
742
746
  const oldRelation = `${table} AT (VERSION => ${parseVersionId(input.fromVersionId)})`;
743
747
  const newRelation = `${table} AT (VERSION => ${parseVersionId(input.toVersionId)})`;
748
+ if (unchanged) {
749
+ return noChanges(after.rowCount ?? before.rowCount ?? await countRows(runtime, newRelation));
750
+ }
744
751
  const previousCount = await uniqueRowCount(runtime, oldRelation, input.keyColumns);
745
752
  const nextCount = await uniqueRowCount(runtime, newRelation, input.keyColumns);
746
753
  if (previousCount === null || nextCount === null)
@@ -806,6 +813,21 @@ class DuckLakeChangeReader {
806
813
  }
807
814
  }
808
815
  }
816
+ function noChanges(rowCount) {
817
+ return {
818
+ fromRowCount: rowCount,
819
+ toRowCount: rowCount,
820
+ changeCount: 0,
821
+ changes: async function* () {
822
+ yield* [];
823
+ }(),
824
+ async close() {}
825
+ };
826
+ }
827
+ async function countRows(runtime, relation) {
828
+ const [row] = await runtime.query(`SELECT count(*) AS total FROM ${relation}`);
829
+ return safeCount(row, "total");
830
+ }
809
831
  async function uniqueRowCount(runtime, relation, keys) {
810
832
  const names = keys.map(quoteIdentifier);
811
833
  const invalid = names.map((name) => `${name} IS NULL OR trim(${name}) = ''`).join(" OR ");
@@ -849,13 +871,78 @@ function localCatalogCoordinationKey(options) {
849
871
  }
850
872
 
851
873
  // src/internal/duckdb-runtime.ts
852
- import { mkdir, mkdtemp, rm } from "fs/promises";
853
- import { join, resolve as resolve2 } from "path";
874
+ import { mkdir, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
875
+ import { join as join2, resolve as resolve2 } from "path";
854
876
  import {
855
877
  DuckDBInstance
856
878
  } from "@duckdb/node-api";
857
879
  import { LakeStorageError as LakeStorageError11 } from "@sixb/core/lake-storage";
858
880
 
881
+ // src/internal/duckdb-extensions.ts
882
+ import { mkdtemp, rm, writeFile } from "fs/promises";
883
+ import { tmpdir } from "os";
884
+ import { join } from "path";
885
+ import { promisify } from "util";
886
+ import { gunzip } from "zlib";
887
+ var extensionsUrl = "https://extensions.duckdb.org";
888
+ var downloadTimeoutMillis = 60000;
889
+ var gunzipAsync = promisify(gunzip);
890
+ async function prefetchDuckDbExtensions(runtime, extensions) {
891
+ await prefetch(runtime, extensions).catch((error) => warnFallback(extensions.join("', '"), error));
892
+ }
893
+ async function prefetch(runtime, extensions) {
894
+ const [target] = await runtime.query(`
895
+ SELECT
896
+ version() AS version,
897
+ (SELECT platform FROM pragma_platform()) AS platform,
898
+ current_setting('custom_extension_repository') AS repository
899
+ `);
900
+ if (target?.repository !== "")
901
+ return;
902
+ const missing = await missingExtensions(runtime, extensions);
903
+ if (missing.length === 0)
904
+ return;
905
+ const directory = await mkdtemp(join(tmpdir(), "sixb-duckdb-extensions-"));
906
+ try {
907
+ const downloads = await Promise.all(missing.map(async (name) => ({
908
+ name,
909
+ file: await download(`${target.version}/${target.platform}/${name}`, directory, name).catch((error) => warnFallback(name, error))
910
+ })));
911
+ for (const { name, file } of downloads) {
912
+ if (file === undefined)
913
+ continue;
914
+ await runtime.run(`INSTALL ${quoteSqlString(file)}`).catch((error) => warnFallback(name, error));
915
+ }
916
+ } finally {
917
+ await rm(directory, { recursive: true, force: true });
918
+ }
919
+ }
920
+ function warnFallback(name, error) {
921
+ const reason = error instanceof Error ? error.message : String(error);
922
+ console.warn(`[SixbDuckLake] Could not install DuckDB extension '${name}' over HTTPS (${reason}); falling back to DuckDB's INSTALL.`);
923
+ return;
924
+ }
925
+ async function missingExtensions(runtime, extensions) {
926
+ const requested = `[${quoteSqlStringList(extensions.map((extension) => extension.toLowerCase()))}]`;
927
+ const rows = await runtime.query(`
928
+ SELECT extension_name
929
+ FROM duckdb_extensions()
930
+ WHERE NOT installed
931
+ AND (list_contains(${requested}, extension_name) OR list_has_any(${requested}, aliases))
932
+ `);
933
+ return rows.map((row) => String(row.extension_name));
934
+ }
935
+ async function download(path, directory, name) {
936
+ const response = await fetch(`${extensionsUrl}/${path}.duckdb_extension.gz`, {
937
+ signal: AbortSignal.timeout(downloadTimeoutMillis)
938
+ });
939
+ if (!response.ok)
940
+ throw new Error(`HTTP ${response.status}`);
941
+ const file = join(directory, `${name}.duckdb_extension`);
942
+ await writeFile(file, await gunzipAsync(new Uint8Array(await response.arrayBuffer())));
943
+ return file;
944
+ }
945
+
859
946
  // src/internal/duckdb-reader.ts
860
947
  import { LakeStorageError as LakeStorageError10 } from "@sixb/core/lake-storage";
861
948
 
@@ -1072,7 +1159,7 @@ class NodeDuckDbRuntime {
1072
1159
  this.connection.closeSync();
1073
1160
  this.instance.closeSync();
1074
1161
  if (this.temporaryDirectory)
1075
- await rm(this.temporaryDirectory, { recursive: true, force: true });
1162
+ await rm2(this.temporaryDirectory, { recursive: true, force: true });
1076
1163
  const failed = results.find((result) => result.status === "rejected");
1077
1164
  if (failed?.status === "rejected")
1078
1165
  throw failed.reason;
@@ -1159,7 +1246,7 @@ async function createDuckDbRuntime(options = {}) {
1159
1246
  if (base !== "") {
1160
1247
  const directory = resolve2(base);
1161
1248
  await mkdir(directory, { recursive: true });
1162
- temporaryDirectory = await mkdtemp(join(directory, "sixb-"));
1249
+ temporaryDirectory = await mkdtemp2(join2(directory, "sixb-"));
1163
1250
  }
1164
1251
  let instance;
1165
1252
  try {
@@ -1171,13 +1258,15 @@ async function createDuckDbRuntime(options = {}) {
1171
1258
  } catch (error) {
1172
1259
  instance?.closeSync();
1173
1260
  if (temporaryDirectory)
1174
- await rm(temporaryDirectory, { recursive: true, force: true });
1261
+ await rm2(temporaryDirectory, { recursive: true, force: true });
1175
1262
  throw error;
1176
1263
  }
1177
1264
  }
1178
1265
  async function installDuckLakeExtensions(runtime, options) {
1266
+ const extensions = requiredExtensions(options);
1267
+ await prefetchDuckDbExtensions(runtime, ["ducklake", ...extensions]);
1179
1268
  await runtime.run("INSTALL ducklake");
1180
- for (const extension of requiredExtensions(options)) {
1269
+ for (const extension of extensions) {
1181
1270
  await runtime.run(`INSTALL ${quoteIdentifier(extension)}`);
1182
1271
  }
1183
1272
  }
@@ -2619,6 +2708,7 @@ import { LakeStorageError as LakeStorageError19 } from "@sixb/core/lake-storage"
2619
2708
  var SNAPSHOT_ROW_BATCH_SIZE = 128;
2620
2709
  var FEED_PAGE_SIZE = 512;
2621
2710
  var SHORT_READ_BATCH_SIZE = 8;
2711
+ var UNCHANGED_ROWS_WALK_LIMIT = 32;
2622
2712
  var CATALOG_SNAPSHOT_SCAN_LIMIT = 512;
2623
2713
 
2624
2714
  class DuckLakeSnapshotReader {
@@ -2661,6 +2751,33 @@ class DuckLakeSnapshotReader {
2661
2751
  return this.getVersionForTableRef(runtime, tableRef, snapshotId);
2662
2752
  });
2663
2753
  }
2754
+ async holdsRowsOf(datasetId, versionId, earlierVersionId) {
2755
+ const snapshotId = parseVersionId(versionId);
2756
+ const earlierSnapshotId = parseVersionId(earlierVersionId);
2757
+ if (snapshotId === earlierSnapshotId) {
2758
+ return true;
2759
+ }
2760
+ if (BigInt(snapshotId) < BigInt(earlierSnapshotId)) {
2761
+ return false;
2762
+ }
2763
+ this.connections.assertOpen();
2764
+ return this.connections.withAttachedRuntime(async (runtime) => {
2765
+ const tableRef = await resolveDatasetTableRef(this.options, runtime, datasetId);
2766
+ if (!tableRef) {
2767
+ return false;
2768
+ }
2769
+ const candidates = await this.querySnapshotCandidates(runtime, {
2770
+ table: tableRef,
2771
+ beforeSnapshotId: String(BigInt(snapshotId) + 1n),
2772
+ fromSnapshotId: earlierSnapshotId,
2773
+ limit: UNCHANGED_ROWS_WALK_LIMIT + 1
2774
+ });
2775
+ if (candidates[0]?.snapshotId !== snapshotId || candidates[candidates.length - 1]?.snapshotId !== earlierSnapshotId) {
2776
+ return false;
2777
+ }
2778
+ return candidates.slice(0, -1).every((candidate) => this.candidateToSnapshotRow(datasetId, tableRef.tableId, candidate)?.rowsChanged === false);
2779
+ });
2780
+ }
2664
2781
  async listDatasetCatalogState(datasetIds) {
2665
2782
  this.connections.assertOpen();
2666
2783
  if (datasetIds.length === 0) {
@@ -3101,6 +3218,9 @@ class DuckLakeSnapshotReader {
3101
3218
  if (input.beforeSnapshotId !== undefined) {
3102
3219
  assertDuckLakeSnapshotId2(input.beforeSnapshotId);
3103
3220
  }
3221
+ if (input.fromSnapshotId !== undefined) {
3222
+ assertDuckLakeSnapshotId2(input.fromSnapshotId);
3223
+ }
3104
3224
  const rows = await runtime.query(buildDuckLakeMetadataQuery(this.options, (table) => {
3105
3225
  const bounds = [
3106
3226
  `changes.snapshot_id >= (
@@ -3115,6 +3235,9 @@ class DuckLakeSnapshotReader {
3115
3235
  if (input.beforeSnapshotId !== undefined) {
3116
3236
  bounds.push(`changes.snapshot_id < ${input.beforeSnapshotId}`);
3117
3237
  }
3238
+ if (input.fromSnapshotId !== undefined) {
3239
+ bounds.push(`changes.snapshot_id >= ${input.fromSnapshotId}`);
3240
+ }
3118
3241
  const namesDatasetTable = datasetTableIds(input.table).map((tableId) => `strpos(',' || changes.changes_made || ',', ${quoteSqlString(`:${tableId},`)}) > 0`).join(" OR ");
3119
3242
  return `
3120
3243
  SELECT
@@ -3175,6 +3298,8 @@ class DuckLakeSnapshotReader {
3175
3298
  tableId,
3176
3299
  createdAt: candidate.createdAt,
3177
3300
  mode: metadata.mode ?? "schema",
3301
+ rowsChanged: metadata.rowsChanged ?? true,
3302
+ lineageChanged: metadata.lineageChanged ?? true,
3178
3303
  metadata
3179
3304
  };
3180
3305
  }
@@ -3184,6 +3309,8 @@ class DuckLakeSnapshotReader {
3184
3309
  tableId,
3185
3310
  createdAt: candidate.createdAt,
3186
3311
  mode: candidate.metadata?.mode ?? (hasDeleteChange ? "snapshot" : "append"),
3312
+ rowsChanged: true,
3313
+ lineageChanged: candidate.metadata?.lineageChanged ?? true,
3187
3314
  ...candidate.metadata !== undefined ? { metadata: candidate.metadata } : {}
3188
3315
  };
3189
3316
  }
@@ -3209,6 +3336,8 @@ function snapshotToVersion(tableRef, row, schema) {
3209
3336
  producer: row.metadata?.producer,
3210
3337
  inputs: row.metadata?.inputs,
3211
3338
  ...storedLineage(tableRef.datasetId, row),
3339
+ rowsChanged: row.rowsChanged,
3340
+ lineageChanged: row.lineageChanged,
3212
3341
  ...row.metadata?.rowCount !== undefined ? { rowCount: row.metadata.rowCount } : {}
3213
3342
  };
3214
3343
  }
@@ -3306,7 +3435,7 @@ async function applyDatasetRowsFromRelation(input) {
3306
3435
  changedTableRows
3307
3436
  };
3308
3437
  }
3309
- const sourceRowCount = await countRows(input.runtime, input.sourceRelationSql);
3438
+ const sourceRowCount = await countRows2(input.runtime, input.sourceRelationSql);
3310
3439
  const knownRowCount = baselineRowCount ?? input.previousRowCount;
3311
3440
  const targetWasEmpty = knownRowCount === undefined ? await isEmptyRelation(input.runtime, table) : knownRowCount === 0;
3312
3441
  await input.runtime.run(`INSERT INTO ${table} (${columnsSql}) SELECT ${columnsSql} FROM ${input.sourceRelationSql}`);
@@ -3376,7 +3505,7 @@ async function fingerprintRelation(runtime, relationSql, columnsSql) {
3376
3505
  function sameFingerprint(left, right) {
3377
3506
  return left.rowCount === right.rowCount && left.hashSum === right.hashSum && left.hashXor === right.hashXor;
3378
3507
  }
3379
- async function countRows(runtime, relationSql) {
3508
+ async function countRows2(runtime, relationSql) {
3380
3509
  const [row] = await runtime.query(`SELECT count(*) AS row_count FROM ${relationSql}`);
3381
3510
  if (row === undefined) {
3382
3511
  return 0;
@@ -4707,6 +4836,7 @@ import { randomUUID as randomUUID8 } from "crypto";
4707
4836
  import {
4708
4837
  assertUnsequencedDatasetWrite,
4709
4838
  getDatasetPrimaryKeyColumns as getDatasetPrimaryKeyColumns4,
4839
+ hasDatasetLineageChanges,
4710
4840
  hasDatasetProvenanceChanges,
4711
4841
  LakeConcurrencyError as LakeConcurrencyError2,
4712
4842
  LakeStorageError as LakeStorageError26,
@@ -4727,7 +4857,7 @@ async function applyDatasetMergeFromRelation(input) {
4727
4857
  }
4728
4858
  const stagingTable = quoteIdentifier(input.stagingTableName);
4729
4859
  const targetTable = qualifiedTableName(input.options, encodeDatasetTableName(input.dataset.id));
4730
- const sourceRowCount = await countRows2(input.runtime, stagingTable);
4860
+ const sourceRowCount = await countRows3(input.runtime, stagingTable);
4731
4861
  const previousRowCount = await currentBaselineRowCount(input, targetTable, primaryKeyColumns);
4732
4862
  return withMaterializedFinalChanges(input, primaryKeyColumns, async (finalChanges, orderingChanged) => {
4733
4863
  const effects = await countMergeEffects(input, targetTable, finalChanges, primaryKeyColumns);
@@ -4827,7 +4957,7 @@ async function applySourceOrdering(input, acceptedTable) {
4827
4957
  }
4828
4958
  async function currentBaselineRowCount(input, targetTable, primaryKeyColumns) {
4829
4959
  if (sameColumns(input.validatedPrimaryKeyColumns, primaryKeyColumns)) {
4830
- return input.previousRowCount ?? countRows2(input.runtime, targetTable);
4960
+ return input.previousRowCount ?? countRows3(input.runtime, targetTable);
4831
4961
  }
4832
4962
  return inspectUniqueKeyedRelation({
4833
4963
  runtime: input.runtime,
@@ -4920,9 +5050,10 @@ function primaryKeyMatchSql(primaryKeyColumns, leftAlias, rightAlias) {
4920
5050
  return primaryKeyColumns.map((column) => `${leftAlias}.${quoteIdentifier(column)} = ${rightAlias}.${quoteIdentifier(column)}`).join(" AND ");
4921
5051
  }
4922
5052
  function datasetRowDifferenceSql(dataset, leftAlias, rightAlias) {
4923
- return dataset.schema.columns.map((column) => `${leftAlias}.${quoteIdentifier(column.name)} IS DISTINCT FROM ${rightAlias}.${quoteIdentifier(column.name)}`).join(" OR ");
5053
+ const differences = dataset.schema.columns.filter((column) => column.name !== dataset.sequenceBy).map((column) => `${leftAlias}.${quoteIdentifier(column.name)} IS DISTINCT FROM ${rightAlias}.${quoteIdentifier(column.name)}`);
5054
+ return differences.length > 0 ? differences.join(" OR ") : "FALSE";
4924
5055
  }
4925
- async function countRows2(runtime, relationSql) {
5056
+ async function countRows3(runtime, relationSql) {
4926
5057
  const [row] = await runtime.query(`SELECT count(*) AS row_count FROM ${relationSql}`);
4927
5058
  return row === undefined ? 0 : Number(getBigIntLike(row, "row_count"));
4928
5059
  }
@@ -5399,13 +5530,22 @@ class DuckLakeWriteCoordinator {
5399
5530
  validatedPrimaryKeyColumns: latestVersion?.validatedPrimaryKeyColumns
5400
5531
  });
5401
5532
  input.signal?.throwIfAborted();
5402
- const noOpBase = changeResult.dataChangeExpected ? undefined : latestVersion === undefined ? await this.latestVersionAtReadSnapshot(input) : latestVersion;
5533
+ const parent = latestVersion === undefined ? await this.latestVersionAtReadSnapshot(input) : latestVersion;
5534
+ const noOpBase = changeResult.dataChangeExpected ? undefined : parent;
5403
5535
  const createsVersion = noOpBase === undefined || this.noOpCreatesVersion(input, noOpBase, lineage);
5404
5536
  if (createsVersion && mustClaimTable(input, changeResult, lineage)) {
5405
5537
  await this.claimTable(input);
5406
5538
  }
5407
5539
  const commitId = randomUUID8();
5408
- await this.setCommitMetadata(input, commitId, commitRowCount(input, changeResult, latestVersion), lineage);
5540
+ await this.setCommitMetadata(input, commitId, commitRowCount(input, changeResult, latestVersion), lineage, {
5541
+ rowsChanged: parent === null || changeResult.changedTableRows,
5542
+ lineageChanged: hasDatasetLineageChanges(parent, {
5543
+ datasetId: input.dataset.id,
5544
+ schema: input.dataset.schema,
5545
+ producer: input.producer,
5546
+ lineage
5547
+ })
5548
+ });
5409
5549
  await commitVersionTransaction(input.runtime, input.dataset.id, createsVersion && mustCommitOnReadSnapshot(input, changeResult) ? { poisonRuntime: () => this.connections.poisonRuntime() } : undefined);
5410
5550
  committed = true;
5411
5551
  const committedWriteSnapshotId = await this.lastCommittedSnapshotId(input.runtime);
@@ -5475,7 +5615,7 @@ class DuckLakeWriteCoordinator {
5475
5615
  }
5476
5616
  return result;
5477
5617
  }
5478
- async setCommitMetadata(input, commitId, rowCount, lineage) {
5618
+ async setCommitMetadata(input, commitId, rowCount, lineage, changed) {
5479
5619
  const validatedPrimaryKeyColumns = getDatasetPrimaryKeyColumns4(input.dataset);
5480
5620
  const metadata = {
5481
5621
  kind: "datasetVersion",
@@ -5486,7 +5626,9 @@ class DuckLakeWriteCoordinator {
5486
5626
  inputs: input.inputs ? structuredClone(input.inputs) : undefined,
5487
5627
  ...rowCount.kind === "exact" ? { rowCount: rowCount.value } : {},
5488
5628
  ...validatedPrimaryKeyColumns !== null ? { validatedPrimaryKeyColumns: [...validatedPrimaryKeyColumns] } : {},
5489
- ...lineage === undefined ? {} : { lineage: encodeLineage(lineage) }
5629
+ ...lineage === undefined ? {} : { lineage: encodeLineage(lineage) },
5630
+ rowsChanged: changed.rowsChanged,
5631
+ lineageChanged: changed.lineageChanged
5490
5632
  };
5491
5633
  await input.runtime.run(`CALL ${quoteIdentifier(duckLakeAlias(this.options))}.set_commit_message(${quoteSqlString("Sixb")}, ${quoteSqlString(input.commitMessage)}, extra_info => ${quoteSqlString(JSON.stringify({ sixb: metadata }))})`);
5492
5634
  }
@@ -5737,4 +5879,4 @@ export {
5737
5879
  DuckLakeStorage
5738
5880
  };
5739
5881
 
5740
- //# debugId=9F26B0918A26BCC464756E2164756E21
5882
+ //# debugId=11CCDDEF58B2058164756E2164756E21