@@ -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 :
0 commit comments