Skip to content

Commit d443cf0

Browse files
[release-18.0] JSON Encoding: Use Type_RAW for marshalling json (#16637) (#16680)
Signed-off-by: Rohit Nayak <rohit@planetscale.com> Co-authored-by: vitess-bot[bot] <108069721+vitess-bot[bot]@users.noreply.github.com> Co-authored-by: Rohit Nayak <rohit@planetscale.com>
1 parent da4aa79 commit d443cf0

File tree

15 files changed

+156
-40
lines changed

15 files changed

+156
-40
lines changed

go/mysql/json/marshal.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,7 @@ func MarshalSQLValue(buf []byte) (*sqltypes.Value, error) {
173173
if err != nil {
174174
return nil, err
175175
}
176-
newVal := sqltypes.MakeTrusted(querypb.Type_JSON, jsonVal.MarshalSQLTo(nil))
177-
if err != nil {
178-
return nil, err
179-
}
176+
177+
newVal := sqltypes.MakeTrusted(querypb.Type_RAW, jsonVal.MarshalSQLTo(nil))
180178
return &newVal, nil
181179
}

go/test/endtoend/vtgate/queries/dml/insert_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,3 +470,29 @@ func TestMixedCases(t *testing.T) {
470470
// final check count on the lookup vindex table.
471471
utils.AssertMatches(t, mcmp.VtConn, "select count(*) from lkp_mixed_idx", "[[INT64(12)]]")
472472
}
473+
474+
// TestInsertJson tests that selected json values are encoded correctly.
475+
func TestInsertJson(t *testing.T) {
476+
utils.SkipIfBinaryIsBelowVersion(t, 21, "vtgate")
477+
utils.SkipIfBinaryIsBelowVersion(t, 21, "vttablet")
478+
479+
mcmp, closer := start(t)
480+
defer closer()
481+
482+
mcmp.Exec(`insert into j_tbl(id, jdoc) values (1, '{}'), (2, '{"a": 1, "b": 2}')`)
483+
mcmp.Exec(`select * from j_tbl order by id`)
484+
485+
mcmp.Exec(`insert into j_tbl(id, jdoc) select 3, json_object("k", "a")`)
486+
mcmp.Exec(`select * from j_tbl order by id`)
487+
488+
mcmp.Exec(`insert into j_tbl(id, jdoc) select 4,JSON_OBJECT(
489+
'date', CAST(1629849600 AS UNSIGNED),
490+
'keywordSourceId', CAST(930701976723823 AS UNSIGNED),
491+
'keywordSourceVersionId', CAST(210825230433 AS UNSIGNED)
492+
)`)
493+
mcmp.Exec(`select * from j_tbl order by id`)
494+
495+
utils.Exec(t, mcmp.VtConn, `insert into uks.j_utbl(id, jdoc) select * from sks.j_tbl`)
496+
utils.AssertMatches(t, mcmp.VtConn, `select * from uks.j_utbl order by id`,
497+
`[[INT64(1) JSON("{}")] [INT64(2) JSON("{\"a\": 1, \"b\": 2}")] [INT64(3) JSON("{\"k\": \"a\"}")] [INT64(4) JSON("{\"date\": 1629849600, \"keywordSourceId\": 930701976723823, \"keywordSourceVersionId\": 210825230433}")]]`)
498+
}

go/test/endtoend/vtgate/queries/dml/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func start(t *testing.T) (utils.MySQLCompare, func()) {
133133

134134
tables := []string{
135135
"s_tbl", "num_vdx_tbl", "user_tbl", "order_tbl", "oevent_tbl", "oextra_tbl",
136-
"auto_tbl", "oid_vdx_tbl", "unq_idx", "nonunq_idx", "u_tbl", "mixed_tbl", "lkp_map_idx",
136+
"auto_tbl", "oid_vdx_tbl", "unq_idx", "nonunq_idx", "u_tbl", "mixed_tbl", "lkp_map_idx", "j_tbl", "j_utbl",
137137
}
138138
for _, table := range tables {
139139
// TODO (@frouioui): following assertions produce different results between MySQL and Vitess

go/test/endtoend/vtgate/queries/dml/sharded_schema.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,10 @@ create table lkp_mixed_idx
8686
keyspace_id varbinary(20),
8787
primary key (lkp_key)
8888
) Engine = InnoDB;
89+
90+
create table j_tbl
91+
(
92+
id bigint,
93+
jdoc json,
94+
primary key (id)
95+
) Engine = InnoDB;

go/test/endtoend/vtgate/queries/dml/unsharded_schema.sql

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,11 @@ values (0, 1, 1000);
3434
insert into auto_seq(id, next_id, cache)
3535
values (0, 666, 1000);
3636
insert into mixed_seq(id, next_id, cache)
37-
values (0, 1, 1000);
37+
values (0, 1, 1000);
38+
39+
create table j_utbl
40+
(
41+
id bigint,
42+
jdoc json,
43+
primary key (id)
44+
) Engine = InnoDB;

go/test/endtoend/vtgate/queries/dml/vschema.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,14 @@
188188
"name": "hash"
189189
}
190190
]
191+
},
192+
"j_tbl": {
193+
"column_vindexes": [
194+
{
195+
"column": "id",
196+
"name": "hash"
197+
}
198+
]
191199
}
192200
}
193201
}

go/vt/proto/query/query.pb.go

Lines changed: 18 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/vt/sqlparser/normalizer_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,23 @@ func TestNormalize(t *testing.T) {
209209
outbv: map[string]*querypb.BindVariable{
210210
"v1": sqltypes.HexNumBindVariable([]byte("0x03")),
211211
},
212+
}, {
213+
// json value in insert
214+
in: "insert into t values ('{\"k\", \"v\"}')",
215+
outstmt: "insert into t values (:bv1 /* VARCHAR */)",
216+
outbv: map[string]*querypb.BindVariable{
217+
"bv1": sqltypes.StringBindVariable("{\"k\", \"v\"}"),
218+
},
219+
}, {
220+
// json function in insert
221+
in: "insert into t values (JSON_OBJECT('_id', 27, 'name', 'carrot'))",
222+
outstmt: "insert into t values (json_object(:bv1 /* VARCHAR */, :bv2 /* INT64 */, :bv3 /* VARCHAR */, :bv4 /* VARCHAR */))",
223+
outbv: map[string]*querypb.BindVariable{
224+
"bv1": sqltypes.StringBindVariable("_id"),
225+
"bv2": sqltypes.Int64BindVariable(27),
226+
"bv3": sqltypes.StringBindVariable("name"),
227+
"bv4": sqltypes.StringBindVariable("carrot"),
228+
},
212229
}, {
213230
// ORDER BY column_position
214231
in: "select a, b from t order by 1 asc",

go/vt/sqlparser/parsed_query.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func EncodeValue(buf *strings.Builder, value *querypb.BindVariable) {
176176
sqltypes.ProtoToValue(bv).EncodeSQLStringBuilder(buf)
177177
}
178178
buf.WriteByte(')')
179-
case querypb.Type_JSON:
179+
case querypb.Type_RAW:
180180
v, _ := sqltypes.BindVariableToValue(value)
181181
buf.Write(v.Raw())
182182
default:

go/vt/sqlparser/parsed_query_test.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"reflect"
2121
"testing"
2222

23+
"github.com/stretchr/testify/require"
24+
2325
"vitess.io/vitess/go/sqltypes"
2426
querypb "vitess.io/vitess/go/vt/proto/query"
2527

@@ -79,6 +81,14 @@ func TestGenerateQuery(t *testing.T) {
7981
"vals": sqltypes.TestBindVariable([]any{1, "aa"}),
8082
},
8183
output: "select * from a where id in (1, 'aa')",
84+
}, {
85+
desc: "json bindvar and raw bindvar",
86+
query: "insert into t values (:v1, :v2)",
87+
bindVars: map[string]*querypb.BindVariable{
88+
"v1": sqltypes.ValueBindVariable(sqltypes.MakeTrusted(querypb.Type_JSON, []byte(`{"key": "value"}`))),
89+
"v2": sqltypes.ValueBindVariable(sqltypes.MakeTrusted(querypb.Type_RAW, []byte(`json_object("k", "v")`))),
90+
},
91+
output: `insert into t values ('{\"key\": \"value\"}', json_object("k", "v"))`,
8292
}, {
8393
desc: "list bind vars 0 arguments",
8494
query: "select * from a where id in ::vals",
@@ -136,20 +146,19 @@ func TestGenerateQuery(t *testing.T) {
136146
}
137147

138148
for _, tcase := range tcases {
139-
tree, err := Parse(tcase.query)
140-
if err != nil {
141-
t.Errorf("parse failed for %s: %v", tcase.desc, err)
142-
continue
143-
}
144-
buf := NewTrackedBuffer(nil)
145-
buf.Myprintf("%v", tree)
146-
pq := buf.ParsedQuery()
147-
bytes, err := pq.GenerateQuery(tcase.bindVars, tcase.extras)
148-
if err != nil {
149-
assert.Equal(t, tcase.output, err.Error())
150-
} else {
151-
assert.Equal(t, tcase.output, string(bytes))
152-
}
149+
t.Run(tcase.query, func(t *testing.T) {
150+
tree, err := Parse(tcase.query)
151+
require.NoError(t, err)
152+
buf := NewTrackedBuffer(nil)
153+
buf.Myprintf("%v", tree)
154+
pq := buf.ParsedQuery()
155+
bytes, err := pq.GenerateQuery(tcase.bindVars, tcase.extras)
156+
if err != nil {
157+
assert.Equal(t, tcase.output, err.Error())
158+
} else {
159+
assert.Equal(t, tcase.output, bytes)
160+
}
161+
})
153162
}
154163
}
155164

java/jdbc/src/test/java/io/vitess/jdbc/FieldWithMetadataTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package io.vitess.jdbc;
1818

19+
import java.util.Set;
20+
import java.util.EnumSet;
21+
1922
import io.vitess.proto.Query;
2023
import io.vitess.util.MysqlDefs;
2124
import io.vitess.util.charset.CharsetMapping;
@@ -274,6 +277,16 @@ public void testNumericAndDateTimeEncoding() throws SQLException {
274277
}
275278
}
276279

280+
// Define the types to skip
281+
Set<Query.Type> typesToSkip = EnumSet.of(
282+
Query.Type.UNRECOGNIZED,
283+
Query.Type.EXPRESSION,
284+
Query.Type.HEXVAL,
285+
Query.Type.HEXNUM,
286+
Query.Type.BITNUM,
287+
Query.Type.RAW
288+
);
289+
277290
@Test
278291
public void testPrecisionAdjustFactor() throws SQLException {
279292
VitessConnection conn = getVitessConnection();
@@ -294,7 +307,8 @@ public void testPrecisionAdjustFactor() throws SQLException {
294307

295308
conn.setIncludedFields(Query.ExecuteOptions.IncludedFields.TYPE_AND_NAME);
296309
for (Query.Type type : Query.Type.values()) {
297-
if (type == Query.Type.UNRECOGNIZED || type == Query.Type.EXPRESSION || type == Query.Type.HEXVAL || type == Query.Type.HEXNUM || type == Query.Type.BITNUM) {
310+
// Skip if the type is in the set
311+
if (typesToSkip.contains(type)) {
298312
continue;
299313
}
300314

proto/query.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ enum Type {
215215
// BITNUM specifies a base 2 binary type (unquoted varbinary).
216216
// Properties: 34, IsText.
217217
BITNUM = 4130;
218+
// RAW specifies a type which won't be quoted but the value used as-is while encoding.
219+
RAW = 2084;
218220
}
219221

220222
// Value represents a typed value.

web/vtadmin/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/vtadmin/src/proto/vtadmin.d.ts

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)