Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion pkg/sql/plan/function/func_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -5708,10 +5708,11 @@ func jsonToStr(
}
} else {
bj := types.DecodeJson(v)
val, err := bj.MarshalJSON()
str, err := bj.Unquote()
if err != nil {
return err
}
Comment thread
aptend marked this conversation as resolved.
Outdated
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))
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 @@ -2596,6 +2596,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
12 changes: 12 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
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