Skip to content

Commit

Permalink
Fix #830, remove restriction on recursive CTE (#835)
Browse files Browse the repository at this point in the history
* Fix #830, remove incorrect restriction on recursive CTE

I tested this with sqlite and the queries execute successfully. It's
possible that there are other incorrect behaviors that can be
pre-emptively caught, perhaps those could be added in a new PR later.

* Add test

---------

Co-authored-by: Andrew Baxter <>
  • Loading branch information
andrewbaxter authored Dec 1, 2024
1 parent 81117a7 commit 6771326
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 9 deletions.
9 changes: 0 additions & 9 deletions src/backend/query_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,15 +774,6 @@ pub trait QueryBuilder:
"Cannot build a with query that has no common table expression!"
);

if with_clause.recursive {
assert_eq!(
with_clause.cte_expressions.len(),
1,
"Cannot build a recursive query with more than one common table! \
A recursive with query must have a single cte inside it that has a union query of \
two queries!"
);
}
for cte in &with_clause.cte_expressions {
if !cte_first {
write!(sql, ", ").unwrap();
Expand Down
44 changes: 44 additions & 0 deletions tests/sqlite/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1792,3 +1792,47 @@ fn sub_query_with_fn() {
r#"SELECT ARRAY((SELECT * FROM "character"))"#
);
}

#[test]
fn recursive_with_multiple_ctes() {
let sub_select1 = Query::select()
.column(Asterisk)
.from(Char::Table)
.to_owned();
let sub_select1_name = SeaRc::new(Alias::new("sub1"));
let mut sub_select1_cte = CommonTableExpression::new();
sub_select1_cte.table_name(sub_select1_name.clone());
sub_select1_cte.column(SeaRc::new(Alias::new("a")));
sub_select1_cte.query(sub_select1);
let sub_select2 = Query::select()
.column(Asterisk)
.from(Char::Table)
.to_owned();
let sub_select2_name = SeaRc::new(Alias::new("sub2"));
let mut sub_select2_cte = CommonTableExpression::new();
sub_select2_cte.table_name(sub_select2_name.clone());
sub_select2_cte.column(SeaRc::new(Alias::new("b")));
sub_select2_cte.query(sub_select2);

let mut with = WithClause::new();
with.recursive(true)
.cte(sub_select1_cte)
.cte(sub_select2_cte);

let mut main_sel2 = Query::select();
main_sel2
.expr(Expr::col(Asterisk))
.from(TableRef::Table(sub_select2_name));
let mut main_sel1 = Query::select();
main_sel1
.expr(Expr::col(Asterisk))
.from(TableRef::Table(sub_select1_name))
.union(UnionType::All, main_sel2);

let query = with.query(main_sel1);

assert_eq!(
query.to_string(SqliteQueryBuilder),
r#"WITH RECURSIVE "sub1" ("a") AS (SELECT * FROM "character") , "sub2" ("b") AS (SELECT * FROM "character") SELECT * FROM "sub1" UNION ALL SELECT * FROM "sub2""#
);
}

0 comments on commit 6771326

Please sign in to comment.