• R/O
  • SSH

execsql: Commit

Default repository for execsql.py


Commit MetaInfo

Révisioneaf78dada4c30a035f0710e169b524827d44ba8e (tree)
l'heure2019-12-22 02:20:48
AuteurDreas Nielsen <dreas.nielsen@gmai...>
CommiterDreas Nielsen

Message de Log

Added the BEGIN SQL and END SQL metacommands.

Change Summary

Modification

diff -r 7b9db9939cd1 -r eaf78dada4c3 CHANGELOG.rst
--- a/CHANGELOG.rst Sat Nov 16 08:16:42 2019 -0800
+++ b/CHANGELOG.rst Sat Dec 21 09:20:48 2019 -0800
@@ -1,6 +1,7 @@
11 ========== ========== =================================================================================
22 Version Date Features
33 ========== ========== =================================================================================
4+1.54.0 2019-12-20 Added the BEGIN/END SQL metacommands.
45 1.53.0 2019-10-27 Added Oracle support.
56 1.52.0 2019-10-11 Added export to XML.
67 1.51.0 2019-10-10 Added WHILE and UNTIL loop control to EXECUTE SCRIPT; implemented deferred variable substitution.
diff -r 7b9db9939cd1 -r eaf78dada4c3 doc/source/metacommands.rst
--- a/doc/source/metacommands.rst Sat Nov 16 08:16:42 2019 -0800
+++ b/doc/source/metacommands.rst Sat Dec 21 09:20:48 2019 -0800
@@ -324,6 +324,40 @@
324324 the SQL statements in that incomplete batch will not be committed.
325325
326326
327+
328+.. index:: !BEGIN SQL metacommand
329+
330+.. _beginsql:
331+
332+BEGIN SQL and END SQL
333+---------------------------------------
334+
335+::
336+
337+ BEGIN SQL
338+
339+::
340+
341+ END SQL
342+
343+The BEGIN SQL and END SQL metacommands define a block of lines in the script
344+file that will be treated as a single SQL statement. Within the block of lines
345+defined by these metacommands, a semicolon at the end of the line will *not* be
346+treated as the end of a SQL statement.
347+
348+The primary intended use case for these metacommands is to bracket procedure
349+and function definitions. A function definition may contain multiple SQL
350+statements, each of which is ended by a semicolon, but which should all be sent
351+to the DBMS as a single statement, not as a series of invididual SQL statements.
352+
353+The BEGIN SQL and END SQL metacommands are an alternative to the use of
354+:ref:`line continuation characters <continuationchars>`.
355+
356+Metacommands that appear within a BEGIN/END SQL block will be treated as
357+comments and will be ignored: they will not be executed when the SQL
358+statement is run, and no error message will be produced.
359+
360+
327361 .. index:: ! BEGIN SCRIPT metacommand
328362 single: CREATE SCRIPT metacommand
329363 single: Scripting
diff -r 7b9db9939cd1 -r eaf78dada4c3 doc/source/usage.rst
--- a/doc/source/usage.rst Sat Nov 16 08:16:42 2019 -0800
+++ b/doc/source/usage.rst Sat Dec 21 09:20:48 2019 -0800
@@ -27,13 +27,21 @@
2727 SQL Statement Recognition and SQL Syntax
2828 ----------------------------------------------
2929
30+.. index::
31+ single: Continuation characters
32+
33+.. _continuationchars:
34+
3035 execsql recognizes a SQL statement as consisting of a sequence of
3136 non-comment lines that ends with a line ending with a semicolon. A
3237 backslash ("&#92;") at the end of a line is treated as a line continuation
3338 character. Backslashes do not need to be used for simple SQL
34-statements, but must be used for procedure and function definitions,
39+statements, but can be used for procedure and function definitions,
3540 where there are semicolons within the body of the definition, and a
36-semicolon appears at the end of lines for readability purposes.
41+semicolon appears at the end of lines for readability purposes. The
42+:ref:`BEGIN SQL <beginsql>` and :ref:`END SQL <beginsql>` metacommands
43+can be used to bracket procedure and function definitions instead
44+of using backslashes.
3745 Backslashes may not be used as continuation characters for
3846 :ref:`metacommands <metacommands>`.
3947
diff -r 7b9db9939cd1 -r eaf78dada4c3 execsql/execsql.py
--- a/execsql/execsql.py Sat Nov 16 08:16:42 2019 -0800
+++ b/execsql/execsql.py Sat Dec 21 09:20:48 2019 -0800
@@ -27,12 +27,12 @@
2727 #
2828 # ===============================================================================
2929
30-__version__ = "1.53.3"
31-__vdate = "2019-11-16"
30+__version__ = "1.54.0"
31+__vdate = "2019-12-21"
3232
3333 primary_vno = 1
34-secondary_vno = 53
35-tertiary_vno = 3
34+secondary_vno = 54
35+tertiary_vno = 0
3636
3737 import os
3838 import os.path
@@ -11213,11 +11213,16 @@
1121311213 # The following metacommands are executed IMMEDIATELY during this process:
1121411214 # * BEGIN SCRIPT <scriptname>
1121511215 # * END SCRIPT
11216+ # * BEGIN SQL
11217+ # * END SQL
1121611218 beginscript = re.compile(r'^--\s*!x!\s*(?:BEGIN|CREATE)\s+SCRIPT\s+(?P<scriptname>\w+)(?:(?P<paramexpr>\s*\S+.*))?$', re.I)
1121711219 endscript = re.compile(r'^--\s*!x!\s*END\s+SCRIPT(?:\s+(?P<scriptname>\w+))?\s*$', re.I)
11220+ beginsql = re.compile(r'^--\s*!x!\s*BEGIN\s+SQL\s*$', re.I)
11221+ endsql = re.compile(r'^--\s*!x!\s*END\s+SQL\s*$', re.I)
1121811222 execline = re.compile(r'^--\s*!x!\s*(?P<cmd>.+)$', re.I)
1121911223 cmtline = re.compile(r'^--')
1122011224 in_block_cmt = False
11225+ in_block_sql = False
1122111226 sz, dt = file_size_date(sql_file_name)
1122211227 exec_log.log_status_info("Reading script file %s (size: %d; date: %s)" % (sql_file_name, sz, dt))
1122311228 scriptname = os.path.basename(sql_file_name)
@@ -11231,6 +11236,7 @@
1123111236 file_lineno += 1
1123211237 line = line.strip()
1123311238 is_comment_line = False
11239+ comment_match = cmtline.match(line)
1123411240 metacommand_match = execline.match(line)
1123511241 if len(line) > 0:
1123611242 if in_block_cmt:
@@ -11245,50 +11251,63 @@
1124511251 if line[-2:] == u"*/":
1124611252 in_block_cmt = False
1124711253 else:
11248- if cmtline.match(line):
11254+ if comment_match:
1124911255 is_comment_line = not metacommand_match
1125011256 if not is_comment_line:
1125111257 if metacommand_match:
1125211258 if len(currcmd) > 0:
1125311259 write_warning("Incomplete SQL statement starting on line %s at metacommand on line %s." % (sqlline, file_lineno))
11254- begs = beginscript.match(line)
11255- if not begs:
11256- ends = endscript.match(line)
11257- if begs:
11258- # This is a BEGIN SCRIPT metacommand.
11259- scriptname = begs.group('scriptname').lower()
11260- paramnames = None
11261- paramexpr = begs.group('paramexpr')
11262- if paramexpr:
11263- withparams = re.compile(r'(?:\s+WITH)?(?:\s+PARAM(?:ETER)?S)?\s*\(\s*(?P<params>\w+(?:\s*,\s*\w+)*)\s*\)\s*$', re.I)
11264- wp = withparams.match(paramexpr)
11265- if not wp:
11266- raise ErrInfo(type="cmd", command_text=line, other_msg="Invalid BEGIN SCRIPT metacommand on line %s of file %s." % (file_lineno, sql_file_name))
11260+ if beginsql.match(line):
11261+ in_block_sql = True
11262+ if in_block_sql:
11263+ if endsql.match(line):
11264+ in_block_sql = False
11265+ if len(currcmd) > 0:
11266+ cmd = ScriptCmd(sql_file_name, sqlline, 'sql', SqlStmt(currcmd.strip()))
11267+ if len(subscript_stack) == 0:
11268+ sqllist.append(cmd)
11269+ else:
11270+ subscript_stack[-1].add(cmd)
11271+ currcmd = ''
11272+ else:
11273+ begs = beginscript.match(line)
11274+ if not begs:
11275+ ends = endscript.match(line)
11276+ if begs:
11277+ # This is a BEGIN SCRIPT metacommand.
11278+ scriptname = begs.group('scriptname').lower()
11279+ paramnames = None
11280+ paramexpr = begs.group('paramexpr')
11281+ if paramexpr:
11282+ withparams = re.compile(r'(?:\s+WITH)?(?:\s+PARAM(?:ETER)?S)?\s*\(\s*(?P<params>\w+(?:\s*,\s*\w+)*)\s*\)\s*$', re.I)
11283+ wp = withparams.match(paramexpr)
11284+ if not wp:
11285+ raise ErrInfo(type="cmd", command_text=line, other_msg="Invalid BEGIN SCRIPT metacommand on line %s of file %s." % (file_lineno, sql_file_name))
11286+ else:
11287+ param_rx = re.compile(r'\w+', re.I)
11288+ paramnames = re.findall(param_rx, wp.group('params'))
11289+ # If there are no parameter names to pass, paramnames will be None
11290+ subscript_stack.append(CommandList([], scriptname, paramnames))
11291+ elif ends:
11292+ # This is an END SCRIPT metacommand.
11293+ endscriptname = ends.group('scriptname')
11294+ if endscriptname is not None:
11295+ endscriptname = endscriptname.lower()
11296+ if len(subscript_stack) == 0:
11297+ raise ErrInfo(type="cmd", command_text=line, other_msg="Unmatched END SCRIPT metacommand on line %s of file %s." % (file_lineno, sql_file_name))
11298+ if len(currcmd) > 0:
11299+ raise ErrInfo(type="cmd", command_text=line, other_msg="Incomplete SQL statement\n (%s)\nat END SCRIPT metacommand on line %s of file %s." % (currcmd, file_lineno, sql_file_name))
11300+ if endscriptname is not None and endscriptname != scriptname:
11301+ raise ErrInfo(type="cmd", command_text=line, other_msg="Mismatched script name in the END SCRIPT metacommand on line %s of file %s." % (file_lineno, sql_file_name))
11302+ sub_script = subscript_stack.pop()
11303+ savedscripts[sub_script.listname] = sub_script
11304+ else:
11305+ # This is a non-IMMEDIATE metacommand.
11306+ cmd = ScriptCmd(sql_file_name, file_lineno, 'cmd', MetacommandStmt(metacommand_match.group('cmd').strip()))
11307+ if len(subscript_stack) == 0:
11308+ sqllist.append(cmd)
1126711309 else:
11268- param_rx = re.compile(r'\w+', re.I)
11269- paramnames = re.findall(param_rx, wp.group('params'))
11270- # If there are no parameter names to pass, paramnames will be None
11271- subscript_stack.append(CommandList([], scriptname, paramnames))
11272- elif ends:
11273- # This is an END SCRIPT metacommand.
11274- endscriptname = ends.group('scriptname')
11275- if endscriptname is not None:
11276- endscriptname = endscriptname.lower()
11277- if len(subscript_stack) == 0:
11278- raise ErrInfo(type="cmd", command_text=line, other_msg="Unmatched END SCRIPT metacommand on line %s of file %s." % (file_lineno, sql_file_name))
11279- if len(currcmd) > 0:
11280- raise ErrInfo(type="cmd", command_text=line, other_msg="Incomplete SQL statement\n (%s)\nat END SCRIPT metacommand on line %s of file %s." % (currcmd, file_lineno, sql_file_name))
11281- if endscriptname is not None and endscriptname != scriptname:
11282- raise ErrInfo(type="cmd", command_text=line, other_msg="Mismatched script name in the END SCRIPT metacommand on line %s of file %s." % (file_lineno, sql_file_name))
11283- sub_script = subscript_stack.pop()
11284- savedscripts[sub_script.listname] = sub_script
11285- else:
11286- # This is a non-IMMEDIATE metacommand.
11287- cmd = ScriptCmd(sql_file_name, file_lineno, 'cmd', MetacommandStmt(metacommand_match.group('cmd').strip()))
11288- if len(subscript_stack) == 0:
11289- sqllist.append(cmd)
11290- else:
11291- subscript_stack[-1].add(cmd)
11310+ subscript_stack[-1].add(cmd)
1129211311 else:
1129311312 # This line is not a comment and not a metacommand, therefore should be
1129411313 # part of a SQL statement.
@@ -11300,7 +11319,7 @@
1130011319 currcmd = line
1130111320 else:
1130211321 currcmd = u"%s \n%s" % (currcmd, line)
11303- if cmd_end:
11322+ if cmd_end and not in_block_sql:
1130411323 cmd = ScriptCmd(sql_file_name, sqlline, 'sql', SqlStmt(currcmd.strip()))
1130511324 if len(subscript_stack) == 0:
1130611325 sqllist.append(cmd)
diff -r 7b9db9939cd1 -r eaf78dada4c3 setup.py
--- a/setup.py Sat Nov 16 08:16:42 2019 -0800
+++ b/setup.py Sat Dec 21 09:20:48 2019 -0800
@@ -4,7 +4,7 @@
44 long_description = f.read()
55
66 setuptools.setup(name='execsql',
7- version='1.53.3',
7+ version='1.54.0',
88 description="Runs a SQL script against a PostgreSQL, MS-Access, SQLite, MS-SQL-Server, MySQL, MariaDB, Firebird, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables. Data can be exported in 13 different formats, including CSV, TSV, ODS, HTML, JSON, LaTeX, and Markdown tables, and using custom templates.",
99 author='Dreas Nielsen',
1010 author_email='dreas.nielsen@gmail.com',
Afficher sur ancien navigateur de dépôt.