google-cloud-spanner 2.26.0 → 2.28.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/lib/google/cloud/spanner/backup/job/list.rb +8 -6
- data/lib/google/cloud/spanner/backup/job.rb +11 -6
- data/lib/google/cloud/spanner/batch_client.rb +18 -5
- data/lib/google/cloud/spanner/batch_write_results.rb +2 -2
- data/lib/google/cloud/spanner/client.rb +55 -14
- data/lib/google/cloud/spanner/convert.rb +5 -0
- data/lib/google/cloud/spanner/instance.rb +14 -5
- data/lib/google/cloud/spanner/interval.rb +309 -0
- data/lib/google/cloud/spanner/partition.rb +7 -1
- data/lib/google/cloud/spanner/pool.rb +17 -2
- data/lib/google/cloud/spanner/project.rb +17 -5
- data/lib/google/cloud/spanner/results.rb +102 -30
- data/lib/google/cloud/spanner/service.rb +142 -10
- data/lib/google/cloud/spanner/session.rb +67 -33
- data/lib/google/cloud/spanner/transaction.rb +77 -31
- data/lib/google/cloud/spanner/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 850ac8a54d873ff6d92cc7d7b835abd1d0fd980d22529296b036b67d5c4ecbfe
|
|
4
|
+
data.tar.gz: 585441baf49b27942fddc9876044221965fe9edd475bdf8f6c53f463a3a75cf0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dca36d5f2479c792b0c7e10051cf52915314c76de12581d09ff8284ae8e591430d2d67f779c71b2956580e4f970054ddcac3d3f8429ed8ced759b8cd0be0339a
|
|
7
|
+
data.tar.gz: 887212c39b640de69edf12df3bca506494a7bb65c6f0b102eb80cefb3c41e8c3511b027f18334669ab58a9d1ba7114ab1d5bdc83d4982c39521ff4d542b94344
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### 2.28.0 (2025-10-22)
|
|
4
|
+
|
|
5
|
+
#### Features
|
|
6
|
+
|
|
7
|
+
* Add support for order_by and lock_hint options ([#182](https://github.com/googleapis/ruby-spanner/issues/182))
|
|
8
|
+
|
|
9
|
+
#### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* use create Transaction when retrying when enumerating rows in `Spanner::Results` ([#191](https://github.com/googleapis/ruby-spanner/issues/191))
|
|
12
|
+
* run explicit BeginTransaction in Client#transaction for mutation-only payloads ([#191](https://github.com/googleapis/ruby-spanner/issues/191))
|
|
13
|
+
* Transaction#initialize is no longer incorrectly marked as public in documentation ([#191](https://github.com/googleapis/ruby-spanner/issues/191))
|
|
14
|
+
* several field accessors in Partition are no longer incorrectly marked as public in documentation ([#191](https://github.com/googleapis/ruby-spanner/issues/191))
|
|
15
|
+
* session keepalive uses Process.clock_gettime instead of Time.now ([#185](https://github.com/googleapis/ruby-spanner/pull/185))
|
|
16
|
+
|
|
17
|
+
### 2.27.0 (2025-05-28)
|
|
18
|
+
|
|
19
|
+
#### Features
|
|
20
|
+
|
|
21
|
+
* Spanner Interval type ([#162](https://github.com/googleapis/ruby-spanner/issues/162))
|
|
22
|
+
* Updated required Ruby version to 3.1 ([#160](https://github.com/googleapis/ruby-spanner/issues/160))
|
|
23
|
+
|
|
3
24
|
### 2.26.0 (2025-03-24)
|
|
4
25
|
|
|
5
26
|
#### Features
|
|
@@ -33,8 +33,9 @@ module Google
|
|
|
33
33
|
# {Google::Cloud::Spanner::Admin::Database.database_admin}.list_backup_operations instead.
|
|
34
34
|
#
|
|
35
35
|
class List < DelegateClass(::Array)
|
|
36
|
+
# The `Spanner::Service` reference.
|
|
36
37
|
# @private
|
|
37
|
-
#
|
|
38
|
+
# @return [::Google::Cloud::Spanner::Service]
|
|
38
39
|
attr_accessor :service
|
|
39
40
|
|
|
40
41
|
# @private
|
|
@@ -148,13 +149,14 @@ module Google
|
|
|
148
149
|
end
|
|
149
150
|
end
|
|
150
151
|
|
|
151
|
-
|
|
152
|
-
# @private
|
|
153
|
-
#
|
|
154
|
-
# New Backup::Job::List from a
|
|
152
|
+
# Creates a new `Spanner::Backup::Job::List` of `Gapic::Operation` operations from a
|
|
155
153
|
# `Gapic::PagedEnumerable<Google::Longrunning::Operation>`
|
|
156
154
|
# object. Operation object is a backup operation.
|
|
157
|
-
#
|
|
155
|
+
# @param grpc [::Gapic::PagedEnumerable<::Google::Longrunning::Operation>]
|
|
156
|
+
# Wrapped `Gapic::PagedEnumberable` reference.
|
|
157
|
+
# @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
|
|
158
|
+
# @private
|
|
159
|
+
# @return [::Google::Cloud::Spanner::Backup::Job::List]
|
|
158
160
|
def self.from_grpc grpc, service
|
|
159
161
|
operations_client =
|
|
160
162
|
service.databases.instance_variable_get "@operations_client"
|
|
@@ -58,12 +58,14 @@ module Google
|
|
|
58
58
|
# end
|
|
59
59
|
#
|
|
60
60
|
class Job
|
|
61
|
-
|
|
62
|
-
# @private
|
|
61
|
+
# The wrapped `Gapic::Operation` object.
|
|
62
|
+
# @private
|
|
63
|
+
# @return [::Gapic::Operation]
|
|
63
64
|
attr_accessor :grpc
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
# @private
|
|
66
|
+
# The `Spanner::Service` reference.
|
|
67
|
+
# @private
|
|
68
|
+
# @return [::Google::Cloud::Spanner::Service]
|
|
67
69
|
attr_accessor :service
|
|
68
70
|
|
|
69
71
|
##
|
|
@@ -265,8 +267,11 @@ module Google
|
|
|
265
267
|
Convert.timestamp_to_time @grpc.metadata.cancel_time
|
|
266
268
|
end
|
|
267
269
|
|
|
268
|
-
|
|
269
|
-
# @
|
|
270
|
+
# Create a new Backup::Job from a `Gapic::Operation` object.
|
|
271
|
+
# @param grpc [::Gapic::Operation`] The wrapped `Gapic::Operation` object.
|
|
272
|
+
# @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
|
|
273
|
+
# @private
|
|
274
|
+
# @return [::Google::Cloud::Spanner::Backup::Job]
|
|
270
275
|
def self.from_grpc grpc, service
|
|
271
276
|
new.tap do |job|
|
|
272
277
|
job.instance_variable_set :@grpc, grpc
|
|
@@ -63,8 +63,18 @@ module Google
|
|
|
63
63
|
# new_partition
|
|
64
64
|
#
|
|
65
65
|
class BatchClient
|
|
66
|
-
|
|
67
|
-
# @
|
|
66
|
+
# Creates a new Spanner BatchClient instance.
|
|
67
|
+
# @param project [::Google::Cloud::Spanner::Project] A `Spanner::Project` ref.
|
|
68
|
+
# @param instance_id [::String] Instance id, e.g. `"my-instance"`.
|
|
69
|
+
# @param database_id [::String] Database id, e.g. `"my-database"`.
|
|
70
|
+
# @param session_labels [::Hash, nil] Optional. The labels to be applied to all sessions
|
|
71
|
+
# created by the client. Example: `"team" => "billing-service"`.
|
|
72
|
+
# @param query_options [::Hash, nil] Optional. A hash of values to specify the custom
|
|
73
|
+
# query options for executing SQL query. Example parameter `:optimizer_version`.
|
|
74
|
+
# @param directed_read_options [::Hash, nil] Optional. Client options used to set
|
|
75
|
+
# the `directed_read_options` for all ReadRequests and ExecuteSqlRequests.
|
|
76
|
+
# Converts to `V1::DirectedReadOptions`. Example option: `:exclude_replicas`.
|
|
77
|
+
# @private
|
|
68
78
|
def initialize project, instance_id, database_id, session_labels: nil,
|
|
69
79
|
query_options: nil, directed_read_options: nil
|
|
70
80
|
@project = project
|
|
@@ -404,15 +414,18 @@ module Google
|
|
|
404
414
|
|
|
405
415
|
protected
|
|
406
416
|
|
|
407
|
-
|
|
408
|
-
# @private Raise an error unless an active connection to the service is
|
|
417
|
+
# Raise an error unless an active connection to the service is
|
|
409
418
|
# available.
|
|
419
|
+
# @private
|
|
420
|
+
# @raise [StandardError]
|
|
421
|
+
# @return [nil]
|
|
410
422
|
def ensure_service!
|
|
411
423
|
raise "Must have active connection to service" unless @project.service
|
|
412
424
|
end
|
|
413
425
|
|
|
414
|
-
##
|
|
415
426
|
# New session for each use.
|
|
427
|
+
# @private
|
|
428
|
+
# @return [::Google::Cloud::Spanner::Session]
|
|
416
429
|
def session
|
|
417
430
|
ensure_service!
|
|
418
431
|
grpc = @project.service.create_session \
|
|
@@ -92,9 +92,9 @@ module Google
|
|
|
92
92
|
#
|
|
93
93
|
# @yield [::Google::Cloud::Spanner::BatchWriteResults::BatchResult]
|
|
94
94
|
#
|
|
95
|
-
def each
|
|
95
|
+
def each(&)
|
|
96
96
|
if defined? @results
|
|
97
|
-
@results.each(&
|
|
97
|
+
@results.each(&)
|
|
98
98
|
else
|
|
99
99
|
results = []
|
|
100
100
|
@enumerable.each do |grpc|
|
|
@@ -51,12 +51,26 @@ module Google
|
|
|
51
51
|
# end
|
|
52
52
|
#
|
|
53
53
|
class Client
|
|
54
|
-
|
|
54
|
+
# A semi-arbitrary constant for thread-wide global parameter name
|
|
55
55
|
# @private
|
|
56
56
|
IS_TRANSACTION_RUNNING_KEY = "ruby_spanner_is_transaction_running".freeze
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
# @
|
|
58
|
+
# Creates a new Spanner Client instance.
|
|
59
|
+
# @param project [::Google::Cloud::Spanner::Project] A `Spanner::Project` ref.
|
|
60
|
+
# @param instance_id [::String] Instance id, e.g. `"my-instance"`.
|
|
61
|
+
# @param database_id [::String] Database id, e.g. `"my-database"`.
|
|
62
|
+
# @param session_labels [::Hash, nil] Optional. The labels to be applied to all sessions
|
|
63
|
+
# created by the client. Example: `"team" => "billing-service"`.
|
|
64
|
+
# @param pool_opts [::Hash] Optional. `Spanner::Pool` creation options.
|
|
65
|
+
# Example parameter: `:keepalive`.
|
|
66
|
+
# @param query_options [::Hash, nil] Optional. A hash of values to specify the custom
|
|
67
|
+
# query options for executing SQL query. Example parameter `:optimizer_version`.
|
|
68
|
+
# @param database_role [::String, nil] Optional. The Spanner session creator role.
|
|
69
|
+
# Example: `analyst`
|
|
70
|
+
# @param directed_read_options [::Hash, nil] Optional. Client options used to set
|
|
71
|
+
# the `directed_read_options` for all ReadRequests and ExecuteSqlRequests.
|
|
72
|
+
# Converts to `V1::DirectedReadOptions`. Example option: `:exclude_replicas`.
|
|
73
|
+
# @private
|
|
60
74
|
def initialize project, instance_id, database_id, session_labels: nil,
|
|
61
75
|
pool_opts: {}, query_options: nil, database_role: nil,
|
|
62
76
|
directed_read_options: nil
|
|
@@ -89,7 +103,7 @@ module Google
|
|
|
89
103
|
end
|
|
90
104
|
|
|
91
105
|
# The Spanner project connected to.
|
|
92
|
-
# @return [Project]
|
|
106
|
+
# @return [::Google::Cloud::Spanner::Project]
|
|
93
107
|
def project
|
|
94
108
|
@project
|
|
95
109
|
end
|
|
@@ -878,6 +892,14 @@ module Google
|
|
|
878
892
|
# and all replicas are exhausted without finding a healthy replica,
|
|
879
893
|
# Spanner will wait for a replica in the list to become available,
|
|
880
894
|
# requests may fail due to DEADLINE_EXCEEDED errors.
|
|
895
|
+
# @param [::Google::Cloud::Spanner::V1::ReadRequest::OrderBy] order_by An option to control the order in which
|
|
896
|
+
# rows are returned from a read.
|
|
897
|
+
# To see the available options refer to
|
|
898
|
+
# [Google::Cloud::Spanner::V1::ReadRequest::OrderBy](https://cloud.google.com/ruby/docs/reference/google-cloud-spanner-v1/latest/Google-Cloud-Spanner-V1-ReadRequest-OrderBy)
|
|
899
|
+
# @param [::Google::Cloud::Spanner::V1::ReadRequest::LockHint] lock_hint A lock hint mechanism for reads done
|
|
900
|
+
# within a transaction.
|
|
901
|
+
# To see the available options refer to
|
|
902
|
+
# [Google::Cloud::Spanner::V1::ReadRequest::LockHint](https://cloud.google.com/ruby/docs/reference/google-cloud-spanner-v1/latest/Google-Cloud-Spanner-V1-ReadRequest-LockHint)
|
|
881
903
|
#
|
|
882
904
|
# @return [Google::Cloud::Spanner::Results] The results of the read.
|
|
883
905
|
#
|
|
@@ -962,7 +984,7 @@ module Google
|
|
|
962
984
|
#
|
|
963
985
|
def read table, columns, keys: nil, index: nil, limit: nil,
|
|
964
986
|
single_use: nil, request_options: nil, call_options: nil,
|
|
965
|
-
directed_read_options: nil
|
|
987
|
+
directed_read_options: nil, order_by: nil, lock_hint: nil
|
|
966
988
|
validate_single_use_args! single_use
|
|
967
989
|
ensure_service!
|
|
968
990
|
|
|
@@ -981,7 +1003,9 @@ module Google
|
|
|
981
1003
|
request_options: request_options,
|
|
982
1004
|
call_options: call_options,
|
|
983
1005
|
directed_read_options: directed_read_options || @directed_read_options,
|
|
984
|
-
route_to_leader: route_to_leader
|
|
1006
|
+
route_to_leader: route_to_leader,
|
|
1007
|
+
order_by: order_by,
|
|
1008
|
+
lock_hint: lock_hint
|
|
985
1009
|
end
|
|
986
1010
|
results
|
|
987
1011
|
end
|
|
@@ -2102,15 +2126,28 @@ module Google
|
|
|
2102
2126
|
begin
|
|
2103
2127
|
Thread.current[IS_TRANSACTION_RUNNING_KEY] = true
|
|
2104
2128
|
yield tx
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2129
|
+
|
|
2130
|
+
unless tx.existing_transaction?
|
|
2131
|
+
# This can happen if the yielded `tx` object was only used to add mutations.
|
|
2132
|
+
# Then it never called any RPCs and didn't create a server-side Transaction object.
|
|
2133
|
+
# In which case we should make an explicit BeginTransaction call here.
|
|
2134
|
+
tx.safe_begin_transaction!(
|
|
2135
|
+
exclude_from_change_streams: exclude_txn_from_change_streams,
|
|
2136
|
+
request_options: request_options,
|
|
2137
|
+
call_options: call_options
|
|
2138
|
+
)
|
|
2139
|
+
end
|
|
2140
|
+
|
|
2141
|
+
transaction_id = tx.transaction_id
|
|
2142
|
+
commit_resp = @project.service.commit(
|
|
2143
|
+
tx.session.path,
|
|
2144
|
+
tx.mutations,
|
|
2109
2145
|
transaction_id: transaction_id,
|
|
2110
2146
|
exclude_txn_from_change_streams: exclude_txn_from_change_streams,
|
|
2111
2147
|
commit_options: commit_options,
|
|
2112
2148
|
request_options: request_options,
|
|
2113
2149
|
call_options: call_options
|
|
2150
|
+
)
|
|
2114
2151
|
resp = CommitResponse.from_grpc commit_resp
|
|
2115
2152
|
commit_options ? resp : resp.timestamp
|
|
2116
2153
|
rescue GRPC::Aborted,
|
|
@@ -2409,17 +2446,21 @@ module Google
|
|
|
2409
2446
|
@pool.reset
|
|
2410
2447
|
end
|
|
2411
2448
|
|
|
2412
|
-
|
|
2449
|
+
# Creates a new Session objece.
|
|
2450
|
+
# @param multiplexed [::Boolean] Optional. Default to `false`.
|
|
2451
|
+
# If `true`, specifies a multiplexed session.
|
|
2413
2452
|
# @private
|
|
2414
|
-
#
|
|
2415
|
-
def create_new_session
|
|
2453
|
+
# @return [::Google::Cloud::Spanner::Session]
|
|
2454
|
+
def create_new_session multiplexed: false
|
|
2416
2455
|
ensure_service!
|
|
2417
2456
|
grpc = @project.service.create_session \
|
|
2418
2457
|
Admin::Database::V1::DatabaseAdmin::Paths.database_path(
|
|
2419
2458
|
project: project_id, instance: instance_id, database: database_id
|
|
2420
2459
|
),
|
|
2421
2460
|
labels: @session_labels,
|
|
2422
|
-
database_role: @database_role
|
|
2461
|
+
database_role: @database_role,
|
|
2462
|
+
multiplexed: multiplexed
|
|
2463
|
+
|
|
2423
2464
|
Session.from_grpc grpc, @project.service, query_options: @query_options
|
|
2424
2465
|
end
|
|
2425
2466
|
|
|
@@ -19,6 +19,7 @@ require "stringio"
|
|
|
19
19
|
require "base64"
|
|
20
20
|
require "bigdecimal"
|
|
21
21
|
require "google/cloud/spanner/data"
|
|
22
|
+
require "google/cloud/spanner/interval"
|
|
22
23
|
|
|
23
24
|
module Google
|
|
24
25
|
module Cloud
|
|
@@ -108,6 +109,8 @@ module Google
|
|
|
108
109
|
else
|
|
109
110
|
Google::Protobuf::Value.new string_value: obj.to_json
|
|
110
111
|
end
|
|
112
|
+
when Interval
|
|
113
|
+
obj.to_s
|
|
111
114
|
when Google::Protobuf::MessageExts
|
|
112
115
|
proto_class = obj.class
|
|
113
116
|
content = proto_class.encode obj
|
|
@@ -252,6 +255,8 @@ module Google
|
|
|
252
255
|
BigDecimal value.string_value
|
|
253
256
|
when :JSON
|
|
254
257
|
JSON.parse value.string_value
|
|
258
|
+
when :INTERVAL
|
|
259
|
+
Interval.parse value.string_value
|
|
255
260
|
when :PROTO
|
|
256
261
|
descriptor = Google::Protobuf::DescriptorPool.generated_pool.lookup(type.proto_type_fqn).msgclass
|
|
257
262
|
content = Base64.decode64 value.string_value
|
|
@@ -74,11 +74,16 @@ module Google
|
|
|
74
74
|
# end
|
|
75
75
|
#
|
|
76
76
|
class Instance
|
|
77
|
-
|
|
78
|
-
# @private
|
|
77
|
+
# The `Spanner::Service` reference.
|
|
78
|
+
# @private
|
|
79
|
+
# @return [::Google::Cloud::Spanner::Service]
|
|
79
80
|
attr_accessor :service
|
|
80
81
|
|
|
81
|
-
#
|
|
82
|
+
# Creates a new `Spanner::Instance` instance.
|
|
83
|
+
# @param grpc [::Google::Cloud::Spanner::Admin::Instance::V1::Instance]
|
|
84
|
+
# The protobuf `V1::Instance` underlying object.
|
|
85
|
+
# @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
|
|
86
|
+
# @private
|
|
82
87
|
def initialize grpc, service
|
|
83
88
|
@grpc = grpc
|
|
84
89
|
@service = service
|
|
@@ -957,9 +962,13 @@ module Google
|
|
|
957
962
|
grpc.permissions
|
|
958
963
|
end
|
|
959
964
|
|
|
960
|
-
|
|
961
|
-
# @private Creates a new Instance instance from a
|
|
965
|
+
# Creates a new Instance instance from a
|
|
962
966
|
# `Google::Cloud::Spanner::Admin::Instance::V1::Instance`.
|
|
967
|
+
# @param grpc [::Google::Cloud::Spanner::Admin::Instance::V1::Instance]
|
|
968
|
+
# The protobuf `V1::Instance` underlying object.
|
|
969
|
+
# @param service [::Google::Cloud::Spanner::Service] A `Spanner::Service` reference.
|
|
970
|
+
# @private
|
|
971
|
+
# @return [::Google::Cloud::Spanner::Instance]
|
|
963
972
|
def self.from_grpc grpc, service
|
|
964
973
|
new grpc, service
|
|
965
974
|
end
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Copyright 2017 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License")
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
module Google
|
|
16
|
+
module Cloud
|
|
17
|
+
module Spanner
|
|
18
|
+
##
|
|
19
|
+
# # Interval
|
|
20
|
+
#
|
|
21
|
+
# Represents an interval of time by storing the time components
|
|
22
|
+
# in months, days and nanoseconds.
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# require "google/cloud/spanner"
|
|
26
|
+
#
|
|
27
|
+
# iso_8601_string = "P1Y2M3DT4H5M6S"
|
|
28
|
+
# interval = Google::Cloud::Spanner::Interval::parse iso_8601_string
|
|
29
|
+
#
|
|
30
|
+
# puts interval # "P1Y2M3DT4H5M6S"
|
|
31
|
+
class Interval
|
|
32
|
+
NANOSECONDS_IN_A_SECOND = 1_000_000_000
|
|
33
|
+
NANOSECONDS_IN_A_MINUTE = NANOSECONDS_IN_A_SECOND * 60
|
|
34
|
+
NANOSECONDS_IN_AN_HOUR = NANOSECONDS_IN_A_MINUTE * 60
|
|
35
|
+
NANOSECONDS_IN_A_MILLISECOND = 1_000_000
|
|
36
|
+
NANOSECONDS_IN_A_MICROSECOND = 1_000
|
|
37
|
+
MAX_MONTHS = 120_000
|
|
38
|
+
MIN_MONTHS = -MAX_MONTHS
|
|
39
|
+
MAX_DAYS = 3_660_000
|
|
40
|
+
MIN_DAYS = -MAX_DAYS
|
|
41
|
+
MAX_NANOSECONDS = 316_224_000_000_000_000_000
|
|
42
|
+
MIN_NANOSECONDS = -316_224_000_000_000_000_000
|
|
43
|
+
|
|
44
|
+
private_constant :NANOSECONDS_IN_A_SECOND, :NANOSECONDS_IN_A_MINUTE, :NANOSECONDS_IN_AN_HOUR,
|
|
45
|
+
:NANOSECONDS_IN_A_MILLISECOND, :NANOSECONDS_IN_A_MICROSECOND, :MAX_MONTHS,
|
|
46
|
+
:MIN_MONTHS, :MAX_DAYS, :MIN_DAYS, :MAX_NANOSECONDS, :MIN_NANOSECONDS
|
|
47
|
+
|
|
48
|
+
class << self
|
|
49
|
+
# rubocop:disable Metrics/AbcSize
|
|
50
|
+
# rubocop:disable Metrics/MethodLength
|
|
51
|
+
|
|
52
|
+
# Parses an ISO8601 string and returns an Interval instance.
|
|
53
|
+
#
|
|
54
|
+
# The accepted format for the ISO8601 standard is:
|
|
55
|
+
# `P[n]Y[n]M[n]DT[n]H[n]M[n[.fraction]]S`
|
|
56
|
+
# where `n` represents an integer number.
|
|
57
|
+
#
|
|
58
|
+
# @param interval_string [String] An ISO8601 formatted string.
|
|
59
|
+
# @return [Google::Cloud::Spanner::Interval]
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# require "google/cloud/spanner"
|
|
63
|
+
#
|
|
64
|
+
# iso_8601_string = "P1Y2M3DT4H5M6S"
|
|
65
|
+
# interval = Google::Cloud::Spanner::Interval::parse iso_8601_string
|
|
66
|
+
#
|
|
67
|
+
# puts interval # "P1Y2M3DT4H5M6S"
|
|
68
|
+
#
|
|
69
|
+
def parse interval_string
|
|
70
|
+
pattern = /^
|
|
71
|
+
P(?!$)
|
|
72
|
+
(?:(?<years>-?\d+)Y)?
|
|
73
|
+
(?:(?<months>-?\d+)M)?
|
|
74
|
+
(?:(?<days>-?\d+)D)?
|
|
75
|
+
(?:T(?!$)
|
|
76
|
+
(?:(?<hours>-?\d+)H)?
|
|
77
|
+
(?:(?<minutes>-?\d+)M)?
|
|
78
|
+
(?:(?<seconds>-?(?!S)\d*(?:[.,]\d{1,9})?)S)?)?
|
|
79
|
+
$
|
|
80
|
+
/x
|
|
81
|
+
interval_months = 0
|
|
82
|
+
interval_days = 0
|
|
83
|
+
interval_nanoseconds = 0
|
|
84
|
+
|
|
85
|
+
matches = interval_string.match pattern
|
|
86
|
+
|
|
87
|
+
raise ArgumentError, "The provided string does not follow ISO8601 standard." if matches.nil?
|
|
88
|
+
|
|
89
|
+
raise ArgumentError, "The provided string does not follow ISO8601 standard." if matches.captures.empty?
|
|
90
|
+
|
|
91
|
+
interval_months += matches[:years].to_i * 12 if matches[:years]
|
|
92
|
+
|
|
93
|
+
interval_months += matches[:months].to_i if matches[:months]
|
|
94
|
+
|
|
95
|
+
interval_days = matches[:days].to_i if matches[:days]
|
|
96
|
+
|
|
97
|
+
interval_nanoseconds += matches[:hours].to_i * NANOSECONDS_IN_AN_HOUR if matches[:hours]
|
|
98
|
+
|
|
99
|
+
interval_nanoseconds += matches[:minutes].to_i * NANOSECONDS_IN_A_MINUTE if matches[:minutes]
|
|
100
|
+
|
|
101
|
+
# Only seconds can be fractional. Both period and comma are valid inputs.
|
|
102
|
+
if matches[:seconds]
|
|
103
|
+
interval_nanoseconds += (matches[:seconds].gsub(",", ".").to_f * NANOSECONDS_IN_A_SECOND).to_i
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
Interval.new interval_months, interval_days, interval_nanoseconds
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# rubocop:enable Metrics/AbcSize
|
|
110
|
+
# rubocop:enable Metrics/MethodLength
|
|
111
|
+
|
|
112
|
+
# Returns an Interval instance with the given months.
|
|
113
|
+
#
|
|
114
|
+
# @param months [Integer]
|
|
115
|
+
# @return [Interval]
|
|
116
|
+
def from_months months
|
|
117
|
+
Interval.new months, 0, 0
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Returns an Interval instance with the given days.
|
|
121
|
+
#
|
|
122
|
+
# @param days [Integer]
|
|
123
|
+
# @return [Interval]
|
|
124
|
+
def from_days days
|
|
125
|
+
Interval.new 0, days, 0
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Returns an Interval instance with the given seconds.
|
|
129
|
+
#
|
|
130
|
+
# @param seconds [Integer]
|
|
131
|
+
# @return [Interval]
|
|
132
|
+
def from_seconds seconds
|
|
133
|
+
nanoseconds = seconds * NANOSECONDS_IN_A_SECOND
|
|
134
|
+
Interval.new 0, 0, nanoseconds
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Returns an Interval instance with the given milliseconds.
|
|
138
|
+
#
|
|
139
|
+
# @param milliseconds [Integer]
|
|
140
|
+
# @return [Interval]
|
|
141
|
+
def from_milliseconds milliseconds
|
|
142
|
+
nanoseconds = milliseconds * NANOSECONDS_IN_A_MILLISECOND
|
|
143
|
+
Interval.new 0, 0, nanoseconds
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns an Interval instance with the given microseconds.
|
|
147
|
+
#
|
|
148
|
+
# @param microseconds [Integer]
|
|
149
|
+
# @return [Interval]
|
|
150
|
+
def from_microseconds microseconds
|
|
151
|
+
nanoseconds = microseconds * NANOSECONDS_IN_A_MICROSECOND
|
|
152
|
+
Interval.new 0, 0, nanoseconds
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Returns an Interval instance with the given nanoseconds.
|
|
156
|
+
#
|
|
157
|
+
# @param nanoseconds [Integer]
|
|
158
|
+
# @return [Interval]
|
|
159
|
+
def from_nanoseconds nanoseconds
|
|
160
|
+
Interval.new 0, 0, nanoseconds
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# Converts the [Interval] to an ISO8601 Standard string.
|
|
166
|
+
# @return [String] The interval's ISO8601 string representation.
|
|
167
|
+
def to_s
|
|
168
|
+
# Memoizing it as the logic can be a bit heavy.
|
|
169
|
+
@to_s ||= to_string
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
##
|
|
173
|
+
# @private Creates a new Google::Cloud::Spanner instance.
|
|
174
|
+
def initialize months, days, nanoseconds
|
|
175
|
+
if months > MAX_MONTHS || months < MIN_MONTHS
|
|
176
|
+
raise ArgumentError, "The Interval class supports months from #{MIN_MONTHS} to #{MAX_MONTHS}."
|
|
177
|
+
end
|
|
178
|
+
@months = months
|
|
179
|
+
|
|
180
|
+
if days > MAX_DAYS || days < MIN_DAYS
|
|
181
|
+
raise ArgumentError, "The Interval class supports days from #{MIN_DAYS} to #{MAX_DAYS}."
|
|
182
|
+
end
|
|
183
|
+
@days = days
|
|
184
|
+
|
|
185
|
+
if nanoseconds > MAX_NANOSECONDS || nanoseconds < MIN_NANOSECONDS
|
|
186
|
+
raise ArgumentError, "The Interval class supports nanoseconds from #{MIN_NANOSECONDS} to #{MAX_NANOSECONDS}"
|
|
187
|
+
end
|
|
188
|
+
@nanoseconds = nanoseconds
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# @return [Integer] The numbers of months in the time interval.
|
|
193
|
+
attr_reader :months
|
|
194
|
+
|
|
195
|
+
# @return [Integer] The numbers of days in the time interval.
|
|
196
|
+
attr_reader :days
|
|
197
|
+
|
|
198
|
+
# @return [Integer] The numbers of nanoseconds in the time interval.
|
|
199
|
+
attr_reader :nanoseconds
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# Standard value equality check for this object.
|
|
204
|
+
#
|
|
205
|
+
# @param [Object] other An object to compare with.
|
|
206
|
+
# @return [Boolean]
|
|
207
|
+
def eql? other
|
|
208
|
+
other.is_a?(Interval) &&
|
|
209
|
+
months == other.months &&
|
|
210
|
+
days == other.days &&
|
|
211
|
+
nanoseconds == other.nanoseconds
|
|
212
|
+
end
|
|
213
|
+
alias == eql?
|
|
214
|
+
|
|
215
|
+
##
|
|
216
|
+
# Generate standard hash code for this object.
|
|
217
|
+
#
|
|
218
|
+
# @return [Integer]
|
|
219
|
+
#
|
|
220
|
+
def hash
|
|
221
|
+
@hash ||= [@months, @days, @nanoseconds].hash
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
private
|
|
225
|
+
|
|
226
|
+
def match_sign value
|
|
227
|
+
value.negative? ? -1 : 1
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# rubocop:disable Metrics/AbcSize
|
|
231
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
232
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
233
|
+
|
|
234
|
+
# Converts [Interval] to an ISO8601 Standard string.
|
|
235
|
+
# @return [String] The interval's ISO8601 string representation.
|
|
236
|
+
def to_string
|
|
237
|
+
# Months should be converted to years and months.
|
|
238
|
+
years = @months.fdiv(12).truncate
|
|
239
|
+
months = @months % (match_sign(@months) * 12)
|
|
240
|
+
|
|
241
|
+
days = @days
|
|
242
|
+
|
|
243
|
+
# Nanoseconds should be converted to hours, minutes and seconds components.
|
|
244
|
+
remaining_nanoseconds = @nanoseconds
|
|
245
|
+
|
|
246
|
+
hours = (remaining_nanoseconds.abs / NANOSECONDS_IN_AN_HOUR) * match_sign(remaining_nanoseconds)
|
|
247
|
+
remaining_nanoseconds %= (match_sign(remaining_nanoseconds) * NANOSECONDS_IN_AN_HOUR)
|
|
248
|
+
minutes = (remaining_nanoseconds.abs / NANOSECONDS_IN_A_MINUTE) * match_sign(remaining_nanoseconds)
|
|
249
|
+
remaining_nanoseconds %= (match_sign(remaining_nanoseconds) * NANOSECONDS_IN_A_MINUTE)
|
|
250
|
+
|
|
251
|
+
# Only seconds can be fractional, and can have a maximum of 9 characters after decimal. Therefore,
|
|
252
|
+
# we convert the remaining nanoseconds to an integer for formatting.
|
|
253
|
+
seconds = (remaining_nanoseconds.abs / NANOSECONDS_IN_A_SECOND) * match_sign(remaining_nanoseconds)
|
|
254
|
+
nanoseconds = remaining_nanoseconds % (match_sign(remaining_nanoseconds) * NANOSECONDS_IN_A_SECOND)
|
|
255
|
+
|
|
256
|
+
interval_string = ["P"]
|
|
257
|
+
|
|
258
|
+
interval_string.append "#{years}Y" if years.nonzero?
|
|
259
|
+
|
|
260
|
+
interval_string.append "#{months}M" if months.nonzero?
|
|
261
|
+
|
|
262
|
+
interval_string.append "#{days}D" if days.nonzero?
|
|
263
|
+
|
|
264
|
+
if hours.nonzero? || minutes.nonzero? || seconds.nonzero? || nanoseconds.nonzero?
|
|
265
|
+
interval_string.append "T"
|
|
266
|
+
|
|
267
|
+
interval_string.append "#{hours}H" if hours.nonzero?
|
|
268
|
+
|
|
269
|
+
interval_string.append "#{minutes}M" if minutes.nonzero?
|
|
270
|
+
|
|
271
|
+
if seconds.nonzero? || nanoseconds.nonzero?
|
|
272
|
+
interval_string.append "#{format_seconds seconds, nanoseconds}S"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
return "P0Y" if interval_string == ["P"]
|
|
277
|
+
|
|
278
|
+
interval_string.join
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# rubocop:enable Metrics/AbcSize
|
|
282
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
283
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
284
|
+
|
|
285
|
+
# Formats decimal values to be in multiples of 3 length.
|
|
286
|
+
# @return [String]
|
|
287
|
+
def format_seconds seconds, nanoseconds
|
|
288
|
+
return seconds if nanoseconds.zero?
|
|
289
|
+
add_sign = seconds.zero? && nanoseconds.negative?
|
|
290
|
+
|
|
291
|
+
nanoseconds_str = nanoseconds.abs.to_s.rjust 9, "0"
|
|
292
|
+
nanoseconds_str = nanoseconds_str.gsub(/0+$/, "")
|
|
293
|
+
|
|
294
|
+
target_length =
|
|
295
|
+
if nanoseconds_str.length <= 3
|
|
296
|
+
3
|
|
297
|
+
elsif nanoseconds_str.length <= 6
|
|
298
|
+
6
|
|
299
|
+
else
|
|
300
|
+
9
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
nanoseconds_str = (nanoseconds_str + ("0" * target_length))[0...target_length]
|
|
304
|
+
"#{add_sign ? '-' : ''}#{seconds}.#{nanoseconds_str}"
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
@@ -44,8 +44,14 @@ module Google
|
|
|
44
44
|
# results = batch_snapshot.execute_partition partition
|
|
45
45
|
#
|
|
46
46
|
class Partition
|
|
47
|
-
#
|
|
47
|
+
# A `V1::ExecuteSqlRequest` that is related to this partition.
|
|
48
|
+
# @private
|
|
49
|
+
# @return [::Google::Cloud::Spanner::V1::ExecuteSqlRequest]
|
|
48
50
|
attr_reader :execute
|
|
51
|
+
|
|
52
|
+
# A `V1::ReadRequest` that is related to this partition.
|
|
53
|
+
# @private
|
|
54
|
+
# @return [::Google::Cloud::Spanner::V1::ReadRequest]
|
|
49
55
|
attr_reader :read
|
|
50
56
|
|
|
51
57
|
##
|