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
47 changes: 46 additions & 1 deletion go/test/endtoend/vtgate/queries/misc/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func start(t *testing.T) (utils.MySQLCompare, func()) {
require.NoError(t, err)

deleteAll := func() {
tables := []string{"t1", "tbl", "unq_idx", "nonunq_idx", "tbl_enum_set", "uks.unsharded"}
tables := []string{"t1", "tbl", "unq_idx", "nonunq_idx", "tbl_enum_set", "uks.unsharded", "all_types"}
for _, table := range tables {
_, _ = mcmp.ExecAndIgnore("delete from " + table)
}
Expand Down Expand Up @@ -596,3 +596,48 @@ func TestSemiJoin(t *testing.T) {
})
}
}

// TestTabletTypeRouting tests that the tablet type routing works as intended.
func TestTabletTypeRouting(t *testing.T) {
// We are gonna configure the routing rules to send the
// query for a replica tablet in ks_misc.t1 to go to a table that doesn't exist.
// I know this doesn't make much practical sense, but makes testing really easy.
routingRules := `{"rules": [
{
"from_table": "ks_misc.t1@replica",
"to_tables": ["uks.unknown"]
}
]}`
err := clusterInstance.VtctldClientProcess.ApplyRoutingRules(routingRules)
require.NoError(t, err)
defer func() {
// Clear the routing rules after the test.
err = clusterInstance.VtctldClientProcess.ApplyRoutingRules("{}")
require.NoError(t, err)
}()

mcmp, closer := start(t)
defer closer()

mcmp.Exec("insert into t1(id1, id2) values (0,0)")

vtConn := mcmp.VtConn
// We first verify that querying the primary tablet goes to the t1 table.
utils.Exec(t, vtConn, "use ks_misc@primary")
utils.AssertMatches(t, vtConn, "select * from ks_misc.t1", `[[INT64(0) INT64(0)]]`)
// Now we change the connection's target
utils.Exec(t, vtConn, "use ks_misc@replica")
// We verify that querying the replica tablet creates an unknown table error.
_, err = utils.ExecAllowError(t, vtConn, "select * from ks_misc.t1")
require.ErrorContains(t, err, "table unknown not found")
}

// TestJoinMixedCaseExpr tests that join condition with expression from both table having in clause is handled correctly.
func TestJoinMixedCaseExpr(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec(`insert into all_types(id, int_unsigned) values (1, 1), (2, 2), (3,3), (4,4), (10,5), (20, 6)`)
mcmp.Exec(`prepare prep_pk from 'SELECT t1.id from all_types t1 join all_types t2 on t1.int_unsigned = (case when t2.int_unsigned in (1, 2, 3) then 1 when t2.int_unsigned = 4 then 10 else 20 end)'`)
mcmp.AssertMatches(`execute prep_pk`, `[[INT64(1)] [INT64(1)] [INT64(1)]]`)
}
16 changes: 15 additions & 1 deletion go/vt/vtgate/evalengine/translate_simplify.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,24 @@ func simplifyExpr(env *ExpressionEnv, e IR) (IR, error) {
if err != nil {
return nil, err
}
return &Literal{inner: simplified}, nil
return evalToIR(simplified), nil
}
if err := e.simplify(env); err != nil {
return nil, err
}
return e, nil
}

// evalToIR turns an internal eval result into an IR:
// - if it’s an evalTuple, it recurses into a TupleExpr
// - otherwise it wraps the single value in a Literal
func evalToIR(simplified eval) IR {
if tuple, isTuple := simplified.(*evalTuple); isTuple {
te := make(TupleExpr, len(tuple.t))
for i, t := range tuple.t {
te[i] = evalToIR(t)
}
return te
}
return &Literal{inner: simplified}
}
44 changes: 44 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/from_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -4720,5 +4720,49 @@
"user.user_extra"
]
}
},
{
"comment": "case statement in join condition for evaluation - tuple‐expr simplification in CASE",
"query": "select 1 from user u1 join user u2 on u1.col = (case when u2.col = 2 then 0 when u2.col in (1, 2) then 1 else 2 end)",
"plan": {
"QueryType": "SELECT",
"Original": "select 1 from user u1 join user u2 on u1.col = (case when u2.col = 2 then 0 when u2.col in (1, 2) then 1 else 2 end)",
"Instructions": {
"OperatorType": "Join",
"Variant": "Join",
"JoinColumnIndexes": "L:0",
"JoinVars": {
"u1_col": 1
},
"TableName": "`user`_`user`",
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select 1, u1.col from `user` as u1 where 1 != 1",
"Query": "select 1, u1.col from `user` as u1",
"Table": "`user`"
},
{
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select 1 from `user` as u2 where 1 != 1",
"Query": "select 1 from `user` as u2 where :u1_col = case when u2.col = 2 then 0 when u2.col in (1, 2) then 1 else 2 end",
"Table": "`user`"
}
]
},
"TablesUsed": [
"user.user"
]
}
}
]
Loading