Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion pkg/planner/core/casetest/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ go_test(
],
data = glob(["testdata/**"]),
flaky = True,
shard_count = 30,
shard_count = 31,
deps = [
"//pkg/domain",
"//pkg/domain/infosync",
Expand Down
26 changes: 26 additions & 0 deletions pkg/planner/core/casetest/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,3 +536,29 @@ func TestIssue56915(t *testing.T) {
"└─TableRowIDScan(Probe) 1.00 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]",
))
}

func TestCaseWhenPreparedStatementBug(t *testing.T) {
store, _ := testkit.CreateMockStoreAndDomain(t)
tk := testkit.NewTestKit(t, store)

tk.MustExec("use test")
tk.MustExec("CREATE TABLE t0(c0 DECIMAL);")
tk.MustExec("INSERT INTO t0 VALUES (0);")

normalResult := tk.MustQuery("SELECT * FROM t0 WHERE CAST((CASE t0.c0 WHEN t0.c0 THEN CAST(t0.c0 AS TIME) ELSE NULL END ) AS DATE);")
t.Logf("Normal query result: %v", normalResult.Rows())

caseResult := tk.MustQuery("SELECT CAST((CASE t0.c0 WHEN t0.c0 THEN CAST(t0.c0 AS TIME) ELSE NULL END ) AS DATE) FROM t0;")
t.Logf("Normal CASE result: %v", caseResult.Rows())

tk.MustExec("SET @b = NULL;")
tk.MustExec("PREPARE prepare_query FROM 'SELECT * FROM t0 WHERE CAST((CASE t0.c0 WHEN t0.c0 THEN CAST(t0.c0 AS TIME) ELSE ? END ) AS DATE)';")
preparedResult := tk.MustQuery("EXECUTE prepare_query USING @b;")
t.Logf("Prepared statement result: %v", preparedResult.Rows())

tk.MustExec("PREPARE prepare_case FROM 'SELECT CAST((CASE t0.c0 WHEN t0.c0 THEN CAST(t0.c0 AS TIME) ELSE ? END ) AS DATE) FROM t0';")
preparedCaseResult := tk.MustQuery("EXECUTE prepare_case USING @b;")
t.Logf("Prepared CASE result: %v", preparedCaseResult.Rows())

require.Equal(t, len(normalResult.Rows()), len(preparedResult.Rows()), "Results should have the same number of rows")
}
13 changes: 8 additions & 5 deletions pkg/types/field_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,14 @@ func InferParamTypeFromDatum(d *Datum, tp *FieldType) {
func InferParamTypeFromUnderlyingValue(value any, tp *FieldType) {
switch value.(type) {
case nil:
tp.SetType(mysql.TypeVarString)
tp.SetFlen(UnspecifiedLength)
tp.SetDecimal(UnspecifiedLength)
// Also set the `charset` and `collation` for it, because some function (e.g. `json_object`) will return error
// if the argument collation is `binary`.
// For NULL parameters, use TypeNull to ensure consistent behavior with literal NULL values
// in control flow functions like CASE WHEN. Previously using TypeVarString caused incorrect
// type inference in prepared statements (issue #62564).
tp.SetType(mysql.TypeNull)
tp.SetFlen(0)
tp.SetDecimal(0)
// Set default charset and collation instead of binary charset to avoid breaking JSON functions
// (see PR #54145 which fixed JSON function argument verification issues).
tp.SetCharset(mysql.DefaultCharset)
tp.SetCollate(mysql.DefaultCollationName)
default:
Expand Down