google-cloud-spanner 2.4.0 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
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