Skip to content

Commit

Permalink
feat(spanner): support pg jsonb (#19116)
Browse files Browse the repository at this point in the history
  • Loading branch information
NivedhaSenthil authored Oct 21, 2022
1 parent 83dd05e commit df2aab2
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 0 deletions.
2 changes: 2 additions & 0 deletions acceptance/data/fixtures.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ def stuff_pg_ddl_statement
date DATE,
numerics numeric[],
dates DATE[],
json jsonb,
-- json_array jsonb[],
PRIMARY KEY(id)
);
STUFFS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "spanner_helper"

describe "Spanner Client", :params, :pgjsonb, :spanner do
let(:db) { spanner_pg_client }
let(:json_params) { { "venue" => "abc", "rating" => 10 } }
let :json_array_params do
3.times.map do |i|
{ "venue" => "abc-#{i}", "rating" => 10 + i }
end
end

before do
skip if emulator_enabled?
end

it "queries and returns a string parameter" do
results = db.execute_query "SELECT $1 AS value", params: { p1: json_params }, types: { p1: :PG_JSONB }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields[:value]).must_equal :JSON
_(results.rows.first[:value]).must_equal json_params
end

it "queries and returns a NULL string parameter" do
results = db.execute_query "SELECT $1 AS value", params: { p1: nil }, types: { p1: :PG_JSONB }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields[:value]).must_equal :JSON
_(results.rows.first[:value]).must_be :nil?
end

it "queries and returns an array of json parameters" do
skip "Arrays not supported yet"
results = db.execute_query "SELECT $1 AS value", params: { p1: json_array_params }, types: { p1: [:PG_JSONB] }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields[:value]).must_equal [:JSON]
_(results.rows.first[:value]).must_equal json_array_params
end

it "queries and returns an array of json parameters with a nil value" do
skip "Arrays not supported yet"
params = [nil].concat json_array_params
results = db.execute_query "SELECT $1 AS value", params: { p1: params }, types: { p1: [:PG_JSONB] }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields[:value]).must_equal [:JSON]
_(results.rows.first[:value]).must_equal params
end

it "queries and returns an empty array of json parameters" do
skip "Arrays not supported yet"
results = db.execute_query "SELECT $1 AS value", params: { p1: [] }, types: { p1: [:PG_JSONB] }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields[:value]).must_equal [:JSON]
_(results.rows.first[:value]).must_equal []
end

it "queries and returns a NULL array of json parameters" do
skip "Arrays not supported yet"
results = db.execute_query "SELECT $1 AS value", params: { p1: nil }, types: { p1: [:PG_JSONB] }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields[:value]).must_equal [:JSON]
_(results.rows.first[:value]).must_be :nil?
end
end
160 changes: 160 additions & 0 deletions google-cloud-spanner/acceptance/spanner/client/types/pgjsonb_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "spanner_helper"

describe "Spanner Client", :types, :json, :spanner do
let(:db) { spanner_pg_client }
let(:table_name) { "stuffs" }
let(:json_params) { { "venue" => "abc", "rating" => 10 } }
let :json_array_params do
3.times.map do |i|
{ "venue" => "abc-#{i}", "rating" => 10 + i }
end
end

before do
skip if emulator_enabled?
end

it "writes and reads json" do
id = SecureRandom.int64
db.upsert table_name, { id: id, json: json_params }
results = db.read table_name, [:id, :json], keys: id

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json: :JSON })
_(results.rows.first.to_h).must_equal({ id: id, json: json_params })
end

it "writes and queries json" do
id = SecureRandom.int64
db.upsert table_name, { id: id, json: json_params }
results = db.execute_query "SELECT id, json FROM #{table_name} WHERE id = $1", params: { p1: id }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json: :JSON })
_(results.rows.first.to_h).must_equal({ id: id, json: json_params })
end

it "writes and reads NULL json" do
id = SecureRandom.int64
db.upsert table_name, { id: id, json: nil }
results = db.read table_name, [:id, :json], keys: id

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json: :JSON })
_(results.rows.first.to_h).must_equal({ id: id, json: nil })
end

it "writes and queries NULL json" do
id = SecureRandom.int64
db.upsert table_name, { id: id, json: nil }
results = db.execute_query "SELECT id, json FROM #{table_name} WHERE id = $1", params: { p1: id }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json: :JSON })
_(results.rows.first.to_h).must_equal({ id: id, json: nil })
end

it "writes and reads array of json" do
skip "Arrays not supported yet"
id = SecureRandom.int64
db.upsert table_name, { id: id, json_array: json_array_params }
results = db.read table_name, [:id, :json_array], keys: id

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: json_array_params })
end

it "writes and queries array of json" do
skip "Arrays not supported yet"
id = SecureRandom.int64
db.upsert table_name, { id: id, json_array: json_array_params }
results = db.execute_query "SELECT id, json_array FROM #{table_name} WHERE id = $1", params: { p1: id }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: json_array_params })
end

it "writes and reads array of json with NULL" do
skip "Arrays not supported yet"
id = SecureRandom.int64
params = [nil].concat json_array_params
db.upsert table_name, { id: id, json_array: params }
results = db.read table_name, [:id, :json_array], keys: id

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: params })
end

it "writes and queries array of json with NULL" do
skip "Arrays not supported yet"
id = SecureRandom.int64
params = [nil].concat json_array_params
db.upsert table_name, { id: id, json_array: params }
results = db.execute_query "SELECT id, json_array FROM #{table_name} WHERE id = $1", params: { p1: id }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: params })
end

it "writes and reads empty array of json" do
skip "Arrays not supported yet"
id = SecureRandom.int64
db.upsert table_name, { id: id, json_array: [] }
results = db.read table_name, [:id, :json_array], keys: id

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: [] })
end

it "writes and queries empty array of json array" do
skip "Arrays not supported yet"
id = SecureRandom.int64
db.upsert table_name, { id: id, json_array: [] }
results = db.execute_query "SELECT id, json_array FROM #{table_name} WHERE id = $1", params: { p1: id }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: [] })
end

it "writes and reads NULL array of json" do
skip "Arrays not supported yet"
id = SecureRandom.int64
db.upsert table_name, { id: id, json_array: nil }
results = db.read table_name, [:id, :json_array], keys: id

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: nil })
end

it "writes and queries NULL array of json" do
skip "Arrays not supported yet"
id = SecureRandom.int64
db.upsert table_name, { id: id, json_array: nil }
results = db.execute_query "SELECT id, json_array FROM #{table_name} WHERE id = $1", params: { p1: id }

_(results).must_be_kind_of Google::Cloud::Spanner::Results
_(results.fields.to_h).must_equal({ id: :INT64, json_array: [:JSON] })
_(results.rows.first.to_h).must_equal({ id: id, json_array: nil })
end
end
2 changes: 2 additions & 0 deletions google-cloud-spanner/lib/google/cloud/spanner/convert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ def grpc_type_for_field field
)
when :PG_NUMERIC
V1::Type.new(code: :NUMERIC, type_annotation: :PG_NUMERIC)
when :PG_JSONB
V1::Type.new(code: :JSON, type_annotation: :PG_JSONB)
else
V1::Type.new(code: field)
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require "helper"
require "bigdecimal"

describe Google::Cloud::Spanner::Convert, :grpc_type_for_field, :mock_spanner do

it "converts a BOOL value" do
field = :BOOL
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :BOOL
end

it "converts a INT64 value" do
field = :INT64
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :INT64
end

it "converts a FLOAT64 value" do
field = :FLOAT64
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :FLOAT64
end

it "converts a TIMESTAMP value" do
field = :TIMESTAMP
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :TIMESTAMP
end

it "converts a DATE value" do
field = :DATE
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :DATE
end

it "converts a STRING value" do
field = :STRING
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :STRING
end

it "converts a BYTES value" do
field = :BYTES
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :BYTES
end

it "converts an ARRAY of INT64 values" do
field = [:INT64]
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :ARRAY
assert_equal type.array_element_type.code, :INT64
end

it "converts a STRUCT value" do
field = :STRUCT
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :STRUCT
end

it "converts a NUMERIC value" do
field = :NUMERIC
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :NUMERIC
end

it "converts a JSON value" do
field = :JSON
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :JSON
end

it "converts a PG_JSONB value" do
field = :PG_JSONB
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :JSON
assert_equal type.type_annotation, :PG_JSONB
end

it "converts a PG_NUMERIC value" do
field = :PG_NUMERIC
type = Google::Cloud::Spanner::Convert.grpc_type_for_field field
assert_equal type.code, :NUMERIC
assert_equal type.type_annotation, :PG_NUMERIC
end
end

0 comments on commit df2aab2

Please sign in to comment.