Skip to content

Commit 170050b

Browse files
authored
Merge pull request #198 from MarketSquare/feature/execute_parameters
Pass parameters list to cursor.execute
2 parents d562452 + e929dc0 commit 170050b

3 files changed

Lines changed: 140 additions & 41 deletions

File tree

src/DatabaseLibrary/assertion.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
from typing import Optional
14+
from typing import List, Optional
1515

1616
from robot.api import logger
1717

@@ -22,7 +22,12 @@ class Assertion:
2222
"""
2323

2424
def check_if_exists_in_database(
25-
self, selectStatement: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None
25+
self,
26+
selectStatement: str,
27+
sansTran: bool = False,
28+
msg: Optional[str] = None,
29+
alias: Optional[str] = None,
30+
parameters: Optional[List] = None,
2631
):
2732
"""
2833
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(
4348
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | sansTran=True |
4449
"""
4550
logger.info(f"Executing : Check If Exists In Database | {selectStatement}")
46-
if not self.query(selectStatement, sansTran, alias=alias):
51+
if not self.query(selectStatement, sansTran, alias=alias, parameters=parameters):
4752
raise AssertionError(
4853
msg or f"Expected to have have at least one row, but got 0 rows from: '{selectStatement}'"
4954
)
5055

5156
def check_if_not_exists_in_database(
52-
self, selectStatement: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None
57+
self,
58+
selectStatement: str,
59+
sansTran: bool = False,
60+
msg: Optional[str] = None,
61+
alias: Optional[str] = None,
62+
parameters: Optional[List] = None,
5363
):
5464
"""
5565
This is the negation of `check_if_exists_in_database`.
@@ -71,14 +81,19 @@ def check_if_not_exists_in_database(
7181
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | sansTran=True |
7282
"""
7383
logger.info(f"Executing : Check If Not Exists In Database | {selectStatement}")
74-
query_results = self.query(selectStatement, sansTran, alias=alias)
84+
query_results = self.query(selectStatement, sansTran, alias=alias, parameters=parameters)
7585
if query_results:
7686
raise AssertionError(
7787
msg or f"Expected to have have no rows from '{selectStatement}', but got some rows: {query_results}"
7888
)
7989

8090
def row_count_is_0(
81-
self, selectStatement: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None
91+
self,
92+
selectStatement: str,
93+
sansTran: bool = False,
94+
msg: Optional[str] = None,
95+
alias: Optional[str] = None,
96+
parameters: Optional[List] = None,
8297
):
8398
"""
8499
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(
99114
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | sansTran=True |
100115
"""
101116
logger.info(f"Executing : Row Count Is 0 | {selectStatement}")
102-
num_rows = self.row_count(selectStatement, sansTran, alias=alias)
117+
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
103118
if num_rows > 0:
104119
raise AssertionError(msg or f"Expected 0 rows, but {num_rows} were returned from: '{selectStatement}'")
105120

@@ -110,6 +125,7 @@ def row_count_is_equal_to_x(
110125
sansTran: bool = False,
111126
msg: Optional[str] = None,
112127
alias: Optional[str] = None,
128+
parameters: Optional[List] = None,
113129
):
114130
"""
115131
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(
129145
| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | sansTran=True |
130146
"""
131147
logger.info(f"Executing : Row Count Is Equal To X | {selectStatement} | {numRows}")
132-
num_rows = self.row_count(selectStatement, sansTran, alias=alias)
148+
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
133149
if num_rows != int(numRows.encode("ascii")):
134150
raise AssertionError(
135151
msg or f"Expected {numRows} rows, but {num_rows} were returned from: '{selectStatement}'"
@@ -142,6 +158,7 @@ def row_count_is_greater_than_x(
142158
sansTran: bool = False,
143159
msg: Optional[str] = None,
144160
alias: Optional[str] = None,
161+
parameters: Optional[List] = None,
145162
):
146163
"""
147164
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(
161178
| Row Count Is Greater Than X | SELECT id FROM person | 1 | sansTran=True |
162179
"""
163180
logger.info(f"Executing : Row Count Is Greater Than X | {selectStatement} | {numRows}")
164-
num_rows = self.row_count(selectStatement, sansTran, alias=alias)
181+
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
165182
if num_rows <= int(numRows.encode("ascii")):
166183
raise AssertionError(
167184
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(
174191
sansTran: bool = False,
175192
msg: Optional[str] = None,
176193
alias: Optional[str] = None,
194+
parameters: Optional[List] = None,
177195
):
178196
"""
179197
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(
194212
195213
"""
196214
logger.info(f"Executing : Row Count Is Less Than X | {selectStatement} | {numRows}")
197-
num_rows = self.row_count(selectStatement, sansTran, alias=alias)
215+
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
198216
if num_rows >= int(numRows.encode("ascii")):
199217
raise AssertionError(
200218
msg or f"Expected less than {numRows} rows, but {num_rows} were returned from '{selectStatement}'"
@@ -204,7 +222,7 @@ def table_must_exist(
204222
self, tableName: str, sansTran: bool = False, msg: Optional[str] = None, alias: Optional[str] = None
205223
):
206224
"""
207-
Check if the table given exists in the database.
225+
Check if the given table exists in the database.
208226
209227
Set optional input ``sansTran`` to True to run command without an
210228
explicit transaction commit or rollback.

src/DatabaseLibrary/query.py

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@ class Query:
2525
"""
2626

2727
def query(
28-
self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None
28+
self, selectStatement: str, sansTran: bool = False, returnAsDict: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None
2929
):
3030
"""
31-
Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples. Set
32-
optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback.
31+
Uses the input ``selectStatement`` to query for the values that will be returned as a list of tuples.
3332
Set optional input ``returnAsDict`` to True to return values as a list of dictionaries.
3433
3534
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
@@ -61,15 +60,20 @@ def query(
6160
And get the following
6261
See, Franz Allan
6362
64-
Using optional ``sansTran`` to run command without an explicit transaction commit or rollback:
63+
Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
64+
depending on the database client):
65+
| parameters | Create List | person |
66+
| Query | SELECT * FROM %s | parameters=${parameters} |
67+
68+
Use optional ``sansTran`` to run command without an explicit transaction commit or rollback:
6569
| @{queryResults} | Query | SELECT * FROM person | True |
6670
"""
6771
db_connection = self.connection_store.get_connection(alias)
6872
cur = None
6973
try:
7074
cur = db_connection.client.cursor()
7175
logger.info(f"Executing : Query | {selectStatement} ")
72-
self.__execute_sql(cur, selectStatement)
76+
self.__execute_sql(cur, selectStatement, parameters=parameters)
7377
all_rows = cur.fetchall()
7478
if returnAsDict:
7579
col_names = [c[0] for c in cur.description]
@@ -79,10 +83,9 @@ def query(
7983
if cur and not sansTran:
8084
db_connection.client.rollback()
8185

82-
def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None):
86+
def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None):
8387
"""
84-
Uses the input ``selectStatement`` to query the database and returns the number of rows from the query. Set
85-
optional input ``sansTran`` to True to run command without an explicit transaction commit or rollback.
88+
Uses the input ``selectStatement`` to query the database and returns the number of rows from the query.
8689
8790
For example, given we have a table `person` with the following data:
8891
| id | first_name | last_name |
@@ -107,15 +110,20 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona
107110
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
108111
than one connection open.
109112
110-
Using optional ``sansTran`` to run command without an explicit transaction commit or rollback:
113+
Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
114+
depending on the database client):
115+
| parameters | Create List | person |
116+
| ${rowCount} | Row Count | SELECT * FROM %s | parameters=${parameters} |
117+
118+
Use optional ``sansTran`` to run command without an explicit transaction commit or rollback:
111119
| ${rowCount} | Row Count | SELECT * FROM person | True |
112120
"""
113121
db_connection = self.connection_store.get_connection(alias)
114122
cur = None
115123
try:
116124
cur = db_connection.client.cursor()
117125
logger.info(f"Executing : Row Count | {selectStatement}")
118-
self.__execute_sql(cur, selectStatement)
126+
self.__execute_sql(cur, selectStatement, parameters=parameters)
119127
data = cur.fetchall()
120128
if db_connection.module_name in ["sqlite3", "ibm_db", "ibm_db_dbi", "pyodbc"]:
121129
return len(data)
@@ -124,10 +132,9 @@ def row_count(self, selectStatement: str, sansTran: bool = False, alias: Optiona
124132
if cur and not sansTran:
125133
db_connection.client.rollback()
126134

127-
def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None):
135+
def description(self, selectStatement: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None):
128136
"""
129-
Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description. Set
130-
optional input ``sansTran` to True to run command without an explicit transaction commit or rollback.
137+
Uses the input ``selectStatement`` to query a table in the db which will be used to determine the description.
131138
132139
For example, given we have a table `person` with the following data:
133140
| id | first_name | last_name |
@@ -146,6 +153,11 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio
146153
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
147154
than one connection open.
148155
156+
Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
157+
depending on the database client):
158+
| parameters | Create List | person |
159+
| ${desc} | Description | SELECT * FROM %s | parameters=${parameters} |
160+
149161
Using optional `sansTran` to run command without an explicit transaction commit or rollback:
150162
| @{queryResults} | Description | SELECT * FROM person | True |
151163
"""
@@ -154,7 +166,7 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio
154166
try:
155167
cur = db_connection.client.cursor()
156168
logger.info("Executing : Description | {selectStatement}")
157-
self.__execute_sql(cur, selectStatement)
169+
self.__execute_sql(cur, selectStatement, parameters=parameters)
158170
description = list(cur.description)
159171
if sys.version_info[0] < 3:
160172
for row in range(0, len(description)):
@@ -166,8 +178,7 @@ def description(self, selectStatement: str, sansTran: bool = False, alias: Optio
166178

167179
def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, alias: Optional[str] = None):
168180
"""
169-
Delete all the rows within a given table. Set optional input `sansTran` to True to run command without an
170-
explicit transaction commit or rollback.
181+
Delete all the rows within a given table.
171182
172183
For example, given we have a table `person` in a database
173184
@@ -184,7 +195,7 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali
184195
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
185196
than one connection open.
186197
187-
Using optional `sansTran` to run command without an explicit transaction commit or rollback:
198+
Use optional `sansTran` to run command without an explicit transaction commit or rollback:
188199
| Delete All Rows From Table | person | True |
189200
"""
190201
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
207218
def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, alias: Optional[str] = None):
208219
"""
209220
Executes the content of the `sqlScriptFileName` as SQL commands. Useful for setting the database to a known
210-
state before running your tests, or clearing out your test data after running each a test. Set optional input
211-
`sansTran` to True to run command without an explicit transaction commit or rollback.
221+
state before running your tests, or clearing out your test data after running each a test.
212222
213223
Sample usage :
214224
| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql |
@@ -262,7 +272,7 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali
262272
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
263273
than one connection open.
264274
265-
Using optional `sansTran` to run command without an explicit transaction commit or rollback:
275+
Use optional `sansTran` to run command without an explicit transaction commit or rollback:
266276
| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql | True |
267277
"""
268278
db_connection = self.connection_store.get_connection(alias)
@@ -331,10 +341,9 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali
331341
if cur and not sansTran:
332342
db_connection.client.rollback()
333343

334-
def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None):
344+
def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Optional[str] = None, parameters: Optional[List] = None):
335345
"""
336-
Executes the sqlString as SQL commands. Useful to pass arguments to your sql. Set optional input `sansTran` to
337-
True to run command without an explicit transaction commit or rollback.
346+
Executes the sqlString as SQL commands. Useful to pass arguments to your sql.
338347
339348
SQL commands are expected to be delimited by a semicolon (';').
340349
@@ -348,15 +357,20 @@ def execute_sql_string(self, sqlString: str, sansTran: bool = False, alias: Opti
348357
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
349358
than one connection open.
350359
351-
Using optional `sansTran` to run command without an explicit transaction commit or rollback:
360+
Use optional ``parameters`` for query variable substitution (variable substitution syntax may be different
361+
depending on the database client):
362+
| parameters | Create List | person_employee_table |
363+
| Execute Sql String | SELECT * FROM %s | parameters=${parameters} |
364+
365+
Use optional `sansTran` to run command without an explicit transaction commit or rollback:
352366
| Execute Sql String | DELETE FROM person_employee_table; DELETE FROM person_table | True |
353367
"""
354368
db_connection = self.connection_store.get_connection(alias)
355369
cur = None
356370
try:
357371
cur = db_connection.client.cursor()
358372
logger.info(f"Executing : Execute SQL String | {sqlString}")
359-
self.__execute_sql(cur, sqlString)
373+
self.__execute_sql(cur, sqlString, parameters=parameters)
360374
if not sansTran:
361375
db_connection.client.commit()
362376
finally:
@@ -381,8 +395,6 @@ def call_stored_procedure(
381395
It also depends on the database, how the procedure returns the values - as params or as result sets.
382396
E.g. calling a procedure in *PostgreSQL* returns even a single value of an OUT param as a result set.
383397
384-
Set optional input `sansTran` to True to run command without an explicit transaction commit or rollback.
385-
386398
Simple example:
387399
| @{Params} = | Create List | Jerry | out_second_name |
388400
| @{Param values} @{Result sets} = | Call Stored Procedure | Get_second_name | ${Params} |
@@ -404,7 +416,7 @@ def call_stored_procedure(
404416
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
405417
than one connection open.
406418
407-
Using optional `sansTran` to run command without an explicit transaction commit or rollback:
419+
Use optional `sansTran` to run command without an explicit transaction commit or rollback:
408420
| @{Param values} @{Result sets} = | Call Stored Procedure | DBName.SchemaName.StoredProcName | ${Params} | True |
409421
"""
410422
db_connection = self.connection_store.get_connection(alias)
@@ -507,7 +519,7 @@ def call_stored_procedure(
507519
if cur and not sansTran:
508520
db_connection.client.rollback()
509521

510-
def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Optional[bool] = None):
522+
def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Optional[bool] = None, parameters: Optional[List] = None):
511523
"""
512524
Runs the `sql_statement` using `cur` as Cursor object.
513525
Use `omit_trailing_semicolon` parameter (bool) for explicit instruction,
@@ -519,5 +531,7 @@ def __execute_sql(self, cur, sql_statement: str, omit_trailing_semicolon: Option
519531
omit_trailing_semicolon = self.omit_trailing_semicolon
520532
if omit_trailing_semicolon:
521533
sql_statement = sql_statement.rstrip(";")
534+
if parameters is None:
535+
parameters = []
522536
logger.debug(f"Executing sql: {sql_statement}")
523-
return cur.execute(sql_statement)
537+
return cur.execute(sql_statement, parameters)

0 commit comments

Comments
 (0)