google-cloud-spanner 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/google-cloud-spanner.rb +106 -0
- data/lib/google/cloud/spanner.rb +382 -0
- data/lib/google/cloud/spanner/admin/database/v1.rb +17 -0
- data/lib/google/cloud/spanner/admin/database/v1/database_admin_client.rb +703 -0
- data/lib/google/cloud/spanner/admin/database/v1/database_admin_client_config.json +73 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/iam/v1/policy.rb +139 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/protobuf/any.rb +114 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/spanner/admin/database/v1/spanner_database_admin.rb +188 -0
- data/lib/google/cloud/spanner/admin/instance/v1.rb +17 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/iam/v1/policy.rb +139 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/any.rb +114 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/field_mask.rb +223 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/spanner/admin/instance/v1/spanner_instance_admin.rb +268 -0
- data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client.rb +868 -0
- data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client_config.json +78 -0
- data/lib/google/cloud/spanner/client.rb +1034 -0
- data/lib/google/cloud/spanner/commit.rb +351 -0
- data/lib/google/cloud/spanner/convert.rb +311 -0
- data/lib/google/cloud/spanner/credentials.rb +32 -0
- data/lib/google/cloud/spanner/data.rb +199 -0
- data/lib/google/cloud/spanner/database.rb +377 -0
- data/lib/google/cloud/spanner/database/job.rb +179 -0
- data/lib/google/cloud/spanner/database/list.rb +171 -0
- data/lib/google/cloud/spanner/errors.rb +73 -0
- data/lib/google/cloud/spanner/fields.rb +252 -0
- data/lib/google/cloud/spanner/instance.rb +472 -0
- data/lib/google/cloud/spanner/instance/config.rb +99 -0
- data/lib/google/cloud/spanner/instance/config/list.rb +171 -0
- data/lib/google/cloud/spanner/instance/job.rb +197 -0
- data/lib/google/cloud/spanner/instance/list.rb +167 -0
- data/lib/google/cloud/spanner/policy.rb +201 -0
- data/lib/google/cloud/spanner/pool.rb +279 -0
- data/lib/google/cloud/spanner/project.rb +480 -0
- data/lib/google/cloud/spanner/range.rb +99 -0
- data/lib/google/cloud/spanner/results.rb +280 -0
- data/lib/google/cloud/spanner/service.rb +458 -0
- data/lib/google/cloud/spanner/session.rb +565 -0
- data/lib/google/cloud/spanner/snapshot.rb +260 -0
- data/lib/google/cloud/spanner/transaction.rb +533 -0
- data/lib/google/cloud/spanner/v1.rb +17 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +77 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +73 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +81 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +148 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +80 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +120 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +175 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +206 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +351 -0
- data/lib/google/cloud/spanner/v1/spanner_client.rb +850 -0
- data/lib/google/cloud/spanner/v1/spanner_client_config.json +78 -0
- data/lib/google/cloud/spanner/version.rb +22 -0
- data/lib/google/spanner/admin/database/v1/spanner_database_admin_pb.rb +85 -0
- data/lib/google/spanner/admin/database/v1/spanner_database_admin_services_pb.rb +95 -0
- data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_pb.rb +106 -0
- data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_services_pb.rb +180 -0
- data/lib/google/spanner/v1/keys_pb.rb +33 -0
- data/lib/google/spanner/v1/mutation_pb.rb +38 -0
- data/lib/google/spanner/v1/query_plan_pb.rb +47 -0
- data/lib/google/spanner/v1/result_set_pb.rb +43 -0
- data/lib/google/spanner/v1/spanner_pb.rb +90 -0
- data/lib/google/spanner/v1/spanner_services_pb.rb +131 -0
- data/lib/google/spanner/v1/transaction_pb.rb +51 -0
- data/lib/google/spanner/v1/type_pb.rb +43 -0
- metadata +309 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
require "google/cloud/spanner/data"
|
|
17
|
+
require "google/cloud/spanner/convert"
|
|
18
|
+
|
|
19
|
+
module Google
|
|
20
|
+
module Cloud
|
|
21
|
+
module Spanner
|
|
22
|
+
##
|
|
23
|
+
# # Commit
|
|
24
|
+
#
|
|
25
|
+
# Accepts mutations for execution within a transaction. All writes will
|
|
26
|
+
# execute atomically at a single logical point in time across columns,
|
|
27
|
+
# rows, and tables in a database.
|
|
28
|
+
#
|
|
29
|
+
# All changes are accumulated in memory until the block passed to
|
|
30
|
+
# {Client#commit} completes.
|
|
31
|
+
#
|
|
32
|
+
# @example
|
|
33
|
+
# require "google/cloud/spanner"
|
|
34
|
+
#
|
|
35
|
+
# spanner = Google::Cloud::Spanner.new
|
|
36
|
+
#
|
|
37
|
+
# db = spanner.client "my-instance", "my-database"
|
|
38
|
+
#
|
|
39
|
+
# db.commit do |c|
|
|
40
|
+
# c.update "users", [{ id: 1, name: "Charlie", active: false }]
|
|
41
|
+
# c.insert "users", [{ id: 2, name: "Harvey", active: true }]
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
class Commit
|
|
45
|
+
##
|
|
46
|
+
# @private
|
|
47
|
+
def initialize
|
|
48
|
+
@mutations = []
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Inserts or updates rows in a table. If any of the rows already exist,
|
|
53
|
+
# then its column values are overwritten with the ones provided. Any
|
|
54
|
+
# column values not explicitly written are preserved.
|
|
55
|
+
#
|
|
56
|
+
# All changes are accumulated in memory until the block passed to
|
|
57
|
+
# {Client#commit} completes.
|
|
58
|
+
#
|
|
59
|
+
# @param [String] table The name of the table in the database to be
|
|
60
|
+
# modified.
|
|
61
|
+
# @param [Array<Hash>] rows One or more hash objects with the hash keys
|
|
62
|
+
# matching the table's columns, and the hash values matching the
|
|
63
|
+
# table's values.
|
|
64
|
+
#
|
|
65
|
+
# Ruby types are mapped to Spanner types as follows:
|
|
66
|
+
#
|
|
67
|
+
# | Spanner | Ruby | Notes |
|
|
68
|
+
# |-------------|----------------|---|
|
|
69
|
+
# | `BOOL` | `true`/`false` | |
|
|
70
|
+
# | `INT64` | `Integer` | |
|
|
71
|
+
# | `FLOAT64` | `Float` | |
|
|
72
|
+
# | `STRING` | `String` | |
|
|
73
|
+
# | `DATE` | `Date` | |
|
|
74
|
+
# | `TIMESTAMP` | `Time`, `DateTime` | |
|
|
75
|
+
# | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
|
|
76
|
+
# | `ARRAY` | `Array` | Nested arrays are not supported. |
|
|
77
|
+
#
|
|
78
|
+
# See [Data
|
|
79
|
+
# types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
|
|
80
|
+
#
|
|
81
|
+
# @example
|
|
82
|
+
# require "google/cloud/spanner"
|
|
83
|
+
#
|
|
84
|
+
# spanner = Google::Cloud::Spanner.new
|
|
85
|
+
#
|
|
86
|
+
# db = spanner.client "my-instance", "my-database"
|
|
87
|
+
#
|
|
88
|
+
# db.commit do |c|
|
|
89
|
+
# c.upsert "users", [{ id: 1, name: "Charlie", active: false },
|
|
90
|
+
# { id: 2, name: "Harvey", active: true }]
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
def upsert table, *rows
|
|
94
|
+
rows = Array(rows).flatten
|
|
95
|
+
return rows if rows.empty?
|
|
96
|
+
rows.delete_if(&:nil?)
|
|
97
|
+
rows.delete_if(&:empty?)
|
|
98
|
+
@mutations += rows.map do |row|
|
|
99
|
+
Google::Spanner::V1::Mutation.new(
|
|
100
|
+
insert_or_update: Google::Spanner::V1::Mutation::Write.new(
|
|
101
|
+
table: table, columns: row.keys.map(&:to_s),
|
|
102
|
+
values: [Convert.raw_to_value(row.values).list_value]
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
rows
|
|
107
|
+
end
|
|
108
|
+
alias_method :save, :upsert
|
|
109
|
+
|
|
110
|
+
##
|
|
111
|
+
# Inserts new rows in a table. If any of the rows already exist, the
|
|
112
|
+
# write or request fails with error {Google::Cloud::AlreadyExistsError}.
|
|
113
|
+
#
|
|
114
|
+
# All changes are accumulated in memory until the block passed to
|
|
115
|
+
# {Client#commit} completes.
|
|
116
|
+
#
|
|
117
|
+
# @param [String] table The name of the table in the database to be
|
|
118
|
+
# modified.
|
|
119
|
+
# @param [Array<Hash>] rows One or more hash objects with the hash keys
|
|
120
|
+
# matching the table's columns, and the hash values matching the
|
|
121
|
+
# table's values.
|
|
122
|
+
#
|
|
123
|
+
# Ruby types are mapped to Spanner types as follows:
|
|
124
|
+
#
|
|
125
|
+
# | Spanner | Ruby | Notes |
|
|
126
|
+
# |-------------|----------------|---|
|
|
127
|
+
# | `BOOL` | `true`/`false` | |
|
|
128
|
+
# | `INT64` | `Integer` | |
|
|
129
|
+
# | `FLOAT64` | `Float` | |
|
|
130
|
+
# | `STRING` | `String` | |
|
|
131
|
+
# | `DATE` | `Date` | |
|
|
132
|
+
# | `TIMESTAMP` | `Time`, `DateTime` | |
|
|
133
|
+
# | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
|
|
134
|
+
# | `ARRAY` | `Array` | Nested arrays are not supported. |
|
|
135
|
+
#
|
|
136
|
+
# See [Data
|
|
137
|
+
# types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
|
|
138
|
+
#
|
|
139
|
+
# @example
|
|
140
|
+
# require "google/cloud/spanner"
|
|
141
|
+
#
|
|
142
|
+
# spanner = Google::Cloud::Spanner.new
|
|
143
|
+
#
|
|
144
|
+
# db = spanner.client "my-instance", "my-database"
|
|
145
|
+
#
|
|
146
|
+
# db.commit do |c|
|
|
147
|
+
# c.insert "users", [{ id: 1, name: "Charlie", active: false },
|
|
148
|
+
# { id: 2, name: "Harvey", active: true }]
|
|
149
|
+
# end
|
|
150
|
+
#
|
|
151
|
+
def insert table, *rows
|
|
152
|
+
rows = Array(rows).flatten
|
|
153
|
+
return rows if rows.empty?
|
|
154
|
+
rows.delete_if(&:nil?)
|
|
155
|
+
rows.delete_if(&:empty?)
|
|
156
|
+
@mutations += rows.map do |row|
|
|
157
|
+
Google::Spanner::V1::Mutation.new(
|
|
158
|
+
insert: Google::Spanner::V1::Mutation::Write.new(
|
|
159
|
+
table: table, columns: row.keys.map(&:to_s),
|
|
160
|
+
values: [Convert.raw_to_value(row.values).list_value]
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
rows
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
##
|
|
168
|
+
# Updates existing rows in a table. If any of the rows does not already
|
|
169
|
+
# exist, the request fails with error {Google::Cloud::NotFoundError}.
|
|
170
|
+
#
|
|
171
|
+
# All changes are accumulated in memory until the block passed to
|
|
172
|
+
# {Client#commit} completes.
|
|
173
|
+
#
|
|
174
|
+
# @param [String] table The name of the table in the database to be
|
|
175
|
+
# modified.
|
|
176
|
+
# @param [Array<Hash>] rows One or more hash objects with the hash keys
|
|
177
|
+
# matching the table's columns, and the hash values matching the
|
|
178
|
+
# table's values.
|
|
179
|
+
#
|
|
180
|
+
# Ruby types are mapped to Spanner types as follows:
|
|
181
|
+
#
|
|
182
|
+
# | Spanner | Ruby | Notes |
|
|
183
|
+
# |-------------|----------------|---|
|
|
184
|
+
# | `BOOL` | `true`/`false` | |
|
|
185
|
+
# | `INT64` | `Integer` | |
|
|
186
|
+
# | `FLOAT64` | `Float` | |
|
|
187
|
+
# | `STRING` | `String` | |
|
|
188
|
+
# | `DATE` | `Date` | |
|
|
189
|
+
# | `TIMESTAMP` | `Time`, `DateTime` | |
|
|
190
|
+
# | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
|
|
191
|
+
# | `ARRAY` | `Array` | Nested arrays are not supported. |
|
|
192
|
+
#
|
|
193
|
+
# See [Data
|
|
194
|
+
# types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
|
|
195
|
+
#
|
|
196
|
+
# @example
|
|
197
|
+
# require "google/cloud/spanner"
|
|
198
|
+
#
|
|
199
|
+
# spanner = Google::Cloud::Spanner.new
|
|
200
|
+
#
|
|
201
|
+
# db = spanner.client "my-instance", "my-database"
|
|
202
|
+
#
|
|
203
|
+
# db.commit do |c|
|
|
204
|
+
# c.update "users", [{ id: 1, name: "Charlie", active: false },
|
|
205
|
+
# { id: 2, name: "Harvey", active: true }]
|
|
206
|
+
# end
|
|
207
|
+
#
|
|
208
|
+
def update table, *rows
|
|
209
|
+
rows = Array(rows).flatten
|
|
210
|
+
return rows if rows.empty?
|
|
211
|
+
rows.delete_if(&:nil?)
|
|
212
|
+
rows.delete_if(&:empty?)
|
|
213
|
+
@mutations += rows.map do |row|
|
|
214
|
+
Google::Spanner::V1::Mutation.new(
|
|
215
|
+
update: Google::Spanner::V1::Mutation::Write.new(
|
|
216
|
+
table: table, columns: row.keys.map(&:to_s),
|
|
217
|
+
values: [Convert.raw_to_value(row.values).list_value]
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
end
|
|
221
|
+
rows
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
##
|
|
225
|
+
# Inserts or replaces rows in a table. If any of the rows already exist,
|
|
226
|
+
# it is deleted, and the column values provided are inserted instead.
|
|
227
|
+
# Unlike #upsert, this means any values not explicitly written become
|
|
228
|
+
# `NULL`.
|
|
229
|
+
#
|
|
230
|
+
# All changes are accumulated in memory until the block passed to
|
|
231
|
+
# {Client#commit} completes.
|
|
232
|
+
#
|
|
233
|
+
# @param [String] table The name of the table in the database to be
|
|
234
|
+
# modified.
|
|
235
|
+
# @param [Array<Hash>] rows One or more hash objects with the hash keys
|
|
236
|
+
# matching the table's columns, and the hash values matching the
|
|
237
|
+
# table's values.
|
|
238
|
+
#
|
|
239
|
+
# Ruby types are mapped to Spanner types as follows:
|
|
240
|
+
#
|
|
241
|
+
# | Spanner | Ruby | Notes |
|
|
242
|
+
# |-------------|----------------|---|
|
|
243
|
+
# | `BOOL` | `true`/`false` | |
|
|
244
|
+
# | `INT64` | `Integer` | |
|
|
245
|
+
# | `FLOAT64` | `Float` | |
|
|
246
|
+
# | `STRING` | `String` | |
|
|
247
|
+
# | `DATE` | `Date` | |
|
|
248
|
+
# | `TIMESTAMP` | `Time`, `DateTime` | |
|
|
249
|
+
# | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
|
|
250
|
+
# | `ARRAY` | `Array` | Nested arrays are not supported. |
|
|
251
|
+
#
|
|
252
|
+
# See [Data
|
|
253
|
+
# types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
|
|
254
|
+
#
|
|
255
|
+
# @example
|
|
256
|
+
# require "google/cloud/spanner"
|
|
257
|
+
#
|
|
258
|
+
# spanner = Google::Cloud::Spanner.new
|
|
259
|
+
#
|
|
260
|
+
# db = spanner.client "my-instance", "my-database"
|
|
261
|
+
#
|
|
262
|
+
# db.commit do |c|
|
|
263
|
+
# c.replace "users", [{ id: 1, name: "Charlie", active: false },
|
|
264
|
+
# { id: 2, name: "Harvey", active: true }]
|
|
265
|
+
# end
|
|
266
|
+
#
|
|
267
|
+
def replace table, *rows
|
|
268
|
+
rows = Array(rows).flatten
|
|
269
|
+
return rows if rows.empty?
|
|
270
|
+
rows.delete_if(&:nil?)
|
|
271
|
+
rows.delete_if(&:empty?)
|
|
272
|
+
@mutations += rows.map do |row|
|
|
273
|
+
Google::Spanner::V1::Mutation.new(
|
|
274
|
+
replace: Google::Spanner::V1::Mutation::Write.new(
|
|
275
|
+
table: table, columns: row.keys.map(&:to_s),
|
|
276
|
+
values: [Convert.raw_to_value(row.values).list_value]
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
end
|
|
280
|
+
rows
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
##
|
|
284
|
+
# Deletes rows from a table. Succeeds whether or not the specified rows
|
|
285
|
+
# were present.
|
|
286
|
+
#
|
|
287
|
+
# All changes are accumulated in memory until the block passed to
|
|
288
|
+
# {Client#commit} completes.
|
|
289
|
+
#
|
|
290
|
+
# @param [String] table The name of the table in the database to be
|
|
291
|
+
# modified.
|
|
292
|
+
# @param [Object, Array<Object>] keys A single, or list of keys or key
|
|
293
|
+
# ranges to match returned data to. Values should have exactly as many
|
|
294
|
+
# elements as there are columns in the primary key.
|
|
295
|
+
#
|
|
296
|
+
# @example
|
|
297
|
+
# require "google/cloud/spanner"
|
|
298
|
+
#
|
|
299
|
+
# spanner = Google::Cloud::Spanner.new
|
|
300
|
+
#
|
|
301
|
+
# db = spanner.client "my-instance", "my-database"
|
|
302
|
+
#
|
|
303
|
+
# db.commit do |c|
|
|
304
|
+
# c.delete "users", [1, 2, 3]
|
|
305
|
+
# end
|
|
306
|
+
#
|
|
307
|
+
def delete table, keys = []
|
|
308
|
+
@mutations += [
|
|
309
|
+
Google::Spanner::V1::Mutation.new(
|
|
310
|
+
delete: Google::Spanner::V1::Mutation::Delete.new(
|
|
311
|
+
table: table, key_set: key_set(keys))
|
|
312
|
+
)
|
|
313
|
+
]
|
|
314
|
+
keys
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# @private
|
|
318
|
+
def mutations
|
|
319
|
+
@mutations
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
protected
|
|
323
|
+
|
|
324
|
+
def key_set keys
|
|
325
|
+
return Google::Spanner::V1::KeySet.new(all: true) if keys.nil?
|
|
326
|
+
keys = [keys] unless keys.is_a? Array
|
|
327
|
+
return Google::Spanner::V1::KeySet.new(all: true) if keys.empty?
|
|
328
|
+
if keys_are_ranges? keys
|
|
329
|
+
keys = [keys] unless keys.is_a? Array
|
|
330
|
+
key_ranges = keys.map do |r|
|
|
331
|
+
Convert.to_key_range(r)
|
|
332
|
+
end
|
|
333
|
+
return Google::Spanner::V1::KeySet.new(ranges: key_ranges)
|
|
334
|
+
end
|
|
335
|
+
key_list = Array(keys).map do |i|
|
|
336
|
+
Convert.raw_to_value(Array(i)).list_value
|
|
337
|
+
end
|
|
338
|
+
Google::Spanner::V1::KeySet.new keys: key_list
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def keys_are_ranges? keys
|
|
342
|
+
keys.each do |key|
|
|
343
|
+
return true if key.is_a? ::Range
|
|
344
|
+
return true if key.is_a? Google::Cloud::Spanner::Range
|
|
345
|
+
end
|
|
346
|
+
false
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
require "time"
|
|
17
|
+
require "date"
|
|
18
|
+
require "stringio"
|
|
19
|
+
require "base64"
|
|
20
|
+
require "google/cloud/spanner/data"
|
|
21
|
+
|
|
22
|
+
module Google
|
|
23
|
+
module Cloud
|
|
24
|
+
module Spanner
|
|
25
|
+
##
|
|
26
|
+
# @private Helper module for converting Spanner values.
|
|
27
|
+
module Convert
|
|
28
|
+
# rubocop:disable all
|
|
29
|
+
|
|
30
|
+
module ClassMethods
|
|
31
|
+
def to_query_params params, types = nil
|
|
32
|
+
types ||= {}
|
|
33
|
+
formatted_params = params.map do |key, obj|
|
|
34
|
+
[String(key), raw_to_value_and_type(obj, types[key])]
|
|
35
|
+
end
|
|
36
|
+
Hash[formatted_params]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def raw_to_value_and_type obj, type = nil
|
|
40
|
+
if NilClass === obj
|
|
41
|
+
if type
|
|
42
|
+
if type.is_a?(Array) && type.count == 1
|
|
43
|
+
[Google::Protobuf::Value.new(null_value: :NULL_VALUE),
|
|
44
|
+
Google::Spanner::V1::Type.new(
|
|
45
|
+
code: :ARRAY, array_element_type:
|
|
46
|
+
Google::Spanner::V1::Type.new(code: type.first))]
|
|
47
|
+
# elsif type.is_a? Fields
|
|
48
|
+
else
|
|
49
|
+
[Google::Protobuf::Value.new(null_value: :NULL_VALUE),
|
|
50
|
+
Google::Spanner::V1::Type.new(code: type)]
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
raise ArgumentError, "Must provide type for nil values."
|
|
54
|
+
end
|
|
55
|
+
elsif String === obj
|
|
56
|
+
[raw_to_value(obj), Google::Spanner::V1::Type.new(code: :STRING)]
|
|
57
|
+
elsif Symbol === obj
|
|
58
|
+
[raw_to_value(obj.to_s),
|
|
59
|
+
Google::Spanner::V1::Type.new(code: :STRING)]
|
|
60
|
+
elsif TrueClass === obj
|
|
61
|
+
[raw_to_value(obj), Google::Spanner::V1::Type.new(code: :BOOL)]
|
|
62
|
+
elsif FalseClass === obj
|
|
63
|
+
[raw_to_value(obj), Google::Spanner::V1::Type.new(code: :BOOL)]
|
|
64
|
+
elsif Integer === obj
|
|
65
|
+
[raw_to_value(obj.to_s),
|
|
66
|
+
Google::Spanner::V1::Type.new(code: :INT64)]
|
|
67
|
+
elsif Numeric === obj # Any number not an integer gets to be a float
|
|
68
|
+
[raw_to_value(obj),
|
|
69
|
+
Google::Spanner::V1::Type.new(code: :FLOAT64)]
|
|
70
|
+
elsif Time === obj || DateTime === obj
|
|
71
|
+
[raw_to_value(obj),
|
|
72
|
+
Google::Spanner::V1::Type.new(code: :TIMESTAMP)]
|
|
73
|
+
elsif Date === obj
|
|
74
|
+
[raw_to_value(obj.to_s),
|
|
75
|
+
Google::Spanner::V1::Type.new(code: :DATE)]
|
|
76
|
+
elsif Array === obj
|
|
77
|
+
if type && !type.is_a?(Array)
|
|
78
|
+
raise ArgumentError, "Array values must have an Array type."
|
|
79
|
+
end
|
|
80
|
+
type ||= begin
|
|
81
|
+
# Find the param type for the first non-nil item the list
|
|
82
|
+
nested_param_value = obj.detect { |x| !x.nil? }
|
|
83
|
+
if nested_param_value.nil?
|
|
84
|
+
raise ArgumentError, "Array values must have an Array type."
|
|
85
|
+
end
|
|
86
|
+
[raw_to_value_and_type(nested_param_value).last.code]
|
|
87
|
+
end
|
|
88
|
+
[raw_to_value(obj),
|
|
89
|
+
Google::Spanner::V1::Type.new(
|
|
90
|
+
code: :ARRAY, array_element_type:
|
|
91
|
+
Google::Spanner::V1::Type.new(code: type.first))]
|
|
92
|
+
elsif Hash === obj
|
|
93
|
+
field_pairs = obj.map do |key, value|
|
|
94
|
+
[key, raw_to_value_and_type(value).last]
|
|
95
|
+
end
|
|
96
|
+
formatted_fields = field_pairs.map do |name, param_type|
|
|
97
|
+
Google::Spanner::V1::StructType::Field.new(
|
|
98
|
+
name: String(name), type: param_type
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
[raw_to_value(obj),
|
|
102
|
+
Google::Spanner::V1::Type.new(
|
|
103
|
+
code: :STRUCT,
|
|
104
|
+
struct_type: Google::Spanner::V1::StructType.new(
|
|
105
|
+
fields: formatted_fields
|
|
106
|
+
))]
|
|
107
|
+
elsif obj.respond_to?(:read) && obj.respond_to?(:rewind)
|
|
108
|
+
obj.rewind
|
|
109
|
+
[raw_to_value(obj),
|
|
110
|
+
Google::Spanner::V1::Type.new(code: :BYTES)]
|
|
111
|
+
else
|
|
112
|
+
raise ArgumentError,
|
|
113
|
+
"A parameter of type #{obj.class} is not supported."
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def raw_to_value obj
|
|
118
|
+
if NilClass === obj
|
|
119
|
+
Google::Protobuf::Value.new null_value: :NULL_VALUE
|
|
120
|
+
elsif String === obj
|
|
121
|
+
Google::Protobuf::Value.new string_value: obj
|
|
122
|
+
elsif Symbol === obj
|
|
123
|
+
Google::Protobuf::Value.new string_value: obj.to_s
|
|
124
|
+
elsif TrueClass === obj
|
|
125
|
+
Google::Protobuf::Value.new bool_value: true
|
|
126
|
+
elsif FalseClass === obj
|
|
127
|
+
Google::Protobuf::Value.new bool_value: false
|
|
128
|
+
elsif Integer === obj
|
|
129
|
+
Google::Protobuf::Value.new string_value: obj.to_s
|
|
130
|
+
elsif Numeric === obj # Any number not an integer gets to be a float
|
|
131
|
+
if obj == Float::INFINITY
|
|
132
|
+
Google::Protobuf::Value.new string_value: "Infinity"
|
|
133
|
+
elsif obj == -Float::INFINITY
|
|
134
|
+
Google::Protobuf::Value.new string_value: "-Infinity"
|
|
135
|
+
elsif obj.respond_to?(:nan?) && obj.nan?
|
|
136
|
+
Google::Protobuf::Value.new string_value: "NaN"
|
|
137
|
+
else
|
|
138
|
+
Google::Protobuf::Value.new number_value: obj.to_f
|
|
139
|
+
end
|
|
140
|
+
elsif Time === obj || DateTime === obj
|
|
141
|
+
Google::Protobuf::Value.new(string_value:
|
|
142
|
+
obj.to_time.utc.strftime("%FT%T.%NZ"))
|
|
143
|
+
elsif Date === obj
|
|
144
|
+
Google::Protobuf::Value.new string_value: obj.to_s
|
|
145
|
+
elsif Array === obj
|
|
146
|
+
Google::Protobuf::Value.new list_value:
|
|
147
|
+
Google::Protobuf::ListValue.new(values:
|
|
148
|
+
obj.map { |o| raw_to_value(o) })
|
|
149
|
+
elsif Hash === obj
|
|
150
|
+
Google::Protobuf::Value.new struct_value:
|
|
151
|
+
Google::Protobuf::Struct.new(fields:
|
|
152
|
+
Hash[obj.map { |k, v| [String(k), raw_to_value(v)] }])
|
|
153
|
+
elsif obj.respond_to?(:read) && obj.respond_to?(:rewind)
|
|
154
|
+
obj.rewind
|
|
155
|
+
content = obj.read.force_encoding("ASCII-8BIT")
|
|
156
|
+
encoded_content = Base64.strict_encode64(content)
|
|
157
|
+
Google::Protobuf::Value.new(string_value: encoded_content)
|
|
158
|
+
else
|
|
159
|
+
raise ArgumentError,
|
|
160
|
+
"A value of type #{obj.class} is not supported."
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def row_to_pairs row_types, row
|
|
165
|
+
row_types.zip(row).map do |field, value|
|
|
166
|
+
[field.name.to_sym, value_to_raw(value, field.type)]
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def row_to_raw row_types, row
|
|
171
|
+
Hash[row_to_pairs(row_types, row)]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def value_to_raw value, type
|
|
175
|
+
return nil if value.kind == :null_value
|
|
176
|
+
case type.code
|
|
177
|
+
when :BOOL
|
|
178
|
+
value.bool_value
|
|
179
|
+
when :INT64
|
|
180
|
+
Integer value.string_value
|
|
181
|
+
when :FLOAT64
|
|
182
|
+
if value.kind == :string_value
|
|
183
|
+
if value.string_value == "Infinity"
|
|
184
|
+
Float::INFINITY
|
|
185
|
+
elsif value.string_value == "-Infinity"
|
|
186
|
+
-Float::INFINITY
|
|
187
|
+
elsif value.string_value == "NaN"
|
|
188
|
+
Float::NAN
|
|
189
|
+
else
|
|
190
|
+
Float value.string_value
|
|
191
|
+
end
|
|
192
|
+
else
|
|
193
|
+
value.number_value
|
|
194
|
+
end
|
|
195
|
+
when :TIMESTAMP
|
|
196
|
+
Time.parse value.string_value
|
|
197
|
+
when :DATE
|
|
198
|
+
Date.parse value.string_value
|
|
199
|
+
when :STRING
|
|
200
|
+
value.string_value
|
|
201
|
+
when :BYTES
|
|
202
|
+
StringIO.new Base64.decode64 value.string_value
|
|
203
|
+
when :ARRAY
|
|
204
|
+
value.list_value.values.map do |v|
|
|
205
|
+
value_to_raw v, type.array_element_type
|
|
206
|
+
end
|
|
207
|
+
when :STRUCT
|
|
208
|
+
Data.from_grpc value.list_value.values, type.struct_type.fields
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
##
|
|
213
|
+
# @private Convert an Object to a Google::Protobuf::Value.
|
|
214
|
+
def object_to_value obj
|
|
215
|
+
case obj
|
|
216
|
+
when NilClass then Google::Protobuf::Value.new null_value:
|
|
217
|
+
:NULL_VALUE
|
|
218
|
+
when Numeric then Google::Protobuf::Value.new number_value: obj
|
|
219
|
+
when String then Google::Protobuf::Value.new string_value: obj
|
|
220
|
+
when TrueClass then Google::Protobuf::Value.new bool_value: true
|
|
221
|
+
when FalseClass then Google::Protobuf::Value.new bool_value: false
|
|
222
|
+
when Hash then Google::Protobuf::Value.new struct_value:
|
|
223
|
+
hash_to_struct(obj)
|
|
224
|
+
when Array then Google::Protobuf::Value.new list_value:
|
|
225
|
+
Google::Protobuf::ListValue.new(values:
|
|
226
|
+
obj.map { |o| object_to_value(o) })
|
|
227
|
+
else
|
|
228
|
+
# TODO: Could raise ArgumentError here, or convert to a string
|
|
229
|
+
Google::Protobuf::Value.new string_value: obj.to_s
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
##
|
|
234
|
+
# @private Convert a Google::Protobuf::Value to an Object.
|
|
235
|
+
def value_to_object value
|
|
236
|
+
# TODO: ArgumentError if struct is not a Google::Protobuf::Value
|
|
237
|
+
if value.kind == :null_value
|
|
238
|
+
nil
|
|
239
|
+
elsif value.kind == :number_value
|
|
240
|
+
value.number_value
|
|
241
|
+
elsif value.kind == :string_value
|
|
242
|
+
value.string_value
|
|
243
|
+
elsif value.kind == :bool_value
|
|
244
|
+
value.bool_value
|
|
245
|
+
elsif value.kind == :struct_value
|
|
246
|
+
struct_to_hash value.struct_value
|
|
247
|
+
elsif value.kind == :list_value
|
|
248
|
+
value.list_value.values.map { |v| value_to_object(v) }
|
|
249
|
+
else
|
|
250
|
+
nil # just in case
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def number_to_duration number
|
|
255
|
+
return nil if number.nil?
|
|
256
|
+
|
|
257
|
+
Google::Protobuf::Duration.new \
|
|
258
|
+
seconds: number.to_i,
|
|
259
|
+
nanos: (number.remainder(1) * 1000000000).round
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def duration_to_number duration
|
|
263
|
+
return nil if duration.nil?
|
|
264
|
+
|
|
265
|
+
return duration.seconds if duration.nanos == 0
|
|
266
|
+
|
|
267
|
+
duration.seconds + (duration.nanos / 1000000000.0)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def time_to_timestamp time
|
|
271
|
+
return nil if time.nil?
|
|
272
|
+
|
|
273
|
+
# Force the object to be a Time object.
|
|
274
|
+
time = time.to_time
|
|
275
|
+
|
|
276
|
+
Google::Protobuf::Timestamp.new \
|
|
277
|
+
seconds: time.to_i,
|
|
278
|
+
nanos: time.nsec
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def timestamp_to_time timestamp
|
|
282
|
+
return nil if timestamp.nil?
|
|
283
|
+
|
|
284
|
+
Time.at timestamp.seconds, Rational(timestamp.nanos, 1000)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def to_key_range range
|
|
288
|
+
range_opts = {
|
|
289
|
+
start_closed: raw_to_value(Array(range.begin)).list_value,
|
|
290
|
+
end_closed: raw_to_value(Array(range.end)).list_value }
|
|
291
|
+
|
|
292
|
+
if range.respond_to?(:exclude_begin?) && range.exclude_begin?
|
|
293
|
+
range_opts[:start_open] = range_opts[:start_closed]
|
|
294
|
+
range_opts.delete :start_closed
|
|
295
|
+
end
|
|
296
|
+
if range.exclude_end?
|
|
297
|
+
range_opts[:end_open] = range_opts[:end_closed]
|
|
298
|
+
range_opts.delete :end_closed
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
Google::Spanner::V1::KeyRange.new range_opts
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# rubocop:enable all
|
|
306
|
+
|
|
307
|
+
extend ClassMethods
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|