diff --git a/src/analyzer.rs b/src/analyzer.rs index 7448274..8557b4c 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -3,7 +3,7 @@ use memchr::memchr; use crate::action::Action; use crate::comment::Comment; use crate::error::SqlfmtError; -use crate::lexer::{self, LexState}; +use crate::lexer::{self, scan_dollar_string, LexState}; use crate::line::Line; use crate::node::{Node, NodeIndex}; use crate::node_manager::NodeManager; @@ -797,6 +797,13 @@ impl Analyzer { i = skip_jinja_block(bytes, i); continue; } + if bytes[i] == b'$' { + let ds_len = scan_dollar_string(&bytes[i..]); + if ds_len > 0 { + i += ds_len; + continue; + } + } if i + 1 < len && bytes[i] == b'/' && bytes[i + 1] == b'*' { in_comment = true; i += 2; diff --git a/src/lexer.rs b/src/lexer.rs index f19c0b9..c69c2a4 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -394,7 +394,7 @@ fn scan_block_comment(bytes: &[u8]) -> usize { } /// Scan a dollar-quoted string ($tag$...$tag$). `bytes` starts at `$`. -fn scan_dollar_string(bytes: &[u8]) -> usize { +pub(crate) fn scan_dollar_string(bytes: &[u8]) -> usize { // Find the end of the opening tag let mut tag_end = 1; while tag_end < bytes.len() diff --git a/tests/data/unformatted/221_dbt_config_dollar_quoted.sql b/tests/data/unformatted/221_dbt_config_dollar_quoted.sql new file mode 100644 index 0000000..d8d0a1e --- /dev/null +++ b/tests/data/unformatted/221_dbt_config_dollar_quoted.sql @@ -0,0 +1,64 @@ +-- disable-parser +{{ + config( + materialized='incremental', + transient=false, + unique_key='event_id', + meta={'final_schema': 'analytics'}, + incremental_strategy='merge', + on_schema_change='sync_all_columns', + full_refresh=false, + ) +}} +{% set app_comment_pattern = "'-- App Context'" %} +SELECT e.event_id AS event_id + , e.event_type AS event_type + , e.user_id AS user_id + , e.created_at AS created_at + , CASE + WHEN e.source = 'api' THEN 'api' + WHEN e.source = 'web' THEN 'web' + ELSE 'other' + END AS event_source + , try_parse_json(regexp_substr(e.payload, $$/\*\s*({.*"app":.*})\s*\*/$$, 1, 1, 'ie')) as event_meta + , e.duration_ms AS duration_ms + FROM {{ source('app', 'events') }} AS e + LEFT JOIN {{ ref('dim_users') }} AS u + ON e.user_id = u.user_id +{% if is_incremental() %} + WHERE e.created_at > ( + SELECT DATEADD(DAY, -2, MAX(created_at)) + FROM {{ this }} + ) +{% endif %} +)))))__SQLFMT_OUTPUT__((((( +-- disable-parser +{{ + config( + materialized='incremental', + transient=false, + unique_key='event_id', + meta={'final_schema': 'analytics'}, + incremental_strategy='merge', + on_schema_change='sync_all_columns', + full_refresh=false, + ) +}} +{% set app_comment_pattern = "'-- App Context'" %} +select + e.event_id as event_id, + e.event_type as event_type, + e.user_id as user_id, + e.created_at as created_at, + case + when e.source = 'api' then 'api' when e.source = 'web' then 'web' else 'other' + end as event_source, + try_parse_json( + regexp_substr(e.payload, $$/\*\s*({.*"app":.*})\s*\*/$$, 1, 1, 'ie') + ) as event_meta, + e.duration_ms as duration_ms +from {{ source("app", "events") }} as e +left join {{ ref("dim_users") }} as u on e.user_id = u.user_id +{% if is_incremental() %} + where e.created_at > (select dateadd(day, -2, max(created_at)) from {{ this }}) +{% endif %} diff --git a/tests/golden_test.rs b/tests/golden_test.rs index 1d07f40..bad0137 100644 --- a/tests/golden_test.rs +++ b/tests/golden_test.rs @@ -459,6 +459,10 @@ golden_test_clickhouse!( golden_unformatted_220_clickhouse_joins, "tests/data/unformatted/220_clickhouse_joins.sql" ); +golden_test!( + golden_unformatted_221_dbt_config_dollar_quoted, + "tests/data/unformatted/221_dbt_config_dollar_quoted.sql" +); // ============================================================================= // Unformatted golden tests — 300-series (Jinja formatting)