Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions google/cloud/spanner_dbapi/parse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ def classify_statement(query, args=None):
:rtype: ParsedStatement
:returns: parsed statement attributes.
"""
# Check for RUN PARTITION command to avoid sqlparse processing it.
# sqlparse fails with "Maximum grouping depth exceeded" on long partition IDs.
if re.match(r"^\s*RUN\s+PARTITION\s+.+", query, re.IGNORECASE):
return client_side_statement_parser.parse_stmt(query.strip())

# sqlparse will strip Cloud Spanner comments,
# still, special commenting styles, like
# PostgreSQL dollar quoted comments are not
Expand Down
3 changes: 3 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,9 @@ def prerelease_deps(session, protobuf_implementation, database_dialect):
"google-cloud-testutils",
# dependencies of google-cloud-testutils"
"click",
# dependency of google-auth
"cffi",
"cryptography",
]

for dep in prerel_deps:
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/spanner_dbapi/test_parse_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,29 @@ def test_run_partition_classify_stmt(self):
),
)

def test_run_partition_classify_stmt_long_id(self):
# Regression test for "Maximum grouping depth exceeded" with sqlparse
long_id = "a" * 5000
query = f"RUN PARTITION {long_id}"
parsed_statement = classify_statement(query)
self.assertEqual(
parsed_statement,
ParsedStatement(
StatementType.CLIENT_SIDE,
Statement(query),
ClientSideStatementType.RUN_PARTITION,
[long_id],
),
)

def test_run_partition_classify_stmt_incomplete(self):
# "RUN PARTITION" without ID should be classified as UNKNOWN (not None)
# because it falls through the specific check and sqlparse handles it.
query = "RUN PARTITION"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens with RUN PARTITION ? (so with a space at the end)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the query is just RUN PARTITION (with a trailing space), it fails the regex requirement because there are no characters after the trailing space and falls through to sqlparse. In this test_run_partition_classify_stmt_incomplete test, I've confirmed this results in StatementType.UNKNOWN, which is consistent with how other incomplete commands are handled.

Let me know if you want me to add a unit test to cover this specific case.

parsed_statement = classify_statement(query)
self.assertEqual(parsed_statement.statement_type, StatementType.UNKNOWN)
self.assertEqual(parsed_statement.statement.sql, query)

def test_run_partitioned_query_classify_stmt(self):
parsed_statement = classify_statement(
" RUN PARTITIONED QUERY SELECT s.SongName FROM Songs AS s "
Expand Down
Loading