google-cloud-spanner 1.6.4 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/lib/google/cloud/spanner/batch_snapshot.rb +43 -32
- data/lib/google/cloud/spanner/client.rb +221 -27
- data/lib/google/cloud/spanner/data.rb +1 -1
- data/lib/google/cloud/spanner/fields.rb +1 -1
- data/lib/google/cloud/spanner/partition.rb +16 -9
- data/lib/google/cloud/spanner/project.rb +2 -2
- data/lib/google/cloud/spanner/results.rb +73 -42
- data/lib/google/cloud/spanner/service.rb +18 -4
- data/lib/google/cloud/spanner/session.rb +39 -27
- data/lib/google/cloud/spanner/snapshot.rb +32 -24
- data/lib/google/cloud/spanner/transaction.rb +135 -25
- data/lib/google/cloud/spanner/version.rb +1 -1
- metadata +2 -2
@@ -34,7 +34,7 @@ module Google
|
|
34
34
|
#
|
35
35
|
# db = spanner.client "my-instance", "my-database"
|
36
36
|
#
|
37
|
-
# results = db.
|
37
|
+
# results = db.execute_query "SELECT * FROM users"
|
38
38
|
#
|
39
39
|
# results.rows.each do |row|
|
40
40
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -33,7 +33,7 @@ module Google
|
|
33
33
|
#
|
34
34
|
# db = spanner.client "my-instance", "my-database"
|
35
35
|
#
|
36
|
-
# results = db.
|
36
|
+
# results = db.execute_query "SELECT * FROM users"
|
37
37
|
#
|
38
38
|
# results.fields.pairs.each do |name, type|
|
39
39
|
# puts "Column #{name} is type #{type}"
|
@@ -52,11 +52,15 @@ module Google
|
|
52
52
|
def initialize; end
|
53
53
|
|
54
54
|
##
|
55
|
-
# Whether the partition was created for an
|
55
|
+
# Whether the partition was created for an execute_query/query
|
56
|
+
# operation.
|
56
57
|
# @return [Boolean]
|
57
|
-
def
|
58
|
+
def execute_query?
|
58
59
|
!@execute.nil?
|
59
60
|
end
|
61
|
+
alias execute? execute_query?
|
62
|
+
alias execute_sql? execute_query?
|
63
|
+
alias query? execute_query?
|
60
64
|
|
61
65
|
##
|
62
66
|
# Whether the partition was created for a read operation.
|
@@ -67,7 +71,8 @@ module Google
|
|
67
71
|
|
68
72
|
##
|
69
73
|
# @private
|
70
|
-
# Whether the partition does not have an
|
74
|
+
# Whether the partition does not have an execute_query or read
|
75
|
+
# operation.
|
71
76
|
# @return [Boolean]
|
72
77
|
def empty?
|
73
78
|
@execute.nil? && @read.nil?
|
@@ -84,7 +89,7 @@ module Google
|
|
84
89
|
def to_h
|
85
90
|
{}.tap do |h|
|
86
91
|
h[:execute] = Base64.strict_encode64(@execute.to_proto) if @execute
|
87
|
-
h[:read]
|
92
|
+
h[:read] = Base64.strict_encode64(@read.to_proto) if @read
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
@@ -163,12 +168,14 @@ module Google
|
|
163
168
|
def self.load data
|
164
169
|
data = JSON.parse data, symbolize_names: true unless data.is_a? Hash
|
165
170
|
|
166
|
-
# TODO: raise if hash[:
|
171
|
+
# TODO: raise if hash[:execute_query].nil? && hash[:read].nil?
|
167
172
|
new.tap do |p|
|
168
173
|
if data[:execute]
|
169
|
-
|
170
|
-
|
171
|
-
|
174
|
+
execute_sql_grpc = \
|
175
|
+
Google::Spanner::V1::ExecuteSqlRequest.decode(
|
176
|
+
Base64.decode64(data[:execute])
|
177
|
+
)
|
178
|
+
p.instance_variable_set :@execute, execute_sql_grpc
|
172
179
|
end
|
173
180
|
if data[:read]
|
174
181
|
read_grpc = Google::Spanner::V1::ReadRequest.decode \
|
@@ -189,7 +196,7 @@ module Google
|
|
189
196
|
##
|
190
197
|
# @private New Partition from a Google::Spanner::V1::ExecuteSqlRequest
|
191
198
|
# object.
|
192
|
-
def self.
|
199
|
+
def self.from_execute_sql_grpc grpc
|
193
200
|
new.tap do |p|
|
194
201
|
p.instance_variable_set :@execute, grpc
|
195
202
|
end
|
@@ -58,7 +58,7 @@ module Google
|
|
58
58
|
# db = spanner.client "my-instance", "my-database"
|
59
59
|
#
|
60
60
|
# db.transaction do |tx|
|
61
|
-
# results = tx.
|
61
|
+
# results = tx.execute_query "SELECT * FROM users"
|
62
62
|
#
|
63
63
|
# results.rows.each do |row|
|
64
64
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -463,7 +463,7 @@ module Google
|
|
463
463
|
# db = spanner.client "my-instance", "my-database"
|
464
464
|
#
|
465
465
|
# db.transaction do |tx|
|
466
|
-
# results = tx.
|
466
|
+
# results = tx.execute_query "SELECT * FROM users"
|
467
467
|
#
|
468
468
|
# results.rows.each do |row|
|
469
469
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -24,7 +24,7 @@ module Google
|
|
24
24
|
#
|
25
25
|
# Represents the result set from an operation returning data.
|
26
26
|
#
|
27
|
-
# See {Google::Cloud::Spanner::Client#
|
27
|
+
# See {Google::Cloud::Spanner::Client#execute_query} and
|
28
28
|
# {Google::Cloud::Spanner::Client#read}.
|
29
29
|
#
|
30
30
|
# @example
|
@@ -34,7 +34,7 @@ module Google
|
|
34
34
|
#
|
35
35
|
# db = spanner.client "my-instance", "my-database"
|
36
36
|
#
|
37
|
-
# results = db.
|
37
|
+
# results = db.execute_query "SELECT * FROM users"
|
38
38
|
#
|
39
39
|
# results.fields.pairs.each do |name, type|
|
40
40
|
# puts "Column #{name} is type #{type}"
|
@@ -63,7 +63,7 @@ module Google
|
|
63
63
|
#
|
64
64
|
# db = spanner.client "my-instance", "my-database"
|
65
65
|
#
|
66
|
-
# results = db.
|
66
|
+
# results = db.execute_query "SELECT * FROM users"
|
67
67
|
#
|
68
68
|
# results.fields.pairs.each do |name, type|
|
69
69
|
# puts "Column #{name} is type #{type}"
|
@@ -88,7 +88,7 @@ module Google
|
|
88
88
|
#
|
89
89
|
# db = spanner.client "my-instance", "my-database"
|
90
90
|
#
|
91
|
-
# results = db.
|
91
|
+
# results = db.execute_query "SELECT * FROM users"
|
92
92
|
#
|
93
93
|
# results.rows.each do |row|
|
94
94
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -102,11 +102,6 @@ module Google
|
|
102
102
|
end
|
103
103
|
|
104
104
|
fields = @metadata.row_type.fields
|
105
|
-
if fields.count.zero?
|
106
|
-
@closed = true
|
107
|
-
return []
|
108
|
-
end
|
109
|
-
|
110
105
|
values = []
|
111
106
|
buffered_responses = []
|
112
107
|
buffer_upper_bound = 10
|
@@ -130,16 +125,18 @@ module Google
|
|
130
125
|
# This can set the resume_token to nil
|
131
126
|
resume_token = grpc.resume_token
|
132
127
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
128
|
+
if fields.count > 0
|
129
|
+
buffered_responses.each do |resp|
|
130
|
+
if chunked_value
|
131
|
+
resp.values.unshift merge(chunked_value, resp.values.shift)
|
132
|
+
chunked_value = nil
|
133
|
+
end
|
134
|
+
to_iterate = values + Array(resp.values)
|
135
|
+
chunked_value = to_iterate.pop if resp.chunked_value
|
136
|
+
values = to_iterate.pop(to_iterate.count % fields.count)
|
137
|
+
to_iterate.each_slice(fields.count) do |slice|
|
138
|
+
yield Data.from_grpc(slice, fields)
|
139
|
+
end
|
143
140
|
end
|
144
141
|
end
|
145
142
|
|
@@ -154,10 +151,10 @@ module Google
|
|
154
151
|
end
|
155
152
|
|
156
153
|
# Resume the stream from the last known resume_token
|
157
|
-
if @
|
158
|
-
@enum = @service.
|
154
|
+
if @execute_query_options
|
155
|
+
@enum = @service.execute_streaming_sql \
|
159
156
|
@session_path, @sql,
|
160
|
-
@
|
157
|
+
@execute_query_options.merge(resume_token: resume_token)
|
161
158
|
else
|
162
159
|
@enum = @service.streaming_read_table \
|
163
160
|
@session_path, @table, @columns,
|
@@ -174,21 +171,23 @@ module Google
|
|
174
171
|
end
|
175
172
|
|
176
173
|
# clear out any remaining values left over
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
174
|
+
if fields.count > 0
|
175
|
+
buffered_responses.each do |resp|
|
176
|
+
if chunked_value
|
177
|
+
resp.values.unshift merge(chunked_value, resp.values.shift)
|
178
|
+
chunked_value = nil
|
179
|
+
end
|
180
|
+
to_iterate = values + Array(resp.values)
|
181
|
+
chunked_value = to_iterate.pop if resp.chunked_value
|
182
|
+
values = to_iterate.pop(to_iterate.count % fields.count)
|
183
|
+
to_iterate.each_slice(fields.count) do |slice|
|
184
|
+
yield Data.from_grpc(slice, fields)
|
185
|
+
end
|
181
186
|
end
|
182
|
-
|
183
|
-
chunked_value = to_iterate.pop if resp.chunked_value
|
184
|
-
values = to_iterate.pop(to_iterate.count % fields.count)
|
185
|
-
to_iterate.each_slice(fields.count) do |slice|
|
187
|
+
values.each_slice(fields.count) do |slice|
|
186
188
|
yield Data.from_grpc(slice, fields)
|
187
189
|
end
|
188
190
|
end
|
189
|
-
values.each_slice(fields.count) do |slice|
|
190
|
-
yield Data.from_grpc(slice, fields)
|
191
|
-
end
|
192
191
|
|
193
192
|
# If we get this far then we can release the session
|
194
193
|
@closed = true
|
@@ -197,6 +196,33 @@ module Google
|
|
197
196
|
|
198
197
|
# rubocop:enable all
|
199
198
|
|
199
|
+
##
|
200
|
+
# @private
|
201
|
+
# Get row count from stats. This will be the exact row count for DML
|
202
|
+
# statements, and the lower bound row count for PDML statements.
|
203
|
+
def row_count
|
204
|
+
return @stats.row_count_lower_bound if row_count_lower_bound?
|
205
|
+
return @stats.row_count_exact if row_count_exact?
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# @private
|
211
|
+
# Whether the row count is the lower bound row count for PDML
|
212
|
+
# statements.
|
213
|
+
def row_count_lower_bound?
|
214
|
+
return nil if @stats.nil?
|
215
|
+
@stats.row_count == :row_count_lower_bound
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# @private
|
220
|
+
# Whether the row count is the exact row count for DML statements.
|
221
|
+
def row_count_exact?
|
222
|
+
return nil if @stats.nil?
|
223
|
+
@stats.row_count == :row_count_exact
|
224
|
+
end
|
225
|
+
|
200
226
|
# @private
|
201
227
|
def self.from_enum enum, service
|
202
228
|
grpc = enum.peek
|
@@ -211,16 +237,21 @@ module Google
|
|
211
237
|
end
|
212
238
|
|
213
239
|
# @private
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
240
|
+
|
241
|
+
def self.execute_query service, session_path, sql, params: nil,
|
242
|
+
types: nil, transaction: nil,
|
243
|
+
partition_token: nil, seqno: nil
|
244
|
+
execute_query_options = {
|
245
|
+
transaction: transaction, params: params, types: types,
|
246
|
+
partition_token: partition_token, seqno: seqno
|
247
|
+
}
|
248
|
+
enum = service.execute_streaming_sql session_path, sql,
|
249
|
+
execute_query_options
|
220
250
|
from_enum(enum, service).tap do |results|
|
221
|
-
results.instance_variable_set :@session_path,
|
222
|
-
results.instance_variable_set :@sql,
|
223
|
-
results.instance_variable_set :@
|
251
|
+
results.instance_variable_set :@session_path, session_path
|
252
|
+
results.instance_variable_set :@sql, sql
|
253
|
+
results.instance_variable_set :@execute_query_options,
|
254
|
+
execute_query_options
|
224
255
|
end
|
225
256
|
end
|
226
257
|
|
@@ -257,16 +257,19 @@ module Google
|
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
260
|
-
def
|
260
|
+
def execute_streaming_sql session_name, sql, transaction: nil,
|
261
261
|
params: nil, types: nil, resume_token: nil,
|
262
|
-
partition_token: nil
|
262
|
+
partition_token: nil, seqno: nil
|
263
263
|
opts = default_options_from_session session_name
|
264
264
|
execute do
|
265
265
|
service.execute_streaming_sql \
|
266
|
-
session_name, sql, transaction: transaction,
|
266
|
+
session_name, sql, transaction: transaction,
|
267
|
+
params: params,
|
267
268
|
param_types: types,
|
268
269
|
resume_token: resume_token,
|
269
|
-
partition_token: partition_token,
|
270
|
+
partition_token: partition_token,
|
271
|
+
seqno: seqno,
|
272
|
+
options: opts
|
270
273
|
end
|
271
274
|
end
|
272
275
|
|
@@ -367,6 +370,17 @@ module Google
|
|
367
370
|
end
|
368
371
|
end
|
369
372
|
|
373
|
+
def create_pdml session_name
|
374
|
+
tx_opts = Google::Spanner::V1::TransactionOptions.new(
|
375
|
+
partitioned_dml: \
|
376
|
+
Google::Spanner::V1::TransactionOptions::PartitionedDml.new
|
377
|
+
)
|
378
|
+
opts = default_options_from_session session_name
|
379
|
+
execute do
|
380
|
+
service.begin_transaction session_name, tx_opts, options: opts
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
370
384
|
def inspect
|
371
385
|
"#{self.class}(#{@project})"
|
372
386
|
end
|
@@ -151,6 +151,8 @@ module Google
|
|
151
151
|
# @param [Google::Spanner::V1::TransactionSelector] transaction The
|
152
152
|
# transaction selector value to send. Only used for single-use
|
153
153
|
# transactions.
|
154
|
+
# @param [Integer] seqno A per-transaction sequence number used to
|
155
|
+
# identify this request.
|
154
156
|
#
|
155
157
|
# @return [Google::Cloud::Spanner::Results] The results of the query
|
156
158
|
# execution.
|
@@ -162,7 +164,7 @@ module Google
|
|
162
164
|
#
|
163
165
|
# db = spanner.client "my-instance", "my-database"
|
164
166
|
#
|
165
|
-
# results = db.
|
167
|
+
# results = db.execute_query "SELECT * FROM users"
|
166
168
|
#
|
167
169
|
# results.rows.each do |row|
|
168
170
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -175,8 +177,10 @@ module Google
|
|
175
177
|
#
|
176
178
|
# db = spanner.client "my-instance", "my-database"
|
177
179
|
#
|
178
|
-
# results = db.
|
179
|
-
#
|
180
|
+
# results = db.execute_query(
|
181
|
+
# "SELECT * FROM users WHERE active = @active",
|
182
|
+
# params: { active: true }
|
183
|
+
# )
|
180
184
|
#
|
181
185
|
# results.rows.each do |row|
|
182
186
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -191,11 +195,13 @@ module Google
|
|
191
195
|
#
|
192
196
|
# user_hash = { id: 1, name: "Charlie", active: false }
|
193
197
|
#
|
194
|
-
# results = db.
|
195
|
-
#
|
196
|
-
#
|
197
|
-
#
|
198
|
-
#
|
198
|
+
# results = db.execute_query(
|
199
|
+
# "SELECT * FROM users WHERE " \
|
200
|
+
# "ID = @user_struct.id " \
|
201
|
+
# "AND name = @user_struct.name " \
|
202
|
+
# "AND active = @user_struct.active",
|
203
|
+
# params: { user_struct: user_hash }
|
204
|
+
# )
|
199
205
|
#
|
200
206
|
# results.rows.each do |row|
|
201
207
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -211,12 +217,14 @@ module Google
|
|
211
217
|
# user_type = db.fields id: :INT64, name: :STRING, active: :BOOL
|
212
218
|
# user_hash = { id: 1, name: nil, active: false }
|
213
219
|
#
|
214
|
-
# results = db.
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
+
# results = db.execute_query(
|
221
|
+
# "SELECT * FROM users WHERE " \
|
222
|
+
# "ID = @user_struct.id " \
|
223
|
+
# "AND name = @user_struct.name " \
|
224
|
+
# "AND active = @user_struct.active",
|
225
|
+
# params: { user_struct: user_hash },
|
226
|
+
# types: { user_struct: user_type }
|
227
|
+
# )
|
220
228
|
#
|
221
229
|
# results.rows.each do |row|
|
222
230
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -232,31 +240,35 @@ module Google
|
|
232
240
|
# user_type = db.fields id: :INT64, name: :STRING, active: :BOOL
|
233
241
|
# user_data = user_type.struct id: 1, name: nil, active: false
|
234
242
|
#
|
235
|
-
# results = db.
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
243
|
+
# results = db.execute_query(
|
244
|
+
# "SELECT * FROM users WHERE " \
|
245
|
+
# "ID = @user_struct.id " \
|
246
|
+
# "AND name = @user_struct.name " \
|
247
|
+
# "AND active = @user_struct.active",
|
248
|
+
# params: { user_struct: user_struct }
|
249
|
+
# )
|
240
250
|
#
|
241
251
|
# results.rows.each do |row|
|
242
252
|
# puts "User #{row[:id]} is #{row[:name]}"
|
243
253
|
# end
|
244
254
|
#
|
245
|
-
def
|
246
|
-
|
255
|
+
def execute_query sql, params: nil, types: nil, transaction: nil,
|
256
|
+
partition_token: nil, seqno: nil
|
247
257
|
ensure_service!
|
248
258
|
|
249
|
-
results = Results.
|
250
|
-
|
251
|
-
|
252
|
-
|
259
|
+
results = Results.execute_query service, path, sql,
|
260
|
+
params: params,
|
261
|
+
types: types,
|
262
|
+
transaction: transaction,
|
263
|
+
partition_token: partition_token,
|
264
|
+
seqno: seqno
|
253
265
|
@last_updated_at = Time.now
|
254
266
|
results
|
255
267
|
end
|
256
268
|
|
257
269
|
##
|
258
270
|
# Read rows from a database table, as a simple alternative to
|
259
|
-
# {#
|
271
|
+
# {#execute_query}.
|
260
272
|
#
|
261
273
|
# @param [String] table The name of the table in the database to be
|
262
274
|
# read.
|
@@ -612,7 +624,7 @@ module Google
|
|
612
624
|
# Keeps the session alive by executing `"SELECT 1"`.
|
613
625
|
def keepalive!
|
614
626
|
ensure_service!
|
615
|
-
|
627
|
+
execute_query "SELECT 1"
|
616
628
|
return true
|
617
629
|
rescue Google::Cloud::NotFoundError
|
618
630
|
labels = @grpc.labels.to_h unless @grpc.labels.to_h.empty?
|