Skip to content

Commit 038d9f6

Browse files
committed
Allow disabling the statement splitting when running an SQL script file
1 parent 352962d commit 038d9f6

2 files changed

Lines changed: 93 additions & 61 deletions

File tree

src/DatabaseLibrary/query.py

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,17 @@ def delete_all_rows_from_table(self, tableName: str, sansTran: bool = False, ali
209209
if cur and not sansTran:
210210
db_connection.client.rollback()
211211

212-
def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, alias: Optional[str] = None):
212+
def execute_sql_script(
213+
self, sqlScriptFileName: str, sansTran: bool = False, split: bool = True, alias: Optional[str] = None
214+
):
213215
"""
214216
Executes the content of the `sqlScriptFileName` as SQL commands. Useful for setting the database to a known
215217
state before running your tests, or clearing out your test data after running each a test.
216218
219+
SQL commands are expected to be delimited by a semicolon (';') - they will be split and executed separately.
220+
You can disable this behaviour setting the parameter `split` to _False_ -
221+
in this case the entire script content will be passed to the database module for execution.
222+
217223
Sample usage :
218224
| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-setup.sql |
219225
| Execute Sql Script | ${EXECDIR}${/}resources${/}DML-setup.sql |
@@ -222,7 +228,6 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali
222228
| Execute Sql Script | ${EXECDIR}${/}resources${/}DML-teardown.sql |
223229
| Execute Sql Script | ${EXECDIR}${/}resources${/}DDL-teardown.sql |
224230
225-
SQL commands are expected to be delimited by a semicolon (';') - they will be executed separately.
226231
227232
For example:
228233
DELETE FROM person_employee_table;
@@ -273,72 +278,77 @@ def execute_sql_script(self, sqlScriptFileName: str, sansTran: bool = False, ali
273278
with open(sqlScriptFileName, encoding="UTF-8") as sql_file:
274279
cur = None
275280
try:
276-
statements_to_execute = []
277281
cur = db_connection.client.cursor()
278282
logger.info(f"Executing : Execute SQL Script | {sqlScriptFileName}")
279-
current_statement = ""
280-
inside_statements_group = False
281-
proc_start_pattern = re.compile("create( or replace)? (procedure|function){1}( )?")
282-
proc_end_pattern = re.compile("end(?!( if;| loop;| case;| while;| repeat;)).*;()?")
283-
for line in sql_file:
284-
line = line.strip()
285-
if line.startswith("#") or line.startswith("--") or line == "/":
286-
continue
287-
288-
# check if the line matches the creating procedure regexp pattern
289-
elif proc_start_pattern.match(line.lower()):
290-
inside_statements_group = True
291-
elif line.lower().startswith("begin"):
292-
inside_statements_group = True
293-
294-
# semicolons inside the line? use them to separate statements
295-
# ... but not if they are inside a begin/end block (aka. statements group)
296-
sqlFragments = line.split(";")
297-
# no semicolons
298-
if len(sqlFragments) == 1:
299-
current_statement += line + " "
300-
continue
301-
quotes = 0
302-
# "select * from person;" -> ["select..", ""]
303-
for sqlFragment in sqlFragments:
304-
if len(sqlFragment.strip()) == 0:
283+
if not split:
284+
logger.info("Statements splitting disabled - pass entire script content to the database module")
285+
self.__execute_sql(cur, sql_file.read())
286+
else:
287+
logger.info("Splitting script file into statements...")
288+
statements_to_execute = []
289+
current_statement = ""
290+
inside_statements_group = False
291+
proc_start_pattern = re.compile("create( or replace)? (procedure|function){1}( )?")
292+
proc_end_pattern = re.compile("end(?!( if;| loop;| case;| while;| repeat;)).*;()?")
293+
for line in sql_file:
294+
line = line.strip()
295+
if line.startswith("#") or line.startswith("--") or line == "/":
305296
continue
306297

307-
if inside_statements_group:
308-
# if statements inside a begin/end block have semicolns,
309-
# they must persist - even with oracle
310-
sqlFragment += "; "
311-
312-
if proc_end_pattern.match(sqlFragment.lower()):
313-
inside_statements_group = False
314-
elif proc_start_pattern.match(sqlFragment.lower()):
298+
# check if the line matches the creating procedure regexp pattern
299+
if proc_start_pattern.match(line.lower()):
315300
inside_statements_group = True
316-
elif sqlFragment.lower().startswith("begin"):
301+
elif line.lower().startswith("begin"):
317302
inside_statements_group = True
318303

319-
# check if the semicolon is a part of the value (quoted string)
320-
quotes += sqlFragment.count("'")
321-
quotes -= sqlFragment.count("\\'")
322-
quotes -= sqlFragment.count("''")
323-
inside_quoted_string = quotes % 2 != 0
324-
if inside_quoted_string:
325-
sqlFragment += ";" # restore the semicolon
326-
327-
current_statement += sqlFragment
328-
if not inside_statements_group and not inside_quoted_string:
329-
statements_to_execute.append(current_statement.strip())
330-
current_statement = ""
331-
quotes = 0
332-
333-
current_statement = current_statement.strip()
334-
if len(current_statement) != 0:
335-
statements_to_execute.append(current_statement)
336-
337-
for statement in statements_to_execute:
338-
logger.info(f"Executing statement from script file: {statement}")
339-
line_ends_with_proc_end = re.compile(r"(\s|;)" + proc_end_pattern.pattern + "$")
340-
omit_semicolon = not line_ends_with_proc_end.search(statement.lower())
341-
self.__execute_sql(cur, statement, omit_semicolon)
304+
# semicolons inside the line? use them to separate statements
305+
# ... but not if they are inside a begin/end block (aka. statements group)
306+
sqlFragments = line.split(";")
307+
# no semicolons
308+
if len(sqlFragments) == 1:
309+
current_statement += line + " "
310+
continue
311+
quotes = 0
312+
# "select * from person;" -> ["select..", ""]
313+
for sqlFragment in sqlFragments:
314+
if len(sqlFragment.strip()) == 0:
315+
continue
316+
317+
if inside_statements_group:
318+
# if statements inside a begin/end block have semicolns,
319+
# they must persist - even with oracle
320+
sqlFragment += "; "
321+
322+
if proc_end_pattern.match(sqlFragment.lower()):
323+
inside_statements_group = False
324+
elif proc_start_pattern.match(sqlFragment.lower()):
325+
inside_statements_group = True
326+
elif sqlFragment.lower().startswith("begin"):
327+
inside_statements_group = True
328+
329+
# check if the semicolon is a part of the value (quoted string)
330+
quotes += sqlFragment.count("'")
331+
quotes -= sqlFragment.count("\\'")
332+
quotes -= sqlFragment.count("''")
333+
inside_quoted_string = quotes % 2 != 0
334+
if inside_quoted_string:
335+
sqlFragment += ";" # restore the semicolon
336+
337+
current_statement += sqlFragment
338+
if not inside_statements_group and not inside_quoted_string:
339+
statements_to_execute.append(current_statement.strip())
340+
current_statement = ""
341+
quotes = 0
342+
343+
current_statement = current_statement.strip()
344+
if len(current_statement) != 0:
345+
statements_to_execute.append(current_statement)
346+
347+
for statement in statements_to_execute:
348+
logger.info(f"Executing statement from script file: {statement}")
349+
line_ends_with_proc_end = re.compile(r"(\s|;)" + proc_end_pattern.pattern + "$")
350+
omit_semicolon = not line_ends_with_proc_end.search(statement.lower())
351+
self.__execute_sql(cur, statement, omit_semicolon)
342352
if not sansTran:
343353
db_connection.client.commit()
344354
finally:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
*** Settings ***
2+
Documentation Tests for the parameter _split_ in the keyword
3+
... _Execute SQL Script_ - special for the issue #184:
4+
... https://github.com/MarketSquare/Robotframework-Database-Library/issues/184
5+
6+
Resource ../../resources/common.resource
7+
Suite Setup Connect To DB
8+
Suite Teardown Disconnect From Database
9+
Test Setup Create Person Table
10+
Test Teardown Drop Tables Person And Foobar
11+
12+
13+
*** Test Cases ***
14+
Split Commands
15+
[Documentation] Such a simple script works always,
16+
... just check if the logs if the parameter value was processed properly
17+
Execute Sql Script ${CURDIR}/../../resources/insert_data_in_person_table.sql split=True
18+
19+
Don't Split Commands
20+
[Documentation] Running such a script as a single statement works for PostgreSQL,
21+
... but fails in Oracle. Check in the logs if the splitting was disabled.
22+
Execute Sql Script ${CURDIR}/../../resources/insert_data_in_person_table.sql split=False

0 commit comments

Comments
 (0)