added rule to flatten PlanOpRelAlias

This commit is contained in:
pokeeffe-molecula 2023-04-10 16:35:51 -05:00
parent 6383a96ac5
commit 0513b282f4
6 changed files with 172 additions and 269 deletions

View file

@ -21,6 +21,7 @@ import (
type PlanOpPQLDistinctScan struct {
planner *ExecutionPlanner
tableName string
aliasName string
column string
filter types.PlanExpression
topExpr types.PlanExpression
@ -44,6 +45,7 @@ func (p *PlanOpPQLDistinctScan) Plan() map[string]interface{} {
result["_op"] = fmt.Sprintf("%T", p)
result["_schema"] = p.Schema().Plan()
result["tableName"] = p.tableName
result["aliasName"] = p.aliasName
if p.topExpr != nil {
result["topExpr"] = p.topExpr.Plan()
@ -71,6 +73,10 @@ func (p *PlanOpPQLDistinctScan) Name() string {
return p.tableName
}
func (p *PlanOpPQLDistinctScan) Alias() string {
return p.aliasName
}
func (p *PlanOpPQLDistinctScan) IsFilterable() bool {
return true
}

View file

@ -24,6 +24,7 @@ type TableQueryHint struct {
type PlanOpPQLTableScan struct {
planner *ExecutionPlanner
tableName string
aliasName string
columns []string
filter types.PlanExpression
timeQuantumFilters []types.PlanExpression
@ -48,6 +49,7 @@ func (p *PlanOpPQLTableScan) Plan() map[string]interface{} {
result["_op"] = fmt.Sprintf("%T", p)
result["_schema"] = p.Schema().Plan()
result["tableName"] = p.tableName
result["aliasName"] = p.aliasName
if p.topExpr != nil {
result["topExpr"] = p.topExpr.Plan()
@ -80,6 +82,10 @@ func (p *PlanOpPQLTableScan) Name() string {
return p.tableName
}
func (p *PlanOpPQLTableScan) Alias() string {
return p.aliasName
}
func (p *PlanOpPQLTableScan) IsFilterable() bool {
return true
}
@ -109,6 +115,7 @@ func (p *PlanOpPQLTableScan) Schema() types.Schema {
result = append(result, &types.PlannerColumn{
ColumnName: string(fld.Name),
RelationName: p.tableName,
AliasName: p.aliasName,
Type: fieldSQLDataType(pilosa.FieldToFieldInfo(fld)),
})
break

View file

@ -12,23 +12,23 @@ import (
// PlanOpRelAlias implements an alias for a relation
type PlanOpRelAlias struct {
ChildOp types.PlanOperator
alias string
warnings []string
ChildOp types.PlanOperator
aliasName string
warnings []string
}
func NewPlanOpRelAlias(alias string, child types.PlanOperator) *PlanOpRelAlias {
return &PlanOpRelAlias{
ChildOp: child,
alias: alias,
warnings: make([]string, 0),
ChildOp: child,
aliasName: alias,
warnings: make([]string, 0),
}
}
func (p *PlanOpRelAlias) Schema() types.Schema {
schema := p.ChildOp.Schema()
for _, s := range schema {
s.AliasName = p.alias
s.AliasName = p.aliasName
}
return schema
}
@ -47,14 +47,14 @@ func (p *PlanOpRelAlias) WithChildren(children ...types.PlanOperator) (types.Pla
if len(children) != 1 {
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
}
return NewPlanOpRelAlias(p.alias, children[0]), nil
return NewPlanOpRelAlias(p.aliasName, children[0]), nil
}
func (p *PlanOpRelAlias) Plan() map[string]interface{} {
result := make(map[string]interface{})
result["_op"] = fmt.Sprintf("%T", p)
result["_schema"] = p.Schema().Plan()
result["alias"] = p.alias
result["aliasName"] = p.aliasName
result["child"] = p.ChildOp.Plan()
return result
}
@ -74,40 +74,40 @@ func (p *PlanOpRelAlias) Warnings() []string {
return w
}
func (p *PlanOpRelAlias) Name() string {
return p.alias
}
// func (p *PlanOpRelAlias) Name() string {
// return p.aliasName
// }
func (p *PlanOpRelAlias) IsFilterable() bool {
ch, ok := p.ChildOp.(types.FilteredRelation)
if !ok {
return false
}
return ch.IsFilterable()
}
// func (p *PlanOpRelAlias) IsFilterable() bool {
// ch, ok := p.ChildOp.(types.FilteredRelation)
// if !ok {
// return false
// }
// return ch.IsFilterable()
// }
func (p *PlanOpRelAlias) UpdateFilters(filterCondition types.PlanExpression) (types.PlanOperator, error) {
ch, ok := p.ChildOp.(types.FilteredRelation)
if !ok {
return nil, sql3.NewErrInternalf("childop is not filterable")
}
newChild, err := ch.UpdateFilters(filterCondition)
if err != nil {
return nil, err
}
p.ChildOp = newChild
return p, nil
}
// func (p *PlanOpRelAlias) UpdateFilters(filterCondition types.PlanExpression) (types.PlanOperator, error) {
// ch, ok := p.ChildOp.(types.FilteredRelation)
// if !ok {
// return nil, sql3.NewErrInternalf("childop is not filterable")
// }
// newChild, err := ch.UpdateFilters(filterCondition)
// if err != nil {
// return nil, err
// }
// p.ChildOp = newChild
// return p, nil
// }
func (p *PlanOpRelAlias) UpdateTimeQuantumFilters(filters ...types.PlanExpression) (types.PlanOperator, error) {
ch, ok := p.ChildOp.(types.FilteredRelation)
if !ok {
return nil, sql3.NewErrInternalf("childop is not filterable")
}
newChild, err := ch.UpdateTimeQuantumFilters(filters...)
if err != nil {
return nil, err
}
p.ChildOp = newChild
return p, nil
}
// func (p *PlanOpRelAlias) UpdateTimeQuantumFilters(filters ...types.PlanExpression) (types.PlanOperator, error) {
// ch, ok := p.ChildOp.(types.FilteredRelation)
// if !ok {
// return nil, sql3.NewErrInternalf("childop is not filterable")
// }
// newChild, err := ch.UpdateTimeQuantumFilters(filters...)
// if err != nil {
// return nil, err
// }
// p.ChildOp = newChild
// return p, nil
// }

View file

@ -12,8 +12,9 @@ import (
// PlanOpSubquery is an operator for a subquery
type PlanOpSubquery struct {
ChildOp types.PlanOperator
warnings []string
ChildOp types.PlanOperator
aliasName string
warnings []string
}
func NewPlanOpSubquery(child types.PlanOperator) *PlanOpSubquery {
@ -24,7 +25,11 @@ func NewPlanOpSubquery(child types.PlanOperator) *PlanOpSubquery {
}
func (p *PlanOpSubquery) Schema() types.Schema {
return p.ChildOp.Schema()
schema := p.ChildOp.Schema()
for _, s := range schema {
s.AliasName = p.aliasName
}
return schema
}
func (p *PlanOpSubquery) Iterator(ctx context.Context, row types.Row) (types.RowIterator, error) {
@ -41,13 +46,16 @@ func (p *PlanOpSubquery) WithChildren(children ...types.PlanOperator) (types.Pla
if len(children) != 1 {
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
}
return NewPlanOpSubquery(children[0]), nil
n := NewPlanOpSubquery(children[0])
n.aliasName = p.aliasName
return n, nil
}
func (p *PlanOpSubquery) Plan() map[string]interface{} {
result := make(map[string]interface{})
result["_op"] = fmt.Sprintf("%T", p)
result["_schema"] = p.Schema().Plan()
result["aliasName"] = p.aliasName
result["child"] = p.ChildOp.Plan()
return result
}
@ -72,3 +80,19 @@ func (p *PlanOpSubquery) Warnings() []string {
func (p *PlanOpSubquery) Name() string {
return ""
}
func (p *PlanOpSubquery) Alias() string {
return p.aliasName
}
func (p *PlanOpSubquery) IsFilterable() bool {
return false
}
func (p *PlanOpSubquery) UpdateFilters(filterCondition types.PlanExpression) (types.PlanOperator, error) {
return p, nil
}
func (p *PlanOpSubquery) UpdateTimeQuantumFilters(filters ...types.PlanExpression) (types.PlanOperator, error) {
return p, nil
}

View file

@ -15,7 +15,6 @@ import (
)
//TODO(pok) give every expression an id 'Expr1234' and use that to match on
//TODO(pok) have a rule to eliminate PlanOpRelAlias
//TODO(pok) push filter down into join condition if terms reference either side of join
//TODO(pok) push order by down as far as possible
//TODO(pok) you can't group by _id in PQL, so we need to not use a PQL group by operator here
@ -28,6 +27,8 @@ type OptimizerFunc func(context.Context, *ExecutionPlanner, types.PlanOperator,
// a list of optimzer rules; order can be important important
var optimizerFunctions = []OptimizerFunc{
removeRelAliasOps,
// fix expression references for having
removeUnusedExtractColumnReferences,
@ -131,14 +132,12 @@ type filterSet struct {
filterConditions []types.PlanExpression
filtersByRelation map[string][]types.PlanExpression
handledFilters []types.PlanExpression
relationAliases RelationAliasesMap
}
func newFilterSet(filter types.PlanExpression, filtersByTable map[string][]types.PlanExpression, tableAliases RelationAliasesMap) *filterSet {
func newFilterSet(filter types.PlanExpression, filtersByTable map[string][]types.PlanExpression) *filterSet {
return &filterSet{
filterConditions: splitOnAnd(filter),
filtersByRelation: filtersByTable,
relationAliases: tableAliases,
}
}
@ -184,86 +183,32 @@ func remainingExpressions(allExprs, lessExprs []types.PlanExpression) []types.Pl
return remainder
}
// RelationAliasesMap is a map of aliases to Relations
type RelationAliasesMap map[string]types.IdentifiableByName
func (ta RelationAliasesMap) addAlias(alias types.IdentifiableByName, target types.IdentifiableByName) error {
lowerName := strings.ToLower(alias.Name())
if _, ok := ta[lowerName]; ok {
return sql3.NewErrInternalf("unexpected duplicate alias name")
}
ta[lowerName] = target
return nil
}
// build a map of alias names to relations
func getRelationAliases(n types.PlanOperator, scope *OptimizerScope) (RelationAliasesMap, error) {
var inspectErr error
aliases := make(RelationAliasesMap)
InspectPlan(n, func(node types.PlanOperator) bool {
if node == nil {
return false
}
switch node := node.(type) {
case *PlanOpRelAlias:
switch t := node.ChildOp.(type) {
case *PlanOpPQLTableScan:
inspectErr = aliases.addAlias(node, t)
case *PlanOpPQLDistinctScan:
inspectErr = aliases.addAlias(node, t)
case *PlanOpSubquery:
inspectErr = aliases.addAlias(node, t)
case *PlanOpTableValuedFunction:
inspectErr = aliases.addAlias(node, t)
default:
inspectErr = sql3.NewErrInternalf("unexpected alias child type '%T", node.ChildOp)
}
return false
case *PlanOpPQLTableScan:
inspectErr = aliases.addAlias(node, node)
return false
case *PlanOpPQLDistinctScan:
inspectErr = aliases.addAlias(node, node)
return false
}
return true
})
if inspectErr != nil {
return nil, inspectErr
}
return aliases, inspectErr
}
// governs how far down filter push down can go
func filterPushdownChildSelector(c ParentContext) bool {
switch c.Parent.(type) {
case *PlanOpRelAlias:
//definitely don't go any further than alias as parent
return false
}
return true
}
// governs how far down filter push down above tables can go
func filterPushdownAboveTablesChildSelector(c ParentContext) bool {
if !filterPushdownChildSelector(c) {
return false
}
switch c.Parent.(type) {
case *PlanOpFilter:
switch c.Operator.(type) {
case *PlanOpRelAlias, *PlanOpPQLTableScan, *PlanOpPQLDistinctScan:
return false
func removeRelAliasOps(ctx context.Context, a *ExecutionPlanner, n types.PlanOperator, scope *OptimizerScope) (types.PlanOperator, bool, error) {
return TransformPlanOp(n, func(node types.PlanOperator) (types.PlanOperator, bool, error) {
switch thisNode := node.(type) {
case *PlanOpRelAlias:
switch childNode := thisNode.ChildOp.(type) {
case *PlanOpPQLTableScan:
childNode.aliasName = thisNode.aliasName
return childNode, false, nil
case *PlanOpPQLDistinctScan:
childNode.aliasName = thisNode.aliasName
return childNode, false, nil
case *PlanOpSubquery:
childNode.aliasName = thisNode.aliasName
return childNode, false, nil
default:
return nil, true, sql3.NewErrInternalf("unexpected op type '%T'", childNode)
}
default:
return thisNode, true, nil
}
}
return true
})
}
// when we compile and create a PlanOpPQLTableScan we just add all the columns to the underlying extract. This is a bad idea, since
@ -288,32 +233,18 @@ func removeUnusedExtractColumnReferences(ctx context.Context, a *ExecutionPlanne
newExtractList := make([]string, 0)
// loop thru the extract list and make a new extract list
// with just the columns we need
alias, ok := c.Parent.(*PlanOpRelAlias)
if ok {
// handle the case where the parent is an alias
for _, ex := range thisNode.columns {
for _, ref := range refs {
if (strings.EqualFold(ref.tableName, thisNode.tableName) || strings.EqualFold(ref.tableName, alias.alias)) && strings.EqualFold(ex, ref.columnName) {
newExtractList = append(newExtractList, ex)
break
}
}
}
} else {
for _, ex := range thisNode.columns {
for _, ref := range refs {
if strings.EqualFold(ref.tableName, thisNode.tableName) && strings.EqualFold(ex, ref.columnName) {
newExtractList = append(newExtractList, ex)
break
}
for _, ex := range thisNode.columns {
for _, ref := range refs {
if (strings.EqualFold(ref.tableName, thisNode.tableName) || strings.EqualFold(ref.tableName, thisNode.aliasName)) && strings.EqualFold(ex, ref.columnName) {
newExtractList = append(newExtractList, ex)
break
}
}
}
// newExtractList should now contain just the cols that are referenced
return NewPlanOpPQLTableScan(a, thisNode.tableName, newExtractList, thisNode.hints), false, nil
// newExtractList should now contain just the cols that are referenced, so update the pql scan op
thisNode.columns = newExtractList
return thisNode, true, nil
default:
return thisNode, true, nil
@ -352,21 +283,11 @@ func removePushedDownConditions(ctx context.Context, a *ExecutionPlanner, node *
return NewPlanOpFilter(a, joinedExpr, node.ChildOp), false, nil
}
func pushdownFiltersToFilterableRelations(ctx context.Context, a *ExecutionPlanner, tableNode types.PlanOperator, scope *OptimizerScope, filters *filterSet, tableAliases RelationAliasesMap) (types.PlanOperator, bool, error) {
var table types.IdentifiableByName
func pushdownFiltersToFilterableRelations(ctx context.Context, a *ExecutionPlanner, tableNode types.PlanOperator, scope *OptimizerScope, filters *filterSet) (types.PlanOperator, bool, error) {
var table types.Relation
// only do this if it is a pql table scan
switch rel := tableNode.(type) {
case *PlanOpRelAlias:
switch rel.ChildOp.(type) {
case *PlanOpPQLTableScan:
table = rel
case *PlanOpPQLDistinctScan:
table = rel
default:
return tableNode, true, nil
}
case *PlanOpPQLTableScan:
table = rel
case *PlanOpPQLDistinctScan:
@ -448,62 +369,9 @@ func pushdownFiltersToFilterableRelations(ctx context.Context, a *ExecutionPlann
return newOp, false, nil
}
func pushdownFiltersToAboveRelation(ctx context.Context, a *ExecutionPlanner, tableNode types.PlanOperator, scope *OptimizerScope, filters *filterSet) (types.PlanOperator, bool, error) {
var table types.IdentifiableByName
// only do this if it is a pql table scan
switch rel := tableNode.(type) {
case *PlanOpRelAlias:
switch rel.ChildOp.(type) {
case *PlanOpPQLTableScan:
table = rel
case *PlanOpPQLDistinctScan:
table = rel
default:
return tableNode, true, nil
}
case *PlanOpPQLTableScan:
table = rel
case *PlanOpPQLDistinctScan:
table = rel
default:
return tableNode, true, nil
}
// reposition any remaining filters for a table to directly above the table itself
var pushedDownFilterExpression types.PlanExpression
if tableFilters := filters.availableFiltersForTable(table.Name()); len(tableFilters) > 0 {
filters.markFiltersHandled(tableFilters...)
// fix the field refs
handled, _, err := fixFieldRefIndexesOnExpressions(ctx, scope, a, tableNode.Schema(), tableFilters...)
if err != nil {
return nil, true, err
}
pushedDownFilterExpression = joinExprsWithAnd(handled...)
}
switch tableNode.(type) {
case *PlanOpRelAlias, *PlanOpPQLTableScan, *PlanOpPQLDistinctScan:
node := tableNode
if pushedDownFilterExpression != nil {
return NewPlanOpFilter(a, pushedDownFilterExpression, node), false, nil
}
return node, true, nil
default:
return nil, true, sql3.NewErrInternalf("unexpected op type '%T'", tableNode)
}
}
func pushdownFilters(ctx context.Context, a *ExecutionPlanner, n types.PlanOperator, scope *OptimizerScope) (types.PlanOperator, bool, error) {
tableAliases, err := getRelationAliases(n, scope)
if err != nil {
return nil, true, err
}
relationsList := getRelations(ctx, a, n, scope)
// push filter terms down into anything that supports being filtered directly
pushdownFiltersForFilterableRelations := func(n *PlanOpFilter, filters *filterSet) (types.PlanOperator, bool, error) {
@ -518,9 +386,9 @@ func pushdownFilters(ctx context.Context, a *ExecutionPlanner, n types.PlanOpera
}
return n, samePred, nil
// PlanOpPQLTableScan supports being filtered, PlanOpRelAlias is included here as a "transparent" op
case *PlanOpRelAlias, *PlanOpPQLTableScan, *PlanOpPQLDistinctScan:
n, samePred, err := pushdownFiltersToFilterableRelations(ctx, a, node, scope, filters, tableAliases)
// PlanOpPQLTableScan supports being filtered, as does PlanOpPQLDistinctScan
case *PlanOpPQLTableScan, *PlanOpPQLDistinctScan:
n, samePred, err := pushdownFiltersToFilterableRelations(ctx, a, node, scope, filters)
if err != nil {
return nil, true, err
}
@ -531,46 +399,16 @@ func pushdownFilters(ctx context.Context, a *ExecutionPlanner, n types.PlanOpera
})
}
pushdownFiltersCloseToRelations := func(n types.PlanOperator, filters *filterSet) (types.PlanOperator, bool, error) {
return TransformPlanOpWithParent(n, filterPushdownAboveTablesChildSelector, func(c ParentContext) (types.PlanOperator, bool, error) {
switch node := c.Operator.(type) {
case *PlanOpFilter:
n, same, err := removePushedDownConditions(ctx, a, node, filters)
if err != nil {
return nil, true, err
}
if same {
return n, true, nil
}
return n, false, nil
case *PlanOpRelAlias, *PlanOpPQLTableScan, *PlanOpPQLDistinctScan:
_, same, err := pushdownFiltersToAboveRelation(ctx, a, node, scope, filters)
if err != nil {
return nil, true, err
}
if same {
return node, true, nil
}
return node, false, nil
default:
return node, true, nil
}
})
}
// look for filter ops and push the conditions within them down to things that can be filtered
return TransformPlanOp(n, func(node types.PlanOperator) (types.PlanOperator, bool, error) {
switch thisNode := node.(type) {
case *PlanOpFilter:
// get the filter conditions from this filter in a map by table
filtersByTable := getFiltersByRelation(n)
filtersByTable := getFiltersByRelation(n, relationsList)
// make a struct to hold the expression for this filter, the broken up filter conditions
// and a map of alias name to relations
filters := newFilterSet(thisNode.Predicate, filtersByTable, tableAliases)
// make a struct to hold the expression for this filter and the broken up filter conditions
filters := newFilterSet(thisNode.Predicate, filtersByTable)
// first push down filters to any op that supports a filter
newNode, sameA, err := pushdownFiltersForFilterableRelations(thisNode, filters)
@ -578,13 +416,7 @@ func pushdownFilters(ctx context.Context, a *ExecutionPlanner, n types.PlanOpera
return nil, true, err
}
// second push down filters as close as possible to the relations they apply to
var sameB bool
newNode, sameB, err = pushdownFiltersCloseToRelations(newNode, filters)
if err != nil {
return nil, true, err
}
return newNode, sameA && sameB, nil
return newNode, sameA, nil
default:
return node, true, nil
@ -593,16 +425,16 @@ func pushdownFilters(ctx context.Context, a *ExecutionPlanner, n types.PlanOpera
}
// getFiltersByRelation returns a map of relations name to filter expressions for the op provided
func getFiltersByRelation(n types.PlanOperator) map[string][]types.PlanExpression {
func getFiltersByRelation(n types.PlanOperator, relations []types.Relation) map[string][]types.PlanExpression {
filters := make(map[string][]types.PlanExpression)
InspectPlan(n, func(node types.PlanOperator) bool {
switch thisNode := node.(type) {
case *PlanOpFilter:
fs := exprToRelationFilters(thisNode.Predicate)
exprFilters := exprToRelationFilters(thisNode.Predicate, relations)
for k, exprs := range fs {
filters[k] = append(filters[k], exprs...)
for k, expr := range exprFilters {
filters[k] = append(filters[k], expr...)
}
}
@ -614,30 +446,47 @@ func getFiltersByRelation(n types.PlanOperator) map[string][]types.PlanExpressio
// exprToRelationFilters returns a map of relation name to filter expressions for the expression
// passed after the expression is split on AND.
func exprToRelationFilters(expr types.PlanExpression) map[string][]types.PlanExpression {
func exprToRelationFilters(expr types.PlanExpression, relations []types.Relation) map[string][]types.PlanExpression {
filters := make(map[string][]types.PlanExpression)
for _, expr := range splitOnAnd(expr) {
// split the expr up on AND and handle each one
for _, splitExpr := range splitOnAnd(expr) {
// keep track of the relations we've seen
var seenTables = make(map[string]bool)
var lastTable string
// keep track of whether or not this expression is a subquery
hasSubquery := false
InspectExpression(expr, func(e types.PlanExpression) bool {
// walk expr and gather relation refs
InspectExpression(splitExpr, func(e types.PlanExpression) bool {
switch thisExpr := e.(type) {
case *qualifiedRefPlanExpression:
if !seenTables[thisExpr.tableName] {
seenTables[thisExpr.tableName] = true
lastTable = thisExpr.tableName
// resolve alias if necessary
tableName := thisExpr.tableName
for _, r := range relations {
if strings.EqualFold(thisExpr.tableName, r.Name()) || strings.EqualFold(thisExpr.tableName, r.Alias()) {
tableName = r.Name()
}
}
// check to see if we have seen this table before
if !seenTables[tableName] {
seenTables[tableName] = true
lastTable = tableName
}
case *subqueryPlanExpression:
// if we found a subquery, set the flag and stop walking the
// tree at this point
hasSubquery = true
return false
}
// keep walking the tree
return true
})
if len(seenTables) == 1 && !hasSubquery {
filters[lastTable] = append(filters[lastTable], expr)
filters[lastTable] = append(filters[lastTable], splitExpr)
}
}
return filters
@ -840,6 +689,7 @@ func tryToReplaceDistinctWithPQLDistinct(ctx context.Context, a *ExecutionPlanne
}
newOp, err := NewPlanOpPQLDistinctScan(a, thisNode.tableName, thisNode.columns[0])
newOp.aliasName = thisNode.aliasName
if err != nil {
return nil, false, err
}
@ -1083,7 +933,7 @@ func fixProjectionReferences(ctx context.Context, a *ExecutionPlanner, n types.P
return thisNode, false, nil
// everything else that can be a child of projection
case *PlanOpRelAlias, *PlanOpFilter, *PlanOpPQLTableScan, *PlanOpPQLDistinctScan, *PlanOpNestedLoops, *PlanOpOrderBy:
case *PlanOpFilter, *PlanOpPQLTableScan, *PlanOpPQLDistinctScan, *PlanOpNestedLoops, *PlanOpOrderBy:
exprs, same, err := fixFieldRefIndexesOnExpressions(ctx, scope, a, childOp.Schema(), thisNode.Projections...)
if err != nil {
return thisNode, true, err
@ -1254,7 +1104,22 @@ func hasJoins(ctx context.Context, a *ExecutionPlanner, n types.PlanOperator, sc
return result, nil
}
// inspects a plan op tree and returns a list (or error) of all the PlanOpTableScan operators
// inspects a plan op tree and returns a list of all the relations
func getRelations(ctx context.Context, a *ExecutionPlanner, n types.PlanOperator, scope *OptimizerScope) []types.Relation {
var relations []types.Relation
//go find all the relations
InspectPlan(n, func(node types.PlanOperator) bool {
switch nd := node.(type) {
case types.Relation:
relations = append(relations, nd)
return false
}
return true
})
return relations
}
// inspects a plan op tree and returns a list of all the PlanOpTableScan operators
func getTableScanOperators(ctx context.Context, a *ExecutionPlanner, n types.PlanOperator, scope *OptimizerScope) []*PlanOpPQLTableScan {
var tables []*PlanOpPQLTableScan
//go find the table scan operators

View file

@ -56,6 +56,7 @@ type PlannerColumn struct {
// Relation is an interface to something that can be treated as a relation
type Relation interface {
Name() string
Alias() string
}
// FilteredRelation is an interface to something that can be treated as a relation that can be filtered