google-cloud-spanner 1.2.0 → 1.3.1

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/google-cloud-spanner.rb +25 -0
  3. data/lib/google/cloud/spanner.rb +71 -11
  4. data/lib/google/cloud/spanner/batch_client.rb +355 -0
  5. data/lib/google/cloud/spanner/batch_snapshot.rb +588 -0
  6. data/lib/google/cloud/spanner/client.rb +27 -20
  7. data/lib/google/cloud/spanner/commit.rb +6 -5
  8. data/lib/google/cloud/spanner/convert.rb +39 -0
  9. data/lib/google/cloud/spanner/credentials.rb +7 -7
  10. data/lib/google/cloud/spanner/data.rb +5 -5
  11. data/lib/google/cloud/spanner/database.rb +13 -7
  12. data/lib/google/cloud/spanner/database/job.rb +7 -2
  13. data/lib/google/cloud/spanner/database/list.rb +1 -1
  14. data/lib/google/cloud/spanner/fields.rb +16 -12
  15. data/lib/google/cloud/spanner/instance.rb +25 -13
  16. data/lib/google/cloud/spanner/instance/config.rb +2 -2
  17. data/lib/google/cloud/spanner/instance/config/list.rb +1 -1
  18. data/lib/google/cloud/spanner/instance/job.rb +7 -2
  19. data/lib/google/cloud/spanner/instance/list.rb +1 -1
  20. data/lib/google/cloud/spanner/partition.rb +208 -0
  21. data/lib/google/cloud/spanner/pool.rb +6 -6
  22. data/lib/google/cloud/spanner/project.rb +59 -16
  23. data/lib/google/cloud/spanner/results.rb +12 -5
  24. data/lib/google/cloud/spanner/service.rb +85 -61
  25. data/lib/google/cloud/spanner/session.rb +45 -9
  26. data/lib/google/cloud/spanner/snapshot.rb +10 -2
  27. data/lib/google/cloud/spanner/status.rb +6 -5
  28. data/lib/google/cloud/spanner/transaction.rb +11 -3
  29. data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +1 -1
  30. data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +1 -1
  31. data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +1 -1
  32. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +1 -1
  33. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +1 -1
  34. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +1 -1
  35. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +1 -1
  36. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +138 -6
  37. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +1 -1
  38. data/lib/google/cloud/spanner/v1/doc/overview.rb +6 -5
  39. data/lib/google/cloud/spanner/v1/spanner_client.rb +257 -33
  40. data/lib/google/cloud/spanner/v1/spanner_client_config.json +10 -0
  41. data/lib/google/cloud/spanner/version.rb +1 -1
  42. data/lib/google/spanner/v1/spanner_pb.rb +35 -0
  43. data/lib/google/spanner/v1/spanner_services_pb.rb +20 -2
  44. metadata +12 -9
@@ -101,6 +101,11 @@ module Google
101
101
  end
102
102
 
103
103
  fields = @metadata.row_type.fields
104
+ if fields.count.zero?
105
+ @closed = true
106
+ return []
107
+ end
108
+
104
109
  values = []
105
110
  buffered_responses = []
106
111
  buffer_upper_bound = 10
@@ -204,9 +209,9 @@ module Google
204
209
 
205
210
  # @private
206
211
  def self.execute service, session_path, sql, params: nil, types: nil,
207
- transaction: nil
212
+ transaction: nil, partition_token: nil
208
213
  execute_options = { transaction: transaction, params: params,
209
- types: types }
214
+ types: types, partition_token: partition_token }
210
215
  enum = service.streaming_execute_sql session_path, sql,
211
216
  execute_options
212
217
  from_enum(enum, service).tap do |results|
@@ -218,9 +223,11 @@ module Google
218
223
 
219
224
  # @private
220
225
  def self.read service, session_path, table, columns, keys: nil,
221
- index: nil, limit: nil, transaction: nil
226
+ index: nil, limit: nil, transaction: nil,
227
+ partition_token: nil
222
228
  read_options = { keys: keys, index: index, limit: limit,
223
- transaction: transaction }
229
+ transaction: transaction,
230
+ partition_token: partition_token }
224
231
  enum = service.streaming_read_table \
225
232
  session_path, table, columns, read_options
226
233
  from_enum(enum, service).tap do |results|
@@ -267,7 +274,7 @@ module Google
267
274
  return left
268
275
  elsif left.kind == :struct_value
269
276
  # Don't worry about this yet since Spanner isn't return STRUCT
270
- fail "STRUCT not implemented yet"
277
+ raise "STRUCT not implemented yet"
271
278
  else
272
279
  raise "Can't merge #{left.kind} values"
273
280
  end
@@ -47,7 +47,8 @@ module Google
47
47
  timeout: timeout,
48
48
  client_config: client_config,
49
49
  lib_name: "gccl",
50
- lib_version: Google::Cloud::Spanner::VERSION)
50
+ lib_version: Google::Cloud::Spanner::VERSION
51
+ )
51
52
  end
52
53
  attr_accessor :mocked_service
53
54
 
@@ -59,7 +60,8 @@ module Google
59
60
  timeout: timeout,
60
61
  client_config: client_config,
61
62
  lib_name: "gccl",
62
- lib_version: Google::Cloud::Spanner::VERSION)
63
+ lib_version: Google::Cloud::Spanner::VERSION
64
+ )
63
65
  end
64
66
  attr_accessor :mocked_instances
65
67
 
@@ -71,7 +73,8 @@ module Google
71
73
  timeout: timeout,
72
74
  client_config: client_config,
73
75
  lib_name: "gccl",
74
- lib_version: Google::Cloud::Spanner::VERSION)
76
+ lib_version: Google::Cloud::Spanner::VERSION
77
+ )
75
78
  end
76
79
  attr_accessor :mocked_databases
77
80
 
@@ -110,7 +113,8 @@ module Google
110
113
 
111
114
  def update_instance instance_obj
112
115
  mask = Google::Protobuf::FieldMask.new(
113
- paths: %w(display_name node_count labels))
116
+ paths: %w[display_name node_count labels]
117
+ )
114
118
 
115
119
  execute do
116
120
  instances.update_instance instance_obj, mask
@@ -252,43 +256,70 @@ module Google
252
256
  end
253
257
 
254
258
  def streaming_execute_sql session_name, sql, transaction: nil,
255
- params: nil, types: nil, resume_token: nil
256
- input_params = nil
257
- input_param_types = nil
258
- unless params.nil?
259
- input_param_pairs = Convert.to_query_params params, types
260
- input_params = Google::Protobuf::Struct.new(
261
- fields: Hash[input_param_pairs.map { |k, v| [k, v.first] }])
262
- input_param_types = Hash[
263
- input_param_pairs.map { |k, v| [k, v.last] }]
264
- end
259
+ params: nil, types: nil, resume_token: nil,
260
+ partition_token: nil
265
261
  opts = default_options_from_session session_name
266
262
  execute do
267
263
  service.execute_streaming_sql \
268
- session_name, sql, transaction: transaction, params: input_params,
269
- param_types: input_param_types,
270
- resume_token: resume_token, options: opts
264
+ session_name, sql, transaction: transaction, params: params,
265
+ param_types: types,
266
+ resume_token: resume_token,
267
+ partition_token: partition_token, options: opts
271
268
  end
272
269
  end
273
270
 
274
271
  def streaming_read_table session_name, table_name, columns, keys: nil,
275
272
  index: nil, transaction: nil, limit: nil,
276
- resume_token: nil
277
- columns.map!(&:to_s)
273
+ resume_token: nil, partition_token: nil
278
274
  opts = default_options_from_session session_name
279
275
  execute do
280
276
  service.streaming_read \
281
- session_name, table_name, columns, key_set(keys),
277
+ session_name, table_name, columns, keys,
282
278
  transaction: transaction, index: index, limit: limit,
283
- resume_token: resume_token, options: opts
279
+ resume_token: resume_token, partition_token: partition_token,
280
+ options: opts
281
+ end
282
+ end
283
+
284
+ def partition_read session_name, table_name, columns, transaction,
285
+ keys: nil, index: nil, partition_size_bytes: nil,
286
+ max_partitions: nil
287
+ partition_opts = partition_options partition_size_bytes,
288
+ max_partitions
289
+
290
+ opts = default_options_from_session session_name
291
+
292
+ execute do
293
+ service.partition_read \
294
+ session_name, table_name, keys,
295
+ transaction: transaction, index: index, columns: columns,
296
+ partition_options: partition_opts, options: opts
297
+ end
298
+ end
299
+
300
+ def partition_query session_name, sql, transaction, params: nil,
301
+ types: nil, partition_size_bytes: nil,
302
+ max_partitions: nil
303
+ partition_opts = partition_options partition_size_bytes,
304
+ max_partitions
305
+
306
+ opts = default_options_from_session session_name
307
+
308
+ execute do
309
+ service.partition_query \
310
+ session_name, sql,
311
+ transaction: transaction,
312
+ params: params, param_types: types,
313
+ partition_options: partition_opts, options: opts
284
314
  end
285
315
  end
286
316
 
287
317
  def commit session_name, mutations = [], transaction_id: nil
288
318
  tx_opts = nil
289
319
  if transaction_id.nil?
290
- tx_opts = Google::Spanner::V1::TransactionOptions.new(read_write:
291
- Google::Spanner::V1::TransactionOptions::ReadWrite.new)
320
+ tx_opts = Google::Spanner::V1::TransactionOptions.new(
321
+ read_write: Google::Spanner::V1::TransactionOptions::ReadWrite.new
322
+ )
292
323
  end
293
324
  opts = default_options_from_session session_name
294
325
  execute do
@@ -307,8 +338,9 @@ module Google
307
338
  end
308
339
 
309
340
  def begin_transaction session_name
310
- tx_opts = Google::Spanner::V1::TransactionOptions.new(read_write:
311
- Google::Spanner::V1::TransactionOptions::ReadWrite.new)
341
+ tx_opts = Google::Spanner::V1::TransactionOptions.new(
342
+ read_write: Google::Spanner::V1::TransactionOptions::ReadWrite.new
343
+ )
312
344
  opts = default_options_from_session session_name
313
345
  execute do
314
346
  service.begin_transaction session_name, tx_opts, options: opts
@@ -317,13 +349,16 @@ module Google
317
349
 
318
350
  def create_snapshot session_name, strong: nil, timestamp: nil,
319
351
  staleness: nil
320
- tx_opts = Google::Spanner::V1::TransactionOptions.new(read_only:
321
- Google::Spanner::V1::TransactionOptions::ReadOnly.new({
322
- strong: strong,
323
- read_timestamp: Convert.time_to_timestamp(timestamp),
324
- exact_staleness: Convert.number_to_duration(staleness),
325
- return_read_timestamp: true
326
- }.delete_if { |_, v| v.nil? }))
352
+ tx_opts = Google::Spanner::V1::TransactionOptions.new(
353
+ read_only: Google::Spanner::V1::TransactionOptions::ReadOnly.new(
354
+ {
355
+ strong: strong,
356
+ read_timestamp: Convert.time_to_timestamp(timestamp),
357
+ exact_staleness: Convert.number_to_duration(staleness),
358
+ return_read_timestamp: true
359
+ }.delete_if { |_, v| v.nil? }
360
+ )
361
+ )
327
362
  opts = default_options_from_session session_name
328
363
  execute do
329
364
  service.begin_transaction session_name, tx_opts, options: opts
@@ -336,37 +371,22 @@ module Google
336
371
 
337
372
  protected
338
373
 
339
- def key_set keys
340
- return Google::Spanner::V1::KeySet.new(all: true) if keys.nil?
341
- keys = [keys] unless keys.is_a? Array
342
- return Google::Spanner::V1::KeySet.new(all: true) if keys.empty?
343
- if keys_are_ranges? keys
344
- keys = [keys] unless keys.is_a? Array
345
- key_ranges = keys.map do |r|
346
- Convert.to_key_range(r)
347
- end
348
- return Google::Spanner::V1::KeySet.new(ranges: key_ranges)
349
- end
350
- key_list = Array(keys).map do |i|
351
- Convert.raw_to_value(Array(i)).list_value
352
- end
353
- Google::Spanner::V1::KeySet.new keys: key_list
354
- end
355
-
356
- def keys_are_ranges? keys
357
- keys.each do |key|
358
- return true if key.is_a? ::Range
359
- return true if key.is_a? Google::Cloud::Spanner::Range
360
- end
361
- false
362
- end
363
-
364
374
  def default_options_from_session session_name
365
375
  default_prefix = session_name.split("/sessions/").first
366
376
  Google::Gax::CallOptions.new kwargs: \
367
377
  { "google-cloud-resource-prefix" => default_prefix }
368
378
  end
369
379
 
380
+ def partition_options partition_size_bytes, max_partitions
381
+ return nil unless partition_size_bytes || max_partitions
382
+ partition_opts = Google::Spanner::V1::PartitionOptions.new
383
+ if partition_size_bytes
384
+ partition_opts.partition_size_bytes = partition_size_bytes
385
+ end
386
+ partition_opts.max_partitions = max_partitions if max_partitions
387
+ partition_opts
388
+ end
389
+
370
390
  def project_path
371
391
  Admin::Instance::V1::InstanceAdminClient.project_path project
372
392
  end
@@ -374,23 +394,27 @@ module Google
374
394
  def instance_path name
375
395
  return name if name.to_s.include? "/"
376
396
  Admin::Instance::V1::InstanceAdminClient.instance_path(
377
- project, name)
397
+ project, name
398
+ )
378
399
  end
379
400
 
380
401
  def instance_config_path name
381
402
  return name if name.to_s.include? "/"
382
403
  Admin::Instance::V1::InstanceAdminClient.instance_config_path(
383
- project, name.to_s)
404
+ project, name.to_s
405
+ )
384
406
  end
385
407
 
386
408
  def database_path instance_id, database_id
387
409
  Admin::Database::V1::DatabaseAdminClient.database_path(
388
- project, instance_id, database_id)
410
+ project, instance_id, database_id
411
+ )
389
412
  end
390
413
 
391
414
  def session_path instance_id, database_id, session_id
392
415
  V1::SpannerClient.session_path(
393
- project, instance_id, database_id, session_id)
416
+ project, instance_id, database_id, session_id
417
+ )
394
418
  end
395
419
 
396
420
  def execute
@@ -180,15 +180,17 @@ module Google
180
180
  # puts "User #{row[:id]} is #{row[:name]}"
181
181
  # end
182
182
  #
183
- def execute sql, params: nil, types: nil, transaction: nil
183
+ def execute sql, params: nil, types: nil, transaction: nil,
184
+ partition_token: nil
184
185
  ensure_service!
186
+
185
187
  results = Results.execute service, path, sql,
186
188
  params: params, types: types,
187
- transaction: transaction
189
+ transaction: transaction,
190
+ partition_token: partition_token
188
191
  @last_updated_at = Time.now
189
192
  results
190
193
  end
191
- alias_method :query, :execute
192
194
 
193
195
  ##
194
196
  # Read rows from a database table, as a simple alternative to
@@ -227,12 +229,44 @@ module Google
227
229
  # end
228
230
  #
229
231
  def read table, columns, keys: nil, index: nil, limit: nil,
230
- transaction: nil
232
+ transaction: nil, partition_token: nil
231
233
  ensure_service!
234
+
232
235
  results = Results.read service, path, table, columns,
233
236
  keys: keys, index: index, limit: limit,
234
- transaction: transaction
237
+ transaction: transaction,
238
+ partition_token: partition_token
239
+ @last_updated_at = Time.now
240
+ results
241
+ end
242
+
243
+ def partition_query sql, transaction, params: nil, types: nil,
244
+ partition_size_bytes: nil, max_partitions: nil
245
+ ensure_service!
246
+
247
+ results = service.partition_query \
248
+ path, sql, transaction, params: params, types: types,
249
+ partition_size_bytes: partition_size_bytes,
250
+ max_partitions: max_partitions
251
+
235
252
  @last_updated_at = Time.now
253
+
254
+ results
255
+ end
256
+
257
+ def partition_read table, columns, transaction, keys: nil,
258
+ index: nil, partition_size_bytes: nil,
259
+ max_partitions: nil
260
+ ensure_service!
261
+
262
+ results = service.partition_read \
263
+ path, table, columns, transaction,
264
+ keys: keys, index: index,
265
+ partition_size_bytes: partition_size_bytes,
266
+ max_partitions: max_partitions
267
+
268
+ @last_updated_at = Time.now
269
+
236
270
  results
237
271
  end
238
272
 
@@ -313,7 +347,7 @@ module Google
313
347
  c.upsert table, rows
314
348
  end
315
349
  end
316
- alias_method :save, :upsert
350
+ alias save upsert
317
351
 
318
352
  ##
319
353
  # Inserts new rows in a table. If any of the rows already exist, the
@@ -503,7 +537,8 @@ module Google
503
537
  rescue Google::Cloud::NotFoundError
504
538
  @grpc = service.create_session \
505
539
  Admin::Database::V1::DatabaseAdminClient.database_path(
506
- project_id, instance_id, database_id)
540
+ project_id, instance_id, database_id
541
+ )
507
542
  @last_updated_at = Time.now
508
543
  return self
509
544
  end
@@ -518,7 +553,8 @@ module Google
518
553
  rescue Google::Cloud::NotFoundError
519
554
  @grpc = service.create_session \
520
555
  Admin::Database::V1::DatabaseAdminClient.database_path(
521
- project_id, instance_id, database_id)
556
+ project_id, instance_id, database_id
557
+ )
522
558
  return false
523
559
  end
524
560
 
@@ -557,7 +593,7 @@ module Google
557
593
  # @private Raise an error unless an active connection to the service is
558
594
  # available.
559
595
  def ensure_service!
560
- fail "Must have active connection to service" unless service
596
+ raise "Must have active connection to service" unless service
561
597
  end
562
598
  end
563
599
  end
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
+ require "google/cloud/spanner/convert"
16
17
  require "google/cloud/spanner/results"
17
18
 
18
19
  module Google
@@ -150,10 +151,13 @@ module Google
150
151
  #
151
152
  def execute sql, params: nil, types: nil
152
153
  ensure_session!
154
+
155
+ params, types = Convert.to_input_params_and_types params, types
156
+
153
157
  session.execute sql, params: params, types: types,
154
158
  transaction: tx_selector
155
159
  end
156
- alias_method :query, :execute
160
+ alias query execute
157
161
 
158
162
  ##
159
163
  # Read rows from a database table, as a simple alternative to
@@ -191,6 +195,10 @@ module Google
191
195
  #
192
196
  def read table, columns, keys: nil, index: nil, limit: nil
193
197
  ensure_session!
198
+
199
+ columns = Array(columns).map(&:to_s)
200
+ keys = Convert.to_key_set keys
201
+
194
202
  session.read table, columns, keys: keys, index: index, limit: limit,
195
203
  transaction: tx_selector
196
204
  end
@@ -252,7 +260,7 @@ module Google
252
260
  # @private Raise an error unless an active connection to the service is
253
261
  # available.
254
262
  def ensure_session!
255
- fail "Must have active connection to service" unless session
263
+ raise "Must have active connection to service" unless session
256
264
  end
257
265
  end
258
266
  end
@@ -42,10 +42,11 @@ module Google
42
42
  #
43
43
  # job = spanner.create_database "my-instance",
44
44
  # "my-new-database"
45
+ # job.wait_until_done!
45
46
  #
46
- # job.error? # true
47
- #
48
- # status = job.error
47
+ # if job.error?
48
+ # status = job.error
49
+ # end
49
50
  #
50
51
  class Status
51
52
  attr_reader :code, :description, :message, :details
@@ -67,12 +68,12 @@ module Google
67
68
 
68
69
  # @private Get a descriptive symbol for a google.rpc.Code integer
69
70
  def self.description_for code
70
- descriptions = %w(
71
+ descriptions = %w[
71
72
  OK CANCELLED UNKNOWN INVALID_ARGUMENT DEADLINE_EXCEEDED NOT_FOUND
72
73
  ALREADY_EXISTS PERMISSION_DENIED RESOURCE_EXHAUSTED
73
74
  FAILED_PRECONDITION ABORTED OUT_OF_RANGE UNIMPLEMENTED INTERNAL
74
75
  UNAVAILABLE DATA_LOSS UNAUTHENTICATED
75
- )
76
+ ]
76
77
  descriptions[code]
77
78
  end
78
79
  end