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: Fix sub query planning when the outer query is a dual query #10009

Merged
merged 1 commit into from
Mar 31, 2022
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
13 changes: 8 additions & 5 deletions go/vt/sqlparser/ast_rewriting.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,19 +264,19 @@ func newExpressionRewriter(keyspace string, selectLimit int) *expressionRewriter
}

const (
//LastInsertIDName is a reserved bind var name for last_insert_id()
// LastInsertIDName is a reserved bind var name for last_insert_id()
LastInsertIDName = "__lastInsertId"

//DBVarName is a reserved bind var name for database()
// DBVarName is a reserved bind var name for database()
DBVarName = "__vtdbname"

//FoundRowsName is a reserved bind var name for found_rows()
// FoundRowsName is a reserved bind var name for found_rows()
FoundRowsName = "__vtfrows"

//RowCountName is a reserved bind var name for row_count()
// RowCountName is a reserved bind var name for row_count()
RowCountName = "__vtrcount"

//UserDefinedVariableName is what we prepend bind var names for user defined variables
// UserDefinedVariableName is what we prepend bind var names for user defined variables
UserDefinedVariableName = "__vtudv"
)

Expand Down Expand Up @@ -514,6 +514,9 @@ func (er *expressionRewriter) funcRewrite(cursor *Cursor, node *FuncExpr) {
}

func (er *expressionRewriter) unnestSubQueries(cursor *Cursor, subquery *Subquery) {
if _, isExists := cursor.Parent().(*ExistsExpr); isExists {
return
}
sel, isSimpleSelect := subquery.Select.(*Select)
if !isSimpleSelect {
return
Expand Down
3 changes: 3 additions & 0 deletions go/vt/sqlparser/ast_rewriting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ func TestRewrites(in *testing.T) {
}, {
in: "select (select 42) from dual",
expected: "select 42 as `(select 42 from dual)` from dual",
}, {
in: "select exists(select 1) from user",
expected: "select exists(select 1) from user",
}, {
in: "select * from user where col = (select 42)",
expected: "select * from user where col = 42",
Expand Down
18 changes: 12 additions & 6 deletions go/vt/vtgate/planbuilder/physical/route_planning.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,17 +558,22 @@ func leaves(op abstract.Operator) (sources []abstract.Operator) {
}

func tryMergeReferenceTable(aRoute, bRoute *Route, merger mergeFunc) (*Route, error) {
// if either side is a reference table, we can just merge it and use the opcode of the other side
var opCode engine.Opcode
var selected *VindexOption
var (
// if either side is a reference table, we can just merge it and use the opcode of the other side
opCode engine.Opcode
vindex *VindexOption
ks *vindexes.Keyspace
)

switch {
case aRoute.RouteOpCode == engine.Reference:
selected = bRoute.Selected
vindex = bRoute.Selected
opCode = bRoute.RouteOpCode
ks = bRoute.Keyspace
case bRoute.RouteOpCode == engine.Reference:
selected = aRoute.Selected
vindex = aRoute.Selected
opCode = aRoute.RouteOpCode
ks = aRoute.Keyspace
default:
return nil, nil
}
Expand All @@ -578,7 +583,8 @@ func tryMergeReferenceTable(aRoute, bRoute *Route, merger mergeFunc) (*Route, er
return nil, err
}
r.RouteOpCode = opCode
r.Selected = selected
r.Selected = vindex
r.Keyspace = ks
return r, nil
}

Expand Down
39 changes: 39 additions & 0 deletions go/vt/vtgate/planbuilder/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,30 @@ func TestOne(t *testing.T) {
testFile(t, "onecase.txt", "", vschema)
}

func TestOneWithMainAsDefault(t *testing.T) {
vschema := &vschemaWrapper{
v: loadSchema(t, "schema_test.json", true),
keyspace: &vindexes.Keyspace{
Name: "main",
Sharded: false,
},
}

testFile(t, "onecase.txt", "", vschema)
}

func TestOneWithSecondUserAsDefault(t *testing.T) {
vschema := &vschemaWrapper{
v: loadSchema(t, "schema_test.json", true),
keyspace: &vindexes.Keyspace{
Name: "second_user",
Sharded: true,
},
}

testFile(t, "onecase.txt", "", vschema)
}

func TestRubyOnRailsQueries(t *testing.T) {
vschemaWrapper := &vschemaWrapper{
v: loadSchema(t, "rails_schema_test.json", true),
Expand Down Expand Up @@ -372,6 +396,21 @@ func TestWithDefaultKeyspaceFromFile(t *testing.T) {
testFile(t, "call_cases.txt", testOutputTempDir, vschema)
}

func TestWithDefaultKeyspaceFromFileSharded(t *testing.T) {
// We are testing this separately so we can set a default keyspace
vschema := &vschemaWrapper{
v: loadSchema(t, "schema_test.json", true),
keyspace: &vindexes.Keyspace{
Name: "second_user",
Sharded: true,
},
tabletType: topodatapb.TabletType_PRIMARY,
}

testOutputTempDir := makeTestOutput(t)
testFile(t, "select_cases_with_default.txt", testOutputTempDir, vschema)
}

func TestWithSystemSchemaAsDefaultKeyspace(t *testing.T) {
// We are testing this separately so we can set a default keyspace
vschema := &vschemaWrapper{
Expand Down
66 changes: 64 additions & 2 deletions go/vt/vtgate/planbuilder/testdata/select_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2207,8 +2207,8 @@ Gen4 plan same as above
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "main",
"Sharded": false
"Name": "user",
"Sharded": true
},
"FieldQuery": "select 42, id from dual, `user` where 1 != 1",
"Query": "select 42, id from dual, `user`",
Expand Down Expand Up @@ -2940,3 +2940,65 @@ Gen4 plan same as above
}
}
Gen4 plan same as above

# yeah, it does not make sense, but it's valid
"select exists(select 1) from user where id = 5"
{
"QueryType": "SELECT",
"Original": "select exists(select 1) from user where id = 5",
"Instructions": {
"OperatorType": "Subquery",
"Variant": "PulloutExists",
"PulloutVars": [
"__sq_has_values1",
"__sq1"
],
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Reference",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"FieldQuery": "select 1 from dual where 1 != 1",
"Query": "select 1 from dual",
"Table": "dual"
},
{
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select :__sq_has_values1 from `user` where 1 != 1",
"Query": "select :__sq_has_values1 from `user` where id = 5",
"Table": "`user`",
"Values": [
"INT64(5)"
],
"Vindex": "user_index"
}
]
}
}
{
"QueryType": "SELECT",
"Original": "select exists(select 1) from user where id = 5",
"Instructions": {
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select exists (select 1 from dual where 1 != 1) from `user` where 1 != 1",
"Query": "select exists (select 1 from dual) from `user` where id = 5",
"Table": "`user`",
"Values": [
"INT64(5)"
],
"Vindex": "user_index"
}
}
61 changes: 61 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/select_cases_with_default.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# EXISTS subquery when the default ks is different than the inner query
"select exists(select * from user where id = 5)"
{
"QueryType": "SELECT",
"Original": "select exists(select * from user where id = 5)",
"Instructions": {
"OperatorType": "Subquery",
"Variant": "PulloutExists",
"PulloutVars": [
"__sq_has_values1",
"__sq1"
],
"Inputs": [
{
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select * from `user` where 1 != 1",
"Query": "select * from `user` where id = 5",
"Table": "`user`",
"Values": [
"INT64(5)"
],
"Vindex": "user_index"
},
{
"OperatorType": "Route",
"Variant": "Reference",
"Keyspace": {
"Name": "second_user",
"Sharded": true
},
"FieldQuery": "select :__sq_has_values1 from dual where 1 != 1",
"Query": "select :__sq_has_values1 from dual",
"Table": "dual"
}
]
}
}
{
"QueryType": "SELECT",
"Original": "select exists(select * from user where id = 5)",
"Instructions": {
"OperatorType": "Route",
"Variant": "EqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select exists (select * from `user` where 1 != 1) from dual where 1 != 1",
"Query": "select exists (select * from `user` where id = 5) from dual",
"Table": "dual",
"Values": [
"INT64(5)"
],
"Vindex": "user_index"
}
}