@gscdump/engine 7.8.4 → 7.9.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,4 +1,4 @@
1
- import { ComparisonFilter, ComparisonOrderBy, ExtraQuery, ResolvedComparisonSQL, ResolvedSQL, ResolvedSQLOptimized, ResolverOptions } from "./types.mjs";
1
+ import { ComparisonFilter, ComparisonOrderBy, ExtraQuery, ResolvedComparisonSQL, ResolvedComparisonTotalsSQL, ResolvedSQL, ResolvedSQLOptimized, ResolverOptions } from "./types.mjs";
2
2
  import { BuilderState } from "gscdump/query";
3
3
  export declare function resolveToSQLOptimized<TK extends string>(state: BuilderState, options: ResolverOptions<TK>): ResolvedSQLOptimized;
4
4
  export declare function resolveToSQL<TK extends string>(state: BuilderState, options: ResolverOptions<TK>): ResolvedSQL;
@@ -6,7 +6,43 @@ export declare function buildTotalsSql<TK extends string>(state: BuilderState, o
6
6
  sql: string;
7
7
  params: unknown[];
8
8
  };
9
+ /**
10
+ * Totals SQL for both windows of a comparison read, as one statement when it
11
+ * can be. Pass the two dimensionless states that {@link buildTotalsSql} would
12
+ * receive; their filters choose the table.
13
+ *
14
+ * The `Combined` statement scans both date ranges once. Each metric sums only
15
+ * the rows of its own window, so days between the windows count in neither
16
+ * total, and days that both windows contain count in both. Its one row holds
17
+ * the current metrics under their own names and the previous metrics as
18
+ * `prevClicks`, `prevImpressions`, `prevCtr`, and `prevPosition`. Read it
19
+ * with {@link splitComparisonTotalsRow}. It is a single-table aggregate with
20
+ * no GROUP BY, the same shape as a `buildTotalsSql` statement.
21
+ *
22
+ * If the windows read different tables or filter different rows, the result
23
+ * is `Separate`: the two `buildTotalsSql` statements.
24
+ */
25
+ export declare function buildComparisonTotalsSql<TK extends string>(current: BuilderState, previous: BuilderState, options: ResolverOptions<TK>): ResolvedComparisonTotalsSQL;
26
+ /**
27
+ * Split the row of a `Combined` {@link buildComparisonTotalsSql} statement
28
+ * into the metrics of each window. A metric the statement did not select stays
29
+ * absent. With no row, both windows are empty.
30
+ */
31
+ export declare function splitComparisonTotalsRow(row: Record<string, unknown> | undefined): {
32
+ current: Record<string, unknown>;
33
+ previous: Record<string, unknown>;
34
+ };
9
35
  export declare function resolveComparisonSQL<TK extends string>(current: BuilderState, previous: BuilderState, options: ResolverOptions<TK>, comparisonFilter?: ComparisonFilter, comparisonOrderBy?: ComparisonOrderBy): ResolvedComparisonSQL;
36
+ /**
37
+ * The comparison row query of {@link resolveComparisonSQL}, with the count in
38
+ * the same statement. Every row carries `totalCount`: the number of joined
39
+ * rows that pass the comparison filter, before LIMIT and OFFSET.
40
+ *
41
+ * Only a row can carry the count. Without OFFSET, an empty result means
42
+ * `totalCount` is 0. With an OFFSET past the last row, run the count query of
43
+ * {@link resolveComparisonSQL} instead.
44
+ */
45
+ export declare function resolveComparisonSQLOptimized<TK extends string>(current: BuilderState, previous: BuilderState, options: ResolverOptions<TK>, comparisonFilter?: ComparisonFilter, comparisonOrderBy?: ComparisonOrderBy): ResolvedSQLOptimized;
10
46
  /**
11
47
  * Enrich Canonical Query groups with their raw variants.
12
48
  * Supply selected keys to limit aggregation to the current page.
@@ -110,12 +110,13 @@ function buildScope(state, options) {
110
110
  if (adapter.searchTypeColRef && searchType != null) wherePredicates.push(sql`${adapter.searchTypeColRef(tableKey)} = ${searchType}`);
111
111
  wherePredicates.push(sql`${adapter.dateColRef(tableKey)} >= ${plan.dateRange.startDate}`);
112
112
  wherePredicates.push(sql`${adapter.dateColRef(tableKey)} <= ${plan.dateRange.endDate}`);
113
- wherePredicates.push(...adapter.prefilterPredicates(prefilters, tableKey));
113
+ const filterPredicates = [...adapter.prefilterPredicates(prefilters, tableKey)];
114
114
  const dimSql = plan.dimensionFilterTree ? compileFilterTree(plan.dimensionFilterTree, adapter, tableKey) : void 0;
115
- if (dimSql) wherePredicates.push(dimSql);
116
- else if (!plan.dimensionFilterTree) wherePredicates.push(...adapter.dimensionPredicates(dimFilters, tableKey));
115
+ if (dimSql) filterPredicates.push(dimSql);
116
+ else if (!plan.dimensionFilterTree) filterPredicates.push(...adapter.dimensionPredicates(dimFilters, tableKey));
117
117
  const tl = adapter.topLevelPredicate(topLevelFilters(plan), tableKey);
118
- if (tl) wherePredicates.push(tl);
118
+ if (tl) filterPredicates.push(tl);
119
+ wherePredicates.push(...filterPredicates);
119
120
  return {
120
121
  plan,
121
122
  tableKey,
@@ -123,6 +124,7 @@ function buildScope(state, options) {
123
124
  hasDate,
124
125
  metrics,
125
126
  wherePredicates,
127
+ filterPredicates,
126
128
  having: adapter.havingPredicates(metricFilters, tableKey),
127
129
  dimFilters,
128
130
  startDate: plan.dateRange.startDate,
@@ -250,7 +252,77 @@ function buildTotalsSql(state, options) {
250
252
  const selectExprs = metrics.map((m) => sql`${adapter.metricSql(m, tableKey)} as ${aliasRaw(m)}`);
251
253
  return compileCollapsed(adapter, wherePredicates.length > 0 ? sql`SELECT ${joinComma(selectExprs)} FROM ${table} WHERE ${joinAnd(wherePredicates)}` : sql`SELECT ${joinComma(selectExprs)} FROM ${table}`);
252
254
  }
253
- function resolveComparisonSQL(current, previous, options, comparisonFilter, comparisonOrderBy) {
255
+ const PREVIOUS_TOTALS_ALIAS = {
256
+ clicks: "prevClicks",
257
+ impressions: "prevImpressions",
258
+ ctr: "prevCtr",
259
+ position: "prevPosition"
260
+ };
261
+ function windowMetricSql(metric, columns, inWindow) {
262
+ const sum = (column) => sql`SUM(CASE WHEN ${inWindow} THEN ${columns[column]} END)`;
263
+ switch (metric) {
264
+ case "clicks": return sql`CAST(${sum("clicks")} AS DOUBLE)`;
265
+ case "impressions": return sql`CAST(${sum("impressions")} AS DOUBLE)`;
266
+ case "ctr": return sql`CAST(${sum("clicks")} AS DOUBLE) / NULLIF(${sum("impressions")}, 0)`;
267
+ case "position": return sql`${sum("sum_position")} / NULLIF(${sum("impressions")}, 0) + 1`;
268
+ }
269
+ }
270
+ function sharesTotalsScope(adapter, current, previous) {
271
+ if (current.tableKey !== previous.tableKey || current.queryCanonicalUsed !== previous.queryCanonicalUsed) return false;
272
+ const left = adapter.compile(joinAnd(current.filterPredicates));
273
+ const right = adapter.compile(joinAnd(previous.filterPredicates));
274
+ return left.sql === right.sql && left.params.length === right.params.length && left.params.every((value, index) => Object.is(value, right.params[index]));
275
+ }
276
+ function buildComparisonTotalsSql(current, previous, options) {
277
+ const { adapter, siteId, searchType } = options;
278
+ const currentScope = buildScope(withMinimumImpressions(current, options), options);
279
+ const previousScope = buildScope(withMinimumImpressions(previous, options), options);
280
+ if (!sharesTotalsScope(adapter, currentScope, previousScope)) return {
281
+ _tag: "Separate",
282
+ current: buildTotalsSql(current, options),
283
+ previous: buildTotalsSql(previous, options)
284
+ };
285
+ const { tableKey } = currentScope;
286
+ const columns = adapter.schema[tableKey];
287
+ const dateCol = adapter.dateColRef(tableKey);
288
+ const currentRange = currentScope.plan.dateRange;
289
+ const previousRange = previousScope.plan.dateRange;
290
+ const inWindow = (range) => sql`${dateCol} >= ${range.startDate} AND ${dateCol} <= ${range.endDate}`;
291
+ const selectExprs = [...currentScope.metrics.map((m) => sql`${windowMetricSql(m, columns, inWindow(currentRange))} as ${aliasRaw(m)}`), ...previousScope.metrics.map((m) => sql`${windowMetricSql(m, columns, inWindow(previousRange))} as ${aliasRaw(PREVIOUS_TOTALS_ALIAS[m])}`)];
292
+ const wherePredicates = [];
293
+ if (adapter.siteIdColRef && siteId != null) wherePredicates.push(sql`${adapter.siteIdColRef(tableKey)} = ${siteId}`);
294
+ if (adapter.searchTypeColRef && searchType != null) wherePredicates.push(sql`${adapter.searchTypeColRef(tableKey)} = ${searchType}`);
295
+ const spanStart = currentRange.startDate < previousRange.startDate ? currentRange.startDate : previousRange.startDate;
296
+ const spanEnd = currentRange.endDate > previousRange.endDate ? currentRange.endDate : previousRange.endDate;
297
+ wherePredicates.push(sql`${dateCol} >= ${spanStart}`);
298
+ wherePredicates.push(sql`${dateCol} <= ${spanEnd}`);
299
+ wherePredicates.push(sql`((${inWindow(currentRange)}) OR (${inWindow(previousRange)}))`);
300
+ wherePredicates.push(...currentScope.filterPredicates);
301
+ const table = adapter.fromSql(tableKey, { queryCanonical: currentScope.queryCanonicalUsed });
302
+ const compiled = compileCollapsed(adapter, sql`SELECT ${joinComma(selectExprs)} FROM ${table} WHERE ${joinAnd(wherePredicates)}`);
303
+ return {
304
+ _tag: "Combined",
305
+ sql: compiled.sql,
306
+ params: compiled.params
307
+ };
308
+ }
309
+ function splitComparisonTotalsRow(row) {
310
+ const current = {};
311
+ const previous = {};
312
+ if (!row) return {
313
+ current,
314
+ previous
315
+ };
316
+ for (const [metric, previousAlias] of Object.entries(PREVIOUS_TOTALS_ALIAS)) {
317
+ if (metric in row) current[metric] = row[metric];
318
+ if (previousAlias in row) previous[metric] = row[previousAlias];
319
+ }
320
+ return {
321
+ current,
322
+ previous
323
+ };
324
+ }
325
+ function buildComparisonQueries(current, previous, options, comparisonFilter, comparisonOrderBy, windowCount) {
254
326
  const { adapter, siteId, searchType } = options;
255
327
  const comparisonPlan = buildComparisonPlan(current, previous, adapter.capabilities);
256
328
  const currentScope = buildScope(current, options);
@@ -295,12 +367,18 @@ function resolveComparisonSQL(current, previous, options, comparisonFilter, comp
295
367
  outerCurrentCols.push(sql.raw("COALESCE(c.ctr, 0) as \"ctr\""));
296
368
  outerCurrentCols.push(sql.raw("COALESCE(c.position, 0) as \"position\""));
297
369
  if (changeOrder) outerCurrentCols.push(changeOrder.column);
298
- const mainQuery = sql`WITH current AS (${currentCte}), previous AS (${previousCte}) SELECT ${joinComma(outerCurrentCols)}, COALESCE(CAST(p.clicks AS DOUBLE), 0) as "prevClicks", COALESCE(CAST(p.impressions AS DOUBLE), 0) as "prevImpressions", COALESCE(p.ctr, 0) as "prevCtr", COALESCE(p.position, 0) as "prevPosition" FROM current c FULL OUTER JOIN previous p ON ${joinOn} WHERE 1=1 ${filterClause} ${orderSql} ${limitSql}`;
370
+ const mainQuery = sql`WITH current AS (${currentCte}), previous AS (${previousCte}) SELECT ${joinComma(outerCurrentCols)}, COALESCE(CAST(p.clicks AS DOUBLE), 0) as "prevClicks", COALESCE(CAST(p.impressions AS DOUBLE), 0) as "prevImpressions", COALESCE(p.ctr, 0) as "prevCtr", COALESCE(p.position, 0) as "prevPosition"${windowCount ? sql.raw(", COUNT(*) OVER() as \"totalCount\"") : sql.raw("")} FROM current c FULL OUTER JOIN previous p ON ${joinOn} WHERE 1=1 ${filterClause} ${orderSql} ${limitSql}`;
299
371
  const firstGroupBy = groupByDims[0] ? groupByDims[0].replace(/\W/g, "") : "clicks";
300
372
  const countInnerSelect = groupByDims[0] ? sql.raw(`COALESCE(c.${firstGroupBy}, p.${firstGroupBy})`) : sql.raw(`c.${firstGroupBy}`);
301
- const countQuery = sql`WITH current AS (${currentCte}), previous AS (${previousCte}) SELECT COUNT(*) as total FROM (SELECT ${countInnerSelect} FROM current c FULL OUTER JOIN previous p ON ${joinOn} WHERE 1=1 ${filterClause})`;
302
- const main = compileCollapsed(adapter, mainQuery);
303
- const count = compileCollapsed(adapter, countQuery);
373
+ return {
374
+ main: mainQuery,
375
+ count: sql`WITH current AS (${currentCte}), previous AS (${previousCte}) SELECT COUNT(*) as total FROM (SELECT ${countInnerSelect} FROM current c FULL OUTER JOIN previous p ON ${joinOn} WHERE 1=1 ${filterClause})`
376
+ };
377
+ }
378
+ function resolveComparisonSQL(current, previous, options, comparisonFilter, comparisonOrderBy) {
379
+ const queries = buildComparisonQueries(current, previous, options, comparisonFilter, comparisonOrderBy, false);
380
+ const main = compileCollapsed(options.adapter, queries.main);
381
+ const count = compileCollapsed(options.adapter, queries.count);
304
382
  return {
305
383
  sql: main.sql,
306
384
  params: main.params,
@@ -308,6 +386,10 @@ function resolveComparisonSQL(current, previous, options, comparisonFilter, comp
308
386
  countParams: count.params
309
387
  };
310
388
  }
389
+ function resolveComparisonSQLOptimized(current, previous, options, comparisonFilter, comparisonOrderBy) {
390
+ const queries = buildComparisonQueries(current, previous, options, comparisonFilter, comparisonOrderBy, true);
391
+ return compileCollapsed(options.adapter, queries.main);
392
+ }
311
393
  function buildExtrasQueries(state, options, queryCanonicalKeys) {
312
394
  const { adapter, siteId, searchType } = options;
313
395
  const plan = buildLogicalPlan(state, adapter.capabilities);
@@ -407,4 +489,4 @@ function mergeExtras(rows, extrasResults) {
407
489
  return enriched;
408
490
  });
409
491
  }
410
- export { buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized };
492
+ export { buildComparisonTotalsSql, buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveComparisonSQLOptimized, resolveToSQL, resolveToSQLOptimized, splitComparisonTotalsRow };
@@ -1,12 +1,12 @@
1
- import { ComparisonFilter, ComparisonOrderBy, ExtraQuery, ResolvedComparisonSQL, ResolvedSQL, ResolvedSQLOptimized, ResolverAdapter, ResolverOptions } from "./types.mjs";
1
+ import { ComparisonFilter, ComparisonOrderBy, ExtraQuery, ResolvedComparisonSQL, ResolvedComparisonTotalsSQL, ResolvedSQL, ResolvedSQLOptimized, ResolverAdapter, ResolverOptions } from "./types.mjs";
2
2
  import { DIMENSION_SURFACES, DimensionBinding, DimensionSurface, LOGICAL_DATASETS, LogicalDataset, LogicalDatasetDefinition, UnresolvableDatasetError, assertDimensionsSupported, dimensionColumn, inferLogicalDataset, isDatasetResolvable, supportsDimensionOnSurface } from "./datasets.mjs";
3
3
  import { SqlFragments, SqlFragmentsConfig, createSqlFragments } from "./fragments.mjs";
4
4
  import { CreateResolverAdapterConfig, createResolverAdapter } from "./adapter.mjs";
5
5
  import { canonicalRollupCovers } from "./canonical-source.mjs";
6
- import { buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized } from "./compile.mjs";
6
+ import { buildComparisonTotalsSql, buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveComparisonSQLOptimized, resolveToSQL, resolveToSQLOptimized, splitComparisonTotalsRow } from "./compile.mjs";
7
7
  import { CanonicalQueryDimSource, ComparisonQueryResult, OptimizedQueryResult, PrimaryColumnarSource, QuerySourceCoverageError, QuerySourceDecision, QuerySourceFallback, QuerySourceFallbackKind, QuerySourceKind, ResolveExtraFn, RunOptimizedQueryOptions, RunQueryCtx, RunSQLFn, runComparisonQuery, runOptimizedQuery } from "./run-query.mjs";
8
8
  import { RollupRowsReader, createRollupExtrasOverlay } from "./extras-overlay.mjs";
9
9
  import { dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, metricValue } from "./filter-utils.mjs";
10
10
  import { PgTableKey, R2SqlResolverAdapterOptions, ResolverAdapterOptions, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "./pg-adapter.mjs";
11
11
  import { AssertSchemaInSyncOptions, assertSchemaInSync } from "./schema-drift.mjs";
12
- export { type AssertSchemaInSyncOptions, type CanonicalQueryDimSource, type ComparisonFilter, type ComparisonOrderBy, type ComparisonQueryResult, type CreateResolverAdapterConfig, DIMENSION_SURFACES, type DimensionBinding, type DimensionSurface, type ExtraQuery, LOGICAL_DATASETS, type LogicalDataset, type LogicalDatasetDefinition, type OptimizedQueryResult, type PgTableKey, type PrimaryColumnarSource, QuerySourceCoverageError, type QuerySourceDecision, type QuerySourceFallback, type QuerySourceFallbackKind, type QuerySourceKind, type R2SqlResolverAdapterOptions, type ResolveExtraFn, type ResolvedComparisonSQL, type ResolvedSQL, type ResolvedSQLOptimized, type ResolverAdapter, type ResolverAdapterOptions, type ResolverOptions, type RollupRowsReader, type RunOptimizedQueryOptions, type RunQueryCtx, type RunSQLFn, type SqlFragments, type SqlFragmentsConfig, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface };
12
+ export { type AssertSchemaInSyncOptions, type CanonicalQueryDimSource, type ComparisonFilter, type ComparisonOrderBy, type ComparisonQueryResult, type CreateResolverAdapterConfig, DIMENSION_SURFACES, type DimensionBinding, type DimensionSurface, type ExtraQuery, LOGICAL_DATASETS, type LogicalDataset, type LogicalDatasetDefinition, type OptimizedQueryResult, type PgTableKey, type PrimaryColumnarSource, QuerySourceCoverageError, type QuerySourceDecision, type QuerySourceFallback, type QuerySourceFallbackKind, type QuerySourceKind, type R2SqlResolverAdapterOptions, type ResolveExtraFn, type ResolvedComparisonSQL, type ResolvedComparisonTotalsSQL, type ResolvedSQL, type ResolvedSQLOptimized, type ResolverAdapter, type ResolverAdapterOptions, type ResolverOptions, type RollupRowsReader, type RunOptimizedQueryOptions, type RunQueryCtx, type RunSQLFn, type SqlFragments, type SqlFragmentsConfig, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildComparisonTotalsSql, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, resolveComparisonSQL, resolveComparisonSQLOptimized, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, splitComparisonTotalsRow, supportsDimensionOnSurface };
@@ -2,10 +2,10 @@ import { DIMENSION_SURFACES, LOGICAL_DATASETS, UnresolvableDatasetError, assertD
2
2
  import { createSqlFragments } from "./fragments.mjs";
3
3
  import { createResolverAdapter } from "./adapter.mjs";
4
4
  import { canonicalRollupCovers } from "./canonical-source.mjs";
5
- import { buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized } from "./compile.mjs";
5
+ import { buildComparisonTotalsSql, buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveComparisonSQLOptimized, resolveToSQL, resolveToSQLOptimized, splitComparisonTotalsRow } from "./compile.mjs";
6
6
  import { createRollupExtrasOverlay } from "./extras-overlay.mjs";
7
7
  import { dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, metricValue } from "./filter-utils.mjs";
8
8
  import { createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, pgResolverAdapter } from "./pg-adapter.mjs";
9
9
  import { QuerySourceCoverageError, runComparisonQuery, runOptimizedQuery } from "./run-query.mjs";
10
10
  import { assertSchemaInSync } from "./schema-drift.mjs";
11
- export { DIMENSION_SURFACES, LOGICAL_DATASETS, QuerySourceCoverageError, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, resolveComparisonSQL, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, supportsDimensionOnSurface };
11
+ export { DIMENSION_SURFACES, LOGICAL_DATASETS, QuerySourceCoverageError, UnresolvableDatasetError, assertDimensionsSupported, assertSchemaInSync, buildComparisonTotalsSql, buildExtrasQueries, buildTotalsSql, canonicalRollupCovers, createIcebergResolverAdapter, createParquetResolverAdapter, createR2SqlResolverAdapter, createResolverAdapter, createRollupExtrasOverlay, createSqlFragments, dimensionColumn, dimensionValue, getDimensionFilters, getFilterDimensions, getInternalFilters, inferLogicalDataset, isDatasetResolvable, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, mergeExtras, metricValue, pgResolverAdapter, resolveComparisonSQL, resolveComparisonSQLOptimized, resolveToSQL, resolveToSQLOptimized, runComparisonQuery, runOptimizedQuery, splitComparisonTotalsRow, supportsDimensionOnSurface };
@@ -74,6 +74,20 @@ export interface ResolvedComparisonSQL {
74
74
  countSql: string;
75
75
  countParams: unknown[];
76
76
  }
77
+ /**
78
+ * Totals SQL for the current and previous windows of a comparison read.
79
+ * `Combined` is one statement for both windows. `Separate` is one statement
80
+ * per window, for windows that read different tables or filter different rows.
81
+ */
82
+ export type ResolvedComparisonTotalsSQL = {
83
+ _tag: 'Combined';
84
+ sql: string;
85
+ params: unknown[];
86
+ } | {
87
+ _tag: 'Separate';
88
+ current: ResolvedSQLOptimized;
89
+ previous: ResolvedSQLOptimized;
90
+ };
77
91
  export interface ExtraQuery {
78
92
  key: string;
79
93
  sql: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine",
3
3
  "type": "module",
4
- "version": "7.8.4",
4
+ "version": "7.9.0",
5
5
  "description": "Append-only Parquet/DuckDB storage engine + planner + adapters for the gscdump pipeline. Node + edge runtimes; opt-in heavy peers.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -189,10 +189,10 @@
189
189
  }
190
190
  },
191
191
  "dependencies": {
192
- "@gscdump/contracts": "^7.8.4",
193
- "@gscdump/lakehouse": "^7.8.4",
192
+ "@gscdump/contracts": "^7.9.0",
193
+ "@gscdump/lakehouse": "^7.9.0",
194
194
  "drizzle-orm": "1.0.0-rc.4",
195
- "gscdump": "^7.8.4",
195
+ "gscdump": "^7.9.0",
196
196
  "pathe": "^2.0.3",
197
197
  "proper-lockfile": "^4.1.2"
198
198
  },