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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f10305755bd4e6a4e31255e8fa54067a6725190a7bd8465321d3db6ab85e5b8
|
4
|
+
data.tar.gz: d6d33eaf5b1e84649fd7ab4d65565acf350afa2f073f2697106444f0a07ccdf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a17174bd2315f5252835be530aa64b264426eef1ba3285682d6546101127203059fb2878733b5f1056e25181cd592828dfd7113916be667503dc7251c60521d6
|
7
|
+
data.tar.gz: 7bce7dd07f441fffd5a56017f92220ad088465dc269a9dc9c967bf24b81e4017487f6bc601e22912c2e0c1206f9dc2488d21788e050f2864c73812f127548547
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 1.7.1 / 2018-10-08
|
4
|
+
|
5
|
+
* Add DML and Partitioned DML support
|
6
|
+
* Add execute_update to process DML statements
|
7
|
+
* Add execute_partition_update for Partitioned DML
|
8
|
+
* Rename execute_query method
|
9
|
+
* Maintain naming consistency with execute_update method.
|
10
|
+
* Maintain compatibility by adding query, execute and execute_sql aliases.
|
11
|
+
|
3
12
|
### 1.6.4 / 2018-09-20
|
4
13
|
|
5
14
|
* Update Spanner generated files.
|
@@ -200,7 +200,7 @@ module Google
|
|
200
200
|
|
201
201
|
results.partitions.map do |grpc|
|
202
202
|
# Convert partition protos to execute sql request protos
|
203
|
-
|
203
|
+
execute_sql_grpc = Google::Spanner::V1::ExecuteSqlRequest.new(
|
204
204
|
{
|
205
205
|
session: session.path,
|
206
206
|
sql: sql,
|
@@ -210,7 +210,7 @@ module Google
|
|
210
210
|
partition_token: grpc.partition_token
|
211
211
|
}.delete_if { |_, v| v.nil? }
|
212
212
|
)
|
213
|
-
Partition.
|
213
|
+
Partition.from_execute_sql_grpc execute_sql_grpc
|
214
214
|
end
|
215
215
|
end
|
216
216
|
|
@@ -425,7 +425,7 @@ module Google
|
|
425
425
|
# batch_client = spanner.batch_client "my-instance", "my-database"
|
426
426
|
# batch_snapshot = batch_client.batch_snapshot
|
427
427
|
#
|
428
|
-
# results = batch_snapshot.
|
428
|
+
# results = batch_snapshot.execute_query "SELECT * FROM users"
|
429
429
|
#
|
430
430
|
# results.rows.each do |row|
|
431
431
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -438,9 +438,11 @@ module Google
|
|
438
438
|
# batch_client = spanner.batch_client "my-instance", "my-database"
|
439
439
|
# batch_snapshot = batch_client.batch_snapshot
|
440
440
|
#
|
441
|
-
# results = batch_snapshot.
|
442
|
-
#
|
443
|
-
#
|
441
|
+
# results = batch_snapshot.execute_query(
|
442
|
+
# "SELECT * FROM users " \
|
443
|
+
# "WHERE active = @active",
|
444
|
+
# params: { active: true }
|
445
|
+
# )
|
444
446
|
#
|
445
447
|
# results.rows.each do |row|
|
446
448
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -455,11 +457,13 @@ module Google
|
|
455
457
|
#
|
456
458
|
# user_hash = { id: 1, name: "Charlie", active: false }
|
457
459
|
#
|
458
|
-
# results = batch_snapshot.
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
460
|
+
# results = batch_snapshot.execute_query(
|
461
|
+
# "SELECT * FROM users WHERE " \
|
462
|
+
# "ID = @user_struct.id " \
|
463
|
+
# "AND name = @user_struct.name " \
|
464
|
+
# "AND active = @user_struct.active",
|
465
|
+
# params: { user_struct: user_hash }
|
466
|
+
# )
|
463
467
|
#
|
464
468
|
# results.rows.each do |row|
|
465
469
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -477,12 +481,14 @@ module Google
|
|
477
481
|
# )
|
478
482
|
# user_hash = { id: 1, name: nil, active: false }
|
479
483
|
#
|
480
|
-
# results = batch_snapshot.
|
481
|
-
#
|
482
|
-
#
|
483
|
-
#
|
484
|
-
#
|
485
|
-
#
|
484
|
+
# results = batch_snapshot.execute_query(
|
485
|
+
# "SELECT * FROM users WHERE " \
|
486
|
+
# "ID = @user_struct.id " \
|
487
|
+
# "AND name = @user_struct.name " \
|
488
|
+
# "AND active = @user_struct.active",
|
489
|
+
# params: { user_struct: user_hash },
|
490
|
+
# types: { user_struct: user_type }
|
491
|
+
# )
|
486
492
|
#
|
487
493
|
# results.rows.each do |row|
|
488
494
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -500,29 +506,33 @@ module Google
|
|
500
506
|
# )
|
501
507
|
# user_data = user_type.struct id: 1, name: nil, active: false
|
502
508
|
#
|
503
|
-
# results = batch_snapshot.
|
504
|
-
#
|
505
|
-
#
|
506
|
-
#
|
507
|
-
#
|
509
|
+
# results = batch_snapshot.execute_query(
|
510
|
+
# "SELECT * FROM users WHERE " \
|
511
|
+
# "ID = @user_struct.id " \
|
512
|
+
# "AND name = @user_struct.name " \
|
513
|
+
# "AND active = @user_struct.active",
|
514
|
+
# params: { user_struct: user_data }
|
515
|
+
# )
|
508
516
|
#
|
509
517
|
# results.rows.each do |row|
|
510
518
|
# puts "User #{row[:id]} is #{row[:name]}"
|
511
519
|
# end
|
512
520
|
#
|
513
|
-
def
|
521
|
+
def execute_query sql, params: nil, types: nil
|
514
522
|
ensure_session!
|
515
523
|
|
516
524
|
params, types = Convert.to_input_params_and_types params, types
|
517
525
|
|
518
|
-
session.
|
519
|
-
|
526
|
+
session.execute_query sql, params: params, types: types,
|
527
|
+
transaction: tx_selector
|
520
528
|
end
|
521
|
-
alias
|
529
|
+
alias execute execute_query
|
530
|
+
alias query execute_query
|
531
|
+
alias execute_sql execute_query
|
522
532
|
|
523
533
|
##
|
524
534
|
# Read rows from a database table, as a simple alternative to
|
525
|
-
# {#
|
535
|
+
# {#execute_query}.
|
526
536
|
#
|
527
537
|
# @param [String] table The name of the table in the database to be
|
528
538
|
# read.
|
@@ -651,11 +661,12 @@ module Google
|
|
651
661
|
end
|
652
662
|
|
653
663
|
def execute_partition_query partition
|
654
|
-
session.
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
664
|
+
session.execute_query \
|
665
|
+
partition.execute.sql,
|
666
|
+
params: partition.execute.params,
|
667
|
+
types: partition.execute.param_types.to_h,
|
668
|
+
transaction: partition.execute.transaction,
|
669
|
+
partition_token: partition.execute.partition_token
|
659
670
|
end
|
660
671
|
|
661
672
|
def execute_partition_read partition
|
@@ -42,7 +42,7 @@ module Google
|
|
42
42
|
# db = spanner.client "my-instance", "my-database"
|
43
43
|
#
|
44
44
|
# db.transaction do |tx|
|
45
|
-
# results = tx.
|
45
|
+
# results = tx.execute_query "SELECT * FROM users"
|
46
46
|
#
|
47
47
|
# results.rows.each do |row|
|
48
48
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -218,7 +218,7 @@ module Google
|
|
218
218
|
#
|
219
219
|
# db = spanner.client "my-instance", "my-database"
|
220
220
|
#
|
221
|
-
# results = db.
|
221
|
+
# results = db.execute_query "SELECT * FROM users"
|
222
222
|
#
|
223
223
|
# results.rows.each do |row|
|
224
224
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -231,8 +231,10 @@ module Google
|
|
231
231
|
#
|
232
232
|
# db = spanner.client "my-instance", "my-database"
|
233
233
|
#
|
234
|
-
# results = db.
|
235
|
-
#
|
234
|
+
# results = db.execute_query(
|
235
|
+
# "SELECT * FROM users WHERE active = @active",
|
236
|
+
# params: { active: true }
|
237
|
+
# )
|
236
238
|
#
|
237
239
|
# results.rows.each do |row|
|
238
240
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -247,11 +249,13 @@ module Google
|
|
247
249
|
#
|
248
250
|
# user_hash = { id: 1, name: "Charlie", active: false }
|
249
251
|
#
|
250
|
-
# results = db.
|
251
|
-
#
|
252
|
-
#
|
253
|
-
#
|
254
|
-
#
|
252
|
+
# results = db.execute_query(
|
253
|
+
# "SELECT * FROM users WHERE " \
|
254
|
+
# "ID = @user_struct.id " \
|
255
|
+
# "AND name = @user_struct.name " \
|
256
|
+
# "AND active = @user_struct.active",
|
257
|
+
# params: { user_struct: user_hash }
|
258
|
+
# )
|
255
259
|
#
|
256
260
|
# results.rows.each do |row|
|
257
261
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -267,12 +271,14 @@ module Google
|
|
267
271
|
# user_type = db.fields id: :INT64, name: :STRING, active: :BOOL
|
268
272
|
# user_hash = { id: 1, name: nil, active: false }
|
269
273
|
#
|
270
|
-
# results = db.
|
271
|
-
#
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
275
|
-
#
|
274
|
+
# results = db.execute_query(
|
275
|
+
# "SELECT * FROM users WHERE " \
|
276
|
+
# "ID = @user_struct.id " \
|
277
|
+
# "AND name = @user_struct.name " \
|
278
|
+
# "AND active = @user_struct.active",
|
279
|
+
# params: { user_struct: user_hash },
|
280
|
+
# types: { user_struct: user_type }
|
281
|
+
# )
|
276
282
|
#
|
277
283
|
# results.rows.each do |row|
|
278
284
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -288,17 +294,19 @@ module Google
|
|
288
294
|
# user_type = db.fields id: :INT64, name: :STRING, active: :BOOL
|
289
295
|
# user_data = user_type.struct id: 1, name: nil, active: false
|
290
296
|
#
|
291
|
-
# results = db.
|
292
|
-
#
|
293
|
-
#
|
294
|
-
#
|
295
|
-
#
|
297
|
+
# results = db.execute_query(
|
298
|
+
# "SELECT * FROM users WHERE " \
|
299
|
+
# "ID = @user_struct.id " \
|
300
|
+
# "AND name = @user_struct.name " \
|
301
|
+
# "AND active = @user_struct.active",
|
302
|
+
# params: { user_struct: user_data }
|
303
|
+
# )
|
296
304
|
#
|
297
305
|
# results.rows.each do |row|
|
298
306
|
# puts "User #{row[:id]} is #{row[:name]}"
|
299
307
|
# end
|
300
308
|
#
|
301
|
-
def
|
309
|
+
def execute_query sql, params: nil, types: nil, single_use: nil
|
302
310
|
validate_single_use_args! single_use
|
303
311
|
ensure_service!
|
304
312
|
|
@@ -307,16 +315,197 @@ module Google
|
|
307
315
|
single_use_tx = single_use_transaction single_use
|
308
316
|
results = nil
|
309
317
|
@pool.with_session do |session|
|
310
|
-
results = session.
|
318
|
+
results = session.execute_query \
|
311
319
|
sql, params: params, types: types, transaction: single_use_tx
|
312
320
|
end
|
313
321
|
results
|
314
322
|
end
|
315
|
-
alias
|
323
|
+
alias execute execute_query
|
324
|
+
alias query execute_query
|
325
|
+
alias execute_sql execute_query
|
326
|
+
|
327
|
+
##
|
328
|
+
# Executes a Partitioned DML SQL statement.
|
329
|
+
#
|
330
|
+
# Partitioned DML is an alternate implementation with looser semantics
|
331
|
+
# to enable large-scale changes without running into transaction size
|
332
|
+
# limits or (accidentally) locking the entire table in one large
|
333
|
+
# transaction. At a high level, it partitions the keyspace and executes
|
334
|
+
# the statement on each partition in separate internal transactions.
|
335
|
+
#
|
336
|
+
# Partitioned DML does not guarantee database-wide atomicity of the
|
337
|
+
# statement - it guarantees row-based atomicity, which includes updates
|
338
|
+
# to any indices. Additionally, it does not guarantee that it will
|
339
|
+
# execute exactly one time against each row - it guarantees "at least
|
340
|
+
# once" semantics.
|
341
|
+
#
|
342
|
+
# Where DML statements must be executed using Transaction (see
|
343
|
+
# {Transaction#execute_update}), Paritioned DML statements are executed
|
344
|
+
# outside of a read/write transaction.
|
345
|
+
#
|
346
|
+
# Not all DML statements can be executed in the Partitioned DML mode and
|
347
|
+
# the backend will return an error for the statements which are not
|
348
|
+
# supported.
|
349
|
+
#
|
350
|
+
# DML statements must be fully-partitionable. Specifically, the
|
351
|
+
# statement must be expressible as the union of many statements which
|
352
|
+
# each access only a single row of the table.
|
353
|
+
# {Google::Cloud::InvalidArgumentError} is raised if the statement does
|
354
|
+
# not qualify.
|
355
|
+
#
|
356
|
+
# The method will block until the update is complete. Running a DML
|
357
|
+
# statement with this method does not offer exactly once semantics, and
|
358
|
+
# therefore the DML statement should be idempotent. The DML statement
|
359
|
+
# must be fully-partitionable. Specifically, the statement must be
|
360
|
+
# expressible as the union of many statements which each access only a
|
361
|
+
# single row of the table. This is a Partitioned DML transaction in
|
362
|
+
# which a single Partitioned DML statement is executed. Partitioned DML
|
363
|
+
# partitions the and runs the DML statement over each partition in
|
364
|
+
# parallel using separate, internal transactions that commit
|
365
|
+
# independently. Partitioned DML transactions do not need to be
|
366
|
+
# committed.
|
367
|
+
#
|
368
|
+
# Partitioned DML updates are used to execute a single DML statement
|
369
|
+
# with a different execution strategy that provides different, and often
|
370
|
+
# better, scalability properties for large, table-wide operations than
|
371
|
+
# DML in a {Transaction#execute_update} transaction. Smaller scoped
|
372
|
+
# statements, such as an OLTP workload, should prefer using
|
373
|
+
# {Transaction#execute_update}.
|
374
|
+
#
|
375
|
+
# That said, Partitioned DML is not a drop-in replacement for standard
|
376
|
+
# DML used in {Transaction#execute_update}.
|
377
|
+
#
|
378
|
+
# * The DML statement must be fully-partitionable. Specifically, the
|
379
|
+
# statement must be expressible as the union of many statements which
|
380
|
+
# each access only a single row of the table.
|
381
|
+
# * The statement is not applied atomically to all rows of the table.
|
382
|
+
# Rather, the statement is applied atomically to partitions of the
|
383
|
+
# table, in independent internal transactions. Secondary index rows
|
384
|
+
# are updated atomically with the base table rows.
|
385
|
+
# * Partitioned DML does not guarantee exactly-once execution semantics
|
386
|
+
# against a partition. The statement will be applied at least once to
|
387
|
+
# each partition. It is strongly recommended that the DML statement
|
388
|
+
# should be idempotent to avoid unexpected results. For instance, it
|
389
|
+
# is potentially dangerous to run a statement such as `UPDATE table
|
390
|
+
# SET column = column + 1` as it could be run multiple times against
|
391
|
+
# some rows.
|
392
|
+
# * The partitions are committed automatically - there is no support for
|
393
|
+
# Commit or Rollback. If the call returns an error, or if the client
|
394
|
+
# issuing the DML statement dies, it is possible that some rows had
|
395
|
+
# the statement executed on them successfully. It is also possible
|
396
|
+
# that statement was never executed against other rows.
|
397
|
+
# * If any error is encountered during the execution of the partitioned
|
398
|
+
# DML operation (for instance, a UNIQUE INDEX violation, division by
|
399
|
+
# zero, or a value that cannot be stored due to schema constraints),
|
400
|
+
# then the operation is stopped at that point and an error is
|
401
|
+
# returned. It is possible that at this point, some partitions have
|
402
|
+
# been committed (or even committed multiple times), and other
|
403
|
+
# partitions have not been run at all.
|
404
|
+
#
|
405
|
+
# Given the above, Partitioned DML is good fit for large, database-wide,
|
406
|
+
# operations that are idempotent, such as deleting old rows from a very
|
407
|
+
# large table.
|
408
|
+
#
|
409
|
+
# @param [String] sql The Partitioned DML statement string. See [Query
|
410
|
+
# syntax](https://cloud.google.com/spanner/docs/query-syntax).
|
411
|
+
#
|
412
|
+
# The Partitioned DML statement string can contain parameter
|
413
|
+
# placeholders. A parameter placeholder consists of "@" followed by
|
414
|
+
# the parameter name. Parameter names consist of any combination of
|
415
|
+
# letters, numbers, and underscores.
|
416
|
+
# @param [Hash] params Parameters for the Partitioned DML statement
|
417
|
+
# string. The parameter placeholders, minus the "@", are the the hash
|
418
|
+
# keys, and the literal values are the hash values. If the query
|
419
|
+
# string contains something like "WHERE id > @msg_id", then the params
|
420
|
+
# must contain something like `:msg_id => 1`.
|
421
|
+
#
|
422
|
+
# Ruby types are mapped to Spanner types as follows:
|
423
|
+
#
|
424
|
+
# | Spanner | Ruby | Notes |
|
425
|
+
# |-------------|----------------|---|
|
426
|
+
# | `BOOL` | `true`/`false` | |
|
427
|
+
# | `INT64` | `Integer` | |
|
428
|
+
# | `FLOAT64` | `Float` | |
|
429
|
+
# | `STRING` | `String` | |
|
430
|
+
# | `DATE` | `Date` | |
|
431
|
+
# | `TIMESTAMP` | `Time`, `DateTime` | |
|
432
|
+
# | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
|
433
|
+
# | `ARRAY` | `Array` | Nested arrays are not supported. |
|
434
|
+
# | `STRUCT` | `Hash`, {Data} | |
|
435
|
+
#
|
436
|
+
# See [Data
|
437
|
+
# types](https://cloud.google.com/spanner/docs/data-definition-language#data_types).
|
438
|
+
#
|
439
|
+
# See [Data Types - Constructing a
|
440
|
+
# STRUCT](https://cloud.google.com/spanner/docs/data-types#constructing-a-struct).
|
441
|
+
# @param [Hash] types Types of the SQL parameters in `params`. It is not
|
442
|
+
# always possible for Cloud Spanner to infer the right SQL type from a
|
443
|
+
# value in `params`. In these cases, the `types` hash can be used to
|
444
|
+
# specify the exact SQL type for some or all of the SQL query
|
445
|
+
# parameters.
|
446
|
+
#
|
447
|
+
# The keys of the hash should be query string parameter placeholders,
|
448
|
+
# minus the "@". The values of the hash should be Cloud Spanner type
|
449
|
+
# codes from the following list:
|
450
|
+
#
|
451
|
+
# * `:BOOL`
|
452
|
+
# * `:BYTES`
|
453
|
+
# * `:DATE`
|
454
|
+
# * `:FLOAT64`
|
455
|
+
# * `:INT64`
|
456
|
+
# * `:STRING`
|
457
|
+
# * `:TIMESTAMP`
|
458
|
+
# * `Array` - Lists are specified by providing the type code in an
|
459
|
+
# array. For example, an array of integers are specified as
|
460
|
+
# `[:INT64]`.
|
461
|
+
# * {Fields} - Nested Structs are specified by providing a Fields
|
462
|
+
# object.
|
463
|
+
# @return [Integer] The lower bound number of rows that were modified.
|
464
|
+
#
|
465
|
+
# @example
|
466
|
+
# require "google/cloud/spanner"
|
467
|
+
#
|
468
|
+
# spanner = Google::Cloud::Spanner.new
|
469
|
+
# db = spanner.client "my-instance", "my-database"
|
470
|
+
#
|
471
|
+
# row_count = db.execute_partition_update \
|
472
|
+
# "UPDATE users SET friends = NULL WHERE active = false"
|
473
|
+
#
|
474
|
+
# @example Query using query parameters:
|
475
|
+
# require "google/cloud/spanner"
|
476
|
+
#
|
477
|
+
# spanner = Google::Cloud::Spanner.new
|
478
|
+
# db = spanner.client "my-instance", "my-database"
|
479
|
+
#
|
480
|
+
# row_count = db.execute_partition_update \
|
481
|
+
# "UPDATE users SET friends = NULL WHERE active = @active",
|
482
|
+
# params: { active: false }
|
483
|
+
#
|
484
|
+
def execute_partition_update sql, params: nil, types: nil
|
485
|
+
ensure_service!
|
486
|
+
|
487
|
+
params, types = Convert.to_input_params_and_types params, types
|
488
|
+
|
489
|
+
results = nil
|
490
|
+
@pool.with_session do |session|
|
491
|
+
results = session.execute_query \
|
492
|
+
sql, params: params, types: types,
|
493
|
+
transaction: pdml_transaction(session)
|
494
|
+
end
|
495
|
+
# Stream all PartialResultSet to get ResultSetStats
|
496
|
+
results.rows.to_a
|
497
|
+
# Raise an error if there is not a row count returned
|
498
|
+
if results.row_count.nil?
|
499
|
+
raise Google::Cloud::InvalidArgumentError,
|
500
|
+
"Partitioned DML statement is invalid."
|
501
|
+
end
|
502
|
+
results.row_count
|
503
|
+
end
|
504
|
+
alias execute_pdml execute_partition_update
|
316
505
|
|
317
506
|
##
|
318
507
|
# Read rows from a database table, as a simple alternative to
|
319
|
-
# {#
|
508
|
+
# {#execute_query}.
|
320
509
|
#
|
321
510
|
# @param [String] table The name of the table in the database to be
|
322
511
|
# read.
|
@@ -764,7 +953,7 @@ module Google
|
|
764
953
|
# db = spanner.client "my-instance", "my-database"
|
765
954
|
#
|
766
955
|
# db.transaction do |tx|
|
767
|
-
# results = tx.
|
956
|
+
# results = tx.execute_query "SELECT * FROM users"
|
768
957
|
#
|
769
958
|
# results.rows.each do |row|
|
770
959
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -883,7 +1072,7 @@ module Google
|
|
883
1072
|
# db = spanner.client "my-instance", "my-database"
|
884
1073
|
#
|
885
1074
|
# db.snapshot do |snp|
|
886
|
-
# results = snp.
|
1075
|
+
# results = snp.execute_query "SELECT * FROM users"
|
887
1076
|
#
|
888
1077
|
# results.rows.each do |row|
|
889
1078
|
# puts "User #{row[:id]} is #{row[:name]}"
|
@@ -1012,7 +1201,7 @@ module Google
|
|
1012
1201
|
# types: users_types
|
1013
1202
|
#
|
1014
1203
|
def fields_for table
|
1015
|
-
|
1204
|
+
execute_query("SELECT * FROM #{table} WHERE 1 = 0").fields
|
1016
1205
|
end
|
1017
1206
|
|
1018
1207
|
##
|
@@ -1159,6 +1348,11 @@ module Google
|
|
1159
1348
|
}.delete_if { |_, v| v.nil? })))
|
1160
1349
|
end
|
1161
1350
|
|
1351
|
+
def pdml_transaction session
|
1352
|
+
pdml_tx_grpc = @project.service.create_pdml session.path
|
1353
|
+
Google::Spanner::V1::TransactionSelector.new id: pdml_tx_grpc.id
|
1354
|
+
end
|
1355
|
+
|
1162
1356
|
##
|
1163
1357
|
# Check for valid snapshot arguments
|
1164
1358
|
def validate_snapshot_args! strong: nil,
|