@@ -293,84 +293,98 @@ def execute_sql_script(
293293 | Execute SQL Script | insert_data_in_person_table.sql | split=False |
294294 """
295295 db_connection = self .connection_store .get_connection (alias )
296- with open ( script_path , encoding = "UTF-8" ) as sql_file :
297- cur = None
298- try :
299- cur = db_connection . client . cursor ()
300- if not split :
296+ cur = None
297+ try :
298+ cur = db_connection . client . cursor ()
299+ if not split :
300+ with open ( script_path , encoding = "UTF-8" ) as sql_file :
301301 logger .info ("Statements splitting disabled - pass entire script content to the database module" )
302302 self ._execute_sql (
303303 cur ,
304304 sql_file .read (),
305305 omit_trailing_semicolon = db_connection .omit_trailing_semicolon ,
306306 )
307- else :
308- logger .info ("Splitting script file into statements..." )
309- statements_to_execute = []
310- current_statement = ""
311- inside_statements_group = False
312- proc_start_pattern = re .compile ("create( or replace)? (procedure|function){1}( )?" )
307+ else :
308+ statements_to_execute = self .split_sql_script (script_path )
309+ for statement in statements_to_execute :
313310 proc_end_pattern = re .compile ("end(?!( if;| loop;| case;| while;| repeat;)).*;()?" )
314- for line in sql_file :
315- line = line .strip ()
316- if line .startswith ("#" ) or line .startswith ("--" ) or line == "/" :
317- continue
318-
319- # check if the line matches the creating procedure regexp pattern
320- if proc_start_pattern .match (line .lower ()):
321- inside_statements_group = True
322- elif line .lower ().startswith ("begin" ):
323- inside_statements_group = True
324-
325- # semicolons inside the line? use them to separate statements
326- # ... but not if they are inside a begin/end block (aka. statements group)
327- sqlFragments = line .split (";" )
328- # no semicolons
329- if len (sqlFragments ) == 1 :
330- current_statement += line + " "
331- continue
311+ line_ends_with_proc_end = re .compile (r"(\s|;)" + proc_end_pattern .pattern + "$" )
312+ omit_semicolon = not line_ends_with_proc_end .search (statement .lower ())
313+ self ._execute_sql (cur , statement , omit_semicolon )
314+ self ._commit_if_needed (db_connection , no_transaction )
315+ except Exception as e :
316+ self ._rollback_and_raise (db_connection , no_transaction , e )
317+
318+ def split_sql_script (
319+ self ,
320+ script_path : str ,
321+ ):
322+ """
323+ Splits the content of the SQL script file loaded from `script_path` into individual SQL commands
324+ and returns them as a list of strings.
325+ SQL commands are expected to be delimited by a semicolon (';').
326+ """
327+ with open (script_path , encoding = "UTF-8" ) as sql_file :
328+ logger .info ("Splitting script file into statements..." )
329+ statements_to_execute = []
330+ current_statement = ""
331+ inside_statements_group = False
332+ proc_start_pattern = re .compile ("create( or replace)? (procedure|function){1}( )?" )
333+ proc_end_pattern = re .compile ("end(?!( if;| loop;| case;| while;| repeat;)).*;()?" )
334+ for line in sql_file :
335+ line = line .strip ()
336+ if line .startswith ("#" ) or line .startswith ("--" ) or line == "/" :
337+ continue
338+
339+ # check if the line matches the creating procedure regexp pattern
340+ if proc_start_pattern .match (line .lower ()):
341+ inside_statements_group = True
342+ elif line .lower ().startswith ("begin" ):
343+ inside_statements_group = True
344+
345+ # semicolons inside the line? use them to separate statements
346+ # ... but not if they are inside a begin/end block (aka. statements group)
347+ sqlFragments = line .split (";" )
348+ # no semicolons
349+ if len (sqlFragments ) == 1 :
350+ current_statement += line + " "
351+ continue
352+ quotes = 0
353+ # "select * from person;" -> ["select..", ""]
354+ for sqlFragment in sqlFragments :
355+ if len (sqlFragment .strip ()) == 0 :
356+ continue
357+
358+ if inside_statements_group :
359+ # if statements inside a begin/end block have semicolns,
360+ # they must persist - even with oracle
361+ sqlFragment += "; "
362+
363+ if proc_end_pattern .match (sqlFragment .lower ()):
364+ inside_statements_group = False
365+ elif proc_start_pattern .match (sqlFragment .lower ()):
366+ inside_statements_group = True
367+ elif sqlFragment .lower ().startswith ("begin" ):
368+ inside_statements_group = True
369+
370+ # check if the semicolon is a part of the value (quoted string)
371+ quotes += sqlFragment .count ("'" )
372+ quotes -= sqlFragment .count ("\\ '" )
373+ inside_quoted_string = quotes % 2 != 0
374+ if inside_quoted_string :
375+ sqlFragment += ";" # restore the semicolon
376+
377+ current_statement += sqlFragment
378+ if not inside_statements_group and not inside_quoted_string :
379+ statements_to_execute .append (current_statement .strip ())
380+ current_statement = ""
332381 quotes = 0
333- # "select * from person;" -> ["select..", ""]
334- for sqlFragment in sqlFragments :
335- if len (sqlFragment .strip ()) == 0 :
336- continue
337-
338- if inside_statements_group :
339- # if statements inside a begin/end block have semicolns,
340- # they must persist - even with oracle
341- sqlFragment += "; "
342-
343- if proc_end_pattern .match (sqlFragment .lower ()):
344- inside_statements_group = False
345- elif proc_start_pattern .match (sqlFragment .lower ()):
346- inside_statements_group = True
347- elif sqlFragment .lower ().startswith ("begin" ):
348- inside_statements_group = True
349-
350- # check if the semicolon is a part of the value (quoted string)
351- quotes += sqlFragment .count ("'" )
352- quotes -= sqlFragment .count ("\\ '" )
353- inside_quoted_string = quotes % 2 != 0
354- if inside_quoted_string :
355- sqlFragment += ";" # restore the semicolon
356-
357- current_statement += sqlFragment
358- if not inside_statements_group and not inside_quoted_string :
359- statements_to_execute .append (current_statement .strip ())
360- current_statement = ""
361- quotes = 0
362-
363- current_statement = current_statement .strip ()
364- if len (current_statement ) != 0 :
365- statements_to_execute .append (current_statement )
366-
367- for statement in statements_to_execute :
368- line_ends_with_proc_end = re .compile (r"(\s|;)" + proc_end_pattern .pattern + "$" )
369- omit_semicolon = not line_ends_with_proc_end .search (statement .lower ())
370- self ._execute_sql (cur , statement , omit_semicolon )
371- self ._commit_if_needed (db_connection , no_transaction )
372- except Exception as e :
373- self ._rollback_and_raise (db_connection , no_transaction , e )
382+
383+ current_statement = current_statement .strip ()
384+ if len (current_statement ) != 0 :
385+ statements_to_execute .append (current_statement )
386+
387+ return statements_to_execute
374388
375389 @renamed_args (
376390 mapping = {
0 commit comments