141 lines
6.1 KiB
Go
141 lines
6.1 KiB
Go
// Copyright 2024 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package pgtransform
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/dolthub/go-mysql-server/sql"
|
|
"github.com/dolthub/go-mysql-server/sql/plan"
|
|
gmstransform "github.com/dolthub/go-mysql-server/sql/transform"
|
|
)
|
|
|
|
// InspectNode functions similarly to GMS' InspectUp function, except it also walks through opaque and disjointed nodes.
|
|
func InspectNode(ctx *sql.Context, node sql.Node, nodeFunc func(*sql.Context, sql.Node) bool) bool {
|
|
// This implementation is based on the one in GMS, except that we use our functions instead to handle disjointed
|
|
stop := errors.New("stop")
|
|
_, _, err := NodeWithOpaque(ctx, node, func(ctx *sql.Context, node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
ok := nodeFunc(ctx, node)
|
|
if ok {
|
|
return nil, gmstransform.NewTree, stop
|
|
}
|
|
return node, gmstransform.SameTree, nil
|
|
})
|
|
return errors.Is(err, stop)
|
|
}
|
|
|
|
// InspectNodeExprs functions similarly to GMS' InspectUp function, except that it traverses expressions (there is no
|
|
// InspectUp derivative for expressions in GMS), and it also walks through opaque and disjointed nodes.
|
|
func InspectNodeExprs(ctx *sql.Context, node sql.Node, exprFunc func(ctx *sql.Context, expr sql.Expression) bool) bool {
|
|
// This implementation is based on the one in GMS, except that we use our functions instead to handle disjointed
|
|
stop := errors.New("stop")
|
|
_, _, err := NodeExprsWithOpaque(ctx, node, func(ctx *sql.Context, expr sql.Expression) (sql.Expression, gmstransform.TreeIdentity, error) {
|
|
ok := exprFunc(ctx, expr)
|
|
if ok {
|
|
return nil, gmstransform.NewTree, stop
|
|
}
|
|
return expr, gmstransform.SameTree, nil
|
|
})
|
|
return errors.Is(err, stop)
|
|
}
|
|
|
|
// NodeWithOpaque functions similarly to GMS' NodeWithOpaque function, except it also walks through disjointed nodes.
|
|
func NodeWithOpaque(ctx *sql.Context, node sql.Node, nodeFunc gmstransform.NodeFunc) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
return gmstransform.NodeWithOpaque(ctx, node, func(ctx *sql.Context, node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
treeIdentity := gmstransform.SameTree
|
|
if disjointedNode, ok := node.(plan.DisjointedChildrenNode); ok {
|
|
var err error
|
|
node, treeIdentity, err = handleDisjointedNodes(disjointedNode, func(node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
return NodeWithOpaque(ctx, node, nodeFunc)
|
|
})
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
}
|
|
node, newTreeIdentity, err := nodeFunc(ctx, node)
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
return node, treeIdentity && newTreeIdentity, nil
|
|
})
|
|
}
|
|
|
|
// NodeExprsWithOpaque functions similarly to GMS' NodeExprsWithOpaque function, except it also walks through disjointed
|
|
// nodes.
|
|
func NodeExprsWithOpaque(ctx *sql.Context, node sql.Node, exprFunc gmstransform.ExprFunc) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
node, disjointCheck, err := gmstransform.NodeWithOpaque(ctx, node, func(ctx *sql.Context, node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
if disjointedNode, ok := node.(plan.DisjointedChildrenNode); ok {
|
|
return handleDisjointedNodes(disjointedNode, func(node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
return NodeExprsWithOpaque(ctx, node, exprFunc)
|
|
})
|
|
}
|
|
return node, gmstransform.SameTree, nil
|
|
})
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
node, exprCheck, err := gmstransform.NodeExprsWithOpaque(ctx, node, exprFunc)
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
return node, disjointCheck && exprCheck, nil
|
|
}
|
|
|
|
// NodeExprsWithNodeWithOpaque functions similarly to GMS' NodeExprsWithNodeWithOpaque function, except it also walks
|
|
// through disjointed nodes.
|
|
func NodeExprsWithNodeWithOpaque(ctx *sql.Context, node sql.Node, exprFunc gmstransform.ExprWithNodeFunc) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
node, disjointCheck, err := gmstransform.NodeWithOpaque(ctx, node, func(ctx *sql.Context, node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
if disjointedNode, ok := node.(plan.DisjointedChildrenNode); ok {
|
|
return handleDisjointedNodes(disjointedNode, func(node sql.Node) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
return NodeExprsWithNodeWithOpaque(ctx, node, exprFunc)
|
|
})
|
|
}
|
|
return node, gmstransform.SameTree, nil
|
|
})
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
node, exprCheck, err := gmstransform.NodeExprsWithNodeWithOpaque(ctx, node, exprFunc)
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
return node, disjointCheck && exprCheck, nil
|
|
}
|
|
|
|
// handleDisjointedNodes handles disjointed nodes for the typical transform functions. This also includes the call on
|
|
// the given disjointed node, so the caller should avoid making the call themselves.
|
|
func handleDisjointedNodes(node plan.DisjointedChildrenNode, f func(sql.Node) (sql.Node, gmstransform.TreeIdentity, error)) (sql.Node, gmstransform.TreeIdentity, error) {
|
|
disjointedChildren := node.DisjointedChildren()
|
|
tree := gmstransform.SameTree
|
|
newChildren := make([][]sql.Node, len(disjointedChildren))
|
|
for firstIndex := range disjointedChildren {
|
|
newSubChildren := make([]sql.Node, len(disjointedChildren[firstIndex]))
|
|
for secondIndex := range disjointedChildren[firstIndex] {
|
|
newSubChild, newTree, err := f(disjointedChildren[firstIndex][secondIndex])
|
|
if err != nil {
|
|
return nil, gmstransform.NewTree, err
|
|
}
|
|
tree = tree && newTree
|
|
newSubChildren[secondIndex] = newSubChild
|
|
}
|
|
newChildren[firstIndex] = newSubChildren
|
|
}
|
|
if tree == gmstransform.SameTree {
|
|
return node, gmstransform.SameTree, nil
|
|
}
|
|
newNode, err := node.WithDisjointedChildren(newChildren)
|
|
return newNode, gmstransform.NewTree, err
|
|
}
|