diff --git a/src/DatabaseLibrary/assertion.py b/src/DatabaseLibrary/assertion.py
index 3d2fd56..926947f 100644
--- a/src/DatabaseLibrary/assertion.py
+++ b/src/DatabaseLibrary/assertion.py
@@ -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,
):
@@ -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.
@@ -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
@@ -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,
):
@@ -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.
@@ -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 (
diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py
index 18af7e6..b1f4e40 100644
--- a/src/DatabaseLibrary/query.py
+++ b/src/DatabaseLibrary/query.py
@@ -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
@@ -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,
@@ -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.
@@ -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:
@@ -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,
):
@@ -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.
@@ -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:
@@ -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,
):
@@ -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.
@@ -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)
@@ -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,
):
@@ -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.
@@ -307,6 +323,7 @@ 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)
@@ -314,7 +331,7 @@ def execute_sql_script(
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)
@@ -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,
@@ -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*,
@@ -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)
@@ -784,9 +810,11 @@ 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).
@@ -794,6 +822,8 @@ def _execute_sql(
"""
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:
{sql_statement}', html=True)
return cur.execute(sql_statement)
diff --git a/test/resources/common.resource b/test/resources/common.resource
index 260002c..7487618 100644
--- a/test/resources/common.resource
+++ b/test/resources/common.resource
@@ -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
diff --git a/test/resources/script_file_tests/select_with_robot_variables.sql b/test/resources/script_file_tests/select_with_robot_variables.sql
new file mode 100644
index 0000000..8d8b042
--- /dev/null
+++ b/test/resources/script_file_tests/select_with_robot_variables.sql
@@ -0,0 +1 @@
+SELECT * FROM ${PERSON_TABLE}
\ No newline at end of file
diff --git a/test/tests/common_tests/replace_robot_variables.robot b/test/tests/common_tests/replace_robot_variables.robot
new file mode 100644
index 0000000..7883f35
--- /dev/null
+++ b/test/tests/common_tests/replace_robot_variables.robot
@@ -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
diff --git a/test/tests/common_tests/script_files.robot b/test/tests/common_tests/script_files.robot
index db94c88..66d04bc 100644
--- a/test/tests/common_tests/script_files.robot
+++ b/test/tests/common_tests/script_files.robot
@@ -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