Skip to content

Commit 68133dc

Browse files
evalengine: disable the static IN hash table for JSON operands
Signed-off-by: Graham Campbell <hello@gjcampbell.co.uk>
1 parent 15366bf commit 68133dc

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

go/vt/vtgate/evalengine/compiler_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,3 +1129,69 @@ func TestCompilerNonConstant(t *testing.T) {
11291129
})
11301130
}
11311131
}
1132+
1133+
// TestJSONInStaticTable checks that IN over folded JSON literals does not
1134+
// compile the static hash table: JSON arrays and objects hash by kind and
1135+
// cardinality only, so a hash hit is not equality. Compilation fails until
1136+
// the compiler learns to push JSON literals; the follow-up literal change
1137+
// flips this test to compiled evaluation.
1138+
func TestJSONInStaticTable(t *testing.T) {
1139+
testCases := []struct {
1140+
expression string
1141+
values []sqltypes.Value
1142+
result string
1143+
}{
1144+
{
1145+
expression: `column0 IN (JSON_ARRAY(2))`,
1146+
values: []sqltypes.Value{sqltypes.MakeTrusted(sqltypes.TypeJSON, []byte(`[1]`))},
1147+
result: `INT64(0)`,
1148+
},
1149+
{
1150+
expression: `column0 IN (JSON_OBJECT('b', 2))`,
1151+
values: []sqltypes.Value{sqltypes.MakeTrusted(sqltypes.TypeJSON, []byte(`{"a": 1}`))},
1152+
result: `INT64(0)`,
1153+
},
1154+
{
1155+
expression: `column0 IN (JSON_ARRAY(1))`,
1156+
values: []sqltypes.Value{sqltypes.MakeTrusted(sqltypes.TypeJSON, []byte(`[1]`))},
1157+
result: `INT64(1)`,
1158+
},
1159+
{
1160+
expression: `column0 IN (JSON_OBJECT('a', 1))`,
1161+
values: []sqltypes.Value{sqltypes.MakeTrusted(sqltypes.TypeJSON, []byte(`{"a": 1}`))},
1162+
result: `INT64(1)`,
1163+
},
1164+
}
1165+
1166+
venv := vtenv.NewTestEnv()
1167+
for _, tc := range testCases {
1168+
t.Run(tc.expression, func(t *testing.T) {
1169+
expr, err := venv.Parser().ParseExpr(tc.expression)
1170+
require.NoError(t, err)
1171+
1172+
fields := evalengine.FieldResolver(makeFields(tc.values))
1173+
cfg := &evalengine.Config{
1174+
ResolveColumn: fields.Column,
1175+
Collation: collations.CollationUtf8mb4ID,
1176+
Environment: venv,
1177+
}
1178+
1179+
translated, err := evalengine.Translate(expr, cfg)
1180+
require.NoError(t, err)
1181+
1182+
untyped, ok := translated.(*evalengine.UntypedExpr)
1183+
require.True(t, ok)
1184+
1185+
env := evalengine.EmptyExpressionEnv(venv)
1186+
env.Row = tc.values
1187+
env.Fields = makeFields(tc.values)
1188+
1189+
res, err := env.EvaluateAST(untyped)
1190+
require.NoError(t, err)
1191+
assert.Equal(t, tc.result, res.String())
1192+
1193+
_, err = untyped.Compile(env)
1194+
require.ErrorContains(t, err, "unsupported literal kind")
1195+
})
1196+
}
1197+
}

go/vt/vtgate/evalengine/expr_compare.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,13 @@ func (i *InExpr) eval(env *ExpressionEnv) (eval, error) {
531531
}
532532

533533
func (i *InExpr) compileTable(lhs ctype, rhs TupleExpr) map[vthash.Hash]struct{} {
534+
// JSON hashes fingerprint arrays and objects by kind and cardinality
535+
// only: a hash hit is not equality, so JSON stays on the comparing
536+
// slow path.
537+
if lhs.Type == sqltypes.TypeJSON {
538+
return nil
539+
}
540+
534541
var (
535542
table = make(map[vthash.Hash]struct{})
536543
hasher = vthash.New()

0 commit comments

Comments
 (0)