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
15 changes: 15 additions & 0 deletions data/test/vtgate/filter_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,21 @@
}
}

# database() call in where clause.
"select id from user where database()"
{
"Original": "select id from user where database()",
"Instructions": {
"Opcode": "SelectScatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"Query": "select id from user where 'targetString'",
"FieldQuery": "select id from user where 1 != 1"
}
}

# outer and inner subquery route reference the same "uu.id" name
# but they refer to different things. The first reference is to the outermost query,
# and the second reference is to the the innermost 'from' subquery.
Expand Down
16 changes: 16 additions & 0 deletions data/test/vtgate/from_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,22 @@
}
}

# database call in ON clause.
# The on clause is weird because the substitution must even for root expressions.
"select u1.a from unsharded u1 join unsharded u2 on database()"
{
"Original": "select u1.a from unsharded u1 join unsharded u2 on database()",
"Instructions": {
"Opcode": "SelectUnsharded",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"Query": "select u1.a from unsharded as u1 join unsharded as u2 on 'targetString'",
"FieldQuery": "select u1.a from unsharded as u1 join unsharded as u2 on 'targetString' where 1 != 1"
}
}

# verify ',' vs JOIN precedence
"select u1.a from unsharded u1, unsharded u2 join unsharded u3 on u1.a = u2.a"
"symbol u1.a not found"
Expand Down
16 changes: 16 additions & 0 deletions data/test/vtgate/select_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@
}
}

# database calls should be substituted
"select database() from dual"
{
"Original": "select database() from dual",
"Instructions": {
"Opcode": "SelectUnsharded",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"Query": "select 'targetString' from dual",
"FieldQuery": "select 'targetString' from dual where 1 != 1"
}
}


# nextval for simple route
"select next value from user"
"NEXT used on a sharded table"
Expand Down
1 change: 1 addition & 0 deletions go/vt/vtgate/planbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ type ContextVSchema interface {
FindTable(tablename sqlparser.TableName) (*vindexes.Table, string, topodatapb.TabletType, key.Destination, error)
FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error)
DefaultKeyspace() (*vindexes.Keyspace, error)
TargetString() string
}

// Build builds a plan for a query based on the specified vschema.
Expand Down
23 changes: 13 additions & 10 deletions go/vt/vtgate/planbuilder/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func skipParenthesis(node sqlparser.Expr) sqlparser.Expr {
//
// If an expression has no references to the current query, then the left-most
// origin is chosen as the default.
func (pb *primitiveBuilder) findOrigin(expr sqlparser.Expr) (origin builder, err error) {
func (pb *primitiveBuilder) findOrigin(expr sqlparser.Expr) (origin builder, pushExpr sqlparser.Expr, err error) {
highestOrigin := pb.bldr.First()
var subroutes []*route
err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
Expand Down Expand Up @@ -120,30 +120,33 @@ func (pb *primitiveBuilder) findOrigin(expr sqlparser.Expr) (origin builder, err
subroutes = append(subroutes, subroute)
return false, nil
case *sqlparser.FuncExpr:
switch {
// If it's last_insert_id, ensure it's a single unsharded route.
if !node.Name.EqualString("last_insert_id") {
return true, nil
}
if rb, isRoute := pb.bldr.(*route); !isRoute || rb.ERoute.Keyspace.Sharded {
return false, errors.New("unsupported: LAST_INSERT_ID is only allowed for unsharded keyspaces")
case node.Name.EqualString("last_insert_id"):
if rb, isRoute := pb.bldr.(*route); !isRoute || rb.ERoute.Keyspace.Sharded {
return false, errors.New("unsupported: LAST_INSERT_ID is only allowed for unsharded keyspaces")
}
case node.Name.EqualString("database"):
expr = sqlparser.ReplaceExpr(expr, node, sqlparser.NewStrVal([]byte(pb.vschema.TargetString())))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vschema contains the current target in the session? 0_o

}
return true, nil
}
return true, nil
}, expr)
if err != nil {
return nil, err
return nil, nil, err
}
highestRoute, isRoute := highestOrigin.(*route)
if !isRoute && len(subroutes) > 0 {
return nil, errors.New("unsupported: subquery cannot be merged with cross-shard subquery")
return nil, nil, errors.New("unsupported: subquery cannot be merged with cross-shard subquery")
}
for _, subroute := range subroutes {
if err := highestRoute.SubqueryCanMerge(pb, subroute); err != nil {
return nil, err
return nil, nil, err
}
subroute.Redirect = highestRoute
}
return highestOrigin, nil
return highestOrigin, expr, nil
}

func hasSubquery(node sqlparser.SQLNode) bool {
Expand Down
11 changes: 5 additions & 6 deletions go/vt/vtgate/planbuilder/from.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,12 @@ func (pb *primitiveBuilder) mergeRoutes(rpb *primitiveBuilder, ajoin *sqlparser.
if ajoin == nil {
return nil
}
_, expr, err := pb.findOrigin(ajoin.Condition.On)
if err != nil {
return err
}
ajoin.Condition.On = expr
for _, filter := range splitAndExpression(nil, ajoin.Condition.On) {
// If VTGate evolves, this section should be rewritten
// to use processExpr.
_, err = pb.findOrigin(filter)
if err != nil {
return err
}
lRoute.UpdatePlan(pb, filter)
}
return nil
Expand Down
4 changes: 4 additions & 0 deletions go/vt/vtgate/planbuilder/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ func (vw *vschemaWrapper) DefaultKeyspace() (*vindexes.Keyspace, error) {
return vw.v.Keyspaces["main"].Keyspace, nil
}

func (vw *vschemaWrapper) TargetString() string {
return "targetString"
}

// For the purposes of this set of tests, just compare the actual plan
// and ignore all the metrics.
type testPlan struct {
Expand Down
7 changes: 4 additions & 3 deletions go/vt/vtgate/planbuilder/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ func (pb *primitiveBuilder) pushFilter(boolExpr sqlparser.Expr, whereType string
filters := splitAndExpression(nil, boolExpr)
reorderBySubquery(filters)
for _, filter := range filters {
origin, err := pb.findOrigin(filter)
origin, expr, err := pb.findOrigin(filter)
if err != nil {
return err
}
if err := pb.bldr.PushFilter(pb, filter, whereType, origin); err != nil {
if err := pb.bldr.PushFilter(pb, expr, whereType, origin); err != nil {
return err
}
}
Expand Down Expand Up @@ -168,10 +168,11 @@ func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs)
for i, node := range selectExprs {
switch node := node.(type) {
case *sqlparser.AliasedExpr:
origin, err := pb.findOrigin(node.Expr)
origin, expr, err := pb.findOrigin(node.Expr)
if err != nil {
return nil, err
}
node.Expr = expr
resultColumns[i], _, err = pb.bldr.PushSelect(node, origin)
if err != nil {
return nil, err
Expand Down
5 changes: 5 additions & 0 deletions go/vt/vtgate/vcursor_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ func (vc *vcursorImpl) DefaultKeyspace() (*vindexes.Keyspace, error) {
return ks.Keyspace, nil
}

// TargetString returns the current TargetString of the session.
func (vc *vcursorImpl) TargetString() string {
return vc.safeSession.TargetString

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see

}

// Execute performs a V3 level execution of the query.
func (vc *vcursorImpl) Execute(method string, query string, BindVars map[string]*querypb.BindVariable, isDML bool) (*sqltypes.Result, error) {
qr, err := vc.executor.Execute(vc.ctx, method, vc.safeSession, vc.marginComments.Leading+query+vc.marginComments.Trailing, BindVars)
Expand Down