-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Implementing client side statements in dbapi (starting with com…
…mit) (#1037) * Implementing client side statement in dbapi starting with commit * Fixing comments * Adding dependency on "deprecated" package * Fix in setup.py * Fixing tests * Lint issue fix * Resolving comments * Fixing formatting issue
- Loading branch information
Showing
9 changed files
with
292 additions
and
91 deletions.
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
google/cloud/spanner_dbapi/client_side_statement_executor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Copyright 2023 Google LLC All rights reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
from google.cloud.spanner_dbapi.parsed_statement import ( | ||
ParsedStatement, | ||
ClientSideStatementType, | ||
) | ||
|
||
|
||
def execute(connection, parsed_statement: ParsedStatement): | ||
"""Executes the client side statements by calling the relevant method. | ||
It is an internal method that can make backwards-incompatible changes. | ||
:type parsed_statement: ParsedStatement | ||
:param parsed_statement: parsed_statement based on the sql query | ||
""" | ||
if parsed_statement.client_side_statement_type == ClientSideStatementType.COMMIT: | ||
return connection.commit() |
42 changes: 42 additions & 0 deletions
42
google/cloud/spanner_dbapi/client_side_statement_parser.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Copyright 2023 Google LLC All rights reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import re | ||
|
||
from google.cloud.spanner_dbapi.parsed_statement import ( | ||
ParsedStatement, | ||
StatementType, | ||
ClientSideStatementType, | ||
) | ||
|
||
RE_COMMIT = re.compile(r"^\s*(COMMIT)(TRANSACTION)?", re.IGNORECASE) | ||
|
||
|
||
def parse_stmt(query): | ||
"""Parses the sql query to check if it matches with any of the client side | ||
statement regex. | ||
It is an internal method that can make backwards-incompatible changes. | ||
:type query: str | ||
:param query: sql query | ||
:rtype: ParsedStatement | ||
:returns: ParsedStatement object. | ||
""" | ||
if RE_COMMIT.match(query): | ||
return ParsedStatement( | ||
StatementType.CLIENT_SIDE, query, ClientSideStatementType.COMMIT | ||
) | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Copyright 20203 Google LLC All rights reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
from dataclasses import dataclass | ||
from enum import Enum | ||
|
||
|
||
class StatementType(Enum): | ||
CLIENT_SIDE = 1 | ||
DDL = 2 | ||
QUERY = 3 | ||
UPDATE = 4 | ||
INSERT = 5 | ||
|
||
|
||
class ClientSideStatementType(Enum): | ||
COMMIT = 1 | ||
BEGIN = 2 | ||
|
||
|
||
@dataclass | ||
class ParsedStatement: | ||
statement_type: StatementType | ||
query: str | ||
client_side_statement_type: ClientSideStatementType = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,8 @@ | |
|
||
from google.cloud import spanner_v1 | ||
from google.cloud._helpers import UTC | ||
|
||
from google.cloud.spanner_dbapi import Cursor | ||
from google.cloud.spanner_dbapi.connection import connect | ||
from google.cloud.spanner_dbapi.connection import Connection | ||
from google.cloud.spanner_dbapi.exceptions import ProgrammingError | ||
|
@@ -72,37 +74,11 @@ def dbapi_database(raw_database): | |
|
||
def test_commit(shared_instance, dbapi_database): | ||
"""Test committing a transaction with several statements.""" | ||
want_row = ( | ||
1, | ||
"updated-first-name", | ||
"last-name", | ||
"[email protected]", | ||
) | ||
# connect to the test database | ||
conn = Connection(shared_instance, dbapi_database) | ||
cursor = conn.cursor() | ||
|
||
# execute several DML statements within one transaction | ||
cursor.execute( | ||
""" | ||
INSERT INTO contacts (contact_id, first_name, last_name, email) | ||
VALUES (1, 'first-name', 'last-name', '[email protected]') | ||
""" | ||
) | ||
cursor.execute( | ||
""" | ||
UPDATE contacts | ||
SET first_name = 'updated-first-name' | ||
WHERE first_name = 'first-name' | ||
""" | ||
) | ||
cursor.execute( | ||
""" | ||
UPDATE contacts | ||
SET email = '[email protected]' | ||
WHERE email = '[email protected]' | ||
""" | ||
) | ||
want_row = _execute_common_precommit_statements(cursor) | ||
conn.commit() | ||
|
||
# read the resulting data from the database | ||
|
@@ -116,6 +92,25 @@ def test_commit(shared_instance, dbapi_database): | |
conn.close() | ||
|
||
|
||
def test_commit_client_side(shared_instance, dbapi_database): | ||
"""Test committing a transaction with several statements.""" | ||
# connect to the test database | ||
conn = Connection(shared_instance, dbapi_database) | ||
cursor = conn.cursor() | ||
|
||
want_row = _execute_common_precommit_statements(cursor) | ||
cursor.execute("""COMMIT""") | ||
|
||
# read the resulting data from the database | ||
cursor.execute("SELECT * FROM contacts") | ||
got_rows = cursor.fetchall() | ||
conn.commit() | ||
cursor.close() | ||
conn.close() | ||
|
||
assert got_rows == [want_row] | ||
|
||
|
||
def test_rollback(shared_instance, dbapi_database): | ||
"""Test rollbacking a transaction with several statements.""" | ||
want_row = (2, "first-name", "last-name", "[email protected]") | ||
|
@@ -810,3 +805,33 @@ def test_dml_returning_delete(shared_instance, dbapi_database, autocommit): | |
assert cur.fetchone() == (1, "first-name") | ||
assert cur.rowcount == 1 | ||
conn.commit() | ||
|
||
|
||
def _execute_common_precommit_statements(cursor: Cursor): | ||
# execute several DML statements within one transaction | ||
cursor.execute( | ||
""" | ||
INSERT INTO contacts (contact_id, first_name, last_name, email) | ||
VALUES (1, 'first-name', 'last-name', '[email protected]') | ||
""" | ||
) | ||
cursor.execute( | ||
""" | ||
UPDATE contacts | ||
SET first_name = 'updated-first-name' | ||
WHERE first_name = 'first-name' | ||
""" | ||
) | ||
cursor.execute( | ||
""" | ||
UPDATE contacts | ||
SET email = '[email protected]' | ||
WHERE email = '[email protected]' | ||
""" | ||
) | ||
return ( | ||
1, | ||
"updated-first-name", | ||
"last-name", | ||
"[email protected]", | ||
) |
Oops, something went wrong.