execsql2 2.26.0__py3-none-any.whl → 2.28.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 (62) hide show
  1. execsql/cli/commands/__init__.py +1 -0
  2. execsql/cli/commands/format.py +2 -1
  3. execsql/cli/commands/listing.py +32 -5
  4. execsql/cli/commands/lsp.py +64 -0
  5. execsql/cli/dispatch.py +1 -1
  6. execsql/cli/help.py +44 -96
  7. execsql/cli/lint.py +58 -22
  8. execsql/db/access.py +4 -4
  9. execsql/db/base.py +17 -3
  10. execsql/db/dsn.py +2 -2
  11. execsql/db/duckdb.py +14 -2
  12. execsql/db/firebird.py +2 -3
  13. execsql/db/mysql.py +2 -2
  14. execsql/db/oracle.py +5 -5
  15. execsql/db/postgres.py +2 -3
  16. execsql/db/sqlserver.py +2 -2
  17. execsql/exporters/duckdb.py +2 -2
  18. execsql/exporters/ods.py +2 -2
  19. execsql/exporters/templates.py +2 -2
  20. execsql/exporters/xls.py +3 -3
  21. execsql/exporters/xlsx.py +2 -2
  22. execsql/exporters/yaml.py +3 -4
  23. execsql/format.py +71 -21
  24. execsql/importers/base.py +6 -10
  25. execsql/lsp/__init__.py +6 -0
  26. execsql/lsp/code_actions.py +217 -0
  27. execsql/lsp/completion.py +203 -0
  28. execsql/lsp/diagnostics.py +66 -0
  29. execsql/lsp/document.py +210 -0
  30. execsql/lsp/folding.py +59 -0
  31. execsql/lsp/formatting.py +37 -0
  32. execsql/lsp/hover.py +119 -0
  33. execsql/lsp/navigation.py +169 -0
  34. execsql/lsp/rename.py +91 -0
  35. execsql/lsp/server.py +358 -0
  36. execsql/metacommands/debug.py +2 -2
  37. execsql/metacommands/dispatch.py +7 -0
  38. execsql/metacommands/io_fileops.py +6 -6
  39. execsql/metacommands/reference.py +1404 -0
  40. execsql/metacommands/upsert.py +2 -5
  41. execsql/models.py +11 -0
  42. execsql/utils/errors.py +16 -0
  43. {execsql2-2.26.0.dist-info → execsql2-2.28.0.dist-info}/METADATA +34 -18
  44. {execsql2-2.26.0.dist-info → execsql2-2.28.0.dist-info}/RECORD +62 -49
  45. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/README.md +0 -0
  46. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  47. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  48. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
  49. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/md_compare.sql +0 -0
  50. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
  51. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
  52. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
  53. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  54. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  55. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/script_template.sql +0 -0
  56. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
  57. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  58. {execsql2-2.26.0.data → execsql2-2.28.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  59. {execsql2-2.26.0.dist-info → execsql2-2.28.0.dist-info}/WHEEL +0 -0
  60. {execsql2-2.26.0.dist-info → execsql2-2.28.0.dist-info}/entry_points.txt +0 -0
  61. {execsql2-2.26.0.dist-info → execsql2-2.28.0.dist-info}/licenses/LICENSE.txt +0 -0
  62. {execsql2-2.26.0.dist-info → execsql2-2.28.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,1404 @@
1
+ """Readable syntax for every metacommand, conditional test and system variable.
2
+
3
+ The dispatch table recognizes metacommands with regular expressions, which
4
+ tell a reader nothing. This module holds what a person should see instead:
5
+ the syntax lines from the reference documentation, a one-line summary and a
6
+ link to the section that explains it. Editors (``execsql lsp``) and
7
+ ``execsql list metacommands`` read it; ``tests/test_reference.py`` keeps it in
8
+ step with the dispatch table, the conditional tests and the docs.
9
+
10
+ Syntax lines use the documentation's notation: ``<name>`` is a value to fill
11
+ in, ``[...]`` is optional and ``A|B`` is a choice.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import re
17
+ from dataclasses import dataclass
18
+
19
+ __all__ = [
20
+ "DOCS_URL",
21
+ "Condition",
22
+ "Metacommand",
23
+ "SystemVariable",
24
+ "condition",
25
+ "conditions",
26
+ "metacommand",
27
+ "metacommands",
28
+ "system_variable",
29
+ "system_variables",
30
+ "to_snippet",
31
+ ]
32
+
33
+ DOCS_URL = "https://execsql2.readthedocs.io/en/latest/reference/"
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class Metacommand:
38
+ """A metacommand keyword as a reader sees it."""
39
+
40
+ keyword: str
41
+ category: str
42
+ forms: tuple[str, ...]
43
+ summary: str
44
+ anchor: str
45
+
46
+ @property
47
+ def url(self) -> str:
48
+ return f"{DOCS_URL}metacommands/#{self.anchor}"
49
+
50
+ @property
51
+ def snippet(self) -> str:
52
+ return to_snippet(self.forms[0])
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class Condition:
57
+ """A conditional test (``HASROWS(...)``) or operator usable in IF, LOOP and ASSERT."""
58
+
59
+ name: str
60
+ forms: tuple[str, ...]
61
+ summary: str
62
+ anchor: str
63
+
64
+ @property
65
+ def url(self) -> str:
66
+ return f"{DOCS_URL}metacommands/#{self.anchor}"
67
+
68
+ @property
69
+ def snippet(self) -> str:
70
+ return to_snippet(self.forms[0])
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class SystemVariable:
75
+ """A variable execsql defines and maintains (``$CURRENT_DATE``, ``$ARG_x``, ...)."""
76
+
77
+ name: str
78
+ summary: str
79
+ doc: str # "<page>.md#<anchor>" under the reference docs
80
+
81
+ @property
82
+ def url(self) -> str:
83
+ page, _, anchor = self.doc.partition("#")
84
+ return f"{DOCS_URL}{page.removesuffix('.md')}/#{anchor}"
85
+
86
+
87
+ def metacommands() -> tuple[Metacommand, ...]:
88
+ return _METACOMMANDS
89
+
90
+
91
+ def metacommand(keyword: str) -> Metacommand | None:
92
+ """The entry for *keyword* (any case), or ``None``."""
93
+ return _BY_KEYWORD.get(" ".join(keyword.upper().split()))
94
+
95
+
96
+ def conditions() -> tuple[Condition, ...]:
97
+ return _CONDITIONS
98
+
99
+
100
+ def condition(name: str) -> Condition | None:
101
+ return _BY_CONDITION.get(name.upper())
102
+
103
+
104
+ def system_variables() -> tuple[SystemVariable, ...]:
105
+ return _SYSTEM_VARIABLES
106
+
107
+
108
+ def system_variable(name: str) -> SystemVariable | None:
109
+ """The entry for ``$NAME``; ``$ARG_3`` and ``$COUNTER_2`` find ``$ARG_x`` and ``$COUNTER_x``."""
110
+ name = name.upper()
111
+ if not name.startswith("$"):
112
+ name = "$" + name
113
+ return _BY_VARIABLE.get(name) or _BY_VARIABLE.get(re.sub(r"_\d+$", "_X", name))
114
+
115
+
116
+ # ---------------------------------------------------------------------------
117
+ # Snippets
118
+ # ---------------------------------------------------------------------------
119
+
120
+ _RX_OPTIONAL = re.compile(r"\s*\[[^\[\]]*\]")
121
+ _RX_TOKEN = re.compile(r"<<(\w[^<>]*)>>|<([^<>]+)>(?:\|\S+)?|\b([A-Z_]+(?:\|[A-Z_]+)+)\b")
122
+
123
+
124
+ def to_snippet(form: str) -> str:
125
+ """Turn a syntax line into an LSP snippet with a tab stop per value.
126
+
127
+ Optional parts are left out, each ``<value>`` becomes a placeholder (a
128
+ ``<<query>>`` keeps its literal ``<<`` ``>>``) and each ``A|B`` keyword
129
+ choice becomes a choice list:
130
+ ``EXPORT <table> TO <file>|stdout AS <format> [DESCRIPTION "<d>"]``
131
+ gives ``EXPORT ${1:table} TO ${2:file} AS ${3:format}``.
132
+ """
133
+ text = form
134
+ while True: # drop optional parts, innermost first
135
+ stripped = _RX_OPTIONAL.sub("", text)
136
+ if stripped == text:
137
+ break
138
+ text = stripped
139
+ text = text.replace("\\", "\\\\").replace("$", "\\$").replace("}", "\\}")
140
+ counter = 0
141
+
142
+ def placeholder(m: re.Match[str]) -> str:
143
+ nonlocal counter
144
+ counter += 1
145
+ if m.group(3):
146
+ return f"${{{counter}|{m.group(3).replace('|', ',')}|}}"
147
+ if m.group(1): # a query is written between literal << and >>
148
+ return f"<<${{{counter}:{m.group(1).strip()}}}>>"
149
+ return f"${{{counter}:{m.group(2).strip()}}}"
150
+
151
+ return _RX_TOKEN.sub(placeholder, text)
152
+
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # Data — generated once from docs/reference/metacommands.md and
156
+ # docs/reference/substitution_vars.md, then edited by hand.
157
+ # ---------------------------------------------------------------------------
158
+
159
+ _METACOMMANDS: tuple[Metacommand, ...] = (
160
+ Metacommand(
161
+ "ANDIF",
162
+ "control",
163
+ ("ANDIF(<conditional expression>)",),
164
+ "Adds a condition that must also be true for the preceding IF or ELSEIF.",
165
+ "if_cmd",
166
+ ),
167
+ Metacommand(
168
+ "APPEND SCRIPT",
169
+ "action",
170
+ ("APPEND SCRIPT <script_2> TO <script_1>",),
171
+ "Adds the statements of one SCRIPT block to the end of another (same as EXTEND SCRIPT).",
172
+ "extend-script",
173
+ ),
174
+ Metacommand(
175
+ "ASK",
176
+ "prompt",
177
+ ('ASK "<question>" SUB <match_string>',),
178
+ "Asks a yes/no question on the console and assigns the answer to a variable.",
179
+ "ask",
180
+ ),
181
+ Metacommand(
182
+ "ASSERT",
183
+ "action",
184
+ ("ASSERT <condition>", 'ASSERT <condition> "<failure message>"', "ASSERT <condition> '<failure message>'"),
185
+ "Halts the script with a message when a condition is false.",
186
+ "assert",
187
+ ),
188
+ Metacommand(
189
+ "AUTOCOMMIT",
190
+ "action",
191
+ ("AUTOCOMMIT OFF", "AUTOCOMMIT ON [WITH COMMIT|ROLLBACK]"),
192
+ "Turns automatic commit of each SQL statement on or off.",
193
+ "autocommit",
194
+ ),
195
+ Metacommand(
196
+ "BEGIN BATCH",
197
+ "block",
198
+ ("BEGIN BATCH",),
199
+ "Starts a batch: statements are committed together at END BATCH, or rolled back.",
200
+ "batch",
201
+ ),
202
+ Metacommand(
203
+ "BEGIN SCRIPT",
204
+ "block",
205
+ (
206
+ "BEGIN SCRIPT <script_name>",
207
+ "BEGIN SCRIPT <script_name> WITH PARAMETERS (param1[, param2[,..]])",
208
+ "BEGIN SCRIPT <script_name>(param1, param2=default_value)",
209
+ ),
210
+ "Starts a named block of statements that EXECUTE SCRIPT runs.",
211
+ "beginscript",
212
+ ),
213
+ Metacommand(
214
+ "BEGIN SQL",
215
+ "block",
216
+ ("BEGIN SQL",),
217
+ "Starts a block of lines run as one SQL statement (for bodies containing semicolons).",
218
+ "beginsql",
219
+ ),
220
+ Metacommand(
221
+ "BREAK",
222
+ "control",
223
+ ("BREAK",),
224
+ "Leaves the current LOOP, SCRIPT or included file immediately.",
225
+ "break",
226
+ ),
227
+ Metacommand(
228
+ "BREAKPOINT",
229
+ "action",
230
+ ("BREAKPOINT",),
231
+ "Pauses the script and opens the interactive debug prompt.",
232
+ "breakpoint",
233
+ ),
234
+ Metacommand(
235
+ "CANCEL_HALT",
236
+ "control",
237
+ ("CANCEL_HALT ON|OFF",),
238
+ "Whether a canceled dialog halts the script (ON, the default) or lets it continue.",
239
+ "cancel_halt",
240
+ ),
241
+ Metacommand("CD", "action", ("CD <directory>",), "Changes the current working directory.", "cd"),
242
+ Metacommand(
243
+ "CONFIG",
244
+ "config",
245
+ (
246
+ "CONFIG BOOLEAN_INT YES|NO",
247
+ "CONFIG BOOLEAN_WORDS YES|NO",
248
+ "CONFIG CLEAN_COLUMN_HEADERS YES|NO",
249
+ "CONFIG CONSOLE WAIT_WHEN_DONE YES|NO",
250
+ "CONFIG CONSOLE WAIT_WHEN_ERROR YES|NO",
251
+ "CONFIG CREATE_COLUMN_HEADERS YES|NO",
252
+ "CONFIG DAO_FLUSH_DELAY_SECS <seconds>",
253
+ "CONFIG DEDUP_COLUMN_HEADERS YES|NO",
254
+ "CONFIG DELETE_EMPTY_COLUMNS YES|NO",
255
+ "CONFIG EMPTY_ROWS YES|NO",
256
+ "CONFIG EMPTY_STRINGS YES|NO",
257
+ "CONFIG EXPORT_ROW_BUFFER <n>",
258
+ "CONFIG FOLD_COLUMN_HEADERS NO|LOWER|UPPER",
259
+ "CONFIG GUI_LEVEL <n>",
260
+ "CONFIG HDF5_TEXT_LEN <n>",
261
+ "CONFIG IMPORT_ROW_BUFFER <n>",
262
+ "CONFIG LOG_DATAVARS YES|NO",
263
+ "CONFIG LOG_SQL YES|NO",
264
+ "CONFIG LOG_WRITE_MESSAGES YES|NO",
265
+ "CONFIG MAKE_EXPORT_DIRS YES|NO",
266
+ "CONFIG MAX_INT <integer_value>",
267
+ "CONFIG ONLY_STRINGS YES|NO",
268
+ "CONFIG QUOTE_ALL_TEXT YES|NO",
269
+ "CONFIG REPLACE_NEWLINES YES|NO",
270
+ "CONFIG SCAN_LINES <n>",
271
+ "CONFIG SHOW_PROGRESS YES|NO",
272
+ "CONFIG TRIM_COLUMN_HEADERS NONE|BOTH|LEFT|RIGHT",
273
+ "CONFIG TRIM_STRINGS YES|NO",
274
+ "CONFIG WRITE_PREFIX <text>",
275
+ "CONFIG WRITE_PREFIX CLEAR",
276
+ "CONFIG WRITE_SUFFIX <text>",
277
+ "CONFIG WRITE_SUFFIX CLEAR",
278
+ "CONFIG WRITE_WARNINGS YES|NO",
279
+ "CONFIG ZIP_BUFFER_MB <n>",
280
+ ),
281
+ "Changes a configuration setting while the script runs.",
282
+ "config",
283
+ ),
284
+ Metacommand(
285
+ "CONNECT",
286
+ "action",
287
+ (
288
+ "CONNECT TO POSTGRESQL(SERVER=<server_name>, DB=<database_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PORT=<port_number>] [, PASSWORD=<password>] [, ENCODING=<encoding>] [, NEW]) AS <alias_name>",
289
+ "CONNECT USER TO POSTGRESQL(SERVER=<server_name>, DB=<database_name> [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
290
+ "CONNECT TO SQLITE(FILE=<database_file> [, NEW]) AS <alias_name>",
291
+ "CONNECT TO ACCESS(FILE=<database_file> [, NEED_PWD=TRUE|FALSE] [, PASSWORD=<password>] [, ENCODING=<encoding>]) AS <alias_name>",
292
+ "CONNECT TO SQLSERVER(SERVER=<server_name>, DB=<database_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PORT=<port_number>] [, PASSWORD=<password>] [, ENCODING=<encoding>]) AS <alias_name>",
293
+ "CONNECT USER TO SQLSERVER(SERVER=<server_name>, DB=<database_name> [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
294
+ "CONNECT TO MYSQL(SERVER=<server_name>, DB=<database_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PORT=<port_number>] [, PASSWORD=<password>] [, ENCODING=<encoding>]) AS <alias_name>",
295
+ "CONNECT USER TO MYSQL(SERVER=<server_name>, DB=<database_name> [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
296
+ "CONNECT TO MARIADB(SERVER=<server_name>, DB=<database_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PORT=<port_number>] [, PASSWORD=<password>] [, ENCODING=<encoding>]) AS <alias_name>",
297
+ "CONNECT USER TO MARIADB(SERVER=<server_name>, DB=<database_name> [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
298
+ "CONNECT TO DUCKDB(FILE=<database_file> [, NEW]) AS <alias_name>",
299
+ "CONNECT TO FIREBIRD(SERVER=<server_name>, DB=<database_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
300
+ "CONNECT USER TO FIREBIRD(SERVER=<server_name>, DB=<database_name> [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
301
+ "CONNECT TO ORACLE(SERVER=<server_name>, DB=<service_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PORT=<port_number>] [, PASSWORD=<password>] [, ENCODING=<encoding>]) AS <alias_name>",
302
+ "CONNECT USER TO ORACLE(SERVER=<server_name>, DB=<service_name> [, PORT=<port_number>] [, ENCODING=<encoding>]) AS <alias_name>",
303
+ "CONNECT TO DSN(DSN=<DSN_name> [, USER=<user>, NEED_PWD=TRUE|FALSE] [, PASSWORD=<password>] [, ENCODING=<encoding>]) AS <alias_name>",
304
+ ),
305
+ "Opens a connection to another database and gives it an alias for USE and COPY.",
306
+ "connect",
307
+ ),
308
+ Metacommand(
309
+ "CONSOLE",
310
+ "prompt",
311
+ (
312
+ "CONSOLE ON|OFF",
313
+ "CONSOLE HIDE|SHOW",
314
+ "CONSOLE HEIGHT <lines>",
315
+ "CONSOLE WIDTH <chars>",
316
+ 'CONSOLE STATUS "<message>"',
317
+ "CONSOLE PROGRESS <number> [/ <total>]",
318
+ "CONSOLE SAVE [APPEND] TO <filename>",
319
+ 'CONSOLE WAIT ["<message>"]',
320
+ ),
321
+ "Opens (ON) or closes (OFF) a GUI console that WRITE output goes to.",
322
+ "console",
323
+ ),
324
+ Metacommand(
325
+ "COPY",
326
+ "action",
327
+ ("COPY <table1_or_view> FROM <alias_name_1> TO [NEW|REPLACEMENT] <table2> IN <alias_name_2>",),
328
+ "Copies a table or view from one database to a table in another.",
329
+ "copy",
330
+ ),
331
+ Metacommand(
332
+ "COPY QUERY",
333
+ "action",
334
+ ("COPY QUERY <<query>> FROM <alias_name_1> TO [NEW|REPLACEMENT] <table> IN <alias_name_2>",),
335
+ "Copies the result of a query from one database to a table in another.",
336
+ "copy-query",
337
+ ),
338
+ Metacommand(
339
+ "DEBUG",
340
+ "action",
341
+ (
342
+ "DEBUG LOG [LOCAL] [USER] SUBVARS",
343
+ "DEBUG LOG CONFIG",
344
+ "DEBUG WRITE [LOCAL] [USER] SUBVARS [[APPEND] TO <filename>]",
345
+ "DEBUG WRITE CONFIG [[APPEND] TO <filename>]",
346
+ "DEBUG WRITE ODBC_DRIVERS [[APPEND] TO <filename>]",
347
+ "DEBUG WRITE METACOMMANDLIST TO <filename>",
348
+ "DEBUG WRITE COMMANDLISTSTACK",
349
+ "DEBUG WRITE IFLEVELS",
350
+ ),
351
+ "Writes internal state to the log file (`LOG` form) or to the terminal or a file (`WRITE` form).",
352
+ "debug",
353
+ ),
354
+ Metacommand(
355
+ "DISCONNECT",
356
+ "action",
357
+ ("DISCONNECT [[FROM] <alias>]",),
358
+ "Closes the current database connection, or the one with the given alias.",
359
+ "disconnect",
360
+ ),
361
+ Metacommand(
362
+ "ELSE",
363
+ "control",
364
+ ("ELSE",),
365
+ "Starts the statements run when no IF or ELSEIF condition was true.",
366
+ "if_cmd",
367
+ ),
368
+ Metacommand(
369
+ "ELSEIF",
370
+ "control",
371
+ ("ELSEIF(<conditional expression>)",),
372
+ "Tests another condition when the IF and earlier ELSEIF conditions were false.",
373
+ "if_cmd",
374
+ ),
375
+ Metacommand(
376
+ "EMAIL",
377
+ "action",
378
+ (
379
+ 'EMAIL FROM <from_address> TO <to_addresses> SUBJECT "<subject>" MESSAGE "<message_text>" [MESSAGE_FILE "<filename>"] [ATTACH_FILE "<attachment_filename>"]',
380
+ ),
381
+ "Sends an email.",
382
+ "email",
383
+ ),
384
+ Metacommand("END BATCH", "block", ("END BATCH",), "Ends a batch and commits its statements.", "batch"),
385
+ Metacommand("END SCRIPT", "block", ("END SCRIPT [script_name]",), "Ends a named SCRIPT block.", "beginscript"),
386
+ Metacommand("END SQL", "block", ("END SQL",), "Ends a BEGIN SQL block.", "beginsql"),
387
+ Metacommand("ENDIF", "control", ("ENDIF",), "Ends an IF block.", "if_cmd"),
388
+ Metacommand(
389
+ "ERROR_HALT",
390
+ "control",
391
+ ("ERROR_HALT ON|OFF",),
392
+ "Whether a SQL error halts the script (ON, the default) or lets it continue.",
393
+ "error_halt",
394
+ ),
395
+ Metacommand(
396
+ "EXECUTE SCRIPT",
397
+ "action",
398
+ (
399
+ "EXECUTE SCRIPT [IF EXISTS] <script_name>",
400
+ "EXECUTE SCRIPT [IF EXISTS] <script_name> WHILE (<conditional expression>)",
401
+ "EXECUTE SCRIPT [IF EXISTS] <script_name> UNTIL (<conditional expression>)",
402
+ "EXECUTE SCRIPT [IF EXISTS] <script_name> WITH ARGUMENTS (param1=val1 [, param2=val2 [,...]])",
403
+ "EXECUTE SCRIPT [IF EXISTS] <script_name> WITH ARGUMENTS (param1=val1 [, param2=val2 [,...]]) WHILE (<conditional_expression>)",
404
+ "EXECUTE SCRIPT [IF EXISTS] <script_name> WITH ARGUMENTS (param1=val1 [, param2=val2 [,...]]) UNTIL (<conditional_expression>)",
405
+ 'EXECUTE SCRIPT do_me_over UNTIL (EQUAL("!!", "10"))',
406
+ 'EXECUTE SCRIPT do_me_over UNTIL (EQUAL("!!loop_ctr!!", "10"))',
407
+ ),
408
+ "Runs a named SCRIPT block, optionally with arguments, WHILE or UNTIL a condition.",
409
+ "executescript",
410
+ ),
411
+ Metacommand(
412
+ "EXPORT",
413
+ "action",
414
+ (
415
+ 'EXPORT <table_or_view> [TEE] [APPEND] TO <filename>|stdout [IN ZIPFILE <zipfilename>] AS <format> [DESCRIPTION "<description>"]',
416
+ "EXPORT <table_or_view> [TEE] [APPEND] TO <filename>|stdout [IN ZIPFILE <zipfilename>] WITH TEMPLATE <template_file>",
417
+ ),
418
+ "Exports data to a file.",
419
+ "export",
420
+ ),
421
+ Metacommand(
422
+ "EXPORT QUERY",
423
+ "action",
424
+ (
425
+ 'EXPORT QUERY <<query>> [TEE] [APPEND] TO <filename>|stdout [IN ZIPFILE <zipfilename>] AS <format> [DESCRIPTION "<description>"]',
426
+ "EXPORT QUERY <<query>> [TEE] [APPEND] TO <filename>|stdout [IN ZIPFILE <zipfilename>] WITH TEMPLATE <template_file>",
427
+ ),
428
+ "Exports the result of a query to a file.",
429
+ "export-query",
430
+ ),
431
+ Metacommand(
432
+ "EXPORT_METADATA",
433
+ "action",
434
+ (
435
+ "EXPORT_METADATA [APPEND] [ALL] TO <filename>|stdout [IN ZIPFILE <zipfilename>] AS <format>",
436
+ "EXPORT_METADATA [ALL] INTO [NEW|REPLACEMENT] TABLE <table_name>",
437
+ ),
438
+ "Writes the metadata recorded for every EXPORT so far (table, file, description, date, user).",
439
+ "export_metadata",
440
+ ),
441
+ Metacommand(
442
+ "EXTEND SCRIPT",
443
+ "action",
444
+ (
445
+ "EXTEND SCRIPT <script> WITH SQL <sql_statement>",
446
+ "EXTEND SCRIPT <script> WITH METACOMMAND <metacommand>",
447
+ "EXTEND SCRIPT <script_1> WITH SCRIPT <script_2>",
448
+ ),
449
+ "Adds statements to the end of an existing SCRIPT block.",
450
+ "extend-script",
451
+ ),
452
+ Metacommand(
453
+ "HALT",
454
+ "control",
455
+ ('HALT [[MESSAGE] "<error_message>" [TEE TO <outfile>]] [DISPLAY <table_or_view>] [EXIT_STATUS <n>]',),
456
+ "Stops the script, optionally with a message and exit status.",
457
+ "halt",
458
+ ),
459
+ Metacommand(
460
+ "IF",
461
+ "control",
462
+ ("IF(<conditional expression>)", "IF(<conditional expression>) <SQL statements and metacommands>"),
463
+ "Runs the statements up to ELSEIF, ELSE or ENDIF only when a condition is true.",
464
+ "if_cmd",
465
+ ),
466
+ Metacommand(
467
+ "IMPORT",
468
+ "action",
469
+ (
470
+ "IMPORT TO [NEW|REPLACEMENT] <table_name> FROM <file_name> [WITH [QUOTE <quote_char> DELIMITER <delim_char>] [ENCODING <encoding>]] [SKIP <lines>]",
471
+ "IMPORT TO [NEW|REPLACEMENT] <table_name> FROM <file_name> SHEET <sheet_name> [SKIP <rows>]",
472
+ "IMPORT TO [NEW|REPLACEMENT] TABLES IN [SCHEMA] <schema_name> FROM <file_name> SHEETS MATCHING <regular_expression> [SKIP <rows>]",
473
+ "IMPORT TO [NEW|REPLACEMENT] <table_name> FROM EXCEL <file_name> SHEET <sheet_name> [SKIP <rows>] [ENCODING <encoding>]",
474
+ "IMPORT TO [NEW|REPLACEMENT] TABLES IN [SCHEMA] <schema_name> FROM EXCEL <file_name> SHEETS MATCHING <regular_expression> [SKIP <rows>] [ENCODING <encoding>]",
475
+ "IMPORT TO [NEW|REPLACEMENT] <table_name> FROM PARQUET <file_name>",
476
+ "IMPORT TO [NEW|REPLACEMENT] <table_name> FROM FEATHER <file_name>",
477
+ "IMPORT TO [NEW|REPLACEMENT] <table_name> FROM JSON <file_name>",
478
+ ),
479
+ "Imports tabular data from a file into a new or existing database table.",
480
+ "import",
481
+ ),
482
+ Metacommand(
483
+ "IMPORT_FILE",
484
+ "action",
485
+ ("IMPORT_FILE TO TABLE <table_name> COLUMN <column_name> FROM <file_name>",),
486
+ "Imports an entire file into a single column, on a new row, of an existing database table.",
487
+ "import_file",
488
+ ),
489
+ Metacommand(
490
+ "INCLUDE",
491
+ "action",
492
+ ("INCLUDE [IF EXISTS] <filename>",),
493
+ "Runs the statements and metacommands of another script file here.",
494
+ "include",
495
+ ),
496
+ Metacommand("LOG", "action", ('LOG "<message>"',), "Writes the specified message to execsql's log file.", "log"),
497
+ Metacommand(
498
+ "LOOP",
499
+ "control",
500
+ (
501
+ "LOOP WHILE (<conditional expression>)",
502
+ "LOOP UNTIL (<conditional expression>)",
503
+ 'LOOP UNTIL (EQUAL("!!", "10"))',
504
+ ),
505
+ "Repeats the statements up to END LOOP while, or until, a condition is true.",
506
+ "loop",
507
+ ),
508
+ Metacommand(
509
+ "MAX_INT",
510
+ "action",
511
+ ("MAX_INT <integer_value>",),
512
+ "Sets the largest value IMPORT and COPY treat as integer rather than bigint (same as CONFIG MAX_INT).",
513
+ "config",
514
+ ),
515
+ Metacommand(
516
+ "METACOMMAND_ERROR_HALT",
517
+ "control",
518
+ ("METACOMMAND_ERROR_HALT ON|OFF",),
519
+ "Whether a metacommand error halts the script (ON, the default) or lets it continue.",
520
+ "metacommanderrorhalt",
521
+ ),
522
+ Metacommand(
523
+ "ON CANCEL_HALT",
524
+ "config",
525
+ (
526
+ 'ON CANCEL_HALT EMAIL FROM <from_address> TO <to_addresses> SUBJECT "<subject>" MESSAGE "<message_text>" [MESSAGE_FILE "<filename>"] [ATTACH_FILE "<attachment_filename>"]',
527
+ "ON CANCEL_HALT EMAIL CLEAR",
528
+ ),
529
+ "Sends an email, writes a message or runs a script when the user cancels the script at a prompt.",
530
+ "on-cancel_halt-email",
531
+ ),
532
+ Metacommand(
533
+ "ON ERROR_HALT",
534
+ "config",
535
+ (
536
+ 'ON ERROR_HALT EMAIL FROM <from_address> TO <to_addresses> SUBJECT "<subject>" MESSAGE "<message_text>" [MESSAGE_FILE "<filename>"] [ATTACH_FILE "<attachment_filename>"]',
537
+ "ON ERROR_HALT EMAIL CLEAR",
538
+ ),
539
+ "Sends an email, writes a message or runs a script when an error halts the script.",
540
+ "on-error_halt-email",
541
+ ),
542
+ Metacommand(
543
+ "ORIF",
544
+ "control",
545
+ ("ORIF(<conditional expression>)",),
546
+ "Adds an alternative condition to the preceding IF or ELSEIF.",
547
+ "if_cmd",
548
+ ),
549
+ Metacommand(
550
+ "PAUSE",
551
+ "control",
552
+ ('PAUSE "<text>" [HALT|CONTINUE AFTER <n> MINUTES|SECONDS]',),
553
+ "Displays the specified text and pauses script processing.",
554
+ "pause",
555
+ ),
556
+ Metacommand(
557
+ "PG_UPSERT",
558
+ "action",
559
+ ("PG_UPSERT FROM <staging_schema> TO <base_schema> TABLES <table1>, <table2> [options]",),
560
+ "Performs QA-checked, FK-dependency-ordered upserts from a staging schema to a base schema on PostgreSQL.",
561
+ "pg_upsert",
562
+ ),
563
+ Metacommand(
564
+ "PG_UPSERT CHECK",
565
+ "action",
566
+ ("PG_UPSERT CHECK FROM <staging_schema> TO <base_schema> TABLES <table1>, <table2> [options]",),
567
+ "Checks that staging columns exist in the base tables with compatible types; never commits.",
568
+ "pg_upsert",
569
+ ),
570
+ Metacommand(
571
+ "PG_UPSERT QA",
572
+ "action",
573
+ ("PG_UPSERT QA FROM <staging_schema> TO <base_schema> TABLES <table1>, <table2> [options]",),
574
+ "Runs every pg-upsert QA check without loading; never commits.",
575
+ "pg_upsert",
576
+ ),
577
+ Metacommand(
578
+ "PG_VACUUM",
579
+ "action",
580
+ ("PG_VACUUM <vacuum arguments>",),
581
+ "Runs VACUUM on the current PostgreSQL database.",
582
+ "pg_vacuum",
583
+ ),
584
+ Metacommand(
585
+ "PROMPT ACTION",
586
+ "prompt",
587
+ (
588
+ 'PROMPT ACTION <specification_table> MESSAGE "<text>" [DISPLAY <table_or_view>] [COMPACT <columns>] [CONTINUE] [HELP <url>]',
589
+ ),
590
+ "Shows a dialog of buttons, each running a named SCRIPT.",
591
+ "prompt_action",
592
+ ),
593
+ Metacommand(
594
+ "PROMPT ASK",
595
+ "prompt",
596
+ (
597
+ 'PROMPT ASK "<question>" SUB <match_string> [HELP <url>]',
598
+ 'PROMPT ASK "<question>" SUB <match_string> [DISPLAY <table_or_view>] [HELP <url>]',
599
+ 'PROMPT ASK "<question>" SUB <match_string> COMPARE <table1> [IN <alias1>] AND|BESIDE <table2> [IN <alias2>] KEY(<col1>[, col2[, col3...]]) [HELP <url>]',
600
+ ),
601
+ "Asks a yes/no question in a dialog and assigns the answer to a variable.",
602
+ "prompt_ask",
603
+ ),
604
+ Metacommand(
605
+ "PROMPT ASK COMPARE",
606
+ "prompt",
607
+ (
608
+ 'PROMPT ASK "<question>" SUB <match_string> COMPARE <table1> [IN <alias1>] AND|BESIDE <table2> [IN <alias2>] KEY(<col1>[, <col2>...]) [HELP <url>]',
609
+ ),
610
+ "Shows two tables side by side or stacked and asks a yes/no question about them.",
611
+ "prompt_ask",
612
+ ),
613
+ Metacommand(
614
+ "PROMPT COMPARE",
615
+ "prompt",
616
+ (
617
+ 'PROMPT COMPARE <table1> [IN <alias1>] AND|BESIDE <table2> [IN <alias2>] KEY(<col1>[, col2[, col3...]]) [HELP <url>] MESSAGE "<text>"',
618
+ ),
619
+ "Displays the two specified tables in a graphical interface.",
620
+ "prompt_compare",
621
+ ),
622
+ Metacommand(
623
+ "PROMPT CONNECT",
624
+ "prompt",
625
+ ('PROMPT [MESSAGE "<text>"] CONNECT AS <alias> [HELP <url>]',),
626
+ "Asks for connection details in a dialog and connects them under an alias.",
627
+ "prompt_connect",
628
+ ),
629
+ Metacommand(
630
+ "PROMPT CREDENTIALS",
631
+ "prompt",
632
+ ('PROMPT [MESSAGE "<text>"] CREDENTIALS <user_var> <pw_var>',),
633
+ "Asks for a user name and password in a dialog and assigns them to variables.",
634
+ "prompt-credentials",
635
+ ),
636
+ Metacommand(
637
+ "PROMPT DIRECTORY",
638
+ "prompt",
639
+ ("PROMPT DIRECTORY SUB <match_string> [FROM <starting_dir>]",),
640
+ "Asks for an existing directory in a dialog and assigns its full path to a variable.",
641
+ "prompt-directory",
642
+ ),
643
+ Metacommand(
644
+ "PROMPT DISPLAY",
645
+ "prompt",
646
+ ('PROMPT DISPLAY <table_or_view_name> MESSAGE "<text>" [HELP <url>]',),
647
+ "Shows a table or view in a window with Continue and Cancel buttons.",
648
+ "prompt",
649
+ ),
650
+ Metacommand(
651
+ "PROMPT ENTER_SUB",
652
+ "prompt",
653
+ (
654
+ 'PROMPT ENTER_SUB <match_string> [PASSWORD] MESSAGE "<text>" [DISPLAY <table_or_view>] [TYPE INT|FLOAT|BOOL|IDENT] [LCASE|UCASE] [INITIALLY "<text>"] [HELP <url>]',
655
+ ),
656
+ "Asks for a value in a dialog and assigns it to a variable.",
657
+ "prompt_enter",
658
+ ),
659
+ Metacommand(
660
+ "PROMPT ENTRY_FORM",
661
+ "prompt",
662
+ ('PROMPT ENTRY_FORM <specification_table> [HELP <url>] MESSAGE "<text>" [DISPLAY <table_or_view>]',),
663
+ "Shows a data entry form defined by a table and assigns the entries to variables.",
664
+ "prompt-entry_form",
665
+ ),
666
+ Metacommand(
667
+ "PROMPT MAP",
668
+ "prompt",
669
+ (
670
+ 'PROMPT MESSAGE "<text>" MAP <table_or_view> LAT <lat_col> LON <lon_col> [LABEL <label_col>] [COLOR <color_col>] [SYMBOL <symbol_col>]',
671
+ ),
672
+ "Opens a dialog showing the rows of the specified table or view as points on an interactive map.",
673
+ "prompt_map",
674
+ ),
675
+ Metacommand(
676
+ "PROMPT MESSAGE",
677
+ "prompt",
678
+ ('PROMPT [MESSAGE] "<text>"',),
679
+ "Displays the specified text in a dialog box with a single 'Close' button.",
680
+ "prompt-message",
681
+ ),
682
+ Metacommand(
683
+ "PROMPT OPENFILE",
684
+ "prompt",
685
+ ("PROMPT OPENFILE SUB <filepath_var> [filename_var] [path_var] [ext_var] [basefn_var] [FROM <starting_dir>]",),
686
+ "Asks for an existing file in a dialog and assigns its full path to a variable.",
687
+ "prompt-openfile",
688
+ ),
689
+ Metacommand(
690
+ "PROMPT PAUSE",
691
+ "prompt",
692
+ ('PROMPT PAUSE "<text>" [HALT|CONTINUE AFTER <n> MINUTES|SECONDS]',),
693
+ "Displays the specified text in a GUI dialog and pauses script processing.",
694
+ "prompt-pause",
695
+ ),
696
+ Metacommand(
697
+ "PROMPT SAVEFILE",
698
+ "prompt",
699
+ ("PROMPT SAVEFILE SUB <filepath_var> [filename_var] [path_var] [ext_var] [basefn_var] [FROM <starting_dir>]",),
700
+ "Asks for a file name to save to in a dialog and assigns its full path to a variable.",
701
+ "prompt-savefile",
702
+ ),
703
+ Metacommand(
704
+ "PROMPT SELECT_ROWS",
705
+ "prompt",
706
+ ('PROMPT SELECT_ROWS FROM <table1> INTO <table2> [HELP <url>] MESSAGE "<prompt_text>"',),
707
+ "Shows two tables and lets the user copy rows from the first into the second.",
708
+ "prompt-select_rows",
709
+ ),
710
+ Metacommand(
711
+ "PROMPT SELECT_SUB",
712
+ "prompt",
713
+ ('PROMPT SELECT_SUB <table_or_view> MESSAGE "<prompt_text>" [CONTINUE] [HELP <url>]',),
714
+ "Shows a table, lets the user pick one row, and assigns it to @column variables.",
715
+ "prompt_selsub",
716
+ ),
717
+ Metacommand(
718
+ "RESET COUNTER",
719
+ "action",
720
+ ("RESET COUNTER <counter_no>", "RESET COUNTERS"),
721
+ "Resets a counter variable so its next reference returns 1.",
722
+ "reset-counter",
723
+ ),
724
+ Metacommand(
725
+ "RESET DIALOG_CANCELED",
726
+ "action",
727
+ ("RESET DIALOG_CANCELED",),
728
+ "Sets the internal 'dialog canceled' flag to False.",
729
+ "reset-dialog_canceled",
730
+ ),
731
+ Metacommand("RM_FILE", "action", ("RM_FILE <file_name>",), "Deletes the specified file.", "rm_file"),
732
+ Metacommand(
733
+ "RM_SUB",
734
+ "action",
735
+ ("RM_SUB <match_string>",),
736
+ "Deletes the specified user-created substitution variable.",
737
+ "rm_sub",
738
+ ),
739
+ Metacommand(
740
+ "ROLLBACK BATCH",
741
+ "block",
742
+ ("ROLLBACK [BATCH]",),
743
+ "Rolls back the statements of the current batch.",
744
+ "batch",
745
+ ),
746
+ Metacommand(
747
+ "RUN",
748
+ "action",
749
+ ("RUN <procedure_name>",),
750
+ "Runs a stored procedure or function (same as EXECUTE).",
751
+ "execute",
752
+ ),
753
+ Metacommand(
754
+ "SELECT_SUB",
755
+ "action",
756
+ ("SELECT_SUB <table_or_view>",),
757
+ "Assigns the first row of a table or view to @column variables.",
758
+ "select_sub",
759
+ ),
760
+ Metacommand(
761
+ "SERVE",
762
+ "action",
763
+ ("SERVE <filename> AS <format>",),
764
+ "Copies the specified file to stdout with Content-Type and Content-Disposition headers.",
765
+ "serve",
766
+ ),
767
+ Metacommand(
768
+ "SET COUNTER",
769
+ "action",
770
+ ("SET COUNTER <counter_no> TO <numeric_expression>",),
771
+ "Assigns the value of the specified numeric expression to the counter.",
772
+ "set-counter",
773
+ ),
774
+ Metacommand(
775
+ "SHOW SCRIPTS",
776
+ "action",
777
+ ("SHOW SCRIPTS [<name>]",),
778
+ "Lists the SCRIPT blocks defined so far, or the details of one.",
779
+ "show_scripts",
780
+ ),
781
+ Metacommand(
782
+ "SUB",
783
+ "action",
784
+ ("SUB <match_string> <replacement_string>",),
785
+ "Defines a substitution variable.",
786
+ "subcmd",
787
+ ),
788
+ Metacommand(
789
+ "SUBDATA",
790
+ "action",
791
+ ("SUBDATA <match_string> <table_or_view_name>",),
792
+ "Assigns the first value of a table or view to a substitution variable.",
793
+ "subdata",
794
+ ),
795
+ Metacommand(
796
+ "SUB_ADD",
797
+ "action",
798
+ ("SUB_ADD <match_string> <numeric_expression>",),
799
+ "Adds a numeric value to a substitution variable.",
800
+ "sub_add",
801
+ ),
802
+ Metacommand(
803
+ "SUB_APPEND",
804
+ "action",
805
+ ("SUB_APPEND <match_string> <new_line>",),
806
+ "Appends a line of text to a substitution variable.",
807
+ "sub_append",
808
+ ),
809
+ Metacommand(
810
+ "SUB_DECRYPT",
811
+ "action",
812
+ ("SUB_DECRYPT <sub_var_name> <encrypted_text>",),
813
+ "Creates a substitution variable containing an unencrypted version of the given encrypted_text.",
814
+ "sub_decrypt",
815
+ ),
816
+ Metacommand(
817
+ "SUB_EMPTY",
818
+ "action",
819
+ ("SUB_EMPTY <match_string>",),
820
+ "Defines a substitution variable containing an empty string.",
821
+ "sub_empty",
822
+ ),
823
+ Metacommand(
824
+ "SUB_ENCRYPT",
825
+ "action",
826
+ ("SUB_ENCRYPT <sub_var_name> <plaintext>",),
827
+ "Creates a substitution variable containing an encrypted version of the given plaintext.",
828
+ "sub_encrypt",
829
+ ),
830
+ Metacommand(
831
+ "SUB_INI",
832
+ "action",
833
+ ("SUB_INI [FILE] <filename> [SECTION] <section>",),
834
+ "Assigns substitution variables as defined in the specified section of an INI file.",
835
+ "sub_ini",
836
+ ),
837
+ Metacommand(
838
+ "SUB_LOCAL",
839
+ "action",
840
+ ("SUB_LOCAL <match_string> <replacement_string>",),
841
+ "Operates identically to the SUB metacommand, but always defines a local substitution variable.",
842
+ "sub_local",
843
+ ),
844
+ Metacommand(
845
+ "SUB_QUERYSTRING",
846
+ "action",
847
+ ("SUB_QUERYSTRING <query_string>",),
848
+ "Creates a substitution variable for each parameter of a URL query string.",
849
+ "sub_querystring",
850
+ ),
851
+ Metacommand(
852
+ "SUB_TEMPFILE",
853
+ "action",
854
+ ("SUB_TEMPFILE <match_string>",),
855
+ "Assigns a new temporary file name to a variable.",
856
+ "sub_tempfile",
857
+ ),
858
+ Metacommand(
859
+ "SYSTEM_CMD",
860
+ "action",
861
+ ("SYSTEM_CMD ( <operating system command line> ) [CONTINUE]",),
862
+ "Runs an operating system command.",
863
+ "system_cmd",
864
+ ),
865
+ Metacommand("TIMER", "config", ("TIMER ON|OFF",), "Starts or stops an internal timer.", "timer"),
866
+ Metacommand(
867
+ "USE",
868
+ "action",
869
+ ("USE <alias_name>",),
870
+ "Makes the database with the given alias the one later statements run against.",
871
+ "use",
872
+ ),
873
+ Metacommand(
874
+ "WAIT_UNTIL",
875
+ "control",
876
+ (
877
+ "WAIT_UNTIL <conditional_expression> HALT|CONTINUE AFTER <n> SECONDS",
878
+ 'WAIT_UNTIL EQUALS("1","0") CONTINUE AFTER 1 SECONDS',
879
+ ),
880
+ "Suspends execution of the SQL script until the specified conditional expression becomes true.",
881
+ "wait_until",
882
+ ),
883
+ Metacommand(
884
+ "WRITE",
885
+ "action",
886
+ ('WRITE "<text>" [[TEE] TO <output>]',),
887
+ "Writes the specified text to the console or a file, or both.",
888
+ "write",
889
+ ),
890
+ Metacommand(
891
+ "WRITE CREATE_TABLE",
892
+ "action",
893
+ (
894
+ 'WRITE CREATE_TABLE <table_name> FROM <file_name> [WITH QUOTE <quote_char> DELIMITER <delim_char>] [ENCODING <encoding>] [SKIP <lines>] [COMMENT "<comment_text>"] [TO <output>]',
895
+ 'WRITE CREATE_TABLE <table_name> FROM <file_name> SHEET <sheet_name> [SKIP <rows>] [COMMENT "<comment_text>"] [TO <output>]',
896
+ 'WRITE CREATE_TABLE <table_name> FROM EXCEL <file_name> SHEET <sheet_name> [SKIP <rows>] [ENCODING <encoding>] [COMMENT "<comment_text>"] [TO <output>]',
897
+ 'WRITE CREATE_TABLE <table_name> FROM <table_name> IN <alias> [COMMENT "<comment_text>"] [TO <output>]',
898
+ ),
899
+ "Writes the CREATE TABLE statement IMPORT would use for a file, without importing it.",
900
+ "write-create_table",
901
+ ),
902
+ Metacommand(
903
+ "WRITE SCRIPT",
904
+ "action",
905
+ ("WRITE SCRIPT <script_name> [[APPEND] TO <output_file>]",),
906
+ "Writes the text of a SCRIPT block.",
907
+ "write-script",
908
+ ),
909
+ Metacommand(
910
+ "ZIP",
911
+ "action",
912
+ ("ZIP <filename> [APPEND] TO ZIPFILE <zipfilename>",),
913
+ "Adds a file to a zip file.",
914
+ "zip",
915
+ ),
916
+ )
917
+
918
+ _CONDITIONS: tuple[Condition, ...] = (
919
+ Condition(
920
+ "ALIAS_DEFINED",
921
+ ("ALIAS_DEFINED(<alias>)",),
922
+ "Evaluates whether a database connection has been made using the specified alias.",
923
+ "alias_defined",
924
+ ),
925
+ Condition(
926
+ "COLUMN_EXISTS",
927
+ ("COLUMN_EXISTS(<column_name> IN <table_name>)",),
928
+ "Evaluates whether there is a column of the given name in the specified database table.",
929
+ "column_exists",
930
+ ),
931
+ Condition("CONSOLE_ON", ("CONSOLE_ON",), "Evaluates whether or not execsql's CONSOLE is on.", "console_on"),
932
+ Condition(
933
+ "CONTAINS",
934
+ ('CONTAINS("<string1>", "<string2>" [, I])',),
935
+ "Evaluates whether string2 is contained within string1.",
936
+ "contains",
937
+ ),
938
+ Condition(
939
+ "DATABASE_NAME",
940
+ ("DATABASE_NAME(<database_name>)",),
941
+ "Evaluates whether the current database name matches the one specified.",
942
+ "database_name",
943
+ ),
944
+ Condition("DBMS", ("DBMS(<dbms_name>)",), "Evaluates whether the current DBMS matches the one specified.", "dbms"),
945
+ Condition(
946
+ "DIALOG_CANCELED",
947
+ ("DIALOG_CANCELED()",),
948
+ "Whether the last dialog was canceled (Cancel, Escape or closing the window).",
949
+ "dialog_canceled",
950
+ ),
951
+ Condition(
952
+ "DIRECTORY_EXISTS",
953
+ ("DIRECTORY_EXISTS(<directory_name>)",),
954
+ "Evaluates whether there is an existing directory with the given name.",
955
+ "directory_exists",
956
+ ),
957
+ Condition(
958
+ "ENDS_WITH",
959
+ ('ENDS_WITH("<string1>", "<string2>" [, I])',),
960
+ "Evaluates whether string1 ends with string2.",
961
+ "ends_with",
962
+ ),
963
+ Condition("EQUAL", ('EQUAL("<string_1>", "<string_2>")',), "Evaluates whether the two values are equal.", "equals"),
964
+ Condition(
965
+ "FILE_EXISTS",
966
+ ("FILE_EXISTS(<filename>)",),
967
+ "Evaluates whether there is a disk file of the given name.",
968
+ "file_exists",
969
+ ),
970
+ Condition(
971
+ "HASROWS",
972
+ ("HASROWS(<table_or_view>)",),
973
+ "Evaluates whether the specified table or view has a non-zero number of rows.",
974
+ "hasrows",
975
+ ),
976
+ Condition(
977
+ "IDENTICAL",
978
+ ('IDENTICAL("<string_1>", "<string_2>")',),
979
+ "Evaluates whether the two quoted strings are exactly identical.",
980
+ "identical",
981
+ ),
982
+ Condition(
983
+ "IS_FALSE",
984
+ ("IS_FALSE(<value>)",),
985
+ "Evaluates whether the value represents False (the inverse of IS_TRUE).",
986
+ "is_false",
987
+ ),
988
+ Condition(
989
+ "IS_GT",
990
+ ("IS_GT(<value1>, <value2>)",),
991
+ "Evaluates whether or not the first of the specified values is greater than the second value.",
992
+ "is_gt",
993
+ ),
994
+ Condition(
995
+ "IS_GTE",
996
+ ("IS_GTE(<value1>, <value2>)",),
997
+ "Evaluates whether or not the first of the specified values is greater than or equal to the second value.",
998
+ "is_gte",
999
+ ),
1000
+ Condition(
1001
+ "IS_NULL",
1002
+ ('IS_NULL("<value>")',),
1003
+ "Evaluates whether the value is empty (a zero-length string).",
1004
+ "is_null",
1005
+ ),
1006
+ Condition(
1007
+ "IS_TRUE",
1008
+ ("IS_TRUE(<value>)",),
1009
+ "Evaluates whether or not the specified value represents a Boolean value of True.",
1010
+ "is_true",
1011
+ ),
1012
+ Condition(
1013
+ "IS_ZERO",
1014
+ ("IS_ZERO(<value>)",),
1015
+ "Evaluates whether or not the specified value is equal to zero.",
1016
+ "is_zero",
1017
+ ),
1018
+ Condition(
1019
+ "METACOMMAND_ERROR",
1020
+ ("METACOMMAND_ERROR()",),
1021
+ "Evaluates whether the previous metacommand generated an error.",
1022
+ "metacommanderror",
1023
+ ),
1024
+ Condition(
1025
+ "NEWER_DATE",
1026
+ ("NEWER_DATE(<filename>, <date>)",),
1027
+ "Evaluates whether the specified file was last modified after the given date.",
1028
+ "newer_date",
1029
+ ),
1030
+ Condition(
1031
+ "NEWER_FILE",
1032
+ ("NEWER_FILE(<filename1>, <filename2>)",),
1033
+ "Evaluates whether the first of the specified files was last modified after the second of the files.",
1034
+ "newer_file",
1035
+ ),
1036
+ Condition("NOT", ("NOT <condition>",), "True when the condition is false.", "if_cmd"),
1037
+ Condition(
1038
+ "OR",
1039
+ ("<condition> OR <condition>",),
1040
+ "True when either condition is true (AND, NOT and parentheses also combine conditions).",
1041
+ "if_cmd",
1042
+ ),
1043
+ Condition(
1044
+ "ROLE_EXISTS",
1045
+ ("ROLE_EXISTS(<role_name>)",),
1046
+ "Whether a role or user exists in the current database.",
1047
+ "role_exists",
1048
+ ),
1049
+ Condition(
1050
+ "ROW_COUNT_EQ",
1051
+ ("ROW_COUNT_EQ(<table_name>, <N>)",),
1052
+ "Evaluates whether the number of rows in the specified table or view is exactly equal to the integer N.",
1053
+ "row_count_eq",
1054
+ ),
1055
+ Condition(
1056
+ "ROW_COUNT_GT",
1057
+ ("ROW_COUNT_GT(<table_name>, <N>)",),
1058
+ "Evaluates whether the number of rows in the specified table or view is strictly greater than the integer N.",
1059
+ "row_count_gt",
1060
+ ),
1061
+ Condition(
1062
+ "ROW_COUNT_GTE",
1063
+ ("ROW_COUNT_GTE(<table_name>, <N>)",),
1064
+ "Evaluates whether the number of rows in the specified table or view is greater than or equal to the integer N.",
1065
+ "row_count_gte",
1066
+ ),
1067
+ Condition(
1068
+ "ROW_COUNT_LT",
1069
+ ("ROW_COUNT_LT(<table_name>, <N>)",),
1070
+ "Evaluates whether the number of rows in the specified table or view is strictly less than the integer N.",
1071
+ "row_count_lt",
1072
+ ),
1073
+ Condition(
1074
+ "SCHEMA_EXISTS",
1075
+ ("SCHEMA_EXISTS(<schema_name>)",),
1076
+ "Evaluates whether or not the specified schema already exists in the database.",
1077
+ "schema_exists",
1078
+ ),
1079
+ Condition(
1080
+ "SCRIPT_EXISTS",
1081
+ ("SCRIPT_EXISTS(<script_name>)",),
1082
+ "Evaluates whether a script with the specified name has been created with the BEGIN SCRIPT metacommand.",
1083
+ "script_exists",
1084
+ ),
1085
+ Condition(
1086
+ "SQL_ERROR",
1087
+ ("SQL_ERROR()",),
1088
+ "Evaluates whether the previous SQL statement generated an error.",
1089
+ "sqlerror",
1090
+ ),
1091
+ Condition(
1092
+ "STARTS_WITH",
1093
+ ('STARTS_WITH("<string1>", "<string2>" [, I])',),
1094
+ "Evaluates whether string1 starts with string2.",
1095
+ "starts_with",
1096
+ ),
1097
+ Condition(
1098
+ "SUB_DEFINED",
1099
+ ("SUB_DEFINED(<match_string>)",),
1100
+ "Evaluates whether a replacement string has been defined for the specified substitution variable (matching string).",
1101
+ "sub_defined",
1102
+ ),
1103
+ Condition(
1104
+ "SUB_EMPTY",
1105
+ ("SUB_EMPTY(<match_string>)",),
1106
+ "Evaluates whether the specified substitution variable is empty (i.e., is a zero-length string).",
1107
+ "sub_empty_cond",
1108
+ ),
1109
+ Condition(
1110
+ "TABLE_EXISTS",
1111
+ ("TABLE_EXISTS(<tablename>)",),
1112
+ "Evaluates whether there is a database table of the given name.",
1113
+ "tableexists",
1114
+ ),
1115
+ Condition(
1116
+ "VIEW_EXISTS",
1117
+ ("VIEW_EXISTS(<viewname>)",),
1118
+ "Evaluates whether there is a database view of the given name.",
1119
+ "view_exists",
1120
+ ),
1121
+ )
1122
+
1123
+ _SYSTEM_VARIABLES: tuple[SystemVariable, ...] = (
1124
+ SystemVariable(
1125
+ "$ARG_x",
1126
+ 'The value of a substitution variable that has been assigned on the command line using the "-a" command-line option.',
1127
+ "substitution_vars.md#system_vars",
1128
+ ),
1129
+ SystemVariable(
1130
+ "$AUTOCOMMIT_STATE",
1131
+ "A value indicating whether or not execsql will automatically commit each SQL statement as it is executed.",
1132
+ "substitution_vars.md#system_vars",
1133
+ ),
1134
+ SystemVariable(
1135
+ "$CANCEL_HALT_STATE",
1136
+ "The value of the status flag that is set by the CANCEL_HALT metacommand.",
1137
+ "substitution_vars.md#system_vars",
1138
+ ),
1139
+ SystemVariable(
1140
+ "$CONSOLE_WAIT_WHEN_DONE_STATE",
1141
+ "ON or OFF: whether the GUI console waits to be closed when the script ends.",
1142
+ "substitution_vars.md#system_vars",
1143
+ ),
1144
+ SystemVariable(
1145
+ "$CONSOLE_WAIT_WHEN_ERROR_HALT_STATE",
1146
+ "ON or OFF: whether the GUI console waits to be closed when an error halts the script.",
1147
+ "substitution_vars.md#system_vars",
1148
+ ),
1149
+ SystemVariable(
1150
+ "$COUNTER_x",
1151
+ "An integer value that is automatically incremented every time that it is referenced.",
1152
+ "substitution_vars.md#system_vars",
1153
+ ),
1154
+ SystemVariable(
1155
+ "$CURRENT_ALIAS",
1156
+ 'The alias of the database in use, or "initial".',
1157
+ "substitution_vars.md#system_vars",
1158
+ ),
1159
+ SystemVariable(
1160
+ "$CURRENT_DATABASE",
1161
+ "The DBMS type and the name of the current database.",
1162
+ "substitution_vars.md#system_vars",
1163
+ ),
1164
+ SystemVariable("$CURRENT_DBMS", "The DBMS type of the database in use.", "substitution_vars.md#system_vars"),
1165
+ SystemVariable("$CURRENT_DIR", "The full path to the current directory.", "substitution_vars.md#system_vars"),
1166
+ SystemVariable(
1167
+ "$CURRENT_PATH",
1168
+ 'The full path to the current directory, including a directory separator character (i.e., "/" or "\\\\") at the end.',
1169
+ "substitution_vars.md#system_vars",
1170
+ ),
1171
+ SystemVariable(
1172
+ "$CURRENT_SCRIPT",
1173
+ "The file name of the script from which the current command originated.",
1174
+ "substitution_vars.md#system_vars",
1175
+ ),
1176
+ SystemVariable(
1177
+ "$CURRENT_SCRIPT_NAME",
1178
+ "The base file name, without a path, of the script from which the current command originated.",
1179
+ "substitution_vars.md#system_vars",
1180
+ ),
1181
+ SystemVariable(
1182
+ "$CURRENT_SCRIPT_PATH",
1183
+ "The full path of the current script's directory, ending in a path separator.",
1184
+ "substitution_vars.md#system_vars",
1185
+ ),
1186
+ SystemVariable(
1187
+ "$CURRENT_DATE",
1188
+ "The current calendar date (in the format `YYYY-MM-DD`) when the current script line is run.",
1189
+ "substitution_vars.md#system_vars",
1190
+ ),
1191
+ SystemVariable(
1192
+ "$CURRENT_SCRIPT_LINE",
1193
+ "The 1-based line number, within `$CURRENT_SCRIPT`, of the command currently being executed.",
1194
+ "substitution_vars.md#system_vars",
1195
+ ),
1196
+ SystemVariable(
1197
+ "$CURRENT_TIME",
1198
+ "The date and time at which the current script line is run.",
1199
+ "substitution_vars.md#system_vars",
1200
+ ),
1201
+ SystemVariable(
1202
+ "$CURRENT_TIME_UTC",
1203
+ "The date and time at which the current script line is run, in Universal Coordinated Time (UTC).",
1204
+ "substitution_vars.md#system_vars",
1205
+ ),
1206
+ SystemVariable(
1207
+ "$DATE_TAG",
1208
+ "The date on which execsql started processing the current script, in the format YYYYMMDD.",
1209
+ "substitution_vars.md#system_vars",
1210
+ ),
1211
+ SystemVariable(
1212
+ "$DATETIME_TAG",
1213
+ "The date and time at which execsql started processing the current script, in the format YYYYMMDD_hhmm.",
1214
+ "substitution_vars.md#system_vars",
1215
+ ),
1216
+ SystemVariable(
1217
+ "$DATETIME_UTC_TAG",
1218
+ "The UTC date and time the script started, as YYYYMMDD_hhmm.",
1219
+ "substitution_vars.md#system_vars",
1220
+ ),
1221
+ SystemVariable(
1222
+ "$DB_NAME",
1223
+ "The name of the database currently in use, as specified on the command line or in a CONNECT metacommand.",
1224
+ "substitution_vars.md#system_vars",
1225
+ ),
1226
+ SystemVariable(
1227
+ "$DB_NEED_PWD",
1228
+ 'A string equal to "TRUE" or "FALSE" indicating whether or not a password was required for the database currently in use.',
1229
+ "substitution_vars.md#system_vars",
1230
+ ),
1231
+ SystemVariable("$DB_SERVER", "The server name of the database in use.", "substitution_vars.md#system_vars"),
1232
+ SystemVariable("$DB_USER", "The user name of the database in use.", "substitution_vars.md#system_vars"),
1233
+ SystemVariable(
1234
+ "$ERROR_HALT_STATE",
1235
+ "The value of the status flag that is set by the ERROR_HALT metacommand.",
1236
+ "substitution_vars.md#system_vars",
1237
+ ),
1238
+ SystemVariable(
1239
+ "$ERROR_MESSAGE",
1240
+ "The message generated by any error, as it would be printed on the terminal by default.",
1241
+ "substitution_vars.md#system_vars",
1242
+ ),
1243
+ SystemVariable(
1244
+ "$HOSTNAME",
1245
+ "The network name of the machine running execsql, as returned by Python's `platform.node()`.",
1246
+ "substitution_vars.md#system_vars",
1247
+ ),
1248
+ SystemVariable(
1249
+ "$LAST_ERROR",
1250
+ "The text of the last SQL statement or metacommand that caused an error.",
1251
+ "substitution_vars.md#system_vars",
1252
+ ),
1253
+ SystemVariable(
1254
+ "$LAST_ROWCOUNT",
1255
+ "The number of rows that were affected by the last INSERT, UPDATE, or SELECT statement.",
1256
+ "substitution_vars.md#system_vars",
1257
+ ),
1258
+ SystemVariable(
1259
+ "$LAST_SQL",
1260
+ "The text of the last SQL statement that ran without error.",
1261
+ "substitution_vars.md#system_vars",
1262
+ ),
1263
+ SystemVariable(
1264
+ "$METACOMMAND_ERROR_HALT_STATE",
1265
+ "The value of the status flag that is set by the METACOMMAND_ERROR_HALT metacommand.",
1266
+ "substitution_vars.md#system_vars",
1267
+ ),
1268
+ SystemVariable("$OS", "The name of the operating system.", "substitution_vars.md#system_vars"),
1269
+ SystemVariable("$PATHSEP", "The path separator used by the operating system.", "substitution_vars.md#system_vars"),
1270
+ SystemVariable(
1271
+ "$PYTHON_EXECUTABLE",
1272
+ "The path and name of the Python interpreter that is running execsql.",
1273
+ "substitution_vars.md#system_vars",
1274
+ ),
1275
+ SystemVariable(
1276
+ "$RANDOM",
1277
+ "A random real number in the semi-open interval \\[0.0, 1.0).",
1278
+ "substitution_vars.md#system_vars",
1279
+ ),
1280
+ SystemVariable(
1281
+ "$RUN_ID",
1282
+ "The run identifier that is used in execsql's log file.",
1283
+ "substitution_vars.md#system_vars",
1284
+ ),
1285
+ SystemVariable(
1286
+ "$SCRIPT_LINE",
1287
+ "The line number of the current script for the current command.",
1288
+ "substitution_vars.md#system_vars",
1289
+ ),
1290
+ SystemVariable(
1291
+ "$SCRIPT_START_TIME",
1292
+ "The date and time at which execsql started processing the current script.",
1293
+ "substitution_vars.md#system_vars",
1294
+ ),
1295
+ SystemVariable(
1296
+ "$SCRIPT_START_TIME_UTC",
1297
+ "The date and time at which execsql started processing the current script, in Universal Coordinated Time (UTC).",
1298
+ "substitution_vars.md#system_vars",
1299
+ ),
1300
+ SystemVariable(
1301
+ "$SHEETS_IMPORTED",
1302
+ "A comma-delimited list of the names of all worksheets IMPORTed when using the SHEETS MATCHING clause.",
1303
+ "substitution_vars.md#system_vars",
1304
+ ),
1305
+ SystemVariable(
1306
+ "$SHEETS_TABLES",
1307
+ "Comma-separated names of the tables created by IMPORT ... SHEETS MATCHING.",
1308
+ "substitution_vars.md#system_vars",
1309
+ ),
1310
+ SystemVariable(
1311
+ "$SHEETS_TABLES_VALUES",
1312
+ "The tables created by IMPORT ... SHEETS MATCHING, as quoted, parenthesized values.",
1313
+ "substitution_vars.md#system_vars",
1314
+ ),
1315
+ SystemVariable(
1316
+ "$STARTING_PATH",
1317
+ "The path of the directory from which execsql was started, including a terminating path separator character.",
1318
+ "substitution_vars.md#system_vars",
1319
+ ),
1320
+ SystemVariable(
1321
+ "$STARTING_SCRIPT",
1322
+ "The file name of the script specified on the command line when execsql is run.",
1323
+ "substitution_vars.md#system_vars",
1324
+ ),
1325
+ SystemVariable(
1326
+ "$STARTING_SCRIPT_NAME",
1327
+ "The base file name of the script specified on the command line when execsql is run, without any path specification.",
1328
+ "substitution_vars.md#system_vars",
1329
+ ),
1330
+ SystemVariable(
1331
+ "$STARTING_SCRIPT_REVTIME",
1332
+ "The date and time of the script specified on the command line when execsql is run.",
1333
+ "substitution_vars.md#system_vars",
1334
+ ),
1335
+ SystemVariable(
1336
+ "$SYSTEM_CMD_EXIT_STATUS",
1337
+ "The exit status of the command executed by the SYSTEM_CMD metacommand.",
1338
+ "substitution_vars.md#system_vars",
1339
+ ),
1340
+ SystemVariable(
1341
+ "$SYSTEM_CMD_PID",
1342
+ "The process ID (PID) of the background process launched by `SYSTEM_CMD … CONTINUE`.",
1343
+ "substitution_vars.md#system_vars",
1344
+ ),
1345
+ SystemVariable("$TIMER", "The elapsed time of the script timer.", "substitution_vars.md#system_vars"),
1346
+ SystemVariable(
1347
+ "$USER",
1348
+ "The name of the person logged in when the script is started.",
1349
+ "substitution_vars.md#system_vars",
1350
+ ),
1351
+ SystemVariable("$UUID", "A new random UUID (version 4) each time it is used.", "substitution_vars.md#system_vars"),
1352
+ SystemVariable("$VERSION1", "Execsql's primary version number.", "substitution_vars.md#system_vars"),
1353
+ SystemVariable("$VERSION2", "Execsql's secondary version number.", "substitution_vars.md#system_vars"),
1354
+ SystemVariable("$VERSION3", "Execsql's tertiary version number.", "substitution_vars.md#system_vars"),
1355
+ SystemVariable(
1356
+ "$PG_UPSERT_QA_PASSED",
1357
+ "TRUE or FALSE: whether every QA check passed.",
1358
+ "metacommands.md#pg_upsert",
1359
+ ),
1360
+ SystemVariable("$PG_UPSERT_ROWS_UPDATED", "Total rows updated across all tables.", "metacommands.md#pg_upsert"),
1361
+ SystemVariable("$PG_UPSERT_ROWS_INSERTED", "Total rows inserted across all tables.", "metacommands.md#pg_upsert"),
1362
+ SystemVariable(
1363
+ "$PG_UPSERT_COMMITTED",
1364
+ "TRUE or FALSE: whether the changes were committed.",
1365
+ "metacommands.md#pg_upsert",
1366
+ ),
1367
+ SystemVariable("$PG_UPSERT_STAGING_SCHEMA", "Staging schema name used.", "metacommands.md#pg_upsert"),
1368
+ SystemVariable("$PG_UPSERT_BASE_SCHEMA", "Base schema name used.", "metacommands.md#pg_upsert"),
1369
+ SystemVariable("$PG_UPSERT_TABLES", "Comma-separated list of table names processed.", "metacommands.md#pg_upsert"),
1370
+ SystemVariable("$PG_UPSERT_METHOD", "Upsert method used.", "metacommands.md#pg_upsert"),
1371
+ SystemVariable("$PG_UPSERT_DURATION", "Elapsed time in seconds.", "metacommands.md#pg_upsert"),
1372
+ SystemVariable("$PG_UPSERT_STARTED_AT", "ISO 8601 start timestamp.", "metacommands.md#pg_upsert"),
1373
+ SystemVariable("$PG_UPSERT_FINISHED_AT", "ISO 8601 end timestamp.", "metacommands.md#pg_upsert"),
1374
+ SystemVariable("$PG_UPSERT_RESULT_JSON", "The full result as JSON.", "metacommands.md#pg_upsert"),
1375
+ SystemVariable(
1376
+ "$PG_UPSERT_CURRENT_TABLE",
1377
+ "The table being processed (updated per table).",
1378
+ "metacommands.md#pg_upsert",
1379
+ ),
1380
+ SystemVariable(
1381
+ "$PG_UPSERT_TABLE_QA_PASSED",
1382
+ "QA result for the current table (updated per table).",
1383
+ "metacommands.md#pg_upsert",
1384
+ ),
1385
+ SystemVariable(
1386
+ "$PG_UPSERT_TABLE_ROWS_UPDATED",
1387
+ "Rows updated for the current table (updated per table).",
1388
+ "metacommands.md#pg_upsert",
1389
+ ),
1390
+ SystemVariable(
1391
+ "$PG_UPSERT_TABLE_ROWS_INSERTED",
1392
+ "Rows inserted for the current table (updated per table).",
1393
+ "metacommands.md#pg_upsert",
1394
+ ),
1395
+ SystemVariable(
1396
+ "$PG_UPSERT_EXPORT_PATH",
1397
+ "Where the QA fix sheet was written, or empty if nothing was exported.",
1398
+ "metacommands.md#pg_upsert",
1399
+ ),
1400
+ )
1401
+
1402
+ _BY_KEYWORD = {m.keyword: m for m in _METACOMMANDS}
1403
+ _BY_CONDITION = {c.name: c for c in _CONDITIONS}
1404
+ _BY_VARIABLE = {v.name.upper(): v for v in _SYSTEM_VARIABLES}