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: add support for JSON type #353

Merged
merged 22 commits into from Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions google/cloud/spanner_v1/_helpers.py
Expand Up @@ -244,6 +244,8 @@ def _parse_value_pb(value_pb, field_type):
]
elif type_code == TypeCode.NUMERIC:
return decimal.Decimal(value_pb.string_value)
elif type_code == TypeCode.JSON:
return value_pb.string_value
else:
raise ValueError("Unknown type: %s" % (field_type,))

Expand Down
1 change: 1 addition & 0 deletions google/cloud/spanner_v1/param_types.py
Expand Up @@ -28,6 +28,7 @@
DATE = Type(code=TypeCode.DATE)
TIMESTAMP = Type(code=TypeCode.TIMESTAMP)
NUMERIC = Type(code=TypeCode.NUMERIC)
JSON = Type(code=TypeCode.JSON)


def Array(element_type): # pylint: disable=invalid-name
Expand Down
1 change: 1 addition & 0 deletions google/cloud/spanner_v1/streamed.py
Expand Up @@ -316,6 +316,7 @@ def _merge_struct(lhs, rhs, type_):
TypeCode.STRUCT: _merge_struct,
TypeCode.TIMESTAMP: _merge_string,
TypeCode.NUMERIC: _merge_string,
TypeCode.JSON: _merge_string,
}


Expand Down
5 changes: 4 additions & 1 deletion tests/_fixtures.py
Expand Up @@ -45,7 +45,10 @@
timestamp_value TIMESTAMP,
timestamp_array ARRAY<TIMESTAMP>,
numeric_value NUMERIC,
numeric_array ARRAY<NUMERIC>)
numeric_array ARRAY<NUMERIC>,
json_value JSON,
json_array ARRAY<JSON>,
)
PRIMARY KEY (pkey);
CREATE TABLE counters (
name STRING(1024),
Expand Down
33 changes: 31 additions & 2 deletions tests/system/test_session_api.py
Expand Up @@ -19,7 +19,7 @@
import struct
import threading
import time

import json
import pytest

import grpc
Expand All @@ -43,6 +43,24 @@
BYTES_2 = b"Ym9vdHM="
NUMERIC_1 = decimal.Decimal("0.123456789")
NUMERIC_2 = decimal.Decimal("1234567890")
JSON_1 = json.dumps(
{
"sample_boolean": True,
"sample_int": 872163,
"sample float": 7871.298,
"sample_null": None,
"sample_string": "abcdef",
"sample_array": [23, 76, 19],
},
sort_keys=True,
separators=(",", ":"),
)
JSON_2 = json.dumps(
{"sample_object": {"name": "Anamika", "id": 2635}},
sort_keys=True,
separators=(",", ":"),
)

COUNTERS_TABLE = "counters"
COUNTERS_COLUMNS = ("name", "value")
ALL_TYPES_TABLE = "all_types"
Expand All @@ -64,8 +82,10 @@
"timestamp_array",
"numeric_value",
"numeric_array",
"json_value",
"json_array",
)
EMULATOR_ALL_TYPES_COLUMNS = LIVE_ALL_TYPES_COLUMNS[:-2]
EMULATOR_ALL_TYPES_COLUMNS = LIVE_ALL_TYPES_COLUMNS[:-4]
AllTypesRowData = collections.namedtuple("AllTypesRowData", LIVE_ALL_TYPES_COLUMNS)
AllTypesRowData.__new__.__defaults__ = tuple([None for colum in LIVE_ALL_TYPES_COLUMNS])
EmulatorAllTypesRowData = collections.namedtuple(
Expand All @@ -88,6 +108,7 @@
AllTypesRowData(pkey=107, timestamp_value=SOME_TIME),
AllTypesRowData(pkey=108, timestamp_value=NANO_TIME),
AllTypesRowData(pkey=109, numeric_value=NUMERIC_1),
AllTypesRowData(pkey=110, json_value=JSON_1),
# empty array values
AllTypesRowData(pkey=201, int_array=[]),
AllTypesRowData(pkey=202, bool_array=[]),
Expand All @@ -97,6 +118,7 @@
AllTypesRowData(pkey=206, string_array=[]),
AllTypesRowData(pkey=207, timestamp_array=[]),
AllTypesRowData(pkey=208, numeric_array=[]),
AllTypesRowData(pkey=209, json_array=[]),
# non-empty array values, including nulls
AllTypesRowData(pkey=301, int_array=[123, 456, None]),
AllTypesRowData(pkey=302, bool_array=[True, False, None]),
Expand All @@ -106,6 +128,7 @@
AllTypesRowData(pkey=306, string_array=["One", "Two", None]),
AllTypesRowData(pkey=307, timestamp_array=[SOME_TIME, NANO_TIME, None]),
AllTypesRowData(pkey=308, numeric_array=[NUMERIC_1, NUMERIC_2, None]),
AllTypesRowData(pkey=309, json_array=[JSON_1, JSON_2, None]),
)
EMULATOR_ALL_TYPES_ROWDATA = (
# all nulls
Expand Down Expand Up @@ -1867,6 +1890,12 @@ def test_execute_sql_w_numeric_bindings(not_emulator, sessions_database):
)


def test_execute_sql_w_json_bindings(not_emulator, sessions_database):
_bind_test_helper(
sessions_database, spanner_v1.TypeCode.JSON, JSON_1, [JSON_1, JSON_2],
)


def test_execute_sql_w_query_param_struct(sessions_database):
name = "Phred"
count = 123
Expand Down
25 changes: 25 additions & 0 deletions tests/unit/test__helpers.py
Expand Up @@ -295,6 +295,17 @@ def test_w_numeric_precision_and_scale_invalid(self):
ValueError, err_msg, lambda: self._callFUT(value),
)

def test_w_json(self):
import json
from google.protobuf.struct_pb2 import Value

value = json.dumps(
{"id": 27863, "Name": "Anamika"}, sort_keys=True, separators=(",", ":")
)
value_pb = self._callFUT(value)
self.assertIsInstance(value_pb, Value)
self.assertEqual(value_pb.string_value, value)


class Test_make_list_value_pb(unittest.TestCase):
def _callFUT(self, *args, **kw):
Expand Down Expand Up @@ -552,6 +563,20 @@ def test_w_numeric(self):

self.assertEqual(self._callFUT(value_pb, field_type), VALUE)

def test_w_json(self):
import json
from google.protobuf.struct_pb2 import Value
from google.cloud.spanner_v1 import Type
from google.cloud.spanner_v1 import TypeCode

VALUE = json.dumps(
{"id": 27863, "Name": "Anamika"}, sort_keys=True, separators=(",", ":")
)
field_type = Type(code=TypeCode.JSON)
value_pb = Value(string_value=VALUE)

self.assertEqual(self._callFUT(value_pb, field_type), VALUE)

def test_w_unknown_type(self):
from google.protobuf.struct_pb2 import Value
from google.cloud.spanner_v1 import Type
Expand Down