-
-
Notifications
You must be signed in to change notification settings - Fork 209
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
TransformUp is now sensitive to tree modifications #867
Conversation
81c52bd
to
12d1204
Compare
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.
Love the performance results, happy you did this work.
How mad would you be if I made you rewrite this to return a sentinel error type instead of changing the return signature of Transform? I think that's a better pattern because a) fewer return params, b) makes it harder to mistakenly under-analyze because a buggy rule returned the wrong boolean. Over-analyzing means we take longer than we should, under-analyzing means we return incorrect results. Basically I think the signal "I made no changes" should be more explicit and harder to send by accident.
See related comments about helper methods being exported inappropriately.
sql/plan/transform.go
Outdated
e, ok := n.(sql.Expressioner) | ||
if !ok { | ||
return n, nil | ||
func TransformUpHelper(node sql.Node, f sql.TransformNodeFunc) (sql.Node, bool, 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.
Why exported?
sql/analyzer/stored_procedures.go
Outdated
if _, ok := paramNames[strings.ToLower(e.Name())]; ok { | ||
return expression.NewProcedureParam(e.Name()), nil | ||
func resolveProcedureParamsTransform(ctx *sql.Context, paramNames map[string]struct{}, n sql.Node) (sql.Node, bool, error) { | ||
return plan.TransformUpHelper(n, func(n sql.Node) (sql.Node, bool, 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.
Big red flag here
The question you should be asking is: why doesn't TransformExpressionsUpWithNode work here on its own?
sql/analyzer/resolve_ctes.go
Outdated
cur = n | ||
for i := 0; i < maxCteDepth && !nodesEqual(prev, cur); i++ { | ||
for i := 0; i < maxCteDepth && mod; i++ { |
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.
Not sure about this, it makes it possible for an analyzer rule to misbehave and claim it didn't change anything when it actually did.
On the other hand, not calling DeepEquals on every node is probably a huge performance savings.
I think I would be more comfortable getting rid of DeepEquals here if a rule had to opt-in to saying it changed nothing, rather than opt-in to saying something changed.
`TransformUp` and related node/expression DFS helper functions expect the visit function to return an additional parameter indicating whether the visit changed the node: `sql.SameTree` or `sql.NewTree`. We use `plan.InspectUp` when possible, and then `plan.TransformUp` where possible, resorting to the more expensive `plan.TransformUpCtx` and `plan.TransformUpCtxSchema` only when necessary.
a258490
to
ceb8e26
Compare
This getting close to ready for review? Needs conflicts resolved |
@zachmu it's next on my list after prepared statements. I spent 4/5 days last week on index issues, this keeps getting deprioritized |
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 looks really good in general, a lot of comments but overall sound
return nil, err | ||
return nil, transform.SameTree, err | ||
} | ||
if same { |
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.
A lot of places you have this block of code could be simplified:
return node, same, err
No need to special-case same
, or the err from WithExpressions
or WithChildren
, just return all of it
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.
if we're always creating a new node, doesn't that defeat the purpose of minimizing allocs?
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.
Yeah on second thought this is a dumb comment in the general case
There are a couple spots you might apply it, but not here. Don't call a With method if you don't have to.
} | ||
|
||
// pushdownIndexToTable attempts to push the index given down to the table given, if it implements | ||
// sql.IndexAddressableTable | ||
func pushdownIndexToTable(a *Analyzer, tableNode NameableNode, index sql.Index, keyExpr []sql.Expression) (sql.Node, error) { | ||
return plan.TransformUp(tableNode, func(n sql.Node) (sql.Node, error) { | ||
func pushdownIndexToTable(a *Analyzer, tableNode NameableNode, index sql.Index, keyExpr []sql.Expression) (sql.Node, transform.TreeIdentity, 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.
Why is this here instead of in pushdown.go?
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 function is only referenced in this file. it's not filter pushdown, just applying an index
Also I forgot to mention this last night, but this would make a really good blog post. |
TransformUp and related node/expression DFS helper functions expect the
visit functions to return an additional boolean parameter indicating
whether the visit changed the node:
TransformUp's implementation uses the modification information to avoid
re-creating a node with identical children:
We use
plan.InspectUp
when possible, and thenplan.TransformUp
where possible, resorting to the more expensive
plan.TransformUpCtx
and
plan.TransformUpCtxSchema
only when necessary.