Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -671,8 +671,15 @@ jobs:
max_ratio: "1.10"
edit_max_ratio: "1.20"
edit_max_cpu_ratio: "1.75"
# Temporary migration allowance; restore 1.10 once the base also runs default Rules.
# Default SQL Rules add a cold cost over select = [] (1.19x to 1.20x wall, 1.32x to
# 1.33x CPU in this job), so only the cold limits scale; warm and edit keep 1.10.
# The compiler-only cache guard at 3,000 models keeps the select = [] budgets.
- kind: fresh
label: fresh default Rules
max_ratio: "1.10"
cold_max_ratio: "1.25"
cold_max_cpu_ratio: "1.41"
- kind: dense
label: contracts CPU vs base
gate_phase: contracts_cpu_ms
Expand Down Expand Up @@ -746,12 +753,16 @@ jobs:
env:
KIND: ${{ matrix.kind }}
MAX_RATIO: ${{ matrix.max_ratio }}
COLD_MAX_RATIO: ${{ matrix.cold_max_ratio }}
COLD_MAX_CPU_RATIO: ${{ matrix.cold_max_cpu_ratio }}
EDIT_MAX_RATIO: ${{ matrix.edit_max_ratio }}
EDIT_MAX_CPU_RATIO: ${{ matrix.edit_max_cpu_ratio }}
run: |
uv run python -m scripts.compare_compile_performance \
--kind "${KIND}" \
--modes cold warm edit \
${COLD_MAX_RATIO:+--cold-max-ratio "$COLD_MAX_RATIO"} \
${COLD_MAX_CPU_RATIO:+--cold-max-cpu-ratio "$COLD_MAX_CPU_RATIO"} \
${EDIT_MAX_RATIO:+--edit-max-ratio "$EDIT_MAX_RATIO"} \
${EDIT_MAX_CPU_RATIO:+--edit-max-cpu-ratio "$EDIT_MAX_CPU_RATIO"} \
--max-ratio "${MAX_RATIO}" \
Expand Down
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ sqlbuild-recall = { path = "crates/sqlbuild-recall" }
sqlbuild-scopes = { path = "crates/sqlbuild-scopes" }
sqlbuild-sqltext = { path = "crates/sqlbuild-sqltext" }
sqlbuild-state = { path = "crates/sqlbuild-state" }
# SQL lint Rules, quality Rules, the formatter and the canonical token stream.
sqlclean = { git = "https://github.com/sqlstack-io/sqlclean.git", rev = "571e40a746b3d8d20d6118620f05428eca45243c" }
stacker = "0.1"
syn = { version = "2", features = ["full", "visit", "extra-traits"] }
tempfile = "3"
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/dbt-compare/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ average.
| Python macros | Jinja macros producing the same SQL |
| `audits [not_null]` | `data_tests: [not_null]` |
| SQL tests with fixture CTEs | `unit_tests` with the same input and expected rows |
| `[rules] select = ["SQBR", "XSQBR"]` (built-in Rules plus one custom Rule) | none |
| `[rules] select = ["SQBR", "SQLCLEAN", "XSQBR"]` (built-in Rules, SQLClean Rules and one custom Rule) | none |

`sqlbuild-norules/` is the same project with `[rules] select = []`. That turns off configurable Rules;
column binding, type inference and contract checks still run.
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/dbt-compare/write_dbt_dense.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def write_norules(project: Path) -> Path:
shutil.copytree(project, target)
toml = target / "sqlbuild_project.toml"
text = toml.read_text()
new = text.replace('select = ["SQBR", "XSQBR"]', "select = []")
new = text.replace('select = ["SQBR", "SQLCLEAN", "XSQBR"]', "select = []")
assert new != text
toml.write_text(new)
return target
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/duckshop-10k/checksum.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
generator 2 files 10874 sha256 7dd651a573bce90f9816889cdeece75b9f837137813a033125daafa28b7b70eb
generator 3 files 10874 sha256 5f3d30b82ebf52f2eb869494f52f77d1699f9a08743febd6ee9ca0cb22ed0611
2 changes: 1 addition & 1 deletion benchmarks/duckshop-10k/duckshop10k/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
regenerate checksum.txt, whenever a change alters any generated file.
"""

GENERATOR_VERSION = "2"
GENERATOR_VERSION = "3"
SEED = 20261024
PROJECT_NAME = "duckshop_10k"
6 changes: 3 additions & 3 deletions benchmarks/duckshop-10k/duckshop10k/declarations.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,23 +321,23 @@ def union_relations(relations: list[SqlResourceRef], columns: list[str]) -> str:

MACRO_EXCEPTIONS: tuple[tuple[str, str, str], ...] = (
(
"SQBRSQL018",
"SQLCLEAN018",
"cdc_latest",
"""
The macro cannot see its callers' keys on its own. Every call site is still checked, against the
unique, non-null _seq that each change-log source declares.
""",
),
(
"SQBRSQL018",
"SQLCLEAN018",
"cdc_history",
"""
The macro cannot see its callers' keys on its own. _seq is unique and non-null in every change-log
source, so ordering by it within a key and load is deterministic.
""",
),
(
"SQBRSQL034",
"SQLCLEAN034",
"load_metadata",
"""
A column-list macro body is a select list by construction. Its columns are inserted into the
Expand Down
6 changes: 3 additions & 3 deletions benchmarks/duckshop-10k/duckshop10k/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ def project_toml() -> str:
lookup_customer_id = "C000001"

[rules]
select = ["SQBRSQL", "SQBRGRAPH", "SQBRCONTRACT", "SQBRTEST", "XSQBRDUCK"]
# SQBRSQL021: `SELECT * FROM final` and the change-log macro's pass-through CTEs are the house
select = ["SQLCLEAN", "SQBRGRAPH", "SQBRCONTRACT", "SQBRTEST", "XSQBRDUCK"]
# SQLCLEAN021: `SELECT * FROM final` and the change-log macro's pass-through CTEs are the house
# style; each model's columns block, not its terminal SELECT, owns the output shape.
# SQBRCONTRACT101: exact contracts are reserved for published dim_ and fct_ models.
# SQBRTEST202: a sample of models carries unit tests, as in a project of this size.
ignore = ["SQBRSQL021", "SQBRCONTRACT101", "SQBRTEST202"]
ignore = ["SQLCLEAN021", "SQBRCONTRACT101", "SQBRTEST202"]
'''


Expand Down
4 changes: 2 additions & 2 deletions benchmarks/duckshop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The core project, without the [edge-case area](#edge-cases):
- 5 seeds, 2 SQL functions (a scalar UDF and a table function) and a Python UDF.
- SQL and Python macros, folder-scoped constants and enums, generic and singular audits, model
schemas, hooks, incremental models with cursor windows, and snapshots with both strategies.
- Rules select `SQBRSQL`, `SQBRGRAPH`, `SQBRCONTRACT`, `SQBRTEST` and the project's own
- Rules select `SQLCLEAN`, `SQBRGRAPH`, `SQBRCONTRACT`, `SQBRTEST` and the project's own
`XSQBRDUCK` rules, with reviewed exceptions in `sqlbuild_project.toml`. Every model must have at least one unit test (`SQBRTEST202`).

## Tests and scenarios
Expand Down Expand Up @@ -225,7 +225,7 @@ noted here:
static types of the columns it reads from a CTE through a table alias (`FROM listings AS l`), so
`rpt_edge_partner_listing_export` declares only the name and audits of `"listingId"`, without
types.
- `SQBRSQL018` can only take proof from single-column keys. Append-only feeds redeliver rows, so
- `SQLCLEAN018` can only take proof from single-column keys. Append-only feeds redeliver rows, so
their id is unique only together with `_loaded_at` (the `unique_per_delivery` audit), and the
staging models that keep the latest delivery carry reviewed exceptions.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ final AS (
THEN 'APAC'
WHEN '965'
THEN 'MENA'
WHEN NULL
THEN NULL
ELSE 'EU'
END::VARCHAR AS caller_region,
CASE
Expand Down
40 changes: 20 additions & 20 deletions benchmarks/duckshop/project/sqlbuild_project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,30 +104,30 @@ edge_delivery_batch = "2"
"ARRAY" = "JSON"

[rules]
select = ["SQBRSQL", "SQBRGRAPH", "SQBRCONTRACT", "SQBRTEST", "XSQBRDUCK"]
# SQBRSQL021: `SELECT * FROM final` and the change-log macro's pass-through CTEs are the house
select = ["SQLCLEAN", "SQBRGRAPH", "SQBRCONTRACT", "SQBRTEST", "XSQBRDUCK"]
# SQLCLEAN021: `SELECT * FROM final` and the change-log macro's pass-through CTEs are the house
# style; each model's columns block, not its terminal SELECT, owns the output shape.
# SQBRCONTRACT101: exact contracts are reserved for published dim_ and fct_ models.
ignore = ["SQBRSQL021", "SQBRCONTRACT101"]
ignore = ["SQLCLEAN021", "SQBRCONTRACT101"]

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/_sqlbuild/macros/sql/cdc_latest.sql"
reason = """
The macro cannot see its callers' keys on its own. Every call site is still checked, against the
unique, non-null _seq that each change-log source declares.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/marts/core/dim_date.sql"
reason = """
SEQ4() is distinct for every row of one GENERATOR call, so ordering by it is unique. ROW_NUMBER()
renumbers it because SEQ4() alone may leave gaps.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/clickstream/stg_app_events.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -137,7 +137,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/clickstream/stg_email_events.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -147,7 +147,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/clickstream/stg_experiment_assignments.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -157,7 +157,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/clickstream/stg_search_queries.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -167,7 +167,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/clickstream/stg_web_events.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -177,7 +177,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/finance/stg_settlement_reports.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -187,7 +187,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/fulfilment/stg_carrier_webhook_errors.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -197,7 +197,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/fulfilment/stg_carrier_webhooks.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -207,7 +207,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/inventory/stg_stock_movements.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -217,7 +217,7 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL018"
rule = "SQLCLEAN018"
path = "models/staging/partners/stg_partner_orders.sql"
reason = """
Append-only feeds redeliver rows, so the source id repeats across loads and only (id, _loaded_at)
Expand All @@ -227,23 +227,23 @@ whichever copy ranks first.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL034"
rule = "SQLCLEAN034"
path = "models/staging/_sqlbuild/macros/sql/load_metadata.sql"
reason = """
A column-list macro body is a select list by construction. Its columns are inserted into the
caller's own CTE, where the rule is checked again.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL034"
rule = "SQLCLEAN034"
path = "macros/sql/money_columns.sql"
reason = """
A column-list macro body is a select list by construction. Its columns are inserted into the
caller's own CTE, where the rule is checked again.
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL028"
rule = "SQLCLEAN028"
path = "models/intermediate/catalogue/int_category_paths.sql"
reason = """
LEVEL and the CONNECT_BY_ROOT operator are hierarchical-query pseudo-columns that cannot be
Expand All @@ -269,7 +269,7 @@ cart_funnel__assigns_furthest_stage_per_session scenario covers the funnel inste
"""

[[rules.rule_exceptions]]
rule = "SQBRSQL038"
rule = "SQLCLEAN038"
path = "models/marts/executive/rpt_executive_kpis_daily.sql"
relation = "metric_regions"
reason = """
Expand All @@ -279,7 +279,7 @@ days without activity report zero instead of disappearing. Both sides are small

# >>> edge-case area (scripts/core_project.py leaves this block out of the core copy)
[[rules.rule_ignores]]
rules = ["SQBRSQL027"]
rules = ["SQLCLEAN027"]
paths = ["models/marts/edge_cases/rpt_edge_stock_reconciliation.sql"]
reason = """
The edge case selects the key columns that FULL OUTER JOIN ... USING merges. They belong to neither
Expand Down
2 changes: 0 additions & 2 deletions crates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ playground project on Windows.

- Tests live next to the module they cover; run them for one crate with
`cargo test -p <crate>` or for all crates with `cargo test --workspace`.
- `sqlbuild-analysis` has a `test-support` feature that exposes test-only helpers to the tests of
dependent crates. Enable it only from `[dev-dependencies]`.
- The build identity (`sqlbuild._native.BUILD_IDENTITY`) hashes the sources of every crate in
this directory, so editing any crate invalidates the native caches keyed on it.
- maturin builds `crates/sqlbuild-python`; the release version lives in
Expand Down
6 changes: 1 addition & 5 deletions crates/sqlbuild-analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ license.workspace = true
publish.workspace = true
rust-version.workspace = true

[features]
default = []
# Expose test-only helpers to the tests of dependent crates.
test-support = []

[dependencies]
memchr.workspace = true
polyglot-sql.workspace = true
Expand All @@ -30,6 +25,7 @@ sqlbuild-model-config.workspace = true
sqlbuild-render.workspace = true
sqlbuild-scopes.workspace = true
sqlbuild-sqltext.workspace = true
sqlclean.workspace = true

[lints]
workspace = true
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use crate::constants::DATETIME_TYPE;
use polyglot_sql::tokens::{Token, TokenType};
use sqlbuild_core::constants::TIMESTAMP_TYPE;

use crate::sql_tokens::main::projection_spans::projection_spans;
use polyglot_sql::{Dialect, DialectType, ValidationError};
use regex::Regex;
use sqlclean::sql_tokens::main::projection_spans::projection_spans;
use std::sync::LazyLock;

const INVALID_CAST: &str = "E218";
Expand Down
Loading
Loading