google-cloud-spanner 2.4.0 → 2.8.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHENTICATION.md +2 -1
  3. data/CHANGELOG.md +56 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/LOGGING.md +1 -1
  6. data/OVERVIEW.md +1 -1
  7. data/lib/google-cloud-spanner.rb +2 -0
  8. data/lib/google/cloud/spanner/backup.rb +67 -4
  9. data/lib/google/cloud/spanner/backup/job.rb +8 -8
  10. data/lib/google/cloud/spanner/backup/job/list.rb +2 -2
  11. data/lib/google/cloud/spanner/backup/list.rb +2 -2
  12. data/lib/google/cloud/spanner/batch_snapshot.rb +11 -4
  13. data/lib/google/cloud/spanner/batch_update.rb +3 -1
  14. data/lib/google/cloud/spanner/client.rb +315 -83
  15. data/lib/google/cloud/spanner/commit.rb +4 -0
  16. data/lib/google/cloud/spanner/data.rb +4 -5
  17. data/lib/google/cloud/spanner/database.rb +88 -2
  18. data/lib/google/cloud/spanner/database/job/list.rb +2 -2
  19. data/lib/google/cloud/spanner/database/list.rb +4 -4
  20. data/lib/google/cloud/spanner/fields.rb +5 -3
  21. data/lib/google/cloud/spanner/instance.rb +91 -3
  22. data/lib/google/cloud/spanner/instance/config/list.rb +4 -4
  23. data/lib/google/cloud/spanner/instance/list.rb +4 -4
  24. data/lib/google/cloud/spanner/partition.rb +4 -2
  25. data/lib/google/cloud/spanner/policy.rb +3 -2
  26. data/lib/google/cloud/spanner/pool.rb +10 -10
  27. data/lib/google/cloud/spanner/project.rb +65 -5
  28. data/lib/google/cloud/spanner/results.rb +13 -9
  29. data/lib/google/cloud/spanner/service.rb +42 -23
  30. data/lib/google/cloud/spanner/session.rb +38 -15
  31. data/lib/google/cloud/spanner/snapshot.rb +10 -2
  32. data/lib/google/cloud/spanner/status.rb +4 -1
  33. data/lib/google/cloud/spanner/transaction.rb +61 -6
  34. data/lib/google/cloud/spanner/version.rb +1 -1
  35. metadata +10 -10
@@ -69,6 +69,7 @@ module Google
69
69
  # | `BOOL` | `true`/`false` | |
70
70
  # | `INT64` | `Integer` | |
71
71
  # | `FLOAT64` | `Float` | |
72
+ # | `NUMERIC` | `BigDecimal` | |
72
73
  # | `STRING` | `String` | |
73
74
  # | `DATE` | `Date` | |
74
75
  # | `TIMESTAMP` | `Time`, `DateTime` | |
@@ -127,6 +128,7 @@ module Google
127
128
  # | `BOOL` | `true`/`false` | |
128
129
  # | `INT64` | `Integer` | |
129
130
  # | `FLOAT64` | `Float` | |
131
+ # | `NUMERIC` | `BigDecimal` | |
130
132
  # | `STRING` | `String` | |
131
133
  # | `DATE` | `Date` | |
132
134
  # | `TIMESTAMP` | `Time`, `DateTime` | |
@@ -184,6 +186,7 @@ module Google
184
186
  # | `BOOL` | `true`/`false` | |
185
187
  # | `INT64` | `Integer` | |
186
188
  # | `FLOAT64` | `Float` | |
189
+ # | `NUMERIC` | `BigDecimal` | |
187
190
  # | `STRING` | `String` | |
188
191
  # | `DATE` | `Date` | |
189
192
  # | `TIMESTAMP` | `Time`, `DateTime` | |
@@ -243,6 +246,7 @@ module Google
243
246
  # | `BOOL` | `true`/`false` | |
244
247
  # | `INT64` | `Integer` | |
245
248
  # | `FLOAT64` | `Float` | |
249
+ # | `NUMERIC` | `BigDecimal` | |
246
250
  # | `STRING` | `String` | |
247
251
  # | `DATE` | `Date` | |
248
252
  # | `TIMESTAMP` | `Time`, `DateTime` | |
@@ -139,9 +139,10 @@ module Google
139
139
  #
140
140
  def to_a skip_dup_check: nil
141
141
  values.map do |value|
142
- if value.is_a? Data
142
+ case value
143
+ when Data
143
144
  value.to_h skip_dup_check: skip_dup_check
144
- elsif value.is_a? Array
145
+ when Array
145
146
  value.map do |v|
146
147
  v.is_a?(Data) ? v.to_h(skip_dup_check: skip_dup_check) : v
147
148
  end
@@ -169,9 +170,7 @@ module Google
169
170
  # or indexes and corresponding values.
170
171
  #
171
172
  def to_h skip_dup_check: nil
172
- unless skip_dup_check
173
- raise DuplicateNameError if fields.duplicate_names?
174
- end
173
+ raise DuplicateNameError if !skip_dup_check && fields.duplicate_names?
175
174
 
176
175
  Hash[keys.zip to_a(skip_dup_check: skip_dup_check)]
177
176
  end
@@ -113,6 +113,38 @@ module Google
113
113
  @grpc.state
114
114
  end
115
115
 
116
+ ##
117
+ # Time at which the database creation started.
118
+ # @return [Time]
119
+ def create_time
120
+ Convert.timestamp_to_time @grpc.create_time
121
+ end
122
+
123
+ # An encryption configuration describing the encryption type and key
124
+ # resources in Cloud KMS.
125
+ #
126
+ # @return [Google::Cloud::Spanner::Admin::Database::V1::EncryptionConfig, nil]
127
+ def encryption_config
128
+ @grpc.encryption_config
129
+ end
130
+
131
+ # Encryption information for the database.
132
+ #
133
+ # For databases that are using customer managed encryption, this
134
+ # field contains the encryption information for the database, such as
135
+ # encryption state and the Cloud KMS key versions that are in use.
136
+ #
137
+ # For databases that are using Google default or other types of encryption,
138
+ # this field is empty.
139
+ #
140
+ # This field is propagated lazily from the backend. There might be a delay
141
+ # from when a key version is being used and when it appears in this field.
142
+ #
143
+ # @return [Array<Google::Cloud::Spanner::Admin::Database::V1::EncryptionInfo>]
144
+ def encryption_info
145
+ @grpc.encryption_info.to_a
146
+ end
147
+
116
148
  ##
117
149
  # The database is still being created. Operations on the database may
118
150
  # raise with `FAILED_PRECONDITION` in this state.
@@ -417,6 +449,26 @@ module Google
417
449
  # it will be automatically set to the backup create time. The version
418
450
  # time can be as far in the past as specified by the database earliest
419
451
  # version time. Optional.
452
+ # @param [Hash] encryption_config An encryption configuration describing
453
+ # the encryption type and key resources in Cloud KMS. Optional. The
454
+ # following settings can be provided:
455
+ #
456
+ # * `:kms_key_name` (String) The name of KMS key to use which should
457
+ # be the full path, e.g., `projects/<project>/locations/<location>\
458
+ # /keyRings/<key_ring>/cryptoKeys/<kms_key_name>`
459
+ # This field should be set only when encryption type
460
+ # `:CUSTOMER_MANAGED_ENCRYPTION`.
461
+ # * `:encryption_type` (Symbol) The encryption type of the backup.
462
+ # Valid values are:
463
+ # 1. `:USE_DATABASE_ENCRYPTION` - Use the same encryption configuration as
464
+ # the database.
465
+ # 2. `:GOOGLE_DEFAULT_ENCRYPTION` - Google default encryption.
466
+ # 3. `:CUSTOMER_MANAGED_ENCRYPTION` - Use customer managed encryption.
467
+ # If specified, `:kms_key_name` must contain a valid Cloud KMS key.
468
+ #
469
+ # @raise [ArgumentError] if `:CUSTOMER_MANAGED_ENCRYPTION` specified without
470
+ # customer managed kms key.
471
+ #
420
472
  # @return [Google::Cloud::Spanner::Backup::Job] The job representing
421
473
  # the long-running, asynchronous processing of a backup create
422
474
  # operation.
@@ -443,14 +495,48 @@ module Google
443
495
  # backup = job.backup
444
496
  # end
445
497
  #
446
- def create_backup backup_id, expire_time, version_time: nil
498
+ # @example Create backup with encryption config
499
+ # require "google/cloud/spanner"
500
+ #
501
+ # spanner = Google::Cloud::Spanner.new
502
+ # database = spanner.database "my-instance", "my-database"
503
+ #
504
+ # kms_key_name = "projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>"
505
+ # encryption_config = {
506
+ # kms_key_name: kms_key_name,
507
+ # encryption_type: :CUSTOMER_MANAGED_ENCRYPTION
508
+ # }
509
+ # job = database.create_backup "my-backup",
510
+ # Time.now + 36000,
511
+ # encryption_config: encryption_config
512
+ #
513
+ # job.done? #=> false
514
+ # job.reload! # API call
515
+ # job.done? #=> true
516
+ #
517
+ # if job.error?
518
+ # status = job.error
519
+ # else
520
+ # backup = job.backup
521
+ # end
522
+ #
523
+ def create_backup backup_id, expire_time,
524
+ version_time: nil, encryption_config: nil
447
525
  ensure_service!
526
+
527
+ if encryption_config&.include?(:kms_key_name) &&
528
+ encryption_config[:encryption_type] != :CUSTOMER_MANAGED_ENCRYPTION
529
+ raise Google::Cloud::InvalidArgumentError,
530
+ "kms_key_name only used with CUSTOMER_MANAGED_ENCRYPTION"
531
+ end
532
+
448
533
  grpc = service.create_backup \
449
534
  instance_id,
450
535
  database_id,
451
536
  backup_id,
452
537
  expire_time,
453
- version_time
538
+ version_time,
539
+ encryption_config: encryption_config
454
540
  Backup::Job.from_grpc grpc, service
455
541
  end
456
542
 
@@ -130,12 +130,12 @@ module Google
130
130
  # job.database.database_id
131
131
  # end
132
132
  #
133
- def all
133
+ def all &block
134
134
  return enum_for :all unless block_given?
135
135
 
136
136
  results = self
137
137
  loop do
138
- results.each { |r| yield r }
138
+ results.each(&block)
139
139
  break unless next?
140
140
  grpc.next_page
141
141
  results = self.class.from_grpc grpc, service
@@ -71,7 +71,7 @@ module Google
71
71
  return nil unless next?
72
72
  ensure_service!
73
73
  options = { token: token, max: @max }
74
- grpc = @service.list_databases @instance_id, options
74
+ grpc = @service.list_databases @instance_id, **options
75
75
  self.class.from_grpc grpc, @service, @max
76
76
  end
77
77
 
@@ -123,17 +123,17 @@ module Google
123
123
  # puts database.database_id
124
124
  # end
125
125
  #
126
- def all request_limit: nil
126
+ def all request_limit: nil, &block
127
127
  request_limit = request_limit.to_i if request_limit
128
128
  unless block_given?
129
129
  return enum_for :all, request_limit: request_limit
130
130
  end
131
131
  results = self
132
132
  loop do
133
- results.each { |r| yield r }
133
+ results.each(&block)
134
134
  if request_limit
135
135
  request_limit -= 1
136
- break if request_limit < 0
136
+ break if request_limit.negative?
137
137
  end
138
138
  break unless results.next?
139
139
  results = results.next
@@ -57,12 +57,13 @@ module Google
57
57
  # Hash values must contain the type value. If a Hash is used the
58
58
  # fields will be created using the same order as the Hash keys.
59
59
  #
60
- # Supported type values incude:
60
+ # Supported type values include:
61
61
  #
62
62
  # * `:BOOL`
63
63
  # * `:BYTES`
64
64
  # * `:DATE`
65
65
  # * `:FLOAT64`
66
+ # * `:NUMERIC`
66
67
  # * `:INT64`
67
68
  # * `:STRING`
68
69
  # * `:TIMESTAMP`
@@ -133,13 +134,14 @@ module Google
133
134
  #
134
135
  def types
135
136
  @grpc_fields.map(&:type).map do |type|
136
- if type.code == :ARRAY
137
+ case type.code
138
+ when :ARRAY
137
139
  if type.array_element_type.code == :STRUCT
138
140
  [Fields.from_grpc(type.array_element_type.struct_type.fields)]
139
141
  else
140
142
  [type.array_element_type.code]
141
143
  end
142
- elsif type.code == :STRUCT
144
+ when :STRUCT
143
145
  Fields.from_grpc type.struct_type.fields
144
146
  else
145
147
  type.code
@@ -68,6 +68,7 @@ module Google
68
68
  def initialize grpc, service
69
69
  @grpc = grpc
70
70
  @service = service
71
+ @current_values = grpc.to_h
71
72
  end
72
73
 
73
74
  # The unique identifier for the project.
@@ -134,6 +135,23 @@ module Google
134
135
  end
135
136
  alias node_count= nodes=
136
137
 
138
+ ##
139
+ # The number of processing units allocated to this instance.
140
+ #
141
+ # @return [Integer]
142
+ def processing_units
143
+ @grpc.processing_units
144
+ end
145
+
146
+ ##
147
+ # Updates number of processing units allocated to this instance.
148
+ #
149
+ # @param units [Integer] The number of processing units allocated
150
+ # to this instance.
151
+ def processing_units= units
152
+ @grpc.processing_units = units
153
+ end
154
+
137
155
  ##
138
156
  # The current instance state. Possible values are `:CREATING` and
139
157
  # `:READY`.
@@ -188,8 +206,50 @@ module Google
188
206
  )
189
207
  end
190
208
 
209
+ ##
210
+ # Update changes.
211
+ # `display_name`, `labels`, `nodes`, `processing_units` can be
212
+ # updated. `processing_units` and `nodes` can be used interchangeably
213
+ # to update.
214
+ #
215
+ # @return [Instance::Job] The job representing the long-running,
216
+ # asynchronous processing of an instance update operation.
217
+ # @raise [ArgumentError] if both processing_units or nodes are specified.
218
+ #
219
+ # @example
220
+ # require "google/cloud/spanner"
221
+ #
222
+ # spanner = Google::Cloud::Spanner.new
223
+ #
224
+ # instance = spanner.instance "my-instance"
225
+ # instance.display_name = "prod-instance"
226
+ # instance.labels = { env: "prod", app: "api" }
227
+ # instance.nodes = 2
228
+ # # OR
229
+ # # instance.processing_units = 500
230
+ #
231
+ # job = instance.save
232
+ #
233
+ # job.done? #=> false
234
+ # job.reload! # API call
235
+ # job.done? #=> true
236
+ #
237
+ # if job.error?
238
+ # status = job.error
239
+ # else
240
+ # instance = job.instance
241
+ # end
242
+ #
191
243
  def save
192
- job_grpc = service.update_instance @grpc
244
+ ensure_service!
245
+
246
+ field_mask = []
247
+ @current_values.each do |field, value|
248
+ field_mask << field unless @grpc[field.to_s] == value
249
+ end
250
+
251
+ job_grpc = service.update_instance @grpc, field_mask: field_mask
252
+ @current_values = @grpc.to_h
193
253
  Instance::Job.from_grpc job_grpc, service
194
254
  end
195
255
  alias update save
@@ -306,6 +366,13 @@ module Google
306
366
  # These statements execute atomically with the creation of the
307
367
  # database: if there is an error in any statement, the database is not
308
368
  # created. Optional.
369
+ # @param [Hash] encryption_config An encryption configuration describing
370
+ # the encryption type and key resources in Cloud KMS. Optional. The
371
+ # following settings can be provided:
372
+ #
373
+ # * `:kms_key_name` (String) The name of KMS key to use which should
374
+ # be the full path, e.g., `projects/<project>/locations/<location>\
375
+ # /keyRings/<key_ring>/cryptoKeys/<kms_key_name>`
309
376
  #
310
377
  # @return [Database::Job] The job representing the long-running,
311
378
  # asynchronous processing of a database create operation.
@@ -328,9 +395,30 @@ module Google
328
395
  # database = job.database
329
396
  # end
330
397
  #
331
- def create_database database_id, statements: []
398
+ # @example Create with encryption config
399
+ # require "google/cloud/spanner"
400
+ #
401
+ # spanner = Google::Cloud::Spanner.new
402
+ #
403
+ # instance = spanner.instance "my-instance"
404
+ #
405
+ # kms_key_name = "projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>"
406
+ # job = instance.create_database "my-new-database", encryption_config: { kms_key_name: kms_key_name }
407
+ #
408
+ # job.done? #=> false
409
+ # job.reload! # API call
410
+ # job.done? #=> true
411
+ #
412
+ # if job.error?
413
+ # status = job.error
414
+ # else
415
+ # database = job.database
416
+ # end
417
+ #
418
+ def create_database database_id, statements: [], encryption_config: nil
332
419
  grpc = service.create_database instance_id, database_id,
333
- statements: statements
420
+ statements: statements,
421
+ encryption_config: encryption_config
334
422
  Database::Job.from_grpc grpc, service
335
423
  end
336
424
 
@@ -73,7 +73,7 @@ module Google
73
73
  return nil unless next?
74
74
  ensure_service!
75
75
  options = { token: token, max: @max }
76
- grpc = @service.list_instance_configs options
76
+ grpc = @service.list_instance_configs(**options)
77
77
  self.class.from_grpc grpc, @service, @max
78
78
  end
79
79
 
@@ -123,17 +123,17 @@ module Google
123
123
  # puts config.instance_config_id
124
124
  # end
125
125
  #
126
- def all request_limit: nil
126
+ def all request_limit: nil, &block
127
127
  request_limit = request_limit.to_i if request_limit
128
128
  unless block_given?
129
129
  return enum_for :all, request_limit: request_limit
130
130
  end
131
131
  results = self
132
132
  loop do
133
- results.each { |r| yield r }
133
+ results.each(&block)
134
134
  if request_limit
135
135
  request_limit -= 1
136
- break if request_limit < 0
136
+ break if request_limit.negative?
137
137
  end
138
138
  break unless results.next?
139
139
  results = results.next
@@ -71,7 +71,7 @@ module Google
71
71
  return nil unless next?
72
72
  ensure_service!
73
73
  options = { token: token, max: @max }
74
- grpc = @service.list_instances options
74
+ grpc = @service.list_instances(**options)
75
75
  self.class.from_grpc grpc, @service, @max
76
76
  end
77
77
 
@@ -120,17 +120,17 @@ module Google
120
120
  # puts instance.instance_id
121
121
  # end
122
122
  #
123
- def all request_limit: nil
123
+ def all request_limit: nil, &block
124
124
  request_limit = request_limit.to_i if request_limit
125
125
  unless block_given?
126
126
  return enum_for :all, request_limit: request_limit
127
127
  end
128
128
  results = self
129
129
  loop do
130
- results.each { |r| yield r }
130
+ results.each(&block)
131
131
  if request_limit
132
132
  request_limit -= 1
133
- break if request_limit < 0
133
+ break if request_limit.negative?
134
134
  end
135
135
  break unless results.next?
136
136
  results = results.next
@@ -45,11 +45,13 @@ module Google
45
45
  #
46
46
  class Partition
47
47
  # @ private
48
- attr_reader :execute, :read
48
+ attr_reader :execute
49
+ attr_reader :read
49
50
 
50
51
  ##
51
52
  # @private Creates a Partition object.
52
- def initialize; end
53
+ def initialize
54
+ end
53
55
 
54
56
  ##
55
57
  # Whether the partition was created for an execute_query/query