Skip to content
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

gen4: Derived tables #8491

Merged
merged 37 commits into from
Jul 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
cadf78c
do table binding when coming up instead of when going down
systay Jul 19, 2021
716d617
added type information in the analyzer
GuptaManan100 Jul 19, 2021
4a3ea92
call interface DepsFor
GuptaManan100 Jul 20, 2021
bbaec51
unexport DepsFor local function
GuptaManan100 Jul 20, 2021
6e2c08f
use TypeFor from the semantic table in the planbuilder
GuptaManan100 Jul 20, 2021
2a719ff
added analysis for expressions coming from derived tables
GuptaManan100 Jul 20, 2021
15cfedc
added first test of derived tables as operator
systay Jul 20, 2021
c778e32
more operator support for derived tables
systay Jul 20, 2021
2a51552
first derived table test that passes plan_test for gen4
systay Jul 20, 2021
369075b
Merge branch main into gen4-derived
systay Jul 20, 2021
7258ad3
fix bad merge and test comments
systay Jul 21, 2021
4f7e45c
more work on derived tables
systay Jul 22, 2021
299bf18
Merge branch main into gen4-derived
systay Jul 22, 2021
b730c5c
goto is considered harmful
systay Jul 22, 2021
109c08b
fixed test assertions
systay Jul 22, 2021
1e48dd1
add expressions to derived tables
systay Jul 23, 2021
2a00621
Support for duplicated field name error in derived tables
frouioui Jul 23, 2021
919c063
Fixed cloning issue with derivedPlan's inner field
frouioui Jul 23, 2021
6064b23
Changing jointree type declaration and visitRelations method
frouioui Jul 23, 2021
054c1a7
Better error handling in joinTree findOutputColumn
frouioui Jul 23, 2021
d177438
Initial support for Derived's push predicates
frouioui Jul 23, 2021
d5f68cd
Keep track of recursived and normal expr dependencies in the analyzer…
frouioui Jul 26, 2021
fb9d1b0
Cleaned up naming and imbricated indentations in the analyzer
frouioui Jul 26, 2021
558deea
Merge branch main into gen4-derived
systay Jul 26, 2021
16d65e0
Small refactor
systay Jul 26, 2021
b626fcc
Added expectations for cases that were already passing
systay Jul 26, 2021
a830e0c
Avoiding duplicated columns when pushing projection on route plans
frouioui Jul 27, 2021
a9b03af
Addition of unit test for checkIfAlreadyExists
frouioui Jul 27, 2021
4cbd5bb
More readable horizon planning unit test
frouioui Jul 27, 2021
7dfe516
Clean up unrequired return argument
frouioui Jul 27, 2021
db656cf
Included Derived to the Operator documentation comment
frouioui Jul 27, 2021
20cb949
Make sure to keep derived table aliases around
systay Jul 27, 2021
7d879df
fail aggregation inside of derived tables
systay Jul 28, 2021
be4c0b6
Merge main into gen4-derived
systay Jul 28, 2021
16cd198
fix formatting error
harshit-gangal Jul 28, 2021
6d89d0f
code refactor
harshit-gangal Jul 28, 2021
8d17e60
Addition of comment in transformToLogicalPlan to explain the derived …
frouioui Jul 28, 2021
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
5 changes: 4 additions & 1 deletion go/mysql/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,9 +571,12 @@ const (
// SSLockDeadlock is ER_LOCK_DEADLOCK
SSLockDeadlock = "40001"

//SSClientError is the state on client errors
// SSClientError is the state on client errors
SSClientError = "42000"

// SSDupFieldName is ER_DUP_FIELD_NAME
SSDupFieldName = "42S21"

// SSBadFieldError is ER_BAD_FIELD_ERROR
SSBadFieldError = "42S22"

Expand Down
1 change: 1 addition & 0 deletions go/mysql/sql_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ var stateToMysqlCode = map[vterrors.State]struct {
vterrors.DataOutOfRange: {num: ERDataOutOfRange, state: SSDataOutOfRange},
vterrors.DbCreateExists: {num: ERDbCreateExists, state: SSUnknownSQLState},
vterrors.DbDropExists: {num: ERDbDropExists, state: SSUnknownSQLState},
vterrors.DupFieldName: {num: ERDupFieldName, state: SSDupFieldName},
vterrors.EmptyQuery: {num: EREmptyQuery, state: SSClientError},
vterrors.IncorrectGlobalLocalVar: {num: ERIncorrectGlobalLocalVar, state: SSUnknownSQLState},
vterrors.InnodbReadOnly: {num: ERInnodbReadOnly, state: SSUnknownSQLState},
Expand Down
1 change: 1 addition & 0 deletions go/vt/vterrors/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
WrongValueForVar
LockOrActiveTransaction
MixOfGroupFuncAndFields
DupFieldName

// failed precondition
NoDB
Expand Down
50 changes: 50 additions & 0 deletions go/vt/vtgate/planbuilder/abstract/derived.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2021 The Vitess Authors.

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 abstract

import (
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vtgate/semantics"
)

// Derived represents a derived table in the query
type Derived struct {
systay marked this conversation as resolved.
Show resolved Hide resolved
Sel *sqlparser.Select
Inner Operator
Alias string
}

var _ Operator = (*Derived)(nil)

// TableID implements the Operator interface
func (d *Derived) TableID() semantics.TableSet {
return d.Inner.TableID()
}

// PushPredicate implements the Operator interface
func (d *Derived) PushPredicate(expr sqlparser.Expr, semTable *semantics.SemTable) error {
tableInfo, err := semTable.TableInfoForExpr(expr)
if err != nil {
return err
}

newExpr, err := semantics.RewriteDerivedExpression(expr, tableInfo)
if err != nil {
return err
}
return d.Inner.PushPredicate(newExpr, semTable)
}
4 changes: 3 additions & 1 deletion go/vt/vtgate/planbuilder/abstract/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ type Join struct {
Exp sqlparser.Expr
}

var _ Operator = (*Join)(nil)

// PushPredicate implements the Operator interface
func (j *Join) PushPredicate(expr sqlparser.Expr, semTable *semantics.SemTable) error {
deps := semTable.Dependencies(expr)
deps := semTable.GetBaseTableDependencies(expr)
switch {
case deps.IsSolvedBy(j.LHS.TableID()):
return j.LHS.PushPredicate(expr, semTable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ type LeftJoin struct {
Predicate sqlparser.Expr
}

var _ Operator = (*LeftJoin)(nil)

// PushPredicate implements the Operator interface
func (oj *LeftJoin) PushPredicate(expr sqlparser.Expr, semTable *semantics.SemTable) error {
deps := semTable.Dependencies(expr)
deps := semTable.GetBaseTableDependencies(expr)
if deps.IsSolvedBy(oj.Left.TableID()) {
return oj.Left.PushPredicate(expr, semTable)
}
Expand Down
35 changes: 25 additions & 10 deletions go/vt/vtgate/planbuilder/abstract/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
type (
// Operator forms the tree of operators, representing the declarative query provided.
// An operator can be:
// * Derived - which represents an expression that generates a table.
// * QueryGraph - which represents a group of tables and predicates that can be evaluated in any order
// while still preserving the results
// * LeftJoin - A left join. These can't be evaluated in any order, so we keep them separate
Expand All @@ -40,17 +41,31 @@ type (
func getOperatorFromTableExpr(tableExpr sqlparser.TableExpr, semTable *semantics.SemTable) (Operator, error) {
switch tableExpr := tableExpr.(type) {
case *sqlparser.AliasedTableExpr:
qg := newQueryGraph()
tableName := tableExpr.Expr.(sqlparser.TableName)
tableID := semTable.TableSetFor(tableExpr)
tableInfo, err := semTable.TableInfoFor(tableID)
if err != nil {
return nil, err
switch tbl := tableExpr.Expr.(type) {
case sqlparser.TableName:
qg := newQueryGraph()
tableID := semTable.TableSetFor(tableExpr)
tableInfo, err := semTable.TableInfoFor(tableID)
if err != nil {
return nil, err
}
isInfSchema := tableInfo.IsInfSchema()
qt := &QueryTable{Alias: tableExpr, Table: tbl, TableID: tableID, IsInfSchema: isInfSchema}
qg.Tables = append(qg.Tables, qt)
return qg, nil
case *sqlparser.DerivedTable:
sel, isSel := tbl.Select.(*sqlparser.Select)
if !isSel {
return nil, semantics.Gen4NotSupportedF("UNION")
}
inner, err := CreateOperatorFromSelect(sel, semTable)
if err != nil {
return nil, err
}
return &Derived{Alias: tableExpr.As.String(), Inner: inner, Sel: sel}, nil
default:
return nil, semantics.Gen4NotSupportedF("%T", tbl)
}
isInfSchema := tableInfo.IsInfSchema()
qt := &QueryTable{Alias: tableExpr, Table: tableName, TableID: tableID, IsInfSchema: isInfSchema}
qg.Tables = append(qg.Tables, qt)
return qg, nil
case *sqlparser.JoinTableExpr:
switch tableExpr.Join {
case sqlparser.NormalJoinType:
Expand Down
34 changes: 34 additions & 0 deletions go/vt/vtgate/planbuilder/abstract/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,34 @@ JoinPredicates:
Predicate: c.id = d.id
}
Predicate: a.id = c.id
}`,
}, {
input: "select 1 from (select 42 as id from tbl) as t",
output: `Derived t: {
Query: select 42 as id from tbl
Inner: QueryGraph: {
Tables:
1:tbl
}
}`,
}, {
input: "select 1 from (select id from tbl limit 10) as t join (select foo, count(*) from usr group by foo) as s on t.id = s.foo",
output: `Join: {
LHS: Derived t: {
Query: select id from tbl limit 10
Inner: QueryGraph: {
Tables:
1:tbl
}
}
RHS: Derived s: {
Query: select foo, count(*) from usr group by foo
Inner: QueryGraph: {
Tables:
4:usr
}
}
Predicate: t.id = s.foo
}`,
}}

Expand Down Expand Up @@ -186,6 +214,10 @@ func testString(op Operator) string {
leftStr := indent(testString(op.Left))
rightStr := indent(testString(op.Right))
return fmt.Sprintf("OuterJoin: {\n\tInner: %s\n\tOuter: %s\n\tPredicate: %s\n}", leftStr, rightStr, sqlparser.String(op.Predicate))
case *Derived:
inner := indent(testString(op.Inner))
query := sqlparser.String(op.Sel)
return fmt.Sprintf("Derived %s: {\n\tQuery: %s\n\tInner:%s\n}", op.Alias, query, inner)
}
return "implement me"
}
Expand All @@ -198,6 +230,8 @@ func indent(s string) string {
return strings.Join(lines, "\n")
}

// the following code is only used by tests

func (qt *QueryTable) testString() string {
var alias string
if !qt.Alias.As.IsEmpty() {
Expand Down
6 changes: 4 additions & 2 deletions go/vt/vtgate/planbuilder/abstract/querygraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type (
}
)

var _ Operator = (*QueryGraph)(nil)

// PushPredicate implements the Operator interface
func (qg *QueryGraph) PushPredicate(expr sqlparser.Expr, semTable *semantics.SemTable) error {
for _, e := range sqlparser.SplitAndExpression(nil, expr) {
Expand Down Expand Up @@ -104,7 +106,7 @@ func (qg *QueryGraph) collectPredicates(sel *sqlparser.Select, semTable *semanti
}

func (qg *QueryGraph) collectPredicateTable(t sqlparser.TableExpr, predicate sqlparser.Expr, semTable *semantics.SemTable) error {
deps := semTable.Dependencies(predicate)
deps := semTable.GetBaseTableDependencies(predicate)
switch deps.NumberOfTables() {
case 0:
qg.addNoDepsPredicate(predicate)
Expand Down Expand Up @@ -135,7 +137,7 @@ func (qg *QueryGraph) collectPredicateTable(t sqlparser.TableExpr, predicate sql
}

func (qg *QueryGraph) collectPredicate(predicate sqlparser.Expr, semTable *semantics.SemTable) error {
deps := semTable.Dependencies(predicate)
deps := semTable.GetBaseTableDependencies(predicate)
switch deps.NumberOfTables() {
case 0:
qg.addNoDepsPredicate(predicate)
Expand Down
8 changes: 4 additions & 4 deletions go/vt/vtgate/planbuilder/expand_star_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,14 @@ func TestSemTableDependenciesAfterExpandStar(t *testing.T) {
assert.Equal(t, tcase.expSQL, sqlparser.String(expandedSelect))
if tcase.otherTbl != -1 {
assert.NotEqual(t,
semTable.Dependencies(expandedSelect.SelectExprs[tcase.otherTbl].(*sqlparser.AliasedExpr).Expr),
semTable.Dependencies(expandedSelect.SelectExprs[tcase.expandedCol].(*sqlparser.AliasedExpr).Expr),
semTable.GetBaseTableDependencies(expandedSelect.SelectExprs[tcase.otherTbl].(*sqlparser.AliasedExpr).Expr),
semTable.GetBaseTableDependencies(expandedSelect.SelectExprs[tcase.expandedCol].(*sqlparser.AliasedExpr).Expr),
)
}
if tcase.sameTbl != -1 {
assert.Equal(t,
semTable.Dependencies(expandedSelect.SelectExprs[tcase.sameTbl].(*sqlparser.AliasedExpr).Expr),
semTable.Dependencies(expandedSelect.SelectExprs[tcase.expandedCol].(*sqlparser.AliasedExpr).Expr),
semTable.GetBaseTableDependencies(expandedSelect.SelectExprs[tcase.sameTbl].(*sqlparser.AliasedExpr).Expr),
semTable.GetBaseTableDependencies(expandedSelect.SelectExprs[tcase.expandedCol].(*sqlparser.AliasedExpr).Expr),
)
}
})
Expand Down
56 changes: 27 additions & 29 deletions go/vt/vtgate/planbuilder/horizon_planning.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *sem
case *joinGen4:
lhsSolves := node.Left.ContainsTables()
rhsSolves := node.Right.ContainsTables()
deps := semTable.Dependencies(expr.Expr)
deps := semTable.GetBaseTableDependencies(expr.Expr)
var column int
var appended bool
switch {
Expand Down Expand Up @@ -103,19 +103,27 @@ func removeQualifierFromColName(expr *sqlparser.AliasedExpr) *sqlparser.AliasedE

func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, sel *sqlparser.Select) int {
for i, selectExpr := range sel.SelectExprs {
if selectExpr, ok := selectExpr.(*sqlparser.AliasedExpr); ok {
if selectExpr.As.IsEmpty() {
// we don't have an alias, so we can compare the expressions
if sqlparser.EqualsExpr(selectExpr.Expr, expr.Expr) {
return i
}
// we have an aliased column, so let's check if the expression is matching the alias
} else if colName, ok := expr.Expr.(*sqlparser.ColName); ok {
if selectExpr.As.Equal(colName.Name) {
return i
}
}
selectExpr, ok := selectExpr.(*sqlparser.AliasedExpr)
if !ok {
continue
}

selectExprCol, isSelectExprCol := selectExpr.Expr.(*sqlparser.ColName)
exprCol, isExprCol := expr.Expr.(*sqlparser.ColName)

if selectExpr.As.IsEmpty() {
// we don't have an alias

if isSelectExprCol && isExprCol && exprCol.Name.Equal(selectExprCol.Name) {
// the expressions are ColName, we compare their name
return i
} else if sqlparser.EqualsExpr(selectExpr.Expr, expr.Expr) {
// the expressions are not ColName, so we just compare the expressions
return i
}
} else if isExprCol && selectExpr.As.Equal(exprCol.Name) {
// we have an aliased column, checking if the the expression is matching the alias
return i
}
}
return -1
Expand Down Expand Up @@ -327,16 +335,15 @@ func wrapAndPushExpr(expr sqlparser.Expr, weightStrExpr sqlparser.Expr, plan log
if err != nil {
return 0, 0, false, err
}
colName, ok := expr.(*sqlparser.ColName)
_, ok := expr.(*sqlparser.ColName)
if !ok {
return 0, 0, false, semantics.Gen4NotSupportedF("group by/order by non-column expression")
}
table := semTable.Dependencies(colName)
tbl, err := semTable.TableInfoFor(table)
if err != nil {
return 0, 0, false, err
qt := semTable.TypeFor(expr)
wsNeeded := true
if qt != nil && sqltypes.IsNumber(*qt) {
wsNeeded = false
}
wsNeeded := needsWeightString(tbl, colName)

weightStringOffset := -1
var wAdded bool
Expand All @@ -361,15 +368,6 @@ func weightStringFor(expr sqlparser.Expr) sqlparser.Expr {

}

func needsWeightString(tbl semantics.TableInfo, colName *sqlparser.ColName) bool {
for _, c := range tbl.GetColumns() {
if colName.Name.String() == c.Name {
return !sqltypes.IsNumber(c.Type)
}
}
return true // we didn't find the column. better to add just to be safe1
}

func (hp *horizonPlanning) planOrderByForJoin(orderExprs []abstract.OrderBy, plan *joinGen4) (logicalPlan, error) {
if allLeft(orderExprs, hp.semTable, plan.Left.ContainsTables()) {
newLeft, err := hp.planOrderBy(orderExprs, plan.Left)
Expand Down Expand Up @@ -455,7 +453,7 @@ func (hp *horizonPlanning) createMemorySortPlan(plan logicalPlan, orderExprs []a

func allLeft(orderExprs []abstract.OrderBy, semTable *semantics.SemTable, lhsTables semantics.TableSet) bool {
for _, expr := range orderExprs {
exprDependencies := semTable.Dependencies(expr.Inner.Expr)
exprDependencies := semTable.GetBaseTableDependencies(expr.Inner.Expr)
if !exprDependencies.IsSolvedBy(lhsTables) {
return false
}
Expand Down
Loading