execsql2 2.11.0__py3-none-any.whl → 2.12.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 +6 -0
- execsql/cli/lint.py +1 -1
- execsql/cli/run.py +18 -12
- execsql/debug/__init__.py +6 -0
- execsql/debug/repl.py +472 -0
- execsql/exporters/xlsx.py +5 -0
- execsql/exporters/yaml.py +2 -0
- execsql/metacommands/__init__.py +1 -1
- execsql/metacommands/conditions.py +1 -1
- execsql/metacommands/control.py +4 -10
- execsql/metacommands/debug.py +1 -1
- execsql/metacommands/dispatch.py +1 -1
- execsql/script/engine.py +15 -5
- execsql/state.py +2 -2
- execsql/utils/gui.py +26 -4
- {execsql2-2.11.0.dist-info → execsql2-2.12.0.dist-info}/METADATA +5 -2
- {execsql2-2.11.0.dist-info → execsql2-2.12.0.dist-info}/RECORD +36 -35
- execsql/metacommands/debug_repl.py +0 -288
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/execsql.conf +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.11.0.data → execsql2-2.12.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.11.0.dist-info → execsql2-2.12.0.dist-info}/WHEEL +0 -0
- {execsql2-2.11.0.dist-info → execsql2-2.12.0.dist-info}/entry_points.txt +0 -0
- {execsql2-2.11.0.dist-info → execsql2-2.12.0.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.11.0.dist-info → execsql2-2.12.0.dist-info}/licenses/NOTICE +0 -0
execsql/metacommands/control.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from execsql.exceptions import ErrInfo
|
|
3
2
|
|
|
4
3
|
"""
|
|
5
4
|
Control-flow metacommand handlers for execsql.
|
|
@@ -18,6 +17,8 @@ Implements the imperative ``x_*`` functions for script flow control:
|
|
|
18
17
|
"""
|
|
19
18
|
|
|
20
19
|
import time
|
|
20
|
+
|
|
21
|
+
from execsql.exceptions import ErrInfo
|
|
21
22
|
from typing import Any
|
|
22
23
|
|
|
23
24
|
import execsql.state as _state
|
|
@@ -62,7 +63,8 @@ def x_assert(**kwargs: Any) -> None:
|
|
|
62
63
|
|
|
63
64
|
result = _state.xcmd_test(condition)
|
|
64
65
|
if result:
|
|
65
|
-
_state.exec_log
|
|
66
|
+
if _state.exec_log is not None:
|
|
67
|
+
_state.exec_log.log_user_msg(f"ASSERT passed: {condition}")
|
|
66
68
|
else:
|
|
67
69
|
raise ErrInfo(type="cmd", other_msg=message)
|
|
68
70
|
|
|
@@ -134,14 +136,6 @@ def x_loop(**kwargs: Any) -> None:
|
|
|
134
136
|
)
|
|
135
137
|
|
|
136
138
|
|
|
137
|
-
def endloop() -> None:
|
|
138
|
-
if len(_state.loopcommandstack) == 0:
|
|
139
|
-
raise ErrInfo("error", other_msg="END LOOP metacommand without a matching preceding LOOP metacommand.")
|
|
140
|
-
_state.compiling_loop = False
|
|
141
|
-
_state.commandliststack.append(_state.loopcommandstack[-1])
|
|
142
|
-
_state.loopcommandstack.pop()
|
|
143
|
-
|
|
144
|
-
|
|
145
139
|
def x_halt(**kwargs: Any) -> None:
|
|
146
140
|
errmsg = kwargs["errmsg"]
|
|
147
141
|
tee = kwargs["tee"]
|
execsql/metacommands/debug.py
CHANGED
|
@@ -74,7 +74,7 @@ def x_debug_log_subvars(**kwargs: Any) -> None:
|
|
|
74
74
|
local = kwargs["local"]
|
|
75
75
|
user = kwargs["user"]
|
|
76
76
|
for s in _state.commandliststack[-1].localvars.substitutions:
|
|
77
|
-
_state.exec_log.log_status_info(f"Substitution [{s}] = [{s}]")
|
|
77
|
+
_state.exec_log.log_status_info(f"Substitution [{s[0]}] = [{s[1]}]")
|
|
78
78
|
if local is None:
|
|
79
79
|
for s in _state.subvars.substitutions:
|
|
80
80
|
if user is None or s[0][0].isalnum() or s[0][0] == "_":
|
execsql/metacommands/dispatch.py
CHANGED
|
@@ -100,7 +100,7 @@ from execsql.metacommands.debug import (
|
|
|
100
100
|
x_debug_write_odbc_drivers,
|
|
101
101
|
x_debug_write_subvars,
|
|
102
102
|
)
|
|
103
|
-
from execsql.
|
|
103
|
+
from execsql.debug.repl import x_breakpoint
|
|
104
104
|
from execsql.metacommands.io import (
|
|
105
105
|
x_cd,
|
|
106
106
|
x_copy,
|
execsql/script/engine.py
CHANGED
|
@@ -194,7 +194,7 @@ class MetaCommandList:
|
|
|
194
194
|
patterns; each compiles into a separate :class:`MetaCommand` prepended to
|
|
195
195
|
the dispatch list so that later registrations take priority.
|
|
196
196
|
"""
|
|
197
|
-
if
|
|
197
|
+
if isinstance(matching_regexes, (tuple, list)):
|
|
198
198
|
raw_patterns = list(matching_regexes)
|
|
199
199
|
regexes = [re.compile(rx, re.I) for rx in raw_patterns]
|
|
200
200
|
else:
|
|
@@ -305,9 +305,14 @@ class SqlStmt:
|
|
|
305
305
|
except Exception:
|
|
306
306
|
e = ErrInfo(type="exception", exception_msg=exception_desc())
|
|
307
307
|
if e:
|
|
308
|
+
from execsql.utils.errors import stamp_errinfo
|
|
309
|
+
|
|
310
|
+
stamp_errinfo(e)
|
|
308
311
|
_state.subvars.add_substitution("$LAST_ERROR", cmd)
|
|
309
|
-
_state.subvars.add_substitution("$ERROR_MESSAGE",
|
|
312
|
+
_state.subvars.add_substitution("$ERROR_MESSAGE", e.errmsg())
|
|
310
313
|
_state.status.sql_error = True
|
|
314
|
+
if _state.exec_log is not None:
|
|
315
|
+
_state.exec_log.log_status_info(f"SQL error: {e.errmsg()}")
|
|
311
316
|
if _state.status.halt_on_err:
|
|
312
317
|
from execsql.utils.errors import exit_now
|
|
313
318
|
|
|
@@ -349,9 +354,14 @@ class MetacommandStmt:
|
|
|
349
354
|
except Exception:
|
|
350
355
|
e = ErrInfo(type="exception", exception_msg=exception_desc())
|
|
351
356
|
if e:
|
|
357
|
+
from execsql.utils.errors import stamp_errinfo
|
|
358
|
+
|
|
359
|
+
stamp_errinfo(e)
|
|
352
360
|
_state.status.metacommand_error = True
|
|
353
361
|
_state.subvars.add_substitution("$LAST_ERROR", cmd)
|
|
354
|
-
_state.subvars.add_substitution("$ERROR_MESSAGE",
|
|
362
|
+
_state.subvars.add_substitution("$ERROR_MESSAGE", e.errmsg())
|
|
363
|
+
if _state.exec_log is not None:
|
|
364
|
+
_state.exec_log.log_status_info(f"Metacommand error: {e.errmsg()}")
|
|
355
365
|
if _state.status.halt_on_metacommand_err:
|
|
356
366
|
# Re-raise the original ErrInfo so its message is preserved, not
|
|
357
367
|
# replaced with the generic "Unknown metacommand" text.
|
|
@@ -495,9 +505,9 @@ class CommandList:
|
|
|
495
505
|
_state.subvars.add_substitution("$SCRIPT_LINE", str(cmditem.line_no))
|
|
496
506
|
if _state.step_mode:
|
|
497
507
|
_state.step_mode = False
|
|
498
|
-
from execsql.
|
|
508
|
+
from execsql.debug.repl import _debug_repl
|
|
499
509
|
|
|
500
|
-
_debug_repl()
|
|
510
|
+
_debug_repl(step=True)
|
|
501
511
|
_profiling = _state.profile_data is not None
|
|
502
512
|
if _profiling:
|
|
503
513
|
import time as _time
|
execsql/state.py
CHANGED
|
@@ -336,8 +336,7 @@ class _StateModule(types.ModuleType):
|
|
|
336
336
|
# via setattr (local) or delattr (non-local). Since context
|
|
337
337
|
# attrs live on _ctx, not __dict__, patch takes the delattr
|
|
338
338
|
# path. We reset to the default rather than truly deleting.
|
|
339
|
-
|
|
340
|
-
setattr(self.__dict__["_ctx"], name, getattr(_defaults, name))
|
|
339
|
+
setattr(self.__dict__["_ctx"], name, getattr(_DEFAULT_CTX, name))
|
|
341
340
|
else:
|
|
342
341
|
super().__delattr__(name)
|
|
343
342
|
|
|
@@ -468,4 +467,5 @@ def initialize(
|
|
|
468
467
|
# ---------------------------------------------------------------------------
|
|
469
468
|
|
|
470
469
|
_ctx = RuntimeContext()
|
|
470
|
+
_DEFAULT_CTX = RuntimeContext() # Cached defaults for __delattr__ reset
|
|
471
471
|
sys.modules[__name__].__class__ = _StateModule
|
execsql/utils/gui.py
CHANGED
|
@@ -358,13 +358,35 @@ def gui_console_wait_user(message: str = "") -> None:
|
|
|
358
358
|
print(message, file=sys.stderr)
|
|
359
359
|
|
|
360
360
|
|
|
361
|
-
def gui_console_width() -> int:
|
|
362
|
-
"""
|
|
361
|
+
def gui_console_width(width: int | None = None) -> int:
|
|
362
|
+
"""Get or set the console width in characters.
|
|
363
|
+
|
|
364
|
+
When *width* is provided, updates the active GUI console (if any).
|
|
365
|
+
Always returns the current width.
|
|
366
|
+
"""
|
|
367
|
+
global _console_width
|
|
368
|
+
if width is not None:
|
|
369
|
+
import execsql.state as _state
|
|
370
|
+
|
|
371
|
+
_console_width = int(width)
|
|
372
|
+
if _state.gui_console is not None and hasattr(_state.gui_console, "set_width"):
|
|
373
|
+
_state.gui_console.set_width(_console_width)
|
|
363
374
|
return _console_width
|
|
364
375
|
|
|
365
376
|
|
|
366
|
-
def gui_console_height() -> int:
|
|
367
|
-
"""
|
|
377
|
+
def gui_console_height(height: int | None = None) -> int:
|
|
378
|
+
"""Get or set the console height in lines.
|
|
379
|
+
|
|
380
|
+
When *height* is provided, updates the active GUI console (if any).
|
|
381
|
+
Always returns the current height.
|
|
382
|
+
"""
|
|
383
|
+
global _console_height
|
|
384
|
+
if height is not None:
|
|
385
|
+
import execsql.state as _state
|
|
386
|
+
|
|
387
|
+
_console_height = int(height)
|
|
388
|
+
if _state.gui_console is not None and hasattr(_state.gui_console, "set_height"):
|
|
389
|
+
_state.gui_console.set_height(_console_height)
|
|
368
390
|
return _console_height
|
|
369
391
|
|
|
370
392
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.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: Repository, https://github.com/geocoug/execsql
|
|
6
6
|
Project-URL: Issues, https://github.com/geocoug/execsql/issues
|
|
@@ -220,8 +220,11 @@ execsql script.sql # read connection from config file
|
|
|
220
220
|
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
|
|
221
221
|
| `-w` | Skip password prompt when a username is supplied |
|
|
222
222
|
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
|
|
223
|
+
| `--output-dir DIR` | Default base directory for EXPORT output files |
|
|
223
224
|
| `--dry-run` | Parse the script and report commands without executing |
|
|
224
225
|
| `--lint` | Static analysis: check structure and warn on issues (no DB) |
|
|
226
|
+
| `--ping` | Test database connectivity and exit |
|
|
227
|
+
| `--profile` | Show per-statement timing summary after execution |
|
|
225
228
|
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
226
229
|
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
227
230
|
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
@@ -298,7 +301,7 @@ execsql-format --check scripts/
|
|
|
298
301
|
```yaml
|
|
299
302
|
repos:
|
|
300
303
|
- repo: https://github.com/geocoug/execsql
|
|
301
|
-
rev: v2.
|
|
304
|
+
rev: v2.11.0
|
|
302
305
|
hooks:
|
|
303
306
|
- id: execsql-format
|
|
304
307
|
args: [--in-place]
|
|
@@ -7,13 +7,13 @@ execsql/format.py,sha256=-6iknDddqbkapMo4NKmT5LAynDLqMW5kHgDWRg0KSws,11990
|
|
|
7
7
|
execsql/models.py,sha256=DxkGp9iWbuZDWPGmnxZp9mvEeyOwxEJNx94fxQQiLfQ,13538
|
|
8
8
|
execsql/parser.py,sha256=mbNSMiAMR1NvNvFtQAZq6nxBOupMGJZXSimLWLtZeNs,15537
|
|
9
9
|
execsql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
execsql/state.py,sha256=
|
|
10
|
+
execsql/state.py,sha256=29b3SwG4GirED2KVQc-cCC7Z_-FsGN3fEt9xQNN-Puo,15090
|
|
11
11
|
execsql/types.py,sha256=HVWb4umIB9lpxCGgqk3xy1hoGYPfN39xci5mHF0Izq4,31882
|
|
12
|
-
execsql/cli/__init__.py,sha256=
|
|
12
|
+
execsql/cli/__init__.py,sha256=YXxOVF2lNkCkifXyjoC7yWrhHJFT9PzI7cnCzsLJwT8,16488
|
|
13
13
|
execsql/cli/dsn.py,sha256=svaZtrUXFRL2W5G6FRRiKtR6kehOp7urrVhIx_642Z8,2820
|
|
14
14
|
execsql/cli/help.py,sha256=Sn_TgSJiQeBx-xZH0fuP5OvR_wasSTumjWF9UHfIX5k,5414
|
|
15
|
-
execsql/cli/lint.py,sha256=
|
|
16
|
-
execsql/cli/run.py,sha256=
|
|
15
|
+
execsql/cli/lint.py,sha256=XWuVcEsheZ8ql48VFWqICWEkAUezB2nIePX6SUiKSg8,16109
|
|
16
|
+
execsql/cli/run.py,sha256=JGfndnBnJMkEqbz26pflhEdXDScZNIdGu6b6jTRLYl8,30681
|
|
17
17
|
execsql/db/__init__.py,sha256=jTbuafuKOqYtXFR1wvCOoKK5Lr3l1uErfaIbIr6UywI,1063
|
|
18
18
|
execsql/db/access.py,sha256=L79gUnAnnM9EJ_f4k42jr7DI0qGcKtLOnJTlBC7uPm0,17879
|
|
19
19
|
execsql/db/base.py,sha256=hfMFj8fXY0T1aXLvWJHqb0aU4EQUDFOc-YrS29HH8U4,30405
|
|
@@ -26,6 +26,8 @@ execsql/db/oracle.py,sha256=AFVHhGlCzBuU7JgrAqeUG6e8TUUkk1Y80XVJQnGOqLM,10547
|
|
|
26
26
|
execsql/db/postgres.py,sha256=oXR7ODzQhR3yO6q-aNa9_il_rO3SpOX9yYGsfIqHwLI,20139
|
|
27
27
|
execsql/db/sqlite.py,sha256=2fD3AfckIGWN1oHcOaqQlQnbig26top1IlW-ejPHttI,10204
|
|
28
28
|
execsql/db/sqlserver.py,sha256=mNwmIIxTzqXU-cOjpNpeFi568HjQHsAk8Xnn-tR6F_E,7563
|
|
29
|
+
execsql/debug/__init__.py,sha256=j6EGUR0dHzUhWN1mHHtf1-Lhjq3Sb1V-vmnq2Ztgj1M,178
|
|
30
|
+
execsql/debug/repl.py,sha256=HeQ9emFKUjo7UTouxuTcmpGCTJIR1nOLxKkRJ5mvd2c,16669
|
|
29
31
|
execsql/exporters/__init__.py,sha256=-Cnji-OgodJV8ftcDcOyTof0kQMy9J5kKVC8GVFpc3o,670
|
|
30
32
|
execsql/exporters/base.py,sha256=W9USFyk_2eztjJ51X6CJh7-chE1i3cSx-STOtbHXCNI,6373
|
|
31
33
|
execsql/exporters/delimited.py,sha256=zMvurTRVl5W-6N8DuYtn_xILUkYLMlfflwWMfvdeaF0,30304
|
|
@@ -44,9 +46,9 @@ execsql/exporters/sqlite.py,sha256=XA0ALLvy-r6Pz1lpOFkWWbvpSP9Hm1tHHiuo_BvPVDk,2
|
|
|
44
46
|
execsql/exporters/templates.py,sha256=T9nk7vJrlxiPGfOWGc79xqqDxK3TCYu0wXq48U02npw,5564
|
|
45
47
|
execsql/exporters/values.py,sha256=HIyud31aux_dbCphfKHEGeZB9fkIPE5PoGXQz817XIE,2520
|
|
46
48
|
execsql/exporters/xls.py,sha256=nPROgxL8XK2oiBVoqN2L-o0j_jynRIMokwB8NpvOBt0,10623
|
|
47
|
-
execsql/exporters/xlsx.py,sha256=
|
|
49
|
+
execsql/exporters/xlsx.py,sha256=Gm8ns_KeqSMu2DONSSJ1DcwPBEjYwpbU7frmX0g5L7c,11487
|
|
48
50
|
execsql/exporters/xml.py,sha256=lqcOM8uKDoCayU42BPSLNH1_2DIHU5D3LtQItREU90c,2564
|
|
49
|
-
execsql/exporters/yaml.py,sha256=
|
|
51
|
+
execsql/exporters/yaml.py,sha256=1Vuc6uMDuLTkCuXCfXWKz4gLkkAVdEXkLs4gEB_67Xo,3110
|
|
50
52
|
execsql/exporters/zip.py,sha256=9-hExltQorONNThiMfxPDYHqHsbTeq9zM9zmtG4oFb8,4410
|
|
51
53
|
execsql/gui/__init__.py,sha256=oCb-cyhLZzVpWJ4WU5HbqEDBrV-lm0ytEwxemrOZyqs,2048
|
|
52
54
|
execsql/gui/base.py,sha256=sfNRkDrf7FhIgMRUOdyZpRLS1Xk9RqNhrV0A1RP6PXU,6068
|
|
@@ -59,14 +61,13 @@ execsql/importers/csv.py,sha256=Mu848WNzuhVO1ade-WurPyxqGOuVNRO8UwRF3-bav_I,4845
|
|
|
59
61
|
execsql/importers/feather.py,sha256=g2B69d2uv9vmnXcmjFyTVsMP40LYEzFYkhk3gD26mGw,1900
|
|
60
62
|
execsql/importers/ods.py,sha256=MJsdsjropzCvxAA3DDZfAL_AnmZ4yij7DnrjGyDJqHQ,2843
|
|
61
63
|
execsql/importers/xls.py,sha256=e0Zfe47ZiCpA1Ae3XDJ1ko3sCiH3-8U6XLKi6NvD0jQ,3683
|
|
62
|
-
execsql/metacommands/__init__.py,sha256=
|
|
63
|
-
execsql/metacommands/conditions.py,sha256=
|
|
64
|
+
execsql/metacommands/__init__.py,sha256=EmYUZZq1oaubbSQ26-8F9jJI_JnOJ2R697NeossXF1Q,11202
|
|
65
|
+
execsql/metacommands/conditions.py,sha256=Fzrk83-pWbFOoKahYdQW7CZjQeh3zByDUbfgpTM_bjQ,29259
|
|
64
66
|
execsql/metacommands/connect.py,sha256=Nsm0D91i3RX-R2rzQQ-Br-gULaI6Uvdn9fqb7DOAVfE,14804
|
|
65
|
-
execsql/metacommands/control.py,sha256=
|
|
67
|
+
execsql/metacommands/control.py,sha256=xNHyTrYUM042OgDarNq7HxslY7JuQs-KOKcb-fHUngM,8510
|
|
66
68
|
execsql/metacommands/data.py,sha256=tRQBGTAuW-eJ2tBNWaoZI9OjTyNNyHJISo7gOdL-sm8,11370
|
|
67
|
-
execsql/metacommands/debug.py,sha256=
|
|
68
|
-
execsql/metacommands/
|
|
69
|
-
execsql/metacommands/dispatch.py,sha256=1Mae6yqrea6wViFLBsvVt33Zgx4xP8tnhOuB_aQC89c,84054
|
|
69
|
+
execsql/metacommands/debug.py,sha256=pnT24dfvfOx8xFu86mO5czfVCGKbcvgBLyXnqaMWO4w,8184
|
|
70
|
+
execsql/metacommands/dispatch.py,sha256=OQwLOo9XT3N9C96wsRt0zmu1Nn4HL-7cSBOsGCfp5V4,84041
|
|
70
71
|
execsql/metacommands/io.py,sha256=Duh60caM4go9JczbGYNMKKYpcMimwPzF6EQ_tshKxdE,2971
|
|
71
72
|
execsql/metacommands/io_export.py,sha256=7lkCSnPhXy9FVau9_hT1u68NOVdG2DsWmvUh9hM1QWI,18359
|
|
72
73
|
execsql/metacommands/io_fileops.py,sha256=RKqbWPTYiwiqCZYG-lpih0w1JVOY4RBFdWr3BJb_pnY,9669
|
|
@@ -77,7 +78,7 @@ execsql/metacommands/script_ext.py,sha256=TUgAldB2LSJAwZrCvDDi804hQ1d9BDQD2GDqHN
|
|
|
77
78
|
execsql/metacommands/system.py,sha256=sUR5kLL7idTVg8WXIMdd-Kv7nkERIiaeL0beWsz8NyY,7293
|
|
78
79
|
execsql/script/__init__.py,sha256=pIo0EJ7-vg67rSMbOvbri_BOUgLoGoSEUfJgxUN7ZS0,3380
|
|
79
80
|
execsql/script/control.py,sha256=s-1eZdGARM6H1FwZ6VDdO_f50j7bvvRtTHesfUm9tbc,6144
|
|
80
|
-
execsql/script/engine.py,sha256=
|
|
81
|
+
execsql/script/engine.py,sha256=NJk4Is7Lp2Ov6zO8NNOOU8ACfmIFs2dD7PdhvJn6nNQ,41083
|
|
81
82
|
execsql/script/variables.py,sha256=MOT9XEHucpuuuHQZM5bklxGMBQcwHzwTBxd0q3aO0XY,11641
|
|
82
83
|
execsql/utils/__init__.py,sha256=0uR6JwVJQRX3vceByNBduCAf5dd5assKjeqJUWvpZoA,278
|
|
83
84
|
execsql/utils/auth.py,sha256=onXzNkNZQZxGC5w7eey06sjvAIAX_Lf9g7nUJtcsel0,7009
|
|
@@ -85,30 +86,30 @@ execsql/utils/crypto.py,sha256=2OnBWwn9bCBGc1ZkyRv16TvhottoCNYtXqgbE3mG3Sg,2960
|
|
|
85
86
|
execsql/utils/datetime.py,sha256=V_itd5vVvUPjT86P_z_hh4mlerMDGhDzI5MwPMDBaI4,7715
|
|
86
87
|
execsql/utils/errors.py,sha256=YKhYD27-3timuZavc2vIrRIfHa71vzih-KVPsAKgvkU,8163
|
|
87
88
|
execsql/utils/fileio.py,sha256=F6M4osE0Mb2ycTcvwwcYnhBXH1L36v6d7Oxdab6J16s,24110
|
|
88
|
-
execsql/utils/gui.py,sha256=
|
|
89
|
+
execsql/utils/gui.py,sha256=UFtwrXPNqNvZCJFCbumZ1aG2d9B-vyaJXIG83fDHteo,18409
|
|
89
90
|
execsql/utils/mail.py,sha256=75A1cMnEfyP0_QMMWuSLv8hrcIjc9cGBCrttLpr2TXQ,5372
|
|
90
91
|
execsql/utils/numeric.py,sha256=xh02ANSRk3nUpQ-rtm66ILoMqoi7HtzCoRMIOT9U8QI,1570
|
|
91
92
|
execsql/utils/regex.py,sha256=diEzTZqU_HHwVMadPAvN1Vgzhl7I03eVaEFGCXyGGL8,3770
|
|
92
93
|
execsql/utils/strings.py,sha256=5Dvzrk-9SIw2lpxXZQkiJbNyo1sy7iXXAtSULlZ0KG8,8488
|
|
93
94
|
execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
|
|
94
|
-
execsql2-2.
|
|
95
|
-
execsql2-2.
|
|
96
|
-
execsql2-2.
|
|
97
|
-
execsql2-2.
|
|
98
|
-
execsql2-2.
|
|
99
|
-
execsql2-2.
|
|
100
|
-
execsql2-2.
|
|
101
|
-
execsql2-2.
|
|
102
|
-
execsql2-2.
|
|
103
|
-
execsql2-2.
|
|
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.
|
|
95
|
+
execsql2-2.12.0.data/data/execsql2_extras/README.md,sha256=sxwVyU0ZahCfANv56LahkyuM505kFjrMhe-1SvWE69E,4845
|
|
96
|
+
execsql2-2.12.0.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
|
|
97
|
+
execsql2-2.12.0.data/data/execsql2_extras/example_config_prompt.sql,sha256=SY3Jxn1qcVm4kPW9xmmTfknHfvURXmeEYTbRjYrjGSw,7487
|
|
98
|
+
execsql2-2.12.0.data/data/execsql2_extras/execsql.conf,sha256=_45iJ-KWZnB8uMW_gEg067MM5pmGJ-dVl7VbAZMunAE,9530
|
|
99
|
+
execsql2-2.12.0.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
|
|
100
|
+
execsql2-2.12.0.data/data/execsql2_extras/md_compare.sql,sha256=B8Wd7LZ0vnMY2qvA139JIEBkPObgRH2i5xj6PejTQt8,24092
|
|
101
|
+
execsql2-2.12.0.data/data/execsql2_extras/md_glossary.sql,sha256=DJRHcU5NbFpxTTX-IwH3yRlsboj1q6BBGrUAHKn4Cuo,10796
|
|
102
|
+
execsql2-2.12.0.data/data/execsql2_extras/md_upsert.sql,sha256=v_7GbWh_N1mBTmw3gvTrkagOVp2q0KmXvM8hE-DlFxY,112524
|
|
103
|
+
execsql2-2.12.0.data/data/execsql2_extras/pg_compare.sql,sha256=9dWa8hnfy5dVJI-z2iGpd9JzQmI4j2ziMlEdpnr66ro,24352
|
|
104
|
+
execsql2-2.12.0.data/data/execsql2_extras/pg_glossary.sql,sha256=pKjIIDsROAgJq2H-1qNEcRMAWManivcZ_AEVHzUUlic,9908
|
|
105
|
+
execsql2-2.12.0.data/data/execsql2_extras/pg_upsert.sql,sha256=k7AFiGTLBy3nf-qO5QIaZrEYTAKvdxxU3JDLx9jqkzs,108315
|
|
106
|
+
execsql2-2.12.0.data/data/execsql2_extras/script_template.sql,sha256=1Estacb_vm1FgK41k_G9nuduP1yiA-fQ1Kn4Z4mv5Ao,11153
|
|
107
|
+
execsql2-2.12.0.data/data/execsql2_extras/ss_compare.sql,sha256=TsVxWm3cEpR5-EiMYXNhtaY0arSNeKZhsJdHdLA7xeI,24833
|
|
108
|
+
execsql2-2.12.0.data/data/execsql2_extras/ss_glossary.sql,sha256=cLM7nN8JOIu9ZVP9oY9qdSK3hrnWJiDcX6nZmQQbQWI,13065
|
|
109
|
+
execsql2-2.12.0.data/data/execsql2_extras/ss_upsert.sql,sha256=BCqmBykXBF-BpCgOFeG1qhf2XfScKsxPD17wd1hYfHw,120647
|
|
110
|
+
execsql2-2.12.0.dist-info/METADATA,sha256=l6mKQU4tPA9vkX6ZlUWNFRbou5rw5KPLML8V3urrV10,17381
|
|
111
|
+
execsql2-2.12.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
112
|
+
execsql2-2.12.0.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
|
|
113
|
+
execsql2-2.12.0.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
|
|
114
|
+
execsql2-2.12.0.dist-info/licenses/NOTICE,sha256=sqVrM73Ys9zfvWC_P797lHfTnoPW_ETeBSrUTFaob0A,339
|
|
115
|
+
execsql2-2.12.0.dist-info/RECORD,,
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
"""
|
|
4
|
-
Interactive debug REPL metacommand handler for execsql.
|
|
5
|
-
|
|
6
|
-
Implements ``x_breakpoint`` — the ``BREAKPOINT`` metacommand — which pauses
|
|
7
|
-
script execution and drops into an interactive read-eval-print loop.
|
|
8
|
-
|
|
9
|
-
The REPL allows the user to:
|
|
10
|
-
|
|
11
|
-
- Inspect and print substitution variables.
|
|
12
|
-
- Run ad-hoc SQL queries against the current database.
|
|
13
|
-
- Step through the script one statement at a time.
|
|
14
|
-
- Resume or abort execution.
|
|
15
|
-
|
|
16
|
-
All REPL commands are dot-prefixed (``.continue``, ``.vars``, ``.next``)
|
|
17
|
-
to avoid ambiguity with variable names and SQL. Anything not starting
|
|
18
|
-
with ``.`` is treated as either a variable lookup (if it matches a known
|
|
19
|
-
variable) or SQL (if it ends with ``;``).
|
|
20
|
-
|
|
21
|
-
In non-interactive environments (CI, piped input, ``sys.stdin.isatty()`` is
|
|
22
|
-
``False``) the metacommand is silently skipped so automated pipelines are not
|
|
23
|
-
blocked.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
import sys
|
|
27
|
-
from typing import Any
|
|
28
|
-
|
|
29
|
-
import execsql.state as _state
|
|
30
|
-
|
|
31
|
-
__all__ = ["x_breakpoint"]
|
|
32
|
-
|
|
33
|
-
# ---------------------------------------------------------------------------
|
|
34
|
-
# Public handler
|
|
35
|
-
# ---------------------------------------------------------------------------
|
|
36
|
-
|
|
37
|
-
_HELP_TEXT = """\
|
|
38
|
-
execsql debug REPL — all commands start with '.'
|
|
39
|
-
|
|
40
|
-
.continue .c Resume script execution
|
|
41
|
-
.abort .q Halt the script (exit 1)
|
|
42
|
-
.vars List user, system, local, and counter variables
|
|
43
|
-
.vars all Include environment variables (&) in the listing
|
|
44
|
-
.next .n Execute the next statement then pause again (step mode)
|
|
45
|
-
.stack Show the command-list stack (script name, line, depth)
|
|
46
|
-
.help Show this help text
|
|
47
|
-
|
|
48
|
-
Everything else:
|
|
49
|
-
varname Print a variable's value (e.g. logfile, $ARG_1, &HOME)
|
|
50
|
-
SELECT ...; Run ad-hoc SQL against the current database
|
|
51
|
-
"""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def x_breakpoint(**kwargs: Any) -> None:
|
|
55
|
-
"""Pause execution and enter the interactive debug REPL.
|
|
56
|
-
|
|
57
|
-
If ``sys.stdin`` is not a TTY (CI, piped input), the metacommand is
|
|
58
|
-
silently skipped — scripts will not hang in automation.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
**kwargs: Keyword arguments injected by the dispatch table (unused).
|
|
62
|
-
"""
|
|
63
|
-
if not sys.stdin.isatty():
|
|
64
|
-
return
|
|
65
|
-
_debug_repl()
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
# ---------------------------------------------------------------------------
|
|
69
|
-
# REPL core
|
|
70
|
-
# ---------------------------------------------------------------------------
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def _debug_repl() -> None:
|
|
74
|
-
"""Interactive read-eval-print loop for script debugging.
|
|
75
|
-
|
|
76
|
-
Reads commands from stdin until the user types ``.continue`` or ``.abort``,
|
|
77
|
-
or until EOF / KeyboardInterrupt.
|
|
78
|
-
"""
|
|
79
|
-
try:
|
|
80
|
-
import readline as _readline # noqa: F401 — side-effect: enables history/arrow keys
|
|
81
|
-
except ImportError:
|
|
82
|
-
pass # readline not available on Windows; continue without it
|
|
83
|
-
|
|
84
|
-
_write("\n[Breakpoint] Script paused. Type '.help' for commands, '.c' to resume.\n")
|
|
85
|
-
|
|
86
|
-
while True:
|
|
87
|
-
try:
|
|
88
|
-
line = input("execsql debug> ").strip()
|
|
89
|
-
except EOFError:
|
|
90
|
-
_write("\n")
|
|
91
|
-
return # Ctrl-D → continue
|
|
92
|
-
except KeyboardInterrupt:
|
|
93
|
-
_write("\n")
|
|
94
|
-
return # Ctrl-C → continue
|
|
95
|
-
|
|
96
|
-
if not line:
|
|
97
|
-
continue
|
|
98
|
-
|
|
99
|
-
# Dot-prefixed → REPL command
|
|
100
|
-
if line.startswith("."):
|
|
101
|
-
_handle_dot_command(line)
|
|
102
|
-
if line.lower().lstrip(".") in ("continue", "c"):
|
|
103
|
-
return
|
|
104
|
-
if line.lower().lstrip(".") in ("abort", "q", "quit"):
|
|
105
|
-
# _handle_dot_command already raised SystemExit, but guard anyway
|
|
106
|
-
return
|
|
107
|
-
if line.lower().lstrip(".") in ("next", "n"):
|
|
108
|
-
return
|
|
109
|
-
continue
|
|
110
|
-
|
|
111
|
-
# SQL (ends with semicolon)
|
|
112
|
-
if line.rstrip().endswith(";"):
|
|
113
|
-
_run_sql(line)
|
|
114
|
-
continue
|
|
115
|
-
|
|
116
|
-
# Everything else → variable lookup
|
|
117
|
-
_print_var(line)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def _handle_dot_command(line: str) -> None:
|
|
121
|
-
"""Dispatch a dot-prefixed REPL command."""
|
|
122
|
-
# Strip the leading dot and normalize
|
|
123
|
-
cmd = line[1:].strip().lower()
|
|
124
|
-
|
|
125
|
-
if cmd in ("continue", "c"):
|
|
126
|
-
return # caller checks and returns from _debug_repl
|
|
127
|
-
elif cmd in ("abort", "q", "quit"):
|
|
128
|
-
raise SystemExit(1)
|
|
129
|
-
elif cmd == "help":
|
|
130
|
-
_write(_HELP_TEXT)
|
|
131
|
-
elif cmd == "vars all":
|
|
132
|
-
_print_all_vars(include_env=True)
|
|
133
|
-
elif cmd == "vars":
|
|
134
|
-
_print_all_vars()
|
|
135
|
-
elif cmd == "stack":
|
|
136
|
-
_print_stack()
|
|
137
|
-
elif cmd in ("next", "n"):
|
|
138
|
-
_enable_step_mode()
|
|
139
|
-
else:
|
|
140
|
-
_write(f" Unknown command: {line!r}. Type '.help' for available commands.\n")
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# ---------------------------------------------------------------------------
|
|
144
|
-
# REPL command implementations
|
|
145
|
-
# ---------------------------------------------------------------------------
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def _write(text: str) -> None:
|
|
149
|
-
"""Write *text* to the execsql output stream (falls back to stdout)."""
|
|
150
|
-
output = _state.output
|
|
151
|
-
if output is not None:
|
|
152
|
-
output.write(text)
|
|
153
|
-
else:
|
|
154
|
-
sys.stdout.write(text)
|
|
155
|
-
sys.stdout.flush()
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def _print_all_vars(*, include_env: bool = False) -> None:
|
|
159
|
-
"""Print substitution variables grouped by type."""
|
|
160
|
-
subvars = _state.subvars
|
|
161
|
-
if subvars is None:
|
|
162
|
-
_write(" (no substitution variables defined)\n")
|
|
163
|
-
return
|
|
164
|
-
items = subvars.substitutions # list of (name, value) tuples
|
|
165
|
-
if not items:
|
|
166
|
-
_write(" (no substitution variables defined)\n")
|
|
167
|
-
return
|
|
168
|
-
|
|
169
|
-
# Group by prefix.
|
|
170
|
-
user_vars: list[tuple[str, str]] = []
|
|
171
|
-
system_vars: list[tuple[str, str]] = []
|
|
172
|
-
counter_vars: list[tuple[str, str]] = []
|
|
173
|
-
local_vars: list[tuple[str, str]] = []
|
|
174
|
-
env_vars: list[tuple[str, str]] = []
|
|
175
|
-
|
|
176
|
-
for name, value in sorted(items):
|
|
177
|
-
if name.startswith("&"):
|
|
178
|
-
env_vars.append((name, value))
|
|
179
|
-
elif name.startswith("~"):
|
|
180
|
-
local_vars.append((name, value))
|
|
181
|
-
elif name.startswith("@"):
|
|
182
|
-
counter_vars.append((name, value))
|
|
183
|
-
elif name.startswith("$"):
|
|
184
|
-
system_vars.append((name, value))
|
|
185
|
-
else:
|
|
186
|
-
user_vars.append((name, value))
|
|
187
|
-
|
|
188
|
-
def _print_group(label: str, group: list[tuple[str, str]]) -> None:
|
|
189
|
-
if not group:
|
|
190
|
-
return
|
|
191
|
-
_write(f" {label}:\n")
|
|
192
|
-
max_name = max(len(n) for n, _ in group)
|
|
193
|
-
for name, value in group:
|
|
194
|
-
_write(f" {name:<{max_name}} = {value}\n")
|
|
195
|
-
|
|
196
|
-
_print_group("User variables", user_vars)
|
|
197
|
-
_print_group("System variables ($)", system_vars)
|
|
198
|
-
_print_group("Local variables (~)", local_vars)
|
|
199
|
-
_print_group("Counter variables (@)", counter_vars)
|
|
200
|
-
if include_env:
|
|
201
|
-
_print_group("Environment variables (&)", env_vars)
|
|
202
|
-
|
|
203
|
-
if not any([user_vars, system_vars, local_vars, counter_vars]):
|
|
204
|
-
if env_vars:
|
|
205
|
-
_write(" (no script variables defined — use '.vars all' to see environment variables)\n")
|
|
206
|
-
else:
|
|
207
|
-
_write(" (no variables defined)\n")
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
def _print_var(varname: str) -> None:
|
|
211
|
-
"""Print the value of a single substitution variable.
|
|
212
|
-
|
|
213
|
-
Tries the name as typed, then with the sigil prefix stripped.
|
|
214
|
-
"""
|
|
215
|
-
subvars = _state.subvars
|
|
216
|
-
if subvars is None:
|
|
217
|
-
_write(f" {varname}: (substitution variables not initialised)\n")
|
|
218
|
-
return
|
|
219
|
-
# Try the name as typed first, then without the sigil prefix ($, &, @, #, ~).
|
|
220
|
-
# SUB creates variables without a prefix (e.g., "logfile"), but users
|
|
221
|
-
# may type "$logfile" at the prompt.
|
|
222
|
-
value = subvars.varvalue(varname)
|
|
223
|
-
if value is None and len(varname) > 1 and varname[0] in "$&@#~":
|
|
224
|
-
value = subvars.varvalue(varname[1:])
|
|
225
|
-
if value is None:
|
|
226
|
-
_write(f" {varname}: (undefined)\n")
|
|
227
|
-
else:
|
|
228
|
-
_write(f" {varname} = {value}\n")
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
def _print_stack() -> None:
|
|
232
|
-
"""Print the current command-list stack (script name, line number, depth)."""
|
|
233
|
-
stack = _state.commandliststack
|
|
234
|
-
if not stack:
|
|
235
|
-
_write(" (command list stack is empty)\n")
|
|
236
|
-
return
|
|
237
|
-
_write(f" Stack depth: {len(stack)}\n")
|
|
238
|
-
for depth, cmdlist in enumerate(stack):
|
|
239
|
-
listname = getattr(cmdlist, "listname", "<unknown>")
|
|
240
|
-
cmdptr = getattr(cmdlist, "cmdptr", 0)
|
|
241
|
-
_write(f" [{depth}] {listname} (cursor at index {cmdptr})\n")
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def _run_sql(sql: str) -> None:
|
|
245
|
-
"""Execute ad-hoc SQL against the current database and pretty-print the results."""
|
|
246
|
-
dbs = _state.dbs
|
|
247
|
-
if dbs is None:
|
|
248
|
-
_write(" (no database connection is active)\n")
|
|
249
|
-
return
|
|
250
|
-
db = dbs.current()
|
|
251
|
-
if db is None:
|
|
252
|
-
_write(" (no database connection is active)\n")
|
|
253
|
-
return
|
|
254
|
-
try:
|
|
255
|
-
colnames, rows = db.select_data(sql)
|
|
256
|
-
except Exception as exc:
|
|
257
|
-
_write(f" SQL error: {exc}\n")
|
|
258
|
-
return
|
|
259
|
-
|
|
260
|
-
if not colnames:
|
|
261
|
-
_write(" (query returned no columns)\n")
|
|
262
|
-
return
|
|
263
|
-
|
|
264
|
-
# Build a simple text table.
|
|
265
|
-
col_widths = [len(c) for c in colnames]
|
|
266
|
-
str_rows: list[list[str]] = []
|
|
267
|
-
for row in rows:
|
|
268
|
-
str_row = [str(v) if v is not None else "NULL" for v in row]
|
|
269
|
-
str_rows.append(str_row)
|
|
270
|
-
for i, cell in enumerate(str_row):
|
|
271
|
-
col_widths[i] = max(col_widths[i], len(cell))
|
|
272
|
-
|
|
273
|
-
sep = "+-" + "-+-".join("-" * w for w in col_widths) + "-+"
|
|
274
|
-
header = "| " + " | ".join(c.ljust(col_widths[i]) for i, c in enumerate(colnames)) + " |"
|
|
275
|
-
_write(sep + "\n")
|
|
276
|
-
_write(header + "\n")
|
|
277
|
-
_write(sep + "\n")
|
|
278
|
-
for str_row in str_rows:
|
|
279
|
-
data_line = "| " + " | ".join(cell.ljust(col_widths[i]) for i, cell in enumerate(str_row)) + " |"
|
|
280
|
-
_write(data_line + "\n")
|
|
281
|
-
_write(sep + "\n")
|
|
282
|
-
row_word = "row" if len(str_rows) == 1 else "rows"
|
|
283
|
-
_write(f" ({len(str_rows)} {row_word})\n")
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
def _enable_step_mode() -> None:
|
|
287
|
-
"""Activate step mode so the engine re-enters the REPL after the next statement."""
|
|
288
|
-
_state.step_mode = True
|
|
File without changes
|
|
File without changes
|
{execsql2-2.11.0.data → execsql2-2.12.0.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
|