Skip to content

Commit

Permalink
Fix CTE insertion position when the model uses WITH RECURSIVE (dbt-la…
Browse files Browse the repository at this point in the history
  • Loading branch information
willbryant committed Apr 20, 2023
1 parent ada8860 commit e9fc2b3
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Fixes-20230420-123221.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Fixes
body: Fix CTE insertion position when the model uses WITH RECURSIVE
time: 2023-04-20T12:32:21.432848+12:00
custom:
Author: willbryant
Issue: "7350"
4 changes: 4 additions & 0 deletions core/dbt/compilation.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ def _inject_ctes_into_sql(self, sql: str, ctes: List[InjectedCTE]) -> str:
for token in parsed.tokens:
if token.is_keyword and token.normalized == "WITH":
with_stmt = token
elif token.is_keyword and token.normalized == "RECURSIVE" and with_stmt is not None:
with_stmt = token
break
elif not token.is_whitespace and with_stmt is not None:
break

if with_stmt is None:
Expand Down
10 changes: 10 additions & 0 deletions tests/functional/compile/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@
}} as fun
"""

with_recursive_model_sql = """
{{ config(materialized = 'ephemeral') }}
with recursive t(n) as (
select * from {{ ref('first_ephemeral_model') }}
union all
select n+1 from t where n < 100
)
select sum(n) from t;
"""

schema_yml = """
version: 2
Expand Down
16 changes: 16 additions & 0 deletions tests/functional/compile/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
first_ephemeral_model_sql,
second_ephemeral_model_sql,
third_ephemeral_model_sql,
with_recursive_model_sql,
schema_yml,
model_multiline_jinja,
)
Expand Down Expand Up @@ -54,6 +55,7 @@ def models(self):
"first_ephemeral_model.sql": first_ephemeral_model_sql,
"second_ephemeral_model.sql": second_ephemeral_model_sql,
"third_ephemeral_model.sql": third_ephemeral_model_sql,
"with_recursive_model.sql": with_recursive_model_sql,
}

def test_first_selector(self, project):
Expand Down Expand Up @@ -102,6 +104,20 @@ def test_no_selector(self, project):
"select 2 as fun",
]

def test_with_recursive_cte(self, project):
run_dbt(["compile"])

assert get_lines("with_recursive_model") == [
"with recursive __dbt__cte__first_ephemeral_model as (",
"select 1 as fun",
"),t(n) as (",
" select * from __dbt__cte__first_ephemeral_model",
" union all",
" select n+1 from t where n < 100",
")",
"select sum(n) from t;",
]


class TestCompile:
@pytest.fixture(scope="class")
Expand Down
1 change: 1 addition & 0 deletions third-party-stubs/sqlparse/sql.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ from typing import Tuple, Iterable
class Token:
def __init__(self, ttype, value): ...
is_keyword: bool
is_whitespace: bool
normalized: str

class TokenList(Token):
Expand Down

0 comments on commit e9fc2b3

Please sign in to comment.