Skip to content

DEV-1378: string-hygiene operators + Mode A predicate routing#113

Merged
ZmeiGorynych merged 4 commits into
mainfrom
egor/dev-1378-add-string-hygiene-operators
May 11, 2026
Merged

DEV-1378: string-hygiene operators + Mode A predicate routing#113
ZmeiGorynych merged 4 commits into
mainfrom
egor/dev-1378-add-string-hygiene-operators

Conversation

@ZmeiGorynych
Copy link
Copy Markdown
Member

@ZmeiGorynych ZmeiGorynych commented May 10, 2026

Summary

Closes two implementation gaps in SLayer's reference semantics (Mode A SQL vs Mode B DSL — see docs/concepts/references.md):

  • Mode A model filters now route through parse_sql_predicate. Previously Column.filter and SlayerModel.filters were created via the SQL path but re-parsed at enrichment time via the DSL parser, so any model filter containing arbitrary SQL functions (json_extract, coalesce, CASE WHEN, …) raised Unknown filter function 'json_extract' at runtime. The fix introduces a dedicated slayer.sql.sql_predicate.parse_sql_predicate module and wires it through every enrichment site that handles Mode A filters; Mode B query filters (SlayerQuery.filters) continue through the DSL parser. Strictness is split: model filters resolve with strict=False, query filters with strict=True.
  • SlayerQuery.filters (Mode B) accepts a small string-hygiene allowlist. Eight lowercase scalars — lower, upper, trim, replace, substr, instr, length, concat — plus the SQL || operator, which is rewritten to concat(...) by a new _preprocess_concat pass. Names are lowercase only, matching SLayer's existing transform convention. sqlglot translates per-dialect at SQL-generation time. Single source of truth: STRING_HYGIENE_OPS frozenset in slayer/core/formula.py.

Also folded in: a SQL generator change to wrap user-supplied predicates in SELECT 1 WHERE … before parsing (_parse_predicate). This dodges a sqlglot trap on SQLite/MySQL where REPLACE at the start of a fragment is parsed as the REPLACE INTO statement keyword, not the function call.

What's in this PR

  • slayer/core/formula.pySTRING_HYGIENE_OPS allowlist, _preprocess_concat (||<< → flat concat(...)), removed the dead parse_filter(mode='sql') branch (~50 LOC), tightened _call_to_sql to the new allowlist, plus a _classify_call_name helper that gives the formula and filter walkers one categorization function instead of duplicated string checks.
  • slayer/sql/sql_predicate.py — new module. parse_sql_predicate rejects DSL constructs (colon syntax, transforms, raw OVER) up front, extracts column-shaped identifiers, and accepts arbitrary SQL function calls. No sqlglot at parse time; full dialect-aware parsing is deferred to SQL generation.
  • slayer/engine/enrichment.py — every Mode A filter site (measure_def.filter, model-filter validation loop, _collect_needed_paths) routes through parse_sql_predicate. Mode-tagged filter list (List[Tuple[str, str]]) threads through _resolve_joins and _collect_needed_paths so each parser applies the right validator. resolve_filter_columns is now called twice with mode-appropriate strictness.
  • slayer/sql/generator.py — new _parse_predicate(text) wraps fragments in SELECT 1 WHERE … to dodge the REPLACE statement-keyword trap. Three call sites updated (measure filter SQL, WHERE assembly, HAVING assembly).
  • slayer/engine/schema_drift.py — minor: _collect_needed_paths signature accepts the mode-tagged filter list.
  • Docs: docs/concepts/queries.md documents the string-hygiene allowlist and || rewrite. CLAUDE.md and .claude/skills/slayer-query.md updated.
  • Tests: 19 new TestStringHygieneFilters cases in tests/test_formula.py; 15 unit tests in new tests/test_sql_predicate.py; 24 dialect-translation + REPLACE-predicate tests in tests/test_sql_generator.py; 3 enrichment-runtime tests in tests/test_reference_semantics.py; 6 SQLite integration tests in tests/integration/test_integration.py.

Scope decisions

After landing the above, I asked whether the formula parser (parse_formulaFieldSpec) and the DSL filter parser (parse_filterParsedFilter) — both living in slayer/core/formula.py and sharing preprocessing — should be partly or wholly merged. The candidate refactor was a shared expression IR with two thin emitters (one for FieldSpec, one for SQL strings).

Decided in scope (and shipped here):

  • Single _classify_call_name helper for call-name categorization (transform / hygiene / like_internal / unknown). Both walkers use it; their decision logic stays put. ~+25 / −5 LOC, no behavior change.

Decided out of scope (deliberately not done):

  • Unified expression IR + walker. An adversarial review surfaced that the apparent duplication is mostly structural (two walkers exist) rather than textual. Three concrete reasons not to merge yet:
    1. MixedArithmeticField.sql is built from ast.unparse(node) after AST mutation, with pre-order _t0 / _t1 placeholder assignment. Tests pin those exact strings; reproducing them from a Pydantic IR effectively requires re-implementing AST serialization.
    2. The two walkers handle non-transform SQL functions (nullif, coalesce, ln, …) differently and correctly: formula rejects them at the root but accepts them inside arithmetic via _replace_calls_in_arith's passthrough; filter is strict. A literal IR-and-emitter implementation would silently change scalar-call acceptance rules.
    3. Filter SQL is string-pinned by tests (exact output for replace, substr, || flattening, LIKE escaping). Round-tripping through an IR is likely to shift quoting / parens / flattening somewhere subtle.
  • Sharing _BINOP_OP_MAP / _compare_op_to_sql / _compare_to_sql. These are filter-only; the formula walker has no equivalents because it uses ast.unparse for arithmetic and comparisons. There is nothing to dedup.
  • Sharing dotted-name resolution between _resolve_dotted_attribute and _collect_names. The two functions look related but produce different shapes (single string vs. list with consumed-Name-node tracking); collapsing them required touching _collect_names's consumed-Name set, with no behavior win.

The IR refactor remains a possible future direction if a third consumer for the parsed expression appears (e.g. a static analyzer or a non-SQL backend); until then, the two walkers + shared preprocessing layer is the right shape.

Test plan

  • poetry run pytest tests/test_formula.py -v — 179 pass
  • poetry run pytest -m "not integration" — 2283 pass
  • poetry run ruff check slayer/ tests/ — clean
  • poetry run pytest tests/integration/ -m integration — verify SQLite / Postgres / DuckDB suites green
  • Spot-check an end-to-end query with a Mode A model filter using json_extract / coalesce and a Mode B query filter using lower(name) || '_x' = 'foo_x'
  • Verify the docs render correctly on the queries / references pages

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Query filters accept a lowercase allowlist of string-hygiene functions: lower, upper, trim, replace, substr, instr, length, concat; SQL || is rewritten to concat(...).
  • Improvements

    • Clear separation of model-level (SQL) vs query-level (DSL) filters for predictable parsing, join-resolution, and dialect-aware SQL emission.
    • Better LIKE/concat handling and more robust predicate parsing for user-supplied filter fragments.
  • Documentation

    • Docs and conventions updated to reflect these behaviors.
  • Tests

    • Added extensive unit and integration tests validating modes, functions, and parsing.

Review Change Stack

ZmeiGorynych and others added 2 commits May 10, 2026 20:13
…gh parse_sql_predicate

Add a lowercase allowlist of scalar string functions (`lower`, `upper`,
`trim`, `replace`, `substr`, `instr`, `length`, `concat`) plus the SQL
`||` operator (folded to `concat(...)`) to `SlayerQuery.filters`. Fold
two pre-existing issues caught while tracing the parser:

1. `parse_filter(mode="sql")` plumbing was dead code — never called
   from anywhere — left over from DEV-1369 round 1. Removed (~50 LOC).

2. `Column.filter` / `SlayerModel.filters` (Mode A SQL) constructed
   cleanly through `parse_sql_predicate` but were re-parsed via the
   DSL `parse_filter` at enrichment time, raising `Unknown filter
   function 'json_extract'` (or any other SQL function) on every
   query. Now routed through `parse_sql_predicate` at all four
   enrichment sites, with `_collect_needed_paths` taking a mode-tagged
   filter list so model and query filters use the right parser. Two
   `resolve_filter_columns` calls (model `strict=False`, query
   `strict=True`) replace the single combined call.

Add `SQLGenerator._parse_predicate` that wraps in `SELECT 1 WHERE ...`
to dodge SQLite/MySQL's `REPLACE`-as-Command parse trap (a pre-existing
latent bug surfaced by the new operators). Swapped at WHERE / HAVING
assembly and the windowed-CTE filter-AST parse.

`||` is preprocessed to Python's `<<` (LShift) — same precedence as
SQL `||` relative to comparison/boolean/arithmetic ops — and
`_binop_to_sql` flattens LShift chains into n-ary `concat(...)` calls.

Test coverage: 19 new `TestStringHygieneFilters` tests in
`test_formula.py`; 24 dialect-translation + replace-predicate tests
in `test_sql_generator.py` (5 dialects × ops); 15 `parse_sql_predicate`
unit tests in new `test_sql_predicate.py`; 3 enrichment-runtime tests
in `test_reference_semantics.py`; 6 SQLite integration tests covering
both stages end-to-end. Updated injection tests to reflect the new
Mode A contract (user-trusted SQL; sqlglot is the dialect-aware gate).
2283 unit / 93 SQLite / 33 DuckDB / 48 Postgres integration tests pass.

Doc sync: `docs/concepts/references.md`, `docs/concepts/queries.md`,
`CLAUDE.md`, `.claude/skills/slayer-query.md`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both `_parse_node` (formula → FieldSpec) and `_call_to_sql` (filter →
SQL) made ad-hoc string checks against `ALL_TRANSFORMS` /
`STRING_HYGIENE_OPS` / `__like__` / `__notlike__`. Extract a
`_classify_call_name(name) -> CallCategory` helper as the single source
of truth; each walker still decides what to do with the category.

No behavior change. Same error messages, same SQL emission.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d1d0670c-e7ec-4fa6-afca-2ba0f7befd56

📥 Commits

Reviewing files that changed from the base of the PR and between 95c4321 and 77a7357.

📒 Files selected for processing (1)
  • tests/test_sql_generator.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_sql_generator.py

📝 Walkthrough

Walkthrough

This PR implements DEV-1378: adds a lowercase-only string-hygiene allowlist for DSL filters, rewrites SQL || into an intermediate concat form for parsing and flattens to concat(...) during emission, routes model/measure filters through SQL-mode parsing (parse_sql_predicate), and updates enrichment, generator, schema-drift, docs, and tests.

Changes

String-Hygiene Filter Functions & Mode Distinction

Layer / File(s) Summary
Documentation & Contracts
.claude/skills/slayer-query.md, CLAUDE.md, docs/concepts/queries.md, docs/concepts/references.md
Documents lowercase-only string-hygiene allowlist and `
Call Classification & ParsedFilter
slayer/core/formula.py
Adds call-name classification and hygiene allowlist, updates transform detection, and adds ParsedFilter.agg_refs.
Concat & LIKE Preprocessing
slayer/core/formula.py
Implements _preprocess_concat() (rewrites `
DSL-only parse_filter pipeline
slayer/core/formula.py
Refactors parse_filter() to DSL-only preprocessing + AST parse and returns ParsedFilter with sql, columns, synthesized_aliases, and agg_refs.
AST→SQL emission restrictions
slayer/core/formula.py
Restricts function emission to internal hygiene calls and internal helpers; flattens chained << to concat(...) during SQL emission.
SQLGenerator: safe predicate parsing
slayer/sql/generator.py
Adds SQLGenerator._parse_predicate() and uses it for measure.filter_sql CASE emission and top-level WHERE/HAVING parsing to avoid sqlglot mis-parsing.
Enrichment: mode-tagging & join resolution
slayer/engine/enrichment.py
Parses measure/model filters with parse_sql_predicate (strict=False) and query.filters with parse_filter (strict=True), builds processed_filters_with_mode, restricts transform extraction to DSL filters, and updates join/path collection and window-function scanning to parse by mode.
Schema drift: filter ref extraction
slayer/engine/schema_drift.py
Uses parse_sql_predicate for SQL-mode refs, adds _filter_refs_dsl to recover DSL agg refs for SlayerQuery.filters, and uses it for query-backed cascade checks.

Test Coverage for String-Hygiene Filters & SQL Mode

Layer / File(s) Summary
SQL predicate parsing tests
tests/test_sql_predicate.py
New tests for parse_sql_predicate: accept arbitrary SQL, validate pf.sql/pf.columns, and reject DSL/window constructs.
Enrichment-time Mode A tests
tests/test_reference_semantics.py
Tests that Mode A SQL filters (e.g., json_extract, lower) and __-delimited join-path filters enrich and persist.
Integration tests (SQLite)
tests/integration/test_integration.py
End-to-end tests for model filters (lower, json_extract, __ join paths), column filters (lower), and query filters (lower, replace).
DSL formula/filter tests
tests/test_formula.py
Adds TestStringHygieneFilters, verifies `
SQL generator & dialect tests
tests/test_sql_generator.py
Adjusts injection tests to accept sqlglot parse/token errors as rejections, pins dialect-specific translations for hygiene functions and concat forms, and validates replace(...) emission.
Cascade validation test
tests/test_validate_models.py
Adds cascade-drift test ensuring query-backed colon-syntax stage filters contribute refs for cascade detection.

Sequence Diagram(s)

sequenceDiagram
  participant User as User Query
  participant DSL as DSL Parser (parse_filter)
  participant SQLP as SQL Predicate Parser (parse_sql_predicate)
  participant Enr as Enrichment
  participant Gen as SQLGenerator
  User->>DSL: SlayerQuery.filters (DSL)
  User->>SQLP: SlayerModel.filters / Column.filter (SQL fragments)
  DSL->>Enr: processed DSL filters + transforms
  SQLP->>Enr: parsed SQL-mode predicates
  Enr->>Gen: enriched filters (WHERE/HAVING, measure.filter_sql)
  Gen->>SQLP: _parse_predicate for fragments
  Gen->>Enr: emits final SQL with concat(...) flattened
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • MotleyAI/slayer#96: Modifies join-path discovery and filter-routing logic in enrichment that overlaps with this PR's mode-distinction changes.
  • MotleyAI/slayer#45: Modifies parse_filter and preprocessing logic overlapping with hygiene/call-rewrite work.
  • MotleyAI/slayer#108: Changes filter-resolution and enrichment logic for model vs. query filters, sharing mode-tagging and parse-dispatch patterns.

Suggested reviewers

  • AivanF

Poem

🐰 I nibble whitespace, fold the case,
lower hops into tidy place.
Pipes become concat, filters find their mode,
DSL keeps tidy, SQL walks its road.
Hooray — the rabbits guard the code!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main technical change: introduction of string-hygiene operators combined with SQL predicate routing for Mode A, which aligns directly with the primary objectives of this PR.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch egor/dev-1378-add-string-hygiene-operators

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 (1)
tests/test_sql_generator.py (1)

3497-3497: ⚡ Quick win

Use keyword args in newly added _generate(...) calls.

The new calls pass 3 positional args; this conflicts with the repo rule and makes call-order mistakes easier.

♻️ Suggested pattern
- sql = await _generate(SQLGenerator(dialect=dialect), query, orders_model)
+ sql = await _generate(
+     generator=SQLGenerator(dialect=dialect),
+     query=query,
+     model=orders_model,
+ )

As per coding guidelines: "Use keyword arguments for functions with more than 1 parameter".

Also applies to: 6736-6737, 6757-6758, 6778-6779, 6800-6801, 6824-6825, 6845-6846

🤖 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_sql_generator.py` at line 3497, Replace positional arguments in
the new _generate(...) calls with keyword arguments to follow the coding
guideline; specifically change calls like
_generate(SQLGenerator(dialect=dialect), query, orders_model) to use named
parameters for the generator, query, and model (referencing the _generate
function, SQLGenerator instantiation, and the variables dialect, query,
orders_model) and apply the same change to the other occurrences listed so
parameters are passed by keyword rather than position.
🤖 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/core/formula.py`:
- Around line 1009-1042: The LIKE-preprocessor (_preprocess_like) only matches
bare/dotted identifiers so expressions like lower(name) like 'a%' fail; update
_preprocess_like to accept call- and parenthesized-left-hand-sides (e.g.,
function calls and parenthesis-wrapped expressions) or move LIKE handling into
the AST stage inside _filter_node_to_sql/_call_to_sql so CALL nodes are
recognized as valid LHS for LIKE/NOT LIKE; specifically, change the
pattern-matching logic in _preprocess_like to allow ast.Call-like text on the
left (functionName(...), (expr), or dotted calls) and ensure replacements
produce the same hygiene-helper form that _call_to_sql expects.

In `@slayer/engine/schema_drift.py`:
- Around line 550-560: The current _filter_refs(filter_str) only handles Mode A
SQL via parse_sql_predicate and is being reused for SlayerQuery.filters (Mode B
DSL); update the call-sites so SQL-only contexts (Column.filter,
SlayerModel.filters) continue to call _filter_refs which uses
parse_sql_predicate, and route SlayerQuery.filters through a new/alternate
DSL-aware extractor (e.g., _filter_refs_dsl or a branch in _filter_refs_on_base)
that uses the DSL parser to extract referenced columns/measures; locate usages
in _filter_refs_on_base and any callers that process SlayerQuery.filters and
switch them to the DSL extractor so validate_models() sees query-backed stage
filters correctly.

---

Nitpick comments:
In `@tests/test_sql_generator.py`:
- Line 3497: Replace positional arguments in the new _generate(...) calls with
keyword arguments to follow the coding guideline; specifically change calls like
_generate(SQLGenerator(dialect=dialect), query, orders_model) to use named
parameters for the generator, query, and model (referencing the _generate
function, SQLGenerator instantiation, and the variables dialect, query,
orders_model) and apply the same change to the other occurrences listed so
parameters are passed by keyword rather than position.
🪄 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: 1651889d-3dc2-4c28-a0ca-5cef3e73229e

📥 Commits

Reviewing files that changed from the base of the PR and between 1e3c1aa and 8df14b5.

📒 Files selected for processing (13)
  • .claude/skills/slayer-query.md
  • CLAUDE.md
  • docs/concepts/queries.md
  • docs/concepts/references.md
  • slayer/core/formula.py
  • slayer/engine/enrichment.py
  • slayer/engine/schema_drift.py
  • slayer/sql/generator.py
  • tests/integration/test_integration.py
  • tests/test_formula.py
  • tests/test_reference_semantics.py
  • tests/test_sql_generator.py
  • tests/test_sql_predicate.py

Comment thread slayer/core/formula.py Outdated
Comment thread slayer/engine/schema_drift.py
Address CodeRabbit + Sonar feedback on the DEV-1378 PR.

Correctness (CodeRabbit, MAJOR):
- formula.py: broaden `_LIKE_RE` so the LHS may be a hygiene call
  (`lower(name) like 'a%'`, `trim(email) not like '%@x'`). Three new
  test cases pin the behaviour.
- schema_drift.py: add `_filter_refs_dsl` for `SlayerQuery.filters`
  (Mode B DSL). The Mode-A switch in DEV-1378 left `_filter_refs_on_base`
  pointing at the SQL-only extractor, so stage filters with colon syntax
  or transforms collapsed to `[]` and `validate_models` missed cascading
  drops on query-backed models. `ParsedFilter` now carries
  `agg_refs: List[AggregatedMeasureRef]` so schema drift can recover the
  underlying measure names. Regression test added.

Sonar (new-code duplication):
- enrichment.py: collapse the duplicated inner-alias resolution branches
  (change/change_pct vs other self-join transforms) into one
  `_resolve_inner_alias` helper inside `_flatten_spec`.
- test_integration.py: extract the shared `items` SQLite setup into
  `_setup_items_db(tmp_path)`.
- test_sql_generator.py: collapse the 47-line cross-model `last`-filter
  test setup into a static `_filtered_last_cross_model_sql` helper on
  `TestFilteredMeasures`.

CodeRabbit nitpick:
- test_sql_generator.py:3497 — swap positional `_generate(...)` args
  for keywords.

2287 unit tests pass; ruff clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

6702-6811: ⚡ Quick win

Use keyword args for _generate(...) calls in these new tests.

Lines 6702, 6723, 6744, 6766, 6790, and 6811 pass 3 positional arguments to _generate(...). Please switch these to keyword arguments for consistency with repo rules.

As per coding guidelines: **/*.py: Use keyword arguments for functions with more than 1 parameter.

🤖 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_sql_generator.py` around lines 6702 - 6811, The tests call
_generate(SQLGenerator(dialect=dialect), query, orders_model) with positional
args; change each call to use keyword arguments (e.g.
_generate(generator=SQLGenerator(dialect=dialect), query=query,
model=orders_model) or the actual parameter names used by _generate) for the
calls in test_instr_translates_per_dialect, test_substr_translates_per_dialect,
test_concat_via_pipe_pipe_translates_per_dialect, test_replace_in_query_filter,
test_replace_in_column_filter, and the earlier invocation at the start of the
file so all six occurrences pass named parameters instead of positional ones.
tests/test_validate_models.py (1)

643-643: ⚡ Quick win

Use keyword args in _entry_for call.

Line 643 should pass named arguments to match repo conventions for multi-parameter calls.

Proposed change
-        qb_entry = _entry_for("qb_orders_filtered", out)
+        qb_entry = _entry_for(model_name="qb_orders_filtered", entries=out)

As per coding guidelines: **/*.py: Use keyword arguments for functions with more than 1 parameter.

🤖 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_validate_models.py` at line 643, Change the positional call to
_entry_for to use keyword arguments per repo convention: replace the positional
invocation _entry_for("qb_orders_filtered", out) with a call that passes
explicit parameter names for the two arguments (e.g., the name for the entry key
and the output variable) so the call to _entry_for uses keyword args; update the
invocation near qb_entry to reference the same variables but as named parameters
consistent with other usages of _entry_for.
🤖 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.

Nitpick comments:
In `@tests/test_sql_generator.py`:
- Around line 6702-6811: The tests call _generate(SQLGenerator(dialect=dialect),
query, orders_model) with positional args; change each call to use keyword
arguments (e.g. _generate(generator=SQLGenerator(dialect=dialect), query=query,
model=orders_model) or the actual parameter names used by _generate) for the
calls in test_instr_translates_per_dialect, test_substr_translates_per_dialect,
test_concat_via_pipe_pipe_translates_per_dialect, test_replace_in_query_filter,
test_replace_in_column_filter, and the earlier invocation at the start of the
file so all six occurrences pass named parameters instead of positional ones.

In `@tests/test_validate_models.py`:
- Line 643: Change the positional call to _entry_for to use keyword arguments
per repo convention: replace the positional invocation
_entry_for("qb_orders_filtered", out) with a call that passes explicit parameter
names for the two arguments (e.g., the name for the entry key and the output
variable) so the call to _entry_for uses keyword args; update the invocation
near qb_entry to reference the same variables but as named parameters consistent
with other usages of _entry_for.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 04b33c24-63f9-48aa-94c7-d204d504adbd

📥 Commits

Reviewing files that changed from the base of the PR and between 8df14b5 and 95c4321.

📒 Files selected for processing (7)
  • slayer/core/formula.py
  • slayer/engine/enrichment.py
  • slayer/engine/schema_drift.py
  • tests/integration/test_integration.py
  • tests/test_formula.py
  • tests/test_sql_generator.py
  • tests/test_validate_models.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • slayer/engine/enrichment.py
  • slayer/core/formula.py
  • tests/integration/test_integration.py

CodeRabbit nitpick — six new ``_generate(...)`` calls in the
``TestStringHygieneDialectTranslation`` and ``TestReplaceFunctionInPredicate``
classes passed positional arguments. Switch to keyword args per
CLAUDE.md ("Use keyword arguments for functions with more than 1
parameter").

The remaining seven positional ``_generate(...)`` calls earlier in the
file pre-date this PR and were not flagged — left alone to keep the
diff focused.

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

@ZmeiGorynych ZmeiGorynych merged commit 013fca7 into main May 11, 2026
4 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