diff --git a/samples/samples/README.rst b/samples/samples/README.rst index b0573c249b..143402fde5 100644 --- a/samples/samples/README.rst +++ b/samples/samples/README.rst @@ -1,4 +1,3 @@ - .. This file is automatically generated. Do not edit this file directly. Google Cloud Spanner Python Samples @@ -15,12 +14,10 @@ This directory contains samples for Google Cloud Spanner. `Google Cloud Spanner` .. _Google Cloud Spanner: https://cloud.google.com/spanner/docs - Setup ------------------------------------------------------------------------------- - Authentication ++++++++++++++ @@ -31,9 +28,6 @@ credentials for applications. .. _Authentication Getting Started Guide: https://cloud.google.com/docs/authentication/getting-started - - - Install Dependencies ++++++++++++++++++++ @@ -48,7 +42,7 @@ Install Dependencies .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup -#. Create a virtualenv. Samples are compatible with Python 3.6+. +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. .. code-block:: bash @@ -64,15 +58,9 @@ Install Dependencies .. _pip: https://pip.pypa.io/ .. _virtualenv: https://virtualenv.pypa.io/ - - - - - Samples ------------------------------------------------------------------------------- - Snippets +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -88,10 +76,32 @@ To run this sample: $ python snippets.py - usage: snippets.py [-h] [--database-id DATABASE_ID] instance_id - {create_instance,create_database,insert_data,delete_data,query_data,read_data,read_stale_data,add_column,update_data,query_data_with_new_column,read_write_transaction,read_only_transaction,add_index,query_data_with_index,read_data_with_index,add_storing_index,read_data_with_storing_index,create_table_with_timestamp,insert_data_with_timestamp,add_timestamp_column,update_data_with_timestamp,query_data_with_timestamp,write_struct_data,query_with_struct,query_with_array_of_struct,query_struct_field,query_nested_struct_field,insert_data_with_dml,update_data_with_dml,delete_data_with_dml,update_data_with_dml_timestamp,dml_write_read_transaction,update_data_with_dml_struct,insert_with_dml,query_data_with_parameter,write_with_dml_transaction,update_data_with_partitioned_dml,delete_data_with_partitioned_dml,update_with_batch_dml,create_table_with_datatypes,insert_datatypes_data,query_data_with_array,query_data_with_bool,query_data_with_bytes,query_data_with_date,query_data_with_float,query_data_with_int,query_data_with_string,query_data_with_timestamp_parameter,query_data_with_query_options,create_client_with_query_options} + {create_database,insert_data,query_data,read_data, + read_stale_data,add_column,update_data, + query_data_with_new_column,read_write_transaction, + read_only_transaction,add_index,query_data_with_index, + read_data_with_index,add_storing_index, + read_data_with_storing_index, + create_table_with_timestamp,insert_data_with_timestamp, + add_timestamp_column,update_data_with_timestamp, + query_data_with_timestamp,write_struct_data, + query_with_struct,query_with_array_of_struct, + query_struct_field,query_nested_struct_field, + insert_data_with_dml,update_data_with_dml, + delete_data_with_dml,update_data_with_dml_timestamp, + dml_write_read_transaction,update_data_with_dml_struct, + insert_with_dml,query_data_with_parameter, + write_with_dml_transaction, + update_data_with_partitioned_dml, + delete_data_with_partitioned_dml,update_with_batch_dml, + create_table_with_datatypes,insert_datatypes_data, + query_data_with_array,query_data_with_bool, + query_data_with_bytes,query_data_with_date, + query_data_with_float,query_data_with_int, + query_data_with_string, + query_data_with_timestamp_parameter} ... This application demonstrates how to do basic operations using Cloud @@ -101,15 +111,32 @@ To run this sample: positional arguments: instance_id Your Cloud Spanner instance ID. - {create_instance,create_database,insert_data,delete_data,query_data,read_data,read_stale_data,add_column,update_data,query_data_with_new_column,read_write_transaction,read_only_transaction,add_index,query_data_with_index,read_data_with_index,add_storing_index,read_data_with_storing_index,create_table_with_timestamp,insert_data_with_timestamp,add_timestamp_column,update_data_with_timestamp,query_data_with_timestamp,write_struct_data,query_with_struct,query_with_array_of_struct,query_struct_field,query_nested_struct_field,insert_data_with_dml,update_data_with_dml,delete_data_with_dml,update_data_with_dml_timestamp,dml_write_read_transaction,update_data_with_dml_struct,insert_with_dml,query_data_with_parameter,write_with_dml_transaction,update_data_with_partitioned_dml,delete_data_with_partitioned_dml,update_with_batch_dml,create_table_with_datatypes,insert_datatypes_data,query_data_with_array,query_data_with_bool,query_data_with_bytes,query_data_with_date,query_data_with_float,query_data_with_int,query_data_with_string,query_data_with_timestamp_parameter,query_data_with_query_options,create_client_with_query_options} - create_instance Creates an instance. + {create_database, insert_data, delete_data, query_data, read_data, + read_stale_data, add_column, update_data, query_data_with_new_column, + read_write_transaction, read_only_transaction, add_index, + query_data_with_index, read_data_with_index, add_storing_index, + read_data_with_storing_index, create_table_with_timestamp, + insert_data_with_timestamp, add_timestamp_column, + update_data_with_timestamp, query_data_with_timestamp, + write_struct_data, query_with_struct, query_with_array_of_struct, + query_struct_field, query_nested_struct_field, insert_data_with_dml, + update_data_with_dml, delete_data_with_dml, + update_data_with_dml_timestamp, dml_write_read_transaction, + update_data_with_dml_struct, insert_with_dml, query_data_with_parameter, + write_with_dml_transaction, update_data_with_partitioned_dml, + delete_data_with_partitioned_dml, update_with_batch_dml, + create_table_with_datatypes, insert_datatypes_data, + query_data_with_array, query_data_with_bool, query_data_with_bytes, + query_data_with_date, query_data_with_float, query_data_with_int, + query_data_with_string, query_data_with_timestamp_parameter} create_database Creates a database and tables for sample data. insert_data Inserts sample data into the given database. The database and table must already exist and can be created using `create_database`. delete_data Deletes sample data from the given database. The - database, table, and data must already exist and can - be created using `create_database` and `insert_data`. + database, table, and data must already exist and + can be created using `create_database` and + `insert_data`. query_data Queries sample data from the database using SQL. read_data Reads sample data from the database. read_stale_data Reads sample data from the database. The data is @@ -210,53 +237,59 @@ To run this sample: Deletes sample data from the database using a DML statement. update_data_with_dml_timestamp - Updates data with Timestamp from the database using a - DML statement. + Updates data with Timestamp from the database using + a DML statement. dml_write_read_transaction First inserts data then reads it from within a transaction using DML. update_data_with_dml_struct Updates data with a DML statement and STRUCT parameters. - insert_with_dml Inserts data with a DML statement into the database. + insert_with_dml Inserts data with a DML statement into the + database. query_data_with_parameter - Queries sample data from the database using SQL with a - parameter. + Queries sample data from the database using SQL + with a parameter. write_with_dml_transaction - Transfers part of a marketing budget from one album to - another. + Transfers part of a marketing budget from one + album to another. update_data_with_partitioned_dml - Update sample data with a partitioned DML statement. + Update sample data with a partitioned DML + statement. delete_data_with_partitioned_dml - Delete sample data with a partitioned DML statement. + Delete sample data with a partitioned DML + statement. update_with_batch_dml - Updates sample data in the database using Batch DML. + Updates sample data in the database using Batch + DML. create_table_with_datatypes Creates a table with supported dataypes. insert_datatypes_data Inserts data with supported datatypes into a table. query_data_with_array - Queries sample data using SQL with an ARRAY parameter. + Queries sample data using SQL with an ARRAY + parameter. query_data_with_bool - Queries sample data using SQL with a BOOL parameter. + Queries sample data using SQL with a BOOL + parameter. query_data_with_bytes - Queries sample data using SQL with a BYTES parameter. + Queries sample data using SQL with a BYTES + parameter. query_data_with_date - Queries sample data using SQL with a DATE parameter. + Queries sample data using SQL with a DATE + parameter. query_data_with_float Queries sample data using SQL with a FLOAT64 parameter. query_data_with_int - Queries sample data using SQL with a INT64 parameter. + Queries sample data using SQL with a INT64 + parameter. query_data_with_string - Queries sample data using SQL with a STRING parameter. + Queries sample data using SQL with a STRING + parameter. query_data_with_timestamp_parameter Queries sample data using SQL with a TIMESTAMP parameter. - query_data_with_query_options - Queries sample data using SQL with query options. - create_client_with_query_options - Create a client with query options. optional arguments: -h, --help show this help message and exit @@ -267,10 +300,6 @@ To run this sample: - - - - The client library ------------------------------------------------------------------------------- @@ -286,5 +315,4 @@ to `browse the source`_ and `report issues`_. https://github.com/GoogleCloudPlatform/google-cloud-python/issues - -.. _Google Cloud SDK: https://cloud.google.com/sdk/ +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/samples/samples/backup_sample.py b/samples/samples/backup_sample.py index 19b758d560..76f04cb85c 100644 --- a/samples/samples/backup_sample.py +++ b/samples/samples/backup_sample.py @@ -69,7 +69,7 @@ def restore_database(instance_id, new_database_id, backup_id): operation = new_database.restore(backup) # Wait for restore operation to complete. - operation.result(1200) + operation.result(1600) # Newly created database has restore information. new_database.reload() diff --git a/samples/samples/backup_sample_test.py b/samples/samples/backup_sample_test.py index 5a87c39d9d..8d73c8acf1 100644 --- a/samples/samples/backup_sample_test.py +++ b/samples/samples/backup_sample_test.py @@ -13,8 +13,10 @@ # limitations under the License. import uuid +from google.api_core.exceptions import DeadlineExceeded from google.cloud import spanner import pytest +from test_utils.retry import RetryErrors import backup_sample @@ -68,6 +70,7 @@ def test_create_backup(capsys, database): assert BACKUP_ID in out +@RetryErrors(exception=DeadlineExceeded, max_tries=2) def test_restore_database(capsys): backup_sample.restore_database(INSTANCE_ID, RESTORE_DB_ID, BACKUP_ID) out, _ = capsys.readouterr() diff --git a/samples/samples/requirements-test.txt b/samples/samples/requirements-test.txt index 676ff949e8..f977b64f81 100644 --- a/samples/samples/requirements-test.txt +++ b/samples/samples/requirements-test.txt @@ -1,2 +1,3 @@ -pytest==5.4.3 +pytest==6.0.1 mock==4.0.2 +google-cloud-testutils==0.1.0 \ No newline at end of file diff --git a/samples/samples/snippets.py b/samples/samples/snippets.py index 1a2c8d60e6..4a47985031 100644 --- a/samples/samples/snippets.py +++ b/samples/samples/snippets.py @@ -23,6 +23,7 @@ import argparse import base64 import datetime +import decimal from google.cloud import spanner from google.cloud.spanner_v1 import param_types @@ -723,6 +724,64 @@ def query_data_with_timestamp(instance_id, database_id): # [END spanner_query_data_with_timestamp_column] +# [START spanner_add_numeric_column] +def add_numeric_column(instance_id, database_id): + """ Adds a new NUMERIC column to the Venues table in the example database. + """ + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + + database = instance.database(database_id) + + operation = database.update_ddl(["ALTER TABLE Venues ADD COLUMN Revenue NUMERIC"]) + + print("Waiting for operation to complete...") + operation.result(120) + + print( + 'Altered table "Venues" on database {} on instance {}.'.format( + database_id, instance_id + ) + ) + + +# [END spanner_add_numeric_column] + + +# [START spanner_update_data_with_numeric_column] +def update_data_with_numeric(instance_id, database_id): + """Updates Venues tables in the database with the NUMERIC + column. + + This updates the `Revenue` column which must be created before + running this sample. You can add the column by running the + `add_numeric_column` sample or by running this DDL statement + against your database: + + ALTER TABLE Venues ADD COLUMN Revenue NUMERIC + """ + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + + database = instance.database(database_id) + + with database.batch() as batch: + batch.update( + table="Venues", + columns=("VenueId", "Revenue"), + values=[ + (4, decimal.Decimal("35000")), + (19, decimal.Decimal("104500")), + (42, decimal.Decimal("99999999999999999999999999999.99")), + ], + ) + + print("Updated data.") + + +# [END spanner_update_data_with_numeric_column] + + # [START spanner_write_data_for_struct_queries] def write_struct_data(instance_id, database_id): """Inserts sample data that can be used to test STRUCT parameters @@ -843,7 +902,7 @@ def query_struct_field(instance_id, database_id): print(u"SingerId: {}".format(*row)) -# [START spanner_field_access_on_struct_parameters] +# [END spanner_field_access_on_struct_parameters] # [START spanner_field_access_on_nested_struct_parameters] @@ -1500,6 +1559,31 @@ def query_data_with_string(instance_id, database_id): # [END spanner_query_with_string_parameter] +def query_data_with_numeric_parameter(instance_id, database_id): + """Queries sample data using SQL with a NUMERIC parameter. """ + # [START spanner_query_with_numeric_parameter] + # instance_id = "your-spanner-instance" + # database_id = "your-spanner-db-id" + spanner_client = spanner.Client() + instance = spanner_client.instance(instance_id) + database = instance.database(database_id) + + example_numeric = decimal.Decimal("100000") + param = {"revenue": example_numeric} + param_type = {"revenue": param_types.NUMERIC} + + with database.snapshot() as snapshot: + results = snapshot.execute_sql( + "SELECT VenueId, Revenue FROM Venues " "WHERE Revenue < @revenue", + params=param, + param_types=param_type, + ) + + for row in results: + print(u"VenueId: {}, Revenue: {}".format(*row)) + # [END spanner_query_with_numeric_parameter] + + def query_data_with_timestamp_parameter(instance_id, database_id): """Queries sample data using SQL with a TIMESTAMP parameter. """ # [START spanner_query_with_timestamp_parameter] @@ -1510,6 +1594,13 @@ def query_data_with_timestamp_parameter(instance_id, database_id): database = instance.database(database_id) example_timestamp = datetime.datetime.utcnow().isoformat() + "Z" + # [END spanner_query_with_timestamp_parameter] + # Avoid time drift on the local machine. + # https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4197. + example_timestamp = ( + datetime.datetime.utcnow() + datetime.timedelta(days=1) + ).isoformat() + "Z" + # [START spanner_query_with_timestamp_parameter] param = {"last_update_time": example_timestamp} param_type = {"last_update_time": param_types.TIMESTAMP} diff --git a/samples/samples/snippets_test.py b/samples/samples/snippets_test.py index a62a3d90aa..237389c8b1 100644 --- a/samples/samples/snippets_test.py +++ b/samples/samples/snippets_test.py @@ -361,10 +361,25 @@ def test_query_data_with_string(capsys): assert "VenueId: 42, VenueName: Venue 42" in out +def test_add_numeric_column(capsys): + snippets.add_numeric_column(INSTANCE_ID, DATABASE_ID) + out, _ = capsys.readouterr() + assert 'Altered table "Venues" on database ' in out + + +def test_update_data_with_numeric(capsys): + snippets.update_data_with_numeric(INSTANCE_ID, DATABASE_ID) + out, _ = capsys.readouterr() + assert "Updated data" in out + + +def test_query_data_with_numeric_parameter(capsys): + snippets.query_data_with_numeric_parameter(INSTANCE_ID, DATABASE_ID) + out, _ = capsys.readouterr() + assert "VenueId: 4, Revenue: 35000" in out + + def test_query_data_with_timestamp_parameter(capsys): - # Wait 5 seconds to avoid a time drift issue for the next query: - # https://github.com/GoogleCloudPlatform/python-docs-samples/issues/4197. - time.sleep(5) snippets.query_data_with_timestamp_parameter(INSTANCE_ID, DATABASE_ID) out, _ = capsys.readouterr() assert "VenueId: 4, VenueName: Venue 4, LastUpdateTime:" in out