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
18 changes: 16 additions & 2 deletions src/DatabaseLibrary/assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ def check_row_count(
retry_timeout="0 seconds",
retry_pause="0.5 seconds",
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
):
Expand All @@ -298,6 +299,8 @@ def check_row_count(
Use ``retry_timeout`` and ``retry_pause`` parameters to enable waiting for assertion to pass.
See `Retry mechanism` for more details.

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement`` and ``sansTran`` are *deprecated*,
please use new parameters ``select_statement`` and ``no_transaction`` instead.
Expand All @@ -317,7 +320,11 @@ def check_row_count(
while not check_ok:
try:
num_rows = self.row_count(
select_statement, no_transaction=no_transaction, alias=alias, parameters=parameters
select_statement,
no_transaction=no_transaction,
alias=alias,
parameters=parameters,
replace_robot_variables=replace_robot_variables,
)
verify_assertion(num_rows, assertion_operator, expected_value, "Wrong row count:", assertion_message)
check_ok = True
Expand All @@ -343,6 +350,7 @@ def check_query_result(
retry_timeout="0 seconds",
retry_pause="0.5 seconds",
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
):
Expand All @@ -368,6 +376,8 @@ def check_query_result(
Use ``retry_timeout`` and ``retry_pause`` parameters to enable waiting for assertion to pass.
See `Retry mechanism` for more details.

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement`` and ``sansTran`` are *deprecated*,
please use new parameters ``select_statement`` and ``no_transaction`` instead.
Expand All @@ -390,7 +400,11 @@ def check_query_result(
while not check_ok:
try:
query_results = self.query(
select_statement, no_transaction=no_transaction, alias=alias, parameters=parameters
select_statement,
no_transaction=no_transaction,
alias=alias,
parameters=parameters,
replace_robot_variables=replace_robot_variables,
)
row_count = len(query_results)
assert (
Expand Down
38 changes: 34 additions & 4 deletions src/DatabaseLibrary/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import sqlparse
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
from robot.utils.dotdict import DotDict

from .connection_manager import Connection
Expand All @@ -46,6 +47,7 @@ def query(
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
returnAsDict: Optional[bool] = None,
Expand All @@ -65,6 +67,8 @@ def query(
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement``, ``sansTran`` and ``returnAsDict`` are *deprecated*,
please use new parameters ``select_statement``, ``no_transaction`` and ``return_dict`` instead.
Expand All @@ -88,6 +92,7 @@ def query(
select_statement,
parameters=parameters,
omit_trailing_semicolon=db_connection.omit_trailing_semicolon,
replace_robot_variables=replace_robot_variables,
)
all_rows = cur.fetchall()
if all_rows is None:
Expand All @@ -109,6 +114,7 @@ def row_count(
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
):
Expand All @@ -123,6 +129,8 @@ def row_count(
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement`` and ``sansTran`` are *deprecated*,
please use new parameters ``select_statement`` and ``no_transaction`` instead.
Expand All @@ -145,6 +153,7 @@ def row_count(
select_statement,
parameters=parameters,
omit_trailing_semicolon=db_connection.omit_trailing_semicolon,
replace_robot_variables=replace_robot_variables,
)
data = cur.fetchall()
if data is None:
Expand All @@ -169,6 +178,7 @@ def description(
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
):
Expand All @@ -183,6 +193,8 @@ def description(
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement`` and ``sansTran`` are *deprecated*,
please use new parameters ``select_statement`` and ``no_transaction`` instead.
Expand All @@ -205,6 +217,7 @@ def description(
select_statement,
parameters=parameters,
omit_trailing_semicolon=db_connection.omit_trailing_semicolon,
replace_robot_variables=replace_robot_variables,
)
self._commit_if_needed(db_connection, no_transaction)
description = list(cur.description)
Expand Down Expand Up @@ -264,8 +277,9 @@ def execute_sql_script(
no_transaction: bool = False,
alias: Optional[str] = None,
split: bool = True,
external_parser=False,
*,
external_parser=False,
replace_robot_variables=False,
sqlScriptFileName: Optional[str] = None,
sansTran: Optional[bool] = None,
):
Expand All @@ -284,6 +298,8 @@ def execute_sql_script(

Use ``alias`` to specify what connection should be used if `Handling multiple database connections`.

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``sqlScriptFileName`` and ``sansTran`` are *deprecated*,
please use new parameters ``script_path`` and ``no_transaction`` instead.
Expand All @@ -307,14 +323,15 @@ def execute_sql_script(
cur,
sql_file.read(),
omit_trailing_semicolon=db_connection.omit_trailing_semicolon,
replace_robot_variables=replace_robot_variables,
)
else:
statements_to_execute = self.split_sql_script(script_path, external_parser=external_parser)
for statement in statements_to_execute:
proc_end_pattern = re.compile("end(?!( if;| loop;| case;| while;| repeat;)).*;()?")
line_ends_with_proc_end = re.compile(r"(\s|;)" + proc_end_pattern.pattern + "$")
omit_semicolon = not line_ends_with_proc_end.search(statement.lower())
self._execute_sql(cur, statement, omit_semicolon)
self._execute_sql(cur, statement, omit_semicolon, replace_robot_variables=replace_robot_variables)
self._commit_if_needed(db_connection, no_transaction)
except Exception as e:
self._rollback_and_raise(db_connection, no_transaction, e)
Expand Down Expand Up @@ -411,6 +428,7 @@ def execute_sql_string(
parameters: Optional[Tuple] = None,
omit_trailing_semicolon: Optional[bool] = None,
*,
replace_robot_variables=False,
sqlString: Optional[str] = None,
sansTran: Optional[bool] = None,
omitTrailingSemicolon: Optional[bool] = None,
Expand All @@ -427,7 +445,9 @@ def execute_sql_string(
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).

Set the ``omit_trailing_semicolon`` to explicitly control the `Omitting trailing semicolon behavior` for the command.
Set ``omit_trailing_semicolon`` to explicitly control the `Omitting trailing semicolon behavior` for the command.

Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.

=== Some parameters were renamed in version 2.0 ===
The old parameters ``sqlString``, ``sansTran`` and ``omitTrailingSemicolon`` are *deprecated*,
Expand All @@ -449,7 +469,13 @@ def execute_sql_string(
cur = db_connection.client.cursor()
if omit_trailing_semicolon is None:
omit_trailing_semicolon = db_connection.omit_trailing_semicolon
self._execute_sql(cur, sql_string, omit_trailing_semicolon=omit_trailing_semicolon, parameters=parameters)
self._execute_sql(
cur,
sql_string,
omit_trailing_semicolon=omit_trailing_semicolon,
parameters=parameters,
replace_robot_variables=replace_robot_variables,
)
self._commit_if_needed(db_connection, no_transaction)
except Exception as e:
self._rollback_and_raise(db_connection, no_transaction, e)
Expand Down Expand Up @@ -784,16 +810,20 @@ def _execute_sql(
sql_statement: str,
omit_trailing_semicolon: Optional[bool] = False,
parameters: Optional[Tuple] = None,
replace_robot_variables=False,
):
"""
Runs the `sql_statement` using `cur` as Cursor object.

Use `omit_trailing_semicolon` parameter (bool) for explicit instruction,
if the trailing semicolon (;) should be removed - otherwise the statement
won't be executed by some databases (e.g. Oracle).
Otherwise, it's decided based on the current database module in use.
"""
if omit_trailing_semicolon:
sql_statement = sql_statement.rstrip(";")
if replace_robot_variables:
sql_statement = BuiltIn().replace_variables(sql_statement)
if parameters is None:
logger.info(f'Executing sql:<br><code style="font-weight: bold;">{sql_statement}</code>', html=True)
return cur.execute(sql_statement)
Expand Down
1 change: 1 addition & 0 deletions test/resources/common.resource
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ${DB_NAME} db
${DB_PASS} pass
${DB_PORT} 5432
${DB_USER} db_user
${Script files dir} ${CURDIR}/script_file_tests

# used for MySQL via PyODBC only
${DB_DRIVER} ODBC Driver 18 for SQL Server
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM ${PERSON_TABLE}
86 changes: 86 additions & 0 deletions test/tests/common_tests/replace_robot_variables.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
*** Settings ***
Documentation Tests which work with the same input params across all databases.

Resource ../../resources/common.resource

Suite Setup Connect To DB
Suite Teardown Disconnect From Database
Test Setup Create Table And Set Test Variable
Test Teardown Drop Tables Person And Foobar


*** Variables ***
${Query with vars} SELECT * FROM \${PERSON_TABLE}
${Script with vars} ${Script files dir}/select_with_robot_variables.sql

&{Error}
... psycopg2=*syntax error*$*
... oracledb=*$*invalid character*
... pymssql=*Incorrect syntax*$*
... pymysql=*error*syntax*
... pyodbc=*error*syntax*
... ibm_db_dbi=*Invalid SQL syntax*
... sqlite3=*unrecognized token*$*


*** Test Cases ***
Query
${results}= Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Query ${Query with vars}
${results}= Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Query ${Query with vars} replace_robot_variables=False
Query ${Query with vars} replace_robot_variables=True

SQL String
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Execute Sql String ${Query with vars}
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Execute Sql String ${Query with vars} replace_robot_variables=False
Execute Sql String ${Query with vars} replace_robot_variables=True

SQL Script
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Execute Sql Script ${Script with vars}
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Execute Sql Script ${Script with vars} replace_robot_variables=False
Execute Sql Script ${Script with vars} replace_robot_variables=True

Row Count
${result}= Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Row Count ${Query with vars}
${result}= Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Row Count ${Query with vars} replace_robot_variables=False
${result}= Row Count ${Query with vars} replace_robot_variables=True

Description
${result}= Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Description ${Query with vars}
${result}= Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Description ${Query with vars} replace_robot_variables=False
${result}= Description ${Query with vars} replace_robot_variables=True

Check Query Result
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Check Query Result ${Query with vars} contains Franz Allan col=1
Run Keyword And Expect Error
... ${Error}[${DB_MODULE}]
... Check Query Result
... ${Query with vars}
... contains
... Franz Allan
... col=1
... replace_robot_variables=False
Check Query Result ${Query with vars} contains Franz Allan col=1 replace_robot_variables=True

Check Row Count
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Check Row Count ${Query with vars} == 2
Run Keyword And Expect Error ${Error}[${DB_MODULE}]
... Check Row Count ${Query with vars} == 2 replace_robot_variables=False
Check Row Count ${Query with vars} == 2 replace_robot_variables=True


*** Keywords ***
Create Table And Set Test Variable
Create Person Table And Insert Data
Set Test Variable ${PERSON_TABLE} person
3 changes: 0 additions & 3 deletions test/tests/common_tests/script_files.robot
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ Suite Teardown Disconnect From Database
Test Setup Create Person Table
Test Teardown Drop Tables Person And Foobar

*** Variables ***
${Script files dir} ${CURDIR}/../../resources/script_file_tests


*** Test Cases ***
Semicolons As Statement Separators In One Line
Expand Down