Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: consistent percents handling in DB API query #619

Merged
merged 3 commits into from Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions google/cloud/bigquery/dbapi/cursor.py
Expand Up @@ -389,7 +389,7 @@ def _format_operation_list(operation, parameters):

try:
return operation % tuple(formatted_params)
except TypeError as exc:
except (TypeError, ValueError) as exc:
raise exceptions.ProgrammingError(exc)


Expand Down Expand Up @@ -419,7 +419,7 @@ def _format_operation_dict(operation, parameters):

try:
return operation % formatted_params
except KeyError as exc:
except (KeyError, ValueError, TypeError) as exc:
raise exceptions.ProgrammingError(exc)


Expand All @@ -441,7 +441,7 @@ def _format_operation(operation, parameters=None):
``parameters`` argument.
"""
if parameters is None or len(parameters) == 0:
return operation
return operation.replace("%%", "%") # Still do percent de-escaping.

if isinstance(parameters, collections_abc.Mapping):
return _format_operation_dict(operation, parameters)
Expand Down
53 changes: 53 additions & 0 deletions tests/unit/test_dbapi_cursor.py
Expand Up @@ -633,6 +633,14 @@ def test__format_operation_w_wrong_dict(self):
{"somevalue-not-here": "hi", "othervalue": "world"},
)

def test__format_operation_w_redundant_dict_key(self):
from google.cloud.bigquery.dbapi import cursor

formatted_operation = cursor._format_operation(
"SELECT %(somevalue)s;", {"somevalue": "foo", "value-not-used": "bar"}
)
self.assertEqual(formatted_operation, "SELECT @`somevalue`;")

def test__format_operation_w_sequence(self):
from google.cloud.bigquery.dbapi import cursor

Expand All @@ -652,8 +660,53 @@ def test__format_operation_w_too_short_sequence(self):
("hello",),
)

def test__format_operation_w_too_long_sequence(self):
from google.cloud.bigquery import dbapi
from google.cloud.bigquery.dbapi import cursor

self.assertRaises(
dbapi.ProgrammingError,
cursor._format_operation,
"SELECT %s, %s;",
("hello", "world", "everyone"),
)

def test__format_operation_w_empty_dict(self):
from google.cloud.bigquery.dbapi import cursor

formatted_operation = cursor._format_operation("SELECT '%f'", {})
self.assertEqual(formatted_operation, "SELECT '%f'")

def test__format_operation_wo_params_single_percent(self):
from google.cloud.bigquery.dbapi import cursor

formatted_operation = cursor._format_operation("SELECT '%'", {})
self.assertEqual(formatted_operation, "SELECT '%'")

def test__format_operation_wo_params_double_percents(self):
from google.cloud.bigquery.dbapi import cursor

formatted_operation = cursor._format_operation("SELECT '%%'", {})
self.assertEqual(formatted_operation, "SELECT '%'")

def test__format_operation_unescaped_percent_w_dict_param(self):
from google.cloud.bigquery import dbapi
from google.cloud.bigquery.dbapi import cursor

self.assertRaises(
dbapi.ProgrammingError,
cursor._format_operation,
"SELECT %(foo)s, '100 %';",
{"foo": "bar"},
)

def test__format_operation_unescaped_percent_w_list_param(self):
from google.cloud.bigquery import dbapi
from google.cloud.bigquery.dbapi import cursor

self.assertRaises(
dbapi.ProgrammingError,
cursor._format_operation,
"SELECT %s, %s, '100 %';",
["foo", "bar"],
plamut marked this conversation as resolved.
Show resolved Hide resolved
)