Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
422 changes: 422 additions & 0 deletions enginetest/queries/index_query_plans.go

Large diffs are not rendered by default.

152 changes: 76 additions & 76 deletions enginetest/queries/integration_plans.go

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions enginetest/scriptgen/setup/scripts/comp_index_tables
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,26 @@ INSERT INTO comp_vector_index_t0 VALUES (0,0,"[3,16]"),(1,2,"[65,9]"),(2,3,"[38,

exec
create VECTOR INDEX v_idx on comp_vector_index_t0 (v2)
----

exec
create table three_pk (
pk1 tinyint,
pk2 tinyint,
pk3 tinyint,
c1 tinyint NOT NULL,
c2 tinyint NOT NULL,
c3 tinyint NOT NULL,
c4 tinyint NOT NULL,
c5 tinyint NOT NULL,
primary key (pk1, pk2, pk3)
)
----

exec
insert into three_pk values
(0,0,0,0,1,2,3,4),
(0,1,10,20,11,12,13,14),
(1,0,20,40,21,22,23,24),
(1,1,30,60,31,32,33,34)
----
6 changes: 5 additions & 1 deletion enginetest/scriptgen/setup/scripts/views
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ CREATE VIEW where_exists_view AS SELECT * FROM three_pks l WHERE EXISTS (SELECT

exec
CREATE VIEW where_exists_view_in_view AS SELECT * FROM where_exists_view WHERE pk1 = 1
----
----

exec
CREATE VIEW join_view_requires_prefix AS SELECT l.pk1 as left_pk, r.pk1 as right_pk FROM three_pks l JOIN three_pks r WHERE l.pk2 = r.pk2
----
17 changes: 17 additions & 0 deletions enginetest/scriptgen/setup/setup_data.sg.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 31 additions & 7 deletions sql/analyzer/aliases.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,27 +294,50 @@ func aliasedExpressionsInNode(n sql.Node) map[string]string {
return aliasesFromExpressionToName
}

// getProjectionExpressions walks a tree to identify all projections and returns a map from the column ids of those
// projections to the underlying expression.
func getProjectionExpressions(n sql.Node) map[sql.ColumnId]sql.Expression {
projectionExpressions := make(map[sql.ColumnId]sql.Expression)
transform.Inspect(n, func(n sql.Node) bool {
switch node := n.(type) {
case *plan.Project:
for _, projExpr := range node.Projections {
if alias, isAlias := projExpr.(*expression.Alias); isAlias {
projectionExpressions[alias.Id()] = alias.Child
}
}
}
return true
})
return projectionExpressions
}

// normalizeExpressions returns the expressions given after normalizing them to replace table and expression aliases
// with their underlying names. This is necessary to match such expressions against those declared by implementors of
// various interfaces that declare expressions to handle, such as Index.Expressions(), FilteredTable, etc.
func normalizeExpressions(tableAliases TableAliases, expr ...sql.Expression) []sql.Expression {
func normalizeExpressions(tableAliases TableAliases, projectionExpressions map[sql.ColumnId]sql.Expression, expr ...sql.Expression) []sql.Expression {
expressions := make([]sql.Expression, len(expr))

for i, e := range expr {
expressions[i] = normalizeExpression(tableAliases, e)
expressions[i] = normalizeExpression(tableAliases, projectionExpressions, e)
}

return expressions
}

// normalizeExpression returns the expression given after normalizing it to replace table aliases with their underlying
// names. This is necessary to match such expressions against those declared by implementors of various interfaces that
// declare expressions to handle, such as Index.Expressions(), FilteredTable, etc.
func normalizeExpression(tableAliases TableAliases, e sql.Expression) sql.Expression {
// names and projection aliases with their underlying expressions. This is necessary to match such expressions
// against those declared by implementors of various interfaces that declare expressions to handle,
// such as Index.Expressions(), FilteredTable, etc.
func normalizeExpression(tableAliases TableAliases, projectionExpressions map[sql.ColumnId]sql.Expression, e sql.Expression) sql.Expression {
// If the query has table aliases, use them to replace any table aliases in column expressions
normalized, _, _ := transform.Expr(e, func(e sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
var tf transform.ExprFunc
tf = func(e sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
if field, ok := e.(*expression.GetField); ok {
table := strings.ToLower(field.Table())
if aliasedExpr, ok := projectionExpressions[field.Id()]; ok {
return transform.Expr(aliasedExpr, tf)
}
if rt, ok, _ := tableAliases.resolveName(table); ok {
return field.WithTable(strings.ToLower(rt.Name())).WithName(strings.ToLower(field.Name())), transform.NewTree, nil
} else {
Expand All @@ -323,7 +346,8 @@ func normalizeExpression(tableAliases TableAliases, e sql.Expression) sql.Expres
}

return e, transform.SameTree, nil
})
}
normalized, _, _ := transform.Expr(e, tf)

return normalized
}
Expand Down
4 changes: 2 additions & 2 deletions sql/analyzer/apply_indexes_from_outer_scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func createIndexKeyExpr(ctx *sql.Context, idx sql.Index, joinExprs []*joinColExp
idxExpressions := idx.Expressions()
normalizedJoinExprStrs := make([]string, len(joinExprs))
for i := range joinExprs {
normalizedJoinExprStrs[i] = normalizeExpression(tableAliases, joinExprs[i].colExpr).String()
normalizedJoinExprStrs[i] = normalizeExpression(tableAliases, nil, joinExprs[i].colExpr).String()
}
if ok, prefixCount := exprsAreIndexSubset(normalizedJoinExprStrs, idxExpressions); !ok || prefixCount != len(normalizedJoinExprStrs) {
return nil, nil, nil
Expand Down Expand Up @@ -260,7 +260,7 @@ func getSubqueryIndexes(
indexCols := exprsByTable[scopeTable]
if indexCols != nil {
col := indexCols[0].comparandCol
idx := ia.MatchingIndex(ctx, col.Table(), col.Database(), normalizeExpressions(tableAliases, extractComparands(indexCols)...)...)
idx := ia.MatchingIndex(ctx, col.Table(), col.Database(), normalizeExpressions(tableAliases, nil, extractComparands(indexCols)...)...)
if idx != nil {
result[indexCols[0].comparandCol.Table()] = idx
}
Expand Down
44 changes: 29 additions & 15 deletions sql/analyzer/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ func (f filtersByTable) size() int {

// getFiltersByTable returns a map of table name to filter expressions on that table for the node provided. Any
// predicates that contain no table or more than one table are not included in the result.
func getFiltersByTable(n sql.Node, scope *plan.Scope) filtersByTable {
func getFiltersByTable(n sql.Node, scope *plan.Scope, projectedExpressions map[sql.ColumnId]sql.Expression) filtersByTable {
filters := newFiltersByTable()
transform.InspectWithOpaque(n, func(node sql.Node) bool {
switch node := node.(type) {
case *plan.Filter:
fs := exprToTableFilters(node.Expression, scope)
fs := exprToTableFilters(node.Expression, scope, projectedExpressions)
filters.merge(fs)
}
if o, ok := node.(sql.OpaqueNode); ok {
Expand All @@ -62,20 +62,25 @@ func getFiltersByTable(n sql.Node, scope *plan.Scope) filtersByTable {
// exprToTableFilters returns a map of table name to filter expressions on that table for all parts of the expression
// given, split at AND. Any expressions that contain subquerys, or refer to more than one table, are not included in
// the result.
func exprToTableFilters(expr sql.Expression, scope *plan.Scope) filtersByTable {
func exprToTableFilters(expr sql.Expression, scope *plan.Scope, projectionExpressions map[sql.ColumnId]sql.Expression) filtersByTable {
filters := newFiltersByTable()
for _, expr := range expression.SplitConjunction(expr) {
var seenTables = make(map[string]bool)
var lastTable string
hasSubquery := false
sql.Inspect(expr, func(e sql.Expression) bool {
var findGetFields func(sql.Expression) bool
findGetFields = func(e sql.Expression) bool {
f, ok := e.(*expression.GetField)
if ok {
// A GetField that resolves to an outer scope or lateral scope
// is effectively constant and can be skipped.
if scope.Correlated().Contains(f.Id()) {
return true
}
if projectionExpression, ok := projectionExpressions[f.Id()]; ok {
sql.Inspect(projectionExpression, findGetFields)
return true
}
if !seenTables[f.Table()] {
seenTables[f.Table()] = true
lastTable = f.Table()
Expand All @@ -86,7 +91,8 @@ func exprToTableFilters(expr sql.Expression, scope *plan.Scope) filtersByTable {
}

return true
})
}
sql.Inspect(expr, findGetFields)

if len(seenTables) == 1 && !hasSubquery {
filters[lastTable] = append(filters[lastTable], expr)
Expand All @@ -97,20 +103,28 @@ func exprToTableFilters(expr sql.Expression, scope *plan.Scope) filtersByTable {
}

type filterSet struct {
filtersByTable filtersByTable
tableAliases TableAliases
filterPredicates []sql.Expression
handledFilters []sql.Expression
handledIndexFilters []string
filtersByTable filtersByTable
tableAliases TableAliases
projectionExpressions map[sql.ColumnId]sql.Expression
filterPredicates []sql.Expression
handledFilters []sql.Expression
handledIndexFilters []string
}

// newFilterSet returns a new filter set that will track available filters with the filters and aliases given. Aliases
// are necessary to normalize expressions from indexes when in the presence of aliases.
func newFilterSet(filter sql.Expression, filtersByTable filtersByTable, tableAliases TableAliases) *filterSet {
func newFilterSet(
filter *plan.Filter,
scope *plan.Scope,
tableAliases TableAliases,
) *filterSet {
projectionExpressions := getProjectionExpressions(filter)
filtersByTable := getFiltersByTable(filter, scope, projectionExpressions)
return &filterSet{
filterPredicates: expression.SplitConjunction(filter),
filtersByTable: filtersByTable,
tableAliases: tableAliases,
filterPredicates: expression.SplitConjunction(filter.Expression),
filtersByTable: filtersByTable,
tableAliases: tableAliases,
projectionExpressions: projectionExpressions,
}
}

Expand Down Expand Up @@ -180,7 +194,7 @@ func (fs *filterSet) subtractUsedIndexes(ctx *sql.Context, all []sql.Expression)
// Careful: index expressions are always normalized (contain actual table names), whereas filter expressions can
// contain aliases for both expressions and table names. We want to normalize all expressions for comparison, but
// return the original expressions.
normalized := normalizeExpressions(fs.tableAliases, all...)
normalized := normalizeExpressions(fs.tableAliases, fs.projectionExpressions, all...)

for i, e := range normalized {
var found bool
Expand Down
Loading
Loading