Skip to content

Commit 6ff12ec

Browse files
authored
feat: add helper function for insert-or-update (#526)
Adds helper functions for insert-or-update and insert-or-ignore and samples for how to use these functions. Fixes #391
1 parent 50a911f commit 6ff12ec

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2024 Google LLC All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from sqlalchemy import Insert, insert
16+
from sqlalchemy.sql._typing import _DMLTableArgument
17+
18+
19+
def insert_or_update(table: _DMLTableArgument) -> Insert:
20+
"""Construct a Spanner-specific insert-or-update statement."""
21+
return insert(table).prefix_with("OR UPDATE")
22+
23+
24+
def insert_or_ignore(table: _DMLTableArgument) -> Insert:
25+
"""Construct a Spanner-specific insert-or-ignore statement."""
26+
return insert(table).prefix_with("OR IGNORE")

samples/insert_or_ignore_sample.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright 2024 Google LLC All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import uuid
16+
17+
from sqlalchemy import create_engine, select
18+
from sqlalchemy.orm import Session
19+
20+
from google.cloud.sqlalchemy_spanner.dml import insert_or_ignore
21+
from sample_helper import run_sample
22+
from model import Singer
23+
24+
# Shows how to use insert-or-ignore using SQLAlchemy and Spanner.
25+
def insert_or_ignore_sample():
26+
engine = create_engine(
27+
"spanner:///projects/sample-project/"
28+
"instances/sample-instance/"
29+
"databases/sample-database",
30+
echo=True,
31+
)
32+
with Session(engine) as session:
33+
stmt = (
34+
insert_or_ignore(Singer)
35+
.values(
36+
id=str(uuid.uuid4()),
37+
first_name="John",
38+
last_name="Smith",
39+
)
40+
.returning(Singer.id)
41+
)
42+
singer_id = session.execute(stmt).scalar()
43+
print(singer_id)
44+
45+
# Use AUTOCOMMIT for sessions that only read. This is more
46+
# efficient than using a read/write transaction to only read.
47+
session.connection(execution_options={"isolation_level": "AUTOCOMMIT"})
48+
stmt = select(Singer).where(Singer.id == singer_id)
49+
singer = session.execute(stmt).scalar()
50+
print(f"Inserted or ignored singer {singer.full_name} successfully")
51+
52+
53+
if __name__ == "__main__":
54+
run_sample(insert_or_ignore_sample)

samples/insert_or_update_sample.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright 2024 Google LLC All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import uuid
16+
17+
from sqlalchemy import create_engine, select
18+
from sqlalchemy.orm import Session
19+
20+
from google.cloud.sqlalchemy_spanner.dml import insert_or_update
21+
from sample_helper import run_sample
22+
from model import Singer
23+
24+
# Shows how to use insert-or-update using SQLAlchemy and Spanner.
25+
def insert_or_update_sample():
26+
engine = create_engine(
27+
"spanner:///projects/sample-project/"
28+
"instances/sample-instance/"
29+
"databases/sample-database",
30+
echo=True,
31+
)
32+
with Session(engine) as session:
33+
stmt = (
34+
insert_or_update(Singer)
35+
.values(
36+
id=str(uuid.uuid4()),
37+
first_name="John",
38+
last_name="Smith",
39+
)
40+
.returning(Singer.id)
41+
)
42+
singer_id = session.execute(stmt).scalar()
43+
print(singer_id)
44+
45+
# Use AUTOCOMMIT for sessions that only read. This is more
46+
# efficient than using a read/write transaction to only read.
47+
session.connection(execution_options={"isolation_level": "AUTOCOMMIT"})
48+
stmt = select(Singer).where(Singer.id == singer_id)
49+
singer = session.execute(stmt).scalar()
50+
print(f"Inserted or updated singer {singer.full_name} successfully")
51+
52+
53+
if __name__ == "__main__":
54+
run_sample(insert_or_update_sample)

0 commit comments

Comments
 (0)