Skip to content

Commit

Permalink
Log query result as a table (#217)
Browse files Browse the repository at this point in the history
* Log query results as a table (fix #147)

* Formatting

* Consistent naming of internal functions
  • Loading branch information
amochin authored Aug 14, 2024
1 parent d1f5b40 commit 261c32a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 11 deletions.
59 changes: 50 additions & 9 deletions src/DatabaseLibrary/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ def query(
try:
cur = db_connection.client.cursor()
logger.info(f"Executing : Query | {selectStatement} ")
self.__execute_sql(cur, selectStatement, parameters=parameters)
self._execute_sql(cur, selectStatement, parameters=parameters)
all_rows = cur.fetchall()
col_names = [c[0] for c in cur.description]
self._log_query_result(col_names, all_rows)
if returnAsDict:
col_names = [c[0] for c in cur.description]
return [dict(zip(col_names, row)) for row in all_rows]
return all_rows
finally:
Expand Down Expand Up @@ -123,13 +124,15 @@ def row_count(
try:
cur = db_connection.client.cursor()
logger.info(f"Executing : Row Count | {selectStatement}")
self.__execute_sql(cur, selectStatement, parameters=parameters)
self._execute_sql(cur, selectStatement, parameters=parameters)
data = cur.fetchall()
col_names = [c[0] for c in cur.description]
if db_connection.module_name in ["sqlite3", "ibm_db", "ibm_db_dbi", "pyodbc"]:
current_row_count = len(data)
else:
current_row_count = cur.rowcount
logger.info(f"Retrieved {current_row_count} rows")
self._log_query_result(col_names, data)
return current_row_count
finally:
if cur and not sansTran:
Expand Down Expand Up @@ -175,7 +178,7 @@ def description(
try:
cur = db_connection.client.cursor()
logger.info("Executing : Description | {selectStatement}")
self.__execute_sql(cur, selectStatement, parameters=parameters)
self._execute_sql(cur, selectStatement, parameters=parameters)
description = list(cur.description)
if sys.version_info[0] < 3:
for row in range(0, len(description)):
Expand Down Expand Up @@ -205,7 +208,7 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali
try:
cur = db_connection.client.cursor()
logger.info(f"Executing : Delete All Rows From Table | {query}")
result = self.__execute_sql(cur, query)
result = self._execute_sql(cur, query)
if result is not None:
if not sansTran:
db_connection.client.commit()
Expand Down Expand Up @@ -289,7 +292,7 @@ def execute_sql_script(
logger.info(f"Executing : Execute SQL Script | {sqlScriptFileName}")
if not split:
logger.info("Statements splitting disabled - pass entire script content to the database module")
self.__execute_sql(cur, sql_file.read())
self._execute_sql(cur, sql_file.read())
else:
logger.info("Splitting script file into statements...")
statements_to_execute = []
Expand Down Expand Up @@ -355,7 +358,7 @@ def execute_sql_script(
logger.info(f"Executing statement from script file: {statement}")
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)
if not sansTran:
db_connection.client.commit()
finally:
Expand Down Expand Up @@ -400,7 +403,7 @@ def execute_sql_string(
try:
cur = db_connection.client.cursor()
logger.info(f"Executing : Execute SQL String | {sqlString}")
self.__execute_sql(cur, sqlString, omit_trailing_semicolon=omitTrailingSemicolon, parameters=parameters)
self._execute_sql(cur, sqlString, omit_trailing_semicolon=omitTrailingSemicolon, parameters=parameters)
if not sansTran:
db_connection.client.commit()
finally:
Expand Down Expand Up @@ -549,7 +552,7 @@ def call_stored_procedure(
if cur and not sansTran:
db_connection.client.rollback()

def __execute_sql(
def _execute_sql(
self,
cur,
sql_statement: str,
Expand All @@ -573,3 +576,41 @@ def __execute_sql(
else:
logger.debug(f"Executing sql '{sql_statement}' with parameters: {parameters}")
return cur.execute(sql_statement, parameters)

def _log_query_result(self, col_names, result_rows, log_head=50):
"""
Logs the `result_rows` of a query in RF log as a HTML table.
The `col_names` are needed for the table header.
Max. `log_head` rows are logged (`0` disables the limit).
"""
cell_border_and_align = "border: 1px solid rgb(160 160 160);padding: 8px 10px;text-align: center;"
table_border = "2px solid rgb(140 140 140)"
row_index_color = "#d6ecd4"
msg = f'<div style="max-width: 100%; overflow-x: auto;">'
msg += f'<table style="width: auto; border-collapse: collapse; border: {table_border}">'
msg += f'<caption style="text-align: left; font-weight: bold; padding: 5px;">Query returned {len(result_rows)} rows</caption>'
msg += "<tr>"
msg += f'<th scope="col" style="background-color: {row_index_color}; {cell_border_and_align}">Row</th>'
for col in col_names:
msg += f'<th scope="col" style="background-color: #505050; color: #fff;{cell_border_and_align}">{col}</th>'
msg += "</tr>"
table_truncated = False
for i, row in enumerate(result_rows):
if log_head and i >= log_head:
table_truncated = True
break
row_style = ""
if i % 2 == 0:
row_style = ' style="background-color: #eee;"'
msg += f"<tr{row_style}>"
msg += f'<th scope="row" style="background-color: {row_index_color};{cell_border_and_align}">{i}</th>'
for cell in row:
msg += f'<td style="{cell_border_and_align}">{cell}</td>'
msg += "</tr>"
msg += "</table>"
if table_truncated:
msg += (
f'<p style="font-weight: bold;">Log limit of {log_head} rows was reached, the table was truncated</p>'
)
msg += "</div>"
logger.info(msg, html=True)
2 changes: 0 additions & 2 deletions test/tests/common_tests/assertion_retry.robot
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ ${Tolerance} ${0.5}
${Request} SELECT first_name FROM person

*** Test Cases ***


Check Query Results With Timeout - Fast If DB Ready
Check Query Result ${Request} contains Allan retry_timeout=${Timeout} seconds
${End time}= Get Current Date
Expand Down

0 comments on commit 261c32a

Please sign in to comment.