execsql2 2.18.0__py3-none-any.whl → 2.19.0__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/cli/__init__.py +3 -5
- execsql/cli/lint.py +433 -18
- execsql/cli/run.py +46 -17
- execsql/data/execsql.conf.template +34 -2
- execsql/db/access.py +0 -6
- execsql/db/base.py +0 -13
- execsql/db/mysql.py +0 -6
- execsql/db/oracle.py +0 -6
- execsql/db/sqlserver.py +0 -6
- execsql/debug/repl.py +117 -35
- execsql/exporters/feather.py +10 -9
- execsql/format.py +23 -4
- execsql/importers/base.py +3 -4
- execsql/importers/xls.py +6 -1
- execsql/metacommands/__init__.py +2 -2
- execsql/metacommands/data.py +1 -0
- execsql/metacommands/dispatch.py +5 -10
- execsql/metacommands/script_ext.py +8 -7
- execsql/script/engine.py +1 -12
- execsql/script/executor.py +2 -6
- execsql/script/parser.py +49 -12
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/example_config_prompt.sql +1 -1
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/execsql.conf +34 -2
- {execsql2-2.18.0.dist-info → execsql2-2.19.0.dist-info}/METADATA +54 -43
- {execsql2-2.18.0.dist-info → execsql2-2.19.0.dist-info}/RECORD +42 -43
- {execsql2-2.18.0.dist-info → execsql2-2.19.0.dist-info}/WHEEL +1 -1
- execsql/cli/lint_ast.py +0 -439
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.18.0.dist-info → execsql2-2.19.0.dist-info}/entry_points.txt +0 -0
- {execsql2-2.18.0.dist-info → execsql2-2.19.0.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.18.0.dist-info → execsql2-2.19.0.dist-info}/licenses/NOTICE +0 -0
execsql/script/parser.py
CHANGED
|
@@ -102,6 +102,19 @@ def _strip_quotes(s: str) -> str:
|
|
|
102
102
|
return s
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
def _unclosed_block_msg(top: _BlockFrame) -> str:
|
|
106
|
+
"""Message blaming an unclosed opening block for a wrong-kind END-keyword error."""
|
|
107
|
+
if top.kind == "if":
|
|
108
|
+
return f"IF on line {top.start_line} of {top.source} has no matching ENDIF."
|
|
109
|
+
if top.kind == "loop":
|
|
110
|
+
return f"LOOP on line {top.start_line} of {top.source} has no matching ENDLOOP."
|
|
111
|
+
if top.kind == "batch":
|
|
112
|
+
return f"BEGIN BATCH on line {top.start_line} of {top.source} has no matching END BATCH."
|
|
113
|
+
if top.kind == "script":
|
|
114
|
+
return f"BEGIN SCRIPT on line {top.start_line} of {top.source} has no matching END SCRIPT."
|
|
115
|
+
return f"{top.kind.upper()} on line {top.start_line} of {top.source} is unclosed."
|
|
116
|
+
|
|
117
|
+
|
|
105
118
|
_EXEC_SCRIPT_RX = re.compile(
|
|
106
119
|
r"^\s*(?:EXEC(?:UTE)?|RUN)\s+SCRIPT"
|
|
107
120
|
r"(?P<exists>\s+IF\s+EXISTS)?"
|
|
@@ -529,11 +542,17 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
529
542
|
end_name = m.group("name")
|
|
530
543
|
if end_name is not None:
|
|
531
544
|
end_name = end_name.lower()
|
|
532
|
-
if not block_stack
|
|
545
|
+
if not block_stack:
|
|
546
|
+
raise ErrInfo(
|
|
547
|
+
type="cmd",
|
|
548
|
+
command_text=line,
|
|
549
|
+
other_msg=f"END SCRIPT on line {file_lineno} of {source_name} has no matching BEGIN SCRIPT.",
|
|
550
|
+
)
|
|
551
|
+
if block_stack[-1].kind != "script":
|
|
533
552
|
raise ErrInfo(
|
|
534
553
|
type="cmd",
|
|
535
554
|
command_text=line,
|
|
536
|
-
other_msg=
|
|
555
|
+
other_msg=_unclosed_block_msg(block_stack[-1]),
|
|
537
556
|
)
|
|
538
557
|
frame = block_stack[-1]
|
|
539
558
|
script_node = frame.node
|
|
@@ -595,7 +614,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
595
614
|
raise ErrInfo(
|
|
596
615
|
type="cmd",
|
|
597
616
|
command_text=line,
|
|
598
|
-
other_msg=f"ELSEIF
|
|
617
|
+
other_msg=f"ELSEIF on line {file_lineno} of {source_name} has no matching IF.",
|
|
599
618
|
)
|
|
600
619
|
frame = block_stack[-1]
|
|
601
620
|
frame._in_else = False
|
|
@@ -616,7 +635,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
616
635
|
raise ErrInfo(
|
|
617
636
|
type="cmd",
|
|
618
637
|
command_text=line,
|
|
619
|
-
other_msg=f"ANDIF
|
|
638
|
+
other_msg=f"ANDIF on line {file_lineno} of {source_name} has no matching IF.",
|
|
620
639
|
)
|
|
621
640
|
modifier = ConditionModifier(
|
|
622
641
|
kind="AND",
|
|
@@ -638,7 +657,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
638
657
|
raise ErrInfo(
|
|
639
658
|
type="cmd",
|
|
640
659
|
command_text=line,
|
|
641
|
-
other_msg=f"ORIF
|
|
660
|
+
other_msg=f"ORIF on line {file_lineno} of {source_name} has no matching IF.",
|
|
642
661
|
)
|
|
643
662
|
modifier = ConditionModifier(
|
|
644
663
|
kind="OR",
|
|
@@ -660,7 +679,7 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
660
679
|
raise ErrInfo(
|
|
661
680
|
type="cmd",
|
|
662
681
|
command_text=line,
|
|
663
|
-
other_msg=f"ELSE
|
|
682
|
+
other_msg=f"ELSE on line {file_lineno} of {source_name} has no matching IF.",
|
|
664
683
|
)
|
|
665
684
|
frame = block_stack[-1]
|
|
666
685
|
frame._in_else = True
|
|
@@ -671,11 +690,17 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
671
690
|
# -- ENDIF --
|
|
672
691
|
m = _ENDIF_RX.match(cmd_text)
|
|
673
692
|
if m:
|
|
674
|
-
if not block_stack
|
|
693
|
+
if not block_stack:
|
|
675
694
|
raise ErrInfo(
|
|
676
695
|
type="cmd",
|
|
677
696
|
command_text=line,
|
|
678
|
-
other_msg=f"ENDIF
|
|
697
|
+
other_msg=f"ENDIF on line {file_lineno} of {source_name} has no matching IF.",
|
|
698
|
+
)
|
|
699
|
+
if block_stack[-1].kind != "if":
|
|
700
|
+
raise ErrInfo(
|
|
701
|
+
type="cmd",
|
|
702
|
+
command_text=line,
|
|
703
|
+
other_msg=_unclosed_block_msg(block_stack[-1]),
|
|
679
704
|
)
|
|
680
705
|
frame = block_stack.pop()
|
|
681
706
|
frame.node.span = SourceSpan(source_name, frame.start_line, file_lineno)
|
|
@@ -702,11 +727,17 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
702
727
|
# -- ENDLOOP --
|
|
703
728
|
m = _ENDLOOP_RX.match(cmd_text)
|
|
704
729
|
if m:
|
|
705
|
-
if not block_stack
|
|
730
|
+
if not block_stack:
|
|
731
|
+
raise ErrInfo(
|
|
732
|
+
type="cmd",
|
|
733
|
+
command_text=line,
|
|
734
|
+
other_msg=f"ENDLOOP on line {file_lineno} of {source_name} has no matching LOOP.",
|
|
735
|
+
)
|
|
736
|
+
if block_stack[-1].kind != "loop":
|
|
706
737
|
raise ErrInfo(
|
|
707
738
|
type="cmd",
|
|
708
739
|
command_text=line,
|
|
709
|
-
other_msg=
|
|
740
|
+
other_msg=_unclosed_block_msg(block_stack[-1]),
|
|
710
741
|
)
|
|
711
742
|
frame = block_stack.pop()
|
|
712
743
|
frame.node.span = SourceSpan(source_name, frame.start_line, file_lineno)
|
|
@@ -729,11 +760,17 @@ def _parse_lines(lines: Iterable[str], source_name: str) -> Script:
|
|
|
729
760
|
# -- END BATCH --
|
|
730
761
|
m = _END_BATCH_RX.match(cmd_text)
|
|
731
762
|
if m:
|
|
732
|
-
if not block_stack
|
|
763
|
+
if not block_stack:
|
|
764
|
+
raise ErrInfo(
|
|
765
|
+
type="cmd",
|
|
766
|
+
command_text=line,
|
|
767
|
+
other_msg=f"END BATCH on line {file_lineno} of {source_name} has no matching BEGIN BATCH.",
|
|
768
|
+
)
|
|
769
|
+
if block_stack[-1].kind != "batch":
|
|
733
770
|
raise ErrInfo(
|
|
734
771
|
type="cmd",
|
|
735
772
|
command_text=line,
|
|
736
|
-
other_msg=
|
|
773
|
+
other_msg=_unclosed_block_msg(block_stack[-1]),
|
|
737
774
|
)
|
|
738
775
|
frame = block_stack.pop()
|
|
739
776
|
frame.node.span = SourceSpan(source_name, frame.start_line, file_lineno)
|
{execsql2-2.18.0.data → execsql2-2.19.0.data}/data/execsql2_extras/example_config_prompt.sql
RENAMED
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
from configspecs cs inner join configusage cu
|
|
123
123
|
on cu.sub_var = cs.sub_var
|
|
124
124
|
where
|
|
125
|
-
usage = '
|
|
125
|
+
usage = !'!#usage!'!;
|
|
126
126
|
-- !x! prompt entry_form !!~spectbl!! message "You may change any of the configuration settings below."
|
|
127
127
|
-- !x! if(sub_defined(~boolean_int)) {config boolean_int !!~boolean_int!!}
|
|
128
128
|
-- !x! if(sub_defined(~boolean_words)) {config boolean_words !!~boolean_words!!}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# execsql.conf — Configuration file for execsql
|
|
2
2
|
#
|
|
3
|
-
# Documentation: https://execsql2.readthedocs.io/reference/configuration/
|
|
3
|
+
# Documentation: https://execsql2.readthedocs.io/en/latest/reference/configuration/
|
|
4
4
|
#
|
|
5
5
|
# This file uses INI format. Section names are case-sensitive (lowercase).
|
|
6
6
|
# Property names are not case-sensitive. Lines starting with # are comments.
|
|
@@ -263,7 +263,7 @@
|
|
|
263
263
|
#message_css=
|
|
264
264
|
|
|
265
265
|
# Obfuscated password (XOR, not cryptographically secure — use keyring instead).
|
|
266
|
-
# See: https://execsql2.readthedocs.io/reference/security/#credentials
|
|
266
|
+
# See: https://execsql2.readthedocs.io/en/latest/reference/security/#credentials
|
|
267
267
|
#enc_password=
|
|
268
268
|
|
|
269
269
|
|
|
@@ -279,6 +279,38 @@
|
|
|
279
279
|
# Values: Yes or No. Default: Yes.
|
|
280
280
|
#allow_system_cmd=Yes
|
|
281
281
|
|
|
282
|
+
# Whether to allow the RM_FILE metacommand (which deletes a file).
|
|
283
|
+
# Set to No to prevent scripts from deleting files.
|
|
284
|
+
# Also controllable via --no-rm-file CLI flag.
|
|
285
|
+
# Values: Yes or No. Default: Yes.
|
|
286
|
+
#allow_rm_file=Yes
|
|
287
|
+
|
|
288
|
+
# Whether to allow the SERVE metacommand (which opens an HTTP server on
|
|
289
|
+
# a local port to serve a single file). Set to No to disable.
|
|
290
|
+
# Also controllable via --no-serve CLI flag.
|
|
291
|
+
# Values: Yes or No. Default: Yes.
|
|
292
|
+
#allow_serve=Yes
|
|
293
|
+
|
|
294
|
+
# Root directory under which INCLUDE / EXECUTE SCRIPT targets must
|
|
295
|
+
# resolve. When set, attempts to include files outside this root via
|
|
296
|
+
# ../, absolute paths, drive letters, or UNC paths are rejected with
|
|
297
|
+
# an error. Default: no containment (any readable path is permitted).
|
|
298
|
+
#include_root=
|
|
299
|
+
|
|
300
|
+
# Root directory under which SERVE targets must resolve. Same
|
|
301
|
+
# containment semantics as include_root. Default: no containment.
|
|
302
|
+
#serve_root=
|
|
303
|
+
|
|
304
|
+
# Root directory under which Jinja2 / string.Template loader paths
|
|
305
|
+
# must resolve. Same containment semantics as include_root.
|
|
306
|
+
# Default: no containment.
|
|
307
|
+
#template_root=
|
|
308
|
+
|
|
309
|
+
# Maximum byte size of any single substitution-variable expansion,
|
|
310
|
+
# enforced by the substitute_vars() engine to defeat exponential-
|
|
311
|
+
# expansion bombs. Default: 10 MB (10485760).
|
|
312
|
+
#max_substitution_bytes=10485760
|
|
313
|
+
|
|
282
314
|
# Whether to log all data variable assignments.
|
|
283
315
|
# Values: Yes or No. Default: Yes.
|
|
284
316
|
#log_datavars=Yes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.19.0
|
|
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
|
|
@@ -41,8 +41,7 @@ Classifier: Topic :: Database :: Front-Ends
|
|
|
41
41
|
Requires-Python: >=3.10
|
|
42
42
|
Requires-Dist: python-dateutil>=2.8
|
|
43
43
|
Requires-Dist: rich>=13.0
|
|
44
|
-
Requires-Dist:
|
|
45
|
-
Requires-Dist: textual>=0.47.0
|
|
44
|
+
Requires-Dist: textual>=1.0
|
|
46
45
|
Requires-Dist: typer>=0.12
|
|
47
46
|
Provides-Extra: all
|
|
48
47
|
Requires-Dist: defusedxml; extra == 'all'
|
|
@@ -59,6 +58,7 @@ Requires-Dist: psycopg2-binary; extra == 'all'
|
|
|
59
58
|
Requires-Dist: pymysql; extra == 'all'
|
|
60
59
|
Requires-Dist: pyodbc; extra == 'all'
|
|
61
60
|
Requires-Dist: pyyaml; extra == 'all'
|
|
61
|
+
Requires-Dist: sqlglot>=25.0; extra == 'all'
|
|
62
62
|
Requires-Dist: tables; extra == 'all'
|
|
63
63
|
Requires-Dist: tkintermapview>=1.29; extra == 'all'
|
|
64
64
|
Requires-Dist: xlrd; extra == 'all'
|
|
@@ -93,6 +93,7 @@ Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
|
|
|
93
93
|
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
94
94
|
Requires-Dist: pyyaml; extra == 'dev'
|
|
95
95
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
96
|
+
Requires-Dist: sqlglot>=25.0; extra == 'dev'
|
|
96
97
|
Requires-Dist: tables; extra == 'dev'
|
|
97
98
|
Requires-Dist: tox-uv>=1.13.1; extra == 'dev'
|
|
98
99
|
Requires-Dist: twine>=6.1.0; extra == 'dev'
|
|
@@ -111,6 +112,8 @@ Requires-Dist: polars; extra == 'formats'
|
|
|
111
112
|
Requires-Dist: pyyaml; extra == 'formats'
|
|
112
113
|
Requires-Dist: tables; extra == 'formats'
|
|
113
114
|
Requires-Dist: xlrd; extra == 'formats'
|
|
115
|
+
Provides-Extra: formatter
|
|
116
|
+
Requires-Dist: sqlglot>=25.0; extra == 'formatter'
|
|
114
117
|
Provides-Extra: map
|
|
115
118
|
Requires-Dist: tkintermapview>=1.29; extra == 'map'
|
|
116
119
|
Provides-Extra: mssql
|
|
@@ -228,44 +231,46 @@ execsql script.sql # read connection from config file
|
|
|
228
231
|
|
|
229
232
|
## Options
|
|
230
233
|
|
|
231
|
-
| Flag | Description
|
|
232
|
-
| ------------------------------------- |
|
|
233
|
-
| `-t {p,m,s,l,k,a,f,o,d}` | Database type
|
|
234
|
-
| `-u USER` | Database username
|
|
235
|
-
| `-p PORT` | Server port
|
|
236
|
-
| `-a VALUE` | Set substitution variable `$ARG_x`
|
|
237
|
-
| `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values
|
|
238
|
-
| `-c SCRIPT` | Execute inline SQL or metacommand string
|
|
239
|
-
| `-d` | Auto-create export directories
|
|
240
|
-
| `-e ENCODING` / `--database-encoding` | Character encoding used in the database
|
|
241
|
-
| `-f ENCODING` | Script file encoding (default: UTF-8)
|
|
242
|
-
| `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output
|
|
243
|
-
| `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT
|
|
244
|
-
| `-l` | Write run log to `~/execsql.log`
|
|
245
|
-
| `-m` | List metacommands and exit
|
|
246
|
-
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist
|
|
247
|
-
| `-o` / `--online-help` | Open the online documentation in the default browser
|
|
248
|
-
| `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file)
|
|
249
|
-
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full)
|
|
250
|
-
| `-w` | Skip password prompt when a username is supplied
|
|
251
|
-
| `-y` / `--encodings` | List available encoding names and exit
|
|
252
|
-
| `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32)
|
|
253
|
-
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`)
|
|
254
|
-
| `--output-dir DIR` | Default base directory for EXPORT output files
|
|
255
|
-
| `--dry-run` | Parse the script and report commands without executing
|
|
256
|
-
| `--lint` | Static analysis: check structure and warn on issues (no DB)
|
|
257
|
-
| `--parse-tree` | Print the script's AST structure and exit (no DB)
|
|
258
|
-
| `--list-plugins` | List discovered plugins and exit
|
|
259
|
-
| `--ping` | Test database connectivity and exit
|
|
260
|
-
| `--profile` | Show per-statement timing summary after execution
|
|
261
|
-
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20)
|
|
262
|
-
| `--progress` | Show a progress bar for long-running IMPORT operations
|
|
263
|
-
| `--config FILE` | Load an explicit config file (highest priority after CLI args)
|
|
264
|
-
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs)
|
|
265
|
-
| `--
|
|
266
|
-
| `--
|
|
267
|
-
| `--
|
|
268
|
-
| `--
|
|
234
|
+
| Flag | Description |
|
|
235
|
+
| ------------------------------------- | ------------------------------------------------------------------ |
|
|
236
|
+
| `-t {p,m,s,l,k,a,f,o,d}` | Database type |
|
|
237
|
+
| `-u USER` | Database username |
|
|
238
|
+
| `-p PORT` | Server port |
|
|
239
|
+
| `-a VALUE` | Set substitution variable `$ARG_x` |
|
|
240
|
+
| `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values |
|
|
241
|
+
| `-c SCRIPT` | Execute inline SQL or metacommand string |
|
|
242
|
+
| `-d` | Auto-create export directories |
|
|
243
|
+
| `-e ENCODING` / `--database-encoding` | Character encoding used in the database |
|
|
244
|
+
| `-f ENCODING` | Script file encoding (default: UTF-8) |
|
|
245
|
+
| `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output |
|
|
246
|
+
| `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT |
|
|
247
|
+
| `-l` | Write run log to `~/execsql.log` |
|
|
248
|
+
| `-m` | List metacommands and exit |
|
|
249
|
+
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
|
|
250
|
+
| `-o` / `--online-help` | Open the online documentation in the default browser |
|
|
251
|
+
| `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file) |
|
|
252
|
+
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
|
|
253
|
+
| `-w` | Skip password prompt when a username is supplied |
|
|
254
|
+
| `-y` / `--encodings` | List available encoding names and exit |
|
|
255
|
+
| `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32) |
|
|
256
|
+
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
|
|
257
|
+
| `--output-dir DIR` | Default base directory for EXPORT output files |
|
|
258
|
+
| `--dry-run` | Parse the script and report commands without executing |
|
|
259
|
+
| `--lint` | Static analysis: check structure and warn on issues (no DB) |
|
|
260
|
+
| `--parse-tree` | Print the script's AST structure and exit (no DB) |
|
|
261
|
+
| `--list-plugins` | List discovered plugins and exit |
|
|
262
|
+
| `--ping` | Test database connectivity and exit |
|
|
263
|
+
| `--profile` | Show per-statement timing summary after execution |
|
|
264
|
+
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20) |
|
|
265
|
+
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
266
|
+
| `--config FILE` | Load an explicit config file (highest priority after CLI args) |
|
|
267
|
+
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs) |
|
|
268
|
+
| `--no-rm-file` | Disable the `RM_FILE` metacommand (no script-driven file deletion) |
|
|
269
|
+
| `--no-serve` | Disable the `SERVE` metacommand (no script-driven file streaming) |
|
|
270
|
+
| `--init-config` | Print a default `execsql.conf` template to stdout and exit |
|
|
271
|
+
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
272
|
+
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
273
|
+
| `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
|
|
269
274
|
|
|
270
275
|
Run `execsql --help` for the full option list, or `execsql -m` to list all metacommands.
|
|
271
276
|
|
|
@@ -378,14 +383,20 @@ The `PROMPT` metacommand produces a GUI display of the data:
|
|
|
378
383
|
|
|
379
384
|
# Formatting Scripts
|
|
380
385
|
|
|
381
|
-
The `execsql-format` command normalizes execsql script files: it uppercases metacommand keywords, corrects block indentation, and optionally reformats SQL via sqlglot
|
|
386
|
+
The `execsql-format` command normalizes execsql script files: it uppercases metacommand keywords, corrects block indentation, and optionally reformats SQL via `sqlglot`. The metacommand / indent / keyword reformatting is built into `execsql2`; SQL reformatting requires the `[formatter]` extra (or pass `--no-sql` to skip it):
|
|
382
387
|
|
|
383
388
|
```bash
|
|
389
|
+
# Install with the SQL-reformatting extra
|
|
390
|
+
pip install execsql2[formatter]
|
|
391
|
+
|
|
384
392
|
# Format files in place
|
|
385
393
|
execsql-format --in-place scripts/
|
|
386
394
|
|
|
387
395
|
# Check formatting without writing (useful in CI)
|
|
388
396
|
execsql-format --check scripts/
|
|
397
|
+
|
|
398
|
+
# Run the formatter without sqlglot — keyword/indent normalization only
|
|
399
|
+
execsql-format --no-sql --in-place scripts/
|
|
389
400
|
```
|
|
390
401
|
|
|
391
402
|
`execsql-format` is also available as a [pre-commit](https://pre-commit.com/) hook:
|
|
@@ -393,7 +404,7 @@ execsql-format --check scripts/
|
|
|
393
404
|
```yaml
|
|
394
405
|
repos:
|
|
395
406
|
- repo: https://github.com/geocoug/execsql
|
|
396
|
-
rev: v2.
|
|
407
|
+
rev: v2.18.0
|
|
397
408
|
hooks:
|
|
398
409
|
- id: execsql-format
|
|
399
410
|
args: [--in-place]
|
|
@@ -3,40 +3,39 @@ execsql/__main__.py,sha256=HdbK-SAhyUmfB6xINY5AzxdMSxGzWSGEG_2dv42Jn64,315
|
|
|
3
3
|
execsql/api.py,sha256=ZFTo_XZPhG21w2vxaeS1lS6o5XmF1FUJRIaypgTOjA8,20919
|
|
4
4
|
execsql/config.py,sha256=OOrCcn9m3CNuXkxVOLp7uMhQikzUS2wh_QVhvIzRqIM,33296
|
|
5
5
|
execsql/exceptions.py,sha256=EkM5cw2s0D9QCOgS4BU29FEyOnEtCxJ0esPT6l1hT9s,9205
|
|
6
|
-
execsql/format.py,sha256=
|
|
6
|
+
execsql/format.py,sha256=HJlWD4kSOvq7lBv99rRF1Cq1FNaMjG-qA3iwSTZtf8Q,26050
|
|
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
|
|
10
10
|
execsql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
execsql/state.py,sha256=T6UoXXxAkUP-4KKQpfFAlI3WMzm2xUi3LSplJDuRLY0,21965
|
|
12
12
|
execsql/types.py,sha256=5K3aTuWQZHftz5slFODwqxlcvvt6lROISUnvUtRUazs,31799
|
|
13
|
-
execsql/cli/__init__.py,sha256=
|
|
13
|
+
execsql/cli/__init__.py,sha256=aJknKKIGxYCXpny0cyHXfqJsJ95dBtlEXXhPASFG8GQ,23114
|
|
14
14
|
execsql/cli/dsn.py,sha256=svaZtrUXFRL2W5G6FRRiKtR6kehOp7urrVhIx_642Z8,2820
|
|
15
15
|
execsql/cli/help.py,sha256=ThwdZuMIfLPxLAPpGWwXFY_UfyWvYOCjdlBNK20Vzd8,5718
|
|
16
|
-
execsql/cli/lint.py,sha256=
|
|
17
|
-
execsql/cli/
|
|
18
|
-
execsql/cli/run.py,sha256=QoSHVBfg20n2knPrqf7RFJmcfFpC5aq7NkwX5o6qRnA,36326
|
|
16
|
+
execsql/cli/lint.py,sha256=YqKzFNUhyb_Th69hYgKk1ZZVjCsZfJMIiUGqp06JwNs,17236
|
|
17
|
+
execsql/cli/run.py,sha256=4plvi8ZaGea5fUeOhtS2f4MrVnGTaetvJyz7qX07s0I,37706
|
|
19
18
|
execsql/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
execsql/data/execsql.conf.template,sha256=
|
|
19
|
+
execsql/data/execsql.conf.template,sha256=Sq1Huwb_Uf_lI7zW7m3h11fqU6zjGpGnCU4OVJ_tCe0,10696
|
|
21
20
|
execsql/db/__init__.py,sha256=jTbuafuKOqYtXFR1wvCOoKK5Lr3l1uErfaIbIr6UywI,1063
|
|
22
|
-
execsql/db/access.py,sha256=
|
|
23
|
-
execsql/db/base.py,sha256=
|
|
21
|
+
execsql/db/access.py,sha256=GcY5Pq_vXdp8Thzm6aSLg9vfp5koovGiYKXipQtRt50,19167
|
|
22
|
+
execsql/db/base.py,sha256=UUiYSztUen8xCWVCxDkRQeD098Yv1iA3WPl_qo4XjDI,34686
|
|
24
23
|
execsql/db/dsn.py,sha256=59OzMAuCIfHcdOZNarK9TlDzaBJjhZ1SFFMvyXlH6u8,6086
|
|
25
24
|
execsql/db/duckdb.py,sha256=79lRzKRhw1Pjfqcrba27S4Oq8a8AbDO_d0XkaNKKPQo,3197
|
|
26
25
|
execsql/db/factory.py,sha256=YHdgyqQYy16548O3fGyElLC5C7DdIgva4Z29OsDxXjs,5367
|
|
27
26
|
execsql/db/firebird.py,sha256=p_7RFWhFI7y5ukKCMXeDPE0wjeQ6dpO4IK6uz2dYjrc,9224
|
|
28
|
-
execsql/db/mysql.py,sha256=
|
|
29
|
-
execsql/db/oracle.py,sha256=
|
|
27
|
+
execsql/db/mysql.py,sha256=gOm1IRzFmxnT4ekGEXiF2yM3jB6r4p38Cmtt1Utj27Y,17712
|
|
28
|
+
execsql/db/oracle.py,sha256=1_odb5xmlm8vjdJdQXz7SHm9dzIbZ5sxP_IxTklH3kA,12018
|
|
30
29
|
execsql/db/postgres.py,sha256=UNzrXzMniEyT3Z7qjCA_HLEUY0PVr1cJShuhAxtl5l0,21241
|
|
31
30
|
execsql/db/sqlite.py,sha256=xooU6bvD9Y3frRpnbyesE63r6E1fwEHkkcN1YD_UIUE,11519
|
|
32
|
-
execsql/db/sqlserver.py,sha256=
|
|
31
|
+
execsql/db/sqlserver.py,sha256=sxtOrcN1pGJQ0x7CctrKIL9XFIaHCDVIVvEvxzEOdzY,8744
|
|
33
32
|
execsql/debug/__init__.py,sha256=j6EGUR0dHzUhWN1mHHtf1-Lhjq3Sb1V-vmnq2Ztgj1M,178
|
|
34
|
-
execsql/debug/repl.py,sha256=
|
|
33
|
+
execsql/debug/repl.py,sha256=JObeoEXh15qyk4Q3WQCCqMcBtojlIcu_Xg-ZRDZJi5Q,25491
|
|
35
34
|
execsql/exporters/__init__.py,sha256=-Cnji-OgodJV8ftcDcOyTof0kQMy9J5kKVC8GVFpc3o,670
|
|
36
35
|
execsql/exporters/base.py,sha256=XTPenHl5TbmbZ3cfPYLVFirGNiVps3Kt3AQKFVKy6ss,6408
|
|
37
36
|
execsql/exporters/delimited.py,sha256=GIEeennL_elvcZgq5oSvgxAKAgwr8ea3o5_M_pLmc4g,32341
|
|
38
37
|
execsql/exporters/duckdb.py,sha256=R4WbvzBEIK1ptnIc8w6c7jcinG-cuuFYC85_NYCumH0,3146
|
|
39
|
-
execsql/exporters/feather.py,sha256=
|
|
38
|
+
execsql/exporters/feather.py,sha256=ardyidIcuhrqRBJh9ftUZkG3ZCY7NS7eGC-VAnpFIXI,4131
|
|
40
39
|
execsql/exporters/html.py,sha256=BPTGYODiC5_5zaQsVkZ9QVAl67yfCWFTsjK0D-QOJkM,9717
|
|
41
40
|
execsql/exporters/json.py,sha256=G9lyJcjgmMvymu_MoVrkSqx2H6JRN7qwA5UEomnPkVQ,4343
|
|
42
41
|
execsql/exporters/latex.py,sha256=w_B83_5vKPe8uYxCWGdqvxwJeq0mw5zzKYDiAb7dbN0,4503
|
|
@@ -60,34 +59,34 @@ execsql/gui/console.py,sha256=pCBUcFGjlKXMkMjztbmt9glP3me9jAKAgQxnmUE38-0,19396
|
|
|
60
59
|
execsql/gui/desktop.py,sha256=hdy-1-QxeKXe3GDG1lhxkQHhTjZxH7KNl_mVg5ki1qw,59080
|
|
61
60
|
execsql/gui/tui.py,sha256=INBCuW6iEEV15P_JrHeviI1JlNCXfoiyPBU-IC5MaLY,65200
|
|
62
61
|
execsql/importers/__init__.py,sha256=zZwdQxMaValCNqUrVdvaA7XPU3J8NmqVJ4uolNWY2iU,299
|
|
63
|
-
execsql/importers/base.py,sha256=
|
|
62
|
+
execsql/importers/base.py,sha256=k4ni92H33_-A32dNfxhBd6R5WPwM3o_8dSGABisUlR0,4136
|
|
64
63
|
execsql/importers/csv.py,sha256=GUVRP294vHlOlF8XNecPEzatUBOOFIqnrEV9cBQkiv0,4849
|
|
65
64
|
execsql/importers/feather.py,sha256=g2B69d2uv9vmnXcmjFyTVsMP40LYEzFYkhk3gD26mGw,1900
|
|
66
65
|
execsql/importers/json.py,sha256=fL47h09Zzx4Qw3TTQRQJbdPdD3qVMiU-kK9C7j1IQzg,5534
|
|
67
66
|
execsql/importers/ods.py,sha256=VsxIkr7opBamB0PbSFmMWt7G11w8lLUH1k_kRwz28zw,2847
|
|
68
|
-
execsql/importers/xls.py,sha256=
|
|
69
|
-
execsql/metacommands/__init__.py,sha256=
|
|
67
|
+
execsql/importers/xls.py,sha256=7mGYqXko5_wyvEUz186xSzIx8Eeobn5A6DCVAg4YfTM,3838
|
|
68
|
+
execsql/metacommands/__init__.py,sha256=H_D1Z5kN6zfYtxatejQhQhQV1tlVZ7rcdFZ_9uhXu2Q,13010
|
|
70
69
|
execsql/metacommands/conditions.py,sha256=5njexsBqSN_MNQdnw9Ra51BcS7ekACHNvBI2MXZF6_g,29995
|
|
71
70
|
execsql/metacommands/connect.py,sha256=W24gYGmYDXNQyzBTsqWtl9-qbX2FS0v_c4s_OHj97mY,15327
|
|
72
71
|
execsql/metacommands/control.py,sha256=btF9hP_jzTuTIODPK72CYF0v_oKYpwXpKLATt-Ti2kc,7988
|
|
73
|
-
execsql/metacommands/data.py,sha256=
|
|
72
|
+
execsql/metacommands/data.py,sha256=PPO6AnF_swFxy9GNrtMsv3YttT5ZrjOMA4QtEORDcso,12136
|
|
74
73
|
execsql/metacommands/debug.py,sha256=MeVXAob8ItEg2QzuSUkKDaQCEABnH6u0XcAwJzw36CE,13015
|
|
75
|
-
execsql/metacommands/dispatch.py,sha256=
|
|
74
|
+
execsql/metacommands/dispatch.py,sha256=t7x0xWHJA0PeCrYf7jYeSMJgf0yxcZ_xh-_YAtBPHLw,87192
|
|
76
75
|
execsql/metacommands/io.py,sha256=vlGBje5sgnqeilooMdhJDgSRIhysHy5_7LrKtik9Xjs,3011
|
|
77
76
|
execsql/metacommands/io_export.py,sha256=-7VDtUUQegwGRantw-NpdkI_9hwIKU-36ZvReIYC2QA,14285
|
|
78
77
|
execsql/metacommands/io_fileops.py,sha256=6yED22UlVNXcRHNxZTgna8HmwFcR8s4nt6epMGLMtHY,10139
|
|
79
78
|
execsql/metacommands/io_import.py,sha256=kUHIiI16WMuxXpqDMIRBc06KvI-_sYuYfJVNMTKOoAo,15706
|
|
80
79
|
execsql/metacommands/io_write.py,sha256=RqOklmeGifu3lQgi_0glOoBWTY9FFIDlWIpOpb_hc3o,12789
|
|
81
80
|
execsql/metacommands/prompt.py,sha256=LUl7doqKx8fLR0qt2lXbhdgNpdF1s6KZ6Ruitt10YnM,37483
|
|
82
|
-
execsql/metacommands/script_ext.py,sha256=
|
|
81
|
+
execsql/metacommands/script_ext.py,sha256=CsUbyq8QFPnnvfX_k7fPlYNvvRw1xLmAWER4Exo5pwc,3658
|
|
83
82
|
execsql/metacommands/system.py,sha256=5ETiWbkVDGsdwTldDaMM4rwY_Smd02wCWUROtf6XH_M,7385
|
|
84
83
|
execsql/metacommands/upsert.py,sha256=wzoMpM8g49pEvU9GkHZ62fPvqV3w1UIUfxVA7HAsS_o,20317
|
|
85
84
|
execsql/script/__init__.py,sha256=eGJPBDWj42aaId2lX_quSrqoKrvGwGElIrGDNCyoV1Y,3547
|
|
86
85
|
execsql/script/ast.py,sha256=TQ4_7Lfw1F8_k6ycdvMZdzwNafrZiljSrthVRWUsuIk,20585
|
|
87
86
|
execsql/script/control.py,sha256=WqLy-HLPqHG3vEzYpKMiIJsD7LpORjyQuUWzFzcGz4w,2327
|
|
88
|
-
execsql/script/engine.py,sha256=
|
|
89
|
-
execsql/script/executor.py,sha256=
|
|
90
|
-
execsql/script/parser.py,sha256=
|
|
87
|
+
execsql/script/engine.py,sha256=52RmtQJGk4KWqXpZY7jfKeiPojAoULHWaigOcm1azm4,20979
|
|
88
|
+
execsql/script/executor.py,sha256=Y0con6Mz4n240krEVeVSqmeCLVvIXyF4aLlNBMyZQyc,35790
|
|
89
|
+
execsql/script/parser.py,sha256=z19gERLqyvEoDx4hDUws3Z8EKqTM7huNOitTOSmg8_0,35300
|
|
91
90
|
execsql/script/variables.py,sha256=t0BwrRuA8m1LYHGLkDPNbqW6QmudXroOFYsO0fwK2N0,16302
|
|
92
91
|
execsql/utils/__init__.py,sha256=0uR6JwVJQRX3vceByNBduCAf5dd5assKjeqJUWvpZoA,278
|
|
93
92
|
execsql/utils/auth.py,sha256=dnLie8jFxN_l7ZrrRufVuxGw92iG62DIVatIjlEb4pM,8717
|
|
@@ -101,24 +100,24 @@ execsql/utils/numeric.py,sha256=xh02ANSRk3nUpQ-rtm66ILoMqoi7HtzCoRMIOT9U8QI,1570
|
|
|
101
100
|
execsql/utils/regex.py,sha256=diEzTZqU_HHwVMadPAvN1Vgzhl7I03eVaEFGCXyGGL8,3770
|
|
102
101
|
execsql/utils/strings.py,sha256=UQNjpRCEFa1UO6feU-M-9e24wWAvizs_iu_4fFusLxo,8516
|
|
103
102
|
execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
|
|
104
|
-
execsql2-2.
|
|
105
|
-
execsql2-2.
|
|
106
|
-
execsql2-2.
|
|
107
|
-
execsql2-2.
|
|
108
|
-
execsql2-2.
|
|
109
|
-
execsql2-2.
|
|
110
|
-
execsql2-2.
|
|
111
|
-
execsql2-2.
|
|
112
|
-
execsql2-2.
|
|
113
|
-
execsql2-2.
|
|
114
|
-
execsql2-2.
|
|
115
|
-
execsql2-2.
|
|
116
|
-
execsql2-2.
|
|
117
|
-
execsql2-2.
|
|
118
|
-
execsql2-2.
|
|
119
|
-
execsql2-2.
|
|
120
|
-
execsql2-2.
|
|
121
|
-
execsql2-2.
|
|
122
|
-
execsql2-2.
|
|
123
|
-
execsql2-2.
|
|
124
|
-
execsql2-2.
|
|
103
|
+
execsql2-2.19.0.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
|
|
104
|
+
execsql2-2.19.0.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
|
|
105
|
+
execsql2-2.19.0.data/data/execsql2_extras/example_config_prompt.sql,sha256=2e8KzzVWhho8KxYVHETSVmZdhW7wodioDsqBLSL6m4s,7487
|
|
106
|
+
execsql2-2.19.0.data/data/execsql2_extras/execsql.conf,sha256=Sq1Huwb_Uf_lI7zW7m3h11fqU6zjGpGnCU4OVJ_tCe0,10696
|
|
107
|
+
execsql2-2.19.0.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
|
|
108
|
+
execsql2-2.19.0.data/data/execsql2_extras/md_compare.sql,sha256=qYYVAjSeHZzjszxV3Bv6bg8Ckbq2kMHl87_gh4sywMU,24140
|
|
109
|
+
execsql2-2.19.0.data/data/execsql2_extras/md_glossary.sql,sha256=hkZ2Onn57LAKKsuXxzhR8tPtcWXkmWEQkwPE58-Tm2k,10796
|
|
110
|
+
execsql2-2.19.0.data/data/execsql2_extras/md_upsert.sql,sha256=_CAK4BzEboRXTNy03SJR-oOjcEdSNMuRBPL6noWUptY,112560
|
|
111
|
+
execsql2-2.19.0.data/data/execsql2_extras/pg_compare.sql,sha256=1zJd4hVUKHR0tncc2qTBC9B4qVV4Us2ITkJpsjN3tMw,24352
|
|
112
|
+
execsql2-2.19.0.data/data/execsql2_extras/pg_glossary.sql,sha256=IKuwna-_8b20ljSkXZruuiQigrCpo7ueQdUqd1MXiuI,9908
|
|
113
|
+
execsql2-2.19.0.data/data/execsql2_extras/pg_upsert.sql,sha256=HpPJtTHvpEjQy03j-3iPxDEOHMRkudOg7O4D4YR38UI,108315
|
|
114
|
+
execsql2-2.19.0.data/data/execsql2_extras/script_template.sql,sha256=2J35ddZPguJ-vwTsz83wErv0jiWUyJcdW_JM0mNKDXA,11155
|
|
115
|
+
execsql2-2.19.0.data/data/execsql2_extras/ss_compare.sql,sha256=j1qVNUPXQsEU7-DoVgDJCGcE0EuIl7whLBT3fgeiMAo,24833
|
|
116
|
+
execsql2-2.19.0.data/data/execsql2_extras/ss_glossary.sql,sha256=2gLxv34xzKt0vy7hSzJH7a9JiMC3ETrv9MofxQwAibU,13065
|
|
117
|
+
execsql2-2.19.0.data/data/execsql2_extras/ss_upsert.sql,sha256=G_8rQ0VzuKIZHWs24O_WrfzpC5S27R1JsL-bFBR3SUQ,117730
|
|
118
|
+
execsql2-2.19.0.dist-info/METADATA,sha256=hafmsG77iDxgwzrG7s4oMg5o97R-fMx_BH513avRy2w,22340
|
|
119
|
+
execsql2-2.19.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
120
|
+
execsql2-2.19.0.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
|
|
121
|
+
execsql2-2.19.0.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
|
|
122
|
+
execsql2-2.19.0.dist-info/licenses/NOTICE,sha256=McYzgxYav3U1OaVsY4Su1sfBrfmplpRdA9b6-gCDQCg,342
|
|
123
|
+
execsql2-2.19.0.dist-info/RECORD,,
|