From 8240bdb83e479f9d0221e3a5d42eb0f06753c028 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:02:22 +0200 Subject: [PATCH 01/16] Update query.py add parameters argument --- src/DatabaseLibrary/query.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py index 2da52fe..dc483c0 100644 --- a/src/DatabaseLibrary/query.py +++ b/src/DatabaseLibrary/query.py @@ -25,7 +25,7 @@ class Query: """ def query( - self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None + self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None ): """ Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set @@ -79,7 +79,7 @@ def query( if cur and not sansTran: db_connection.client.rollback() - def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None): + def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. Set optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. @@ -124,7 +124,7 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona if cur and not sansTran: db_connection.client.rollback() - def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None): + def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. Set optional input ``sansTran` to True to run command without an explicit transaction commit or rollback. @@ -331,7 +331,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali if cur and not sansTran: db_connection.client.rollback() - def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None): + def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ Executes the sqlString as SQL commands. Useful to pass arguments to your sql. Set optional input `sansTran` to True to run command without an explicit transaction commit or rollback. @@ -507,7 +507,7 @@ def call_stored_procedure( if cur and not sansTran: db_connection.client.rollback() - def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Optional[bool] = None): + def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Optional[bool] = None, parameters: Optional[List] = None): """ Runs the `sql_statement` using `cur` as Cursor object. Use `omit_trailing_semicolon` parameter (bool) for explicit instruction, @@ -519,5 +519,7 @@ def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Option omit_trailing_semicolon = self.omit_trailing_semicolon if omit_trailing_semicolon: sql_statement = sql_statement.rstrip(";") + if parameters is None: + parameters = [] logger.debug(f"Executing sql: {sql_statement}") - return cur.execute(sql_statement) + return cur.execute(sql_statement, parameters) From a8cbb94589027c2566c4c717ef19844607af3aad Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:15:47 +0200 Subject: [PATCH 02/16] Update query.py pass parameters argument to execute function --- src/DatabaseLibrary/query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py index dc483c0..4c4bc2b 100644 --- a/src/DatabaseLibrary/query.py +++ b/src/DatabaseLibrary/query.py @@ -69,7 +69,7 @@ def query( try: cur = db_connection.client.cursor() logger.info(f"Executing : Query | {selectStatement} ") - self.__execute_sql(cur, selectStatement) + self.__execute_sql(cur, selectStatement, parameters=parameters) all_rows = cur.fetchall() if returnAsDict: col_names = [c[0] for c in cur.description] @@ -115,7 +115,7 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona try: cur = db_connection.client.cursor() logger.info(f"Executing : Row Count | {selectStatement}") - self.__execute_sql(cur, selectStatement) + self.__execute_sql(cur, selectStatement, parameters=parameters) data = cur.fetchall() if db_connection.module_name in ["sqlite3", "ibm_db", "ibm_db_dbi", "pyodbc"]: return len(data) @@ -154,7 +154,7 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio try: cur = db_connection.client.cursor() logger.info("Executing : Description | {selectStatement}") - self.__execute_sql(cur, selectStatement) + self.__execute_sql(cur, selectStatement, parameters=parameters) description = list(cur.description) if sys.version_info[0] < 3: for row in range(0, len(description)): @@ -356,7 +356,7 @@ def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Opti try: cur = db_connection.client.cursor() logger.info(f"Executing : Execute SQL String | {sqlString}") - self.__execute_sql(cur, sqlString) + self.__execute_sql(cur, sqlString, parameters=parameters) if not sansTran: db_connection.client.commit() finally: From 7d0332398412ac093a96fc1357626ed5e5db92e5 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:32:00 +0200 Subject: [PATCH 03/16] Update basic_tests.robot added test using parameters argument --- test/tests/common_tests/basic_tests.robot | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 2fe17bc..2f28772 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -16,6 +16,10 @@ SQL Statement Ending With Semicolon Works SQL Statement Ending Without Semicolon Works Query SELECT * FROM person; +SQL Statement With Parameters Works + ${output}= Query SELECT * FROM person WHERE id < ? parameters=[1] + Length Should Be ${output} 1 + Create Person Table [Setup] Log No setup for this test ${output}= Create Person Table From 7095c0878a53394558b5f84986c8207da1519f6f Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:55:05 +0200 Subject: [PATCH 04/16] Update basic_tests.robot use correct list parameter --- test/tests/common_tests/basic_tests.robot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 2f28772..386c5ae 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -17,7 +17,8 @@ SQL Statement Ending Without Semicolon Works Query SELECT * FROM person; SQL Statement With Parameters Works - ${output}= Query SELECT * FROM person WHERE id < ? parameters=[1] + @{params}= Create List 2 + ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} Length Should Be ${output} 1 Create Person Table From b88358244854f8105b09cd7c733a850203a62715 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:42:47 +0200 Subject: [PATCH 05/16] Update basic_tests.robot try different syntax for postgres, oracle etc. --- test/tests/common_tests/basic_tests.robot | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 386c5ae..8f1f28e 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -18,7 +18,11 @@ SQL Statement Ending Without Semicolon Works SQL Statement With Parameters Works @{params}= Create List 2 - ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} + TRY + ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} + EXCEPT + ${output}= Query SELECT * FROM person WHERE id < :id parameters=${params} + END Length Should Be ${output} 1 Create Person Table From 1174c7e2988e2da830b5e32b33275ddd862b57d2 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:09:11 +0200 Subject: [PATCH 06/16] Update basic_tests.robot extra syntax alternative for postgres, pymsql --- test/tests/common_tests/basic_tests.robot | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 8f1f28e..4db398c 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -18,11 +18,15 @@ SQL Statement Ending Without Semicolon Works SQL Statement With Parameters Works @{params}= Create List 2 - TRY - ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} - EXCEPT + + IF "${DB_MODULE}" in ["oracledb"] ${output}= Query SELECT * FROM person WHERE id < :id parameters=${params} + ELSE IF "${DB_MODULE}" in ["sqlite3", "pyodbc"] + ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} + ELSE + ${output}= Query SELECT * FROM person WHERE id < %s parameters=${params} END + Length Should Be ${output} 1 Create Person Table From 03c5f5db3034d34af0ce96a80d8b0fba6ec03c93 Mon Sep 17 00:00:00 2001 From: Bartlomiej Hirsz Date: Sun, 19 Nov 2023 16:56:23 +0100 Subject: [PATCH 07/16] Update documentation with parameters examples --- src/DatabaseLibrary/query.py | 52 ++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py index 4c4bc2b..01a4842 100644 --- a/src/DatabaseLibrary/query.py +++ b/src/DatabaseLibrary/query.py @@ -28,8 +28,7 @@ def query( self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None ): """ - Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set - optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. + Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set optional input ``returnAsDict`` to True to return values as a list of dictionaries. Use optional ``alias`` parameter to specify what connection should be used for the query if you have more @@ -61,7 +60,12 @@ def query( And get the following See, Franz Allan - Using optional ``sansTran`` to run command without an explicit transaction commit or rollback: + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person | + | Query | SELECT * FROM %s | parameters=${parameters} | + + Use optional ``sansTran`` to run command without an explicit transaction commit or rollback: | @{queryResults} | Query | SELECT * FROM person | True | """ db_connection = self.connection_store.get_connection(alias) @@ -81,8 +85,7 @@ def query( def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ - Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. Set - optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. + Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. For example, given we have a table `person` with the following data: | id | first_name | last_name | @@ -107,7 +110,12 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional ``sansTran`` to run command without an explicit transaction commit or rollback: + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person | + | ${rowCount} | Row Count | SELECT * FROM %s | parameters=${parameters} | + + Use optional ``sansTran`` to run command without an explicit transaction commit or rollback: | ${rowCount} | Row Count | SELECT * FROM person | True | """ db_connection = self.connection_store.get_connection(alias) @@ -126,8 +134,7 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ - Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. Set - optional input ``sansTran` to True to run command without an explicit transaction commit or rollback. + Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. For example, given we have a table `person` with the following data: | id | first_name | last_name | @@ -146,6 +153,11 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person | + | ${desc} | Description | SELECT * FROM %s | parameters=${parameters} | + Using optional `sansTran` to run command without an explicit transaction commit or rollback: | @{queryResults} | Description | SELECT * FROM person | True | """ @@ -166,8 +178,7 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, alias: Optional[str] = None): """ - Delete all the rows within a given table. Set optional input `sansTran` to True to run command without an - explicit transaction commit or rollback. + Delete all the rows within a given table. For example, given we have a table `person` in a database @@ -184,7 +195,7 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | Delete All Rows From Table | person | True | """ db_connection = self.connection_store.get_connection(alias) @@ -207,8 +218,7 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, alias: Optional[str] = None): """ Executes the content of the `sqlScriptFileName` as SQL commands. Useful for setting the database to a known - state before running your tests, or clearing out your test data after running each a test. Set optional input - `sansTran` to True to run command without an explicit transaction commit or rollback. + state before running your tests, or clearing out your test data after running each a test. Sample usage : | Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql | @@ -262,7 +272,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql | True | """ db_connection = self.connection_store.get_connection(alias) @@ -333,8 +343,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ - Executes the sqlString as SQL commands. Useful to pass arguments to your sql. Set optional input `sansTran` to - True to run command without an explicit transaction commit or rollback. + Executes the sqlString as SQL commands. Useful to pass arguments to your sql. SQL commands are expected to be delimited by a semicolon (';'). @@ -348,7 +357,12 @@ def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Opti Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person_employee_table | + | Execute Sql String | SELECT * FROM %s | parameters=${parameters} | + + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | Execute Sql String | DELETE FROM person_employee_table; DELETE FROM person_table | True | """ db_connection = self.connection_store.get_connection(alias) @@ -381,8 +395,6 @@ def call_stored_procedure( It also depends on the database, how the procedure returns the values - as params or as result sets. E.g. calling a procedure in *PostgreSQL* returns even a single value of an OUT param as a result set. - Set optional input `sansTran` to True to run command without an explicit transaction commit or rollback. - Simple example: | @{Params} = | Create List | Jerry | out_second_name | | @{Param values} @{Result sets} = | Call Stored Procedure | Get_second_name | ${Params} | @@ -404,7 +416,7 @@ def call_stored_procedure( Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | @{Param values} @{Result sets} = | Call Stored Procedure | DBName.SchemaName.StoredProcName | ${Params} | True | """ db_connection = self.connection_store.get_connection(alias) From 1a9987dbdd98eed70670e91496750f64c37e0368 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:02:22 +0200 Subject: [PATCH 08/16] Update query.py add parameters argument --- src/DatabaseLibrary/query.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py index 2da52fe..dc483c0 100644 --- a/src/DatabaseLibrary/query.py +++ b/src/DatabaseLibrary/query.py @@ -25,7 +25,7 @@ class Query: """ def query( - self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None + self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None ): """ Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set @@ -79,7 +79,7 @@ def query( if cur and not sansTran: db_connection.client.rollback() - def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None): + def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. Set optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. @@ -124,7 +124,7 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona if cur and not sansTran: db_connection.client.rollback() - def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None): + def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. Set optional input ``sansTran` to True to run command without an explicit transaction commit or rollback. @@ -331,7 +331,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali if cur and not sansTran: db_connection.client.rollback() - def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None): + def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ Executes the sqlString as SQL commands. Useful to pass arguments to your sql. Set optional input `sansTran` to True to run command without an explicit transaction commit or rollback. @@ -507,7 +507,7 @@ def call_stored_procedure( if cur and not sansTran: db_connection.client.rollback() - def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Optional[bool] = None): + def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Optional[bool] = None, parameters: Optional[List] = None): """ Runs the `sql_statement` using `cur` as Cursor object. Use `omit_trailing_semicolon` parameter (bool) for explicit instruction, @@ -519,5 +519,7 @@ def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Option omit_trailing_semicolon = self.omit_trailing_semicolon if omit_trailing_semicolon: sql_statement = sql_statement.rstrip(";") + if parameters is None: + parameters = [] logger.debug(f"Executing sql: {sql_statement}") - return cur.execute(sql_statement) + return cur.execute(sql_statement, parameters) From 402664c42acf1bf41555ff031299ae764bd71798 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:15:47 +0200 Subject: [PATCH 09/16] Update query.py pass parameters argument to execute function --- src/DatabaseLibrary/query.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py index dc483c0..4c4bc2b 100644 --- a/src/DatabaseLibrary/query.py +++ b/src/DatabaseLibrary/query.py @@ -69,7 +69,7 @@ def query( try: cur = db_connection.client.cursor() logger.info(f"Executing : Query | {selectStatement} ") - self.__execute_sql(cur, selectStatement) + self.__execute_sql(cur, selectStatement, parameters=parameters) all_rows = cur.fetchall() if returnAsDict: col_names = [c[0] for c in cur.description] @@ -115,7 +115,7 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona try: cur = db_connection.client.cursor() logger.info(f"Executing : Row Count | {selectStatement}") - self.__execute_sql(cur, selectStatement) + self.__execute_sql(cur, selectStatement, parameters=parameters) data = cur.fetchall() if db_connection.module_name in ["sqlite3", "ibm_db", "ibm_db_dbi", "pyodbc"]: return len(data) @@ -154,7 +154,7 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio try: cur = db_connection.client.cursor() logger.info("Executing : Description | {selectStatement}") - self.__execute_sql(cur, selectStatement) + self.__execute_sql(cur, selectStatement, parameters=parameters) description = list(cur.description) if sys.version_info[0] < 3: for row in range(0, len(description)): @@ -356,7 +356,7 @@ def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Opti try: cur = db_connection.client.cursor() logger.info(f"Executing : Execute SQL String | {sqlString}") - self.__execute_sql(cur, sqlString) + self.__execute_sql(cur, sqlString, parameters=parameters) if not sansTran: db_connection.client.commit() finally: From 3f94ad43d8758181b3323f501baa2229372e599b Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:32:00 +0200 Subject: [PATCH 10/16] Update basic_tests.robot added test using parameters argument --- test/tests/common_tests/basic_tests.robot | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 2fe17bc..2f28772 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -16,6 +16,10 @@ SQL Statement Ending With Semicolon Works SQL Statement Ending Without Semicolon Works Query SELECT * FROM person; +SQL Statement With Parameters Works + ${output}= Query SELECT * FROM person WHERE id < ? parameters=[1] + Length Should Be ${output} 1 + Create Person Table [Setup] Log No setup for this test ${output}= Create Person Table From 16a281e51ffea0106ceec745deaaa8523f406390 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:55:05 +0200 Subject: [PATCH 11/16] Update basic_tests.robot use correct list parameter --- test/tests/common_tests/basic_tests.robot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 2f28772..386c5ae 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -17,7 +17,8 @@ SQL Statement Ending Without Semicolon Works Query SELECT * FROM person; SQL Statement With Parameters Works - ${output}= Query SELECT * FROM person WHERE id < ? parameters=[1] + @{params}= Create List 2 + ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} Length Should Be ${output} 1 Create Person Table From 5f2c0226fcb4e45fd4928d0a2f0cee1b62fc9043 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Mon, 14 Aug 2023 13:42:47 +0200 Subject: [PATCH 12/16] Update basic_tests.robot try different syntax for postgres, oracle etc. --- test/tests/common_tests/basic_tests.robot | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 386c5ae..8f1f28e 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -18,7 +18,11 @@ SQL Statement Ending Without Semicolon Works SQL Statement With Parameters Works @{params}= Create List 2 - ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} + TRY + ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} + EXCEPT + ${output}= Query SELECT * FROM person WHERE id < :id parameters=${params} + END Length Should Be ${output} 1 Create Person Table From 6ce795c32c215526ec3590e9b6376ca86921dc94 Mon Sep 17 00:00:00 2001 From: carnegiemedal <126577777+carnegiemedal@users.noreply.github.com> Date: Mon, 14 Aug 2023 14:09:11 +0200 Subject: [PATCH 13/16] Update basic_tests.robot extra syntax alternative for postgres, pymsql --- test/tests/common_tests/basic_tests.robot | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 8f1f28e..4db398c 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -18,11 +18,15 @@ SQL Statement Ending Without Semicolon Works SQL Statement With Parameters Works @{params}= Create List 2 - TRY - ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} - EXCEPT + + IF "${DB_MODULE}" in ["oracledb"] ${output}= Query SELECT * FROM person WHERE id < :id parameters=${params} + ELSE IF "${DB_MODULE}" in ["sqlite3", "pyodbc"] + ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} + ELSE + ${output}= Query SELECT * FROM person WHERE id < %s parameters=${params} END + Length Should Be ${output} 1 Create Person Table From 8902334e6b7748ba085e13f74b21f2d87ecd3cf4 Mon Sep 17 00:00:00 2001 From: Bartlomiej Hirsz Date: Sun, 19 Nov 2023 16:56:23 +0100 Subject: [PATCH 14/16] Update documentation with parameters examples --- src/DatabaseLibrary/query.py | 52 ++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/src/DatabaseLibrary/query.py b/src/DatabaseLibrary/query.py index 4c4bc2b..01a4842 100644 --- a/src/DatabaseLibrary/query.py +++ b/src/DatabaseLibrary/query.py @@ -28,8 +28,7 @@ def query( self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None ): """ - Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set - optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. + Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set optional input ``returnAsDict`` to True to return values as a list of dictionaries. Use optional ``alias`` parameter to specify what connection should be used for the query if you have more @@ -61,7 +60,12 @@ def query( And get the following See, Franz Allan - Using optional ``sansTran`` to run command without an explicit transaction commit or rollback: + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person | + | Query | SELECT * FROM %s | parameters=${parameters} | + + Use optional ``sansTran`` to run command without an explicit transaction commit or rollback: | @{queryResults} | Query | SELECT * FROM person | True | """ db_connection = self.connection_store.get_connection(alias) @@ -81,8 +85,7 @@ def query( def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ - Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. Set - optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. + Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. For example, given we have a table `person` with the following data: | id | first_name | last_name | @@ -107,7 +110,12 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional ``sansTran`` to run command without an explicit transaction commit or rollback: + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person | + | ${rowCount} | Row Count | SELECT * FROM %s | parameters=${parameters} | + + Use optional ``sansTran`` to run command without an explicit transaction commit or rollback: | ${rowCount} | Row Count | SELECT * FROM person | True | """ db_connection = self.connection_store.get_connection(alias) @@ -126,8 +134,7 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ - Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. Set - optional input ``sansTran` to True to run command without an explicit transaction commit or rollback. + Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. For example, given we have a table `person` with the following data: | id | first_name | last_name | @@ -146,6 +153,11 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person | + | ${desc} | Description | SELECT * FROM %s | parameters=${parameters} | + Using optional `sansTran` to run command without an explicit transaction commit or rollback: | @{queryResults} | Description | SELECT * FROM person | True | """ @@ -166,8 +178,7 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, alias: Optional[str] = None): """ - Delete all the rows within a given table. Set optional input `sansTran` to True to run command without an - explicit transaction commit or rollback. + Delete all the rows within a given table. For example, given we have a table `person` in a database @@ -184,7 +195,7 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | Delete All Rows From Table | person | True | """ db_connection = self.connection_store.get_connection(alias) @@ -207,8 +218,7 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, alias: Optional[str] = None): """ Executes the content of the `sqlScriptFileName` as SQL commands. Useful for setting the database to a known - state before running your tests, or clearing out your test data after running each a test. Set optional input - `sansTran` to True to run command without an explicit transaction commit or rollback. + state before running your tests, or clearing out your test data after running each a test. Sample usage : | Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql | @@ -262,7 +272,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql | True | """ db_connection = self.connection_store.get_connection(alias) @@ -333,8 +343,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None): """ - Executes the sqlString as SQL commands. Useful to pass arguments to your sql. Set optional input `sansTran` to - True to run command without an explicit transaction commit or rollback. + Executes the sqlString as SQL commands. Useful to pass arguments to your sql. SQL commands are expected to be delimited by a semicolon (';'). @@ -348,7 +357,12 @@ def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Opti Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different + depending on the database client): + | parameters | Create List | person_employee_table | + | Execute Sql String | SELECT * FROM %s | parameters=${parameters} | + + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | Execute Sql String | DELETE FROM person_employee_table; DELETE FROM person_table | True | """ db_connection = self.connection_store.get_connection(alias) @@ -381,8 +395,6 @@ def call_stored_procedure( It also depends on the database, how the procedure returns the values - as params or as result sets. E.g. calling a procedure in *PostgreSQL* returns even a single value of an OUT param as a result set. - Set optional input `sansTran` to True to run command without an explicit transaction commit or rollback. - Simple example: | @{Params} = | Create List | Jerry | out_second_name | | @{Param values} @{Result sets} = | Call Stored Procedure | Get_second_name | ${Params} | @@ -404,7 +416,7 @@ def call_stored_procedure( Use optional ``alias`` parameter to specify what connection should be used for the query if you have more than one connection open. - Using optional `sansTran` to run command without an explicit transaction commit or rollback: + Use optional `sansTran` to run command without an explicit transaction commit or rollback: | @{Param values} @{Result sets} = | Call Stored Procedure | DBName.SchemaName.StoredProcName | ${Params} | True | """ db_connection = self.connection_store.get_connection(alias) From 2981b161d2b9494df0b4ad8e48190d6f868fc9ee Mon Sep 17 00:00:00 2001 From: Bartlomiej Hirsz Date: Mon, 20 Nov 2023 17:59:12 +0100 Subject: [PATCH 15/16] Add missing parameters arg to assertions keywords --- src/DatabaseLibrary/assertion.py | 40 +++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/DatabaseLibrary/assertion.py b/src/DatabaseLibrary/assertion.py index 64a8723..7e0862f 100644 --- a/src/DatabaseLibrary/assertion.py +++ b/src/DatabaseLibrary/assertion.py @@ -11,7 +11,7 @@ # 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 typing import Optional +from typing import List, Optional from robot.api import logger @@ -22,7 +22,12 @@ class Assertion: """ def check_if_exists_in_database( - self, selectStatement: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None + self, + selectStatement: str, + sansTran: bool = False, + msg: Optional[str] = None, + alias: Optional[str] = None, + parameters: Optional[List] = None, ): """ Check if any row would be returned by given the input ``selectStatement``. If there are no results, then this will @@ -43,13 +48,18 @@ def check_if_exists_in_database( | Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | sansTran=True | """ logger.info(f"Executing : Check If Exists In Database | {selectStatement}") - if not self.query(selectStatement, sansTran, alias=alias): + if not self.query(selectStatement, sansTran, alias=alias, parameters=parameters): raise AssertionError( msg or f"Expected to have have at least one row, but got 0 rows from: '{selectStatement}'" ) def check_if_not_exists_in_database( - self, selectStatement: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None + self, + selectStatement: str, + sansTran: bool = False, + msg: Optional[str] = None, + alias: Optional[str] = None, + parameters: Optional[List] = None, ): """ This is the negation of `check_if_exists_in_database`. @@ -71,14 +81,19 @@ def check_if_not_exists_in_database( | Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | sansTran=True | """ logger.info(f"Executing : Check If Not Exists In Database | {selectStatement}") - query_results = self.query(selectStatement, sansTran, alias=alias) + query_results = self.query(selectStatement, sansTran, alias=alias, parameters=parameters) if query_results: raise AssertionError( msg or f"Expected to have have no rows from '{selectStatement}', but got some rows: {query_results}" ) def row_count_is_0( - self, selectStatement: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None + self, + selectStatement: str, + sansTran: bool = False, + msg: Optional[str] = None, + alias: Optional[str] = None, + parameters: Optional[List] = None, ): """ Check if any rows are returned from the submitted ``selectStatement``. If there are, then this will throw an @@ -99,7 +114,7 @@ def row_count_is_0( | Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | sansTran=True | """ logger.info(f"Executing : Row Count Is 0 | {selectStatement}") - num_rows = self.row_count(selectStatement, sansTran, alias=alias) + num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters) if num_rows > 0: raise AssertionError(msg or f"Expected 0 rows, but {num_rows} were returned from: '{selectStatement}'") @@ -110,6 +125,7 @@ def row_count_is_equal_to_x( sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None, + parameters: Optional[List] = None, ): """ Check if the number of rows returned from ``selectStatement`` is equal to the value submitted. If not, then this @@ -129,7 +145,7 @@ def row_count_is_equal_to_x( | Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | sansTran=True | """ logger.info(f"Executing : Row Count Is Equal To X | {selectStatement} | {numRows}") - num_rows = self.row_count(selectStatement, sansTran, alias=alias) + num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters) if num_rows != int(numRows.encode("ascii")): raise AssertionError( msg or f"Expected {numRows} rows, but {num_rows} were returned from: '{selectStatement}'" @@ -142,6 +158,7 @@ def row_count_is_greater_than_x( sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None, + parameters: Optional[List] = None, ): """ Check if the number of rows returned from ``selectStatement`` is greater than the value submitted. If not, then @@ -161,7 +178,7 @@ def row_count_is_greater_than_x( | Row Count Is Greater Than X | SELECT id FROM person | 1 | sansTran=True | """ logger.info(f"Executing : Row Count Is Greater Than X | {selectStatement} | {numRows}") - num_rows = self.row_count(selectStatement, sansTran, alias=alias) + num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters) if num_rows <= int(numRows.encode("ascii")): raise AssertionError( msg or f"Expected more than {numRows} rows, but {num_rows} were returned from '{selectStatement}'" @@ -174,6 +191,7 @@ def row_count_is_less_than_x( sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None, + parameters: Optional[List] = None, ): """ Check if the number of rows returned from ``selectStatement`` is less than the value submitted. If not, then this @@ -194,7 +212,7 @@ def row_count_is_less_than_x( """ logger.info(f"Executing : Row Count Is Less Than X | {selectStatement} | {numRows}") - num_rows = self.row_count(selectStatement, sansTran, alias=alias) + num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters) if num_rows >= int(numRows.encode("ascii")): raise AssertionError( msg or f"Expected less than {numRows} rows, but {num_rows} were returned from '{selectStatement}'" @@ -204,7 +222,7 @@ def table_must_exist( self, tableName: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None ): """ - Check if the table given exists in the database. + Check if the given table exists in the database. Set optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback. From 79560d7cd147ad562a518df8a58413c92ec26ddc Mon Sep 17 00:00:00 2001 From: amochin Date: Mon, 20 Nov 2023 18:01:29 +0100 Subject: [PATCH 16/16] Extend tests for queries with params and move them into a separe test suite --- test/tests/common_tests/basic_tests.robot | 13 ----- test/tests/common_tests/query_params.robot | 67 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 test/tests/common_tests/query_params.robot diff --git a/test/tests/common_tests/basic_tests.robot b/test/tests/common_tests/basic_tests.robot index 4db398c..2fe17bc 100644 --- a/test/tests/common_tests/basic_tests.robot +++ b/test/tests/common_tests/basic_tests.robot @@ -16,19 +16,6 @@ SQL Statement Ending With Semicolon Works SQL Statement Ending Without Semicolon Works Query SELECT * FROM person; -SQL Statement With Parameters Works - @{params}= Create List 2 - - IF "${DB_MODULE}" in ["oracledb"] - ${output}= Query SELECT * FROM person WHERE id < :id parameters=${params} - ELSE IF "${DB_MODULE}" in ["sqlite3", "pyodbc"] - ${output}= Query SELECT * FROM person WHERE id < ? parameters=${params} - ELSE - ${output}= Query SELECT * FROM person WHERE id < %s parameters=${params} - END - - Length Should Be ${output} 1 - Create Person Table [Setup] Log No setup for this test ${output}= Create Person Table diff --git a/test/tests/common_tests/query_params.robot b/test/tests/common_tests/query_params.robot new file mode 100644 index 0000000..839059b --- /dev/null +++ b/test/tests/common_tests/query_params.robot @@ -0,0 +1,67 @@ +*** Settings *** +Documentation Keywords with query params as seperate arguments work across all databases. + +Resource ../../resources/common.resource + +Suite Setup Connect To DB And Build Query +Suite Teardown Disconnect From Database +Test Setup Create Person Table And Insert Data +Test Teardown Drop Tables Person And Foobar + + +*** Variables *** +@{PARAMS} Franz Allan + + +*** Keywords *** +Connect To DB And Build Query + Connect To DB + Build Query String With Params + +Build Query String With Params + ${sql}= Set Variable SELECT id FROM person WHERE FIRST_NAME= + IF "${DB_MODULE}" in ["oracledb", "cx_Oracle"] + ${sql}= Catenate ${sql} :id + ELSE IF "${DB_MODULE}" in ["sqlite3", "pyodbc"] + ${sql}= Catenate ${sql} ? + ELSE + ${sql}= Catenate ${sql} %s + END + Set Suite Variable ${QUERY} ${sql} + + +*** Test Cases *** +Query + ${out}= Query ${QUERY} parameters=${PARAMS} + Length Should Be ${out} 1 + +Row Count + ${out}= Row Count ${QUERY} parameters=${PARAMS} + Should Be Equal As Strings ${out} 1 + +Description + ${out}= Description ${QUERY} parameters=${PARAMS} + Length Should Be ${out} 1 + +Execute SQL String + Execute Sql String ${QUERY} parameters=${PARAMS} + +Check If Exists In DB + Check If Exists In Database ${QUERY} parameters=${PARAMS} + +Check If Not Exists In DB + @{Wrong params}= Create List Joe + Check If Not Exists In Database ${QUERY} parameters=${Wrong params} + +Row Count is 0 + @{Wrong params}= Create List Joe + Row Count is 0 ${QUERY} parameters=${Wrong params} + +Row Count is Equal to X + Row Count is Equal to X ${QUERY} 1 parameters=${PARAMS} + +Row Count is Less Than X + Row Count is Less Than X ${QUERY} 5 parameters=${PARAMS} + +Row Count is Greater Than X + Row Count is Greater Than X ${QUERY} 0 parameters=${PARAMS}