execsql2 2.16.8__py3-none-any.whl → 2.16.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- execsql/debug/repl.py +59 -0
- execsql/format.py +2 -0
- execsql/metacommands/debug.py +89 -1
- execsql/metacommands/dispatch.py +18 -0
- execsql/script/ast.py +43 -6
- execsql/script/executor.py +28 -13
- execsql/script/parser.py +135 -6
- execsql/utils/fileio.py +9 -1
- {execsql2-2.16.8.dist-info → execsql2-2.16.12.dist-info}/METADATA +1 -1
- {execsql2-2.16.8.dist-info → execsql2-2.16.12.dist-info}/RECORD +29 -29
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/execsql.conf +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.16.8.dist-info → execsql2-2.16.12.dist-info}/WHEEL +0 -0
- {execsql2-2.16.8.dist-info → execsql2-2.16.12.dist-info}/entry_points.txt +0 -0
- {execsql2-2.16.8.dist-info → execsql2-2.16.12.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.16.8.dist-info → execsql2-2.16.12.dist-info}/licenses/NOTICE +0 -0
execsql/debug/repl.py
CHANGED
|
@@ -111,6 +111,8 @@ _HELP_COMMANDS = [
|
|
|
111
111
|
(".where", ".w", "Show the current script location and upcoming statement"),
|
|
112
112
|
(".stack", "", "Show the command-list stack (script name, line, depth)"),
|
|
113
113
|
(".set VAR VAL", ".s", "Set or update a substitution variable"),
|
|
114
|
+
(".scripts", "", "List all registered SCRIPT definitions"),
|
|
115
|
+
(".scripts NAME", "", "Show detail for a specific SCRIPT"),
|
|
114
116
|
(".help", ".h", "Show this help text"),
|
|
115
117
|
]
|
|
116
118
|
|
|
@@ -290,6 +292,12 @@ def _handle_dot_command(line: str) -> None:
|
|
|
290
292
|
varname = parts[0]
|
|
291
293
|
value = parts[1] if len(parts) > 1 else ""
|
|
292
294
|
_set_var(varname, value)
|
|
295
|
+
elif cmd.startswith("scripts"):
|
|
296
|
+
rest = cmd[7:].strip()
|
|
297
|
+
if rest:
|
|
298
|
+
_print_script_detail(rest)
|
|
299
|
+
else:
|
|
300
|
+
_print_scripts()
|
|
293
301
|
else:
|
|
294
302
|
_write(f" {_c(_RED, 'Unknown command:')} {line!r}. Type '.help' for available commands.\n")
|
|
295
303
|
|
|
@@ -507,3 +515,54 @@ def _set_var(varname: str, value: str) -> None:
|
|
|
507
515
|
else:
|
|
508
516
|
subvars.add_substitution(varname, value)
|
|
509
517
|
_write(f" {_c(_CYAN, varname)} {_c(_DIM, '=')} {value}\n")
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _print_scripts() -> None:
|
|
521
|
+
"""Print all registered SCRIPT definitions."""
|
|
522
|
+
from execsql.metacommands.debug import _format_script_signature, _format_script_source
|
|
523
|
+
|
|
524
|
+
scripts = _state.ast_scripts
|
|
525
|
+
if not scripts:
|
|
526
|
+
_write(" (no scripts registered)\n\n")
|
|
527
|
+
return
|
|
528
|
+
_write_rule(f" {_c(_BOLD + _YELLOW, 'Scripts')} {_c(_DIM, f'({len(scripts)})')} ")
|
|
529
|
+
sigs = {name: _format_script_signature(name, block.param_defs) for name, block in scripts.items()}
|
|
530
|
+
max_sig = max(len(s) for s in sigs.values())
|
|
531
|
+
for name, block in scripts.items():
|
|
532
|
+
sig = sigs[name]
|
|
533
|
+
src = _format_script_source(block.span)
|
|
534
|
+
_write(f" {_c(_CYAN, sig):<{max_sig + len(_CYAN) + len(_RESET)}} {_c(_DIM, src)}\n")
|
|
535
|
+
_write("\n")
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def _print_script_detail(name: str) -> None:
|
|
539
|
+
"""Print detail for a single SCRIPT definition."""
|
|
540
|
+
from execsql.metacommands.debug import _format_script_signature, _format_script_source
|
|
541
|
+
|
|
542
|
+
script_name = name.lower()
|
|
543
|
+
scripts = _state.ast_scripts
|
|
544
|
+
if script_name not in scripts:
|
|
545
|
+
_write(f" {_c(_RED, 'No script named')} {_c(_CYAN, repr(script_name))} {_c(_RED, 'is registered.')}\n")
|
|
546
|
+
return
|
|
547
|
+
block = scripts[script_name]
|
|
548
|
+
sig = _format_script_signature(block.name, block.param_defs)
|
|
549
|
+
src = _format_script_source(block.span)
|
|
550
|
+
_write_rule(f" {_c(_BOLD + _YELLOW, 'Script')} {_c(_DIM, '──')} {_c(_CYAN, sig)} ")
|
|
551
|
+
_write(f" {_c(_BOLD, 'Source:')} {src}\n")
|
|
552
|
+
if block.param_defs:
|
|
553
|
+
_write(f" {_c(_BOLD, 'Parameters:')}\n")
|
|
554
|
+
max_name = max(len(p.name) for p in block.param_defs)
|
|
555
|
+
for p in block.param_defs:
|
|
556
|
+
if p.default is not None:
|
|
557
|
+
_write(
|
|
558
|
+
f" {_c(_CYAN, p.name):<{max_name + len(_CYAN) + len(_RESET)}} {_c(_DIM, f'(optional, default: {p.default})')}\n",
|
|
559
|
+
)
|
|
560
|
+
else:
|
|
561
|
+
_write(f" {_c(_CYAN, p.name):<{max_name + len(_CYAN) + len(_RESET)}} {_c(_DIM, '(required)')}\n")
|
|
562
|
+
else:
|
|
563
|
+
_write(f" {_c(_BOLD, 'Parameters:')} (none)\n")
|
|
564
|
+
if block.doc:
|
|
565
|
+
_write("\n")
|
|
566
|
+
for doc_line in block.doc.split("\n"):
|
|
567
|
+
_write(f" {_c(_DIM, doc_line)}\n")
|
|
568
|
+
_write("\n")
|
execsql/format.py
CHANGED
execsql/metacommands/debug.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from execsql.utils.errors import fatal_error
|
|
3
2
|
|
|
4
3
|
"""
|
|
5
4
|
Debug metacommand handlers for execsql.
|
|
@@ -7,11 +6,16 @@ Debug metacommand handlers for execsql.
|
|
|
7
6
|
Provides ``x_debug_write_metacommands``, which implements the
|
|
8
7
|
``WRITE METACOMMANDS`` debug metacommand that prints the full registered
|
|
9
8
|
metacommand list to the log/console for troubleshooting.
|
|
9
|
+
|
|
10
|
+
Also provides ``x_show_scripts`` and ``x_show_script`` for runtime
|
|
11
|
+
introspection of registered SCRIPT blocks.
|
|
10
12
|
"""
|
|
11
13
|
|
|
14
|
+
from pathlib import Path
|
|
12
15
|
from typing import Any
|
|
13
16
|
|
|
14
17
|
import execsql.state as _state
|
|
18
|
+
from execsql.utils.errors import fatal_error
|
|
15
19
|
from execsql.utils.fileio import EncodedFile, filewriter_open_as_new, filewriter_write
|
|
16
20
|
|
|
17
21
|
|
|
@@ -173,3 +177,87 @@ def x_debug_write_config(**kwargs: Any) -> None:
|
|
|
173
177
|
|
|
174
178
|
for line in lines:
|
|
175
179
|
write(f"{line}\n")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
# Helpers for SCRIPT introspection (shared by metacommands and REPL)
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _format_script_signature(name: str, param_defs: Any) -> str:
|
|
188
|
+
"""Return ``name(param1, param2, opt=default)`` or ``name()``.
|
|
189
|
+
|
|
190
|
+
*param_defs* may be a list of :class:`ParamDef` objects (preferred) or
|
|
191
|
+
a plain list of strings (backward compat).
|
|
192
|
+
"""
|
|
193
|
+
if not param_defs:
|
|
194
|
+
return f"{name}()"
|
|
195
|
+
parts: list[str] = []
|
|
196
|
+
for p in param_defs:
|
|
197
|
+
if hasattr(p, "default") and p.default is not None:
|
|
198
|
+
parts.append(f"{p.name}={p.default}")
|
|
199
|
+
elif hasattr(p, "name"):
|
|
200
|
+
parts.append(p.name)
|
|
201
|
+
else:
|
|
202
|
+
parts.append(str(p))
|
|
203
|
+
return f"{name}({', '.join(parts)})"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _format_script_source(span: Any) -> str:
|
|
207
|
+
"""Return ``file:start-end`` from a SourceSpan."""
|
|
208
|
+
filename = Path(span.file).name if span and span.file else "<unknown>"
|
|
209
|
+
if span and span.start_line is not None:
|
|
210
|
+
if span.end_line is not None and span.end_line != span.start_line:
|
|
211
|
+
return f"{filename}:{span.start_line}-{span.end_line}"
|
|
212
|
+
return f"{filename}:{span.start_line}"
|
|
213
|
+
return filename
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# ---------------------------------------------------------------------------
|
|
217
|
+
# SHOW SCRIPTS / SHOW SCRIPT metacommand handlers
|
|
218
|
+
# ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def x_show_scripts(**kwargs: Any) -> None:
|
|
222
|
+
"""List all registered SCRIPT definitions with parameters and source location."""
|
|
223
|
+
scripts = _state.ast_scripts
|
|
224
|
+
if not scripts:
|
|
225
|
+
_state.output.write("No scripts registered.\n")
|
|
226
|
+
return
|
|
227
|
+
_state.output.write(f"Registered scripts ({len(scripts)}):\n\n")
|
|
228
|
+
# Compute column width for alignment
|
|
229
|
+
sigs = {name: _format_script_signature(name, block.param_defs) for name, block in scripts.items()}
|
|
230
|
+
max_sig = max(len(s) for s in sigs.values())
|
|
231
|
+
for name, block in scripts.items():
|
|
232
|
+
sig = sigs[name]
|
|
233
|
+
src = _format_script_source(block.span)
|
|
234
|
+
_state.output.write(f" {sig:<{max_sig}} {src}\n")
|
|
235
|
+
_state.output.write("\n")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def x_show_script(**kwargs: Any) -> None:
|
|
239
|
+
"""Show detail for a single registered SCRIPT definition."""
|
|
240
|
+
script_name = kwargs.get("script_id", "").lower()
|
|
241
|
+
scripts = _state.ast_scripts
|
|
242
|
+
if script_name not in scripts:
|
|
243
|
+
_state.output.write(f"No script named '{script_name}' is registered.\n")
|
|
244
|
+
return
|
|
245
|
+
block = scripts[script_name]
|
|
246
|
+
sig = _format_script_signature(block.name, block.param_defs)
|
|
247
|
+
src = _format_script_source(block.span)
|
|
248
|
+
_state.output.write(f"Script: {sig}\n")
|
|
249
|
+
_state.output.write(f"Source: {src}\n")
|
|
250
|
+
if block.param_defs:
|
|
251
|
+
_state.output.write("Parameters:\n")
|
|
252
|
+
max_name = max(len(p.name) for p in block.param_defs)
|
|
253
|
+
for p in block.param_defs:
|
|
254
|
+
if p.default is not None:
|
|
255
|
+
_state.output.write(f" {p.name:<{max_name}} (optional, default: {p.default})\n")
|
|
256
|
+
else:
|
|
257
|
+
_state.output.write(f" {p.name:<{max_name}} (required)\n")
|
|
258
|
+
else:
|
|
259
|
+
_state.output.write("Parameters: (none)\n")
|
|
260
|
+
if block.doc:
|
|
261
|
+
_state.output.write("\n")
|
|
262
|
+
for doc_line in block.doc.split("\n"):
|
|
263
|
+
_state.output.write(f" {doc_line}\n")
|
execsql/metacommands/dispatch.py
CHANGED
|
@@ -99,6 +99,8 @@ from execsql.metacommands.debug import (
|
|
|
99
99
|
x_debug_write_metacommands,
|
|
100
100
|
x_debug_write_odbc_drivers,
|
|
101
101
|
x_debug_write_subvars,
|
|
102
|
+
x_show_script,
|
|
103
|
+
x_show_scripts,
|
|
102
104
|
)
|
|
103
105
|
from execsql.debug.repl import x_breakpoint
|
|
104
106
|
from execsql.metacommands.io import (
|
|
@@ -1747,6 +1749,22 @@ def build_dispatch_table() -> MetaCommandList:
|
|
|
1747
1749
|
run_when_false=False,
|
|
1748
1750
|
)
|
|
1749
1751
|
|
|
1752
|
+
# ------------------------------------------------------------------
|
|
1753
|
+
# SHOW SCRIPTS / SHOW SCRIPT
|
|
1754
|
+
# ------------------------------------------------------------------
|
|
1755
|
+
mcl.add(
|
|
1756
|
+
r"^\s*SHOW\s+SCRIPTS\s*$",
|
|
1757
|
+
x_show_scripts,
|
|
1758
|
+
description="SHOW SCRIPTS",
|
|
1759
|
+
category="action",
|
|
1760
|
+
)
|
|
1761
|
+
mcl.add(
|
|
1762
|
+
r"^\s*SHOW\s+SCRIPT\s+(?P<script_id>\w+)\s*$",
|
|
1763
|
+
x_show_script,
|
|
1764
|
+
description="SHOW SCRIPT",
|
|
1765
|
+
category="action",
|
|
1766
|
+
)
|
|
1767
|
+
|
|
1750
1768
|
# ------------------------------------------------------------------
|
|
1751
1769
|
# IF / ORIF / ANDIF / ELSEIF / ELSE / ENDIF
|
|
1752
1770
|
# ------------------------------------------------------------------
|
execsql/script/ast.py
CHANGED
|
@@ -52,6 +52,7 @@ __all__ = [
|
|
|
52
52
|
"IfBlock",
|
|
53
53
|
"LoopBlock",
|
|
54
54
|
"BatchBlock",
|
|
55
|
+
"ParamDef",
|
|
55
56
|
"ScriptBlock",
|
|
56
57
|
"SqlBlock",
|
|
57
58
|
"IncludeDirective",
|
|
@@ -290,6 +291,24 @@ class BatchBlock(Node):
|
|
|
290
291
|
return f"BatchBlock({self.span}, body={len(self.body)})"
|
|
291
292
|
|
|
292
293
|
|
|
294
|
+
@dataclass(frozen=True, slots=True)
|
|
295
|
+
class ParamDef:
|
|
296
|
+
"""A single SCRIPT parameter definition with an optional default value.
|
|
297
|
+
|
|
298
|
+
Attributes:
|
|
299
|
+
name: The parameter name (as declared, without ``#`` prefix).
|
|
300
|
+
default: The default value string, or ``None`` for required parameters.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
name: str
|
|
304
|
+
default: str | None = None
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def required(self) -> bool:
|
|
308
|
+
"""Return ``True`` if this parameter has no default value."""
|
|
309
|
+
return self.default is None
|
|
310
|
+
|
|
311
|
+
|
|
293
312
|
@dataclass
|
|
294
313
|
class ScriptBlock(Node):
|
|
295
314
|
"""A BEGIN SCRIPT name ... END SCRIPT structure.
|
|
@@ -299,20 +318,31 @@ class ScriptBlock(Node):
|
|
|
299
318
|
|
|
300
319
|
Attributes:
|
|
301
320
|
name: The script block name (lowercased).
|
|
302
|
-
|
|
321
|
+
param_defs: Optional list of :class:`ParamDef` parameter definitions.
|
|
322
|
+
doc: Optional docstring extracted from comments immediately following
|
|
323
|
+
the BEGIN SCRIPT line.
|
|
303
324
|
body: Nodes within the script block.
|
|
304
325
|
"""
|
|
305
326
|
|
|
306
327
|
name: str
|
|
307
|
-
|
|
328
|
+
param_defs: list[ParamDef] | None = None
|
|
329
|
+
doc: str | None = None
|
|
308
330
|
body: list[Node] = field(default_factory=list)
|
|
309
331
|
|
|
332
|
+
@property
|
|
333
|
+
def param_names(self) -> list[str] | None:
|
|
334
|
+
"""Return parameter names only (backward compatibility)."""
|
|
335
|
+
if self.param_defs is None:
|
|
336
|
+
return None
|
|
337
|
+
return [p.name for p in self.param_defs]
|
|
338
|
+
|
|
310
339
|
def children(self) -> Iterator[Node]:
|
|
311
340
|
yield from self.body
|
|
312
341
|
|
|
313
342
|
def __repr__(self) -> str:
|
|
314
|
-
params = f", params={self.param_names}" if self.
|
|
315
|
-
|
|
343
|
+
params = f", params={self.param_names}" if self.param_defs else ""
|
|
344
|
+
doc_tag = ", doc=True" if self.doc else ""
|
|
345
|
+
return f"ScriptBlock({self.span}, name={self.name!r}{params}{doc_tag}, body={len(self.body)})"
|
|
316
346
|
|
|
317
347
|
|
|
318
348
|
@dataclass
|
|
@@ -547,8 +577,15 @@ def _node_label(node: Node) -> str:
|
|
|
547
577
|
if isinstance(node, BatchBlock):
|
|
548
578
|
return f"{_tag('BATCH')} BEGIN BATCH"
|
|
549
579
|
if isinstance(node, ScriptBlock):
|
|
550
|
-
|
|
551
|
-
|
|
580
|
+
if node.param_defs:
|
|
581
|
+
parts = []
|
|
582
|
+
for p in node.param_defs:
|
|
583
|
+
parts.append(f"{p.name}={p.default}" if p.default is not None else p.name)
|
|
584
|
+
params = f" ({', '.join(parts)})"
|
|
585
|
+
else:
|
|
586
|
+
params = ""
|
|
587
|
+
doc_tag = " [doc]" if node.doc else ""
|
|
588
|
+
return f"{_tag('SCRIPT')} {node.name}{params}{doc_tag}"
|
|
552
589
|
if isinstance(node, SqlBlock):
|
|
553
590
|
return f"{_tag('SQL_BLK')} BEGIN SQL"
|
|
554
591
|
if isinstance(node, IncludeDirective):
|
execsql/script/executor.py
CHANGED
|
@@ -648,7 +648,10 @@ def _execute_include(
|
|
|
648
648
|
WHILE/UNTIL loops are handled natively too.
|
|
649
649
|
"""
|
|
650
650
|
if node.is_execute_script:
|
|
651
|
-
target
|
|
651
|
+
# Substitute variables in the target — the script name may be passed
|
|
652
|
+
# as a parameter (e.g., EXECUTE SCRIPT !!#script_name!!).
|
|
653
|
+
effective_locals = _stack_localvars(ctx) or localvars
|
|
654
|
+
target = substitute_vars(node.target, effective_locals, ctx=ctx).strip().lower()
|
|
652
655
|
|
|
653
656
|
# Native path: target is in our AST registry
|
|
654
657
|
if target in ctx.ast_scripts:
|
|
@@ -713,23 +716,35 @@ def _execute_script_native(
|
|
|
713
716
|
for param, arg in all_prepared_args:
|
|
714
717
|
paramvals.add_substitution(param, arg)
|
|
715
718
|
|
|
716
|
-
# Validate parameter names match
|
|
717
|
-
if script_block.
|
|
719
|
+
# Validate parameter names match — with default parameter support
|
|
720
|
+
if script_block.param_defs is not None:
|
|
718
721
|
passed_names = [p[0].lstrip("#") for p in all_prepared_args]
|
|
719
|
-
|
|
722
|
+
required = [p.name for p in script_block.param_defs if p.required]
|
|
723
|
+
missing = [p for p in required if p not in passed_names]
|
|
724
|
+
if missing:
|
|
720
725
|
raise ErrInfo(
|
|
721
726
|
"error",
|
|
722
|
-
other_msg=f"
|
|
727
|
+
other_msg=(f"Missing required parameter(s) ({', '.join(missing)}) in call to {script_block.name}."),
|
|
723
728
|
)
|
|
729
|
+
# Inject defaults for optional params not provided
|
|
730
|
+
for pdef in script_block.param_defs:
|
|
731
|
+
if pdef.default is not None and pdef.name not in passed_names:
|
|
732
|
+
paramvals.add_substitution(f"#{pdef.name}", pdef.default)
|
|
724
733
|
else:
|
|
725
|
-
if script_block.
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
734
|
+
if script_block.param_defs is not None:
|
|
735
|
+
required = [p.name for p in script_block.param_defs if p.required]
|
|
736
|
+
if required:
|
|
737
|
+
raise ErrInfo(
|
|
738
|
+
"error",
|
|
739
|
+
other_msg=(
|
|
740
|
+
f"Missing required parameter(s) ({', '.join(required)}) in call to {script_block.name}."
|
|
741
|
+
),
|
|
742
|
+
)
|
|
743
|
+
# No args provided but all params have defaults — inject them all
|
|
744
|
+
paramvals = ScriptArgSubVarSet()
|
|
745
|
+
for pdef in script_block.param_defs:
|
|
746
|
+
if pdef.default is not None:
|
|
747
|
+
paramvals.add_substitution(f"#{pdef.name}", pdef.default)
|
|
733
748
|
|
|
734
749
|
# Push a CommandList frame onto the stack so that:
|
|
735
750
|
# - get_subvarset() can find ~local and +outer-scope variables
|
execsql/script/parser.py
CHANGED
|
@@ -42,6 +42,7 @@ from execsql.script.ast import (
|
|
|
42
42
|
LoopBlock,
|
|
43
43
|
MetaCommandStatement,
|
|
44
44
|
Node,
|
|
45
|
+
ParamDef,
|
|
45
46
|
Script,
|
|
46
47
|
ScriptBlock,
|
|
47
48
|
SourceSpan,
|
|
@@ -106,7 +107,7 @@ def _strip_quotes(s: str) -> str:
|
|
|
106
107
|
_EXEC_SCRIPT_RX = re.compile(
|
|
107
108
|
r"^\s*(?:EXEC(?:UTE)?|RUN)\s+SCRIPT"
|
|
108
109
|
r"(?P<exists>\s+IF\s+EXISTS)?"
|
|
109
|
-
r"\s+(?P<script_id
|
|
110
|
+
r"\s+(?P<script_id>(?:\w+|!(?:['\"]?)![^!]+!(?:['\"]?)!))"
|
|
110
111
|
r"(?:(?:\s+WITH)?(?:\s+ARG(?:UMENT)?S?)?\s*\(\s*(?P<argexp>.+?)\s*\))?"
|
|
111
112
|
r"(?:\s+(?P<looptype>WHILE|UNTIL)\s*\(\s*(?P<loopcond>.+)\s*\))?"
|
|
112
113
|
r"\s*$",
|
|
@@ -114,10 +115,52 @@ _EXEC_SCRIPT_RX = re.compile(
|
|
|
114
115
|
)
|
|
115
116
|
|
|
116
117
|
_WITH_PARAMS_RX = re.compile(
|
|
117
|
-
r"(?:\s+WITH)?(?:\s+PARAM(?:ETER)?S)?\s*\(\s*(?P<params
|
|
118
|
+
r"(?:\s+WITH)?(?:\s+PARAM(?:ETER)?S)?\s*\(\s*(?P<params>"
|
|
119
|
+
r"\w+(?:\s*=\s*\S+)?(?:\s*,\s*\w+(?:\s*=\s*\S+)?)*"
|
|
120
|
+
r")\s*\)\s*$",
|
|
118
121
|
re.I,
|
|
119
122
|
)
|
|
120
123
|
|
|
124
|
+
_PARAM_TOKEN_RX = re.compile(r"(\w+)(?:\s*=\s*(\S+))?")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _parse_param_defs(
|
|
128
|
+
params_str: str,
|
|
129
|
+
lineno: int,
|
|
130
|
+
source: str,
|
|
131
|
+
) -> list[ParamDef]:
|
|
132
|
+
"""Parse ``'a, b, c=100, d=false'`` into a list of :class:`ParamDef`.
|
|
133
|
+
|
|
134
|
+
Required parameters (no default) must precede optional parameters
|
|
135
|
+
(with default). Raises :class:`ErrInfo` if ordering is violated.
|
|
136
|
+
"""
|
|
137
|
+
tokens = [t.strip() for t in params_str.split(",")]
|
|
138
|
+
defs: list[ParamDef] = []
|
|
139
|
+
seen_optional: str | None = None # name of first optional param
|
|
140
|
+
for token in tokens:
|
|
141
|
+
m = _PARAM_TOKEN_RX.match(token.strip())
|
|
142
|
+
if not m:
|
|
143
|
+
raise ErrInfo(
|
|
144
|
+
type="cmd",
|
|
145
|
+
other_msg=(f"Invalid parameter token '{token}' on line {lineno} of {source}."),
|
|
146
|
+
)
|
|
147
|
+
name, default = m.group(1), m.group(2)
|
|
148
|
+
if default is not None:
|
|
149
|
+
if seen_optional is None:
|
|
150
|
+
seen_optional = name
|
|
151
|
+
elif seen_optional is not None:
|
|
152
|
+
raise ErrInfo(
|
|
153
|
+
type="cmd",
|
|
154
|
+
other_msg=(
|
|
155
|
+
f"Required parameter '{name}' after optional parameter "
|
|
156
|
+
f"'{seen_optional}' on line {lineno} of {source}. "
|
|
157
|
+
f"Required parameters must precede optional parameters."
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
defs.append(ParamDef(name=name, default=default))
|
|
161
|
+
return defs
|
|
162
|
+
|
|
163
|
+
|
|
121
164
|
# Line classification
|
|
122
165
|
_EXEC_LINE_RX = re.compile(r"^\s*--\s*!x!\s*(?P<cmd>.+)$", re.I)
|
|
123
166
|
_COMMENT_LINE_RX = re.compile(r"^\s*--")
|
|
@@ -135,7 +178,16 @@ class _BlockFrame:
|
|
|
135
178
|
to close the block correctly.
|
|
136
179
|
"""
|
|
137
180
|
|
|
138
|
-
__slots__ = (
|
|
181
|
+
__slots__ = (
|
|
182
|
+
"node",
|
|
183
|
+
"kind",
|
|
184
|
+
"start_line",
|
|
185
|
+
"source",
|
|
186
|
+
"_in_else",
|
|
187
|
+
"_in_elseif",
|
|
188
|
+
"collecting_doc",
|
|
189
|
+
"doc_lines",
|
|
190
|
+
)
|
|
139
191
|
|
|
140
192
|
def __init__(self, node: Node, kind: str, start_line: int, source: str) -> None:
|
|
141
193
|
self.node = node
|
|
@@ -144,6 +196,8 @@ class _BlockFrame:
|
|
|
144
196
|
self.source = source
|
|
145
197
|
self._in_else = False
|
|
146
198
|
self._in_elseif = False
|
|
199
|
+
self.collecting_doc: bool = kind == "script" # auto-collect doc after BEGIN SCRIPT
|
|
200
|
+
self.doc_lines: list[str] = []
|
|
147
201
|
|
|
148
202
|
|
|
149
203
|
# ---------------------------------------------------------------------------
|
|
@@ -206,9 +260,67 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
206
260
|
)
|
|
207
261
|
line_comment_lines = []
|
|
208
262
|
|
|
263
|
+
def _stop_doc_collection() -> None:
|
|
264
|
+
"""Stop collecting docstring lines and finalize the doc on the script node."""
|
|
265
|
+
if block_stack and block_stack[-1].collecting_doc:
|
|
266
|
+
frame = block_stack[-1]
|
|
267
|
+
frame.collecting_doc = False
|
|
268
|
+
if frame.doc_lines:
|
|
269
|
+
doc_text = "\n".join(frame.doc_lines).strip()
|
|
270
|
+
if doc_text and isinstance(frame.node, ScriptBlock):
|
|
271
|
+
frame.node.doc = doc_text
|
|
272
|
+
|
|
209
273
|
for file_lineno, raw_line in enumerate(lines, 1):
|
|
210
274
|
line = raw_line.rstrip()
|
|
211
275
|
|
|
276
|
+
# --- Docstring collection for SCRIPT blocks ---
|
|
277
|
+
# Comments immediately following BEGIN SCRIPT are captured as the
|
|
278
|
+
# docstring. A blank line terminates the doc.
|
|
279
|
+
if block_stack and block_stack[-1].collecting_doc and not in_block_comment:
|
|
280
|
+
frame = block_stack[-1]
|
|
281
|
+
if not line:
|
|
282
|
+
# Blank line terminates doc collection
|
|
283
|
+
_stop_doc_collection()
|
|
284
|
+
_flush_line_comments()
|
|
285
|
+
continue
|
|
286
|
+
# Check for block comment opening
|
|
287
|
+
stripped = line.lstrip()
|
|
288
|
+
if stripped.startswith("/*"):
|
|
289
|
+
# Collect block comment as doc lines
|
|
290
|
+
comment_body = stripped[2:]
|
|
291
|
+
if comment_body.rstrip().endswith("*/"):
|
|
292
|
+
# Single-line block comment: /* text */
|
|
293
|
+
comment_body = comment_body.rstrip()[:-2].strip()
|
|
294
|
+
if comment_body:
|
|
295
|
+
frame.doc_lines.append(comment_body)
|
|
296
|
+
continue
|
|
297
|
+
# Multi-line block comment — collect until */
|
|
298
|
+
if comment_body.strip():
|
|
299
|
+
frame.doc_lines.append(comment_body.rstrip())
|
|
300
|
+
# Let the block comment tracker handle the rest, but mark
|
|
301
|
+
# that we're collecting doc inside a block comment.
|
|
302
|
+
in_block_comment = True
|
|
303
|
+
block_comment_lines = [line]
|
|
304
|
+
block_comment_start = file_lineno
|
|
305
|
+
sql_accum_at_block_comment = False
|
|
306
|
+
# We'll handle the doc finalization when */ is found below.
|
|
307
|
+
# For now, mark doc as still collecting.
|
|
308
|
+
continue
|
|
309
|
+
# Check for single-line comment (not metacommand)
|
|
310
|
+
metacommand_match_doc = _EXEC_LINE_RX.match(line)
|
|
311
|
+
if not metacommand_match_doc and _COMMENT_LINE_RX.match(line):
|
|
312
|
+
# Strip -- prefix and optional leading space
|
|
313
|
+
text = stripped
|
|
314
|
+
if text.startswith("--"):
|
|
315
|
+
text = text[2:]
|
|
316
|
+
if text.startswith(" "):
|
|
317
|
+
text = text[1:]
|
|
318
|
+
frame.doc_lines.append(text.rstrip())
|
|
319
|
+
continue
|
|
320
|
+
# Non-comment line (metacommand or SQL) — stop doc collection
|
|
321
|
+
_stop_doc_collection()
|
|
322
|
+
# Fall through to normal processing
|
|
323
|
+
|
|
212
324
|
# --- Block comment tracking ---
|
|
213
325
|
if not line:
|
|
214
326
|
_flush_line_comments()
|
|
@@ -219,6 +331,23 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
219
331
|
if len(line) > 1 and line.rstrip().endswith("*/"):
|
|
220
332
|
in_block_comment = False
|
|
221
333
|
comment_text = "\n".join(block_comment_lines)
|
|
334
|
+
# If we were collecting doc lines when the block comment opened,
|
|
335
|
+
# feed the content into the docstring instead of creating a node.
|
|
336
|
+
if block_stack and block_stack[-1].collecting_doc:
|
|
337
|
+
frame = block_stack[-1]
|
|
338
|
+
# Extract text between /* and */, stripping delimiters
|
|
339
|
+
for bc_line in block_comment_lines:
|
|
340
|
+
stripped_bc = bc_line.strip()
|
|
341
|
+
if stripped_bc.startswith("/*"):
|
|
342
|
+
stripped_bc = stripped_bc[2:]
|
|
343
|
+
if stripped_bc.endswith("*/"):
|
|
344
|
+
stripped_bc = stripped_bc[:-2]
|
|
345
|
+
stripped_bc = stripped_bc.strip()
|
|
346
|
+
if stripped_bc:
|
|
347
|
+
frame.doc_lines.append(stripped_bc)
|
|
348
|
+
block_comment_lines = []
|
|
349
|
+
sql_accum_at_block_comment = False
|
|
350
|
+
continue
|
|
222
351
|
if sql_accum_at_block_comment:
|
|
223
352
|
# Block comment started inside a SQL statement — fold it
|
|
224
353
|
# back into sql_accum so the statement isn't split.
|
|
@@ -332,7 +461,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
332
461
|
if m:
|
|
333
462
|
name = m.group("name").lower()
|
|
334
463
|
paramexpr = m.group("paramexpr")
|
|
335
|
-
|
|
464
|
+
param_defs = None
|
|
336
465
|
if paramexpr:
|
|
337
466
|
wp = _WITH_PARAMS_RX.match(paramexpr)
|
|
338
467
|
if not wp:
|
|
@@ -341,13 +470,13 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
341
470
|
command_text=line,
|
|
342
471
|
other_msg=f"Invalid BEGIN SCRIPT metacommand on line {file_lineno} of file {source_name}.",
|
|
343
472
|
)
|
|
344
|
-
|
|
473
|
+
param_defs = _parse_param_defs(wp.group("params"), file_lineno, source_name)
|
|
345
474
|
block_stack.append(
|
|
346
475
|
_BlockFrame(
|
|
347
476
|
ScriptBlock(
|
|
348
477
|
span=SourceSpan(source_name, file_lineno),
|
|
349
478
|
name=name,
|
|
350
|
-
|
|
479
|
+
param_defs=param_defs,
|
|
351
480
|
),
|
|
352
481
|
kind="script",
|
|
353
482
|
start_line=file_lineno,
|
execsql/utils/fileio.py
CHANGED
|
@@ -284,7 +284,15 @@ class FileWriter(multiprocessing.Process):
|
|
|
284
284
|
self.active = False
|
|
285
285
|
self.close_all()
|
|
286
286
|
|
|
287
|
-
def run(self) -> None:
|
|
287
|
+
def run(self) -> None: # pragma: no cover – runs in a subprocess
|
|
288
|
+
# Ignore SIGINT in the child process — the parent owns Ctrl+C handling
|
|
289
|
+
# and will shut us down via CMD_SHUTDOWN on the queue. Without this,
|
|
290
|
+
# KeyboardInterrupt races through queue.get() and close_all(), producing
|
|
291
|
+
# ugly tracebacks on stderr.
|
|
292
|
+
import signal
|
|
293
|
+
|
|
294
|
+
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
295
|
+
|
|
288
296
|
# Messages in the input queue consist of a 2-tuple, of which the first element
|
|
289
297
|
# is a command and the second is a tuple of arguments for the function indicated
|
|
290
298
|
# by that command.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.16.
|
|
3
|
+
Version: 2.16.12
|
|
4
4
|
Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, 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.
|
|
5
5
|
Project-URL: Homepage, https://execsql2.readthedocs.io
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -3,7 +3,7 @@ execsql/__main__.py,sha256=HdbK-SAhyUmfB6xINY5AzxdMSxGzWSGEG_2dv42Jn64,315
|
|
|
3
3
|
execsql/api.py,sha256=0D2Rl329fvDXERNrJBUzoWEt_VxqCHwRgaGzrq04rVs,19709
|
|
4
4
|
execsql/config.py,sha256=6icjr8PKenUGfFF6lciSclvejjDzY8GTW1OZ1-IZt-Y,29480
|
|
5
5
|
execsql/exceptions.py,sha256=j8hykBiof9H3Za9hwLIbDcVB2Xn65ODXXplp1jkvdgM,8453
|
|
6
|
-
execsql/format.py,sha256=
|
|
6
|
+
execsql/format.py,sha256=RZVxfIHduF6AU2c_O6k93I0kxTUa8gfUn6bRbcgxHYM,12762
|
|
7
7
|
execsql/models.py,sha256=kCTUQg9-vReM6WNFfB_ZrEppuOW5u1uMBQThSkfPC0o,13264
|
|
8
8
|
execsql/parser.py,sha256=P3ea8k7T_XLMrbhpFNZXwytdShrY302MKnhosqza1lo,15493
|
|
9
9
|
execsql/plugins.py,sha256=2voLwT6eFap6BCBoZYndNNC_bMEJO1f_aP6xQTVXwYI,12815
|
|
@@ -29,7 +29,7 @@ execsql/db/postgres.py,sha256=-GpaA9fi6_vdIDTGyRGxNRaPYAAkEB_aCb-EyLFShsM,21120
|
|
|
29
29
|
execsql/db/sqlite.py,sha256=or2JDDt_MyBTAtSXBVhNTcojE9jCE4b98tq39sNOyUg,10401
|
|
30
30
|
execsql/db/sqlserver.py,sha256=d8PwfNumt-Spit_0llafqQ900suv3CZMbrRsHLU1iB4,7683
|
|
31
31
|
execsql/debug/__init__.py,sha256=j6EGUR0dHzUhWN1mHHtf1-Lhjq3Sb1V-vmnq2Ztgj1M,178
|
|
32
|
-
execsql/debug/repl.py,sha256=
|
|
32
|
+
execsql/debug/repl.py,sha256=TlIw6XrxwP3QUxuOaQZKp9vbJI55kEfiMtRNMxQRZo8,20747
|
|
33
33
|
execsql/exporters/__init__.py,sha256=-Cnji-OgodJV8ftcDcOyTof0kQMy9J5kKVC8GVFpc3o,670
|
|
34
34
|
execsql/exporters/base.py,sha256=Uhq0PBz8N_pJ7WlIN9225wY-HyxfzWSb9YabHXicBA8,6387
|
|
35
35
|
execsql/exporters/delimited.py,sha256=Po9RV4UwBLOuRZmPFJk5CErpFvTdLCy--nFJJffZgxM,32383
|
|
@@ -69,8 +69,8 @@ execsql/metacommands/conditions.py,sha256=B6fBumkqoPO4wcQbw_ypYITaSnzPemAA1g5GrN
|
|
|
69
69
|
execsql/metacommands/connect.py,sha256=Wnlp5PeeaNDaVlaWjCetvarTgQIwIeMPYe8cyslPYeA,14969
|
|
70
70
|
execsql/metacommands/control.py,sha256=PlTAq34OkcmnOsPm3bZxF4mg1MaNBpENa8wzIKVEY10,8302
|
|
71
71
|
execsql/metacommands/data.py,sha256=tRQBGTAuW-eJ2tBNWaoZI9OjTyNNyHJISo7gOdL-sm8,11370
|
|
72
|
-
execsql/metacommands/debug.py,sha256=
|
|
73
|
-
execsql/metacommands/dispatch.py,sha256=
|
|
72
|
+
execsql/metacommands/debug.py,sha256=3QVm0N5uc7mcZco36kqlDq8tiWW0sdo8E8BoQZkE_DI,11784
|
|
73
|
+
execsql/metacommands/dispatch.py,sha256=Gao7rwiBFdghmYKcbWVm21Va30evobQuoghLY_FOZto,87602
|
|
74
74
|
execsql/metacommands/io.py,sha256=vlGBje5sgnqeilooMdhJDgSRIhysHy5_7LrKtik9Xjs,3011
|
|
75
75
|
execsql/metacommands/io_export.py,sha256=J2FnXA6452FIGStv6vbZXORmA-Ges70na0W7YwpMESA,13253
|
|
76
76
|
execsql/metacommands/io_fileops.py,sha256=QKFj-94W32zxJiDcMpRsaohSCdtCRPIOG10K0WPigFk,9616
|
|
@@ -81,42 +81,42 @@ execsql/metacommands/script_ext.py,sha256=sw4YKUQl0SRlVlmhIoGbMokOo_hVqh1EcTuYCN
|
|
|
81
81
|
execsql/metacommands/system.py,sha256=azRbv_P8l0t8BkDM9bmAUkhpnLSLHSCcmByqs-a3FxQ,7352
|
|
82
82
|
execsql/metacommands/upsert.py,sha256=XE_P3mjaUkqT-LR4_28n2NbXwdjSgAGXJyZmKZC4Oy4,21136
|
|
83
83
|
execsql/script/__init__.py,sha256=3WaBklMVIWjtCsYQ-BVo9UAVEIATOgeGsuyv21YKnxo,3969
|
|
84
|
-
execsql/script/ast.py,sha256=
|
|
84
|
+
execsql/script/ast.py,sha256=SLn1fp0QDsqvJegNeIuyKV9f1BFnkEdzRota5EaNTfo,20053
|
|
85
85
|
execsql/script/control.py,sha256=s-1eZdGARM6H1FwZ6VDdO_f50j7bvvRtTHesfUm9tbc,6144
|
|
86
86
|
execsql/script/engine.py,sha256=EhuVBniOrFkzAW4I3NIZLt3INHTZJvlYoF7B99rZBLI,29391
|
|
87
|
-
execsql/script/executor.py,sha256=
|
|
88
|
-
execsql/script/parser.py,sha256=
|
|
87
|
+
execsql/script/executor.py,sha256=T-pGa3o5S0oX1ySB5zztzRoJHYRZI58CKw3qcFz_95Q,37602
|
|
88
|
+
execsql/script/parser.py,sha256=huznh4RnW6Y_yJyIIoMG5McUS2T9o3kfOW37CXPR5dk,31689
|
|
89
89
|
execsql/script/variables.py,sha256=ZSBGQUsoii6w3dLDVY9xxoPIV6wY0sAV_BNIQ6pgQAE,14328
|
|
90
90
|
execsql/utils/__init__.py,sha256=0uR6JwVJQRX3vceByNBduCAf5dd5assKjeqJUWvpZoA,278
|
|
91
91
|
execsql/utils/auth.py,sha256=onXzNkNZQZxGC5w7eey06sjvAIAX_Lf9g7nUJtcsel0,7009
|
|
92
92
|
execsql/utils/crypto.py,sha256=KlT6lPk-v0-qQQfAMfWvT9Q6kRdCD3_5JVaLajNocKM,3051
|
|
93
93
|
execsql/utils/datetime.py,sha256=rMCXAbvj6bxKCYzC97vrludO6PU5DYQ39buZ0smDC5A,3573
|
|
94
94
|
execsql/utils/errors.py,sha256=I3xiPyDYS8Ftv7Y8P0uqzrTKlCN1dTXe1pMKKFFjmoA,8651
|
|
95
|
-
execsql/utils/fileio.py,sha256=
|
|
95
|
+
execsql/utils/fileio.py,sha256=x0kl2oLydCIGbhRCAJDHFknUyGcbKEOnV_M4lWcFmTk,24259
|
|
96
96
|
execsql/utils/gui.py,sha256=h_7V2zYfp0l6C3Ft8QtUJAPgxCUrFkgIqwimz5s1bbQ,22821
|
|
97
97
|
execsql/utils/mail.py,sha256=4QXwdPgMh6vQBKLtkdH7IDh3JBczumc3l5up2AUSOZU,5483
|
|
98
98
|
execsql/utils/numeric.py,sha256=xh02ANSRk3nUpQ-rtm66ILoMqoi7HtzCoRMIOT9U8QI,1570
|
|
99
99
|
execsql/utils/regex.py,sha256=diEzTZqU_HHwVMadPAvN1Vgzhl7I03eVaEFGCXyGGL8,3770
|
|
100
100
|
execsql/utils/strings.py,sha256=5Dvzrk-9SIw2lpxXZQkiJbNyo1sy7iXXAtSULlZ0KG8,8488
|
|
101
101
|
execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
|
|
102
|
-
execsql2-2.16.
|
|
103
|
-
execsql2-2.16.
|
|
104
|
-
execsql2-2.16.
|
|
105
|
-
execsql2-2.16.
|
|
106
|
-
execsql2-2.16.
|
|
107
|
-
execsql2-2.16.
|
|
108
|
-
execsql2-2.16.
|
|
109
|
-
execsql2-2.16.
|
|
110
|
-
execsql2-2.16.
|
|
111
|
-
execsql2-2.16.
|
|
112
|
-
execsql2-2.16.
|
|
113
|
-
execsql2-2.16.
|
|
114
|
-
execsql2-2.16.
|
|
115
|
-
execsql2-2.16.
|
|
116
|
-
execsql2-2.16.
|
|
117
|
-
execsql2-2.16.
|
|
118
|
-
execsql2-2.16.
|
|
119
|
-
execsql2-2.16.
|
|
120
|
-
execsql2-2.16.
|
|
121
|
-
execsql2-2.16.
|
|
122
|
-
execsql2-2.16.
|
|
102
|
+
execsql2-2.16.12.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
|
|
103
|
+
execsql2-2.16.12.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
|
|
104
|
+
execsql2-2.16.12.data/data/execsql2_extras/example_config_prompt.sql,sha256=SY3Jxn1qcVm4kPW9xmmTfknHfvURXmeEYTbRjYrjGSw,7487
|
|
105
|
+
execsql2-2.16.12.data/data/execsql2_extras/execsql.conf,sha256=_45iJ-KWZnB8uMW_gEg067MM5pmGJ-dVl7VbAZMunAE,9530
|
|
106
|
+
execsql2-2.16.12.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
|
|
107
|
+
execsql2-2.16.12.data/data/execsql2_extras/md_compare.sql,sha256=B8Wd7LZ0vnMY2qvA139JIEBkPObgRH2i5xj6PejTQt8,24092
|
|
108
|
+
execsql2-2.16.12.data/data/execsql2_extras/md_glossary.sql,sha256=DJRHcU5NbFpxTTX-IwH3yRlsboj1q6BBGrUAHKn4Cuo,10796
|
|
109
|
+
execsql2-2.16.12.data/data/execsql2_extras/md_upsert.sql,sha256=v_7GbWh_N1mBTmw3gvTrkagOVp2q0KmXvM8hE-DlFxY,112524
|
|
110
|
+
execsql2-2.16.12.data/data/execsql2_extras/pg_compare.sql,sha256=9dWa8hnfy5dVJI-z2iGpd9JzQmI4j2ziMlEdpnr66ro,24352
|
|
111
|
+
execsql2-2.16.12.data/data/execsql2_extras/pg_glossary.sql,sha256=pKjIIDsROAgJq2H-1qNEcRMAWManivcZ_AEVHzUUlic,9908
|
|
112
|
+
execsql2-2.16.12.data/data/execsql2_extras/pg_upsert.sql,sha256=k7AFiGTLBy3nf-qO5QIaZrEYTAKvdxxU3JDLx9jqkzs,108315
|
|
113
|
+
execsql2-2.16.12.data/data/execsql2_extras/script_template.sql,sha256=1Estacb_vm1FgK41k_G9nuduP1yiA-fQ1Kn4Z4mv5Ao,11153
|
|
114
|
+
execsql2-2.16.12.data/data/execsql2_extras/ss_compare.sql,sha256=TsVxWm3cEpR5-EiMYXNhtaY0arSNeKZhsJdHdLA7xeI,24833
|
|
115
|
+
execsql2-2.16.12.data/data/execsql2_extras/ss_glossary.sql,sha256=cLM7nN8JOIu9ZVP9oY9qdSK3hrnWJiDcX6nZmQQbQWI,13065
|
|
116
|
+
execsql2-2.16.12.data/data/execsql2_extras/ss_upsert.sql,sha256=BCqmBykXBF-BpCgOFeG1qhf2XfScKsxPD17wd1hYfHw,120647
|
|
117
|
+
execsql2-2.16.12.dist-info/METADATA,sha256=Op2AjS7Au3YMFnJ8rX0eE3DJm4SZcRVCzoCIhE3cDEo,20921
|
|
118
|
+
execsql2-2.16.12.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
119
|
+
execsql2-2.16.12.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
|
|
120
|
+
execsql2-2.16.12.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
|
|
121
|
+
execsql2-2.16.12.dist-info/licenses/NOTICE,sha256=sqVrM73Ys9zfvWC_P797lHfTnoPW_ETeBSrUTFaob0A,339
|
|
122
|
+
execsql2-2.16.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{execsql2-2.16.8.data → execsql2-2.16.12.data}/data/execsql2_extras/example_config_prompt.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|