Skip to content

Commit efebea9

Browse files
committed
Fix Jinja source parsing boundaries
1 parent 2888f85 commit efebea9

3 files changed

Lines changed: 104 additions & 6 deletions

File tree

src/parser/source_tree.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,18 @@ mod tests {
301301
assert!(viz.starts_with("VISUALISE FROM mtcars"));
302302
}
303303

304+
#[test]
305+
fn test_extract_sql_visualise_from_jinja_ref() {
306+
let query = "VISUALISE FROM {{ ref('fct_orders') }} DRAW point MAPPING x AS x, y AS y";
307+
let tree = SourceTree::new(query).unwrap();
308+
309+
let sql = tree.extract_sql().unwrap();
310+
assert_eq!(sql, "SELECT * FROM {{ ref('fct_orders') }}");
311+
312+
let viz = tree.extract_visualise().unwrap();
313+
assert!(viz.starts_with("VISUALISE FROM {{ ref('fct_orders') }}"));
314+
}
315+
304316
#[test]
305317
fn test_extract_sql_visualise_from_with_cte() {
306318
let query =
@@ -419,6 +431,15 @@ mod tests {
419431
assert!(sql.contains("SELECT * FROM 'mtcars.csv'"));
420432
}
421433

434+
#[test]
435+
fn test_extract_sql_from_first_jinja_ref() {
436+
let query = "FROM {{ ref('fct_orders') }} VISUALISE DRAW point MAPPING x AS x, y AS y";
437+
let tree = SourceTree::new(query).unwrap();
438+
439+
let sql = tree.extract_sql().unwrap();
440+
assert_eq!(sql, "SELECT * FROM {{ ref('fct_orders') }}");
441+
}
442+
422443
#[test]
423444
fn test_extract_sql_from_first_case_insensitive() {
424445
let query = "from sales visualise DRAW point MAPPING x AS x, y AS y";

tree-sitter-ggsql/grammar.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ function caseInsensitive(keyword) {
1717
module.exports = grammar({
1818
name: 'ggsql',
1919

20+
inline: $ => [
21+
$.source_ref,
22+
],
23+
2024
conflicts: $ => [
2125
[$.sql_portion],
2226
],
@@ -480,9 +484,16 @@ module.exports = grammar({
480484
repeat(seq('.', $.identifier))
481485
)),
482486

487+
source_ref: $ => choice(
488+
$.qualified_name,
489+
$.string,
490+
$.namespaced_identifier,
491+
$.jinja_template
492+
),
493+
483494
table_ref: $ => prec.right(seq(
484495
choice(
485-
field('table', choice($.qualified_name, $.string, $.namespaced_identifier, $.jinja_template)),
496+
field('table', $.source_ref),
486497
$.subquery,
487498
),
488499
optional(seq(
@@ -601,14 +612,14 @@ module.exports = grammar({
601612
// Option 1: Just FROM (inherit global mappings)
602613
seq(
603614
caseInsensitive('FROM'),
604-
field('layer_source', choice($.qualified_name, $.string, $.namespaced_identifier))
615+
field('layer_source', $.source_ref)
605616
),
606617
// Option 2: Mapping list (uses shared structure), optionally followed by FROM
607618
seq(
608619
$.mapping_list,
609620
optional(seq(
610621
caseInsensitive('FROM'),
611-
field('layer_source', choice($.qualified_name, $.string, $.namespaced_identifier))
622+
field('layer_source', $.source_ref)
612623
))
613624
)
614625
)
@@ -942,9 +953,9 @@ module.exports = grammar({
942953
// before ggsql executes SQL, but the parser must preserve them while
943954
// splitting SQL from VISUALISE.
944955
jinja_template: $ => token(choice(
945-
/\{\{[^}]*\}\}/,
946-
/\{%[^%]*%\}/,
947-
/\{#[^#]*#\}/
956+
seq('{{', repeat(choice(/[^}]+/, /}[^}]/)), '}}'),
957+
seq('{%', repeat(choice(/[^%]+/, /%[^%]/)), '%}'),
958+
seq('{#', repeat(choice(/[^#]+/, /#[^#]/)), '#}')
948959
)),
949960

950961
// Identifier for use in filter expressions - uses lower precedence so that

tree-sitter-ggsql/test/corpus/basic.txt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3649,3 +3649,69 @@ DRAW point
36493649
(viz_clause
36503650
(draw_clause
36513651
(geom_type)))))
3652+
3653+
================================================================================
3654+
SQL source with Jinja var containing dict literal
3655+
================================================================================
3656+
3657+
SELECT * FROM {{ var('table', {'fallback': 'orders'}) }}
3658+
VISUALISE x AS x, y AS y
3659+
DRAW point
3660+
3661+
--------------------------------------------------------------------------------
3662+
3663+
(query
3664+
(sql_portion
3665+
(sql_statement
3666+
(select_statement
3667+
(select_body
3668+
(from_clause
3669+
(table_ref
3670+
table: (jinja_template)))))))
3671+
(visualise_statement
3672+
(visualise_keyword)
3673+
(global_mapping
3674+
(mapping_list
3675+
(mapping_element
3676+
(explicit_mapping
3677+
value: (mapping_value
3678+
(column_reference
3679+
(identifier
3680+
(bare_identifier))))
3681+
name: (aesthetic_name)))
3682+
(mapping_element
3683+
(explicit_mapping
3684+
value: (mapping_value
3685+
(column_reference
3686+
(identifier
3687+
(bare_identifier))))
3688+
name: (aesthetic_name)))))
3689+
(viz_clause
3690+
(draw_clause
3691+
(geom_type)))))
3692+
3693+
================================================================================
3694+
Layer source with Jinja ref
3695+
================================================================================
3696+
3697+
VISUALISE
3698+
DRAW point MAPPING x AS x FROM {{ ref('fct_orders') }}
3699+
3700+
--------------------------------------------------------------------------------
3701+
3702+
(query
3703+
(visualise_statement
3704+
(visualise_keyword)
3705+
(viz_clause
3706+
(draw_clause
3707+
(geom_type)
3708+
(mapping_clause
3709+
(mapping_list
3710+
(mapping_element
3711+
(explicit_mapping
3712+
value: (mapping_value
3713+
(column_reference
3714+
(identifier
3715+
(bare_identifier))))
3716+
name: (aesthetic_name))))
3717+
layer_source: (jinja_template))))))

0 commit comments

Comments
 (0)