google-cloud-spanner 1.6.4 → 1.7.1
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 +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,
|