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
19 changes: 15 additions & 4 deletions pkg/sql/plan/function/func_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -5761,15 +5761,26 @@ func jsonToStr(
}
} else {
bj := types.DecodeJson(v)
val, err := bj.MarshalJSON()
if err != nil {
return err
var str string
if bj.Type == bytejson.TpCodeString {
s, err := bj.Unquote()
if err != nil {
return err
}
str = s
} else {
bs, err := bj.MarshalJSON()
if err != nil {
return err
}
str = string(bs)
}
val := []byte(str)
if len(val) > int(toType.Width) && toType.Oid != types.T_text && toType.Oid != types.T_blob && toType.Oid != types.T_datalink {
return formatCastError(ctx, from.GetSourceVector(), toType, fmt.Sprintf(
"%v is larger than Dest length %v", val, toType.Width))
}
if err = to.AppendBytes(val, false); err != nil {
if err := to.AppendBytes(val, false); err != nil {
return err
}
}
Expand Down
22 changes: 22 additions & 0 deletions pkg/sql/plan/function/func_cast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2724,6 +2724,28 @@ func TestCastJsonToJsonOverloadResolution(t *testing.T) {
require.NoError(t, err)
}

// TestCastJsonToVarchar verifies that casting a JSON value to VARCHAR uses JSON_UNQUOTE semantics,
// i.e. JSON strings lose their outer double-quotes (MySQL-compatible behavior).
func TestCastJsonToVarchar(t *testing.T) {
proc := testutil.NewProcess(t)

jsonTexts := []string{`"active"`, `42`, `true`, `null`, `[1,2,3]`, `{"k":"v"}`}
// After unquote: JSON strings lose outer quotes; other types keep their JSON text representation.
expected := []string{"active", "42", "true", "null", "[1, 2, 3]", `{"k": "v"}`}
nulls := []bool{false, false, false, false, false, false}
encoded := makeJSONEncodedFromText(t, jsonTexts, nulls)

toType := types.New(types.T_varchar, 256, 0)
inputs := []FunctionTestInput{
NewFunctionTestInput(types.T_json.ToType(), encoded, nulls),
NewFunctionTestInput(toType, []string{}, []bool{}),
}
expect := NewFunctionTestResult(toType, false, expected, nulls)
fcTC := NewFunctionTestCase(proc, inputs, expect, NewCast)
succeed, info := fcTC.Run()
require.True(t, succeed, info)
}

// emptySliceForCastTarget returns an empty slice of the right type for the second (target type) cast parameter.
func emptySliceForCastTarget(oid types.T) any {
switch oid {
Expand Down
11 changes: 11 additions & 0 deletions pkg/sql/plan/function/func_compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ func otherCompareOperatorSupports(typ1, typ2 types.Type) bool {
return true
}

// jsonOrderingWithStringNotSupported returns true when one operand is JSON and the other
// is a MySQL string type, indicating that ordering comparisons (>, <, >=, <=) are not
// supported for this combination. Only equality/inequality (= and !=) are allowed.
func jsonOrderingWithStringNotSupported(inputs []types.Type) bool {
if len(inputs) != 2 {
return false
}
return (inputs[0].Oid == types.T_json) != (inputs[1].Oid == types.T_json) &&
(inputs[0].Oid.IsMySQLString() || inputs[1].Oid.IsMySQLString())
}

func equalAndNotEqualOperatorSupports(typ1, typ2 types.Type) bool {
if typ1.Oid != typ2.Oid {
return false
Expand Down
17 changes: 17 additions & 0 deletions pkg/sql/plan/function/list_operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ var supportedOperators = []FuncNew{
layout: COMPARISON_OPERATOR,
checkFn: func(overloads []overload, inputs []types.Type) checkResult {
if len(inputs) == 2 {
if jsonOrderingWithStringNotSupported(inputs) {
return newCheckResultWithFailure(failedFunctionParametersWrong)
}
Comment thread
aptend marked this conversation as resolved.
has, t1, t2 := fixedTypeCastRule1(inputs[0], inputs[1])
if has {
if otherCompareOperatorSupports(t1, t2) {
Expand Down Expand Up @@ -165,6 +168,9 @@ var supportedOperators = []FuncNew{
layout: COMPARISON_OPERATOR,
checkFn: func(overloads []overload, inputs []types.Type) checkResult {
if len(inputs) == 2 {
if jsonOrderingWithStringNotSupported(inputs) {
return newCheckResultWithFailure(failedFunctionParametersWrong)
}
has, t1, t2 := fixedTypeCastRule1(inputs[0], inputs[1])
if has {
if otherCompareOperatorSupports(t1, t2) {
Expand Down Expand Up @@ -200,6 +206,9 @@ var supportedOperators = []FuncNew{
layout: COMPARISON_OPERATOR,
checkFn: func(overloads []overload, inputs []types.Type) checkResult {
if len(inputs) == 2 {
if jsonOrderingWithStringNotSupported(inputs) {
return newCheckResultWithFailure(failedFunctionParametersWrong)
}
has, t1, t2 := fixedTypeCastRule1(inputs[0], inputs[1])
if has {
if otherCompareOperatorSupports(t1, t2) {
Expand Down Expand Up @@ -235,6 +244,9 @@ var supportedOperators = []FuncNew{
layout: COMPARISON_OPERATOR,
checkFn: func(overloads []overload, inputs []types.Type) checkResult {
if len(inputs) == 2 {
if jsonOrderingWithStringNotSupported(inputs) {
return newCheckResultWithFailure(failedFunctionParametersWrong)
}
has, t1, t2 := fixedTypeCastRule1(inputs[0], inputs[1])
if has {
if otherCompareOperatorSupports(t1, t2) {
Expand Down Expand Up @@ -272,6 +284,11 @@ var supportedOperators = []FuncNew{
return newCheckResultWithFailure(failedFunctionParametersWrong)
}

if jsonOrderingWithStringNotSupported([]types.Type{inputs[0], inputs[1]}) ||
jsonOrderingWithStringNotSupported([]types.Type{inputs[0], inputs[2]}) {
return newCheckResultWithFailure(failedFunctionParametersWrong)
}

has0, t01, t1 := fixedTypeCastRule1(inputs[0], inputs[1])
has1, t02, t2 := fixedTypeCastRule1(inputs[0], inputs[2])
if t01.Oid != t02.Oid {
Expand Down
8 changes: 4 additions & 4 deletions pkg/sql/plan/function/type_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -937,13 +937,13 @@ func initFixed1() {
{types.T_json, types.T_time, types.T_time, types.T_time},
{types.T_json, types.T_datetime, types.T_datetime, types.T_datetime},
{types.T_json, types.T_timestamp, types.T_timestamp, types.T_timestamp},
{types.T_json, types.T_char, types.T_json, types.T_json},
{types.T_json, types.T_varchar, types.T_json, types.T_json},
{types.T_json, types.T_char, types.T_varchar, types.T_varchar},
{types.T_json, types.T_varchar, types.T_varchar, types.T_varchar},
{types.T_json, types.T_uuid, types.T_uuid, types.T_uuid},
{types.T_json, types.T_binary, types.T_json, types.T_json},
{types.T_json, types.T_varbinary, types.T_json, types.T_json},
{types.T_json, types.T_blob, types.T_json, types.T_json},
{types.T_json, types.T_text, types.T_json, types.T_json},
{types.T_json, types.T_blob, types.T_varchar, types.T_varchar},
{types.T_json, types.T_text, types.T_varchar, types.T_varchar},
{types.T_uuid, types.T_char, types.T_uuid, types.T_uuid},
{types.T_uuid, types.T_varchar, types.T_uuid, types.T_uuid},
{types.T_uuid, types.T_binary, types.T_uuid, types.T_uuid},
Expand Down
4 changes: 4 additions & 0 deletions test/distributed/cases/function/func_json_arrow.result
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,8 @@ insert into jt_diff values (2, '{"status": "inactive", "score": 60}');
insert into jt_diff values (3, '{"status": "active", "score": 85}');
select id from jt_diff where doc -> '$.status' = '"active"' order by id;
➤ id[4,11,0]
select id from jt_diff where doc -> '$.status' = 'active' order by id;
➤ id[4,11,0] 𝄀
1 𝄀
3
drop table if exists jt_diff;
5 changes: 3 additions & 2 deletions test/distributed/cases/function/func_json_arrow.sql
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ create table jt_diff(id int primary key, doc json);
insert into jt_diff values (1, '{"status": "active", "score": 90}');
insert into jt_diff values (2, '{"status": "inactive", "score": 60}');
insert into jt_diff values (3, '{"status": "active", "score": 85}');
-- @bvt:issue#23935
-- comparing -> result with JSON-quoted string should return empty set (MySQL compatible)
select id from jt_diff where doc -> '$.status' = '"active"' order by id;
-- @bvt:issue
-- comparing -> result with unquoted string should return matching rows (MySQL compatible)
select id from jt_diff where doc -> '$.status' = 'active' order by id;
drop table if exists jt_diff;
6 changes: 3 additions & 3 deletions test/distributed/cases/function/func_json_extract.result
Original file line number Diff line number Diff line change
Expand Up @@ -603,13 +603,13 @@ INSERT INTO example_table (data)
VALUES (
'{"CODE": "CODE-3", "LINE": "LINE-4", "PANEL": "PANEL-17", "item1": "value1", "item2": "value2", "item3": "value3", "item4": "value4", "item5": "value5", "item6": "value6", "item7": "value7", "item8": "value8", "item9": "value9", "item10": "value10", "item11": "value11", "item12": "value12", "item13": "value13", "item14": "value14", "item15": "value15", "item16": "value16", "item17": "value17", "item18": "value18", "item19": "value19", "item20": "value20"}'
);
select * from example_table where json_extract(data, '$.CODE') = '"CODE-3"';
select * from example_table where json_extract(data, '$.CODE') = 'CODE-3';
id data
1 {"CODE": "CODE-3", "LINE": "LINE-4", "PANEL": "PANEL-17", "item1": "value1", "item10": "value10", "item11": "value11", "item12": "value12", "item13": "value13", "item14": "value14", "item15": "value15", "item16": "value16", "item17": "value17", "item18": "value18", "item19": "value19", "item2": "value2", "item20": "value20", "item3": "value3", "item4": "value4", "item5": "value5", "item6": "value6", "item7": "value7", "item8": "value8", "item9": "value9"}
select * from example_table where json_extract(data, '$.LINE') = '"LINE-4"';
select * from example_table where json_extract(data, '$.LINE') = 'LINE-4';
id data
1 {"CODE": "CODE-3", "LINE": "LINE-4", "PANEL": "PANEL-17", "item1": "value1", "item10": "value10", "item11": "value11", "item12": "value12", "item13": "value13", "item14": "value14", "item15": "value15", "item16": "value16", "item17": "value17", "item18": "value18", "item19": "value19", "item2": "value2", "item20": "value20", "item3": "value3", "item4": "value4", "item5": "value5", "item6": "value6", "item7": "value7", "item8": "value8", "item9": "value9"}
select * from example_table where json_extract(data, '$.PANEL') = '"PANEL-17"';
select * from example_table where json_extract(data, '$.PANEL') = 'PANEL-17';
id data
1 {"CODE": "CODE-3", "LINE": "LINE-4", "PANEL": "PANEL-17", "item1": "value1", "item10": "value10", "item11": "value11", "item12": "value12", "item13": "value13", "item14": "value14", "item15": "value15", "item16": "value16", "item17": "value17", "item18": "value18", "item19": "value19", "item2": "value2", "item20": "value20", "item3": "value3", "item4": "value4", "item5": "value5", "item6": "value6", "item7": "value7", "item8": "value8", "item9": "value9"}
drop database test;
6 changes: 3 additions & 3 deletions test/distributed/cases/function/func_json_extract.test
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ INSERT INTO example_table (data)
VALUES (
'{"CODE": "CODE-3", "LINE": "LINE-4", "PANEL": "PANEL-17", "item1": "value1", "item2": "value2", "item3": "value3", "item4": "value4", "item5": "value5", "item6": "value6", "item7": "value7", "item8": "value8", "item9": "value9", "item10": "value10", "item11": "value11", "item12": "value12", "item13": "value13", "item14": "value14", "item15": "value15", "item16": "value16", "item17": "value17", "item18": "value18", "item19": "value19", "item20": "value20"}'
);
select * from example_table where json_extract(data, '$.CODE') = '"CODE-3"';
select * from example_table where json_extract(data, '$.LINE') = '"LINE-4"';
select * from example_table where json_extract(data, '$.PANEL') = '"PANEL-17"';
select * from example_table where json_extract(data, '$.CODE') = 'CODE-3';
select * from example_table where json_extract(data, '$.LINE') = 'LINE-4';
select * from example_table where json_extract(data, '$.PANEL') = 'PANEL-17';
drop database test;
4 changes: 2 additions & 2 deletions test/distributed/cases/function/func_like.result
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ result
1
select json_extract('{"a":"1"}', '$.a') like '1' as result;
result
0
1
select json_extract('{"a":"1"}', '$.a') like 1 as result;
result
0
1
select json_extract('{"a":"1"}', '$.a') like '%1%' as result;
result
1
Expand Down
8 changes: 4 additions & 4 deletions test/distributed/cases/operator/row_constructor.result
Original file line number Diff line number Diff line change
Expand Up @@ -540,13 +540,13 @@ create table row05(col1 blob,col2 json,col3 binary(10) not null);
insert into row05 values('abcdef','{"t1":"a"}',456);
insert into row05 values('_bcdef','{"t1":"c"}',100000);
insert into row05 values('__cdef',null,0);
select (col1,col2) = ('abcdef','{"t1":"a"}') from row05;
(col1, col2) = (abcdef, {"t1":"a"})
select (col1,col2) = ('abcdef','{"t1": "a"}') from row05;
(col1, col2) = (abcdef, {"t1": "a"})
1
0
0
select (col1,col2) != ('abcdef','{"ehyiuwqnve":"ashyiujewv"}') from row05;
(col1, col2) != (abcdef, {"ehyiuwqnve":"ashyiujewv"})
select (col1,col2) != ('abcdef','{"ehyiuwqnve": "ashyiujewv"}') from row05;
(col1, col2) != (abcdef, {"ehyiuwqnve": "ashyiujewv"})
1
1
1
Expand Down
4 changes: 2 additions & 2 deletions test/distributed/cases/operator/row_constructor.sql
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ create table row05(col1 blob,col2 json,col3 binary(10) not null);
insert into row05 values('abcdef','{"t1":"a"}',456);
insert into row05 values('_bcdef','{"t1":"c"}',100000);
insert into row05 values('__cdef',null,0);
select (col1,col2) = ('abcdef','{"t1":"a"}') from row05;
select (col1,col2) != ('abcdef','{"ehyiuwqnve":"ashyiujewv"}') from row05;
select (col1,col2) = ('abcdef','{"t1": "a"}') from row05;
select (col1,col2) != ('abcdef','{"ehyiuwqnve": "ashyiujewv"}') from row05;
drop table row05;

drop database test;
Loading