Skip to content

Commit 07408b1

Browse files
authored
Added Spanner DML/PDML samples. (#1742)
* Added Spanner DML/PDML samples. * Fixed lint issues and bumped version.
1 parent 779a525 commit 07408b1

File tree

3 files changed

+373
-1
lines changed

3 files changed

+373
-1
lines changed

spanner/cloud-client/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
google-cloud-spanner==1.4.0
1+
google-cloud-spanner==1.6.0
22
futures==3.2.0; python_version < "3"

spanner/cloud-client/snippets.py

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,273 @@ def query_nested_struct_field(instance_id, database_id):
748748
# [END spanner_field_access_on_nested_struct_parameters]
749749

750750

751+
def insert_data_with_dml(instance_id, database_id):
752+
"""Inserts sample data into the given database using a DML statement. """
753+
# [START spanner_dml_standard_insert]
754+
# instance_id = "your-spanner-instance"
755+
# database_id = "your-spanner-db-id"
756+
757+
spanner_client = spanner.Client()
758+
instance = spanner_client.instance(instance_id)
759+
database = instance.database(database_id)
760+
761+
def insert_singers(transaction):
762+
row_ct = transaction.execute_update(
763+
"INSERT Singers (SingerId, FirstName, LastName) "
764+
" VALUES (10, 'Virginia', 'Watson')"
765+
)
766+
767+
print("{} record(s) inserted.".format(row_ct))
768+
769+
database.run_in_transaction(insert_singers)
770+
# [END spanner_dml_standard_insert]
771+
772+
773+
def update_data_with_dml(instance_id, database_id):
774+
"""Updates sample data from the database using a DML statement. """
775+
# [START spanner_dml_standard_update]
776+
# instance_id = "your-spanner-instance"
777+
# database_id = "your-spanner-db-id"
778+
779+
spanner_client = spanner.Client()
780+
instance = spanner_client.instance(instance_id)
781+
database = instance.database(database_id)
782+
783+
def update_albums(transaction):
784+
row_ct = transaction.execute_update(
785+
"UPDATE Albums "
786+
"SET MarketingBudget = MarketingBudget * 2 "
787+
"WHERE SingerId = 1 and AlbumId = 1"
788+
)
789+
790+
print("{} record(s) updated.".format(row_ct))
791+
792+
database.run_in_transaction(update_albums)
793+
# [END spanner_dml_standard_update]
794+
795+
796+
def delete_data_with_dml(instance_id, database_id):
797+
"""Deletes sample data from the database using a DML statement. """
798+
# [START spanner_dml_standard_delete]
799+
# instance_id = "your-spanner-instance"
800+
# database_id = "your-spanner-db-id"
801+
802+
spanner_client = spanner.Client()
803+
instance = spanner_client.instance(instance_id)
804+
database = instance.database(database_id)
805+
806+
def delete_singers(transaction):
807+
row_ct = transaction.execute_update(
808+
"DELETE Singers WHERE FirstName = 'Alice'"
809+
)
810+
811+
print("{} record(s) deleted.".format(row_ct))
812+
813+
database.run_in_transaction(delete_singers)
814+
# [END spanner_dml_standard_delete]
815+
816+
817+
def update_data_with_dml_timestamp(instance_id, database_id):
818+
"""Updates data with Timestamp from the database using a DML statement. """
819+
# [START spanner_dml_standard_update_with_timestamp]
820+
# instance_id = "your-spanner-instance"
821+
# database_id = "your-spanner-db-id"
822+
823+
spanner_client = spanner.Client()
824+
instance = spanner_client.instance(instance_id)
825+
database = instance.database(database_id)
826+
827+
def update_albums(transaction):
828+
row_ct = transaction.execute_update(
829+
"UPDATE Albums "
830+
"SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() "
831+
"WHERE SingerId = 1"
832+
)
833+
834+
print("{} record(s) updated.".format(row_ct))
835+
836+
database.run_in_transaction(update_albums)
837+
# [END spanner_dml_standard_update_with_timestamp]
838+
839+
840+
def dml_write_read_transaction(instance_id, database_id):
841+
"""First inserts data then reads it from within a transaction using DML."""
842+
# [START spanner_dml_write_then_read]
843+
# instance_id = "your-spanner-instance"
844+
# database_id = "your-spanner-db-id"
845+
846+
spanner_client = spanner.Client()
847+
instance = spanner_client.instance(instance_id)
848+
database = instance.database(database_id)
849+
850+
def read_then_write(transaction):
851+
# Insert record.
852+
row_ct = transaction.execute_update(
853+
"INSERT Singers (SingerId, FirstName, LastName) "
854+
" VALUES (11, 'Timothy', 'Campbell')"
855+
)
856+
print("{} record(s) inserted.".format(row_ct))
857+
858+
# Read newly inserted record.
859+
results = transaction.execute_sql(
860+
"SELECT FirstName, LastName FROM Singers WHERE SingerId = 11"
861+
)
862+
for result in results:
863+
print("FirstName: {}, LastName: {}".format(*result))
864+
865+
database.run_in_transaction(read_then_write)
866+
# [END spanner_dml_write_then_read]
867+
868+
869+
def update_data_with_dml_struct(instance_id, database_id):
870+
"""Updates data with a DML statement and STRUCT parameters. """
871+
# [START spanner_dml_structs]
872+
# instance_id = "your-spanner-instance"
873+
# database_id = "your-spanner-db-id"
874+
875+
spanner_client = spanner.Client()
876+
instance = spanner_client.instance(instance_id)
877+
database = instance.database(database_id)
878+
879+
record_type = param_types.Struct([
880+
param_types.StructField('FirstName', param_types.STRING),
881+
param_types.StructField('LastName', param_types.STRING)
882+
])
883+
record_value = ('Timothy', 'Campbell')
884+
885+
def write_with_struct(transaction):
886+
row_ct = transaction.execute_update(
887+
"UPDATE Singers SET LastName = 'Grant' "
888+
"WHERE STRUCT<FirstName STRING, LastName STRING>"
889+
"(FirstName, LastName) = @name",
890+
params={'name': record_value},
891+
param_types={'name': record_type}
892+
)
893+
print("{} record(s) updated.".format(row_ct))
894+
895+
database.run_in_transaction(write_with_struct)
896+
# [END spanner_dml_structs]
897+
898+
899+
def insert_with_dml(instance_id, database_id):
900+
"""Inserts data with a DML statement into the database. """
901+
# [START spanner_dml_getting_started_insert]
902+
# instance_id = "your-spanner-instance"
903+
# database_id = "your-spanner-db-id"
904+
spanner_client = spanner.Client()
905+
instance = spanner_client.instance(instance_id)
906+
database = instance.database(database_id)
907+
908+
def insert_singers(transaction):
909+
row_ct = transaction.execute_update(
910+
"INSERT Singers (SingerId, FirstName, LastName) VALUES "
911+
"(12, 'Melissa', 'Garcia'), "
912+
"(13, 'Russell', 'Morales'), "
913+
"(14, 'Jacqueline', 'Long'), "
914+
"(15, 'Dylan', 'Shaw')"
915+
)
916+
print("{} record(s) inserted.".format(row_ct))
917+
918+
database.run_in_transaction(insert_singers)
919+
# [END spanner_dml_getting_started_insert]
920+
921+
922+
def write_with_dml_transaction(instance_id, database_id):
923+
""" Transfers a marketing budget from one album to another. """
924+
# [START spanner_dml_getting_started_update]
925+
# instance_id = "your-spanner-instance"
926+
# database_id = "your-spanner-db-id"
927+
928+
spanner_client = spanner.Client()
929+
instance = spanner_client.instance(instance_id)
930+
database = instance.database(database_id)
931+
932+
def transfer_budget(transaction):
933+
# Transfer marketing budget from one album to another. Performed in a
934+
# single transaction to ensure that the transfer is atomic.
935+
first_album_result = transaction.execute_sql(
936+
"SELECT MarketingBudget from Albums "
937+
"WHERE SingerId = 1 and AlbumId = 1"
938+
)
939+
first_album_row = list(first_album_result)[0]
940+
first_album_budget = first_album_row[0]
941+
942+
transfer_amount = 300000
943+
944+
# Transaction will only be committed if this condition still holds at
945+
# the time of commit. Otherwise it will be aborted and the callable
946+
# will be rerun by the client library
947+
if first_album_budget >= transfer_amount:
948+
second_album_result = transaction.execute_sql(
949+
"SELECT MarketingBudget from Albums "
950+
"WHERE SingerId = 1 and AlbumId = 1"
951+
)
952+
second_album_row = list(second_album_result)[0]
953+
second_album_budget = second_album_row[0]
954+
955+
first_album_budget -= transfer_amount
956+
second_album_budget += transfer_amount
957+
958+
# Update first album
959+
transaction.execute_update(
960+
"UPDATE Albums "
961+
"SET MarketingBudget = @AlbumBudget "
962+
"WHERE SingerId = 1 and AlbumId = 1",
963+
params={"AlbumBudget": first_album_budget},
964+
param_types={"AlbumBudget": spanner.param_types.INT64}
965+
)
966+
967+
# Update second album
968+
transaction.execute_update(
969+
"UPDATE Albums "
970+
"SET MarketingBudget = @AlbumBudget "
971+
"WHERE SingerId = 2 and AlbumId = 2",
972+
params={"AlbumBudget": second_album_budget},
973+
param_types={"AlbumBudget": spanner.param_types.INT64}
974+
)
975+
976+
print("Transferred {} from Album1's budget to Album2's".format(
977+
transfer_amount))
978+
979+
database.run_in_transaction(transfer_budget)
980+
# [END spanner_dml_getting_started_update]
981+
982+
983+
def update_data_with_partitioned_dml(instance_id, database_id):
984+
""" Update sample data with a partitioned DML statement. """
985+
# [START spanner_dml_partitioned_update]
986+
# instance_id = "your-spanner-instance"
987+
# database_id = "your-spanner-db-id"
988+
989+
spanner_client = spanner.Client()
990+
instance = spanner_client.instance(instance_id)
991+
database = instance.database(database_id)
992+
993+
row_ct = database.execute_partitioned_dml(
994+
"UPDATE Albums SET MarketingBudget = 100000 WHERE SingerId > 1"
995+
)
996+
997+
print("{} records updated.".format(row_ct))
998+
# [END spanner_dml_partitioned_update]
999+
1000+
1001+
def delete_data_with_partitioned_dml(instance_id, database_id):
1002+
""" Delete sample data with a partitioned DML statement. """
1003+
# [START spanner_dml_partitioned_delete]
1004+
# instance_id = "your-spanner-instance"
1005+
# database_id = "your-spanner-db-id"
1006+
spanner_client = spanner.Client()
1007+
instance = spanner_client.instance(instance_id)
1008+
database = instance.database(database_id)
1009+
1010+
row_ct = database.execute_partitioned_dml(
1011+
"DELETE Singers WHERE SingerId > 10"
1012+
)
1013+
1014+
print("{} record(s) deleted.".format(row_ct))
1015+
# [END spanner_dml_partitioned_delete]
1016+
1017+
7511018
if __name__ == '__main__': # noqa: C901
7521019
parser = argparse.ArgumentParser(
7531020
description=__doc__,
@@ -802,6 +1069,30 @@ def query_nested_struct_field(instance_id, database_id):
8021069
'query_struct_field', help=query_struct_field.__doc__)
8031070
subparsers.add_parser(
8041071
'query_nested_struct_field', help=query_nested_struct_field.__doc__)
1072+
subparsers.add_parser(
1073+
'insert_data_with_dml', help=insert_data_with_dml.__doc__)
1074+
subparsers.add_parser(
1075+
'update_data_with_dml', help=update_data_with_dml.__doc__)
1076+
subparsers.add_parser(
1077+
'delete_data_with_dml', help=delete_data_with_dml.__doc__)
1078+
subparsers.add_parser(
1079+
'update_data_with_dml_timestamp',
1080+
help=update_data_with_dml_timestamp.__doc__)
1081+
subparsers.add_parser(
1082+
'dml_write_read_transaction',
1083+
help=dml_write_read_transaction.__doc__)
1084+
subparsers.add_parser(
1085+
'update_data_with_dml_struct',
1086+
help=update_data_with_dml_struct.__doc__)
1087+
subparsers.add_parser('insert_with_dml', help=insert_with_dml.__doc__)
1088+
subparsers.add_parser(
1089+
'write_with_dml_transaction', help=write_with_dml_transaction.__doc__)
1090+
subparsers.add_parser(
1091+
'update_data_with_partitioned_dml',
1092+
help=update_data_with_partitioned_dml.__doc__)
1093+
subparsers.add_parser(
1094+
'delete_data_with_partitioned_dml',
1095+
help=delete_data_with_partitioned_dml.__doc__)
8051096

8061097
args = parser.parse_args()
8071098

@@ -857,3 +1148,23 @@ def query_nested_struct_field(instance_id, database_id):
8571148
query_struct_field(args.instance_id, args.database_id)
8581149
elif args.command == 'query_nested_struct_field':
8591150
query_nested_struct_field(args.instance_id, args.database_id)
1151+
elif args.command == 'insert_data_with_dml':
1152+
insert_data_with_dml(args.instance_id, args.database_id)
1153+
elif args.command == 'update_data_with_dml':
1154+
update_data_with_dml(args.instance_id, args.database_id)
1155+
elif args.command == 'delete_data_with_dml':
1156+
delete_data_with_dml(args.instance_id, args.database_id)
1157+
elif args.command == 'update_data_with_dml_timestamp':
1158+
update_data_with_dml_timestamp(args.instance_id, args.database_id)
1159+
elif args.command == 'dml_write_read_transaction':
1160+
dml_write_read_transaction(args.instance_id, args.database_id)
1161+
elif args.command == 'update_data_with_dml_struct':
1162+
update_data_with_dml_struct(args.instance_id, args.database_id)
1163+
elif args.command == 'insert_with_dml':
1164+
insert_with_dml(args.instance_id, args.database_id)
1165+
elif args.command == 'write_with_dml_transaction':
1166+
write_with_dml_transaction(args.instance_id, args.database_id)
1167+
elif args.command == 'update_data_with_partitioned_dml':
1168+
update_data_with_partitioned_dml(args.instance_id, args.database_id)
1169+
elif args.command == 'delete_data_with_partitioned_dml':
1170+
delete_data_with_partitioned_dml(args.instance_id, args.database_id)

spanner/cloud-client/snippets_test.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,64 @@ def test_query_nested_struct_field(capsys):
209209
out, _ = capsys.readouterr()
210210
assert 'SingerId: 6 SongName: Imagination' in out
211211
assert 'SingerId: 9 SongName: Imagination' in out
212+
213+
214+
def test_insert_data_with_dml(capsys):
215+
snippets.insert_data_with_dml(INSTANCE_ID, DATABASE_ID)
216+
out, _ = capsys.readouterr()
217+
assert '1 record(s) inserted.' in out
218+
219+
220+
def test_update_data_with_dml(capsys):
221+
snippets.update_data_with_dml(INSTANCE_ID, DATABASE_ID)
222+
out, _ = capsys.readouterr()
223+
assert '1 record(s) updated.' in out
224+
225+
226+
def test_delete_data_with_dml(capsys):
227+
snippets.delete_data_with_dml(INSTANCE_ID, DATABASE_ID)
228+
out, _ = capsys.readouterr()
229+
assert '1 record(s) deleted.' in out
230+
231+
232+
def test_update_data_with_dml_timestamp(capsys):
233+
snippets.update_data_with_dml_timestamp(INSTANCE_ID, DATABASE_ID)
234+
out, _ = capsys.readouterr()
235+
assert '2 record(s) updated.' in out
236+
237+
238+
def test_dml_write_read_transaction(capsys):
239+
snippets.dml_write_read_transaction(INSTANCE_ID, DATABASE_ID)
240+
out, _ = capsys.readouterr()
241+
assert '1 record(s) inserted.' in out
242+
assert 'FirstName: Timothy, LastName: Campbell' in out
243+
244+
245+
def test_update_data_with_dml_struct(capsys):
246+
snippets.update_data_with_dml_struct(INSTANCE_ID, DATABASE_ID)
247+
out, _ = capsys.readouterr()
248+
assert '1 record(s) updated' in out
249+
250+
251+
def test_insert_with_dml(capsys):
252+
snippets.insert_with_dml(INSTANCE_ID, DATABASE_ID)
253+
out, _ = capsys.readouterr()
254+
assert '4 record(s) inserted' in out
255+
256+
257+
def test_write_with_dml_transaction(capsys):
258+
snippets.write_with_dml_transaction(INSTANCE_ID, DATABASE_ID)
259+
out, _ = capsys.readouterr()
260+
assert "Transferred 300000 from Album1's budget to Album2's" in out
261+
262+
263+
def update_data_with_partitioned_dml(capsys):
264+
snippets.update_data_with_partitioned_dml(INSTANCE_ID, DATABASE_ID)
265+
out, _ = capsys.readouterr()
266+
assert "3 record(s) updated" in out
267+
268+
269+
def delete_data_with_partitioned_dml(capsys):
270+
snippets.delete_data_with_partitioned_dml(INSTANCE_ID, DATABASE_ID)
271+
out, _ = capsys.readouterr()
272+
assert "5 record(s) deleted" in out

0 commit comments

Comments
 (0)