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.
@@ -34,7 +34,7 @@ module Google
34
34
  #
35
35
  # db = spanner.client "my-instance", "my-database"
36
36
  #
37
- # results = db.execute "SELECT * FROM users"
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.execute "SELECT * FROM users"
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 execute/query operation.
55
+ # Whether the partition was created for an execute_query/query
56
+ # operation.
56
57
  # @return [Boolean]
57
- def execute?
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 execute or read operation.
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] = Base64.strict_encode64(@read.to_proto) if @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[:execute].nil? && hash[:read].nil?
171
+ # TODO: raise if hash[:execute_query].nil? && hash[:read].nil?
167
172
  new.tap do |p|
168
173
  if data[:execute]
169
- execute_grpc = Google::Spanner::V1::ExecuteSqlRequest.decode \
170
- Base64.decode64(data[:execute])
171
- p.instance_variable_set :@execute, execute_grpc
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.from_execute_grpc grpc
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.execute "SELECT * FROM users"
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.execute "SELECT * FROM users"
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#execute} and
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.execute "SELECT * FROM users"
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.execute "SELECT * FROM users"
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.execute "SELECT * FROM users"
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
- buffered_responses.each do |resp|
134
- if chunked_value
135
- resp.values.unshift merge(chunked_value, resp.values.shift)
136
- chunked_value = nil
137
- end
138
- to_iterate = values + Array(resp.values)
139
- chunked_value = to_iterate.pop if resp.chunked_value
140
- values = to_iterate.pop(to_iterate.count % fields.count)
141
- to_iterate.each_slice(fields.count) do |slice|
142
- yield Data.from_grpc(slice, fields)
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 @execute_options
158
- @enum = @service.streaming_execute_sql \
154
+ if @execute_query_options
155
+ @enum = @service.execute_streaming_sql \
159
156
  @session_path, @sql,
160
- @execute_options.merge(resume_token: resume_token)
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
- buffered_responses.each do |resp|
178
- if chunked_value
179
- resp.values.unshift merge(chunked_value, resp.values.shift)
180
- chunked_value = nil
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
- to_iterate = values + Array(resp.values)
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
- def self.execute service, session_path, sql, params: nil, types: nil,
215
- transaction: nil, partition_token: nil
216
- execute_options = { transaction: transaction, params: params,
217
- types: types, partition_token: partition_token }
218
- enum = service.streaming_execute_sql session_path, sql,
219
- execute_options
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, session_path
222
- results.instance_variable_set :@sql, sql
223
- results.instance_variable_set :@execute_options, execute_options
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 streaming_execute_sql session_name, sql, transaction: nil,
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, params: params,
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, options: opts
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.execute "SELECT * FROM users"
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.execute "SELECT * FROM users WHERE active = @active",
179
- # params: { active: true }
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.execute "SELECT * FROM users WHERE " \
195
- # "ID = @user_struct.id " \
196
- # "AND name = @user_struct.name " \
197
- # "AND active = @user_struct.active",
198
- # params: { user_struct: user_hash }
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.execute "SELECT * FROM users WHERE " \
215
- # "ID = @user_struct.id " \
216
- # "AND name = @user_struct.name " \
217
- # "AND active = @user_struct.active",
218
- # params: { user_struct: user_hash },
219
- # types: { user_struct: user_type }
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.execute "SELECT * FROM users WHERE " \
236
- # "ID = @user_struct.id " \
237
- # "AND name = @user_struct.name " \
238
- # "AND active = @user_struct.active",
239
- # params: { user_struct: user_struct }
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 execute sql, params: nil, types: nil, transaction: nil,
246
- partition_token: nil
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.execute service, path, sql,
250
- params: params, types: types,
251
- transaction: transaction,
252
- partition_token: partition_token
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
- # {#execute}.
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
- execute "SELECT 1"
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?