google-cloud-spanner 2.26.0 → 2.27.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 +7 -0
- data/lib/google/cloud/spanner/batch_write_results.rb +2 -2
- data/lib/google/cloud/spanner/convert.rb +5 -0
- data/lib/google/cloud/spanner/interval.rb +309 -0
- data/lib/google/cloud/spanner/pool.rb +2 -2
- 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: 2950745d1ef3cf6be982e2bc64a29ce467e8a15710293265181d41dd138a80ad
|
4
|
+
data.tar.gz: 6e96c531e76300e6d77d5d261ec0d09da96157f82aa3f4c32c502b52ce6b6f74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55291fac48fb628c64d4df17266cf1a07babbd918b8411e7116e8ba23b0430c20dbf0f210098f27052bbcd9d97c9e3ed0ec2937a4c342cb5994946df2dcf2ac6
|
7
|
+
data.tar.gz: b2e91d7c79a316a69b3657eace1232936a13013236117170b974a9487e0d9fdd5c9de34bba851ffbe310a26c6864954fd94de68df91fdb1a9d64bd06773d5aff
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 2.27.0 (2025-05-28)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* Spanner Interval type ([#162](https://github.com/googleapis/ruby-spanner/issues/162))
|
8
|
+
* Updated required Ruby version to 3.1 ([#160](https://github.com/googleapis/ruby-spanner/issues/160))
|
9
|
+
|
3
10
|
### 2.26.0 (2025-03-24)
|
4
11
|
|
5
12
|
#### Features
|
@@ -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|
|
@@ -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
|
@@ -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
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google-cloud-spanner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.27.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Moore
|
8
8
|
- Chris Smith
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -150,6 +150,7 @@ files:
|
|
150
150
|
- lib/google/cloud/spanner/instance/config/list.rb
|
151
151
|
- lib/google/cloud/spanner/instance/job.rb
|
152
152
|
- lib/google/cloud/spanner/instance/list.rb
|
153
|
+
- lib/google/cloud/spanner/interval.rb
|
153
154
|
- lib/google/cloud/spanner/lar_headers.rb
|
154
155
|
- lib/google/cloud/spanner/mutation_group.rb
|
155
156
|
- lib/google/cloud/spanner/partition.rb
|
@@ -175,14 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
176
|
requirements:
|
176
177
|
- - ">="
|
177
178
|
- !ruby/object:Gem::Version
|
178
|
-
version: '3.
|
179
|
+
version: '3.1'
|
179
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
180
181
|
requirements:
|
181
182
|
- - ">="
|
182
183
|
- !ruby/object:Gem::Version
|
183
184
|
version: '0'
|
184
185
|
requirements: []
|
185
|
-
rubygems_version: 3.6.
|
186
|
+
rubygems_version: 3.6.9
|
186
187
|
specification_version: 4
|
187
188
|
summary: API Client library for Google Cloud Spanner API
|
188
189
|
test_files: []
|