Highlights
This minor release brings expanded BigQuery pipe-syntax support, a wave of ClickHouse grammar additions, a new rule, and continued progress on the Rust-backed parsing engine.
- BigQuery pipe syntax (
|>) is now modelled natively as a postfix operation on selectables, so pipe queries can follow aWITHclause, useEXCEPT/REPLACEafter a wildcard, and get correct indentation forAGGREGATE/EXTENDclauses. - ClickHouse gains support for
TRUNCATE TABLES/TRUNCATE DATABASE,EXCHANGE, theREGEXPoperator,GROUP BY WITH ROLLUP/CUBE/TOTALS, multi-columnLIMIT BY, andORDER BY ... WITH FILL ... INTERPOLATE, among other additions. - A new rule,
RF07, flags window-clause references that shadow aSELECTalias. lint,fix, andformatall gain a consistent--quietoutput mode.- This release introduces the first Rust-native lint rule detection (
CP01,CP03,CP04), extending the Rust engine beyond parsing and into linting for the first time — a major step toward our broader performance goals. Alongside this, numerous parity fixes (bracket matching, error messages, cache invalidation) keep the Rust and Python engines in sync.
Beyond that, there are parser improvements across Oracle, PostgreSQL, Snowflake, Databricks, T-SQL, MySQL/MariaDB, Teradata, Athena, DuckDB, SQLite, Impala, and SparkSQL, plus rule fixes that remove false positives from RF01-RF03, AL05, AM04, CV10, CV12, ST06, ST07, and PG01.
This release also includes first-time contributions from forty-seven new contributors. Thank you all for your contributions. 🏆
What’s Changed
- fix(rust): avoid double allocation in pyo3 getters for collection fields (trim/type/escape) (#8247) @rubytobi
- fix(rust): match Rust parser bracket matching to Python's behaviour on crossed, unclosed, and dialect-specific brackets (#8243) @rubytobi
- fix: pass sync=True to GhApi in release script (#8281) @alanmcruickshank
- feat(clickhouse): add support for
TRUNCATE TABLESstatement (#8279) @AndrewKurg - Allows %TYPE column type references in TABLE returns for functions in Postgres (#8269) @KuttKatrea
- test(ST06): pin reordering of a qualified reference with a parameterised cast (#8276) @adhavan18
- fix: auto-detect cached_property names and invalidate on pos_marker change (#8274) @JoeJoeflyn
- Db2: parse COMMENT ON, multi-grantee GRANT and view WITH [NO] ROW MOVEMENT (#8262) @davidpavlovschi
- CV12: don't fix join conditions when the join clause is templated (#8255) @devanshranjan10
- Support quiet output across lint, fix, and format (#8258) @TrapsterDK
- Fix Oracle SELECT INTO record field parsing (#8252) @subotac
- fix(ST02): preserve indexed expressions in NULL simplification (#8202) @dev-willbird1936
- fix(sparksql): allow expression values in SET statements (#8187) @Otto-Deviant1904
- Parse round-bracketed MAP types in Athena (#8246) @davidpavlovschi
- Fix AL01 loop on bracketed column aliases (#8245) @davidpavlovschi
- Snowflake: parse ADAPTIVE refresh mode, INITIALIZATION_WAREHOUSE, and streams on dynamic tables (#8241) @adhavan18
- test(ST11): pin the UNNEST + deeply nested struct case from #6997 (#8240) @adhavan18
- fix(rust): lexer/parser divergences in trim_chars inheritance and escape_replacements handling (#8224) @rubytobi
- fix(rust): match Python's parse-error message text (Sequence wording, token class/repr, grammar repr truncation) (#8225) @rubytobi
- duckdb: parse USING SAMPLE / TABLESAMPLE (#8229) @hdimer
- ST07: don't fix when a USING column is referenced unqualified (#8175) @vidigoat
- feat(clickhouse): add support for REGEXP operator (#8235) @AndrewKurg
- BigQuery: parse EXCEPT/REPLACE after a wildcard on an array element (#8194) @adhavan18
- CV10: don't corrupt string literals that use quote-doubling escapes (fixes #8179) (#8193) @rahul188
- perf(rust): frame-free terminal evaluation (#8168) @rubytobi
- fix(dialects/oracle): support positional substitution variables (&1, &&1) (#8233) @felipeatom
- rust(fix): Resolve two codegen-determinism hazards (id-cache collisions and unsorted currency-symbol regex) (#8226) @rubytobi
- Databricks: config to stop ST12 flagging command cell delimiters as consecutive terminators (#8197) @tsedeus
- feat(clickhouse): add support for EXCHANGE statement (#8221) @AndrewKurg
- Impala: add support for VALUES aliases (#8185) @strang3nt
- feat(clickhouse): add support for
TRUNCATE DATABASE(#8228) @AndrewKurg - fix(dialects/postgres): parse MERGE ... WHEN NOT MATCHED BY SOURCE/TARGET (#8216) @hdimer
- Postgres: stop LT01 spacing array types like
int [](#8222) @apoorva-01 - feat(postgres): support MERGE ... THEN DO NOTHING (#8223) @mbsdeepak
- fix(ci): collapse agent-scan into one pull_request_target workflow (#8217) @keraion
- Teradata: parse data type attributes ((NOT) CASESPECIFIC/CS, UPPERCASE) on expressions (#8124) @adhavan18
- Add a Python-vs-Rust parser/lexer parity test harness (#8198) @rubytobi
- Add GitHub Actions workflows for agent scan analysis and moderation (#8208) @peterbud
- Fix Databricks materialized view expectation constraints (#8212) @vetrovk
- feat(clickhouse): add clickhouse specific truncate table grammar (#8210) @AndrewKurg
- Fix CI lint instability caused by upgrading to Ruff 0.16.0 in scheduled runs. (#8211) @peterbud
- BigQuery: model pipe syntax as a postfix on selectables (#8195) @ricardoalencar-pr
- perf(templater): skip Jinja machinery for template-free files (#8161) @rubytobi
- AM04: resolve each UNION branch's wildcard against its own branch (#8183) @chuenchen309
- BigQuery: allow a pipe query after a WITH clause (#8191) @ricardoalencar-pr
- Indent BigQuery pipe AGGREGATE and EXTEND clause lists (#8190) @ricardoalencar-pr
- Fix ST07 pairing the wrong tables when a comma-join precedes USING (#8180) @chuenchen309
- Guard BigQuery and ClickHouse explicit view column lists in ST06 (#8178) @chuenchen309
- RF01: resolve UPDATE target alias nested in from_expression (MySQL) (#8176) @vidigoat
- Snowflake: parse scripting bind variables (:var) in expressions (#8163) @adhavan18
- Teradata: lex scientific-notation and leading-dot numeric literals (#8181) @chuenchen309
- fix(oracle): allow trailing-dot numeric literals like 1. (#8155) @Otto-Deviant1904
- fix(dialects/postgres): parse ORDER BY ... USING operator (#8182) @chuenchen309
- MySQL: parse CONVERT(expr, type) and CONVERT(expr USING charset) (#8177) @chuenchen309
- MariaDB: support IF [NOT] EXISTS on ALTER TABLE index and constraint clauses (#8174) @mrsinham
- Leave the jinja context out of config path resolution (#8173) @chuenchen309
- Fix IndexError on a two-character inline config value ending in a colon (#8170) @chuenchen309
- fix(config): validate render_variant_limit and runaway_limit (#8152) @anxkhn
- fix(rust): match Python's repr()-quoted, truncated unlexable-error text in the Rust lexer (#8147) @rubytobi
- fix(convention): make CV07's fix order deterministic (#8144) @keraion
- Don't hoist a subquery correlated in a later set expression branch (#8169) @chuenchen309
- Leave a schema-qualified NVL/IFNULL alone (#8172) @chuenchen309
- fix(docs): read stable_version as a nested pyproject key (#8150) @anxkhn
- docs: update dialect contributing guide to current grammar API (#8151) @anxkhn
- fix(parser): stop genexpr/comprehension recursion from halving safe parse-tree depth (#8148) @rubytobi
- Fix CV12 false positive on NATURAL JOIN (#8165) @chuenchen309
- Added support for * EXCEPT (col1, col2) in MERGE statement for SparkSQL (#8154) @timothyvries
- Add rule RF07 to flag window-clause references that shadow a select alias (#8153) @Booyaka101
- Fix clippy failures blocking main CI (#8160) @rubytobi
- Enforce clean clippy in CI and remove dead code across the Rust workspace (#8098) @rubytobi
- fix(rust): key the TemplatedFile conversion cache by object identity (#8143) @keraion
- fix(convention): build CV11 replacements parse-shaped (#8142) @keraion
- perf: lazy SegmentCloneMap in ST05 — clone only when a subquery is rewritten (#8141) @keraion
- refactor(rules): façade-safe segment checks — is_type over isinstance, uuid over id() (#8140) @keraion
- fix(rust): bare-class grammars consume matching tokens unchanged (#8138) @keraion
- Rename Rust skip-backward-to-code helpers to match Python (#8149) @apoorva-01
- feat(rust): port CP03 and CP04 to Rust-native detection (#8069) @WittierDinosaur
- Add opt-in T-SQL rule to prefer
ASaliasing overalias = expression(#8085) @copilot-swe-agent[bot] - fix(core): re-parent provided children in
BaseSegment.copy(#8109) @keraion - fix(config): coerce quoted allow_implicit_indents deprecation value (#8135) @anxkhn
- Oracle: parse XMLATTRIBUTES alias list in XMLELEMENT (#8113) @apoorva-01
- MySQL: don't flag index prefix length with LT01 (#8128) @apoorva-01
- docs: fix developing-rules guide to document crawl_behaviour, not removed fields (#8137) @anxkhn
- Fix nested bracket-type mismatch recovering instead of raising, matching Python's resolve_bracket (#8120) @rubytobi
- docs(reflow): fix ReflowSequence.respace filter value in docstring (#8136) @anxkhn
- fix(rust): drop Indent/Dedent that are direct children of Bracketed (#8108) @keraion
- fix(core): remove orphaned temp file when safe file replace fails (#8134) @anxkhn
- Teradata: make OVERLAPS an infix comparison operator (#8123) @apoorva-01
- feat(mysql): support GROUP BY ... WITH ROLLUP (supersedes #8066, indent-safe) (#8130) @Synvoya
- Fix unclosed GREEDY-mode bracket recovering instead of raising (#8121) @rubytobi
- Fix isinstance-preserved token type dropped by RustParser.parse() (#8122) @rubytobi
- fix: RF02 should not flag BigQuery whole-row table references (#8133) @mvanhorn
- Postgres: stop LT01 splitting psql variables like :status (#8131) @Rishi943
- RF03: treat Athena as a struct dialect (#8132) @Rishi943
- Athena: accept ARRAY(type) as well as ARRAY (#8127) @apoorva-01
- Fix mismatched bracket type giving generic error only (#8119) @rubytobi
- Fix Bracketed GREEDY mode absorbing trailing trivia into unparsable segment (#8118) @rubytobi
- Fix/greedy match stray closing bracket (#8117) @rubytobi
- Read Jinja macro files with the configured encoding (#6633) (#8103) @apoorvdarshan
- fix GREEDY sequence partial-match failure dropping matched children (#8116) @rubytobi
- fix(sqlite): parse CTE materialization hints (#8126) @karimudev
- ClickHouse: parse GROUP BY WITH ROLLUP / CUBE / TOTALS (#8037) (#8101) @apoorvdarshan
- fix(rust): honor RegexParser ignore_case (#8107) @keraion
- ClickHouse: parse multi-column LIMIT n BY a, b (#8030) (#8105) @apoorvdarshan
- ClickHouse: parse ORDER BY / LIMIT / SETTINGS on non-final union members (#8031) (#8100) @apoorvdarshan
- Don't strip newlines that sit directly before a comment (#8057) @CodeBlackwell
- perf(linter): dedupe source patches with a set, not a list (#8084) @anxkhn
- [databricks]: support bare Databricks notebook magic cells (#8011) @peterbud
- Add regression tests for Lexer and Parser divergences (#8106) @rubytobi
- Oracle: parse FOR UPDATE ... NOWAIT / WAIT n / SKIP LOCKED (#8097) (#8099) @apoorvdarshan
- Parse Oracle function RETURN with %type and %rowtype (#8090) @apoorva-01
- fix(clickhouse): parse tuple element access on any expression (#8062) @Sanjays2402
- Stop LT01 spacing Oracle %type/%rowtype like modulo (#8096) @apoorva-01
- TSQL: restore normalization kwargs on the patched single_quote lexer (RF05 false positive on quoted aliases) (#8088) @Pawansingh3889
- clickhouse: parse ORDER BY ... WITH FILL ... INTERPOLATE (#8029) (#8065) @apoorvdarshan
- perf: bypass setattr in RawSegment.init via direct dict writes (#8093) @rubytobi
- perf: remove duplicate cache invalidation in
BaseSegment.__init__(#8091) @rubytobi - perf: avoid quadratic tuple concatenation when applying match results (#8089) @rubytobi
- fix: raise SQLFluffUserError for invalid rule config option values (#8095) @anxkhn
- docs: fix BaseGrammar.copy terminators arg reference (#8094) @anxkhn
- docs(cli): correct fix --quiet help note about --force (#8083) @anxkhn
- docs(parser): correct min_delimiters type and default in Delimited docstring (#8082) @anxkhn
- fix(sparksql): treat LEFT/RIGHT as non-reserved so they can be lambda variables (#8050) @anxkhn
- Fix Oracle SQLPlus SET SCAN parsing (#8081) @vetrovk
- perf(python): avoid deep-copying dialect_obj/templater_obj in FluffConfig.copy (#8079) @rubytobi
- perf(rust): compare Arc by pointer instead of by value in PositionMarker merge (#8078) @rubytobi
- Parse Oracle ALTER TABLE ADD with bracketed constraints (#8076) @apoorva-01
- Parse Oracle FOR loop ranges with function-call bounds (#8077) @apoorva-01
- Parse Oracle SQL*Plus ACCEPT and REMARK commands (#8067) @vetrovk
- [codex] Parse SparkSQL SET literal lists (#8052) @EltonChang1
- Fix LT07 non-idempotent fix for CTE brackets that become multi-line (#8068) @gaoflow
- fix(postgres): parse COPY ... FROM STDIN inline data blocks (#8059) @truffle-dev
- Parse ClickHouse parametrized JSON type arguments (#8053) @billykern
- spike(rust): Rust-native lint rule detection (CP01 proof-of-concept) (#7984) @WittierDinosaur
- Support WITH TAG clause in Snowflake CREATE TASK (#8061) @apoorvdarshan
- Fix ClickHouse double equals parsing (#8045) @ishaanlabs-gg
- fix(postgres): parse psql variable placeholders as COPY targets (#8027) @truffle-dev
- docs(dbt): recommend explicit utf-8 encoding (#8041) @ishaanlabs-gg
- Split PG01 foreign key NOT VALID check (#7967) @cyphercodes
- fix(snowflake): parse SYSTEM function expressions (#8040) @ishaanlabs-gg
- Dialect/Snowflake: support dbt projects and dcm projects (#8039) @evgf
- fix(rust): correct subquery/SELECT parse via GREEDY-family cache skip (#8026) @keraion
- fix(mysql): parse JSON_VALUE RETURNING type with CHARACTER SET (#8028) @anxkhn
- fix(mysql): parse JSON_TABLE COLUMNS as identifiers not references (#8025) @truffle-dev
- perf(rust): share class_types as a per-kind Arc instead of per-token HashSet (#8022) @keraion
- fix(teradata): parse BTEQ FILE= command arguments (#8023) @anxkhn
- perf(rust): memoize Ref child-grammar resolution (~22% faster parsing) (#8024) @keraion
- perf(rust): borrow static Token strings as Cow<'static, str> (#8021) @keraion
- Fix RF02 false positive on table alias inside a query hint (#8018) @truffle-dev
- perf(rust): borrow static segment type/class strings into the Node + arena tree (#8020) @keraion
- perf(rust): compute raw_normalized lazily with cached regex compilation (#8019) @keraion
- perf(rust): replace HashMap results store with single Option slot in parse frame stack (#8017) @rubytobi
- Parse ClickHouse C-style ternary conditional operator (#8016) @truffle-dev
- refactor(rust): encapsulate raw/raw_upper in RawString to enforce invariant (#8009) @rubytobi
- fix: preserve Oracle execute file at-sign spacing (#8015) @ekkoitac
- fix(oracle): parse INTERVAL data types in CAST expressions (#8014) @copilot-swe-agent[bot]
- [Oracle] Parse
TIMESTAMP WITH LOCAL TIME ZONEinCAST(...)(#8012) @copilot-swe-agent[bot] - fix(rust): match Python's bracket-nesting depth in Anything grammars (#8013) @keraion
- perf(rust): fused RsMatchResult->BaseSegment builder (native AST, gated) (#7983) @WittierDinosaur
- Fix RF03 false positive on whole-row references (e.g. postgres
->>) (#7921) @Labib-Bin-Salam - Fix RF02 false positive on SELECT * EXCEPT (...) columns (#7999) @arpitjain099
- fix(rust): Make Parser metrics accessible again (#8006) @keraion
- perf(rust): replace Vec/String frame fields with SmallVec and static str (#8002) @rubytobi
- feat(rust): id-addressable arena tree (RsTree/RsHandle) substrate (#7986) @keraion
- perf(rust): remove RefCell wrappers from parser caches (#8004) @rubytobi
- perf(rust): precompute raw_upper on token construction (#7991) @rubytobi
- perf: replace per-token uuid4 with a tagged monotonic counter (Python + Rust) (#7997) @keraion
- fix(rust): match Python leaf segment types for TypedParser and Anything brackets (#8001) @keraion
- fix(rust): flatten only raw-segment wrappers in
Node::to_tuple(#8000) @keraion - fix(rust): correct aux_end so node class_types aren't truncated (#7985) @WittierDinosaur
- fix(oracle): parse PIVOT/UNPIVOT with WHERE or num label (#7973) (#7974) @GHRF
- perf(rust): defer RawSegment.representation to cached_property and replace uuid4 with os.urandom (#7988) @rubytobi
- perf(rust): fix O(n²) validation in map_template_slices (#7995) @rubytobi
- perf(rust): eliminate dummy
Arc<MatchResult>sentinel inRefState(#7996) @rubytobi - Parse Databricks view comments as comment clauses (#7993) @vetrovk
- perf(python): guard linter stringify calls behind isEnabledFor (#7989) @rubytobi
- perf(rust): wrap cached RegexMode entries in Arc to avoid cloning on cache hit (#7990) @rubytobi
- fix: don't crash when a directory named .sqlfluff exists (#6617) (#7975) @Shevilll
- Support T-SQL ALTER CREDENTIAL and ALTER/DROP DATABASE SCOPED CREDENTIAL (#7970) @Pawansingh3889
- perf(rust): add per-stage profiling to the Rust parse path (#7982) @WittierDinosaur
- fix: allow EXTENDED as identifier in Athena SELECT/CTE (#7977) @Jyriu
- rust: move parser metrics into its own struct (#7981) @keraion
- rust: Fix Bracketed
allow_gaps=falseSTRICT to match Python (#7979) @keraion - docs: refresh sqlfluffrs AGENTS.md for the multi-crate workspace (#7980) @WittierDinosaur
- refactor/docs: FrameContext/phase + Engine docs (#7978) @keraion
- perf: cache terminator match results in greedy_match_table_driven (#7925) @rubytobi
- reflow: Support strict fixes; LT03 add pipe operator (#7966) @keraion
- sparksql: Add iceberg merge support (#7968) @keraion
- fix(oracle): support DEFAULT keyword in pragma restrict_references (fixes #7957) (#7965) @gaoflow
- refactor: extract reference/alias segment methods into dialect-dispatched free functions (#7962) @keraion
- perf: replace inline OneOf terminator check with is_terminated_table_driven (#7924) @rubytobi
- perf: setup TPC benchmarks and collect initial numbers (#7923) @rubytobi
- Don't flag required alias on T-SQL table-valued XML methods (AL05) (#7955) @gaoflow
- chore: bump dependency click<8.5.0 (#7961) @keraion
- rust: Don't strip debuginfo on bench for flamegraph (#7963) @keraion
- Added support for INSERT ... BY NAME for SparkSQL (#7959) @timothyvries
- [oracle] Parse legacy
(+)joins against literals without LT02 false positives (#7953) @copilot-swe-agent[bot] - fix: AL05 false positive on whole-row alias references (postgres) (#7906) @Bortlesboat
- ci: Add Rust fmt/clippy/unit-test job for sqlfluffrs (#7942) @keraion
- Support Oracle slash batch delimiter after views (#7945) @koriyoshi2041
- Support Oracle KEEP (DENSE_RANK ... FIRST/LAST ... ) syntax (#7950) @GHRF
- Allow Snowflake UNPIVOT as an identifier (#7946) @koriyoshi2041
- chore(rust): bump pyo3 to 0.29.0 (#7954) @keraion
- databricks: Fix foreign key ON DELETE/ON UPDATE action ordering (#7952) @copilot-swe-agent[bot]
- Support Databricks infix collation (#7943) @koriyoshi2041
- Support Databricks TEMP VIEW (#7951) @koriyoshi2041
- Support Oracle LANGUAGE JAVA NAME function clauses (#7944) @vetrovk
- Support Databricks ?:: try_cast operator (#7927) @Booyaka101
- refactor(rust): Drop unused
transparent_positionsframe field (#7936) @keraion - codegen: Error on missing grammar handlers at build time (#7935) @keraion
- fix(rust): Update stale GrammarTables::new test calls (#7934) @keraion
- fix(LT02): indent oscillation for loop-conditional trailing commas (#7928) @keraion
- Support for
DBT_ENGINE_configs and additionalDBT_ENGINE_PROFILE(#7907) @yann1cks - parser: Increase default max_parse_depth to
600(#7922) @keraion - Support T-SQL CREATE CREDENTIAL (#7920) @Booyaka101
- Fix LT13 autofix before empty-rendering dbt config blocks (#7901) @copilot-swe-agent[bot]
- Fix Teradata date cast parsing (#7919) @henyanagar
- fix: ST10 false positive when RHS operand is part of a binary expression (#7918) @Booyaka101
- Update so that sqlfluff can parse backquoted databricks function argu… (#7917) @VictorAtIfInsurance
- fix(rust): Bracket optimization shouldn't match non-code. (#7916) @keraion
New Contributors
- @henyanagar made their first contribution in #7919
- @yann1cks made their first contribution in #7907
- @vetrovk made their first contribution in #7944
- @GHRF made their first contribution in #7950
- @Bortlesboat made their first contribution in #7906
- @timothyvries made their first contribution in #7959
- @gaoflow made their first contribution in #7955
- @Jyriu made their first contribution in #7977
- @Pawansingh3889 made their first contribution in #7970
- @Shevilll made their first contribution in #7975
- @arpitjain099 made their first contribution in #7999
- @Labib-Bin-Salam made their first contribution in #7921
- @ekkoitac made their first contribution in #8015
- @truffle-dev made their first contribution in #8016
- @anxkhn made their first contribution in #8023
- @evgf made their first contribution in #8039
- @ishaanlabs-gg made their first contribution in #8040
- @apoorvdarshan made their first contribution in #8061
- @billykern made their first contribution in #8053
- @EltonChang1 made their first contribution in #8052
- @apoorva-01 made their first contribution in #8077
- @Sanjays2402 made their first contribution in #8062
- @CodeBlackwell made their first contribution in #8057
- @karimudev made their first contribution in #8126
- @Rishi943 made their first contribution in #8132
- @mvanhorn made their first contribution in #8133
- @Synvoya made their first contribution in #8130
- @chuenchen309 made their first contribution in #8165
- @mrsinham made their first contribution in #8174
- @Otto-Deviant1904 made their first contribution in #8155
- @adhavan18 made their first contribution in #8163
- @vidigoat made their first contribution in #8176
- @ricardoalencar-pr made their first contribution in #8190
- @AndrewKurg made their first contribution in #8210
- @mbsdeepak made their first contribution in #8223
- @hdimer made their first contribution in #8216
- @strang3nt made their first contribution in #8185
- @tsedeus made their first contribution in #8197
- @felipeatom made their first contribution in #8233
- @rahul188 made their first contribution in #8193
- @davidpavlovschi made their first contribution in #8245
- @dev-willbird1936 made their first contribution in #8202
- @subotac made their first contribution in #8252
- @TrapsterDK made their first contribution in #8258
- @devanshranjan10 made their first contribution in #8255
- @JoeJoeflyn made their first contribution in #8274
- @KuttKatrea made their first contribution in #8269