execsql2 2.23.1__py3-none-any.whl → 2.25.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.
Files changed (92) hide show
  1. execsql/api.py +169 -160
  2. execsql/cli/__init__.py +19 -586
  3. execsql/cli/application.py +309 -0
  4. execsql/cli/commands/__init__.py +13 -0
  5. execsql/cli/commands/config.py +205 -0
  6. execsql/cli/commands/format.py +100 -0
  7. execsql/cli/commands/init.py +135 -0
  8. execsql/cli/commands/lint.py +225 -0
  9. execsql/cli/commands/listing.py +81 -0
  10. execsql/cli/commands/run.py +459 -0
  11. execsql/cli/dispatch.py +66 -0
  12. execsql/cli/dsn.py +11 -4
  13. execsql/cli/help.py +175 -24
  14. execsql/cli/lint.py +780 -100
  15. execsql/cli/options.py +108 -0
  16. execsql/cli/run.py +96 -32
  17. execsql/config.py +256 -173
  18. execsql/config_validation.py +282 -0
  19. execsql/data/execsql.conf.template +38 -5
  20. execsql/data/script_header.sql +23 -0
  21. execsql/db/access.py +29 -8
  22. execsql/db/base.py +40 -8
  23. execsql/db/dsn.py +13 -4
  24. execsql/db/duckdb.py +3 -1
  25. execsql/db/factory.py +13 -4
  26. execsql/db/firebird.py +21 -7
  27. execsql/db/mysql.py +37 -16
  28. execsql/db/oracle.py +49 -16
  29. execsql/db/postgres.py +30 -6
  30. execsql/db/sqlite.py +7 -9
  31. execsql/db/sqlserver.py +25 -8
  32. execsql/debug/repl.py +51 -193
  33. execsql/exceptions.py +9 -0
  34. execsql/exporters/delimited.py +87 -28
  35. execsql/exporters/xlsx.py +24 -9
  36. execsql/exporters/zip.py +1 -2
  37. execsql/format.py +115 -76
  38. execsql/gui/desktop.py +1 -3
  39. execsql/gui/tui.py +2 -2
  40. execsql/importers/base.py +35 -1
  41. execsql/importers/csv.py +3 -1
  42. execsql/interactive.py +232 -0
  43. execsql/manifest.py +200 -0
  44. execsql/metacommands/conditions.py +7 -8
  45. execsql/metacommands/control.py +2 -3
  46. execsql/metacommands/data.py +4 -1
  47. execsql/metacommands/debug.py +2 -4
  48. execsql/metacommands/effects.py +124 -0
  49. execsql/metacommands/io_fileops.py +62 -37
  50. execsql/metacommands/io_import.py +0 -1
  51. execsql/metacommands/prompt.py +2 -0
  52. execsql/metacommands/upsert.py +5 -2
  53. execsql/models.py +3 -2
  54. execsql/parser.py +14 -5
  55. execsql/plugins.py +24 -0
  56. execsql/script/__init__.py +0 -9
  57. execsql/script/ast.py +1 -1
  58. execsql/script/engine.py +46 -104
  59. execsql/script/executor.py +162 -39
  60. execsql/script/parser.py +167 -118
  61. execsql/state.py +34 -11
  62. execsql/state.pyi +11 -0
  63. execsql/types.py +22 -13
  64. execsql/utils/auth.py +169 -24
  65. execsql/utils/color.py +18 -0
  66. execsql/utils/datetime.py +55 -17
  67. execsql/utils/errors.py +82 -10
  68. execsql/utils/fileio.py +404 -117
  69. execsql/utils/gui.py +3 -0
  70. execsql/utils/mail.py +37 -12
  71. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/README.md +1 -1
  72. {execsql2-2.23.1.dist-info → execsql2-2.25.0.dist-info}/METADATA +122 -63
  73. execsql2-2.25.0.dist-info/RECORD +140 -0
  74. execsql2-2.25.0.dist-info/entry_points.txt +2 -0
  75. execsql2-2.23.1.dist-info/RECORD +0 -124
  76. execsql2-2.23.1.dist-info/entry_points.txt +0 -3
  77. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  78. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  79. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
  80. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/md_compare.sql +0 -0
  81. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
  82. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
  83. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
  84. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  85. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  86. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/script_template.sql +0 -0
  87. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
  88. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  89. {execsql2-2.23.1.data → execsql2-2.25.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  90. {execsql2-2.23.1.dist-info → execsql2-2.25.0.dist-info}/WHEEL +0 -0
  91. {execsql2-2.23.1.dist-info → execsql2-2.25.0.dist-info}/licenses/LICENSE.txt +0 -0
  92. {execsql2-2.23.1.dist-info → execsql2-2.25.0.dist-info}/licenses/NOTICE +0 -0
execsql/utils/gui.py CHANGED
@@ -533,6 +533,9 @@ def gui_credentials(
533
533
  _state.subvars.add_substitution(username, uname)
534
534
  if pwtext:
535
535
  _state.subvars.add_substitution(pwtext, passwd)
536
+ from execsql.utils.fileio import register_secret
537
+
538
+ register_secret(passwd)
536
539
 
537
540
 
538
541
  # ---------------------------------------------------------------------------
execsql/utils/mail.py CHANGED
@@ -12,6 +12,7 @@ metacommand and the halt/cancel email-notification hooks.
12
12
 
13
13
  import re
14
14
  import smtplib
15
+ import ssl
15
16
  from email import encoders
16
17
  from email.mime.base import MIMEBase
17
18
  from email.mime.multipart import MIMEMultipart
@@ -25,6 +26,20 @@ from execsql.exceptions import ErrInfo
25
26
  __all__ = ["MailSpec", "Mailer"]
26
27
 
27
28
 
29
+ def _tls_context(conf: object) -> ssl.SSLContext:
30
+ """The TLS context for the SMTP connection: verifying, unless ``[email] verify_certificate`` is off.
31
+
32
+ ``smtplib`` given no context uses an unverified one, which accepts any
33
+ certificate and hands the SMTP password to whoever answers.
34
+ """
35
+ if not getattr(conf, "smtp_verify_certificate", True):
36
+ ctx = ssl.create_default_context()
37
+ ctx.check_hostname = False
38
+ ctx.verify_mode = ssl.CERT_NONE
39
+ return ctx
40
+ return ssl.create_default_context(cafile=getattr(conf, "smtp_ca_file", None))
41
+
42
+
28
43
  class Mailer:
29
44
  def __repr__(self) -> str:
30
45
  return "Mailer()"
@@ -56,20 +71,30 @@ class Mailer:
56
71
  # 30 s connect/read timeout matches the DB-adapter default so a
57
72
  # silently-dropped SMTP peer can't hang a script (or a CI run).
58
73
  smtp_timeout = 30
59
- if conf.smtp_port is None:
74
+ port = conf.smtp_port or 0
75
+ try:
60
76
  if conf.smtp_ssl:
61
- self.smtpconn = smtplib.SMTP_SSL(conf.smtp_host, timeout=smtp_timeout)
62
- else:
63
- self.smtpconn = smtplib.SMTP(conf.smtp_host, timeout=smtp_timeout)
64
- else:
65
- if conf.smtp_ssl:
66
- self.smtpconn = smtplib.SMTP_SSL(conf.smtp_host, conf.smtp_port, timeout=smtp_timeout)
77
+ self.smtpconn = smtplib.SMTP_SSL(
78
+ conf.smtp_host,
79
+ port,
80
+ timeout=smtp_timeout,
81
+ context=_tls_context(conf),
82
+ )
67
83
  else:
68
- self.smtpconn = smtplib.SMTP(conf.smtp_host, conf.smtp_port, timeout=smtp_timeout)
69
- self.smtpconn.ehlo_or_helo_if_needed()
70
- if conf.smtp_tls:
71
- self.smtpconn.starttls()
72
- self.smtpconn.ehlo(conf.smtp_host)
84
+ self.smtpconn = smtplib.SMTP(conf.smtp_host, port, timeout=smtp_timeout)
85
+ self.smtpconn.ehlo_or_helo_if_needed()
86
+ if conf.smtp_tls:
87
+ self.smtpconn.starttls(context=_tls_context(conf))
88
+ self.smtpconn.ehlo(conf.smtp_host)
89
+ except ssl.SSLCertVerificationError as exc:
90
+ raise ErrInfo(
91
+ type="error",
92
+ other_msg=(
93
+ f"Can't send email; the certificate of SMTP server {conf.smtp_host} could not be verified "
94
+ f"({exc.verify_message}). For a server with an internal or self-signed certificate, set "
95
+ "[email] ca_file to its CA certificate, or set verify_certificate = No."
96
+ ),
97
+ ) from exc
73
98
  if conf.smtp_username:
74
99
  if not conf.smtp_password:
75
100
  # smtplib.login() requires a password; calling it with only a
@@ -2,7 +2,7 @@
2
2
 
3
3
  Several types of templates are provided that may be useful in conjunction with execsql. These are:
4
4
 
5
- - **execsql.conf** — An annotated reference of every configuration setting with notes on its usage. Generate a fresh copy in any directory with `execsql --init-config > execsql.conf`; the canonical content ships inside the installed package and is loaded via `importlib.resources` (no need to find or copy a source file).
5
+ - **execsql.conf** — An annotated reference of every configuration setting with notes on its usage. Generate a fresh copy in any directory with `execsql config --init > execsql.conf`; the canonical content ships inside the installed package and is loaded via `importlib.resources` (no need to find or copy a source file).
6
6
 
7
7
  - **script_template.sql** — A framework for SQL scripts that make use of several execsql features. It includes sections for custom configuration settings, custom logfile creation, and reporting of unexpected script exits (through user cancellation or errors).
8
8
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execsql2
3
- Version: 2.23.1
3
+ Version: 2.25.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
@@ -42,7 +42,7 @@ Requires-Python: >=3.10
42
42
  Requires-Dist: python-dateutil>=2.8
43
43
  Requires-Dist: rich>=13.0
44
44
  Requires-Dist: textual>=1.0
45
- Requires-Dist: typer>=0.12
45
+ Requires-Dist: typer>=0.26
46
46
  Provides-Extra: all
47
47
  Requires-Dist: defusedxml>=0.7; extra == 'all'
48
48
  Requires-Dist: duckdb>=1.0; extra == 'all'
@@ -52,7 +52,7 @@ Requires-Dist: keyring>=25.0; extra == 'all'
52
52
  Requires-Dist: odfpy>=1.4; extra == 'all'
53
53
  Requires-Dist: openpyxl>=3.1; extra == 'all'
54
54
  Requires-Dist: oracledb>=3.0; extra == 'all'
55
- Requires-Dist: pg-upsert>=1.23.0; extra == 'all'
55
+ Requires-Dist: pg-upsert>=1.25.0; extra == 'all'
56
56
  Requires-Dist: polars>=1.0; extra == 'all'
57
57
  Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'all'
58
58
  Requires-Dist: pymysql>=1.1; extra == 'all'
@@ -126,7 +126,7 @@ Requires-Dist: oracledb>=3.0; extra == 'oracle'
126
126
  Provides-Extra: postgres
127
127
  Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'postgres'
128
128
  Provides-Extra: upsert
129
- Requires-Dist: pg-upsert>=1.23.0; extra == 'upsert'
129
+ Requires-Dist: pg-upsert>=1.25.0; extra == 'upsert'
130
130
  Description-Content-Type: text/markdown
131
131
 
132
132
  > [!NOTE]
@@ -154,7 +154,54 @@ Description-Content-Type: text/markdown
154
154
 
155
155
  ## Overview
156
156
 
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`.
157
+ *execsql* is a toolchain for SQL scripts: **write** them with editor support, **format** them consistently, **lint** them without a database, and **run** them against nine DBMSs.
158
+
159
+ Scripts are ordinary SQL plus metacommands embedded in comments (`-- !x!`), which add importing and exporting data, copying between databases, conditional execution, looping, substitution variables, and interactive prompts. Because the metacommands live in comments, the scripts stay valid SQL and other tools (`psql`, `sqlcmd`, your editor) ignore them.
160
+
161
+ | Command | What it does | Needs a database? |
162
+ | ------------------------------------------ | ----------------------------------------------------------------------- | ----------------- |
163
+ | `execsql format` | Normalize keywords, indentation, and SQL layout | No |
164
+ | `execsql lint` | Static analysis: structure, unknown metacommands, variables, targets | No |
165
+ | `execsql run` | Run the script against PostgreSQL, MySQL, SQLite, DuckDB, … | Yes |
166
+ | `execsql config` | Show every config option, its value, and the file that set it | No |
167
+ | `execsql list` | Metacommands, encodings, plugins, or the keyword vocabulary | No |
168
+ | `execsql init` | Set up a project: config file, a script with a header, pre-commit hooks | No |
169
+ | [VS Code extension](extras/vscode-execsql) | Syntax highlighting for metacommands and variables | No |
170
+
171
+ `format` and `lint` take files or directories.
172
+
173
+ > [!WARNING]
174
+ > The original positional form, `execsql script.sql myserver mydb`, still works
175
+ > but is deprecated: it prints a warning and stops working in execsql2 3.0. Use
176
+ > `execsql run script.sql myserver mydb`.
177
+
178
+ ## Quick start: no database required
179
+
180
+ `format` and `lint` work on a bare `.sql` file, with no server and no config. Try them first:
181
+
182
+ ```bash
183
+ uv tool install "execsql2[formatter]" # or: pipx install "execsql2[formatter]"
184
+
185
+ execsql format --check scripts/ # is it formatted?
186
+ execsql format -i scripts/ # format it
187
+ execsql lint scripts/ # find problems before they cost you a run
188
+ ```
189
+
190
+ `lint` reports unmatched blocks, misspelled or malformed metacommands and conditions, undefined and unused substitution variables, unreachable branches, and `INCLUDE`/`SCRIPT` targets that do not exist: the failures that otherwise surface halfway through a run against a live database:
191
+
192
+ ```text
193
+ $ execsql lint load_data.sql
194
+ load_data.sql
195
+ 1 warning V002 variable !!site_cd!! is never used
196
+ 2 warning I001 INCLUDE file does not exist: common/setup.sql
197
+ 3 warning V001 undefined variable !!site_code!!
198
+
199
+ Found 3 issues in 1 file: 3 warnings (1 file checked)
200
+ ```
201
+
202
+ `V002` and `V001` together point at a typo: `site_cd` is defined and `site_code` is used. Every issue has a rule code for `--select` and `--ignore`; `--output-format concise` prints one `path:line` line per issue and `json` feeds CI tools. The [lint rules](https://execsql2.readthedocs.io/en/latest/reference/lint/) page explains each one.
203
+
204
+ `execsql format` also runs as a [pre-commit hook](#formatting-scripts), so formatting is enforced without anyone remembering to run it.
158
205
 
159
206
  ## Example
160
207
 
@@ -197,49 +244,59 @@ The `PROMPT` metacommand produces a GUI display of the data:
197
244
  ## Installation
198
245
 
199
246
  ```bash
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
247
+ uv tool install execsql2 # core: SQLite works with no extras
248
+ uv tool install "execsql2[postgres]" # with a driver: postgres, mysql, mssql, duckdb, firebird, oracle, odbc
249
+ uv tool install "execsql2[all]" # everything: all drivers plus all feature extras
203
250
  ```
204
251
 
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.
252
+ `pipx install` takes the same arguments. Quote the name when it has extras
253
+ (zsh reports `no matches found` otherwise). To call execsql from Python code,
254
+ add it to that project instead: `uv add "execsql2[postgres]"` or
255
+ `pip install "execsql2[postgres]"` in its virtual environment.
256
+
257
+ 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.
206
258
 
207
259
  ## Usage
208
260
 
209
261
  ```text
210
- execsql [OPTIONS] SQL_SCRIPT [SERVER DATABASE | DATABASE_FILE]
262
+ execsql run [OPTIONS] SQL_SCRIPT [SERVER DATABASE | DATABASE_FILE]
263
+ execsql format [--check | -i] [--indent N] FILE_OR_DIR...
264
+ execsql lint [OPTIONS] FILE_OR_DIR...
265
+ execsql config [SQL_SCRIPT] [--init] [--config FILE]
266
+ execsql list metacommands|encodings|plugins|keywords
267
+ execsql init [DIR] [--script NAME] [--no-script] [--no-config] [--no-pre-commit]
211
268
  ```
212
269
 
213
270
  Examples:
214
271
 
215
272
  ```bash
216
- execsql -tp script.sql myserver mydb # PostgreSQL
217
- execsql -tm script.sql myserver mydb # MySQL / MariaDB
218
- execsql -ts script.sql myserver mydb # SQL Server
219
- execsql -tl script.sql mydb.sqlite # SQLite
220
- execsql -tk script.sql mydb.duckdb # DuckDB
221
- execsql -to script.sql myserver myservice # Oracle
222
- execsql script.sql # read connection from config file
273
+ execsql run -tp script.sql myserver mydb # PostgreSQL
274
+ execsql run -tm script.sql myserver mydb # MySQL / MariaDB
275
+ execsql run -ts script.sql myserver mydb # SQL Server
276
+ execsql run -tl script.sql mydb.sqlite # SQLite
277
+ execsql run -tk script.sql mydb.duckdb # DuckDB
278
+ execsql run -to script.sql myserver myservice # Oracle
279
+ execsql run script.sql # read connection from config file
223
280
  ```
224
281
 
225
282
  ### Supported Databases
226
283
 
227
- | Flag | Database | Support |
228
- | ---- | --------------- | ------------------------------------------------ |
229
- | `p` | PostgreSQL | Supported — verified against a live server in CI |
230
- | `m` | MySQL / MariaDB | Supported — verified against a live server in CI |
231
- | `s` | MS SQL Server | Supported — verified against a live server in CI |
232
- | `l` | SQLite | Supported — verified against real files in CI |
233
- | `k` | DuckDB | Supported — verified against real files in CI |
234
- | `a` | MS Access | Best effort — not verified in CI |
235
- | `f` | Firebird | Best effort — not verified in CI |
236
- | `o` | Oracle | Best effort — not verified in CI |
237
- | `d` | ODBC DSN | Best effort — not verified in CI |
284
+ | Flag | Database | Support |
285
+ | ---- | --------------- | ----------------------------------------------- |
286
+ | `p` | PostgreSQL | Supported: verified against a live server in CI |
287
+ | `m` | MySQL / MariaDB | Supported: verified against a live server in CI |
288
+ | `s` | MS SQL Server | Supported: verified against a live server in CI |
289
+ | `l` | SQLite | Supported: verified against real files in CI |
290
+ | `k` | DuckDB | Supported: verified against real files in CI |
291
+ | `a` | MS Access | Best effort: not verified in CI |
292
+ | `f` | Firebird | Best effort: not verified in CI |
293
+ | `o` | Oracle | Best effort: not verified in CI |
294
+ | `d` | ODBC DSN | Best effort: not verified in CI |
238
295
 
239
296
  **Supported** adapters run against a live server or a real database file on
240
297
  every CI run; a regression blocks the release and bugs get fixed. **Best
241
298
  effort** adapters are carried forward from the upstream monolith and are not
242
- exercised anywhere in CI — the code is there and may work, but nothing proves
299
+ exercised anywhere in CI. The code is there and may work, but nothing proves
243
300
  it still does. Issues and pull requests are welcome for them; no guarantee is
244
301
  made. Opening a best-effort connection prints one informational line, which
245
302
  `support_tier_notice=No` in the `[interface]` section of `execsql.conf`
@@ -247,24 +304,24 @@ silences.
247
304
 
248
305
  ### Common options
249
306
 
250
- | Flag | Description |
251
- | ------------------------------------------------- | --------------------------------------------------------------- |
252
- | `-t {p,m,s,l,k,a,f,o,d}` | Database type |
253
- | `-u USER` | Database username |
254
- | `-p PORT` | Server port |
255
- | `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
256
- | `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
257
- | `-c SCRIPT` | Execute inline SQL or metacommand string |
258
- | `-a VALUE` | Set substitution variable `$ARG_x` |
259
- | `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
260
- | `--config FILE` | Load an explicit config file |
261
- | `--dry-run` | Parse the script and report commands without executing |
262
- | `--lint` | Static analysis: check structure and warn on issues (no DB) |
263
- | `--ping` | Test database connectivity and exit |
264
- | `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
265
- | `--no-system-cmd` / `--no-rm-file` / `--no-serve` | Disable the `SYSTEM_CMD` / `RM_FILE` / `SERVE` metacommands |
266
-
267
- 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.
307
+ | Flag | Description |
308
+ | ------------------------------------------------- | -------------------------------------------------------------------------- |
309
+ | `-t {p,m,s,l,k,a,f,o,d}` | Database type |
310
+ | `-u USER` | Database username |
311
+ | `-p PORT` | Server port |
312
+ | `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
313
+ | `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
314
+ | `-c SCRIPT` | Execute inline SQL or metacommand string |
315
+ | `-a VALUE` | Set substitution variable `$ARG_x` |
316
+ | `--var NAME=VALUE` | Set the substitution variable `!!NAME!!` |
317
+ | `-v {0,1,2,3}` | GUI level (0=none, 1=password/PAUSE, 2=+HALT and DB selection, 3=console) |
318
+ | `--config FILE` | Load an explicit config file |
319
+ | `--dry-run` | Parse the script and report commands without executing |
320
+ | `--manifest FILE` | Write a JSON record of the run: files read and written, statements, errors |
321
+ | `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
322
+ | `--no-system-cmd` / `--no-rm-file` / `--no-serve` | Disable the `SYSTEM_CMD` / `RM_FILE` / `SERVE` metacommands |
323
+
324
+ See the [full options reference](https://execsql2.readthedocs.io/en/latest/getting-started/syntax/#options) or run `execsql run --help` for the complete list, `execsql run --ping` to test a connection, `execsql config` to see which config files set what, and `execsql list metacommands` for all metacommands.
268
325
 
269
326
  ## Features
270
327
 
@@ -279,7 +336,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/latest/getti
279
336
  - Conditionally execute SQL and metacommands using `IF`/`ELSE`/`ENDIF` based on data values, DBMS type, or user input.
280
337
  - Loop over blocks of SQL and metacommands using `LOOP`/`ENDLOOP`; include or chain scripts with `INCLUDE` and `SCRIPT`.
281
338
  - Use substitution variables (`SUB`, `$ARG_x`, built-in variables like `$date_tag`) to parameterize scripts.
282
- - Validate data with `ASSERT` — halt the script with a clear error message if a condition is false (ideal for CI pipelines).
339
+ - Validate data with `ASSERT`: halt the script with a clear error message if a condition is false (ideal for CI pipelines).
283
340
 
284
341
  **Interaction & observability**
285
342
 
@@ -308,7 +365,8 @@ result = run(
308
365
  new_db=True,
309
366
  )
310
367
 
311
- # With substitution variables
368
+ # With substitution variables: each key becomes a $ variable,
369
+ # so the script refers to these as !!$SCHEMA!! and !!$DATE!!
312
370
  result = run(
313
371
  script="etl.sql",
314
372
  dsn="sqlite:///data.db",
@@ -319,7 +377,7 @@ result = run(
319
377
  print(result.success) # True
320
378
  print(result.commands_run) # 2
321
379
  print(result.elapsed) # 0.003 (seconds)
322
- print(result.variables) # {"SCHEMA": "public", ...}
380
+ print(result.variables) # {"schema": "public", ...} (names lower-cased, no $)
323
381
  ```
324
382
 
325
383
  Error handling:
@@ -334,7 +392,7 @@ if not result.success:
334
392
  result.raise_on_error() # raises ExecSqlError
335
393
  ```
336
394
 
337
- 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:
395
+ Use a pre-existing database connection instead of a DSN. This is useful for reusing one connection across multiple `run()` calls, or when connection parameters come from your application's configuration rather than a URL:
338
396
 
339
397
  ```python
340
398
  from execsql import run
@@ -357,7 +415,7 @@ loaded = run(
357
415
  variables={"RUN_DATE": "2026-07-03"},
358
416
  )
359
417
 
360
- # run() does NOT close the connection — you manage its lifecycle
418
+ # run() does NOT close the connection; you manage its lifecycle
361
419
  conn.close()
362
420
 
363
421
  # SQLite works the same way
@@ -366,43 +424,44 @@ result = run(sql="SELECT 1;", connection=conn)
366
424
  conn.close()
367
425
  ```
368
426
 
369
- 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/).
427
+ 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/).
370
428
 
371
429
  Each call to `run()` uses an isolated `RuntimeContext`, so multiple calls do not share state.
372
430
 
373
431
  ## Formatting Scripts
374
432
 
375
- 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):
433
+ 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):
376
434
 
377
435
  ```bash
378
436
  # Install with the SQL-reformatting extra
379
- pip install execsql2[formatter]
437
+ uv tool install "execsql2[formatter]"
380
438
 
381
439
  # Format files in place
382
- execsql-format --in-place scripts/
440
+ execsql format --in-place scripts/
383
441
 
384
442
  # Check formatting without writing (useful in CI)
385
- execsql-format --check scripts/
443
+ execsql format --check scripts/
386
444
 
387
- # Run the formatter without sqlglot — keyword/indent normalization only
388
- execsql-format --no-sql --in-place scripts/
445
+ # Run the formatter without sqlglot: keyword/indent normalization only
446
+ execsql format --no-sql --in-place scripts/
389
447
  ```
390
448
 
391
- `execsql-format` is also available as a [pre-commit](https://pre-commit.com/) hook:
449
+ `execsql format` and `execsql lint` are also available as [pre-commit](https://pre-commit.com/) hooks. The format hook id is unchanged, so existing configs keep working:
392
450
 
393
451
  ```yaml
394
452
  repos:
395
453
  - repo: https://github.com/geocoug/execsql
396
- rev: v2.23.1
454
+ rev: v2.25.0
397
455
  hooks:
398
456
  - id: execsql-format
457
+ - id: execsql-lint
399
458
  ```
400
459
 
401
- 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.
460
+ The format hook rewrites `*.sql` files in place by default; the lint hook fails the commit when a script has a lint error. See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for `--check`, `--indent`, and other options.
402
461
 
403
462
  ## VS Code Syntax Highlighting
404
463
 
405
- 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.
464
+ 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.
406
465
 
407
466
  To install, symlink the extension folder into your VS Code extensions directory:
408
467
 
@@ -0,0 +1,140 @@
1
+ execsql/__init__.py,sha256=ZeVnpEWu8M2bn3qLmVM72NhWRueUGNbsX8UAPTermo0,901
2
+ execsql/__main__.py,sha256=HdbK-SAhyUmfB6xINY5AzxdMSxGzWSGEG_2dv42Jn64,315
3
+ execsql/api.py,sha256=UJJSlFHySuINeoExaMXqhl00hVSPH1s7G4m8T0NX_xI,26484
4
+ execsql/config.py,sha256=f9UMX5LECVL6KnvvTmIIAMrqOfYe_elQxjW1TOSt4fA,38040
5
+ execsql/config_validation.py,sha256=a4ZszyJFGTNgs1jkvXE3yMCaYFtF1Uqih2ojMDVh1v8,10556
6
+ execsql/exceptions.py,sha256=aHavdblmP7m5ZLxyyKKmPSwwbdmP962j4ShP1Gd9r64,9651
7
+ execsql/format.py,sha256=rpe-eB7V9J2z7Itr_MFtdUeU2-sR8F6B7a3vYmHXKd0,54253
8
+ execsql/interactive.py,sha256=7vm7NAlcarBJJOIffcLOcJqH9ZCInvHB_CmC2sQsXf8,8241
9
+ execsql/manifest.py,sha256=ibceEZtH2FqsUiujbTGy5D3W0WgO4tFCs7bjIHVdep4,8062
10
+ execsql/models.py,sha256=yXR9FMCSCRonfyiuWlYwqLBMLzWQcvFQOjGHogM8Veg,15556
11
+ execsql/parser.py,sha256=bw26e1lD-PZscV11VtBdqSLkjpcYfNLQC2EYfYD5UwU,16012
12
+ execsql/plugins.py,sha256=RykMlIjTyWcnQc9ygjLErS3wdcsZSsG9QTIvg4DwfAI,13626
13
+ execsql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ execsql/state.py,sha256=NDAtv38b2cmknz-jEgOaOeqZCc6GdxN6mz-KCqb9pqg,23263
15
+ execsql/state.pyi,sha256=pEuTT4G6VNiwy4Ii7OUXc1RvzcQvjFkeFazSGEOprs0,4300
16
+ execsql/types.py,sha256=qUJ5kVKz_Crc9hti5u9_VnVzLBQ2n5Jtbje5hSwxGzE,32809
17
+ execsql/cli/__init__.py,sha256=8wDwIAFq7AI9iSGuw-Xfky-izN_7oHjUegr_brEBot0,3197
18
+ execsql/cli/application.py,sha256=qKjkhDMWB7fEjEbqOLZ3pNFw80piBnRSeW_DfZg-9MA,12448
19
+ execsql/cli/dispatch.py,sha256=ew10mZNXsFHwiNd_etvpSQYD3T8CiexXkjC3Ja94C4w,2697
20
+ execsql/cli/dsn.py,sha256=uBspf8DXCn2pXmT1Ug1c4IG7IgR5IFGKp9138puaubo,3289
21
+ execsql/cli/help.py,sha256=Edr3d2ahKv387a6WGgyJi5vKIFe39tGPgTv1GKp3itE,10848
22
+ execsql/cli/lint.py,sha256=7t4HXMqPCxHR6PoRxna0X3myu9hlxuK1rbPn5h2tX1Y,43390
23
+ execsql/cli/options.py,sha256=Srhf1gEOB5yy_7PxkTKBv07xK8Qzw8H-z7PUUnItpwE,2916
24
+ execsql/cli/run.py,sha256=5f0SX6QyNl8Ur2MwuW2GaN1OqVpWbEI8CQV3k-hBwH8,41987
25
+ execsql/cli/commands/__init__.py,sha256=wN5sbhKs11S8EZOTBd_GPP6TPjhYfiQkrI48Sy8n6U0,521
26
+ execsql/cli/commands/config.py,sha256=V7CGGkhSCqeJIlAXxAYyG_xNhnsORiwqth2XWEVPzss,8213
27
+ execsql/cli/commands/format.py,sha256=2pftY7nl8FexJ5tnvncxRG-7UjQ_NO0mHxmqAOKd-Nw,3948
28
+ execsql/cli/commands/init.py,sha256=kmuFgGz8sv_1KfiNh190E3A23evqkgYZeBRTU6vdjbs,5465
29
+ execsql/cli/commands/lint.py,sha256=Lba5IDsXtiUbQ6DkhC9fdLnoSpfoaewoAWF-ei3h59s,8189
30
+ execsql/cli/commands/listing.py,sha256=gWnjkgcw7uF-DJw3cMZbttOKdplDwAMlhmaBoI5drVw,2511
31
+ execsql/cli/commands/run.py,sha256=sGFDku66CazJrorr0mmrhUI3QDvQVSMEYlU8G1Pd5fQ,16492
32
+ execsql/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ execsql/data/execsql.conf.template,sha256=fTxA6Y01D-7BFABPgnJbEfPD5SSQ-l1xnaptMEBXuzc,12326
34
+ execsql/data/script_header.sql,sha256=a7WKScekNgJoxVEmMRTkWv8NsAzW1jJ4vJfGy9HVy0g,349
35
+ execsql/db/__init__.py,sha256=jTbuafuKOqYtXFR1wvCOoKK5Lr3l1uErfaIbIr6UywI,1063
36
+ execsql/db/access.py,sha256=O19alGOCjhBtVFaCnGN8zwVZ2wR57h1spizAF5dhUOg,21207
37
+ execsql/db/base.py,sha256=K_NJ5Tpn68qUyc5epnT3V3jUxkv1lCAXF0rGul-8otc,42264
38
+ execsql/db/dsn.py,sha256=a3_q7oucR37CMCKNO9S5ShsB_JDwmZY4d8QI1aqeFSU,6893
39
+ execsql/db/duckdb.py,sha256=h62nEuwNl8Bz8ZcJJNIAfFK1o91KXxAeh8unlwTivKc,7047
40
+ execsql/db/factory.py,sha256=cMptU3BL1Nsilo7LKLAI5RR-LiC9RTH-3UC9rHT2JLQ,5519
41
+ execsql/db/firebird.py,sha256=sU2XqYV0U6hTgUuetFfMdlZwlBlXgPVXXn_wi1XCy1U,10196
42
+ execsql/db/mysql.py,sha256=ooH4uNomr_VatEldJ70ynsOth4_STa8-761O33NfndY,21496
43
+ execsql/db/oracle.py,sha256=-n8CgbZd9zaGnbfQQmWAdFgPA_B-HkLNC6yvENbzqyc,13720
44
+ execsql/db/postgres.py,sha256=QXL34lyg6w7zj2S-1AAlyswPGjbXZz7GZS2qFB4pANY,23604
45
+ execsql/db/sqlite.py,sha256=fEpNPkfJGnqm1Wx_rZK5SEqaWsBqLy85Jxk_5NsnqgM,12109
46
+ execsql/db/sqlserver.py,sha256=e4UP2Ps0EXjVjX4TOYNslsviBF2dorx8TEvPhxb9rrY,9503
47
+ execsql/db/tiers.py,sha256=PH2sUYwlYqcANGVKXprWQKVIlM6fpWU9q74djn8oFwc,2924
48
+ execsql/debug/__init__.py,sha256=j6EGUR0dHzUhWN1mHHtf1-Lhjq3Sb1V-vmnq2Ztgj1M,178
49
+ execsql/debug/repl.py,sha256=jnwrqRac4krc1pES7Ni-j1Dyw3mJIcWl5LQQIX1GkRw,20753
50
+ execsql/exporters/__init__.py,sha256=-Cnji-OgodJV8ftcDcOyTof0kQMy9J5kKVC8GVFpc3o,670
51
+ execsql/exporters/base.py,sha256=_GoO0YqwlTxrjogxHI3CxxRR64sMgIEjRogt0KCqzoY,6402
52
+ execsql/exporters/delimited.py,sha256=N0cP_CNlb_LLCUqbHmTQsOGufWnD_ddGdeFnXPE8Erc,35606
53
+ execsql/exporters/duckdb.py,sha256=912TMdYaSVD-kb-_oebTBHQp8FvYMbWTDEDD80w5Z50,3455
54
+ execsql/exporters/feather.py,sha256=tah48aep0cQRikZcmADXUPTzFddgDUT78Ndw_l0Hsj4,4178
55
+ execsql/exporters/html.py,sha256=hwf9UHs7EZ7xiP3p9ilBIsAukXcDu-KO2dcQHVh2z30,10111
56
+ execsql/exporters/json.py,sha256=wWGT6zjvIdWlO5FmXbaEw1xOhy23f1YY6bm5uQD_NS0,4393
57
+ execsql/exporters/latex.py,sha256=bdUN3GwfENUZHgpw6bvjsjfivDgzyf-HRkYTP-eH-pM,5480
58
+ execsql/exporters/markdown.py,sha256=FuMGFxm9uZUOrdEuXT2ZZLCqmVDly7SMrFlrjP6kVEI,4827
59
+ execsql/exporters/ods.py,sha256=Ke9pfcyT6R85LWIWcMI6pzRzkKir5w_gmiptCeeuoBc,20730
60
+ execsql/exporters/parquet.py,sha256=mbCo2Wlit1lkXmyo5EyLd_ATIHSICS49bouiBT2kdAs,1121
61
+ execsql/exporters/pretty.py,sha256=SiJHBXIjoFsOHegXcdrtxYa6xSIPga1U0DQEy7pK4QY,4145
62
+ execsql/exporters/protocol.py,sha256=BxATgz0xKHbB2FpZBeNg7wZfIiCohhD1awlr3JCec0c,4372
63
+ execsql/exporters/raw.py,sha256=9o3BMt7UYmnjbP8OEzmM2kNKfwwERi2yDcuNfUoEfuQ,2530
64
+ execsql/exporters/sqlite.py,sha256=QjP_W90S-hecNqp2A-Vy58DD8MZitME1sTq4lqIS43w,3401
65
+ execsql/exporters/templates.py,sha256=nRFurwTb5nxoyHpx609FGIp3MDbX_mYQ3IvgswaEbyA,7054
66
+ execsql/exporters/values.py,sha256=UPxN5IprasBZLqM5jfUXQihjdTeBFTxbJslOEtmGOcY,3384
67
+ execsql/exporters/xls.py,sha256=iZ_N7hneyfaDKbO1XdYv6doJCFPqJWEJql5rduoNyRU,11197
68
+ execsql/exporters/xlsx.py,sha256=hmL0i-2HweXFenl5uXjM3-w2DW2Ky8DeYTcZnFzmeOU,12708
69
+ execsql/exporters/xml.py,sha256=t1PSr3i8xvTCWzd9jaxxAy5NcRpl6sqjnZALafKCqpM,2575
70
+ execsql/exporters/yaml.py,sha256=R61Rs_0rd5G3bg344L4hC84GM79IfYXaDO2tkF-BluY,3121
71
+ execsql/exporters/zip.py,sha256=0vDkXB19WzLzgCACBVNnGJYxacgBwEehQDL7Inl-rgw,5002
72
+ execsql/gui/__init__.py,sha256=oCb-cyhLZzVpWJ4WU5HbqEDBrV-lm0ytEwxemrOZyqs,2048
73
+ execsql/gui/base.py,sha256=DMTqHLlpZlGCsNKyrLlJPbaxvukC0n1YmR4aHEN3jPY,14983
74
+ execsql/gui/console.py,sha256=dzdSl4NJNvRd9VvlwRCXHvfnXjQPimyTx7NcibcUZC8,18305
75
+ execsql/gui/desktop.py,sha256=y-T317NzSs9kcA4-NZW-Dx2cZ4Bv1mPoCr7hwQCJYoo,59467
76
+ execsql/gui/tui.py,sha256=VoUtjSMopYtbtc8_-qV7YQAFvc-bS7zrZ5J2hl1cPxM,66853
77
+ execsql/importers/__init__.py,sha256=zZwdQxMaValCNqUrVdvaA7XPU3J8NmqVJ4uolNWY2iU,299
78
+ execsql/importers/base.py,sha256=ikvJ98vVCcDBqp5ZdwwHwf-6zQCO4f8uI355VCVukHo,5783
79
+ execsql/importers/csv.py,sha256=4zIv-K02fuHKxnbqS5iTvLOeVnfssLYYJrs0Gh3qv7I,5021
80
+ execsql/importers/feather.py,sha256=g2B69d2uv9vmnXcmjFyTVsMP40LYEzFYkhk3gD26mGw,1900
81
+ execsql/importers/json.py,sha256=fL47h09Zzx4Qw3TTQRQJbdPdD3qVMiU-kK9C7j1IQzg,5534
82
+ execsql/importers/ods.py,sha256=VsxIkr7opBamB0PbSFmMWt7G11w8lLUH1k_kRwz28zw,2847
83
+ execsql/importers/xls.py,sha256=D7Ul42gIRYZZC5QxD35ft_wmqQv2f1jNMSbnqlzV0ZA,3851
84
+ execsql/metacommands/__init__.py,sha256=H_D1Z5kN6zfYtxatejQhQhQV1tlVZ7rcdFZ_9uhXu2Q,13010
85
+ execsql/metacommands/conditions.py,sha256=4DsMXr6vnfbtcKqgnxYV4UK4NmAfE_hpfgkFjlH0bjo,29844
86
+ execsql/metacommands/connect.py,sha256=SPk4h9yiUgTD3w1gNOT_oYx8VShggsO357by5Hoy6OI,15344
87
+ execsql/metacommands/control.py,sha256=9swW6X8jXDUpu5cHiAnQVx1ahFyWxlx7mjg4dqYfX5Y,9355
88
+ execsql/metacommands/data.py,sha256=gaKJ0xA-maFnezT9TJv-q4G4Etn-kJnF7vZWZoG4UqI,12305
89
+ execsql/metacommands/debug.py,sha256=XyMoKnQKyYHdzcfGUGFEaecIlDOi1LMirwbbpbiRO7I,12922
90
+ execsql/metacommands/dispatch.py,sha256=t7x0xWHJA0PeCrYf7jYeSMJgf0yxcZ_xh-_YAtBPHLw,87192
91
+ execsql/metacommands/effects.py,sha256=cRSo48R4nRJzkRR3OGHLhJzofcdAaNIobaxmn1N9TGw,3813
92
+ execsql/metacommands/io.py,sha256=vlGBje5sgnqeilooMdhJDgSRIhysHy5_7LrKtik9Xjs,3011
93
+ execsql/metacommands/io_export.py,sha256=YamxQwURRjZRpCG2z8W3QurncCmEmAyLFqDkxtHo5dY,14434
94
+ execsql/metacommands/io_fileops.py,sha256=uXP5PdHOc8yi1rPo8PbWmvAx_yp69ozCkK_SHYoL0NE,12646
95
+ execsql/metacommands/io_import.py,sha256=gJICeK1MOfG6D73I7m8zBCalvr_ZJzD-QBKOpKYeYXo,15684
96
+ execsql/metacommands/io_write.py,sha256=yXDkMbQ3haDARVATVUYZprakyJHSyk1NywdKkFLpQ14,12783
97
+ execsql/metacommands/prompt.py,sha256=PlXhtBYCembG_1SSCrwpQR10mGF3_jXodu4aCSHlSCM,37950
98
+ execsql/metacommands/script_ext.py,sha256=CsUbyq8QFPnnvfX_k7fPlYNvvRw1xLmAWER4Exo5pwc,3658
99
+ execsql/metacommands/system.py,sha256=6X2qmMpGUHDzyEluEotXt0Ks9jKQTpc2x0eoYNgN12M,7997
100
+ execsql/metacommands/upsert.py,sha256=qez06e41ivxsLlB-Af91fkVzD6XhHVxpvv5sLMPul6M,20429
101
+ execsql/script/__init__.py,sha256=I7hVO6wm3iOocxRRGEcFHhjdtL1moKbUYLB-wKbjPdk,3267
102
+ execsql/script/ast.py,sha256=WqCpFxhxrT2IIGvbArTp3QS0BZryRZAb7IL4ri3I6xg,20591
103
+ execsql/script/control.py,sha256=WqLy-HLPqHG3vEzYpKMiIJsD7LpORjyQuUWzFzcGz4w,2327
104
+ execsql/script/engine.py,sha256=wjXKwPIis-T2Y8rLnRXQ_ymUFdXff1MlI2IIYmxihKo,18448
105
+ execsql/script/executor.py,sha256=lvMFjKe2h4fafhKNiuoXAYpDe8pLHHuYMiwPkLYJe9I,41527
106
+ execsql/script/parser.py,sha256=z73v6t6ZbPUKkRu-UOIL6q1cCRDeoRvG2aXXC8NpMvE,37162
107
+ execsql/script/variables.py,sha256=_hklqCeqPbdOT646Yg3ZcpmPke38_8bukMG3eZMTKZw,16384
108
+ execsql/utils/__init__.py,sha256=0uR6JwVJQRX3vceByNBduCAf5dd5assKjeqJUWvpZoA,278
109
+ execsql/utils/auth.py,sha256=BV7VtLM-ID8jNB42vx7GEz8yb3Z1Qs9xCr4cUMqL864,14539
110
+ execsql/utils/color.py,sha256=goQqhnLg8yERpFolQikA9zYSVcuAm0unKTJiWTbQm-U,634
111
+ execsql/utils/crypto.py,sha256=KlT6lPk-v0-qQQfAMfWvT9Q6kRdCD3_5JVaLajNocKM,3051
112
+ execsql/utils/datetime.py,sha256=rqtGrc0t37V00mvirJ8wJKHmDtDSYzTg_UgCyIBzv9E,5583
113
+ execsql/utils/errors.py,sha256=JqV5mDKJnsYF8Uam6iLE3HNqkL1cUiiP8ExHpBBFnC8,11630
114
+ execsql/utils/fileio.py,sha256=ADp8Iltf_zfv0PL0VRdO0BDIHkVpKVVh59_5zmkTfkw,47564
115
+ execsql/utils/gui.py,sha256=1iiupP9AsLdR6ST0gcLE143o6y6YGUFlNv_pdVPxfCE,23943
116
+ execsql/utils/mail.py,sha256=4RiKtXa_sqij_YGdYmjQ3PG0u3G9qjs-9caQwBErtBw,7278
117
+ execsql/utils/numeric.py,sha256=_7bi5R6pHjMCkOPne_Sh5nUj-B8e9g6WFmjptFHaunA,1573
118
+ execsql/utils/regex.py,sha256=B3mSBhRRx_DpUnX_t9IoYd05KvWAebiLMCZqQAmEwcw,3885
119
+ execsql/utils/strings.py,sha256=UQNjpRCEFa1UO6feU-M-9e24wWAvizs_iu_4fFusLxo,8516
120
+ execsql/utils/timer.py,sha256=eDYf5VzCNFk7oo90InJucUm3XcBdhYMogjZMqeg9xzc,1899
121
+ execsql2-2.25.0.data/data/execsql2_extras/README.md,sha256=7SG9FbBpdqkgQTTt1aBTbCgymBZyV0rLrQMQrendzxs,5032
122
+ execsql2-2.25.0.data/data/execsql2_extras/config_settings.sqlite,sha256=aY5cxR7Q7J6zJ4bC9lu5mHUrhy211Cq3MNKPQVCt02E,20480
123
+ execsql2-2.25.0.data/data/execsql2_extras/example_config_prompt.sql,sha256=2e8KzzVWhho8KxYVHETSVmZdhW7wodioDsqBLSL6m4s,7487
124
+ execsql2-2.25.0.data/data/execsql2_extras/make_config_db.sql,sha256=WwyC6dK-Eh5CAVppiBCDHqiI1_wEI9U95Ytpr4lsZkg,8726
125
+ execsql2-2.25.0.data/data/execsql2_extras/md_compare.sql,sha256=qYYVAjSeHZzjszxV3Bv6bg8Ckbq2kMHl87_gh4sywMU,24140
126
+ execsql2-2.25.0.data/data/execsql2_extras/md_glossary.sql,sha256=hkZ2Onn57LAKKsuXxzhR8tPtcWXkmWEQkwPE58-Tm2k,10796
127
+ execsql2-2.25.0.data/data/execsql2_extras/md_upsert.sql,sha256=_CAK4BzEboRXTNy03SJR-oOjcEdSNMuRBPL6noWUptY,112560
128
+ execsql2-2.25.0.data/data/execsql2_extras/pg_compare.sql,sha256=1zJd4hVUKHR0tncc2qTBC9B4qVV4Us2ITkJpsjN3tMw,24352
129
+ execsql2-2.25.0.data/data/execsql2_extras/pg_glossary.sql,sha256=IKuwna-_8b20ljSkXZruuiQigrCpo7ueQdUqd1MXiuI,9908
130
+ execsql2-2.25.0.data/data/execsql2_extras/pg_upsert.sql,sha256=HpPJtTHvpEjQy03j-3iPxDEOHMRkudOg7O4D4YR38UI,108315
131
+ execsql2-2.25.0.data/data/execsql2_extras/script_template.sql,sha256=2J35ddZPguJ-vwTsz83wErv0jiWUyJcdW_JM0mNKDXA,11155
132
+ execsql2-2.25.0.data/data/execsql2_extras/ss_compare.sql,sha256=j1qVNUPXQsEU7-DoVgDJCGcE0EuIl7whLBT3fgeiMAo,24833
133
+ execsql2-2.25.0.data/data/execsql2_extras/ss_glossary.sql,sha256=2gLxv34xzKt0vy7hSzJH7a9JiMC3ETrv9MofxQwAibU,13065
134
+ execsql2-2.25.0.data/data/execsql2_extras/ss_upsert.sql,sha256=G_8rQ0VzuKIZHWs24O_WrfzpC5S27R1JsL-bFBR3SUQ,117730
135
+ execsql2-2.25.0.dist-info/METADATA,sha256=8-U51LILehuLSC5E0zYP_tYCuJEtff0DkTsMSQ4RvHg,25703
136
+ execsql2-2.25.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
137
+ execsql2-2.25.0.dist-info/entry_points.txt,sha256=jOf53-4IVGjEZeNYUOF1GGaiPNfVZcNKxP3vDFKEyMA,58
138
+ execsql2-2.25.0.dist-info/licenses/LICENSE.txt,sha256=LBdhuxejF8_bLCHZ2kWfmDXpDGUu914Gbd6_3JjCRe0,676
139
+ execsql2-2.25.0.dist-info/licenses/NOTICE,sha256=McYzgxYav3U1OaVsY4Su1sfBrfmplpRdA9b6-gCDQCg,342
140
+ execsql2-2.25.0.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ execsql = execsql.cli.dispatch:dispatch