google-cloud-spanner 2.3.0 → 2.8.0

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/CHANGELOG.md +64 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/LOGGING.md +1 -1
  5. data/OVERVIEW.md +1 -1
  6. data/lib/google-cloud-spanner.rb +2 -0
  7. data/lib/google/cloud/spanner/backup.rb +75 -4
  8. data/lib/google/cloud/spanner/backup/job.rb +8 -8
  9. data/lib/google/cloud/spanner/backup/job/list.rb +2 -2
  10. data/lib/google/cloud/spanner/backup/list.rb +2 -2
  11. data/lib/google/cloud/spanner/batch_snapshot.rb +11 -4
  12. data/lib/google/cloud/spanner/batch_update.rb +3 -1
  13. data/lib/google/cloud/spanner/client.rb +315 -83
  14. data/lib/google/cloud/spanner/commit.rb +4 -0
  15. data/lib/google/cloud/spanner/data.rb +4 -5
  16. data/lib/google/cloud/spanner/database.rb +111 -3
  17. data/lib/google/cloud/spanner/database/backup_info.rb +12 -3
  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 +44 -24
  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
@@ -85,6 +85,18 @@ module Google
85
85
  @grpc.name.split("/")[5]
86
86
  end
87
87
 
88
+ # The version retention period for a database.
89
+ # @return [String]
90
+ def version_retention_period
91
+ @grpc.version_retention_period
92
+ end
93
+
94
+ # The earliest available version time for a database.
95
+ # @return [Time]
96
+ def earliest_version_time
97
+ Convert.timestamp_to_time @grpc.earliest_version_time
98
+ end
99
+
88
100
  ##
89
101
  # The full path for the database resource. Values are of the form
90
102
  # `projects/<project_id>/instances/<instance_id>/databases/<database_id>`.
@@ -101,6 +113,38 @@ module Google
101
113
  @grpc.state
102
114
  end
103
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
+
104
148
  ##
105
149
  # The database is still being created. Operations on the database may
106
150
  # raise with `FAILED_PRECONDITION` in this state.
@@ -400,6 +444,31 @@ module Google
400
444
  # 366 days from the time the request is received. Required.
401
445
  # Once the `expire_time` has passed, Cloud Spanner will delete the
402
446
  # backup and free the resources used by the backup. Required.
447
+ # @param [Time] version_time Specifies the time to have an externally
448
+ # consistent copy of the database. If no version time is specified,
449
+ # it will be automatically set to the backup create time. The version
450
+ # time can be as far in the past as specified by the database earliest
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
+ #
403
472
  # @return [Google::Cloud::Spanner::Backup::Job] The job representing
404
473
  # the long-running, asynchronous processing of a backup create
405
474
  # operation.
@@ -410,7 +479,36 @@ module Google
410
479
  # spanner = Google::Cloud::Spanner.new
411
480
  # database = spanner.database "my-instance", "my-database"
412
481
  #
413
- # job = database.create_backup "my-backup", Time.now + 36000
482
+ # backup_id = "my-backup"
483
+ # expire_time = Time.now + (24 * 60 * 60) # 1 day from now
484
+ # version_time = Time.now - (24 * 60 * 60) # 1 day ago (optional)
485
+ #
486
+ # job = database.create_backup backup_id, expire_time, version_time: version_time
487
+ #
488
+ # job.done? #=> false
489
+ # job.reload! # API call
490
+ # job.done? #=> true
491
+ #
492
+ # if job.error?
493
+ # status = job.error
494
+ # else
495
+ # backup = job.backup
496
+ # end
497
+ #
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
414
512
  #
415
513
  # job.done? #=> false
416
514
  # job.reload! # API call
@@ -422,13 +520,23 @@ module Google
422
520
  # backup = job.backup
423
521
  # end
424
522
  #
425
- def create_backup backup_id, expire_time
523
+ def create_backup backup_id, expire_time,
524
+ version_time: nil, encryption_config: nil
426
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
+
427
533
  grpc = service.create_backup \
428
534
  instance_id,
429
535
  database_id,
430
536
  backup_id,
431
- expire_time
537
+ expire_time,
538
+ version_time,
539
+ encryption_config: encryption_config
432
540
  Backup::Job.from_grpc grpc, service
433
541
  end
434
542
 
@@ -84,14 +84,23 @@ module Google
84
84
  end
85
85
 
86
86
  ##
87
- # The backup contains an externally consistent copy of
88
- # `source_database` at the timestamp specified by `create_time`.
89
- # received.
87
+ # The timestamp indicating the creation of the backup.
90
88
  # @return [Time]
91
89
  def create_time
92
90
  Convert.timestamp_to_time @grpc.create_time
93
91
  end
94
92
 
93
+ ##
94
+ # The backup contains an externally consistent copy of
95
+ # `source_database` at the timestamp specified by
96
+ # the `version_time` received. If no `version_time` was
97
+ # given during the creation of the backup, the `version_time`
98
+ # will be the same as the `create_time`.
99
+ # @return [Time]
100
+ def version_time
101
+ Convert.timestamp_to_time @grpc.version_time
102
+ end
103
+
95
104
  ##
96
105
  # @private Creates a new Database::BackupInfo instance from a
97
106
  # `Google::Cloud::Spanner::Admin::Database::V1::BackupInfo`.
@@ -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