Skip to content

Freeze current-time functions into literals in DuckDB SQL - #413

Merged
nicosuave merged 4 commits into
mainfrom
relative-date-literals
Oct 12, 2026
Merged

nicosuave merged 4 commits into
mainfrom
relative-date-literals

Conversation

@nicosuave

Copy link
Copy Markdown
Member

Compiled DuckDB SQL now replaces CURRENT_DATE, today(), now(), CURRENT_TIMESTAMP, get_current_timestamp() and transaction_timestamp() with literals taken from one instant per query. Every reference in a query sees the same time, and scans that cannot fold these functions before partition pruning (Iceberg on DuckDB 1.5) still skip files.

  • On by default only for the DuckDB dialect: CURRENT_DATE depends on the session timezone, which sidemantic can match for in-process DuckDB but not for remote servers. SemanticLayer(freeze_current_time=...) overrides it.
  • CURRENT_DATE arithmetic with day/week/hour/minute/second intervals folds into a DATE or TIMESTAMP literal. now() becomes an exact UTC TIMESTAMPTZ literal; day intervals on it stay as intervals so DST behavior is unchanged. Month/quarter/year arithmetic is never folded.
  • The rewrite works on token spans, so the rest of the SQL, including the pre-aggregation marker, is unchanged. Strings, comments, quoted identifiers, qualified columns such as t.current_date, and non-SELECT statements are left alone.
  • Applied at the end of compile() and rewrite_transport_sql(). Generator and sql() caches keep the unfrozen SQL and freeze on each call, so cached queries never reuse a stale time.
  • Rust engine output routed through Python is frozen too. The standalone Rust CLI, DuckDB extension and wasm builds do not freeze yet.
  • The clock is injectable (layer.clock).

CURRENT_DATE, now() and CURRENT_TIMESTAMP are replaced with literals from a
single instant per query, so every reference in one query sees the same time
and engines that cannot fold these functions before pruning still skip files.
On by default for DuckDB only, since other dialects use a server timezone
sidemantic cannot see. The cached SQL stays unfrozen and is frozen per call.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T01:01:58.347987Z 58fab67 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 58fab67efe

ℹ️ 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".

Comment thread sidemantic/core/semantic_layer.py Outdated
return sql
from sidemantic.sql.frozen_time import freeze_current_time

return freeze_current_time(sql, dialect, now=self.clock())

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 Use the DuckDB session timezone when freezing dates

When a DuckDB connection sets TimeZone through the supported init_sql option or a prior SET TimeZone, this call omits the session timezone, so freeze_current_time derives CURRENT_DATE from the Python process timezone instead. If those zones have different calendar dates—such as a UTC process querying an America/Los_Angeles session near midnight—the generated literal is a day off and silently changes filter results; obtain the connection's effective timezone or avoid enabling date freezing unless it is known to match.

Useful? React with 👍 / 👎.

Comment on lines +181 to +185
def _is_single_query(tokens: list[Token]) -> bool:
if not tokens or tokens[0].token_type not in _QUERY_START_TYPES:
return False
semicolons = [i for i, token in enumerate(tokens) if token.token_type == TokenType.SEMICOLON]
return not semicolons or semicolons == [len(tokens) - 1]

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 Parse the root statement before classifying it as a query

For a CTE-prefixed DML statement such as WITH x AS (...) INSERT INTO history SELECT CURRENT_DATE, the first token is WITH and there is only one statement, so this check classifies it as a query and freezes the value despite the function's contract that DDL/DML remain unchanged. Non-strict SQL transports can pass such statements through, and rewritten SQL retained for later execution then persists a stale compile-time literal; inspect the parsed root expression rather than inferring statement kind from the first token.

Useful? React with 👍 / 👎.

…xed DML

CURRENT_DATE depends on the connection's TimeZone setting, which init_sql or
SET can change, so read it from the layer's own DuckDB session; the setting
is session-scoped, so a cursor would see the default. When the timezone is
unknown, leave CURRENT_DATE unchanged unless freezing was explicitly enabled.

Classify statements by their parsed root so WITH ... INSERT and other
CTE-prefixed DML keep their current-time calls.
The parity harness replaced the dates module's layer fixture with a fresh
Rust layer, so its pinned clock was dropped and the frozen dates followed
the wall clock, failing whenever the UTC date differed from the pinned one.
Build the layer from the module fixture, which already uses the patched
Rust SemanticLayer.
The Rust engine renders pushed-down filters as (timestamp) >= ...; the
frozen literals were correct but the assertions matched only the Python
engine's rendering.
@nicosuave
nicosuave merged commit a44b735 into main Oct 12, 2026
27 checks passed
@nicosuave
nicosuave deleted the relative-date-literals branch October 12, 2026 02:54
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