Skip to content

DEV-1716: Route typed-generator emission through slayer/sql/dialects/ (DEV-1542 parity) - #257

Merged
ZmeiGorynych merged 8 commits into
egor/dev-1704-dev-1703-stage-0-merge-originmain-into-the-typed-pipelinefrom
egor/dev-1716-dev-1703-route-typed-generator-emission-through
Jul 27, 2026
Merged

DEV-1716: Route typed-generator emission through slayer/sql/dialects/ (DEV-1542 parity)#257
ZmeiGorynych merged 8 commits into
egor/dev-1704-dev-1703-stage-0-merge-originmain-into-the-typed-pipelinefrom
egor/dev-1716-dev-1703-route-typed-generator-emission-through

Conversation

@ZmeiGorynych

@ZmeiGorynych ZmeiGorynych commented Jul 27, 2026

Copy link
Copy Markdown
Member

What

Ports main's DEV-1542 dialect-strategy delegation into the typed pipeline so every dialect-specific SQL decision routes through slayer/sql/dialects/, flipping all DEV-1716-owned strict-xfail parity pins (127 unit + 9 MySQL integration → zero DEV-1716 pins remain).

Generator (slayer/sql/generator.py)

  • __init__(dialect: str | SqlDialect) + read-only dialect property (self._dialect.sqlglot_name)
  • _parse/_parse_predicate apply rewrite_parsed_ast (parse-dialect) + rewrite_target_ast (target-dialect — Postgres 2-arg ROUND cast)
  • Delegated _build_date_trunc / _build_time_offset_expr / _duration_interval_exprs / _add_intervals_expr / _build_median / _build_percentile / _build_stat_agg / _build_outer_wrap; _rewrite_log_aliasesshould_use_native_log
  • _build_agg(measure=EnrichedMeasure) compat param (preserves the DEV-1452 AggRenderSpec split)
  • Terminal rewrite_emitted_sql on generate() + generate_planned_stages (DB-bound terminals, not per-stage)
  • New _quote_ident replaces raw ANSI "..." identifiers across both the enriched and planned paths (time-shift/self-join/step CTEs, cross-model combined, consecutive-periods, projections, ORDER BY, join-ON) so MySQL/BigQuery/T-SQL get correct quoting (identity for Postgres/SQLite/DuckDB); _ordered suppresses T-SQL NULLS-emulation CASE WHEN

Engine + metadata

  • decode_result_keys on the data path; _dialect_for_typedialect_for_ds_type().sqlglot_name (drop duplicate map); _build_explain_sqlbuild_explain_sql hook
  • response_meta: decode SQL-derived expected_columns so BigQuery/T-SQL attributes survive

Tests

  • Un-pinned all DEV-1716 parity entries
  • Added engine EXPLAIN-dispatch, multi-stage mangling, attribute-survival, decode-scoping, and interval/parse delegation coverage
  • Removed stale MySQL-raises / T-SQL duplicate tests superseded by tests/dialects/

Verification

  • Full non-integration suite: 7700 passed
  • MySQL integration (live testcontainers): 47 passed — all 9 formerly-pinned tests pass
  • SQLite / DuckDB / Postgres integration: 191 passed
  • T-SQL / BigQuery integration not runnable locally; covered by unit tests + their CI workflows

Also merges the latest DEV-1704 (Stage-0) state into this branch.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Improvements

    • Refined dialect-driven SQL generation for identifier quoting, outer wrapping/pagination, time-shifts/intervals, and statistical aggregates, including correct alias rewrites for multi-stage queries.
    • Standardized EXPLAIN SQL construction via the active dialect strategy (legacy output preserved where supported).
    • Improved expected-column decoding/normalization so wrapped-query attribute mappings stay consistent across dialect-specific alias mangling (notably BigQuery and T-SQL).
  • Bug Fixes

    • Ensured row decoding isn’t applied on dry_run/EXPLAIN execution paths.
  • Tests

    • Added and expanded dialect delegation, wrapped-reference, and week_sunday time-shift coverage; pruned obsolete parity xfails.

ZmeiGorynych and others added 3 commits July 27, 2026 14:54
Port main's DEV-1542 dialect-strategy delegation into the typed pipeline so
every dialect-specific SQL decision routes through slayer/sql/dialects/,
flipping the 127 strict-xfail parity pins DEV-1716 owns.

Generator:
- __init__(dialect: str | SqlDialect) + read-only `dialect` property
- _parse/_parse_predicate apply rewrite_parsed_ast + rewrite_target_ast
- delegate _build_date_trunc / _build_time_offset_expr /
  _duration_interval_exprs / _add_intervals_expr / _build_median /
  _build_percentile / _build_stat_agg / _build_outer_wrap; _rewrite_log_aliases
  -> should_use_native_log
- _build_agg(measure=EnrichedMeasure) compat (keeps DEV-1452 AggRenderSpec)
- terminal rewrite_emitted_sql on generate() + generate_planned_stages
- _quote_ident replaces raw ANSI quoting at time-shift/self-join CTE, transform,
  projection and ORDER BY sites; _ordered suppresses T-SQL NULLS-emulation

Engine: decode_result_keys on the data path; _dialect_for_type ->
dialect_for_ds_type().sqlglot_name (drop duplicate map); _build_explain_sql ->
build_explain_sql hook. response_meta: decode SQL-derived expected_columns so
BigQuery/T-SQL attributes survive.

Tests: un-pin the 127 DEV-1716 parity entries; add engine EXPLAIN-dispatch,
multi-stage mangling, attribute-survival, decode-scoping and interval/parse
delegation coverage; drop stale MySQL-raises / T-SQL duplicate tests superseded
by tests/dialects/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…erge-originmain-into-the-typed-pipeline' into egor/dev-1716-dev-1703-route-typed-generator-emission-through

# Conflicts:
#	tests/parity_xfails.py
…-model / consecutive / projection / order)

Complete the §3d identifier-quoting port on the PLANNED path (generate_from_planned
and its helpers) that the engine uses — the prior commit only covered the enriched
generate() path that the unit tests exercise. The planned path built CTE / projection
/ ORDER BY / join-ON SQL with raw ANSI double-quoted identifiers, which MySQL reads as
string literals (1064 syntax error) and BigQuery/T-SQL can't mangle.

Route every raw `f'"{alias}"'` site through `_quote_ident` (backticks on MySQL/BigQuery,
brackets on T-SQL, ANSI on Postgres/SQLite/DuckDB — identity there, so no change for
ANSI dialects): planned time-shift shifted/self-join CTEs, cross-model combined SELECT +
join-ON, transform-chain step/outer projection, consecutive-periods reset/value CTEs,
window-transform measure/time/partition refs, planned ORDER BY entries, and the
post-filter qualified-alias wrap. A couple of enriched leftovers folded in for consistency.

Verified: all 9 formerly-pinned MySQL integration tests pass (live testcontainers);
full non-integration suite green (7700); SQLite/DuckDB/Postgres integration green.
Un-pin the 6 remaining DEV-1716 MySQL integration parity entries -> zero DEV-1716 pins.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear

linear Bot commented Jul 27, 2026

Copy link
Copy Markdown

DEV-1716

DEV-1542

DEV-1703

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change centralizes SQL dialect behavior in shared strategies. Query execution and metadata decode dialect-mangled keys, while SQLGenerator delegates parsing, quoting, rewriting, intervals, transforms, ordering, and aggregates to dialect hooks. Tests cover dispatch, BigQuery/T-SQL aliases, EXPLAIN, multistage SQL, and parity.

Changes

Dialect strategy integration

Layer / File(s) Summary
Engine dialect entrypoints
slayer/engine/query_engine.py, slayer/engine/response_meta.py, tests/dialects/test_engine_explain_dispatch.py, tests/dialects/test_bigquery.py, tests/dialects/test_tsql.py
EXPLAIN generation, datasource-to-dialect resolution, wrapped aliases, result-row decoding, and expected-column normalization now use dialect strategies, with BigQuery and T-SQL coverage.
Dialect-aware generator core
slayer/sql/generator.py, tests/dialects/test_generator_dispatch.py
SQLGenerator accepts dialect objects, applies dialect AST and SQL rewrites, and uses dialect-specific quoting, wrapping, ordering, and transform rendering.
Dialect expression and aggregate builders
slayer/sql/generator.py, slayer/sql/dialects/tsql.py, tests/dialects/test_generator_dispatch.py, tests/dialects/test_generator_delegation.py
Time intervals, date truncation, logging, median, percentile, covariance, correlation, and statistical aggregates dispatch through dialect builders; T-SQL normalizes week_sunday to WEEK.
Terminal stage emission and parity coverage
slayer/sql/generator.py, slayer/sql/stage_wrapper.py, tests/test_generator2_multistage.py, tests/test_stage_wrapper_helper.py, tests/parity_xfails.py, tests/test_sql_generator.py
Terminal and stage-wrapper SQL applies dialect alias rewriting and decoding, while multistage and dialect expectation tests are enabled or updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant QueryPlanner
  participant SQLGenerator
  participant SqlDialect
  participant Database
  QueryPlanner->>SQLGenerator: generate planned SQL
  SQLGenerator->>SqlDialect: rewrite AST and render dialect SQL
  SqlDialect-->>SQLGenerator: dialect-specific SQL
  SQLGenerator->>Database: execute terminal SQL
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: routing typed-generator emission through the dialect strategy layer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch egor/dev-1716-dev-1703-route-typed-generator-emission-through

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/test_generator2_multistage.py (1)

365-365: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move the re import to module scope.

Line 365 violates the repository rule to keep imports at the top of files.

As per coding guidelines, “keep imports at the top of files.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_generator2_multistage.py` at line 365, Move the aliased `re`
import from its current local position to the module-level import section at the
top of tests/test_generator2_multistage.py, preserving the `_re` alias and
removing the duplicate local import.

Source: Coding guidelines

slayer/sql/generator.py (1)

2419-2440: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dual spec= / measure= entry points on _build_agg are a compatibility wart.

Two mutually exclusive inputs with a runtime None guard is easy to misuse. Consider keeping _build_agg(spec) as the only signature and having the delegation tests adapt via _agg_render_spec_from_enriched themselves; then drop the measure= branch once those tests move.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@slayer/sql/generator.py` around lines 2419 - 2440, The _build_agg method
exposes an unnecessary compatibility-only measure entry point alongside spec.
Remove the measure keyword parameter, its _agg_render_spec_from_enriched
adaptation branch, and the related None guard; keep _build_agg accepting only
the required AggRenderSpec and update callers or delegation tests to adapt
EnrichedMeasure through _agg_render_spec_from_enriched before invoking it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@slayer/sql/generator.py`:
- Around line 764-769: Update _query_as_model to use the shared flat-rename
wrapper from stage_wrapper.py when emitting aliased columns, rather than
constructing f'"{alias}" AS {short}' directly. Ensure the wrapper receives the
dialect-mangled alias names so wrapped BigQuery/T-SQL inner references stay
consistent and quoting remains dialect-aware, including MySQL.

In `@tests/test_generator2_multistage.py`:
- Around line 384-386: Strengthen the assertion in the multi-stage T-SQL
regression test to reject bracketed dotted identifiers such as
[stage1.amount_sum] and [orders.*], not only ANSI double-quoted forms. Preserve
the existing SQL diagnostic and continue validating that dotted aliases are
mangled.

---

Nitpick comments:
In `@slayer/sql/generator.py`:
- Around line 2419-2440: The _build_agg method exposes an unnecessary
compatibility-only measure entry point alongside spec. Remove the measure
keyword parameter, its _agg_render_spec_from_enriched adaptation branch, and the
related None guard; keep _build_agg accepting only the required AggRenderSpec
and update callers or delegation tests to adapt EnrichedMeasure through
_agg_render_spec_from_enriched before invoking it.

In `@tests/test_generator2_multistage.py`:
- Line 365: Move the aliased `re` import from its current local position to the
module-level import section at the top of tests/test_generator2_multistage.py,
preserving the `_re` alias and removing the duplicate local import.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e3edb127-4124-4234-87ee-1ea81c0c2ac4

📥 Commits

Reviewing files that changed from the base of the PR and between baae7b7 and 8ca9b77.

📒 Files selected for processing (10)
  • slayer/engine/query_engine.py
  • slayer/engine/response_meta.py
  • slayer/sql/generator.py
  • tests/dialects/test_bigquery.py
  • tests/dialects/test_engine_explain_dispatch.py
  • tests/dialects/test_generator_dispatch.py
  • tests/dialects/test_tsql.py
  • tests/parity_xfails.py
  • tests/test_generator2_multistage.py
  • tests/test_sql_generator.py
💤 Files with no reviewable changes (1)
  • tests/parity_xfails.py

Comment thread slayer/sql/generator.py
Comment thread tests/test_generator2_multistage.py
…CodeRabbit nits

- Codex (major): _query_as_model built its outer rename wrapper with raw ANSI
  "orders.status" refs, but generate(render_mode="wrapped") now dialect-quotes
  AND (BigQuery/T-SQL) alias-mangles the inner projection — so the outer ref
  pointed at a column the mangled inner no longer exposes (and was a string
  literal on MySQL/BigQuery/T-SQL). Dialect-quote + rewrite_emitted_sql each
  inner ref (identity for Postgres/SQLite/DuckDB). + regression test.
- Sonar S5778: hoist BigqueryDialect() construction out of the pytest.raises
  block in test_engine_explain_dispatch.
- Sonar S3776: NOSONAR on build_response_metadata (pre-existing per-slot
  metadata-loop complexity; matches the codebase's existing S3776 pattern).
- CodeRabbit: move the `re` import to module scope in test_generator2_multistage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@slayer/engine/query_engine.py`:
- Around line 2386-2398: Replace the raw f-string projection assembly around
`_inner_ref` and `rename_parts` with a sqlglot AST helper that builds each
wrapper projection from the alias map and renders it through the existing
dialect pipeline. Preserve the current alias rewriting, short-name mapping, and
generated `AS` semantics while eliminating SQL string concatenation in this
wrapper construction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 987e765d-3d07-41c7-8730-155a127e43fa

📥 Commits

Reviewing files that changed from the base of the PR and between 8ca9b77 and d4a5b99.

📒 Files selected for processing (5)
  • slayer/engine/query_engine.py
  • slayer/engine/response_meta.py
  • tests/dialects/test_bigquery.py
  • tests/dialects/test_engine_explain_dispatch.py
  • tests/test_generator2_multistage.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • slayer/engine/response_meta.py
  • tests/test_generator2_multistage.py
  • tests/dialects/test_bigquery.py
  • tests/dialects/test_engine_explain_dispatch.py

Comment thread slayer/engine/query_engine.py
ZmeiGorynych and others added 3 commits July 27, 2026 16:21
…entifiers

CodeRabbit: the multi-stage T-SQL regression test only rejected ANSI-quoted
dotted identifiers; add a check that no bracketed identifier ([stage1.amount_sum])
contains a dot either, so a missed T-SQL alias-mangle is caught.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…set_expr

Codex review: delegating _build_time_offset_expr exposed a T-SQL gap — a
time_shift over a WEEK_SUNDAY time dimension passed granularity="week_sunday"
to TsqlDialect.build_time_offset_expr, whose unit_map lacked the key and fell
through to DATEADD(WEEK_SUNDAY, ...), invalid T-SQL. The base _granularity_to_unit
already normalizes it (a one-period Sunday-week shift is one week, DEV-1572);
add the same to the T-SQL unit map + a coverage test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pper

Codex review: generate_planned_stages now returns BigQuery/T-SQL alias-mangled
terminal SQL, but _expand_query_backed_model feeds that into
build_flat_rename_wrapper, which strips the "<source_relation>." prefix
expecting canonical dotted aliases. After mangling it saw orders___status,
couldn't strip "orders.", and the produced/expected assertion raised for
BigQuery/T-SQL query-backed models.

Decode each rendered output name to canonical dotted form (via the dialect's
decode_result_keys, identity for non-mangling dialects and a no-op on
already-dotted names) for the prefix-strip + flat-name computation, while still
referencing the ACTUAL mangled name as the inner-column source. + tests.

(Keeping wrapped-mode mangling — it's a main-parity pin; the consumer decodes,
matching how the legacy pipeline's consumers handle mangled aliases.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_stage_wrapper_helper.py`:
- Line 95: Move the build_flat_rename_wrapper import out of the affected test
functions and add it once alongside the module-level imports in
tests/test_stage_wrapper_helper.py. Remove the duplicate local imports while
leaving the test behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fcc8fefb-e6fc-408e-9365-b617cdcaf96b

📥 Commits

Reviewing files that changed from the base of the PR and between 44c7f05 and 7bdf323.

📒 Files selected for processing (2)
  • slayer/sql/stage_wrapper.py
  • tests/test_stage_wrapper_helper.py

Comment thread tests/test_stage_wrapper_helper.py Outdated
the expected flat schema, while still referencing the ACTUAL mangled inner
column. Without the decode the prefix-strip misses and the produced/expected
assertion raises for BigQuery/T-SQL query-backed models."""
from slayer.sql.stage_wrapper import build_flat_rename_wrapper

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move the new imports to module scope.

The added build_flat_rename_wrapper imports are inside test functions, contrary to the **/*.py rule requiring imports at the top of the file. Import it once with the module-level imports.

Also applies to: 120-120

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_stage_wrapper_helper.py` at line 95, Move the
build_flat_rename_wrapper import out of the affected test functions and add it
once alongside the module-level imports in tests/test_stage_wrapper_helper.py.
Remove the duplicate local imports while leaving the test behavior unchanged.

Source: Coding guidelines

… nit

- Codex: get_column_types runs a LIMIT-0 probe whose SQL is alias-mangled on
  BigQuery/T-SQL (it must be, to execute), so the cursor returns mangled keys
  (orders___amount_max). The qualified-alias map-back looked up canonical dotted
  keys and silently returned {} for those dialects. Decode raw_types keys via the
  dialect (identity elsewhere) before the map-back. + end-to-end test.
- CodeRabbit: hoist the build_flat_rename_wrapper import to module scope in
  test_stage_wrapper_helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@ZmeiGorynych
ZmeiGorynych merged commit d685377 into egor/dev-1704-dev-1703-stage-0-merge-originmain-into-the-typed-pipeline Jul 27, 2026
6 checks passed
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