Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spanner): add support for JSON data type #4104

Merged
merged 15 commits into from Aug 24, 2021
49 changes: 42 additions & 7 deletions spanner/integration_test.go
Expand Up @@ -18,6 +18,7 @@ package spanner

import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -113,6 +114,8 @@ var (
DateArray ARRAY<DATE>,
Timestamp TIMESTAMP,
TimestampArray ARRAY<TIMESTAMP>,
Numeric NUMERIC,
NumericArray ARRAY<NUMERIC>
) PRIMARY KEY (RowID)`,
}

Expand Down Expand Up @@ -169,6 +172,8 @@ var (
DateArray ARRAY<DATE>,
Timestamp TIMESTAMP,
TimestampArray ARRAY<TIMESTAMP>,
Numeric NUMERIC,
NumericArray ARRAY<NUMERIC>
) PRIMARY KEY (RowID)`,
}

Expand Down Expand Up @@ -1561,21 +1566,22 @@ func TestIntegration_BasicTypes(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
stmts := singerDBStatements
stmts = []string{
`CREATE TABLE Singers (
if !isEmulatorEnvSet() {
stmts = []string{
`CREATE TABLE Singers (
SingerId INT64 NOT NULL,
FirstName STRING(1024),
LastName STRING(1024),
SingerInfo BYTES(MAX)
) PRIMARY KEY (SingerId)`,
`CREATE INDEX SingerByName ON Singers(FirstName, LastName)`,
`CREATE TABLE Accounts (
`CREATE INDEX SingerByName ON Singers(FirstName, LastName)`,
`CREATE TABLE Accounts (
AccountId INT64 NOT NULL,
Nickname STRING(100),
Balance INT64 NOT NULL,
) PRIMARY KEY (AccountId)`,
`CREATE INDEX AccountByNickname ON Accounts(Nickname) STORING (Balance)`,
`CREATE TABLE Types (
`CREATE INDEX AccountByNickname ON Accounts(Nickname) STORING (Balance)`,
`CREATE TABLE Types (
RowID INT64 NOT NULL,
String STRING(MAX),
StringArray ARRAY<STRING(MAX)>,
Expand All @@ -1592,8 +1598,11 @@ func TestIntegration_BasicTypes(t *testing.T) {
Timestamp TIMESTAMP,
TimestampArray ARRAY<TIMESTAMP>,
Numeric NUMERIC,
NumericArray ARRAY<NUMERIC>
NumericArray ARRAY<NUMERIC>,
JSON JSON,
JSONArray ARRAY<JSON>
) PRIMARY KEY (RowID)`,
}
}
client, _, cleanup := prepareIntegrationTest(ctx, t, DefaultSessionPoolConfig, stmts)
defer cleanup()
Expand All @@ -1613,6 +1622,16 @@ func TestIntegration_BasicTypes(t *testing.T) {
n1 := *n1p
n2 := *n2p

type Message struct {
Name string
Body string
Time int64
}
msg := Message{"Alice", "Hello", 1294706395881547000}
jsonStr := `{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`
var unmarshalledJSONstruct interface{}
json.Unmarshal([]byte(jsonStr), &unmarshalledJSONstruct)

tests := []struct {
col string
val interface{}
Expand Down Expand Up @@ -1771,6 +1790,22 @@ func TestIntegration_BasicTypes(t *testing.T) {
}
}

if !isEmulatorEnvSet() {
tests = append(tests, []struct {
col string
val interface{}
want interface{}
}{
{col: "JSON", val: NullJSON{msg, true}, want: msg},
{col: "JSON", val: NullJSON{msg, true}, want: NullJSON{unmarshalledJSONstruct, true}},
hengfengli marked this conversation as resolved.
Show resolved Hide resolved
{col: "JSON", val: NullJSON{msg, false}},
{col: "JSON", val: nil, want: NullJSON{}},
{col: "JSONArray", val: []NullJSON(nil)},
{col: "JSONArray", val: []NullJSON{}},
{col: "JSONArray", val: []NullJSON{{msg, true}, {msg, true}, {}}},
hengfengli marked this conversation as resolved.
Show resolved Hide resolved
}...)
}

// Verify that we can insert the rows using mutations.
var muts []*Mutation
for i, test := range tests {
Expand Down
4 changes: 4 additions & 0 deletions spanner/protoutils.go
Expand Up @@ -73,6 +73,10 @@ func numericType() *sppb.Type {
return &sppb.Type{Code: sppb.TypeCode_NUMERIC}
}

func jsonType() *sppb.Type {
return &sppb.Type{Code: sppb.TypeCode_JSON}
}

func bytesProto(b []byte) *proto3.Value {
return &proto3.Value{Kind: &proto3.Value_StringValue{StringValue: base64.StdEncoding.EncodeToString(b)}}
}
Expand Down