-
Notifications
You must be signed in to change notification settings - Fork 597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(binder): correctly bind rcte in bind_with
& bind_relation_by_name
#16023
Conversation
src/frontend/src/binder/query.rs
Outdated
// https://www.postgresql.org/docs/16/sql-select.html#:~:text=the%20recursive%20self%2Dreference%20must%20appear%20on%20the%20right%2Dhand%20side%20of%20the%20UNION | ||
let bound_base = self.bind_set_expr(*left)?; | ||
// todo: to be further reviewed | ||
fn gen_query(s: SetExpr) -> Query { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ps. the below example is copied from https://github.com/risingwavelabs/risingwave/pull/15522/files#r1524367781.
with recursive t as
((select 1 limit 1)
union all
(select n+1 from t as t(n) where n < 5 limit 3));
since under current definition of union
, we can only get SetExpr
rather than an entire Query
for left
and right
.
thus, should we use BoundSetExpr
as a workaround (and also KISS) for RecursiveUnion
at present, or just initially conforming to postgres's behavior - which of course, need to modify some more stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @chenzl25, @xiangjinwu.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using BoundSetExpr
LGTM, because BoundQuery
and BoundSetExpr
contains each other actually. 🥵
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then let's stick with BoundSetExpr
first.
@@ -377,16 +378,23 @@ impl Binder { | |||
Ok(Relation::BackCteRef(Box::new(BoundBackCteRef { share_id }))) | |||
} | |||
BindingCteState::Bound { query } => { | |||
let schema = match query.clone() { | |||
Left(normal) => normal.body.schema().clone(), | |||
Right(recursive) => recursive.recursive.body.schema().clone(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same, could we just use the recursive
schema here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RecursiveUnion is also a union, so we need to align its inputs' schema, after that, we can use any input's schema as the union's schema.
See for more details
pub(super) fn bind_set_expr(&mut self, set_expr: SetExpr) -> Result<BoundSetExpr> { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plus, for the case like below (e.g., <value> union all <select stmt>
),
we might directly use rhs's schema as the final schema.
- since
align_schema
only handlesselect expr(s)
.
with recursive t(n) as (
values (1)
union all
select n + 1 from t where n < 100
)
select * from t;
What will happen if we write a CTE with
cc @xiangjinwu Can you give some ideas? |
one solution might be - treat it as normal but, |
cc @dylan Not necessary to resolve the case in the PR. |
@@ -58,6 +58,11 @@ impl Planner { | |||
Relation::BackCteRef(..) => { | |||
bail_not_implemented!(issue = 15135, "recursive CTE is not supported") | |||
} | |||
// todo: ensure this will always be wrapped in a `Relation::Share` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct me if this is not true.
Yes, I think we can just bind it to a normal UNION operator when it is proved that it is not a recursive CTE. |
If we know, at the binding phase, it is not recursive, we can just bind to a normal union (wrapped inside |
I guess it’s a little hard for our binder to know this, since we don’t have visitor on binder structure? Or we can just store a flag in context. |
a somewhat "hacky" solution would be, (with problem is - this is definitely error-prone and add extra (potentially ugly) hard-coded checking when
could you elaborate on this approach? I don't quite get the idea. |
RecursiveUnion
and related function(s)bind_with
& bind_relation_by_name
} | ||
|
||
impl RewriteExprsRecursive for BoundRecursiveUnion { | ||
fn rewrite_exprs_recursive(&mut self, _rewriter: &mut impl crate::expr::ExprRewriter) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation seems should be blanket?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The operator should be similar to BoundSetExpr, but not BoundBackRef
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated - now we will rewrite the two BoundSetExpr
respectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really hard to test binder in our codebase, the codes LGTM. We can refine it later when finish the planner part.
src/frontend/src/binder/query.rs
Outdated
// https://www.postgresql.org/docs/16/sql-select.html#:~:text=the%20recursive%20self%2Dreference%20must%20appear%20on%20the%20right%2Dhand%20side%20of%20the%20UNION | ||
let bound_base = self.bind_set_expr(*left)?; | ||
// todo: to be further reviewed | ||
fn gen_query(s: SetExpr) -> Query { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using BoundSetExpr
LGTM, because BoundQuery
and BoundSetExpr
contains each other actually. 🥵
e.key() | ||
)) | ||
.into()); | ||
if let BindingCteState::Bound { .. } = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the special check here, is because the lateral context typically contains the same stuff of the new bound context, when binding the final statement for a rcte - that's why we don't what to return error for the case like this.
with recursive t(n) as (
values (1)
union all
select n + 1 from t where n < 100
)
select * from t;
______________^ here
ps. need further review, cc @chenzl25 @TennyZhuang.
pss. I'll merge this pr first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated here: #16282
…oundRecursiveUnion
I hereby agree to the terms of the RisingWave Labs, Inc. Contributor License Agreement.
What's changed and what's your intention?
some subsequent work(s) of #15522 for binding rcte.
related: #15681.
Checklist
./risedev check
(or alias,./risedev c
)Documentation
Release note
If this PR includes changes that directly affect users or other significant modifications relevant to the community, kindly draft a release note to provide a concise summary of these changes. Please prioritize highlighting the impact these changes will have on users.