execsql2 2.22.0__py3-none-any.whl → 2.22.2__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/api.py +8 -0
- execsql/cli/__init__.py +1 -1
- execsql/config.py +3 -0
- execsql/script/executor.py +2 -0
- execsql/utils/fileio.py +3 -1
- execsql/utils/gui.py +15 -6
- {execsql2-2.22.0.dist-info → execsql2-2.22.2.dist-info}/METADATA +114 -132
- {execsql2-2.22.0.dist-info → execsql2-2.22.2.dist-info}/RECORD +26 -26
- {execsql2-2.22.0.dist-info → execsql2-2.22.2.dist-info}/WHEEL +1 -1
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.22.0.data → execsql2-2.22.2.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.22.0.dist-info → execsql2-2.22.2.dist-info}/entry_points.txt +0 -0
- {execsql2-2.22.0.dist-info → execsql2-2.22.2.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.22.0.dist-info → execsql2-2.22.2.dist-info}/licenses/NOTICE +0 -0
execsql/api.py
CHANGED
|
@@ -528,6 +528,14 @@ def run(
|
|
|
528
528
|
except Exception as exc:
|
|
529
529
|
errors.append(ScriptError(message=str(exc), source="<runtime>"))
|
|
530
530
|
|
|
531
|
+
# Non-halting errors (halt_on_error=False, METACOMMAND_ERROR_HALT OFF)
|
|
532
|
+
# are recorded in status.error_history as execution continues; a
|
|
533
|
+
# halting error never reaches the history, so there is no overlap
|
|
534
|
+
# with the exception paths above.
|
|
535
|
+
if ctx.status is not None:
|
|
536
|
+
for err_source, err_line, err_cmd, err_msg in ctx.status.error_history:
|
|
537
|
+
errors.append(ScriptError(message=err_msg, source=err_source, line=err_line, sql=err_cmd))
|
|
538
|
+
|
|
531
539
|
elapsed = time.perf_counter() - t0
|
|
532
540
|
|
|
533
541
|
# ------------------------------------------------------------------
|
execsql/cli/__init__.py
CHANGED
|
@@ -292,7 +292,7 @@ def main(
|
|
|
292
292
|
"-a",
|
|
293
293
|
"--assign-arg",
|
|
294
294
|
metavar="VALUE",
|
|
295
|
-
help="Define the replacement string for a substitution variable [cyan]
|
|
295
|
+
help="Define the replacement string for a substitution variable [cyan]$ARG_x[/cyan].",
|
|
296
296
|
),
|
|
297
297
|
user_logfile: bool = typer.Option(
|
|
298
298
|
False,
|
execsql/config.py
CHANGED
|
@@ -56,6 +56,9 @@ class StatObj:
|
|
|
56
56
|
self.metacommand_error = False
|
|
57
57
|
self.cancel_halt = True
|
|
58
58
|
self.dialog_canceled = False
|
|
59
|
+
# Cumulative record of non-halting errors: (source, line_no, command, message).
|
|
60
|
+
# Unlike sql_error/metacommand_error, this is never reset between statements.
|
|
61
|
+
self.error_history: list[tuple[str, int | None, str, str]] = []
|
|
59
62
|
# BatchLevels is defined in the main module; defer import to avoid circular deps
|
|
60
63
|
from execsql.script import BatchLevels
|
|
61
64
|
|
execsql/script/executor.py
CHANGED
|
@@ -278,6 +278,7 @@ def _exec_sql(
|
|
|
278
278
|
ctx.exec_log.log_status_info(f"SQL error: {e.errmsg()}")
|
|
279
279
|
if status.halt_on_err:
|
|
280
280
|
exit_now(1, e)
|
|
281
|
+
status.error_history.append((source, line_no, cmd, e.errmsg()))
|
|
281
282
|
return
|
|
282
283
|
subvars.add_substitution("$LAST_SQL", cmd)
|
|
283
284
|
|
|
@@ -334,6 +335,7 @@ def _exec_metacommand(
|
|
|
334
335
|
ctx.exec_log.log_status_info(f"Metacommand error: {e.errmsg()}")
|
|
335
336
|
if status.halt_on_metacommand_err:
|
|
336
337
|
raise e
|
|
338
|
+
status.error_history.append((source, line_no, cmd, e.errmsg()))
|
|
337
339
|
return None
|
|
338
340
|
# No handler matched — truly unknown metacommand
|
|
339
341
|
status.metacommand_error = True
|
execsql/utils/fileio.py
CHANGED
|
@@ -78,7 +78,9 @@ def check_dir(filename: str) -> None:
|
|
|
78
78
|
import execsql.state as _state
|
|
79
79
|
|
|
80
80
|
conf = _state.conf
|
|
81
|
-
|
|
81
|
+
# conf is None when called via the library API (e.g. a db factory
|
|
82
|
+
# function) before run() has initialized global state.
|
|
83
|
+
if conf is not None and conf.make_export_dirs:
|
|
82
84
|
make_export_dirs(filename)
|
|
83
85
|
else:
|
|
84
86
|
dn = str(Path(filename).parent)
|
execsql/utils/gui.py
CHANGED
|
@@ -540,18 +540,27 @@ def gui_credentials(
|
|
|
540
540
|
# ---------------------------------------------------------------------------
|
|
541
541
|
|
|
542
542
|
|
|
543
|
-
def get_yn(prompt: str) ->
|
|
544
|
-
"""Prompt for a yes/no answer on the terminal.
|
|
543
|
+
def get_yn(prompt: str) -> str:
|
|
544
|
+
"""Prompt for a yes/no answer on the terminal.
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
``"y"`` or ``"n"``, or ``chr(27)`` (ESC) if the user aborts via EOF
|
|
548
|
+
(Ctrl-D / piped stdin closed). Matches the upstream v1.130.1
|
|
549
|
+
``get_yn`` character contract expected by callers such as ``x_ask``.
|
|
550
|
+
"""
|
|
545
551
|
while True:
|
|
546
|
-
|
|
552
|
+
try:
|
|
553
|
+
answer = input(f"{prompt} [y/n]: ").strip().lower()
|
|
554
|
+
except EOFError:
|
|
555
|
+
return chr(27)
|
|
547
556
|
if answer in ("y", "yes"):
|
|
548
|
-
return
|
|
557
|
+
return "y"
|
|
549
558
|
if answer in ("n", "no"):
|
|
550
|
-
return
|
|
559
|
+
return "n"
|
|
551
560
|
print("Please enter y or n.", file=sys.stderr)
|
|
552
561
|
|
|
553
562
|
|
|
554
|
-
def get_yn_win(prompt: str) ->
|
|
563
|
+
def get_yn_win(prompt: str) -> str:
|
|
555
564
|
"""GUI yes/no dialog — falls back to terminal in headless mode."""
|
|
556
565
|
return get_yn(prompt)
|
|
557
566
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.22.
|
|
3
|
+
Version: 2.22.2
|
|
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
|
|
@@ -130,10 +130,7 @@ Requires-Dist: pg-upsert>=1.23.0; extra == 'upsert'
|
|
|
130
130
|
Description-Content-Type: text/markdown
|
|
131
131
|
|
|
132
132
|
> [!NOTE]
|
|
133
|
-
>
|
|
134
|
-
> The original monolith has been fully refactored into a modular package.
|
|
135
|
-
> The CLI and configuration are backwards-compatible with upstream v1.130.1.
|
|
136
|
-
> Report issues at [github.com/geocoug/execsql/issues](https://github.com/geocoug/execsql/issues).
|
|
133
|
+
> ***execsql2* is a maintained fork of [execsql](https://execsql.readthedocs.io/)**, originally authored by R.Dreas Nielsen and no longer actively maintained upstream. The monolith has been fully refactored into a modular package; the CLI and configuration remain backwards-compatible with upstream v1.130.1. Maintained by [Caleb Grant](https://github.com/geocoug) and distributed on PyPI as [`execsql2`](https://pypi.org/project/execsql2/). Report issues at [github.com/geocoug/execsql/issues](https://github.com/geocoug/execsql/issues).
|
|
137
134
|
|
|
138
135
|
<div align="center">
|
|
139
136
|
|
|
@@ -155,48 +152,59 @@ Description-Content-Type: text/markdown
|
|
|
155
152
|
|
|
156
153
|
</div>
|
|
157
154
|
|
|
158
|
-
|
|
155
|
+
## Overview
|
|
159
156
|
|
|
160
|
-
*
|
|
157
|
+
*execsql* runs SQL scripts against PostgreSQL, MySQL/MariaDB, SQLite, DuckDB, MS-SQL-Server, MS-Access, Firebird, Oracle, or an ODBC DSN. In addition to standard SQL, it supports a set of metacommands (embedded in SQL comments) for importing and exporting data, copying data between databases, conditional execution, looping, substitution variables, and interactive prompts. Because metacommands live in SQL comments, scripts remain valid SQL and are ignored by other tools such as `psql` or `sqlcmd`.
|
|
161
158
|
|
|
162
|
-
|
|
159
|
+
## Example
|
|
163
160
|
|
|
164
|
-
|
|
161
|
+
Lines prefixed with `-- !x!` are metacommands; identifiers wrapped in `!!` are substitution variables:
|
|
165
162
|
|
|
166
|
-
|
|
163
|
+
```sql
|
|
164
|
+
-- ==== Configuration ====
|
|
165
|
+
-- Put the (date-tagged) logfile name in the 'inputfile' substitution variable.
|
|
166
|
+
-- !x! SUB inputfile logs/errors_!!$date_tag!!
|
|
167
|
+
-- Ensure that the export directory will be created if necessary.
|
|
168
|
+
-- !x! CONFIG MAKE_EXPORT_DIRS Yes
|
|
167
169
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
+
-- ==== Display Fatal Errors ====
|
|
171
|
+
-- !x! IF(file_exists(!!inputfile!!))
|
|
172
|
+
-- Import the data to a staging table.
|
|
173
|
+
-- !x! IMPORT TO REPLACEMENT staging.errorlog FROM !!inputfile!!
|
|
174
|
+
-- Create a view to display only fatal errors.
|
|
175
|
+
create temporary view fatals as
|
|
176
|
+
select user, run_time, process
|
|
177
|
+
from staging.errorlog
|
|
178
|
+
where severity = 'FATAL';
|
|
179
|
+
-- !x! IF(HASROWS(fatals))
|
|
180
|
+
-- Export the fatal errors to a dated report.
|
|
181
|
+
-- !x! EXPORT fatals TO reports/error_report_!!$date_tag!! AS CSV
|
|
182
|
+
-- Also display it to the user in a GUI.
|
|
183
|
+
-- !x! PROMPT MESSAGE "Fatal errors in !!inputfile!!:" DISPLAY fatals
|
|
184
|
+
-- !x! ELSE
|
|
185
|
+
-- !x! WRITE "There are no fatal errors."
|
|
186
|
+
-- !x! ENDIF
|
|
187
|
+
-- !x! ELSE
|
|
188
|
+
-- !x! WRITE "There is no error log."
|
|
189
|
+
-- !x! ENDIF
|
|
190
|
+
drop table if exists staging.errorlog cascade;
|
|
170
191
|
```
|
|
171
192
|
|
|
172
|
-
|
|
193
|
+
The `PROMPT` metacommand produces a GUI display of the data:
|
|
194
|
+
|
|
195
|
+

|
|
196
|
+
|
|
197
|
+
## Installation
|
|
173
198
|
|
|
174
199
|
```bash
|
|
175
|
-
#
|
|
176
|
-
pip install execsql2[postgres] #
|
|
177
|
-
pip install execsql2[
|
|
178
|
-
pip install execsql2[mssql] # SQL Server (pyodbc)
|
|
179
|
-
pip install execsql2[duckdb] # DuckDB
|
|
180
|
-
pip install execsql2[firebird] # Firebird (firebird-driver)
|
|
181
|
-
pip install execsql2[oracle] # Oracle (oracledb)
|
|
182
|
-
pip install execsql2[odbc] # ODBC DSN (pyodbc)
|
|
183
|
-
|
|
184
|
-
# Feature bundles
|
|
185
|
-
pip install execsql2[formats] # ODS, Excel, Jinja2, Feather, Parquet, HDF5
|
|
186
|
-
pip install execsql2[upsert] # pg-upsert for PostgreSQL upsert operations
|
|
187
|
-
pip install execsql2[auth] # OS keyring integration
|
|
188
|
-
pip install execsql2[auth-plaintext] # Keyring + plaintext file backend (headless Linux)
|
|
189
|
-
pip install execsql2[auth-encrypted] # Keyring + encrypted file backend (headless Linux)
|
|
190
|
-
pip install execsql2[map] # tkintermapview for PROMPT MAP
|
|
191
|
-
|
|
192
|
-
# Convenience
|
|
193
|
-
pip install execsql2[all-db] # All database drivers
|
|
194
|
-
pip install execsql2[all] # Everything
|
|
200
|
+
pip install execsql2 # core — SQLite works with no extras
|
|
201
|
+
pip install "execsql2[postgres]" # add a driver: postgres, mysql, mssql, duckdb, firebird, oracle, odbc
|
|
202
|
+
pip install "execsql2[all]" # everything: all drivers plus all feature extras
|
|
195
203
|
```
|
|
196
204
|
|
|
197
|
-
|
|
205
|
+
Feature extras cover spreadsheet and Parquet/Feather formats, keyring authentication, PostgreSQL upsert, and more — see the [installation guide](https://execsql2.readthedocs.io/en/latest/getting-started/installation/) for the full list.
|
|
198
206
|
|
|
199
|
-
|
|
207
|
+
## Usage
|
|
200
208
|
|
|
201
209
|
```text
|
|
202
210
|
execsql [OPTIONS] SQL_SCRIPT [SERVER DATABASE | DATABASE_FILE]
|
|
@@ -214,7 +222,7 @@ execsql -to script.sql myserver myservice # Oracle
|
|
|
214
222
|
execsql script.sql # read connection from config file
|
|
215
223
|
```
|
|
216
224
|
|
|
217
|
-
|
|
225
|
+
### Supported Databases
|
|
218
226
|
|
|
219
227
|
| Flag | Database |
|
|
220
228
|
| ---- | --------------- |
|
|
@@ -228,67 +236,53 @@ execsql script.sql # read connection from config file
|
|
|
228
236
|
| `o` | Oracle |
|
|
229
237
|
| `d` | ODBC DSN |
|
|
230
238
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
| Flag
|
|
234
|
-
|
|
|
235
|
-
| `-t {p,m,s,l,k,a,f,o,d}`
|
|
236
|
-
| `-u USER`
|
|
237
|
-
| `-p PORT`
|
|
238
|
-
|
|
|
239
|
-
| `-
|
|
240
|
-
| `-c SCRIPT`
|
|
241
|
-
| `-
|
|
242
|
-
| `-
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
247
|
-
|
|
|
248
|
-
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
|
|
256
|
-
| `--output-dir DIR` | Default base directory for EXPORT output files |
|
|
257
|
-
| `--dry-run` | Parse the script and report commands without executing |
|
|
258
|
-
| `--lint` | Static analysis: check structure and warn on issues (no DB) |
|
|
259
|
-
| `--parse-tree` | Print the script's AST structure and exit (no DB) |
|
|
260
|
-
| `--list-plugins` | List discovered plugins and exit |
|
|
261
|
-
| `--ping` | Test database connectivity and exit |
|
|
262
|
-
| `--profile` | Show per-statement timing summary after execution |
|
|
263
|
-
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20) |
|
|
264
|
-
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
265
|
-
| `--config FILE` | Load an explicit config file (highest priority after CLI args) |
|
|
266
|
-
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs) |
|
|
267
|
-
| `--no-rm-file` | Disable the `RM_FILE` metacommand (no script-driven file deletion) |
|
|
268
|
-
| `--no-serve` | Disable the `SERVE` metacommand (no script-driven file streaming) |
|
|
269
|
-
| `--init-config` | Print a default `execsql.conf` template to stdout and exit |
|
|
270
|
-
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
271
|
-
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
272
|
-
| `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
|
|
273
|
-
|
|
274
|
-
Run `execsql --help` for the full option list, or `execsql -m` to list all metacommands.
|
|
275
|
-
|
|
276
|
-
# Features
|
|
239
|
+
### Common options
|
|
240
|
+
|
|
241
|
+
| Flag | Description |
|
|
242
|
+
| ------------------------------------------------- | --------------------------------------------------------------- |
|
|
243
|
+
| `-t {p,m,s,l,k,a,f,o,d}` | Database type |
|
|
244
|
+
| `-u USER` | Database username |
|
|
245
|
+
| `-p PORT` | Server port |
|
|
246
|
+
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
|
|
247
|
+
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
|
|
248
|
+
| `-c SCRIPT` | Execute inline SQL or metacommand string |
|
|
249
|
+
| `-a VALUE` | Set substitution variable `$ARG_x` |
|
|
250
|
+
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
|
|
251
|
+
| `--config FILE` | Load an explicit config file |
|
|
252
|
+
| `--dry-run` | Parse the script and report commands without executing |
|
|
253
|
+
| `--lint` | Static analysis: check structure and warn on issues (no DB) |
|
|
254
|
+
| `--ping` | Test database connectivity and exit |
|
|
255
|
+
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
256
|
+
| `--no-system-cmd` / `--no-rm-file` / `--no-serve` | Disable the `SYSTEM_CMD` / `RM_FILE` / `SERVE` metacommands |
|
|
257
|
+
|
|
258
|
+
See the [full options reference](https://execsql2.readthedocs.io/en/latest/getting-started/syntax/#options) or run `execsql --help` for the complete list, and `execsql -m` for all metacommands.
|
|
259
|
+
|
|
260
|
+
## Features
|
|
261
|
+
|
|
262
|
+
**Data movement**
|
|
277
263
|
|
|
278
264
|
- Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
|
|
279
265
|
- Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
280
266
|
- Copy data between databases, including across different DBMS types.
|
|
267
|
+
|
|
268
|
+
**Script logic**
|
|
269
|
+
|
|
281
270
|
- Conditionally execute SQL and metacommands using `IF`/`ELSE`/`ENDIF` based on data values, DBMS type, or user input.
|
|
282
|
-
-
|
|
283
|
-
- Loop over blocks of SQL and metacommands using `LOOP`/`ENDLOOP`.
|
|
271
|
+
- Loop over blocks of SQL and metacommands using `LOOP`/`ENDLOOP`; include or chain scripts with `INCLUDE` and `SCRIPT`.
|
|
284
272
|
- Use substitution variables (`SUB`, `$ARG_x`, built-in variables like `$date_tag`) to parameterize scripts.
|
|
285
|
-
-
|
|
273
|
+
- Validate data with `ASSERT` — halt the script with a clear error message if a condition is false (ideal for CI pipelines).
|
|
274
|
+
|
|
275
|
+
**Interaction & observability**
|
|
276
|
+
|
|
286
277
|
- Display query results in a GUI dialog; optionally prompt the user to select a row, enter a value, or submit a form.
|
|
287
278
|
- Write status messages or tabular output to the console or a file during execution.
|
|
288
279
|
- Automatically log each run, recording databases used, scripts executed, and user responses.
|
|
280
|
+
|
|
281
|
+
**Extensibility**
|
|
282
|
+
|
|
289
283
|
- Extend with custom metacommands, exporters, and importers via the plugin system.
|
|
290
284
|
|
|
291
|
-
|
|
285
|
+
## Library API
|
|
292
286
|
|
|
293
287
|
execsql can be used as a Python library for programmatic script execution:
|
|
294
288
|
|
|
@@ -331,56 +325,43 @@ if not result.success:
|
|
|
331
325
|
result.raise_on_error() # raises ExecSqlError
|
|
332
326
|
```
|
|
333
327
|
|
|
334
|
-
Use a pre-existing database connection instead of a DSN:
|
|
328
|
+
Use a pre-existing database connection instead of a DSN — useful for reusing one connection across multiple `run()` calls, or when connection parameters come from your application's configuration rather than a URL:
|
|
335
329
|
|
|
336
330
|
```python
|
|
337
|
-
from execsql
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
#
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
#
|
|
331
|
+
from execsql import run
|
|
332
|
+
from execsql.db.factory import db_Postgres, db_SQLite
|
|
333
|
+
|
|
334
|
+
# PostgreSQL connection object (psycopg3 under the hood)
|
|
335
|
+
conn = db_Postgres(
|
|
336
|
+
"db.example.com", # server
|
|
337
|
+
"warehouse", # database
|
|
338
|
+
user="etl",
|
|
339
|
+
port=5432, # optional, default 5432
|
|
340
|
+
password="s3cret", # omit to use keyring / interactive prompt
|
|
341
|
+
)
|
|
346
342
|
|
|
347
|
-
|
|
343
|
+
# Reuse the same connection across multiple calls
|
|
344
|
+
staged = run(script="stage.sql", connection=conn)
|
|
345
|
+
loaded = run(
|
|
346
|
+
script="load.sql",
|
|
347
|
+
connection=conn,
|
|
348
|
+
variables={"RUN_DATE": "2026-07-03"},
|
|
349
|
+
)
|
|
348
350
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
-- Put the (date-tagged) logfile name in the 'inputfile' substitution variable.
|
|
352
|
-
-- !x! SUB inputfile logs/errors_!!$date_tag!!
|
|
353
|
-
-- Ensure that the export directory will be created if necessary.
|
|
354
|
-
-- !x! CONFIG MAKE_EXPORT_DIRS Yes
|
|
351
|
+
# run() does NOT close the connection — you manage its lifecycle
|
|
352
|
+
conn.close()
|
|
355
353
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
-- Create a view to display only fatal errors.
|
|
361
|
-
create temporary view fatals as
|
|
362
|
-
select user, run_time, process
|
|
363
|
-
from staging.errorlog
|
|
364
|
-
where severity = 'FATAL';
|
|
365
|
-
-- !x! IF(HASROWS(fatals))
|
|
366
|
-
-- Export the fatal errors to a dated report.
|
|
367
|
-
-- !x! EXPORT fatals TO reports/error_report_!!$date_tag!! AS CSV
|
|
368
|
-
-- Also display it to the user in a GUI.
|
|
369
|
-
-- !x! PROMPT MESSAGE "Fatal errors in !!inputfile!!:" DISPLAY fatals
|
|
370
|
-
-- !x! ELSE
|
|
371
|
-
-- !x! WRITE "There are no fatal errors."
|
|
372
|
-
-- !x! ENDIF
|
|
373
|
-
-- !x! ELSE
|
|
374
|
-
-- !x! WRITE "There is no error log."
|
|
375
|
-
-- !x! ENDIF
|
|
376
|
-
drop table if exists staging.errorlog cascade;
|
|
354
|
+
# SQLite works the same way
|
|
355
|
+
conn = db_SQLite("my.db", new_db=True)
|
|
356
|
+
result = run(sql="SELECT 1;", connection=conn)
|
|
357
|
+
conn.close()
|
|
377
358
|
```
|
|
378
359
|
|
|
379
|
-
|
|
360
|
+
Factory functions exist for every supported backend (`db_Postgres`, `db_MySQL`, `db_SQLite`, `db_DuckDB`, `db_SqlServer`, `db_Oracle`, `db_Firebird`, `db_Access`, `db_Dsn`) — see [`execsql.db.factory`](https://execsql2.readthedocs.io/en/latest/api/db/).
|
|
380
361
|
|
|
381
|
-
|
|
362
|
+
Each call to `run()` uses an isolated `RuntimeContext`, so multiple calls do not share state.
|
|
382
363
|
|
|
383
|
-
|
|
364
|
+
## Formatting Scripts
|
|
384
365
|
|
|
385
366
|
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):
|
|
386
367
|
|
|
@@ -403,14 +384,14 @@ execsql-format --no-sql --in-place scripts/
|
|
|
403
384
|
```yaml
|
|
404
385
|
repos:
|
|
405
386
|
- repo: https://github.com/geocoug/execsql
|
|
406
|
-
rev: v2.22.
|
|
387
|
+
rev: v2.22.2
|
|
407
388
|
hooks:
|
|
408
389
|
- id: execsql-format
|
|
409
390
|
```
|
|
410
391
|
|
|
411
392
|
The hook rewrites `*.sql` files in place by default. See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for `--check`, `--indent`, and other options.
|
|
412
393
|
|
|
413
|
-
|
|
394
|
+
## VS Code Syntax Highlighting
|
|
414
395
|
|
|
415
396
|
A VS Code extension for execsql syntax highlighting is included in [`extras/vscode-execsql`](extras/vscode-execsql). It injects a TextMate grammar into `.sql` files, adding highlighting for `-- !x!` metacommand markers, keywords (control flow, block, action, directive), variable substitutions (`!!var!!`, `!{var}!`), built-in functions, export formats, and config options — all layered on top of standard SQL highlighting.
|
|
416
397
|
|
|
@@ -422,7 +403,7 @@ ln -s /path/to/execsql/extras/vscode-execsql ~/.vscode/extensions/execsql-syntax
|
|
|
422
403
|
|
|
423
404
|
See the [extension README](extras/vscode-execsql/README.md) for Windows instructions, color customization, and troubleshooting.
|
|
424
405
|
|
|
425
|
-
|
|
406
|
+
## Templates
|
|
426
407
|
|
|
427
408
|
The `templates/` directory in this repository includes ready-to-use execsql scripts:
|
|
428
409
|
|
|
@@ -432,13 +413,14 @@ The `templates/` directory in this repository includes ready-to-use execsql scri
|
|
|
432
413
|
- **`script_template.sql`**: A framework for new scripts with sections for configuration, logging, and error reporting.
|
|
433
414
|
- **`execsql.conf`**: An annotated configuration file covering all available settings.
|
|
434
415
|
|
|
435
|
-
|
|
416
|
+
## Documentation
|
|
436
417
|
|
|
437
418
|
Full documentation, including a complete metacommand reference and 30+ examples, is at [execsql2.readthedocs.io](https://execsql2.readthedocs.io/).
|
|
438
419
|
|
|
439
|
-
|
|
420
|
+
## Copyright and License
|
|
440
421
|
|
|
441
422
|
Copyright (c) 2007-2025 R.Dreas Nielsen
|
|
423
|
+
|
|
442
424
|
Copyright (c) 2026-present Caleb Grant
|
|
443
425
|
|
|
444
426
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [GNU General Public License](http://www.gnu.org/licenses/) for more details.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
execsql/__init__.py,sha256=ZeVnpEWu8M2bn3qLmVM72NhWRueUGNbsX8UAPTermo0,901
|
|
2
2
|
execsql/__main__.py,sha256=HdbK-SAhyUmfB6xINY5AzxdMSxGzWSGEG_2dv42Jn64,315
|
|
3
|
-
execsql/api.py,sha256=
|
|
4
|
-
execsql/config.py,sha256=
|
|
3
|
+
execsql/api.py,sha256=EOtBwMnm-8fmB_KmvxyjglR6C7CjdYhCPBisJ9CFfJg,22227
|
|
4
|
+
execsql/config.py,sha256=txKziUJLV7UPonj2sGFgkmlinDdSV50aHmR_cuCbEIo,34369
|
|
5
5
|
execsql/exceptions.py,sha256=GlphAiR8iZu4UR4U50WgSqEUMd9_8AeQJ_hhPcJOeQU,9381
|
|
6
6
|
execsql/format.py,sha256=ON8Jy5BSen4UI4SGY5cHKRwsvR0ZoLhH0BdblMwydYE,33670
|
|
7
7
|
execsql/models.py,sha256=-DWTVrPNwM-n1h3e3rXMoBv9-wpWVdZahyy4RB7fRRE,15502
|
|
@@ -11,7 +11,7 @@ execsql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
11
11
|
execsql/state.py,sha256=T6UoXXxAkUP-4KKQpfFAlI3WMzm2xUi3LSplJDuRLY0,21965
|
|
12
12
|
execsql/state.pyi,sha256=ES5xdoId-ALCGnLIQvQIGBZljZRB6aOOEMRLKXjO9hA,3770
|
|
13
13
|
execsql/types.py,sha256=eKMF2kb5LHrEwKsiFpy6z_fEH05yC0Cpsg98Ea4SpVA,32231
|
|
14
|
-
execsql/cli/__init__.py,sha256=
|
|
14
|
+
execsql/cli/__init__.py,sha256=WPwEIELz-9XFG3Dpf-037eeYBdjtZStxWkT1bc3tMek,23112
|
|
15
15
|
execsql/cli/dsn.py,sha256=svaZtrUXFRL2W5G6FRRiKtR6kehOp7urrVhIx_642Z8,2820
|
|
16
16
|
execsql/cli/help.py,sha256=ThwdZuMIfLPxLAPpGWwXFY_UfyWvYOCjdlBNK20Vzd8,5718
|
|
17
17
|
execsql/cli/lint.py,sha256=YqKzFNUhyb_Th69hYgKk1ZZVjCsZfJMIiUGqp06JwNs,17236
|
|
@@ -86,7 +86,7 @@ execsql/script/__init__.py,sha256=eGJPBDWj42aaId2lX_quSrqoKrvGwGElIrGDNCyoV1Y,35
|
|
|
86
86
|
execsql/script/ast.py,sha256=TQ4_7Lfw1F8_k6ycdvMZdzwNafrZiljSrthVRWUsuIk,20585
|
|
87
87
|
execsql/script/control.py,sha256=WqLy-HLPqHG3vEzYpKMiIJsD7LpORjyQuUWzFzcGz4w,2327
|
|
88
88
|
execsql/script/engine.py,sha256=KbSB5aXkuDlazh3rRP4vRym_bVTDKmChLQ_OFf3-N8Q,21016
|
|
89
|
-
execsql/script/executor.py,sha256=
|
|
89
|
+
execsql/script/executor.py,sha256=NEIro9TIIcOvugoqdtk2xs08DQnrzkeQIo1CUwaTbIY,37092
|
|
90
90
|
execsql/script/parser.py,sha256=VhFTJK8kODNRWgIDc3LjYjA3_SMPFATkpjVl82dYnrM,35179
|
|
91
91
|
execsql/script/variables.py,sha256=_hklqCeqPbdOT646Yg3ZcpmPke38_8bukMG3eZMTKZw,16384
|
|
92
92
|
execsql/utils/__init__.py,sha256=0uR6JwVJQRX3vceByNBduCAf5dd5assKjeqJUWvpZoA,278
|
|
@@ -94,30 +94,30 @@ execsql/utils/auth.py,sha256=8Wixkx5nayEBG7RT4WTUQpCiElIKS3yOTV_6_-hHTRs,8889
|
|
|
94
94
|
execsql/utils/crypto.py,sha256=KlT6lPk-v0-qQQfAMfWvT9Q6kRdCD3_5JVaLajNocKM,3051
|
|
95
95
|
execsql/utils/datetime.py,sha256=8BrD24KWeMoouJZiFaXK4HF7plc-O_xrzIw12qOEyp8,3629
|
|
96
96
|
execsql/utils/errors.py,sha256=bDWooUmGdN8xqUt6csq466wPqKPLHHSi7U4hsmrFZxA,8577
|
|
97
|
-
execsql/utils/fileio.py,sha256=
|
|
98
|
-
execsql/utils/gui.py,sha256=
|
|
97
|
+
execsql/utils/fileio.py,sha256=_15UIdcRZxZn2R6YP4xQn-6C9CVK2yxj1oEfxULo2_c,33329
|
|
98
|
+
execsql/utils/gui.py,sha256=X5aw6LVoeaAdHaQkymm4ba176jga0cS2GrqxSFWTWTg,23853
|
|
99
99
|
execsql/utils/mail.py,sha256=SXpTBDx0WIPl2NSjkjmnoChHYVEMyZhIKU4QiJFFZNY,6011
|
|
100
100
|
execsql/utils/numeric.py,sha256=_7bi5R6pHjMCkOPne_Sh5nUj-B8e9g6WFmjptFHaunA,1573
|
|
101
101
|
execsql/utils/regex.py,sha256=B3mSBhRRx_DpUnX_t9IoYd05KvWAebiLMCZqQAmEwcw,3885
|
|
102
102
|
execsql/utils/strings.py,sha256=UQNjpRCEFa1UO6feU-M-9e24wWAvizs_iu_4fFusLxo,8516
|
|
103
103
|
execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
|
|
104
|
-
execsql2-2.22.
|
|
105
|
-
execsql2-2.22.
|
|
106
|
-
execsql2-2.22.
|
|
107
|
-
execsql2-2.22.
|
|
108
|
-
execsql2-2.22.
|
|
109
|
-
execsql2-2.22.
|
|
110
|
-
execsql2-2.22.
|
|
111
|
-
execsql2-2.22.
|
|
112
|
-
execsql2-2.22.
|
|
113
|
-
execsql2-2.22.
|
|
114
|
-
execsql2-2.22.
|
|
115
|
-
execsql2-2.22.
|
|
116
|
-
execsql2-2.22.
|
|
117
|
-
execsql2-2.22.
|
|
118
|
-
execsql2-2.22.
|
|
119
|
-
execsql2-2.22.
|
|
120
|
-
execsql2-2.22.
|
|
121
|
-
execsql2-2.22.
|
|
122
|
-
execsql2-2.22.
|
|
123
|
-
execsql2-2.22.
|
|
104
|
+
execsql2-2.22.2.data/data/execsql2_extras/README.md,sha256=vX4NTL095dUoA_hesyRMGYBorEZ_Y_tJ9qrd-MVV09I,5032
|
|
105
|
+
execsql2-2.22.2.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
|
|
106
|
+
execsql2-2.22.2.data/data/execsql2_extras/example_config_prompt.sql,sha256=2e8KzzVWhho8KxYVHETSVmZdhW7wodioDsqBLSL6m4s,7487
|
|
107
|
+
execsql2-2.22.2.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
|
|
108
|
+
execsql2-2.22.2.data/data/execsql2_extras/md_compare.sql,sha256=qYYVAjSeHZzjszxV3Bv6bg8Ckbq2kMHl87_gh4sywMU,24140
|
|
109
|
+
execsql2-2.22.2.data/data/execsql2_extras/md_glossary.sql,sha256=hkZ2Onn57LAKKsuXxzhR8tPtcWXkmWEQkwPE58-Tm2k,10796
|
|
110
|
+
execsql2-2.22.2.data/data/execsql2_extras/md_upsert.sql,sha256=_CAK4BzEboRXTNy03SJR-oOjcEdSNMuRBPL6noWUptY,112560
|
|
111
|
+
execsql2-2.22.2.data/data/execsql2_extras/pg_compare.sql,sha256=1zJd4hVUKHR0tncc2qTBC9B4qVV4Us2ITkJpsjN3tMw,24352
|
|
112
|
+
execsql2-2.22.2.data/data/execsql2_extras/pg_glossary.sql,sha256=IKuwna-_8b20ljSkXZruuiQigrCpo7ueQdUqd1MXiuI,9908
|
|
113
|
+
execsql2-2.22.2.data/data/execsql2_extras/pg_upsert.sql,sha256=HpPJtTHvpEjQy03j-3iPxDEOHMRkudOg7O4D4YR38UI,108315
|
|
114
|
+
execsql2-2.22.2.data/data/execsql2_extras/script_template.sql,sha256=2J35ddZPguJ-vwTsz83wErv0jiWUyJcdW_JM0mNKDXA,11155
|
|
115
|
+
execsql2-2.22.2.data/data/execsql2_extras/ss_compare.sql,sha256=j1qVNUPXQsEU7-DoVgDJCGcE0EuIl7whLBT3fgeiMAo,24833
|
|
116
|
+
execsql2-2.22.2.data/data/execsql2_extras/ss_glossary.sql,sha256=2gLxv34xzKt0vy7hSzJH7a9JiMC3ETrv9MofxQwAibU,13065
|
|
117
|
+
execsql2-2.22.2.data/data/execsql2_extras/ss_upsert.sql,sha256=G_8rQ0VzuKIZHWs24O_WrfzpC5S27R1JsL-bFBR3SUQ,117730
|
|
118
|
+
execsql2-2.22.2.dist-info/METADATA,sha256=qL8AZS7bCoWljac0GuIdc5uHHJCUtBKegwm_sayfAlQ,20382
|
|
119
|
+
execsql2-2.22.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
120
|
+
execsql2-2.22.2.dist-info/entry_points.txt,sha256=sUOxkM-dN1eBGGpSpDLsAaE0yNXYQKWZAfxPOlMkQyk,90
|
|
121
|
+
execsql2-2.22.2.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
|
|
122
|
+
execsql2-2.22.2.dist-info/licenses/NOTICE,sha256=McYzgxYav3U1OaVsY4Su1sfBrfmplpRdA9b6-gCDQCg,342
|
|
123
|
+
execsql2-2.22.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{execsql2-2.22.0.data → execsql2-2.22.2.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
|