google-cloud-spanner 2.3.0 → 2.8.0

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