Skip to content

Commit eca358b

Browse files
authored
Merge pull request #204 from github/backport-19003
Backport 19003 - Fix query planning for complex queries with impossible conditions
2 parents fc274c8 + 63f633d commit eca358b

4 files changed

Lines changed: 442 additions & 4 deletions

File tree

go/vt/vtgate/planbuilder/operators/subquery_builder.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ func createSubquery(
169169
sqc := &SubQueryBuilder{totalID: totalID, subqID: subqID, outerID: outerID}
170170

171171
predicates, joinCols := sqc.inspectStatement(ctx, subq.Select)
172-
correlated := !ctx.SemTable.RecursiveDeps(subq).IsEmpty()
172+
173+
subqDependencies := ctx.SemTable.RecursiveDeps(subq)
174+
correlated := subqDependencies.KeepOnly(outerID).NotEmpty()
173175

174176
opInner := translateQueryToOp(ctx, subq.Select)
175177

go/vt/vtgate/planbuilder/testdata/select_cases.json

Lines changed: 304 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4321,6 +4321,85 @@
43214321
]
43224322
}
43234323
},
4324+
{
4325+
"comment": "Subquery with `IN` condition using columns with matching lookup vindexes, impossible conditions and limit clause",
4326+
"query": "SELECT music.id FROM music WHERE music.id IN (SELECT * FROM (SELECT music.id FROM music WHERE music.user_id IN (1, 2, 3) AND 1 = 0 AND music.col1 = 'bar' LIMIT 0, 100) _inner)",
4327+
"plan": {
4328+
"Type": "Complex",
4329+
"QueryType": "SELECT",
4330+
"Original": "SELECT music.id FROM music WHERE music.id IN (SELECT * FROM (SELECT music.id FROM music WHERE music.user_id IN (1, 2, 3) AND 1 = 0 AND music.col1 = 'bar' LIMIT 0, 100) _inner)",
4331+
"Instructions": {
4332+
"OperatorType": "UncorrelatedSubquery",
4333+
"Variant": "PulloutIn",
4334+
"PulloutVars": [
4335+
"__sq_has_values",
4336+
"__sq1"
4337+
],
4338+
"Inputs": [
4339+
{
4340+
"InputName": "SubQuery",
4341+
"OperatorType": "Limit",
4342+
"Count": "100",
4343+
"Offset": "0",
4344+
"Inputs": [
4345+
{
4346+
"OperatorType": "Route",
4347+
"Variant": "None",
4348+
"Keyspace": {
4349+
"Name": "user",
4350+
"Sharded": true
4351+
},
4352+
"FieldQuery": "select id from (select music.id from music where 1 != 1) as _inner where 1 != 1",
4353+
"Query": "select id from (select music.id from music where 0) as _inner limit 100"
4354+
}
4355+
]
4356+
},
4357+
{
4358+
"InputName": "Outer",
4359+
"OperatorType": "VindexLookup",
4360+
"Variant": "IN",
4361+
"Keyspace": {
4362+
"Name": "user",
4363+
"Sharded": true
4364+
},
4365+
"Values": [
4366+
"::__sq1"
4367+
],
4368+
"Vindex": "music_user_map",
4369+
"Inputs": [
4370+
{
4371+
"OperatorType": "Route",
4372+
"Variant": "IN",
4373+
"Keyspace": {
4374+
"Name": "user",
4375+
"Sharded": true
4376+
},
4377+
"FieldQuery": "select `name`, keyspace_id from name_user_vdx where 1 != 1",
4378+
"Query": "select `name`, keyspace_id from name_user_vdx where `name` in ::__vals",
4379+
"Values": [
4380+
"::name"
4381+
],
4382+
"Vindex": "user_index"
4383+
},
4384+
{
4385+
"OperatorType": "Route",
4386+
"Variant": "ByDestination",
4387+
"Keyspace": {
4388+
"Name": "user",
4389+
"Sharded": true
4390+
},
4391+
"FieldQuery": "select music.id from music where 1 != 1",
4392+
"Query": "select music.id from music where :__sq_has_values and music.id in ::__vals"
4393+
}
4394+
]
4395+
}
4396+
]
4397+
},
4398+
"TablesUsed": [
4399+
"user.music"
4400+
]
4401+
}
4402+
},
43244403
{
43254404
"comment": "Subquery with `IN` condition using columns with matching lookup vindexes",
43264405
"query": "SELECT music.id FROM music WHERE music.id IN (SELECT music.id FROM music WHERE music.user_id IN (1, 2, 3)) and music.user_id = 5",
@@ -5879,8 +5958,231 @@
58795958
"Sharded": true
58805959
},
58815960
"FieldQuery": "select 1 from (select id as uid from `user` where 1 != 1) as t, `user` where 1 != 1",
5882-
"Query": "select 1 from (select id as uid from `user`) as t, `user` where t.uid = `user`.id",
5883-
"Table": "`user`"
5961+
"Query": "select 1 from (select id as uid from `user`) as t, `user` where t.uid = `user`.id"
5962+
},
5963+
"TablesUsed": [
5964+
"user.user"
5965+
]
5966+
}
5967+
},
5968+
{
5969+
"comment": "Window function with IN clause - ROW_NUMBER with PARTITION BY routing column",
5970+
"query": "SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn FROM user WHERE id IN (1,2,3,4)",
5971+
"plan": {
5972+
"Type": "MultiShard",
5973+
"QueryType": "SELECT",
5974+
"Original": "SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn FROM user WHERE id IN (1,2,3,4)",
5975+
"Instructions": {
5976+
"OperatorType": "Route",
5977+
"Variant": "IN",
5978+
"Keyspace": {
5979+
"Name": "user",
5980+
"Sharded": true
5981+
},
5982+
"FieldQuery": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn from `user` where 1 != 1",
5983+
"Query": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn from `user` where id in ::__vals",
5984+
"Values": [
5985+
"(1, 2, 3, 4)"
5986+
],
5987+
"Vindex": "user_index"
5988+
},
5989+
"TablesUsed": [
5990+
"user.user"
5991+
]
5992+
}
5993+
},
5994+
{
5995+
"comment": "UNION ALL with window functions - each branch has window function with different primary vindex values",
5996+
"query": "SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn FROM user WHERE id = 1 UNION ALL SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn FROM user WHERE id = 2",
5997+
"plan": {
5998+
"Type": "Complex",
5999+
"QueryType": "SELECT",
6000+
"Original": "SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn FROM user WHERE id = 1 UNION ALL SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn FROM user WHERE id = 2",
6001+
"Instructions": {
6002+
"OperatorType": "Concatenate",
6003+
"Inputs": [
6004+
{
6005+
"OperatorType": "Route",
6006+
"Variant": "EqualUnique",
6007+
"Keyspace": {
6008+
"Name": "user",
6009+
"Sharded": true
6010+
},
6011+
"FieldQuery": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn from `user` where 1 != 1",
6012+
"Query": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn from `user` where id = 1",
6013+
"Values": [
6014+
"1"
6015+
],
6016+
"Vindex": "user_index"
6017+
},
6018+
{
6019+
"OperatorType": "Route",
6020+
"Variant": "EqualUnique",
6021+
"Keyspace": {
6022+
"Name": "user",
6023+
"Sharded": true
6024+
},
6025+
"FieldQuery": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn from `user` where 1 != 1",
6026+
"Query": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn from `user` where id = 2",
6027+
"Values": [
6028+
"2"
6029+
],
6030+
"Vindex": "user_index"
6031+
}
6032+
]
6033+
},
6034+
"TablesUsed": [
6035+
"user.user"
6036+
]
6037+
}
6038+
},
6039+
{
6040+
"comment": "Window function with RANK on single shard - PARTITION BY includes primary vindex",
6041+
"query": "SELECT id, textcol1, RANK() OVER (PARTITION BY id, textcol1 ORDER BY id) as rnk FROM user WHERE id = 5",
6042+
"plan": {
6043+
"Type": "Passthrough",
6044+
"QueryType": "SELECT",
6045+
"Original": "SELECT id, textcol1, RANK() OVER (PARTITION BY id, textcol1 ORDER BY id) as rnk FROM user WHERE id = 5",
6046+
"Instructions": {
6047+
"OperatorType": "Route",
6048+
"Variant": "EqualUnique",
6049+
"Keyspace": {
6050+
"Name": "user",
6051+
"Sharded": true
6052+
},
6053+
"FieldQuery": "select id, textcol1, rank() over (partition by id, textcol1 order by id asc) as rnk from `user` where 1 != 1",
6054+
"Query": "select id, textcol1, rank() over (partition by id, textcol1 order by id asc) as rnk from `user` where id = 5",
6055+
"Values": [
6056+
"5"
6057+
],
6058+
"Vindex": "user_index"
6059+
},
6060+
"TablesUsed": [
6061+
"user.user"
6062+
]
6063+
}
6064+
},
6065+
{
6066+
"comment": "Multiple window functions with same partition in single shard query",
6067+
"query": "SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn, RANK() OVER (PARTITION BY id ORDER BY intcol) as rnk FROM user WHERE id = 100",
6068+
"plan": {
6069+
"Type": "Passthrough",
6070+
"QueryType": "SELECT",
6071+
"Original": "SELECT id, intcol, ROW_NUMBER() OVER (PARTITION BY id ORDER BY intcol) as rn, RANK() OVER (PARTITION BY id ORDER BY intcol) as rnk FROM user WHERE id = 100",
6072+
"Instructions": {
6073+
"OperatorType": "Route",
6074+
"Variant": "EqualUnique",
6075+
"Keyspace": {
6076+
"Name": "user",
6077+
"Sharded": true
6078+
},
6079+
"FieldQuery": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn, rank() over (partition by id order by intcol asc) as rnk from `user` where 1 != 1",
6080+
"Query": "select id, intcol, row_number() over (partition by id order by intcol asc) as rn, rank() over (partition by id order by intcol asc) as rnk from `user` where id = 100",
6081+
"Values": [
6082+
"100"
6083+
],
6084+
"Vindex": "user_index"
6085+
},
6086+
"TablesUsed": [
6087+
"user.user"
6088+
]
6089+
}
6090+
},
6091+
{
6092+
"comment": "Window function with composite vindex (multi-column) - PARTITION BY includes all primary vindex columns",
6093+
"query": "SELECT cola, colb, colc, RANK() OVER (PARTITION BY cola, colb ORDER BY colc) as rnk FROM multicol_tbl WHERE cola = 'A' AND colb = 'B'",
6094+
"plan": {
6095+
"Type": "Passthrough",
6096+
"QueryType": "SELECT",
6097+
"Original": "SELECT cola, colb, colc, RANK() OVER (PARTITION BY cola, colb ORDER BY colc) as rnk FROM multicol_tbl WHERE cola = 'A' AND colb = 'B'",
6098+
"Instructions": {
6099+
"OperatorType": "Route",
6100+
"Variant": "EqualUnique",
6101+
"Keyspace": {
6102+
"Name": "user",
6103+
"Sharded": true
6104+
},
6105+
"FieldQuery": "select cola, colb, colc, rank() over (partition by cola, colb order by colc asc) as rnk from multicol_tbl where 1 != 1",
6106+
"Query": "select cola, colb, colc, rank() over (partition by cola, colb order by colc asc) as rnk from multicol_tbl where cola = 'A' and colb = 'B'",
6107+
"Values": [
6108+
"'A'",
6109+
"'B'"
6110+
],
6111+
"Vindex": "multicolIdx"
6112+
},
6113+
"TablesUsed": [
6114+
"user.multicol_tbl"
6115+
]
6116+
}
6117+
},
6118+
{
6119+
"comment": "window function in subquery on unsharded table",
6120+
"query": "select * from (select rank() over (partition by col) as r from unsharded) as t",
6121+
"plan": {
6122+
"Type": "Passthrough",
6123+
"QueryType": "SELECT",
6124+
"Original": "select * from (select rank() over (partition by col) as r from unsharded) as t",
6125+
"Instructions": {
6126+
"OperatorType": "Route",
6127+
"Variant": "Unsharded",
6128+
"Keyspace": {
6129+
"Name": "main",
6130+
"Sharded": false
6131+
},
6132+
"FieldQuery": "select * from (select rank() over (partition by col) as r from unsharded where 1 != 1) as t where 1 != 1",
6133+
"Query": "select * from (select rank() over (partition by col) as r from unsharded) as t"
6134+
},
6135+
"TablesUsed": [
6136+
"main.unsharded"
6137+
]
6138+
}
6139+
},
6140+
{
6141+
"comment": "window function in subquery on sharded table with single shard predicate",
6142+
"query": "select * from (select rank() over (partition by col) as r from user where id = 1) as t",
6143+
"plan": {
6144+
"Type": "Passthrough",
6145+
"QueryType": "SELECT",
6146+
"Original": "select * from (select rank() over (partition by col) as r from user where id = 1) as t",
6147+
"Instructions": {
6148+
"OperatorType": "Route",
6149+
"Variant": "EqualUnique",
6150+
"Keyspace": {
6151+
"Name": "user",
6152+
"Sharded": true
6153+
},
6154+
"FieldQuery": "select r from (select rank() over (partition by col) as r from `user` where 1 != 1) as t where 1 != 1",
6155+
"Query": "select r from (select rank() over (partition by col) as r from `user` where id = 1) as t",
6156+
"Values": [
6157+
"1"
6158+
],
6159+
"Vindex": "user_index"
6160+
},
6161+
"TablesUsed": [
6162+
"user.user"
6163+
]
6164+
}
6165+
},
6166+
{
6167+
"comment": "window function in subquery on sharded table with outer predicate",
6168+
"query": "select * from (select rank() over (partition by col) as r, id from user) as t where id = 1",
6169+
"plan": {
6170+
"Type": "Passthrough",
6171+
"QueryType": "SELECT",
6172+
"Original": "select * from (select rank() over (partition by col) as r, id from user) as t where id = 1",
6173+
"Instructions": {
6174+
"OperatorType": "Route",
6175+
"Variant": "EqualUnique",
6176+
"Keyspace": {
6177+
"Name": "user",
6178+
"Sharded": true
6179+
},
6180+
"FieldQuery": "select r, id from (select rank() over (partition by col) as r, id from `user` where 1 != 1) as t where 1 != 1",
6181+
"Query": "select r, id from (select rank() over (partition by col) as r, id from `user` where id = 1) as t",
6182+
"Values": [
6183+
"1"
6184+
],
6185+
"Vindex": "user_index"
58846186
},
58856187
"TablesUsed": [
58866188
"user.user"

go/vt/vtgate/semantics/semantic_table.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,20 @@ func (st *SemTable) CopySemanticInfo(from, to sqlparser.SQLNode) {
545545
if !ok {
546546
return
547547
}
548-
st.CopyDependencies(f, t)
548+
549+
// Not all expressions are valid map keys
550+
if !ValidAsMapKey(t) || !ValidAsMapKey(f) {
551+
return
552+
}
553+
554+
if _, ok := t.(*sqlparser.ColName); ok {
555+
// If this is introducing a new column, we should copy all dependencies over
556+
// as we can't recalculate them later
557+
st.CopyDependencies(f, t)
558+
} else {
559+
// Otherwise, we only copy over the type information
560+
st.CopyExprInfo(f, t)
561+
}
549562
case *sqlparser.Union:
550563
t, ok := to.(*sqlparser.Union)
551564
if !ok {

0 commit comments

Comments
 (0)