Skip to content

Fix pre-aggregation materialization correctness - #302

Merged
nicosuave merged 3 commits into
mainfrom
recut-283-279-foundation
Aug 2, 2026
Merged

Fix pre-aggregation materialization correctness#302
nicosuave merged 3 commits into
mainfrom
recut-283-279-foundation

Conversation

@nicosuave

Copy link
Copy Markdown
Member

Extracts the filtered-metric, total-rollup, and incremental lookback correctness fixes from #283 and #279.

@nicosuave
nicosuave merged commit ee4e36b into main Aug 2, 2026
19 of 21 checks passed
@nicosuave
nicosuave deleted the recut-283-279-foundation branch August 2, 2026 05:06

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c569d57d97

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# range before inserting its replacement so repeated refreshes remain
# idempotent instead of accumulating duplicate rollup rows.
if lookback:
connection.execute(f"DELETE FROM {table_name} WHERE {watermark_column} >= {watermark_str}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Match the delete boundary to the source predicate

When a lookback refresh uses the documented/common WHERE date > {WATERMARK} source query, this deletes the row exactly at the computed cutoff with >=, while the subsequent insert selects only rows strictly after it. For example, a max watermark of January 10 with a five-day lookback permanently removes January 5; the deletion boundary must preserve or reload exactly the same range selected by the source query.

Useful? React with 👍 / 👎.

Comment on lines +778 to 779
connection.execute(f"DELETE FROM {table_name} WHERE {watermark_column} >= {watermark_str}")
connection.execute(f"INSERT INTO {table_name} {incremental_sql}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make lookback replacement atomic

On autocommit connections such as a direct DuckDB connection, any schema, type, or execution error in the insert after this delete leaves the pre-aggregation permanently missing its entire lookback window. Run the delete-and-insert replacement in one transaction or stage the replacement before deleting existing rows.

Useful? React with 👍 / 👎.

Comment on lines +243 to +246
if agg_type == "COUNT" and not measure.sql:
measure_input = f"CASE WHEN {filter_sql} THEN 1 ELSE NULL END"
else:
measure_input = f"CASE WHEN {filter_sql} THEN {measure_input} ELSE NULL END"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat sql="*" as a filtered row count

When a filtered row-count metric is represented as Metric(agg="count", sql="*", filters=[...]), measure.sql is truthy and this branch produces COUNT(CASE WHEN ... THEN * ELSE NULL END), which DuckDB and other engines reject because * cannot appear inside a CASE. Handle sql == "*" like the existing no-SQL row-count case and place 1 in the filtered aggregate input.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant