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
36 changes: 25 additions & 11 deletions go/test/endtoend/vtgate/lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/google/go-cmp/cmp"

"vitess.io/vitess/go/test/utils"
"github.com/stretchr/testify/assert"

"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -516,19 +516,33 @@ func TestSelectNull(t *testing.T) {
exec(t, conn, "begin")
exec(t, conn, "insert into t5_null_vindex(id, idx) values(1, 'a'), (2, 'b'), (3, null)")
exec(t, conn, "commit")
qr := exec(t, conn, "select id, idx from t5_null_vindex order by id")
utils.MustMatch(t, fmt.Sprintf("%v", qr.Rows), "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")] [INT64(3) NULL]]", "")

qr = exec(t, conn, "select id, idx from t5_null_vindex where idx = null")
require.Empty(t, qr.Rows)
assertMatches(t, conn, "select id, idx from t5_null_vindex order by id", "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")] [INT64(3) NULL]]")
assertIsEmpty(t, conn, "select id, idx from t5_null_vindex where idx = null")
assertMatches(t, conn, "select id, idx from t5_null_vindex where idx is null", "[[INT64(3) NULL]]")
assertMatches(t, conn, "select id, idx from t5_null_vindex where idx is not null order by id", "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")]]")
assertIsEmpty(t, conn, "select id, idx from t5_null_vindex where id IN (null)")
assertMatches(t, conn, "select id, idx from t5_null_vindex where id IN (1,2,null) order by id", "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")]]")
assertIsEmpty(t, conn, "select id, idx from t5_null_vindex where id NOT IN (1,null) order by id")
assertMatches(t, conn, "select id, idx from t5_null_vindex where id NOT IN (1,3)", "[[INT64(2) VARCHAR(\"b\")]]")

qr = exec(t, conn, "select id, idx from t5_null_vindex where idx is null")
utils.MustMatch(t, fmt.Sprintf("%v", qr.Rows), "[[INT64(3) NULL]]", "")
exec(t, conn, "delete from t5_null_vindex")
}

qr = exec(t, conn, "select id, idx from t5_null_vindex where idx is not null order by id")
utils.MustMatch(t, fmt.Sprintf("%v", qr.Rows), "[[INT64(1) VARCHAR(\"a\")] [INT64(2) VARCHAR(\"b\")]]", "")
func assertMatches(t *testing.T, conn *mysql.Conn, query, expected string) {
t.Helper()
qr := exec(t, conn, query)
got := fmt.Sprintf("%v", qr.Rows)
diff := cmp.Diff(expected, got)
if diff != "" {
t.Errorf("Query: %s (-want +got):\n%s", query, diff)
}
}

exec(t, conn, "delete from t5_null_vindex")
func assertIsEmpty(t *testing.T, conn *mysql.Conn, query string) {
t.Helper()
qr := exec(t, conn, query)
assert.Empty(t, qr.Rows)
}

func exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result {
Expand Down
20 changes: 20 additions & 0 deletions go/vt/vtgate/planbuilder/route_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ func (ro *routeOption) computePlan(pb *primitiveBuilder, filter sqlparser.Expr)
return ro.computeEqualPlan(pb, node)
case sqlparser.InStr:
return ro.computeINPlan(pb, node)
case sqlparser.NotInStr:
return ro.computeNotInPlan(node.Right), nil, nil
}
case *sqlparser.IsExpr:
return ro.computeISPlan(pb, node)
Expand Down Expand Up @@ -316,6 +318,10 @@ func (ro *routeOption) computeINPlan(pb *primitiveBuilder, comparison *sqlparser
}
switch node := comparison.Right.(type) {
case sqlparser.ValTuple:
if len(node) == 1 && sqlparser.IsNull(node[0]) {
return engine.SelectNone, nil, nil
}

for _, n := range node {
if !ro.exprIsValue(n) {
return engine.SelectScatter, nil, nil
Expand All @@ -328,6 +334,20 @@ func (ro *routeOption) computeINPlan(pb *primitiveBuilder, comparison *sqlparser
return engine.SelectScatter, nil, nil
}

// computeNotInPlan looks for null values to produce a SelectNone if found
func (*routeOption) computeNotInPlan(right sqlparser.Expr) engine.RouteOpcode {
switch node := right.(type) {
case sqlparser.ValTuple:
for _, n := range node {
if sqlparser.IsNull(n) {
return engine.SelectNone
}
}
}

return engine.SelectScatter
}

var planCost = map[engine.RouteOpcode]int{
engine.SelectUnsharded: 0,
engine.SelectNext: 0,
Expand Down
58 changes: 58 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/filter_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1351,3 +1351,61 @@
"Table": "music"
}
}

# Single table with unique vindex match and IN (null)
"select id from music where user_id = 4 and id IN (null)"
{
"QueryType": "SELECT",
"Original": "select id from music where user_id = 4 and id IN (null)",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectNone",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select id from music where 1 != 1",
"Query": "select id from music where user_id = 4 and id in (null)",
"Table": "music"
}
}

# Single table with unique vindex match and IN (null, 1, 2)
"select id from music where user_id = 4 and id IN (null, 1, 2)"
{
"QueryType": "SELECT",
"Original": "select id from music where user_id = 4 and id IN (null, 1, 2)",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select id from music where 1 != 1",
"Query": "select id from music where user_id = 4 and id in (null, 1, 2)",
"Table": "music",
"Values": [
4
],
"Vindex": "user_index"
}
}

# Single table with unique vindex match and NOT IN (null, 1, 2)
"select id from music where user_id = 4 and id NOT IN (null, 1, 2)"
{
"QueryType": "SELECT",
"Original": "select id from music where user_id = 4 and id NOT IN (null, 1, 2)",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectNone",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select id from music where 1 != 1",
"Query": "select id from music where user_id = 4 and id not in (null, 1, 2)",
"Table": "music"
}
}