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.
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