spanner/spec/spanner_samples_spec.rb (1,380 lines of code) (raw):

# Copyright 2017 Google, Inc # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require_relative "./spec_helper" describe "Google Cloud Spanner API samples" do before :each do cleanup_database_resources end after :each do cleanup_database_resources cleanup_instance_resources end example "create_instance" do instance_id = "rb-test-#{seed}" @created_instance_ids << instance_id capture do create_instance project_id: @project_id, instance_id: instance_id end expect(captured_output).to include( "Waiting for create instance operation to complete" ) expect(captured_output).to include( "Created instance #{instance_id}" ) end example "create_instance_with_processing_units" do instance_id = "rb-test-pu-#{seed}" @created_instance_ids << instance_id capture do create_instance_with_processing_units project_id: @project_id, instance_id: instance_id end expect(captured_output).to include( "Waiting for creating instance operation to complete" ) expect(captured_output).to include( "Instance #{instance_id} has 500 processing units." ) end example "create_database" do expect(@instance.databases.map(&:database_id)).not_to include @database_id capture do create_database project_id: @project_id, instance_id: @instance.instance_id, database_id: @database_id end expect(captured_output).to include( "Waiting for create database operation to complete" ) expect(captured_output).to include( "Created database #{@database_id} on instance #{@instance.instance_id}" ) @test_database = @instance.database @database_id expect(@test_database).not_to be nil data_definition_statements = @test_database.ddl expect(data_definition_statements.size).to eq 2 expect(data_definition_statements).to include(match "CREATE TABLE Singers") expect(data_definition_statements).to include(match "CREATE TABLE Albums") end example "create_database_with_version_retention_period" do expect(@instance.databases.map(&:database_id)).not_to include @database_id capture do create_database_with_version_retention_period project_id: @project_id, instance_id: @instance.instance_id, database_id: @database_id end expect(captured_output).to include( "Waiting for create database operation to complete" ) expect(captured_output).to include( "Created database #{@database_id} on instance #{@instance.instance_id}" ) expect(captured_output).to include( "\tVersion retention period: 7d" ) @test_database = @instance.database @database_id expect(@test_database).not_to be nil data_definition_statements = @test_database.ddl expect(data_definition_statements.size).to eq 3 expect(data_definition_statements).to include(match "CREATE TABLE Singers") expect(data_definition_statements).to include(match "CREATE TABLE Albums") expect(data_definition_statements).to include(match "version_retention_period = '7d'") end example "create_database_with_encryption_key" do expect(@instance.databases.map(&:database_id)).not_to include @database_id kms_key_name = "projects/#{@project_id}/locations/us-central1/keyRings/spanner-test-keyring/cryptoKeys/spanner-test-cmek" capture do create_database_with_encryption_key project_id: @project_id, instance_id: @instance.instance_id, database_id: @database_id, kms_key_name: kms_key_name end expect(captured_output).to include( "Database #{@database_id} created with encryption key #{kms_key_name}" ) end example "create database with multiple KMS keys" do capture do create_database_with_multiple_kms_keys project_id: @project_id, instance_id: @mr_instance_id, database_id: @database_id, kms_key_names: @kms_key_names end expect(captured_output).to include("Database #{@database_id}") expect(captured_output).to include("#{@kms_key_names[0]}") expect(captured_output).to include("#{@kms_key_names[1]}") expect(captured_output).to include("#{@kms_key_names[2]}") end example "create table with timestamp column" do database = create_singers_albums_database expect(@instance.databases.map(&:database_id)).to include @database_id capture do create_table_with_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: @database_id end expect(captured_output).to include( "Waiting for update database operation to complete" ) expect(captured_output).to include( "Created table Performances in #{@database_id}" ) data_definition_statements = database.ddl force: true expect(data_definition_statements.size).to eq 3 expect(data_definition_statements.last).to include "CREATE TABLE Performances" end example "insert data" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 0 expect(client.execute("SELECT * FROM Albums").rows.count).to eq 0 expect { insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("Inserted data\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 5 expect(singers.find { |s| s[:FirstName] == "Catalina" }).not_to be nil albums = client.execute("SELECT * FROM Albums").rows.to_a expect(albums.count).to eq 5 expect(albums.find { |s| s[:AlbumTitle] == "Go, Go, Go" }).not_to be nil end example "insert data with timestamp column" do database = create_singers_albums_database create_performances_table # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Performances").rows.count).to eq 0 expect { insert_data_with_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("Inserted data\n").to_stdout performances = client.execute("SELECT * FROM Performances").rows.to_a expect(performances.count).to eq 3 end example "query data" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 1 Total Junk" expect(captured_output).to include "1 2 Go, Go, Go" expect(captured_output).to include "2 1 Green" expect(captured_output).to include "2 2 Forever Hold Your Peace" expect(captured_output).to include "2 3 Terrified" end example "query with struct" do database = create_singers_albums_database capture do write_struct_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_with_struct project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to match(/6/) end example "query with array of struct" do database = create_singers_albums_database capture do write_struct_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_with_array_of_struct project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "8" expect(captured_output).to include "7" expect(captured_output).to include "6" end example "query struct field" do database = create_singers_albums_database capture do write_struct_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_struct_field project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to match(/6/) end example "query nested struct field" do database = create_singers_albums_database capture do write_struct_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_nested_struct_field project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to match(/6\nImagination\n9\nImagination/) end example "query data with timestamp column" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id update_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id add_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id update_data_with_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_data_with_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to match(/1 1 100000 \d+/) end example "read data" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do read_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 1 Total Junk" expect(captured_output).to include "1 2 Go, Go, Go" expect(captured_output).to include "2 1 Green" expect(captured_output).to include "2 2 Forever Hold Your Peace" expect(captured_output).to include "2 3 Terrified" end example "delete data" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do delete_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do read_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 0 expect(client.execute("SELECT * FROM Albums").rows.count).to eq 0 end example "read stale data" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do read_stale_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).not_to include "1 1 Total Junk" expect(captured_output).not_to include "1 2 Go, Go, Go" expect(captured_output).not_to include "2 1 Green" expect(captured_output).not_to include "2 2 Forever Hold Your Peace" expect(captured_output).not_to include "2 3 Terrified" sleep 16 # read_stale_data expects staleness of at least 15 seconds capture do read_stale_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 1 Total Junk" expect(captured_output).to include "1 2 Go, Go, Go" expect(captured_output).to include "2 1 Green" expect(captured_output).to include "2 2 Forever Hold Your Peace" expect(captured_output).to include "2 3 Terrified" end example "create index" do database = create_singers_albums_database expect(database.ddl(force: true).join).not_to include( "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)" ) capture do create_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Waiting for database update to complete" expect(captured_output).to include "Added the AlbumsByAlbumTitle index" expect(database.ddl(force: true).join).to include( "CREATE INDEX AlbumsByAlbumTitle ON Albums(AlbumTitle)" ) end example "create storing index" do database = create_singers_albums_database # Ignore the following capture block capture do # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(database.ddl(force: true).join).not_to include( "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)" ) capture do create_storing_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Waiting for database update to complete" expect(captured_output).to include "Added the AlbumsByAlbumTitle2 storing index" expect(database.ddl(force: true).join).to include( "CREATE INDEX AlbumsByAlbumTitle2 ON Albums(AlbumTitle) STORING (MarketingBudget)" ) end example "add column" do database = create_singers_albums_database expect(database.ddl(force: true).join).not_to include( "MarketingBudget INT64" ) capture do add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(database.ddl(force: true).join).to include( "MarketingBudget INT64" ) end example "add column timestamp column" do database = create_singers_albums_database expect(database.ddl(force: true).join).not_to include( "MarketingBudget INT64" ) capture do add_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(database.ddl(force: true).join).to include( "LastUpdateTime TIMESTAMP" ) end example "update data" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end # Add MarketingBudget column (re-use add_column to add) capture do add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end albums = client.execute("SELECT * FROM Albums").rows.map(&:to_h) expect(albums).to include( SingerId: 1, AlbumId: 1, AlbumTitle: "Total Junk", MarketingBudget: nil ) capture do update_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Updated data" albums = client.execute("SELECT * FROM Albums").rows.map(&:to_h) expect(albums).to include( SingerId: 1, AlbumId: 1, AlbumTitle: "Total Junk", MarketingBudget: 100_000 ) end example "update data with timestamp column" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add Timestamp column add_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end albums = client.execute("SELECT * FROM Albums").rows.map(&:to_h) expect(albums).to include( SingerId: 1, AlbumId: 1, AlbumTitle: "Total Junk", MarketingBudget: nil, LastUpdateTime: nil ) capture do update_data_with_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Updated data" albums = client.execute("SELECT * FROM Albums").rows.map(&:to_h) expect(albums).not_to include( LastUpdateTime: nil ) end example "query data with new column" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add data to MarketingBudget column (re-use update_data to populate) update_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_data_with_new_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 1 100000" expect(captured_output).to include "1 2" expect(captured_output).to include "2 1" expect(captured_output).to include "2 2 500000" expect(captured_output).to include "2 3" end example "read/write transaction (successful transfer)" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Second Album(2, 2) needs at least $200,000 to transfer successfully # to Album(1, 1). This should transfer successfully. client.commit do |c| c.update "Albums", [ { SingerId: 1, AlbumId: 1, MarketingBudget: 100_000 }, { SingerId: 2, AlbumId: 2, MarketingBudget: 500_000 } ] end end capture do read_write_transaction project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Transaction complete" first_album = client.read("Albums", [:MarketingBudget], keys: [[1, 1]]).rows.first second_album = client.read("Albums", [:MarketingBudget], keys: [[2, 2]]).rows.first expect(first_album[:MarketingBudget]).to eq 300_000 expect(second_album[:MarketingBudget]).to eq 300_000 end example "read/write transaction (not enough funds)" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Second Album(2, 2) needs at least $200,000 to transfer successfully # to Album(1, 1). Without enough funds, an exception should be raised. client.commit do |c| c.update "Albums", [ { SingerId: 1, AlbumId: 1, MarketingBudget: 100_000 }, { SingerId: 2, AlbumId: 2, MarketingBudget: 199_999 } ] end end expect { read_write_transaction project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to raise_error("The second album does not have enough funds to transfer") end example "query data with index" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add index on Albums(AlbumTitle) (re-use create_index to add) create_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_data_with_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "2 Go, Go, Go" expect(captured_output).to include "2 Forever Hold Your Peace" end example "read data with index" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add index on Albums(AlbumTitle) (re-use create_index to add) create_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do read_data_with_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 Total Junk" expect(captured_output).to include "2 Forever Hold Your Peace" end example "read data with storing index" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add index on Albums(AlbumTitle) (re-use create_index to add) create_storing_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do read_data_with_storing_index project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 Total Junk" expect(captured_output).to include "2 Forever Hold Your Peace" end example "read only transaction" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do read_only_transaction project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 Total Junk 1" expect(captured_output).to include "2 Forever Hold Your Peace 2" end example "batch client read partitions across threads" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do spanner_batch_client project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Total Records: 5" end example "insert data using dml" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 5 expect { insert_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("1 record inserted.\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 6 expect(singers.find { |s| s[:FirstName] == "Virginia" }).not_to be nil end example "update using dml" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # First Album(1, 1) set MarketingBudget to $300,000 client.commit do |c| c.update "Albums", [ { SingerId: 1, AlbumId: 1, MarketingBudget: 300_000 } ] end end capture do update_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "1 record updated." first_album = client.read("Albums", [:MarketingBudget], keys: [[1, 1]]).rows.first expect(first_album[:MarketingBudget]).to eq 600_000 end example "delete data using dml" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 5 expect { delete_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("1 record deleted.\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 4 expect(singers.find { |s| s[:FirstName] == "Alice" }).to be_nil end example "update data using dml with timestamp column" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add Timestamp column add_timestamp_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end original_timestamp = client.read("Albums", [:LastUpdateTime], keys: [[1, 1]]).rows.first.to_h capture do update_using_dml_with_timestamp project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "2 records updated." updated_timestamp = client.read("Albums", [:LastUpdateTime], keys: [[1, 1]]).rows.first.to_h expect(original_timestamp[:LastUpdateTime].to_i < updated_timestamp[:LastUpdateTime].to_i).to be true end example "write and read data using dml" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 5 expect { write_and_read_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output(/1 record updated.\nTimothy Campbell\n/).to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 6 end example "update data using dml with struct" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Insert single Singer record to be updated write_and_read_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 6 expect { update_using_dml_with_struct project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("1 record updated.\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 6 expect(singers.find { |s| s[:LastName] == "Grant" }).not_to be nil end example "insert multiple records using dml" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 5 expect { write_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("14 records inserted.\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 19 expect(singers.find { |s| s[:FirstName] == "Dylan" }).not_to be nil expect { query_with_parameter project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("12 Melissa Garcia\n").to_stdout end example "write with transaction using dml (successful transfer)" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # First Album(2, 2) needs at least $200,000 to transfer successfully # to Album(1, 1). This should transfer successfully. client.commit do |c| c.update "Albums", [ { SingerId: 1, AlbumId: 1, MarketingBudget: 100_000 }, { SingerId: 2, AlbumId: 2, MarketingBudget: 500_000 } ] end end capture do write_with_transaction_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end first_album = client.read("Albums", [:MarketingBudget], keys: [[1, 1]]).rows.first second_album = client.read("Albums", [:MarketingBudget], keys: [[2, 2]]).rows.first expect(first_album[:MarketingBudget]).to eq 300_000 expect(second_album[:MarketingBudget]).to eq 300_000 end example "update data using partioned dml" do database = create_singers_albums_database client = @spanner.client @instance.instance_id, database.database_id # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do update_using_partitioned_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end first_album = client.read("Albums", [:MarketingBudget], keys: [[2, 1]]).rows.first second_album = client.read("Albums", [:MarketingBudget], keys: [[2, 2]]).rows.first third_album = client.read("Albums", [:MarketingBudget], keys: [[2, 3]]).rows.first expect(first_album[:MarketingBudget]).to eq 100_000 expect(second_album[:MarketingBudget]).to eq 100_000 expect(third_album[:MarketingBudget]).to eq 100_000 end example "delete multiple records using dml" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Insert additinal multiple records into Singers table write_using_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 19 expect { delete_using_partitioned_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("14 records deleted.\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 5 expect(singers.find { |s| s[:FirstName] == "Melissa" }).to be_nil end example "insert and update a record using batch dml" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id # Add MarketingBudget column (re-use add_column to add) add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Albums").rows.count).to eq 5 expect { update_using_batch_dml project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("Executed 2 SQL statements using Batch DML.\n").to_stdout albums = client.execute("SELECT * FROM Albums").rows.to_a expect(albums.count).to eq 6 expect(albums.find { |s| s[:AlbumTitle] == "Test Album Title" }).not_to be nil album = client.read("Albums", [:MarketingBudget], keys: [[1, 3]]).rows.first expect(album[:MarketingBudget]).to eq 20_000 end example "create table with supported datatypes columns" do database = create_singers_albums_database expect(@instance.databases.map(&:database_id)).to include @database_id capture do create_table_with_datatypes project_id: @project_id, instance_id: @instance.instance_id, database_id: @database_id end expect(captured_output).to include( "Waiting for update database operation to complete" ) expect(captured_output).to include( "Created table Venues in #{@database_id}" ) data_definition_statements = database.ddl force: true expect(data_definition_statements.size).to eq 3 expect(data_definition_statements.last).to include "CREATE TABLE Venues" end example "insert datatypes data" do database = create_singers_albums_database create_venues_table client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Venues").rows.count).to eq 0 expect { write_datatypes_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("Inserted data\n").to_stdout venues = client.execute("SELECT * FROM Venues").rows.to_a expect(venues.count).to eq 3 end example "query data with datatypes" do database = create_singers_albums_database create_venues_table # Ignore the following capture block capture do write_datatypes_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_with_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "19 Venue 19 2020-11-01" expect(captured_output).to include "42 Venue 42 2020-10-01" capture do query_with_bool project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "19 Venue 19 true" capture do query_with_bytes project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 Venue 4" capture do query_with_date project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 Venue 4 2018-09-02" expect(captured_output).to include "42 Venue 42 2018-10-01" capture do query_with_float project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 Venue 4 0.8" expect(captured_output).to include "19 Venue 19 0.9" capture do query_with_int project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "19 Venue 19 6300" expect(captured_output).to include "42 Venue 42 3000" capture do query_with_string project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "42 Venue 42" capture do query_with_timestamp project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 Venue 4" expect(captured_output).to include "19 Venue 19" expect(captured_output).to include "42 Venue 42" end example "query data with query options" do database = create_singers_albums_database create_venues_table # Ignore the following capture block capture do write_datatypes_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do query_with_query_options project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 Venue 4" expect(captured_output).to include "19 Venue 19" expect(captured_output).to include "42 Venue 42" capture do create_client_with_query_options project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 Venue 4" expect(captured_output).to include "19 Venue 19" expect(captured_output).to include "42 Venue 42" end example "query data with numeric column" do database = create_singers_albums_database create_venues_table # Ignore the following capture block capture do write_datatypes_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do add_numeric_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Added the Revenue as a numeric column in Venues table" capture do update_data_with_numeric_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "Updated data" capture do query_with_numeric_parameter project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "4 0.35e5" end example "write data with array types and read" do database = create_boxes_database capture do write_read_bool_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output.split("\n")).to match_array(["true", "false", "true"]) capture do write_read_empty_int64_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "true" capture do write_read_null_int64_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output.split("\n")).to match_array(["true", "true", "true"]) capture do write_read_int64_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "10" expect(captured_output).to include "11" expect(captured_output).to include "12" capture do write_read_empty_float64_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "true" capture do write_read_null_float64_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output.split("\n")).to match_array(["true", "true", "true"]) capture do write_read_float64_array project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to include "10.001" expect(captured_output).to include "11.1212" expect(captured_output).to include "104.4123101" end xexample "create backup" do cleanup_backup_resources database = create_database_with_data client = @spanner.client @instance.instance_id, database.database_id version_time = client.execute("SELECT CURRENT_TIMESTAMP() as timestamp").rows.first[:timestamp] capture do create_backup project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id, backup_id: @backup_id, version_time: version_time end expect(captured_output).to include( "Backup operation in progress" ) expect(captured_output).to match( /Backup #{@backup_id} of size \d+ bytes was created at/ ) expect(captured_output).to match( /for version of database at/ ) @test_backup = @instance.backup @backup_id expect(@test_backup).not_to be nil end xexample "copy backup" do cleanup_backup_resources create_backup_with_data backup_id = "test_bu_copied" capture do copy_backup project_id: @project_id, instance_id: @instance.instance_id, backup_id: backup_id, source_backup_id: @backup_id end expect(captured_output).to include( "Copy backup operation in progress" ) expect(captured_output).to match( /Backup #{backup_id} of size \d+ bytes was copied at/ ) test_backup = @instance.backup backup_id expect(test_backup).not_to be nil # Clean up copied backup test_backup&.delete test_backup = @instance.backup backup_id expect(test_backup).to be_nil end xexample "copy backup with multiple KMS keys" do create_backup_with_data @mr_instance backup_id = "test_mr_cmek_copied" capture do copy_backup_with_multiple_kms_keys project_id: @project_id, instance_id: @mr_instance.instance_id, backup_id: backup_id, source_backup_id: @backup_id, kms_key_names: @kms_key_names end expect(captured_output).to include("Copy backup operation in progress") expect(captured_output).to match( /Backup #{backup_id} of size \d+ bytes was copied at/ ) expect(captured_output).to include("#{@kms_key_names[0]}") expect(captured_output).to include("#{@kms_key_names[1]}") expect(captured_output).to include("#{@kms_key_names[2]}") base_backup = @mr_instance.backup @backup_id test_backup = @mr_instance.backup backup_id expect(base_backup).not_to be nil expect(test_backup).not_to be nil # Clean up backups base_backup&.delete test_backup&.delete end xexample "create backup with encryption key" do cleanup_backup_resources database = create_database_with_data client = @spanner.client @instance.instance_id, database.database_id kms_key_name = "projects/#{@project_id}/locations/us-central1/keyRings/spanner-test-keyring/cryptoKeys/spanner-test-cmek" capture do create_backup_with_encryption_key project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id, backup_id: @backup_id, kms_key_name: kms_key_name end expect(captured_output).to include( "Backup operation in progress" ) expect(captured_output).to match( /Backup #{@backup_id} of size \d+ bytes was created at/ ) expect(captured_output).to match( /using encryption key #{kms_key_name}/ ) @test_backup = @instance.backup @backup_id expect(@test_backup).not_to be nil end xexample "create backup with multiple KMS keys" do database = create_database_with_data @mr_instance backup_id = "test_mr_cmek_created" capture do create_backup_with_multiple_kms_keys project_id: @project_id, instance_id: @mr_instance.instance_id, database_id: database.database_id, backup_id: backup_id, kms_key_names: @kms_key_names end expect(captured_output).to include("Backup operation in progress") expect(captured_output).to match( /Backup #{backup_id} of size \d+ bytes was created at/ ) expect(captured_output).to include("#{@kms_key_names[0]}") expect(captured_output).to include("#{@kms_key_names[1]}") expect(captured_output).to include("#{@kms_key_names[2]}") test_backup = @mr_instance.backup backup_id expect(test_backup).not_to be nil # Clean up test_backup&.delete end xexample "restore backup" do backup = create_backup_with_data database = @instance.database @database_id capture do restore_backup project_id: @project_id, instance_id: @instance.instance_id, database_id: @restored_database_id, backup_id: backup.backup_id end expect(captured_output).to include( "Waiting for restore backup operation to complete" ) expect(captured_output).to match( /Database #{database.path} was restored to #{@restored_database_id} from backup #{backup.path} with version time/ ) @test_database = @instance.database @restored_database_id expect(@test_database).not_to be nil end xexample "restore database with encryption key" do backup = create_backup_with_data database = @instance.database @database_id kms_key_name = "projects/#{@project_id}/locations/us-central1/keyRings/spanner-test-keyring/cryptoKeys/spanner-test-cmek" capture do restore_database_with_encryption_key project_id: @project_id, instance_id: @instance.instance_id, database_id: @restored_database_id, backup_id: backup.backup_id, kms_key_name: kms_key_name end expect(captured_output).to include( "Waiting for restore backup operation to complete" ) expect(captured_output).to match( /Database #{database.path} was restored to #{@restored_database_id} from backup #{backup.path} using encryption key #{kms_key_name}/ ) @test_database = @instance.database @restored_database_id expect(@test_database).not_to be nil end xexample "restore database with multiple KMS keys" do backup = create_backup_with_data @mr_instance database = @mr_instance.database @database_id capture do restore_database_with_multiple_kms_keys project_id: @project_id, instance_id: @mr_instance_id, database_id: @restored_database_id, backup_id: backup.backup_id, kms_key_names: @kms_key_names end expect(captured_output).to include( "Waiting for restore backup operation to complete" ) expect(captured_output).to include("#{@kms_key_names[0]}") expect(captured_output).to include("#{@kms_key_names[1]}") expect(captured_output).to include("#{@kms_key_names[2]}") @test_database = @mr_instance.database @restored_database_id expect(@test_database).not_to be nil test_backup = @mr_instance.backup backup_id expect(test_backup).not_to be nil # Clean up backup test_backup&.delete end xexample "cancel backup operation" do database = create_database_with_data cancel_backup_id = "cancel_#{@backup_id}" capture do create_backup_cancel project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id, backup_id: cancel_backup_id end expect(captured_output).to include( "Backup operation in progress" ) expect(captured_output).to include( "#{cancel_backup_id} creation job cancelled" ) @test_backup = @instance.backup cancel_backup_id expect(@test_backup).to be nil end xexample "list backup operations" do backup = create_backup_with_data capture do list_backup_operations project_id: @project_id, instance_id: @instance.instance_id, database_id: @database_id end expect(captured_output).to match( /Backup #{backup.path} on database #{@database_id} is \d+% complete/ ) @test_backup = @instance.backup @backup_id expect(@test_backup).not_to be nil end xexample "list copy backup operations" do backup = create_backup_with_data copied_backup = create_copy_backup capture do list_copy_backup_operations project_id: @project_id, instance_id: @instance.instance_id, backup_id: @backup_id end expect(captured_output).to match( /Backup #{copied_backup.path} on source backup #{@backup_id} is \d+% complete/ ) @test_backup = @instance.backup @backup_id expect(@test_backup).not_to be nil end xexample "list database operations" do database = restore_database_from_backup capture do list_database_operations project_id: @project_id, instance_id: @instance.instance_id end expect(captured_output).to match( /List database operations with optimized database filter found \d+ jobs/ ) end xexample "list backups with various filters" do backup = create_backup_with_data capture do list_backups project_id: @project_id, instance_id: @instance.instance_id, backup_id: @backup_id, database_id: backup.database_id end # Segregate each list backup filters output. output_segments = captured_output.split(/(All backups)/) .reject(&:empty?) .each_slice(2) .map(&:join) expect(output_segments.shift).to include("All backups", backup.path) expect(output_segments.shift).to include( "All backups with backup name containing", "\"#{backup.backup_id}\":\n#{backup.path}" ) expect(output_segments.shift).to include( "All backups for databases with a name containing", "\"#{backup.database_id}\":\n#{backup.path}" ) expect(output_segments.shift).to include( "All backups that expire before a timestamp", backup.backup_id ) expect(output_segments.shift).to include( "All backups with a size greater than 500 bytes", backup.backup_id ) expect(output_segments.shift).to include( "All backups that were created after a timestamp that are also ready", backup.backup_id ) expect(output_segments.shift).to include( "All backups with pagination", backup.backup_id ) @test_backup = @instance.backup @backup_id expect(@test_backup).not_to be nil end xexample "delete backup" do backup = create_backup_with_data capture do delete_backup project_id: @project_id, instance_id: @instance.instance_id, backup_id: backup.backup_id end expect(captured_output).to include( "Backup #{backup.backup_id} deleted" ) @test_backup = @instance.backup @backup_id expect(@test_backup).to be nil end xexample "update backup" do backup = create_backup_with_data capture do update_backup project_id: @project_id, instance_id: @instance.instance_id, backup_id: backup.backup_id end expect(captured_output).to include( "Expiration time updated: #{backup.expire_time + 2_592_000}" ) end example "set custom timeout and retry settings" do database = create_singers_albums_database # Ignore the following capture block capture do # Insert Singers and Albums (re-use insert_data sample to populate) insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end client = @spanner.client @instance.instance_id, database.database_id expect(client.execute("SELECT * FROM Singers").rows.count).to eq 5 expect { set_custom_timeout_and_retry project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id }.to output("1 record inserted.\n").to_stdout singers = client.execute("SELECT * FROM Singers").rows.to_a expect(singers.count).to eq 6 expect(singers.find { |s| s[:FirstName] == "Virginia" }).not_to be nil end example "get commit stats" do database = create_singers_albums_database capture do insert_data project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id add_column project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end capture do commit_stats project_id: @project_id, instance_id: @instance.instance_id, database_id: database.database_id end expect(captured_output).to match(/Updated data with \d+ mutations/) end end