google-cloud-spanner 1.2.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/google-cloud-spanner.rb +25 -0
- data/lib/google/cloud/spanner.rb +71 -11
- data/lib/google/cloud/spanner/batch_client.rb +355 -0
- data/lib/google/cloud/spanner/batch_snapshot.rb +588 -0
- data/lib/google/cloud/spanner/client.rb +27 -20
- data/lib/google/cloud/spanner/commit.rb +6 -5
- data/lib/google/cloud/spanner/convert.rb +39 -0
- data/lib/google/cloud/spanner/credentials.rb +7 -7
- data/lib/google/cloud/spanner/data.rb +5 -5
- data/lib/google/cloud/spanner/database.rb +13 -7
- data/lib/google/cloud/spanner/database/job.rb +7 -2
- data/lib/google/cloud/spanner/database/list.rb +1 -1
- data/lib/google/cloud/spanner/fields.rb +16 -12
- data/lib/google/cloud/spanner/instance.rb +25 -13
- data/lib/google/cloud/spanner/instance/config.rb +2 -2
- data/lib/google/cloud/spanner/instance/config/list.rb +1 -1
- data/lib/google/cloud/spanner/instance/job.rb +7 -2
- data/lib/google/cloud/spanner/instance/list.rb +1 -1
- data/lib/google/cloud/spanner/partition.rb +208 -0
- data/lib/google/cloud/spanner/pool.rb +6 -6
- data/lib/google/cloud/spanner/project.rb +59 -16
- data/lib/google/cloud/spanner/results.rb +12 -5
- data/lib/google/cloud/spanner/service.rb +85 -61
- data/lib/google/cloud/spanner/session.rb +45 -9
- data/lib/google/cloud/spanner/snapshot.rb +10 -2
- data/lib/google/cloud/spanner/status.rb +6 -5
- data/lib/google/cloud/spanner/transaction.rb +11 -3
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +138 -6
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +1 -1
- data/lib/google/cloud/spanner/v1/doc/overview.rb +6 -5
- data/lib/google/cloud/spanner/v1/spanner_client.rb +257 -33
- data/lib/google/cloud/spanner/v1/spanner_client_config.json +10 -0
- data/lib/google/cloud/spanner/version.rb +1 -1
- data/lib/google/spanner/v1/spanner_pb.rb +35 -0
- data/lib/google/spanner/v1/spanner_services_pb.rb +20 -2
- metadata +12 -9
@@ -237,6 +237,8 @@ module Google
|
|
237
237
|
validate_single_use_args! single_use
|
238
238
|
ensure_service!
|
239
239
|
|
240
|
+
params, types = Convert.to_input_params_and_types params, types
|
241
|
+
|
240
242
|
single_use_tx = single_use_transaction single_use
|
241
243
|
results = nil
|
242
244
|
@pool.with_session do |session|
|
@@ -245,7 +247,7 @@ module Google
|
|
245
247
|
end
|
246
248
|
results
|
247
249
|
end
|
248
|
-
|
250
|
+
alias query execute
|
249
251
|
|
250
252
|
##
|
251
253
|
# Read rows from a database table, as a simple alternative to
|
@@ -349,6 +351,9 @@ module Google
|
|
349
351
|
validate_single_use_args! single_use
|
350
352
|
ensure_service!
|
351
353
|
|
354
|
+
columns = Array(columns).map(&:to_s)
|
355
|
+
keys = Convert.to_key_set keys
|
356
|
+
|
352
357
|
single_use_tx = single_use_transaction single_use
|
353
358
|
results = nil
|
354
359
|
@pool.with_session do |session|
|
@@ -414,7 +419,7 @@ module Google
|
|
414
419
|
session.upsert table, rows
|
415
420
|
end
|
416
421
|
end
|
417
|
-
|
422
|
+
alias save upsert
|
418
423
|
|
419
424
|
##
|
420
425
|
# Inserts new rows in a table. If any of the rows already exist, the
|
@@ -654,7 +659,7 @@ module Google
|
|
654
659
|
# end
|
655
660
|
#
|
656
661
|
def commit &block
|
657
|
-
|
662
|
+
raise ArgumentError, "Must provide a block" unless block_given?
|
658
663
|
|
659
664
|
@pool.with_session do |session|
|
660
665
|
session.commit(&block)
|
@@ -721,10 +726,10 @@ module Google
|
|
721
726
|
# end
|
722
727
|
# end
|
723
728
|
#
|
724
|
-
def transaction deadline: 120
|
729
|
+
def transaction deadline: 120
|
725
730
|
ensure_service!
|
726
731
|
unless Thread.current[:transaction_id].nil?
|
727
|
-
|
732
|
+
raise "Nested transactions are not allowed"
|
728
733
|
end
|
729
734
|
|
730
735
|
deadline = validate_deadline deadline
|
@@ -734,7 +739,7 @@ module Google
|
|
734
739
|
@pool.with_transaction do |tx|
|
735
740
|
begin
|
736
741
|
Thread.current[:transaction_id] = tx.transaction_id
|
737
|
-
|
742
|
+
yield tx
|
738
743
|
commit_resp = @project.service.commit \
|
739
744
|
tx.session.path, tx.mutations, transaction_id: tx.transaction_id
|
740
745
|
return Convert.timestamp_to_time commit_resp.commit_timestamp
|
@@ -751,7 +756,7 @@ module Google
|
|
751
756
|
# Create new transaction on the session and retry the block
|
752
757
|
tx = tx.session.create_transaction
|
753
758
|
retry
|
754
|
-
rescue => err
|
759
|
+
rescue StandardError => err
|
755
760
|
# Rollback transaction when handling unexpected error
|
756
761
|
tx.session.rollback tx.transaction_id
|
757
762
|
# Return nil if raised with rollback.
|
@@ -829,7 +834,7 @@ module Google
|
|
829
834
|
|
830
835
|
ensure_service!
|
831
836
|
unless Thread.current[:transaction_id].nil?
|
832
|
-
|
837
|
+
raise "Nested snapshots are not allowed"
|
833
838
|
end
|
834
839
|
|
835
840
|
@pool.with_session do |session|
|
@@ -945,7 +950,8 @@ module Google
|
|
945
950
|
ensure_service!
|
946
951
|
grpc = @project.service.create_session \
|
947
952
|
Admin::Database::V1::DatabaseAdminClient.database_path(
|
948
|
-
project_id, instance_id, database_id
|
953
|
+
project_id, instance_id, database_id
|
954
|
+
)
|
949
955
|
Session.from_grpc(grpc, @project.service)
|
950
956
|
end
|
951
957
|
|
@@ -966,22 +972,22 @@ module Google
|
|
966
972
|
# @private Raise an error unless an active connection to the service is
|
967
973
|
# available.
|
968
974
|
def ensure_service!
|
969
|
-
|
975
|
+
raise "Must have active connection to service" unless @project.service
|
970
976
|
end
|
971
977
|
|
972
978
|
##
|
973
979
|
# Check for valid snapshot arguments
|
974
980
|
def validate_single_use_args! opts
|
975
981
|
return true if opts.nil? || opts.empty?
|
976
|
-
valid_keys = [
|
977
|
-
|
978
|
-
|
982
|
+
valid_keys = %i[strong timestamp read_timestamp staleness
|
983
|
+
exact_staleness bounded_timestamp
|
984
|
+
min_read_timestamp bounded_staleness max_staleness]
|
979
985
|
if opts.keys.count == 1 && valid_keys.include?(opts.keys.first)
|
980
986
|
return true
|
981
987
|
end
|
982
|
-
|
983
|
-
|
984
|
-
|
988
|
+
raise ArgumentError,
|
989
|
+
"Must provide only one of the following single_use values: " \
|
990
|
+
"#{valid_keys}"
|
985
991
|
end
|
986
992
|
|
987
993
|
##
|
@@ -1018,9 +1024,10 @@ module Google
|
|
1018
1024
|
valid_args_count = [strong, timestamp, read_timestamp, staleness,
|
1019
1025
|
exact_staleness].compact.count
|
1020
1026
|
return true if valid_args_count <= 1
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1027
|
+
raise ArgumentError,
|
1028
|
+
"Can only provide one of the following arguments: " \
|
1029
|
+
"(strong, timestamp, read_timestamp, staleness, " \
|
1030
|
+
"exact_staleness)"
|
1024
1031
|
end
|
1025
1032
|
|
1026
1033
|
def validate_deadline deadline
|
@@ -1050,7 +1057,7 @@ module Google
|
|
1050
1057
|
end
|
1051
1058
|
# No metadata? Try the inner error
|
1052
1059
|
delay_from_aborted(err.cause)
|
1053
|
-
rescue
|
1060
|
+
rescue StandardError
|
1054
1061
|
# Any error indicates the backoff should be handled elsewhere
|
1055
1062
|
return nil
|
1056
1063
|
end
|
@@ -105,7 +105,7 @@ module Google
|
|
105
105
|
end
|
106
106
|
rows
|
107
107
|
end
|
108
|
-
|
108
|
+
alias save upsert
|
109
109
|
|
110
110
|
##
|
111
111
|
# Inserts new rows in a table. If any of the rows already exist, the
|
@@ -308,7 +308,8 @@ module Google
|
|
308
308
|
@mutations += [
|
309
309
|
Google::Spanner::V1::Mutation.new(
|
310
310
|
delete: Google::Spanner::V1::Mutation::Delete.new(
|
311
|
-
table: table, key_set: key_set(keys)
|
311
|
+
table: table, key_set: key_set(keys)
|
312
|
+
)
|
312
313
|
)
|
313
314
|
]
|
314
315
|
keys
|
@@ -326,14 +327,14 @@ module Google
|
|
326
327
|
keys = [keys] unless keys.is_a? Array
|
327
328
|
return Google::Spanner::V1::KeySet.new(all: true) if keys.empty?
|
328
329
|
if keys_are_ranges? keys
|
329
|
-
keys = [keys] unless keys.is_a? Array
|
330
330
|
key_ranges = keys.map do |r|
|
331
331
|
Convert.to_key_range(r)
|
332
332
|
end
|
333
333
|
return Google::Spanner::V1::KeySet.new(ranges: key_ranges)
|
334
334
|
end
|
335
|
-
key_list =
|
336
|
-
|
335
|
+
key_list = keys.map do |key|
|
336
|
+
key = [key] unless key.is_a? Array
|
337
|
+
Convert.raw_to_value(key).list_value
|
337
338
|
end
|
338
339
|
Google::Spanner::V1::KeySet.new keys: key_list
|
339
340
|
end
|
@@ -258,6 +258,45 @@ module Google
|
|
258
258
|
|
259
259
|
Google::Spanner::V1::KeyRange.new range_opts
|
260
260
|
end
|
261
|
+
|
262
|
+
def to_key_set keys
|
263
|
+
return Google::Spanner::V1::KeySet.new(all: true) if keys.nil?
|
264
|
+
keys = [keys] unless keys.is_a? Array
|
265
|
+
return Google::Spanner::V1::KeySet.new(all: true) if keys.empty?
|
266
|
+
|
267
|
+
if keys_are_ranges? keys
|
268
|
+
key_ranges = keys.map { |r| to_key_range(r) }
|
269
|
+
return Google::Spanner::V1::KeySet.new(ranges: key_ranges)
|
270
|
+
end
|
271
|
+
|
272
|
+
key_list = keys.map do |key|
|
273
|
+
key = [key] unless key.is_a? Array
|
274
|
+
raw_to_value(key).list_value
|
275
|
+
end
|
276
|
+
Google::Spanner::V1::KeySet.new keys: key_list
|
277
|
+
end
|
278
|
+
|
279
|
+
def keys_are_ranges? keys
|
280
|
+
keys.each do |key|
|
281
|
+
return true if key.is_a? ::Range
|
282
|
+
return true if key.is_a? Google::Cloud::Spanner::Range
|
283
|
+
end
|
284
|
+
false
|
285
|
+
end
|
286
|
+
|
287
|
+
def to_input_params_and_types params, types
|
288
|
+
input_params = nil
|
289
|
+
input_param_types = nil
|
290
|
+
unless params.nil?
|
291
|
+
input_param_pairs = to_query_params params, types
|
292
|
+
input_params = Google::Protobuf::Struct.new \
|
293
|
+
fields: Hash[input_param_pairs.map { |k, v| [k, v.first] }]
|
294
|
+
input_param_types = Hash[
|
295
|
+
input_param_pairs.map { |k, v| [k, v.last] }]
|
296
|
+
end
|
297
|
+
|
298
|
+
[input_params, input_param_types]
|
299
|
+
end
|
261
300
|
end
|
262
301
|
|
263
302
|
# rubocop:enable all
|
@@ -38,20 +38,20 @@ module Google
|
|
38
38
|
# spanner.project_id #=> "my-project"
|
39
39
|
#
|
40
40
|
class Credentials < Google::Auth::Credentials
|
41
|
-
SCOPE = %w
|
42
|
-
https://www.googleapis.com/auth/spanner.data
|
43
|
-
PATH_ENV_VARS = %w
|
41
|
+
SCOPE = %w[https://www.googleapis.com/auth/cloud-platform
|
42
|
+
https://www.googleapis.com/auth/spanner.data].freeze
|
43
|
+
PATH_ENV_VARS = %w[SPANNER_CREDENTIALS
|
44
44
|
SPANNER_KEYFILE
|
45
45
|
GOOGLE_CLOUD_CREDENTIALS
|
46
46
|
GOOGLE_CLOUD_KEYFILE
|
47
|
-
GCLOUD_KEYFILE
|
48
|
-
JSON_ENV_VARS = %w
|
47
|
+
GCLOUD_KEYFILE].freeze
|
48
|
+
JSON_ENV_VARS = %w[SPANNER_CREDENTIALS_JSON
|
49
49
|
SPANNER_KEYFILE_JSON
|
50
50
|
GOOGLE_CLOUD_CREDENTIALS_JSON
|
51
51
|
GOOGLE_CLOUD_KEYFILE_JSON
|
52
|
-
GCLOUD_KEYFILE_JSON
|
52
|
+
GCLOUD_KEYFILE_JSON].freeze
|
53
53
|
DEFAULT_PATHS = \
|
54
|
-
["~/.config/gcloud/application_default_credentials.json"]
|
54
|
+
["~/.config/gcloud/application_default_credentials.json"].freeze
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -80,7 +80,7 @@ module Google
|
|
80
80
|
# @return [Array<Object>] An array containing the values.
|
81
81
|
#
|
82
82
|
def values
|
83
|
-
keys.count
|
83
|
+
Array.new(keys.count) { |i| self[i] }
|
84
84
|
end
|
85
85
|
|
86
86
|
##
|
@@ -113,8 +113,8 @@ module Google
|
|
113
113
|
@grpc_fields[key].type)
|
114
114
|
end
|
115
115
|
name_count = @grpc_fields.find_all { |f| f.name == String(key) }.count
|
116
|
-
return nil if name_count
|
117
|
-
|
116
|
+
return nil if name_count.zero?
|
117
|
+
raise DuplicateNameError if name_count > 1
|
118
118
|
index = @grpc_fields.find_index { |f| f.name == String(key) }
|
119
119
|
Convert.value_to_raw(@grpc_values[index], @grpc_fields[index].type)
|
120
120
|
end
|
@@ -148,7 +148,7 @@ module Google
|
|
148
148
|
# or indexes and corresponding values.
|
149
149
|
#
|
150
150
|
def to_h
|
151
|
-
|
151
|
+
raise DuplicateNameError if fields.duplicate_names?
|
152
152
|
hashified_pairs = pairs.map do |key, value|
|
153
153
|
if value.is_a? Data
|
154
154
|
[key, value.to_h]
|
@@ -171,7 +171,7 @@ module Google
|
|
171
171
|
def to_s
|
172
172
|
named_values = pairs.map do |key, value|
|
173
173
|
if key.is_a? Integer
|
174
|
-
|
174
|
+
value.inspect
|
175
175
|
else
|
176
176
|
"(#{key})#{value.inspect}"
|
177
177
|
end
|
@@ -48,7 +48,11 @@ module Google
|
|
48
48
|
# job.reload! # API call
|
49
49
|
# job.done? #=> true
|
50
50
|
#
|
51
|
-
#
|
51
|
+
# if job.error?
|
52
|
+
# status = job.error
|
53
|
+
# else
|
54
|
+
# database = job.database
|
55
|
+
# end
|
52
56
|
#
|
53
57
|
class Database
|
54
58
|
##
|
@@ -101,7 +105,7 @@ module Google
|
|
101
105
|
|
102
106
|
##
|
103
107
|
# The database is still being created. Operations on the database may
|
104
|
-
#
|
108
|
+
# raise with `FAILED_PRECONDITION` in this state.
|
105
109
|
# @return [Boolean]
|
106
110
|
def creating?
|
107
111
|
state == :CREATING
|
@@ -260,7 +264,7 @@ module Google
|
|
260
264
|
policy = Policy.from_grpc grpc
|
261
265
|
return policy unless block_given?
|
262
266
|
yield policy
|
263
|
-
|
267
|
+
update_policy policy
|
264
268
|
end
|
265
269
|
|
266
270
|
##
|
@@ -290,14 +294,15 @@ module Google
|
|
290
294
|
#
|
291
295
|
# policy.add "roles/owner", "user:owner@example.com"
|
292
296
|
#
|
293
|
-
# database.
|
297
|
+
# database.update_policy policy # API call
|
294
298
|
#
|
295
|
-
def
|
299
|
+
def update_policy new_policy
|
296
300
|
ensure_service!
|
297
301
|
grpc = service.set_database_policy \
|
298
302
|
instance_id, database_id, new_policy.to_grpc
|
299
303
|
Policy.from_grpc grpc
|
300
304
|
end
|
305
|
+
alias policy= update_policy
|
301
306
|
|
302
307
|
##
|
303
308
|
# Tests the specified permissions against the [Cloud
|
@@ -361,12 +366,13 @@ module Google
|
|
361
366
|
# @private Raise an error unless an active connection to the service is
|
362
367
|
# available.
|
363
368
|
def ensure_service!
|
364
|
-
|
369
|
+
raise "Must have active connection to service" unless service
|
365
370
|
end
|
366
371
|
|
367
372
|
def session_path instance_id, database_id, session_id
|
368
373
|
V1::SpannerClient.session_path(
|
369
|
-
project_id, instance_id, database_id, session_id
|
374
|
+
project_id, instance_id, database_id, session_id
|
375
|
+
)
|
370
376
|
end
|
371
377
|
end
|
372
378
|
end
|
@@ -41,7 +41,12 @@ module Google
|
|
41
41
|
# job.done? #=> false
|
42
42
|
# job.reload! # API call
|
43
43
|
# job.done? #=> true
|
44
|
-
#
|
44
|
+
#
|
45
|
+
# if job.error?
|
46
|
+
# status = job.error
|
47
|
+
# else
|
48
|
+
# database = job.database
|
49
|
+
# end
|
45
50
|
#
|
46
51
|
class Job
|
47
52
|
##
|
@@ -169,7 +174,7 @@ module Google
|
|
169
174
|
@grpc.reload!
|
170
175
|
self
|
171
176
|
end
|
172
|
-
|
177
|
+
alias refresh! reload!
|
173
178
|
|
174
179
|
##
|
175
180
|
# Reloads the job until the operation is complete. The delay between
|
@@ -53,7 +53,7 @@ module Google
|
|
53
53
|
@fields << field(type)
|
54
54
|
end
|
55
55
|
else
|
56
|
-
|
56
|
+
raise ArgumentError, "can only accept Array or Hash"
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -135,8 +135,8 @@ module Google
|
|
135
135
|
def [] key
|
136
136
|
return types[key] if key.is_a? Integer
|
137
137
|
name_count = @fields.find_all { |f| f.name == String(key) }.count
|
138
|
-
return nil if name_count
|
139
|
-
|
138
|
+
return nil if name_count.zero?
|
139
|
+
raise DuplicateNameError if name_count > 1
|
140
140
|
index = @fields.find_index { |f| f.name == String(key) }
|
141
141
|
types[index]
|
142
142
|
end
|
@@ -151,7 +151,8 @@ module Google
|
|
151
151
|
# @return [Array<Symbol>] An array containing the type codes.
|
152
152
|
#
|
153
153
|
def to_a
|
154
|
-
keys.count
|
154
|
+
Array.new(keys.count) do |i|
|
155
|
+
field = self[i]
|
155
156
|
if field.is_a? Fields
|
156
157
|
field.to_h
|
157
158
|
elsif field.is_a? Array
|
@@ -169,7 +170,7 @@ module Google
|
|
169
170
|
# or indexes and corresponding types.
|
170
171
|
#
|
171
172
|
def to_h
|
172
|
-
|
173
|
+
raise DuplicateNameError if duplicate_names?
|
173
174
|
hashified_pairs = pairs.map do |key, value|
|
174
175
|
if value.is_a? Fields
|
175
176
|
[key, value.to_h]
|
@@ -189,7 +190,7 @@ module Google
|
|
189
190
|
values = data.map { |datum| Convert.raw_to_value datum }
|
190
191
|
Data.from_grpc values, @fields
|
191
192
|
end
|
192
|
-
|
193
|
+
alias new data
|
193
194
|
|
194
195
|
# @private
|
195
196
|
def == other
|
@@ -201,7 +202,7 @@ module Google
|
|
201
202
|
def to_s
|
202
203
|
named_types = pairs.map do |key, type|
|
203
204
|
if key.is_a? Integer
|
204
|
-
|
205
|
+
type.inspect
|
205
206
|
else
|
206
207
|
"(#{key})#{type.inspect}"
|
207
208
|
end
|
@@ -228,22 +229,25 @@ module Google
|
|
228
229
|
def field pair
|
229
230
|
if pair.is_a?(Array)
|
230
231
|
unless pair.count == 2
|
231
|
-
|
232
|
+
raise ArgumentError, "can only accept pairs of name and type"
|
232
233
|
end
|
233
234
|
if pair.first.nil? || pair.first.is_a?(Integer)
|
234
235
|
Google::Spanner::V1::StructType::Field.new(
|
235
|
-
type: Google::Spanner::V1::Type.new(code: pair.last)
|
236
|
+
type: Google::Spanner::V1::Type.new(code: pair.last)
|
237
|
+
)
|
236
238
|
else
|
237
239
|
Google::Spanner::V1::StructType::Field.new(
|
238
240
|
name: String(pair.first),
|
239
|
-
type: Google::Spanner::V1::Type.new(code: pair.last)
|
241
|
+
type: Google::Spanner::V1::Type.new(code: pair.last)
|
242
|
+
)
|
240
243
|
end
|
241
244
|
else
|
242
245
|
unless pair.is_a?(Symbol)
|
243
|
-
|
246
|
+
raise ArgumentError, "type must be a symbol"
|
244
247
|
end
|
245
248
|
Google::Spanner::V1::StructType::Field.new(
|
246
|
-
type: Google::Spanner::V1::Type.new(code: pair)
|
249
|
+
type: Google::Spanner::V1::Type.new(code: pair)
|
250
|
+
)
|
247
251
|
end
|
248
252
|
end
|
249
253
|
end
|