diff --git a/sql3/planner/oppqldistinctscan.go b/sql3/planner/oppqldistinctscan.go index 47a2a6a4f..7f48e3bfc 100644 --- a/sql3/planner/oppqldistinctscan.go +++ b/sql3/planner/oppqldistinctscan.go @@ -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 } diff --git a/sql3/planner/oppqltablescan.go b/sql3/planner/oppqltablescan.go index 739524d18..5ef153f3a 100644 --- a/sql3/planner/oppqltablescan.go +++ b/sql3/planner/oppqltablescan.go @@ -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 diff --git a/sql3/planner/oprelalias.go b/sql3/planner/oprelalias.go index 1691e0a02..279d3ea6f 100644 --- a/sql3/planner/oprelalias.go +++ b/sql3/planner/oprelalias.go @@ -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 +// } diff --git a/sql3/planner/opsubquery.go b/sql3/planner/opsubquery.go index e056df4ea..4036def24 100644 --- a/sql3/planner/opsubquery.go +++ b/sql3/planner/opsubquery.go @@ -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 +} diff --git a/sql3/planner/planoptimizer.go b/sql3/planner/planoptimizer.go index fe73539eb..a00f337b3 100644 --- a/sql3/planner/planoptimizer.go +++ b/sql3/planner/planoptimizer.go @@ -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 diff --git a/sql3/planner/types/operator.go b/sql3/planner/types/operator.go index d2159136b..92530e9cd 100644 --- a/sql3/planner/types/operator.go +++ b/sql3/planner/types/operator.go @@ -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