1818import sys
1919from typing import List , Optional , Tuple
2020
21+ import sqlparse
2122from robot .api import logger
2223from robot .utils .dotdict import DotDict
2324
@@ -263,6 +264,7 @@ def execute_sql_script(
263264 no_transaction : bool = False ,
264265 alias : Optional [str ] = None ,
265266 split : bool = True ,
267+ external_parser = False ,
266268 * ,
267269 sqlScriptFileName : Optional [str ] = None ,
268270 sansTran : Optional [bool ] = None ,
@@ -274,6 +276,8 @@ def execute_sql_script(
274276 Set ``split`` to _False_ to disable this behavior - in this case the entire script content
275277 will be passed to the database module for execution as a single command.
276278
279+ Set `external_parser` to _True_ to use the external `sqlparse` library for splitting the script.
280+
277281 Set ``no_transaction`` to _True_ to run command without explicit transaction commit
278282 or rollback in case of error.
279283 See `Commit behavior` for details.
@@ -305,7 +309,7 @@ def execute_sql_script(
305309 omit_trailing_semicolon = db_connection .omit_trailing_semicolon ,
306310 )
307311 else :
308- statements_to_execute = self .split_sql_script (script_path )
312+ statements_to_execute = self .split_sql_script (script_path , external_parser = external_parser )
309313 for statement in statements_to_execute :
310314 proc_end_pattern = re .compile ("end(?!( if;| loop;| case;| while;| repeat;)).*;()?" )
311315 line_ends_with_proc_end = re .compile (r"(\s|;)" + proc_end_pattern .pattern + "$" )
@@ -318,71 +322,77 @@ def execute_sql_script(
318322 def split_sql_script (
319323 self ,
320324 script_path : str ,
325+ external_parser = False ,
321326 ):
322327 """
323328 Splits the content of the SQL script file loaded from `script_path` into individual SQL commands
324329 and returns them as a list of strings.
325330 SQL commands are expected to be delimited by a semicolon (';').
331+
332+ Set `external_parser` to _True_ to use the external `sqlparse` library.
326333 """
327334 with open (script_path , encoding = "UTF-8" ) as sql_file :
328335 logger .info ("Splitting script file into statements..." )
329336 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 :
337+ if external_parser :
338+ statements_to_execute = sqlparse .split (sql_file .read ())
339+ else :
340+ current_statement = ""
341+ inside_statements_group = False
342+ proc_start_pattern = re .compile ("create( or replace)? (procedure|function){1}( )?" )
343+ proc_end_pattern = re .compile ("end(?!( if;| loop;| case;| while;| repeat;)).*;()?" )
344+ for line in sql_file :
345+ line = line .strip ()
346+ if line .startswith ("#" ) or line .startswith ("--" ) or line == "/" :
356347 continue
357348
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 ()):
349+ # check if the line matches the creating procedure regexp pattern
350+ if proc_start_pattern .match (line .lower ()):
366351 inside_statements_group = True
367- elif sqlFragment .lower ().startswith ("begin" ):
352+ elif line .lower ().startswith ("begin" ):
368353 inside_statements_group = True
369354
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 = ""
381- quotes = 0
382-
383- current_statement = current_statement .strip ()
384- if len (current_statement ) != 0 :
385- statements_to_execute .append (current_statement )
355+ # semicolons inside the line? use them to separate statements
356+ # ... but not if they are inside a begin/end block (aka. statements group)
357+ sqlFragments = line .split (";" )
358+ # no semicolons
359+ if len (sqlFragments ) == 1 :
360+ current_statement += line + " "
361+ continue
362+ quotes = 0
363+ # "select * from person;" -> ["select..", ""]
364+ for sqlFragment in sqlFragments :
365+ if len (sqlFragment .strip ()) == 0 :
366+ continue
367+
368+ if inside_statements_group :
369+ # if statements inside a begin/end block have semicolns,
370+ # they must persist - even with oracle
371+ sqlFragment += "; "
372+
373+ if proc_end_pattern .match (sqlFragment .lower ()):
374+ inside_statements_group = False
375+ elif proc_start_pattern .match (sqlFragment .lower ()):
376+ inside_statements_group = True
377+ elif sqlFragment .lower ().startswith ("begin" ):
378+ inside_statements_group = True
379+
380+ # check if the semicolon is a part of the value (quoted string)
381+ quotes += sqlFragment .count ("'" )
382+ quotes -= sqlFragment .count ("\\ '" )
383+ inside_quoted_string = quotes % 2 != 0
384+ if inside_quoted_string :
385+ sqlFragment += ";" # restore the semicolon
386+
387+ current_statement += sqlFragment
388+ if not inside_statements_group and not inside_quoted_string :
389+ statements_to_execute .append (current_statement .strip ())
390+ current_statement = ""
391+ quotes = 0
392+
393+ current_statement = current_statement .strip ()
394+ if len (current_statement ) != 0 :
395+ statements_to_execute .append (current_statement )
386396
387397 return statements_to_execute
388398
0 commit comments