@gscdump/engine 0.32.11 → 0.33.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.
@@ -145,21 +145,6 @@ declare const queries: import("drizzle-orm/pg-core").PgTableWithColumns<{
145
145
  identity: undefined;
146
146
  generated: undefined;
147
147
  }>;
148
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
149
- name: string;
150
- tableName: "queries";
151
- dataType: "string";
152
- data: string;
153
- driverParam: string;
154
- notNull: false;
155
- hasDefault: false;
156
- isPrimaryKey: false;
157
- isAutoincrement: false;
158
- hasRuntimeDefault: false;
159
- enumValues: undefined;
160
- identity: undefined;
161
- generated: undefined;
162
- }>;
163
148
  date: import("drizzle-orm/pg-core").PgBuildColumn<"queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
164
149
  name: string;
165
150
  tableName: "queries";
@@ -339,21 +324,6 @@ declare const page_queries: import("drizzle-orm/pg-core").PgTableWithColumns<{
339
324
  identity: undefined;
340
325
  generated: undefined;
341
326
  }>;
342
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"page_queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
343
- name: string;
344
- tableName: "page_queries";
345
- dataType: "string";
346
- data: string;
347
- driverParam: string;
348
- notNull: false;
349
- hasDefault: false;
350
- isPrimaryKey: false;
351
- isAutoincrement: false;
352
- hasRuntimeDefault: false;
353
- enumValues: undefined;
354
- identity: undefined;
355
- generated: undefined;
356
- }>;
357
327
  date: import("drizzle-orm/pg-core").PgBuildColumn<"page_queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
358
328
  name: string;
359
329
  tableName: "page_queries";
@@ -863,21 +833,6 @@ declare const search_appearance_queries: import("drizzle-orm/pg-core").PgTableWi
863
833
  identity: undefined;
864
834
  generated: undefined;
865
835
  }>;
866
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
867
- name: string;
868
- tableName: "search_appearance_queries";
869
- dataType: "string";
870
- data: string;
871
- driverParam: string;
872
- notNull: false;
873
- hasDefault: false;
874
- isPrimaryKey: false;
875
- isAutoincrement: false;
876
- hasRuntimeDefault: false;
877
- enumValues: undefined;
878
- identity: undefined;
879
- generated: undefined;
880
- }>;
881
836
  date: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
882
837
  name: string;
883
838
  tableName: "search_appearance_queries";
@@ -990,21 +945,6 @@ declare const search_appearance_page_queries: import("drizzle-orm/pg-core").PgTa
990
945
  identity: undefined;
991
946
  generated: undefined;
992
947
  }>;
993
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_page_queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
994
- name: string;
995
- tableName: "search_appearance_page_queries";
996
- dataType: "string";
997
- data: string;
998
- driverParam: string;
999
- notNull: false;
1000
- hasDefault: false;
1001
- isPrimaryKey: false;
1002
- isAutoincrement: false;
1003
- hasRuntimeDefault: false;
1004
- enumValues: undefined;
1005
- identity: undefined;
1006
- generated: undefined;
1007
- }>;
1008
948
  date: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_page_queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
1009
949
  name: string;
1010
950
  tableName: "search_appearance_page_queries";
@@ -1267,21 +1207,6 @@ declare const drizzleSchema: {
1267
1207
  identity: undefined;
1268
1208
  generated: undefined;
1269
1209
  }>;
1270
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
1271
- name: string;
1272
- tableName: "queries";
1273
- dataType: "string";
1274
- data: string;
1275
- driverParam: string;
1276
- notNull: false;
1277
- hasDefault: false;
1278
- isPrimaryKey: false;
1279
- isAutoincrement: false;
1280
- hasRuntimeDefault: false;
1281
- enumValues: undefined;
1282
- identity: undefined;
1283
- generated: undefined;
1284
- }>;
1285
1210
  date: import("drizzle-orm/pg-core").PgBuildColumn<"queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
1286
1211
  name: string;
1287
1212
  tableName: "queries";
@@ -1461,21 +1386,6 @@ declare const drizzleSchema: {
1461
1386
  identity: undefined;
1462
1387
  generated: undefined;
1463
1388
  }>;
1464
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"page_queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
1465
- name: string;
1466
- tableName: "page_queries";
1467
- dataType: "string";
1468
- data: string;
1469
- driverParam: string;
1470
- notNull: false;
1471
- hasDefault: false;
1472
- isPrimaryKey: false;
1473
- isAutoincrement: false;
1474
- hasRuntimeDefault: false;
1475
- enumValues: undefined;
1476
- identity: undefined;
1477
- generated: undefined;
1478
- }>;
1479
1389
  date: import("drizzle-orm/pg-core").PgBuildColumn<"page_queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
1480
1390
  name: string;
1481
1391
  tableName: "page_queries";
@@ -1969,21 +1879,6 @@ declare const drizzleSchema: {
1969
1879
  identity: undefined;
1970
1880
  generated: undefined;
1971
1881
  }>;
1972
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
1973
- name: string;
1974
- tableName: "search_appearance_queries";
1975
- dataType: "string";
1976
- data: string;
1977
- driverParam: string;
1978
- notNull: false;
1979
- hasDefault: false;
1980
- isPrimaryKey: false;
1981
- isAutoincrement: false;
1982
- hasRuntimeDefault: false;
1983
- enumValues: undefined;
1984
- identity: undefined;
1985
- generated: undefined;
1986
- }>;
1987
1882
  date: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
1988
1883
  name: string;
1989
1884
  tableName: "search_appearance_queries";
@@ -2096,21 +1991,6 @@ declare const drizzleSchema: {
2096
1991
  identity: undefined;
2097
1992
  generated: undefined;
2098
1993
  }>;
2099
- query_canonical: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_page_queries", import("drizzle-orm/pg-core").PgVarcharBuilder<[string, ...string[]]>, {
2100
- name: string;
2101
- tableName: "search_appearance_page_queries";
2102
- dataType: "string";
2103
- data: string;
2104
- driverParam: string;
2105
- notNull: false;
2106
- hasDefault: false;
2107
- isPrimaryKey: false;
2108
- isAutoincrement: false;
2109
- hasRuntimeDefault: false;
2110
- enumValues: undefined;
2111
- identity: undefined;
2112
- generated: undefined;
2113
- }>;
2114
1994
  date: import("drizzle-orm/pg-core").PgBuildColumn<"search_appearance_page_queries", import("drizzle-orm/pg-core").SetNotNull<import("drizzle-orm/pg-core").PgDateStringBuilder>, {
2115
1995
  name: string;
2116
1996
  tableName: "search_appearance_page_queries";
@@ -14,7 +14,6 @@ const pages = pgTable("pages", {
14
14
  });
15
15
  const queries = pgTable("queries", {
16
16
  query: varchar("query").notNull(),
17
- query_canonical: varchar("query_canonical"),
18
17
  date: dateCol(),
19
18
  ...metricCols()
20
19
  });
@@ -26,7 +25,6 @@ const countries = pgTable("countries", {
26
25
  const page_queries = pgTable("page_queries", {
27
26
  url: varchar("url").notNull(),
28
27
  query: varchar("query").notNull(),
29
- query_canonical: varchar("query_canonical"),
30
28
  date: dateCol(),
31
29
  ...metricCols()
32
30
  });
@@ -60,7 +58,6 @@ const search_appearance_pages = pgTable("search_appearance_pages", {
60
58
  const search_appearance_queries = pgTable("search_appearance_queries", {
61
59
  searchAppearance: varchar("searchAppearance").notNull(),
62
60
  query: varchar("query").notNull(),
63
- query_canonical: varchar("query_canonical"),
64
61
  date: dateCol(),
65
62
  ...metricCols()
66
63
  });
@@ -68,7 +65,6 @@ const search_appearance_page_queries = pgTable("search_appearance_page_queries",
68
65
  searchAppearance: varchar("searchAppearance").notNull(),
69
66
  url: varchar("url").notNull(),
70
67
  query: varchar("query").notNull(),
71
- query_canonical: varchar("query_canonical"),
72
68
  date: dateCol(),
73
69
  ...metricCols()
74
70
  });
@@ -99,7 +95,7 @@ const TABLE_METADATA = {
99
95
  queries: {
100
96
  sortKey: ["date", "query"],
101
97
  clusterKey: ["query", "date"],
102
- version: 2
98
+ version: 3
103
99
  },
104
100
  countries: {
105
101
  sortKey: ["date", "country"],
@@ -117,7 +113,7 @@ const TABLE_METADATA = {
117
113
  "query",
118
114
  "date"
119
115
  ],
120
- version: 2
116
+ version: 3
121
117
  },
122
118
  dates: {
123
119
  sortKey: ["date"],
@@ -153,7 +149,7 @@ const TABLE_METADATA = {
153
149
  "query",
154
150
  "date"
155
151
  ],
156
- version: 1
152
+ version: 2
157
153
  },
158
154
  search_appearance_page_queries: {
159
155
  sortKey: [
@@ -168,7 +164,7 @@ const TABLE_METADATA = {
168
164
  "query",
169
165
  "date"
170
166
  ],
171
- version: 1
167
+ version: 2
172
168
  },
173
169
  hourly_pages: {
174
170
  sortKey: [
@@ -10,6 +10,7 @@ const ICEBERG_TABLES = [
10
10
  "search_appearance_queries",
11
11
  "search_appearance_page_queries"
12
12
  ];
13
+ const DEFAULT_PARTITION_KEY_ENCODING = "int";
13
14
  const SEARCH_TYPE_INT = {
14
15
  web: 1,
15
16
  image: 2,
@@ -30,7 +31,7 @@ const ICEBERG_PARTITION_COLUMNS = [{
30
31
  required: true,
31
32
  fieldId: 2
32
33
  }];
33
- function icebergPartitionColumns(encoding = "string") {
34
+ function icebergPartitionColumns(encoding = "int") {
34
35
  if (encoding === "string") return ICEBERG_PARTITION_COLUMNS;
35
36
  return [{
36
37
  name: "site_id",
@@ -71,7 +72,7 @@ function mapColumnType(t) {
71
72
  case "DATE": return "DATE";
72
73
  }
73
74
  }
74
- function icebergTableSpec(table, encoding = "string") {
75
+ function icebergTableSpec(table, encoding = "int") {
75
76
  const base = SCHEMAS[table];
76
77
  const dataColumns = base.columns.map((col, i) => ({
77
78
  name: col.name,
@@ -90,10 +91,11 @@ function icebergTableSpec(table, encoding = "string") {
90
91
  ]
91
92
  };
92
93
  }
93
- const ICEBERG_SCHEMAS = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, icebergTableSpec(t)]));
94
+ const ICEBERG_SCHEMAS_STRING = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, icebergTableSpec(t, "string")]));
94
95
  const ICEBERG_SCHEMAS_INT = Object.fromEntries(ICEBERG_TABLES.map((t) => [t, icebergTableSpec(t, "int")]));
95
- function icebergSchemasFor(encoding = "string") {
96
- return encoding === "int" ? ICEBERG_SCHEMAS_INT : ICEBERG_SCHEMAS;
96
+ const ICEBERG_SCHEMAS = ICEBERG_SCHEMAS_INT;
97
+ function icebergSchemasFor(encoding = "int") {
98
+ return encoding === "int" ? ICEBERG_SCHEMAS_INT : ICEBERG_SCHEMAS_STRING;
97
99
  }
98
100
  const ICEBERG_TABLE_SET = new Set(ICEBERG_TABLES);
99
101
  function isIcebergTable(table) {
@@ -103,4 +105,4 @@ function assertIcebergTable(table) {
103
105
  if (!isIcebergTable(table)) throw new Error(`Unknown Iceberg table '${table}'. Expected one of: ${ICEBERG_TABLES.join(", ")}`);
104
106
  return table;
105
107
  }
106
- export { ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable };
108
+ export { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable };
@@ -53,7 +53,7 @@ interface IcebergColumn {
53
53
  /**
54
54
  * Partition-key encoding for the two identity columns (`site_id`, `search_type`).
55
55
  *
56
- * - `'string'` (default, legacy): both columns are STRING. Correct, but R2 SQL's
56
+ * - `'string'` (legacy): both columns are STRING. Correct, but R2 SQL's
57
57
  * string min/max statistics are truncated in predicate pushdown, so a bare
58
58
  * `WHERE site_id='<uuid>'` UNDERCOUNTS — callers must CONCAT(col,'') to stay
59
59
  * correct, which defeats partition pruning.
@@ -66,10 +66,12 @@ interface IcebergColumn {
66
66
  * {@link SEARCH_TYPE_INT} for `search_type` (engine-owned, fixed enum).
67
67
  *
68
68
  * New per-team catalogs are provisioned `'int'`; existing catalogs stay
69
- * `'string'`. Purely additive: `'string'` is the default everywhere so existing
70
- * tables, writers, and readers are unchanged.
69
+ * `'string'`. Callers that read or write legacy catalogs must pass
70
+ * `encoding: 'string'` explicitly.
71
71
  */
72
72
  type PartitionKeyEncoding = 'string' | 'int';
73
+ /** Default for new Iceberg/R2 Data Catalog tables. */
74
+ declare const DEFAULT_PARTITION_KEY_ENCODING: PartitionKeyEncoding;
73
75
  /**
74
76
  * Stable `search_type` enum → int map for `'int'`-encoded catalogs. Engine-owned
75
77
  * and FROZEN: never renumber or reuse an id (it's the on-disk partition value).
@@ -142,11 +144,13 @@ declare const ICEBERG_PARTITION_SPEC: readonly IcebergPartitionField[];
142
144
  * source of truth — do not hand-list columns elsewhere.
143
145
  */
144
146
  declare function icebergTableSpec(table: IcebergTableName, encoding?: PartitionKeyEncoding): IcebergTableSpec;
145
- /** All Iceberg table specs (legacy `'string'` encoding), keyed by table name. */
146
- declare const ICEBERG_SCHEMAS: Record<IcebergTableName, IcebergTableSpec>;
147
+ /** All Iceberg table specs in legacy `'string'` encoding, keyed by table name. */
148
+ declare const ICEBERG_SCHEMAS_STRING: Record<IcebergTableName, IcebergTableSpec>;
147
149
  /** All Iceberg table specs in `'int'` encoding (INT site_id + INT search_type). */
148
150
  declare const ICEBERG_SCHEMAS_INT: Record<IcebergTableName, IcebergTableSpec>;
149
- /** Table specs for the given encoding (`'string'` default). */
151
+ /** All Iceberg table specs for the default new-catalog encoding. */
152
+ declare const ICEBERG_SCHEMAS: Record<IcebergTableName, IcebergTableSpec>;
153
+ /** Table specs for the given encoding (`'int'` default). */
150
154
  declare function icebergSchemasFor(encoding?: PartitionKeyEncoding): Record<IcebergTableName, IcebergTableSpec>;
151
155
  /** True when `table` is one of the canonical {@link ICEBERG_TABLES}. */
152
156
  declare function isIcebergTable(table: string): table is IcebergTableName;
@@ -385,7 +389,8 @@ interface ListIcebergDataFilesOptions {
385
389
  /**
386
390
  * Partition-key encoding of the catalog. `'int'` changes how manifest-summary
387
391
  * bounds are decoded (int bytes vs UTF-8) and how the per-file partition value
388
- * is compared. Defaults to `'string'`.
392
+ * is compared. Defaults to `'int'` for new catalogs; pass `'string'` for
393
+ * legacy catalogs.
389
394
  */
390
395
  encoding?: PartitionKeyEncoding;
391
396
  /**
@@ -542,10 +547,11 @@ interface IcebergAppendSinkOptions extends SinkOptions {
542
547
  */
543
548
  commitRetry?: CommitRetryOptions;
544
549
  /**
545
- * Partition-key encoding (default `'string'`). `'int'` writes BOTH `site_id`
546
- * and `search_type` as INT — the caller MUST pass the numeric `site_id` (a
547
- * numeric string is fine; it's `Number()`-coerced) in `slice.ctx.siteId`. A
548
- * small INT is ample (≪ 2.1B sites), so no LONG/BigInt is involved. See
550
+ * Partition-key encoding (default `'int'` for new catalogs). `'int'` writes
551
+ * BOTH `site_id` and `search_type` as INT — the caller MUST pass the numeric
552
+ * `site_id` (a numeric string is fine; it's `Number()`-coerced) in
553
+ * `slice.ctx.siteId`. Pass `'string'` explicitly for legacy catalogs. A small
554
+ * INT is ample (≪ 2.1B sites), so no LONG/BigInt is involved. See
549
555
  * {@link import('./iceberg/schema').PartitionKeyEncoding}.
550
556
  */
551
557
  encoding?: PartitionKeyEncoding;
@@ -559,4 +565,4 @@ interface LocalIcebergSinkOptions extends SinkOptions {
559
565
  /** S3-compatible warehouse location (POC: MinIO). */
560
566
  warehouse: string;
561
567
  }
562
- export { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_TABLES, INT_SEARCH_TYPE, IcebergAppendSinkOptions, IcebergCatalogConfig, IcebergColumn, IcebergColumnType, IcebergConnection, IcebergListedDataFile, IcebergPartitionField, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPartitionTransform, IcebergPrimitiveType, IcebergS3Config, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableName, IcebergTableOpResult, IcebergTableSpec, ListIcebergDataFilesOptions, LocalIcebergSinkOptions, PartitionKeyEncoding, SEARCH_TYPE_INT, Sink, SinkCapabilities, SinkCloseResult, SinkOptions, SinkSlice, SinkWriteResult, SliceOverwriteWriter, assertIcebergTable, connectIcebergCatalog, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables };
568
+ export { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, IcebergAppendSinkOptions, IcebergCatalogConfig, IcebergColumn, IcebergColumnType, IcebergConnection, IcebergListedDataFile, IcebergPartitionField, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPartitionTransform, IcebergPrimitiveType, IcebergS3Config, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableName, IcebergTableOpResult, IcebergTableSpec, ListIcebergDataFilesOptions, LocalIcebergSinkOptions, PartitionKeyEncoding, SEARCH_TYPE_INT, Sink, SinkCapabilities, SinkCloseResult, SinkOptions, SinkSlice, SinkWriteResult, SliceOverwriteWriter, assertIcebergTable, connectIcebergCatalog, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables };
@@ -10,6 +10,9 @@ interface ResolverAdapter<TableKey extends string = string> {
10
10
  dimColumn: (dim: Dimension, tableKey: TableKey) => string;
11
11
  isMetricDimension: (dim: string) => dim is Metric;
12
12
  tableRef: (tableKey: TableKey) => SQL;
13
+ fromSql: (tableKey: TableKey, options?: {
14
+ queryCanonical?: boolean;
15
+ }) => SQL;
13
16
  dateColRef: (tableKey: TableKey) => SQL;
14
17
  urlToPathExpr: (col: string) => string;
15
18
  siteIdColRef?: (tableKey: TableKey) => SQL;
@@ -1,4 +1,4 @@
1
- import { SCHEMAS, TABLE_METADATA, dedupeByNaturalKey } from "../_chunks/schema.mjs";
1
+ import { SCHEMAS, TABLE_METADATA } from "../_chunks/schema.mjs";
2
2
  import { parquetReadObjects } from "../_chunks/libs/hyparquet.mjs";
3
3
  import { ByteWriter, parquetWriteRows } from "../_chunks/libs/hyparquet-writer.mjs";
4
4
  const ROW_GROUP_SIZE = 25e3;
@@ -119,6 +119,9 @@ function sortRowsByClusterKey(table, rows) {
119
119
  });
120
120
  return copy;
121
121
  }
122
+ function naturalKeyFor(table, row) {
123
+ return TABLE_METADATA[table].sortKey.map((col) => `${row[col] ?? ""}`).join("\0");
124
+ }
122
125
  function encodeOrderedRows(rows, columns, rowGroupSize) {
123
126
  const schema = buildWriteSchema(columns);
124
127
  const isDate = columns.map((col) => col.type === "DATE");
@@ -222,12 +225,12 @@ function createHyparquetCodec(options = {}) {
222
225
  rowCount: 0
223
226
  };
224
227
  }
225
- const allRows = [];
228
+ const byNaturalKey = /* @__PURE__ */ new Map();
226
229
  for (const key of inputKeys) {
227
230
  const rows = await decodeParquetToRows(await dataSource.read(key));
228
- for (let i = 0; i < rows.length; i++) allRows.push(rows[i]);
231
+ for (let i = 0; i < rows.length; i++) byNaturalKey.set(naturalKeyFor(ctx.table, rows[i]), rows[i]);
229
232
  }
230
- const rows = dedupeByNaturalKey(ctx.table, allRows);
233
+ const rows = [...byNaturalKey.values()];
231
234
  const bytes = encodeRowsToParquet(ctx.table, rows);
232
235
  await dataSource.write(outputKey, bytes);
233
236
  return {
@@ -4,10 +4,26 @@ import { err, ok, unwrapResult } from "gscdump/result";
4
4
  const SHARD_RE = /^u_[^/]+\/manifest\/(?<siteId>[^/]+)\/(?<table>[^/]+)\/HEAD$/;
5
5
  const CAS_BACKOFF_BASE_MS = 5;
6
6
  const CAS_BACKOFF_CAP_MS = 250;
7
+ const SHARD_IO_CONCURRENCY = 8;
7
8
  async function casBackoff(attempt) {
8
9
  const ceil = Math.min(CAS_BACKOFF_CAP_MS, CAS_BACKOFF_BASE_MS * 2 ** attempt);
9
10
  await new Promise((resolve) => setTimeout(resolve, Math.random() * ceil));
10
11
  }
12
+ async function mapWithConcurrency(items, concurrency, fn) {
13
+ if (items.length === 0) return [];
14
+ const workerCount = Math.max(1, Math.min(items.length, Math.floor(concurrency)));
15
+ const results = Array.from({ length: items.length }, () => void 0);
16
+ let nextIndex = 0;
17
+ async function worker() {
18
+ while (true) {
19
+ const index = nextIndex++;
20
+ if (index >= items.length) return;
21
+ results[index] = await fn(items[index], index);
22
+ }
23
+ }
24
+ await Promise.all(Array.from({ length: workerCount }, worker));
25
+ return results;
26
+ }
11
27
  function defaultSnapshotId() {
12
28
  return `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
13
29
  }
@@ -155,16 +171,15 @@ function createR2ManifestStore(opts) {
155
171
  return (await listShards()).filter((s) => (filter.siteId === void 0 || s.siteId === filter.siteId) && (filter.table === void 0 || s.table === filter.table));
156
172
  }
157
173
  async function readEntriesAcrossShards(filter, includeRetired) {
158
- const shards = await shardsForFilter(filter);
159
- const all = [];
160
- for (const { siteId, table } of shards) {
174
+ return (await mapWithConcurrency(await shardsForFilter(filter), SHARD_IO_CONCURRENCY, async ({ siteId, table }) => {
161
175
  const { snapshot } = await readShard(siteId, table);
176
+ const entries = [];
162
177
  for (const entry of snapshot.entries) {
163
178
  if (!includeRetired && entry.retiredAt !== void 0) continue;
164
- if (matchesEntryFilter(entry, filter)) all.push(entry);
179
+ if (matchesEntryFilter(entry, filter)) entries.push(entry);
165
180
  }
166
- }
167
- return all;
181
+ return entries;
182
+ })).flat();
168
183
  }
169
184
  function groupBySiteTable(entries) {
170
185
  const out = /* @__PURE__ */ new Map();
@@ -195,7 +210,7 @@ function createR2ManifestStore(opts) {
195
210
  }
196
211
  for (const e of newEntries) bucket(e, "new");
197
212
  if (superseding) for (const e of superseding) bucket(e, "super");
198
- for (const [shardKey, { newEntries: news, superseding: supers }] of byShard) {
213
+ await mapWithConcurrency([...byShard], SHARD_IO_CONCURRENCY, async ([shardKey, { newEntries: news, superseding: supers }]) => {
199
214
  const [siteId, table] = shardKey.split("\0");
200
215
  await mutateShard(siteId, table, (snap) => {
201
216
  const byObjectKey = new Map(snap.entries.map((e) => [e.objectKey, e]));
@@ -209,7 +224,7 @@ function createR2ManifestStore(opts) {
209
224
  for (const n of news) byObjectKey.set(n.objectKey, n);
210
225
  snap.entries = Array.from(byObjectKey.values());
211
226
  });
212
- }
227
+ });
213
228
  }
214
229
  return {
215
230
  async listLive(filter) {
@@ -226,32 +241,29 @@ function createR2ManifestStore(opts) {
226
241
  return registerVersionsImpl(entries, superseding);
227
242
  },
228
243
  async listRetired(olderThan) {
229
- const shards = await listShards();
230
- const out = [];
231
- for (const { siteId, table } of shards) {
244
+ return (await mapWithConcurrency(await listShards(), SHARD_IO_CONCURRENCY, async ({ siteId, table }) => {
232
245
  const { snapshot } = await readShard(siteId, table);
233
- for (const e of snapshot.entries) if (e.retiredAt !== void 0 && e.retiredAt <= olderThan) out.push(e);
234
- }
235
- return out;
246
+ const retired = [];
247
+ for (const e of snapshot.entries) if (e.retiredAt !== void 0 && e.retiredAt <= olderThan) retired.push(e);
248
+ return retired;
249
+ })).flat();
236
250
  },
237
251
  async delete(toDelete) {
238
- const grouped = groupBySiteTable(toDelete);
239
- for (const [shardKey, entries] of grouped) {
252
+ await mapWithConcurrency([...groupBySiteTable(toDelete)], SHARD_IO_CONCURRENCY, async ([shardKey, entries]) => {
240
253
  const [siteId, table] = shardKey.split("\0");
241
254
  await mutateShard(siteId, table, (snap) => {
242
255
  const drop = new Set(entries.map((e) => e.objectKey));
243
256
  snap.entries = snap.entries.filter((e) => !drop.has(e.objectKey));
244
257
  });
245
- }
258
+ });
246
259
  },
247
260
  async getWatermarks(filter) {
248
- const shards = await shardsForFilter(filter);
249
- const out = [];
250
- for (const { siteId, table } of shards) {
261
+ return (await mapWithConcurrency(await shardsForFilter(filter), SHARD_IO_CONCURRENCY, async ({ siteId, table }) => {
251
262
  const { snapshot } = await readShard(siteId, table);
252
- for (const w of snapshot.watermarks) if (matchesWatermarkFilter(w, filter)) out.push(w);
253
- }
254
- return out;
263
+ const watermarks = [];
264
+ for (const w of snapshot.watermarks) if (matchesWatermarkFilter(w, filter)) watermarks.push(w);
265
+ return watermarks;
266
+ })).flat();
255
267
  },
256
268
  async bumpWatermark(scope, date, at) {
257
269
  if (scope.siteId === void 0) throw new Error("R2 manifest store requires watermarks to carry siteId");
@@ -282,13 +294,12 @@ function createR2ManifestStore(opts) {
282
294
  });
283
295
  },
284
296
  async getSyncStates(filter) {
285
- const shards = await shardsForFilter(filter);
286
- const out = [];
287
- for (const { siteId, table } of shards) {
297
+ return (await mapWithConcurrency(await shardsForFilter(filter), SHARD_IO_CONCURRENCY, async ({ siteId, table }) => {
288
298
  const { snapshot } = await readShard(siteId, table);
289
- for (const s of snapshot.syncStates) if (matchesSyncStateFilter(s, filter)) out.push(s);
290
- }
291
- return out;
299
+ const states = [];
300
+ for (const s of snapshot.syncStates) if (matchesSyncStateFilter(s, filter)) states.push(s);
301
+ return states;
302
+ })).flat();
292
303
  },
293
304
  async setSyncState(scope, state, detail) {
294
305
  if (scope.siteId === void 0) throw new Error("R2 manifest store requires sync states to carry siteId");
@@ -34,8 +34,8 @@ interface QueryDimDeps {
34
34
  }
35
35
  /**
36
36
  * Pure: distinct raw queries → dimension records. De-dupes, drops empties, and
37
- * folds an empty/whitespace canonical back to the raw query so the key is
38
- * total (matches the read path's `COALESCE(NULLIF(query_canonical, ''), query)`).
37
+ * folds an empty/whitespace canonical back to the raw query so the dimension
38
+ * is total for read-time joins.
39
39
  */
40
40
  declare function buildQueryDimRecords(queries: Iterable<string>, deps: QueryDimDeps): QueryDimRecord[];
41
41
  interface QueryDimStore {
@@ -1,4 +1,4 @@
1
- import { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_TABLES, INT_SEARCH_TYPE, IcebergAppendSinkOptions, IcebergCatalogConfig, IcebergColumn, IcebergColumnType, IcebergConnection, IcebergListedDataFile, IcebergPartitionField, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPartitionTransform, IcebergPrimitiveType, IcebergS3Config, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableName, IcebergTableOpResult, IcebergTableSpec, ListIcebergDataFilesOptions, PartitionKeyEncoding, SEARCH_TYPE_INT, Sink, assertIcebergTable, connectIcebergCatalog, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables } from "../_chunks/sink.mjs";
1
+ import { CatalogCache, CommitRetryOptions, ConnectIcebergOptions, DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, IcebergAppendSinkOptions, IcebergCatalogConfig, IcebergColumn, IcebergColumnType, IcebergConnection, IcebergListedDataFile, IcebergPartitionField, IcebergPartitionSpec, IcebergPartitionSpecField, IcebergPartitionTransform, IcebergPrimitiveType, IcebergS3Config, IcebergSchema, IcebergSchemaField, IcebergSortOrder, IcebergSortOrderField, IcebergTableName, IcebergTableOpResult, IcebergTableSpec, ListIcebergDataFilesOptions, PartitionKeyEncoding, SEARCH_TYPE_INT, Sink, assertIcebergTable, connectIcebergCatalog, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables } from "../_chunks/sink.mjs";
2
2
  import { icebergCreateTable, icebergManifests, restCatalogLoadTable } from "../_chunks/libs/icebird.mjs";
3
3
  type IcebergAppendSink = Sink;
4
4
  /**
@@ -10,4 +10,4 @@ type IcebergAppendSink = Sink;
10
10
  * with no rows never touches the network.
11
11
  */
12
12
  declare function createIcebergAppendSink(options: IcebergAppendSinkOptions): IcebergAppendSink;
13
- export { type CatalogCache, type CommitRetryOptions, type ConnectIcebergOptions, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_TABLES, INT_SEARCH_TYPE, type IcebergAppendSink, type IcebergAppendSinkOptions, type IcebergCatalogConfig, type IcebergColumn, type IcebergColumnType, type IcebergConnection, type IcebergListedDataFile, type IcebergPartitionField, type IcebergPartitionSpec, type IcebergPartitionSpecField, type IcebergPartitionTransform, type IcebergPrimitiveType, type IcebergS3Config, type IcebergSchema, type IcebergSchemaField, type IcebergSortOrder, type IcebergSortOrderField, type IcebergTableName, type IcebergTableOpResult, type IcebergTableSpec, type ListIcebergDataFilesOptions, type PartitionKeyEncoding, SEARCH_TYPE_INT, assertIcebergTable, connectIcebergCatalog, createIcebergAppendSink, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergCreateTable, icebergManifests, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables, restCatalogLoadTable };
13
+ export { type CatalogCache, type CommitRetryOptions, type ConnectIcebergOptions, DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, type IcebergAppendSink, type IcebergAppendSinkOptions, type IcebergCatalogConfig, type IcebergColumn, type IcebergColumnType, type IcebergConnection, type IcebergListedDataFile, type IcebergPartitionField, type IcebergPartitionSpec, type IcebergPartitionSpecField, type IcebergPartitionTransform, type IcebergPrimitiveType, type IcebergS3Config, type IcebergSchema, type IcebergSchemaField, type IcebergSortOrder, type IcebergSortOrderField, type IcebergTableName, type IcebergTableOpResult, type IcebergTableSpec, type ListIcebergDataFilesOptions, type PartitionKeyEncoding, SEARCH_TYPE_INT, assertIcebergTable, connectIcebergCatalog, createIcebergAppendSink, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergCreateTable, icebergManifests, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables, restCatalogLoadTable };
@@ -1,6 +1,6 @@
1
1
  import { TABLE_METADATA } from "../_chunks/schema.mjs";
2
2
  import { engineErrors } from "../errors.mjs";
3
- import { ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable } from "../_chunks/schema2.mjs";
3
+ import { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, icebergPartitionColumns, icebergSchemasFor, icebergTableSpec, isIcebergTable } from "../_chunks/schema2.mjs";
4
4
  import { cachingResolver, icebergAppend, icebergCreateTable, icebergDropTable, icebergManifests, restCatalogConnect, restCatalogCreateNamespace, restCatalogListTables, restCatalogLoadTable, s3SignedResolver } from "../_chunks/libs/icebird.mjs";
5
5
  import { err, ok } from "gscdump/result";
6
6
  async function cacheGet(cache, key, now) {
@@ -36,7 +36,7 @@ function decodeInt(bytes) {
36
36
  if (u == null) return null;
37
37
  return new DataView(u.buffer, u.byteOffset, u.byteLength).getInt32(0, true);
38
38
  }
39
- function buildPartitionFilter(siteId, searchType, wantedMonths, encoding = "string") {
39
+ function buildPartitionFilter(siteId, searchType, wantedMonths, encoding = "int") {
40
40
  return (partitions) => {
41
41
  const parts = partitions;
42
42
  if (!parts || parts.length === 0) return true;
@@ -79,7 +79,7 @@ const ICEBERG_TYPE_MAP = {
79
79
  DOUBLE: "double",
80
80
  DATE: "date"
81
81
  };
82
- function icebergSchemaFor(table, encoding = "string") {
82
+ function icebergSchemaFor(table, encoding = "int") {
83
83
  return {
84
84
  "type": "struct",
85
85
  "schema-id": 0,
@@ -91,7 +91,7 @@ function icebergSchemaFor(table, encoding = "string") {
91
91
  }))
92
92
  };
93
93
  }
94
- function icebergPartitionSpecFor(table, encoding = "string") {
94
+ function icebergPartitionSpecFor(table, encoding = "int") {
95
95
  const fields = icebergSchemasFor(encoding)[table].columns;
96
96
  const fieldId = (name) => {
97
97
  const col = fields.find((c) => c.name === name);
@@ -108,7 +108,7 @@ function icebergPartitionSpecFor(table, encoding = "string") {
108
108
  }))
109
109
  };
110
110
  }
111
- function icebergSortOrderFor(table, encoding = "string") {
111
+ function icebergSortOrderFor(table, encoding = "int") {
112
112
  const fields = icebergSchemasFor(encoding)[table].columns;
113
113
  const fieldId = (name) => {
114
114
  const col = fields.find((c) => c.name === name);
@@ -230,7 +230,7 @@ async function appendAlreadyLanded(args, appendId) {
230
230
  async function ensureIcebergNamespace(conn) {
231
231
  await restCatalogCreateNamespace(conn.catalog, { namespace: conn.namespace }).catch(() => {});
232
232
  }
233
- async function createIcebergTables(conn, tables = ICEBERG_TABLES, encoding = "string") {
233
+ async function createIcebergTables(conn, tables = ICEBERG_TABLES, encoding = "int") {
234
234
  const results = [];
235
235
  for (const table of tables) await icebergCreateTable({
236
236
  catalog: conn.catalog,
@@ -344,7 +344,7 @@ async function listIcebergDataFiles(conn, opts) {
344
344
  if (snapshotId == null || !metadata) return [];
345
345
  }
346
346
  const endWalk = profiler?.start("iceberg.walk");
347
- const partitionFilter = buildPartitionFilter(opts.siteId, opts.searchType, wantedMonths, opts.encoding ?? "string");
347
+ const partitionFilter = buildPartitionFilter(opts.siteId, opts.searchType, wantedMonths, opts.encoding ?? "int");
348
348
  const manifests = await icebergManifests({
349
349
  metadata,
350
350
  resolver: conn.resolver,
@@ -466,7 +466,7 @@ function toRecords(slice, rows, encoding) {
466
466
  }
467
467
  function createIcebergAppendSink(options) {
468
468
  let connection;
469
- const encoding = options.encoding ?? "string";
469
+ const encoding = options.encoding ?? "int";
470
470
  const buffers = /* @__PURE__ */ new Map();
471
471
  function connect() {
472
472
  connection ??= connectIcebergCatalog(options.catalog);
@@ -530,4 +530,4 @@ function createIcebergAppendSink(options) {
530
530
  }
531
531
  };
532
532
  }
533
- export { ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, connectIcebergCatalog, createIcebergAppendSink, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergCreateTable, icebergManifests, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables, restCatalogLoadTable };
533
+ export { DEFAULT_PARTITION_KEY_ENCODING, ICEBERG_FIELD_ID_BASE, ICEBERG_PARTITION_COLUMNS, ICEBERG_PARTITION_SPEC, ICEBERG_SCHEMAS, ICEBERG_SCHEMAS_INT, ICEBERG_SCHEMAS_STRING, ICEBERG_TABLES, INT_SEARCH_TYPE, SEARCH_TYPE_INT, assertIcebergTable, connectIcebergCatalog, createIcebergAppendSink, createIcebergTables, dropIcebergTables, ensureIcebergNamespace, icebergAppendRetrying, icebergCreateTable, icebergManifests, icebergPartitionColumns, icebergPartitionSpecFor, icebergSchemaFor, icebergSchemasFor, icebergSortOrderFor, icebergTableSpec, isCommitRateLimited, isIcebergTable, listIcebergDataFiles, listIcebergTables, restCatalogLoadTable };