-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Support recursive CTEs #16427
Support recursive CTEs #16427
Conversation
Review ChecklistHello reviewers! 👋 Please follow this checklist when reviewing this Pull Request. General
Tests
Documentation
New flags
If a workflow is added or modified:
Backward compatibility
|
e08d482
to
6864482
Compare
eac93e3
to
15e11cc
Compare
4b854ed
to
c22f9b1
Compare
c22f9b1
to
08b3b1a
Compare
b5f0583
to
fd531fb
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #16427 +/- ##
==========================================
+ Coverage 68.83% 68.86% +0.03%
==========================================
Files 1558 1562 +4
Lines 200042 200618 +576
==========================================
+ Hits 137693 138163 +470
- Misses 62349 62455 +106 ☔ View full report in Codecov by Sentry. |
0add00f
to
dbd11c7
Compare
Signed-off-by: Andres Taylor <[email protected]>
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.
looks like there are simple mysql test files available here which can be used for testing
https://github.com/search?q=repo%3Amysql%2Fmysql-server+path%3A%2F%5Emysql-test%5C%2Ft%5C%2F%2F+WITH+RECURSIVE&type=code
@@ -97,6 +97,10 @@ var ( | |||
VT09023 = errorWithoutState("VT09023", vtrpcpb.Code_FAILED_PRECONDITION, "could not map %v to a keyspace id", "Unable to determine the shard for the given row.") | |||
VT09024 = errorWithoutState("VT09024", vtrpcpb.Code_FAILED_PRECONDITION, "could not map %v to a unique keyspace id: %v", "Unable to determine the shard for the given row.") | |||
VT09025 = errorWithoutState("VT09025", vtrpcpb.Code_FAILED_PRECONDITION, "atomic transaction error: %v", "Error in atomic transactions") | |||
VT09026 = errorWithState("VT09026", vtrpcpb.Code_FAILED_PRECONDITION, CTERecursiveRequiresUnion, "Recursive Common Table Expression '%s' should contain a UNION", "") |
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.
These new errors need to be added to the Errors slice below in that file.
go/vt/sqlparser/ast_funcs.go
Outdated
|
||
tableName, ok := node.Expr.(TableName) | ||
if !ok { | ||
panic(vterrors.Errorf(vtrpcpb.Code_INTERNAL, "BUG: Derived table should have an alias. This should not be possible")) |
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.
nit: should use vterrors.VT13001
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.
Rest LGTM
if vcursor.Session().InTransaction() { | ||
res, err := r.TryExecute(ctx, vcursor, bindVars, wantfields) | ||
if err != nil { | ||
return err | ||
} | ||
return callback(res) | ||
} |
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.
Why do we do this? How is the the running in Streaming or non-streaming mode connected to transactions?
return r.recurse(ctx, vcursor, bindVars, result, callback) | ||
}) |
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.
Why don't we do the same for loop like the one for TryExecute? Why is this recursion required?
if r.Seed.GetKeyspaceName() == r.Term.GetKeyspaceName() { | ||
return r.Seed.GetKeyspaceName() | ||
} | ||
return r.Seed.GetKeyspaceName() + "_" + r.Term.GetKeyspaceName() |
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.
I know this is printing only, but just pointing out that it won't always give unique keyspace names.
In the sense that if Seed returns user_t1
and Term returns user_t2.
, the correct output would be user_t1_t2
but not user_t1_user_t2
.
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.
I hate GetKeyspaceName
and I think we should get rid of it
func (r *RecurseCTE) GetTableName() string { | ||
return r.Seed.GetTableName() | ||
} |
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.
Shouldn't we be also accounting for Term's table name?
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.
Don't like GetTableName
either.
|
||
func (r *RecurseCTE) NeedsTransaction() bool { | ||
return r.Seed.NeedsTransaction() || r.Term.NeedsTransaction() | ||
} |
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.
AFAIK, this engine primitive only deals with select statements. This should be noTxNeeded
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.
WITH
is valid with SELECT
, UPDATE
and DELETE
. might as well leave it open for both and just check what the inputs do, right?
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.
Update and Delete are not part of the recursive CTE inputs.
The output of the CTE will be used to perform the Update and Delete,
so CTE output becomes the input for Update/Delete operator/engine.
go/vt/vtgate/engine/recurse_cte.go
Outdated
recurseRows = append(recurseRows, rresult.Rows...) | ||
res.Rows = append(res.Rows, rresult.Rows...) | ||
} | ||
} | ||
return res, nil |
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.
coercion is not done, either we add the support or fail on type mismatch,
this only needs to be done with result of seed and first result of term
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.
yep. coercion and typing are still TODO, and I'm not planning on doing that in this PR
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.
This could lead to wrong output. I am suggesting we at least add a failing condition check on field types.
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.
mysql> with recursive cte as (select 1 union all select 1 from cte) select * from cte;
ERROR 3636 (HY000): Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.
We should also add this check in CTE engine to avoid infinite loop
Signed-off-by: Andres Taylor <[email protected]>
Signed-off-by: Andres Taylor <[email protected]>
ERCTERecursiveRequiresSingleReference = ErrorCode(3577) | ||
ERCTEMaxRecursionDepth = ErrorCode(3636) | ||
ERRegexpStringNotTerminated = ErrorCode(3684) | ||
ERRegexpBufferOverflow = ErrorCode(3684) |
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.
Error code 3684 is duplicated. Is that an error?
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 the same on the MySQL manual. I think the duplication in Vitess it's intentional 🤷
Also, the whole page is about errors, so clearly it's an error. 😜
Signed-off-by: Manan Gupta <[email protected]> Signed-off-by: Andres Taylor <[email protected]>
Description
Adds support for recursive CTEs in Vitess.
A recursive CTE looks like:
This PR includes both planner code that tries to merge the seed and term with each other, so the recursion can be pushed down to mysql, and also an engine primitive that can execute the recursion.
Related Issue(s)
Part of #16415
Builds on top of #16569
Website: vitessio/website#1815
Checklist
Deployment Notes