Skip to content

Commit 7f4d478

Browse files
author
Ilya Gurov
authored
feat: remove adding a dummy WHERE clause into UPDATE and DELETE statements (#169)
* feat: don't add dummy WHERE clause into UPDATE and DELETE queries * fix docstrings
1 parent 8cfea48 commit 7f4d478

File tree

3 files changed

+27
-29
lines changed

3 files changed

+27
-29
lines changed

google/cloud/spanner_dbapi/parse_utils.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,11 +523,19 @@ def get_param_types(params):
523523
def ensure_where_clause(sql):
524524
"""
525525
Cloud Spanner requires a WHERE clause on UPDATE and DELETE statements.
526-
Add a dummy WHERE clause if necessary.
526+
Raise an error, if the given sql doesn't include it.
527+
528+
:type sql: `str`
529+
:param sql: SQL code to check.
530+
531+
:raises: :class:`ProgrammingError` if the given sql doesn't include a WHERE clause.
527532
"""
528533
if any(isinstance(token, sqlparse.sql.Where) for token in sqlparse.parse(sql)[0]):
529534
return sql
530-
return sql + " WHERE 1=1"
535+
536+
raise ProgrammingError(
537+
"Cloud Spanner requires a WHERE clause when executing DELETE or UPDATE query"
538+
)
531539

532540

533541
def escape_name(name):

tests/unit/spanner_dbapi/test_cursor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def test_do_execute_update(self):
100100
def run_helper(ret_value):
101101
transaction.execute_update.return_value = ret_value
102102
res = cursor._do_execute_update(
103-
transaction=transaction, sql="sql", params=None
103+
transaction=transaction, sql="SELECT * WHERE true", params={},
104104
)
105105
return res
106106

tests/unit/spanner_dbapi/test_parse_utils.py

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -391,36 +391,26 @@ def test_get_param_types_none(self):
391391

392392
@unittest.skipIf(skip_condition, skip_message)
393393
def test_ensure_where_clause(self):
394+
from google.cloud.spanner_dbapi.exceptions import ProgrammingError
394395
from google.cloud.spanner_dbapi.parse_utils import ensure_where_clause
395396

396-
cases = [
397-
(
398-
"UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1",
399-
"UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1",
400-
),
401-
(
402-
"UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5",
403-
"UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5 WHERE 1=1",
404-
),
405-
(
406-
"UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2",
407-
"UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2",
408-
),
409-
(
410-
"UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)",
411-
"UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)",
412-
),
413-
(
414-
"UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)",
415-
"UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)",
416-
),
417-
("DELETE * FROM TABLE", "DELETE * FROM TABLE WHERE 1=1"),
418-
]
397+
cases = (
398+
"UPDATE a SET a.b=10 FROM articles a JOIN d c ON a.ai = c.ai WHERE c.ci = 1",
399+
"UPDATE T SET A = 1 WHERE C1 = 1 AND C2 = 2",
400+
"UPDATE T SET r=r*0.9 WHERE id IN (SELECT id FROM items WHERE r / w >= 1.3 AND q > 100)",
401+
)
402+
err_cases = (
403+
"UPDATE (SELECT * FROM A JOIN c ON ai.id = c.id WHERE cl.ci = 1) SET d=5",
404+
"DELETE * FROM TABLE",
405+
)
406+
for sql in cases:
407+
with self.subTest(sql=sql):
408+
ensure_where_clause(sql)
419409

420-
for sql, want in cases:
410+
for sql in err_cases:
421411
with self.subTest(sql=sql):
422-
got = ensure_where_clause(sql)
423-
self.assertEqual(got, want)
412+
with self.assertRaises(ProgrammingError):
413+
ensure_where_clause(sql)
424414

425415
@unittest.skipIf(skip_condition, skip_message)
426416
def test_escape_name(self):

0 commit comments

Comments
 (0)