Skip to content

Commit

Permalink
[release-18.0] Avoid rewriting unsharded queries and split semantic a…
Browse files Browse the repository at this point in the history
…nalysis in two (vitessio#15217) (vitessio#15229)

Signed-off-by: Andres Taylor <[email protected]>
Signed-off-by: Florent Poinsard <[email protected]>
Co-authored-by: Florent Poinsard <[email protected]>
  • Loading branch information
vitess-bot[bot] and frouioui authored Feb 15, 2024
1 parent c678773 commit af8f73c
Show file tree
Hide file tree
Showing 12 changed files with 529 additions and 347 deletions.
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/testdata/ddl_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@
"Name": "main",
"Sharded": false
},
"Query": "create view view_a as select col1, col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a"
"Query": "create view view_a as select * from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a"
},
"TablesUsed": [
"main.view_a"
Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/testdata/filter_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -4062,7 +4062,7 @@
"Sharded": false
},
"FieldQuery": "select col + 2 as a from unsharded where 1 != 1",
"Query": "select col + 2 as a from unsharded having col + 2 = 42",
"Query": "select col + 2 as a from unsharded having a = 42",
"Table": "unsharded"
},
"TablesUsed": [
Expand Down
10 changes: 5 additions & 5 deletions go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,7 @@
"Sharded": false
},
"FieldQuery": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = 'foo' where 1 != 1",
"Query": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = 'foo' where (u_tbl8.col8) in ::fkc_vals and u_tbl9.col9 is null limit 1 lock in share mode",
"Query": "select 1 from u_tbl8 left join u_tbl9 on u_tbl9.col9 = 'foo' where u_tbl9.col9 is null and (u_tbl8.col8) in ::fkc_vals limit 1 lock in share mode",
"Table": "u_tbl8, u_tbl9"
},
{
Expand Down Expand Up @@ -1208,7 +1208,7 @@
"Sharded": false
},
"FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = 'foo' where 1 != 1",
"Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = 'foo' where (u_tbl4.col4) in ::fkc_vals and u_tbl3.col3 is null limit 1 lock in share mode",
"Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = 'foo' where u_tbl3.col3 is null and (u_tbl4.col4) in ::fkc_vals limit 1 lock in share mode",
"Table": "u_tbl3, u_tbl4"
},
{
Expand All @@ -1220,7 +1220,7 @@
"Sharded": false
},
"FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1",
"Query": "select 1 from u_tbl4, u_tbl9 where (u_tbl4.col4) in ::fkc_vals and (u_tbl9.col9) not in (('foo')) and u_tbl4.col4 = u_tbl9.col9 limit 1 lock in share mode",
"Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and ('foo' is null or (u_tbl9.col9) not in (('foo'))) and u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and ('foo' is null or (u_tbl9.col9) not in (('foo'))) limit 1 lock in share mode",
"Table": "u_tbl4, u_tbl9"
},
{
Expand Down Expand Up @@ -1297,7 +1297,7 @@
"Sharded": false
},
"FieldQuery": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = :v1 where 1 != 1",
"Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = :v1 where (u_tbl4.col4) in ::fkc_vals and u_tbl3.col3 is null limit 1 lock in share mode",
"Query": "select 1 from u_tbl4 left join u_tbl3 on u_tbl3.col3 = :v1 where u_tbl3.col3 is null and (u_tbl4.col4) in ::fkc_vals limit 1 lock in share mode",
"Table": "u_tbl3, u_tbl4"
},
{
Expand All @@ -1309,7 +1309,7 @@
"Sharded": false
},
"FieldQuery": "select 1 from u_tbl4, u_tbl9 where 1 != 1",
"Query": "select 1 from u_tbl4, u_tbl9 where (u_tbl4.col4) in ::fkc_vals and (:v1 is null or (u_tbl9.col9) not in ((:v1))) and u_tbl4.col4 = u_tbl9.col9 limit 1 lock in share mode",
"Query": "select 1 from u_tbl4, u_tbl9 where u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (:v1 is null or (u_tbl9.col9) not in ((:v1))) and u_tbl4.col4 = u_tbl9.col9 and (u_tbl4.col4) in ::fkc_vals and (:v1 is null or (u_tbl9.col9) not in ((:v1))) limit 1 lock in share mode",
"Table": "u_tbl4, u_tbl9"
},
{
Expand Down
8 changes: 4 additions & 4 deletions go/vt/vtgate/planbuilder/testdata/from_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -711,8 +711,8 @@
"Name": "main",
"Sharded": false
},
"FieldQuery": "select m1.col from unsharded as m1 join unsharded as m2 where 1 != 1",
"Query": "select m1.col from unsharded as m1 join unsharded as m2",
"FieldQuery": "select m1.col from unsharded as m1 straight_join unsharded as m2 where 1 != 1",
"Query": "select m1.col from unsharded as m1 straight_join unsharded as m2",
"Table": "unsharded"
},
"TablesUsed": [
Expand Down Expand Up @@ -3989,8 +3989,8 @@
"Name": "main",
"Sharded": false
},
"FieldQuery": "select A.col1 as col1, A.col2 as col2, B.col2 as col2 from unsharded_authoritative as A left join unsharded_authoritative as B on A.col1 = B.col1 where 1 != 1",
"Query": "select A.col1 as col1, A.col2 as col2, B.col2 as col2 from unsharded_authoritative as A left join unsharded_authoritative as B on A.col1 = B.col1",
"FieldQuery": "select * from unsharded_authoritative as A left join unsharded_authoritative as B using (col1) where 1 != 1",
"Query": "select * from unsharded_authoritative as A left join unsharded_authoritative as B using (col1)",
"Table": "unsharded_authoritative"
},
"TablesUsed": [
Expand Down
6 changes: 3 additions & 3 deletions go/vt/vtgate/planbuilder/testdata/select_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -1428,8 +1428,8 @@
"Name": "main",
"Sharded": false
},
"FieldQuery": "select col1, col2 from (select col1, col2 from unsharded where 1 != 1 union select col1, col2 from unsharded where 1 != 1) as a where 1 != 1",
"Query": "select col1, col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a",
"FieldQuery": "select * from (select col1, col2 from unsharded where 1 != 1 union select col1, col2 from unsharded where 1 != 1) as a where 1 != 1",
"Query": "select * from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a",
"Table": "unsharded"
},
"TablesUsed": [
Expand Down Expand Up @@ -2544,7 +2544,7 @@
"Sharded": false
},
"FieldQuery": "select 1 from (select col, count(*) as a from unsharded where 1 != 1 group by col) as f left join unsharded as u on f.col = u.id where 1 != 1",
"Query": "select 1 from (select col, count(*) as a from unsharded group by col having count(*) > 0 limit 0, 12) as f left join unsharded as u on f.col = u.id",
"Query": "select 1 from (select col, count(*) as a from unsharded group by col having a > 0 limit 0, 12) as f left join unsharded as u on f.col = u.id",
"Table": "unsharded"
},
"TablesUsed": [
Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/testdata/union_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -882,7 +882,7 @@
"Sharded": false
},
"FieldQuery": "(select 1 from unsharded where 1 != 1 union select 1 from unsharded where 1 != 1 union all select 1 from unsharded where 1 != 1) union select 1 from unsharded where 1 != 1 union all select 1 from unsharded where 1 != 1",
"Query": "(select 1 from unsharded union select 1 from unsharded union all select 1 from unsharded order by `1` asc) union select 1 from unsharded union all select 1 from unsharded order by `1` asc",
"Query": "(select 1 from unsharded union select 1 from unsharded union all select 1 from unsharded order by 1 asc) union select 1 from unsharded union all select 1 from unsharded order by 1 asc",
"Table": "unsharded"
},
"TablesUsed": [
Expand Down
111 changes: 89 additions & 22 deletions go/vt/vtgate/semantics/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,47 +25,60 @@ import (
// analyzer controls the flow of the analysis.
// It starts the tree walking and controls which part of the analysis sees which parts of the tree
type analyzer struct {
scoper *scoper
tables *tableCollector
binder *binder
typer *typer
rewriter *earlyRewriter
sig QuerySignature
scoper *scoper
earlyTables *earlyTableCollector
tables *tableCollector
binder *binder
typer *typer
rewriter *earlyRewriter
sig QuerySignature
si SchemaInformation
currentDb string

err error
inProjection int

projErr error
unshardedErr error
warning string
projErr error
unshardedErr error
warning string
singleUnshardedKeyspace bool
fullAnalysis bool
}

// newAnalyzer create the semantic analyzer
func newAnalyzer(dbName string, si SchemaInformation) *analyzer {
func newAnalyzer(dbName string, si SchemaInformation, fullAnalysis bool) *analyzer {
// TODO dependencies between these components are a little tangled. We should try to clean up
s := newScoper()
a := &analyzer{
scoper: s,
tables: newTableCollector(s, si, dbName),
typer: newTyper(),
scoper: s,
earlyTables: newEarlyTableCollector(si, dbName),
typer: newTyper(),
si: si,
currentDb: dbName,
fullAnalysis: fullAnalysis,
}
s.org = a
a.tables.org = a
return a
}

b := newBinder(s, a, a.tables, a.typer)
a.binder = b
func (a *analyzer) lateInit() {
a.tables = a.earlyTables.newTableCollector(a.scoper, a)
a.binder = newBinder(a.scoper, a, a.tables, a.typer)
a.scoper.binder = a.binder
a.rewriter = &earlyRewriter{
scoper: s,
binder: b,
scoper: a.scoper,
binder: a.binder,
expandedColumns: map[sqlparser.TableName][]*sqlparser.ColName{},
}
s.binder = b
return a
}

// Analyze analyzes the parsed query.
func Analyze(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) {
analyzer := newAnalyzer(currentDb, newSchemaInfo(si))
return analyseAndGetSemTable(statement, currentDb, si, false)
}

func analyseAndGetSemTable(statement sqlparser.Statement, currentDb string, si SchemaInformation, fullAnalysis bool) (*SemTable, error) {
analyzer := newAnalyzer(currentDb, newSchemaInfo(si), fullAnalysis)

// Analysis for initial scope
err := analyzer.analyze(statement)
Expand All @@ -79,7 +92,7 @@ func Analyze(statement sqlparser.Statement, currentDb string, si SchemaInformati

// AnalyzeStrict analyzes the parsed query, and fails the analysis for any possible errors
func AnalyzeStrict(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) {
st, err := Analyze(statement, currentDb, si)
st, err := analyseAndGetSemTable(statement, currentDb, si, true)
if err != nil {
return nil, err
}
Expand All @@ -103,6 +116,27 @@ func (a *analyzer) newSemTable(
if isCommented {
comments = commentedStmt.GetParsedComments()
}

if a.singleUnshardedKeyspace {
return &SemTable{
Tables: a.earlyTables.Tables,
Comments: comments,
Warning: a.warning,
Collation: coll,
ExprTypes: map[sqlparser.Expr]Type{},
NotSingleRouteErr: a.projErr,
NotUnshardedErr: a.unshardedErr,
Recursive: ExprDependencies{},
Direct: ExprDependencies{},
ColumnEqualities: map[columnName][]sqlparser.Expr{},
ExpandedColumns: map[sqlparser.TableName][]*sqlparser.ColName{},
columns: map[*sqlparser.Union]sqlparser.SelectExprs{},
comparator: nil,
StatementIDs: a.scoper.statementIDs,
QuerySignature: QuerySignature{},
}, nil
}

columns := map[*sqlparser.Union]sqlparser.SelectExprs{}
for union, info := range a.tables.unionInfo {
columns[union] = info.exprs
Expand Down Expand Up @@ -280,10 +314,43 @@ func (a *analyzer) depsForExpr(expr sqlparser.Expr) (direct, recursive TableSet,
}

func (a *analyzer) analyze(statement sqlparser.Statement) error {
_ = sqlparser.Rewrite(statement, nil, a.earlyUp)
if a.err != nil {
return a.err
}

if a.canShortCut(statement) {
return nil
}

a.lateInit()

_ = sqlparser.Rewrite(statement, a.analyzeDown, a.analyzeUp)
return a.err
}

// canShortCut checks if we are dealing with a single unsharded keyspace and no tables that have managed foreign keys
// if so, we can stop the analyzer early
func (a *analyzer) canShortCut(statement sqlparser.Statement) bool {
if a.fullAnalysis {
return false
}
ks, _ := singleUnshardedKeyspace(a.earlyTables.Tables)
if ks == nil {
return false
}

a.singleUnshardedKeyspace = !sqlparser.IsDMLStatement(statement)
return a.singleUnshardedKeyspace
}

// earlyUp collects tables in the query, so we can check
// if this a single unsharded query we are dealing with
func (a *analyzer) earlyUp(cursor *sqlparser.Cursor) bool {
a.earlyTables.up(cursor)
return true
}

func (a *analyzer) shouldContinue() bool {
return a.err == nil
}
Expand Down
Loading

0 comments on commit af8f73c

Please sign in to comment.