Skip to content

Commit

Permalink
JSON Encoding: Use Type_RAW for marshalling json (#16637)
Browse files Browse the repository at this point in the history
Signed-off-by: Harshit Gangal <harshit@planetscale.com>
Signed-off-by: Rohit Nayak <rohit@planetscale.com>
Co-authored-by: Harshit Gangal <harshit@planetscale.com>
  • Loading branch information
rohit-nayak-ps and harshit-gangal committed Aug 29, 2024
1 parent d087b63 commit e89f684
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 34 deletions.
2 changes: 1 addition & 1 deletion go/mysql/json/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,6 @@ func MarshalSQLValue(buf []byte) (*sqltypes.Value, error) {
return nil, err
}

newVal := sqltypes.MakeTrusted(querypb.Type_JSON, jsonVal.MarshalSQLTo(nil))
newVal := sqltypes.MakeTrusted(querypb.Type_RAW, jsonVal.MarshalSQLTo(nil))
return &newVal, nil
}
23 changes: 23 additions & 0 deletions go/test/endtoend/vtgate/queries/dml/insert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,26 @@ func TestInsertAlias(t *testing.T) {
// this validates the record.
mcmp.Exec("select id, region_id, name from user_tbl order by id")
}

// TestInsertJson tests that selected json values are encoded correctly.
func TestInsertJson(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec(`insert into j_tbl(id, jdoc) values (1, '{}'), (2, '{"a": 1, "b": 2}')`)
mcmp.Exec(`select * from j_tbl order by id`)

mcmp.Exec(`insert into j_tbl(id, jdoc) select 3, json_object("k", "a")`)
mcmp.Exec(`select * from j_tbl order by id`)

mcmp.Exec(`insert into j_tbl(id, jdoc) select 4,JSON_OBJECT(
'date', CAST(1629849600 AS UNSIGNED),
'keywordSourceId', CAST(930701976723823 AS UNSIGNED),
'keywordSourceVersionId', CAST(210825230433 AS UNSIGNED)
)`)
mcmp.Exec(`select * from j_tbl order by id`)

utils.Exec(t, mcmp.VtConn, `insert into uks.j_utbl(id, jdoc) select * from sks.j_tbl`)
utils.AssertMatches(t, mcmp.VtConn, `select * from uks.j_utbl order by id`,
`[[INT64(1) JSON("{}")] [INT64(2) JSON("{\"a\": 1, \"b\": 2}")] [INT64(3) JSON("{\"k\": \"a\"}")] [INT64(4) JSON("{\"date\": 1629849600, \"keywordSourceId\": 930701976723823, \"keywordSourceVersionId\": 210825230433}")]]`)
}
2 changes: 1 addition & 1 deletion go/test/endtoend/vtgate/queries/dml/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func start(t *testing.T) (utils.MySQLCompare, func()) {

tables := []string{
"s_tbl", "num_vdx_tbl", "user_tbl", "order_tbl", "oevent_tbl", "oextra_tbl",
"auto_tbl", "oid_vdx_tbl", "unq_idx", "nonunq_idx", "u_tbl", "mixed_tbl", "lkp_map_idx",
"auto_tbl", "oid_vdx_tbl", "unq_idx", "nonunq_idx", "u_tbl", "mixed_tbl", "lkp_map_idx", "j_tbl", "j_utbl",
}
for _, table := range tables {
// TODO (@frouioui): following assertions produce different results between MySQL and Vitess
Expand Down
7 changes: 7 additions & 0 deletions go/test/endtoend/vtgate/queries/dml/sharded_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,10 @@ create table lkp_mixed_idx
keyspace_id varbinary(20),
primary key (lkp_key)
) Engine = InnoDB;

create table j_tbl
(
id bigint,
jdoc json,
primary key (id)
) Engine = InnoDB;
9 changes: 8 additions & 1 deletion go/test/endtoend/vtgate/queries/dml/unsharded_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,11 @@ values (0, 1, 1000);
insert into auto_seq(id, next_id, cache)
values (0, 666, 1000);
insert into mixed_seq(id, next_id, cache)
values (0, 1, 1000);
values (0, 1, 1000);

create table j_utbl
(
id bigint,
jdoc json,
primary key (id)
) Engine = InnoDB;
8 changes: 8 additions & 0 deletions go/test/endtoend/vtgate/queries/dml/vschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@
"name": "hash"
}
]
},
"j_tbl": {
"column_vindexes": [
{
"column": "id",
"name": "hash"
}
]
}
}
}
32 changes: 18 additions & 14 deletions go/vt/proto/query/query.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions go/vt/sqlparser/normalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ func TestNormalize(t *testing.T) {
outbv: map[string]*querypb.BindVariable{
"v1": sqltypes.BitNumBindVariable([]byte("0b11")),
},
}, {
// json value in insert
in: "insert into t values ('{\"k\", \"v\"}')",
outstmt: "insert into t values (:bv1 /* VARCHAR */)",
outbv: map[string]*querypb.BindVariable{
"bv1": sqltypes.StringBindVariable("{\"k\", \"v\"}"),
},
}, {
// json function in insert
in: "insert into t values (JSON_OBJECT('_id', 27, 'name', 'carrot'))",
outstmt: "insert into t values (json_object(:bv1 /* VARCHAR */, :bv2 /* INT64 */, :bv3 /* VARCHAR */, :bv4 /* VARCHAR */))",
outbv: map[string]*querypb.BindVariable{
"bv1": sqltypes.StringBindVariable("_id"),
"bv2": sqltypes.Int64BindVariable(27),
"bv3": sqltypes.StringBindVariable("name"),
"bv4": sqltypes.StringBindVariable("carrot"),
},
}, {
// ORDER BY column_position
in: "select a, b from t order by 1 asc",
Expand Down
2 changes: 1 addition & 1 deletion go/vt/sqlparser/parsed_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func EncodeValue(buf *strings.Builder, value *querypb.BindVariable) {
sqltypes.ProtoToValue(bv).EncodeSQLStringBuilder(buf)
}
buf.WriteByte(')')
case querypb.Type_JSON:
case querypb.Type_RAW:
v, _ := sqltypes.BindVariableToValue(value)
buf.Write(v.Raw())
default:
Expand Down
35 changes: 21 additions & 14 deletions go/vt/sqlparser/parsed_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ func TestGenerateQuery(t *testing.T) {
"vals": sqltypes.TestBindVariable([]any{1, "aa"}),
},
output: "select * from a where id in (1, 'aa')",
}, {
desc: "json bindvar and raw bindvar",
query: "insert into t values (:v1, :v2)",
bindVars: map[string]*querypb.BindVariable{
"v1": sqltypes.ValueBindVariable(sqltypes.MakeTrusted(querypb.Type_JSON, []byte(`{"key": "value"}`))),
"v2": sqltypes.ValueBindVariable(sqltypes.MakeTrusted(querypb.Type_RAW, []byte(`json_object("k", "v")`))),
},
output: `insert into t values ('{"key": "value"}', json_object("k", "v"))`,
}, {
desc: "list bind vars 0 arguments",
query: "select * from a where id in ::vals",
Expand Down Expand Up @@ -139,20 +147,19 @@ func TestGenerateQuery(t *testing.T) {

parser := NewTestParser()
for _, tcase := range tcases {
tree, err := parser.Parse(tcase.query)
if err != nil {
t.Errorf("parse failed for %s: %v", tcase.desc, err)
continue
}
buf := NewTrackedBuffer(nil)
buf.Myprintf("%v", tree)
pq := buf.ParsedQuery()
bytes, err := pq.GenerateQuery(tcase.bindVars, tcase.extras)
if err != nil {
assert.Equal(t, tcase.output, err.Error())
} else {
assert.Equal(t, tcase.output, string(bytes))
}
t.Run(tcase.query, func(t *testing.T) {
tree, err := parser.Parse(tcase.query)
require.NoError(t, err)
buf := NewTrackedBuffer(nil)
buf.Myprintf("%v", tree)
pq := buf.ParsedQuery()
bytes, err := pq.GenerateQuery(tcase.bindVars, tcase.extras)
if err != nil {
assert.Equal(t, tcase.output, err.Error())
} else {
assert.Equal(t, tcase.output, bytes)
}
})
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package io.vitess.jdbc;

import java.util.Set;
import java.util.EnumSet;

import io.vitess.proto.Query;
import io.vitess.util.MysqlDefs;
import io.vitess.util.charset.CharsetMapping;
Expand Down Expand Up @@ -274,6 +277,16 @@ public void testNumericAndDateTimeEncoding() throws SQLException {
}
}

// Define the types to skip
Set<Query.Type> typesToSkip = EnumSet.of(
Query.Type.UNRECOGNIZED,
Query.Type.EXPRESSION,
Query.Type.HEXVAL,
Query.Type.HEXNUM,
Query.Type.BITNUM,
Query.Type.RAW
);

@Test
public void testPrecisionAdjustFactor() throws SQLException {
VitessConnection conn = getVitessConnection();
Expand All @@ -294,7 +307,8 @@ public void testPrecisionAdjustFactor() throws SQLException {

conn.setIncludedFields(Query.ExecuteOptions.IncludedFields.TYPE_AND_NAME);
for (Query.Type type : Query.Type.values()) {
if (type == Query.Type.UNRECOGNIZED || type == Query.Type.EXPRESSION || type == Query.Type.HEXVAL || type == Query.Type.HEXNUM || type == Query.Type.BITNUM) {
// Skip if the type is in the set
if (typesToSkip.contains(type)) {
continue;
}

Expand Down
2 changes: 2 additions & 0 deletions proto/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ enum Type {
// VECTOR specifies a VECTOR type
// Properties: 35, IsQuoted.
VECTOR = 2083;
// RAW specifies a type which won't be quoted but the value used as-is while encoding.
RAW = 2084;
}

// Value represents a typed value.
Expand Down
3 changes: 2 additions & 1 deletion web/vtadmin/src/proto/vtadmin.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit e89f684

Please sign in to comment.