execsql2 2.1.2__py3-none-any.whl → 2.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- execsql/cli/__init__.py +436 -0
- execsql/cli/dsn.py +86 -0
- execsql/cli/help.py +140 -0
- execsql/{cli.py → cli/run.py} +14 -589
- execsql/config.py +65 -1
- execsql/db/access.py +27 -15
- execsql/db/base.py +328 -215
- execsql/db/dsn.py +10 -5
- execsql/db/duckdb.py +6 -2
- execsql/db/factory.py +21 -0
- execsql/db/firebird.py +27 -19
- execsql/db/mysql.py +12 -7
- execsql/db/oracle.py +15 -11
- execsql/db/postgres.py +31 -16
- execsql/db/sqlite.py +15 -11
- execsql/db/sqlserver.py +16 -5
- execsql/exceptions.py +25 -7
- execsql/exporters/base.py +12 -1
- execsql/exporters/delimited.py +80 -35
- execsql/exporters/duckdb.py +6 -2
- execsql/exporters/feather.py +10 -6
- execsql/exporters/html.py +89 -69
- execsql/exporters/json.py +52 -45
- execsql/exporters/latex.py +37 -27
- execsql/exporters/ods.py +32 -11
- execsql/exporters/parquet.py +5 -2
- execsql/exporters/pretty.py +16 -9
- execsql/exporters/raw.py +22 -16
- execsql/exporters/sqlite.py +6 -2
- execsql/exporters/templates.py +39 -21
- execsql/exporters/values.py +26 -20
- execsql/exporters/xls.py +30 -11
- execsql/exporters/xml.py +31 -13
- execsql/exporters/zip.py +15 -0
- execsql/importers/base.py +6 -4
- execsql/importers/csv.py +8 -6
- execsql/importers/feather.py +6 -4
- execsql/importers/ods.py +6 -4
- execsql/importers/xls.py +6 -4
- execsql/metacommands/__init__.py +208 -1548
- execsql/metacommands/conditions.py +101 -27
- execsql/metacommands/control.py +8 -4
- execsql/metacommands/data.py +6 -6
- execsql/metacommands/debug.py +6 -2
- execsql/metacommands/dispatch.py +2011 -0
- execsql/metacommands/io.py +67 -1310
- execsql/metacommands/io_export.py +442 -0
- execsql/metacommands/io_fileops.py +287 -0
- execsql/metacommands/io_import.py +398 -0
- execsql/metacommands/io_write.py +248 -0
- execsql/metacommands/prompt.py +22 -66
- execsql/metacommands/system.py +7 -2
- execsql/models.py +7 -0
- execsql/parser.py +10 -0
- execsql/py.typed +0 -0
- execsql/script/__init__.py +95 -0
- execsql/script/control.py +162 -0
- execsql/{script.py → script/engine.py} +184 -402
- execsql/script/variables.py +281 -0
- execsql/types.py +49 -20
- execsql/utils/auth.py +2 -0
- execsql/utils/crypto.py +4 -6
- execsql/utils/datetime.py +1 -0
- execsql/utils/errors.py +11 -0
- execsql/utils/fileio.py +33 -8
- execsql/utils/gui.py +46 -0
- execsql/utils/mail.py +7 -17
- execsql/utils/numeric.py +2 -0
- execsql/utils/regex.py +9 -0
- execsql/utils/strings.py +16 -0
- execsql/utils/timer.py +2 -0
- execsql2-2.4.0.data/data/execsql2_extras/README.md +65 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/execsql.conf +1 -1
- {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/METADATA +13 -6
- execsql2-2.4.0.dist-info/RECORD +108 -0
- execsql2-2.1.2.data/data/execsql2_extras/READ_ME.rst +0 -127
- execsql2-2.1.2.dist-info/RECORD +0 -96
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/WHEEL +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/entry_points.txt +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""WRITE metacommand handlers.
|
|
2
|
+
|
|
3
|
+
Implements ``x_write``, ``x_write_create_table`` (CSV, ODS, XLS, alias),
|
|
4
|
+
``x_write_prefix``, ``x_write_suffix``, and ``x_writescript``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import execsql.state as _state
|
|
13
|
+
from execsql.exceptions import ErrInfo
|
|
14
|
+
from execsql.exporters.delimited import CsvFile
|
|
15
|
+
from execsql.importers.ods import ods_data
|
|
16
|
+
from execsql.importers.xls import xls_data
|
|
17
|
+
from execsql.models import DataTable
|
|
18
|
+
from execsql.script import substitute_vars
|
|
19
|
+
from execsql.utils.errors import exception_desc
|
|
20
|
+
from execsql.utils.fileio import check_dir, filewriter_close, filewriter_open_as_new, filewriter_write
|
|
21
|
+
from execsql.utils.gui import ConsoleUIError
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def x_write(**kwargs: Any) -> None:
|
|
25
|
+
msg = f"{kwargs['text']}\n"
|
|
26
|
+
tee = kwargs["tee"]
|
|
27
|
+
tee = bool(tee)
|
|
28
|
+
outf = kwargs["filename"]
|
|
29
|
+
if _state.conf.write_prefix is not None:
|
|
30
|
+
msg = substitute_vars(_state.conf.write_prefix) + " " + msg
|
|
31
|
+
if _state.conf.write_suffix is not None:
|
|
32
|
+
msg = msg[:-1] + " " + substitute_vars(_state.conf.write_suffix) + "\n"
|
|
33
|
+
if outf:
|
|
34
|
+
check_dir(outf)
|
|
35
|
+
filewriter_write(outf, msg)
|
|
36
|
+
if (not outf) or tee:
|
|
37
|
+
try:
|
|
38
|
+
_state.output.write(msg)
|
|
39
|
+
except TypeError as e:
|
|
40
|
+
raise ErrInfo(
|
|
41
|
+
type="other",
|
|
42
|
+
command_text=kwargs["metacommandline"],
|
|
43
|
+
other_msg="TypeError in 'write' metacommand.",
|
|
44
|
+
) from e
|
|
45
|
+
except ConsoleUIError as e:
|
|
46
|
+
_state.output.reset()
|
|
47
|
+
_state.exec_log.log_status_info(f"Console UI write failed (message {{{e.value}}}); output reset to stdout.")
|
|
48
|
+
_state.output.write(msg.encode(_state.conf.output_encoding))
|
|
49
|
+
if _state.conf.tee_write_log:
|
|
50
|
+
_state.exec_log.log_user_msg(msg)
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def x_write_create_table(**kwargs: Any) -> None:
|
|
55
|
+
filename = kwargs["filename"]
|
|
56
|
+
if not Path(filename).exists():
|
|
57
|
+
raise ErrInfo(
|
|
58
|
+
type="cmd",
|
|
59
|
+
command_text=kwargs["metacommandline"],
|
|
60
|
+
other_msg="Input file does not exist",
|
|
61
|
+
)
|
|
62
|
+
quotechar = kwargs["quotechar"]
|
|
63
|
+
delimchar = kwargs["delimchar"]
|
|
64
|
+
encoding = kwargs["encoding"]
|
|
65
|
+
if delimchar:
|
|
66
|
+
if delimchar.lower() == "tab":
|
|
67
|
+
delimchar = chr(9)
|
|
68
|
+
elif delimchar.lower() in ("unitsep", "us"):
|
|
69
|
+
delimchar = chr(31)
|
|
70
|
+
junk_hdrs = kwargs["skip"]
|
|
71
|
+
if not junk_hdrs:
|
|
72
|
+
junk_hdrs = 0
|
|
73
|
+
else:
|
|
74
|
+
junk_hdrs = int(junk_hdrs)
|
|
75
|
+
enc = encoding if encoding else _state.conf.import_encoding
|
|
76
|
+
inf = CsvFile(filename, enc, junk_header_lines=junk_hdrs)
|
|
77
|
+
if quotechar and delimchar:
|
|
78
|
+
inf.lineformat(delimchar, quotechar, None)
|
|
79
|
+
inf.evaluate_column_types()
|
|
80
|
+
sql = inf.create_table(_state.dbs.current().type, kwargs["schema"], kwargs["table"], pretty=True)
|
|
81
|
+
inf.close()
|
|
82
|
+
comment = kwargs["comment"]
|
|
83
|
+
outfile = kwargs["outfile"]
|
|
84
|
+
|
|
85
|
+
def write(txt: str) -> None:
|
|
86
|
+
if outfile is None or outfile == "stdout":
|
|
87
|
+
_state.output.write(txt)
|
|
88
|
+
else:
|
|
89
|
+
filewriter_write(outfile, txt)
|
|
90
|
+
|
|
91
|
+
if outfile:
|
|
92
|
+
check_dir(outfile)
|
|
93
|
+
if comment:
|
|
94
|
+
write(f"-- {comment}\n")
|
|
95
|
+
write(f"{sql}\n")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def x_write_create_table_ods(**kwargs: Any) -> None:
|
|
99
|
+
schemaname = kwargs["schema"]
|
|
100
|
+
tablename = kwargs["table"]
|
|
101
|
+
filename = kwargs["filename"]
|
|
102
|
+
sheetname = kwargs["sheet"]
|
|
103
|
+
hdr_rows = kwargs["skip"]
|
|
104
|
+
if not hdr_rows:
|
|
105
|
+
hdr_rows = 0
|
|
106
|
+
else:
|
|
107
|
+
hdr_rows = int(hdr_rows)
|
|
108
|
+
comment = kwargs["comment"]
|
|
109
|
+
outfile = kwargs["outfile"]
|
|
110
|
+
if not Path(filename).exists():
|
|
111
|
+
raise ErrInfo(
|
|
112
|
+
type="cmd",
|
|
113
|
+
command_text=kwargs["metacommandline"],
|
|
114
|
+
other_msg="Input file does not exist",
|
|
115
|
+
)
|
|
116
|
+
hdrs, data = ods_data(filename, sheetname, hdr_rows)
|
|
117
|
+
tablespec = DataTable(hdrs, data)
|
|
118
|
+
sql = tablespec.create_table(_state.dbs.current().type, schemaname, tablename, pretty=True)
|
|
119
|
+
if outfile:
|
|
120
|
+
if comment:
|
|
121
|
+
filewriter_write(outfile, f"-- {comment}\n")
|
|
122
|
+
filewriter_write(outfile, sql)
|
|
123
|
+
filewriter_close(outfile)
|
|
124
|
+
else:
|
|
125
|
+
if comment:
|
|
126
|
+
_state.output.write(f"-- {comment}\n")
|
|
127
|
+
_state.output.write(f"{sql}\n")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def x_write_create_table_xls(**kwargs: Any) -> None:
|
|
131
|
+
schemaname = kwargs["schema"]
|
|
132
|
+
tablename = kwargs["table"]
|
|
133
|
+
filename = kwargs["filename"]
|
|
134
|
+
sheetname = kwargs["sheet"]
|
|
135
|
+
junk_hdrs = kwargs["skip"]
|
|
136
|
+
encoding = kwargs["encoding"]
|
|
137
|
+
enc = encoding if encoding else _state.conf.import_encoding
|
|
138
|
+
if not junk_hdrs:
|
|
139
|
+
junk_hdrs = 0
|
|
140
|
+
else:
|
|
141
|
+
junk_hdrs = int(junk_hdrs)
|
|
142
|
+
comment = kwargs["comment"]
|
|
143
|
+
outfile = kwargs["outfile"]
|
|
144
|
+
if not Path(filename).exists():
|
|
145
|
+
raise ErrInfo(
|
|
146
|
+
type="cmd",
|
|
147
|
+
command_text=kwargs["metacommandline"],
|
|
148
|
+
other_msg="Input file does not exist",
|
|
149
|
+
)
|
|
150
|
+
hdrs, data = xls_data(filename, sheetname, junk_hdrs, enc)
|
|
151
|
+
tablespec = DataTable(hdrs, data)
|
|
152
|
+
sql = tablespec.create_table(_state.dbs.current().type, schemaname, tablename, pretty=True)
|
|
153
|
+
if outfile:
|
|
154
|
+
if comment:
|
|
155
|
+
filewriter_write(outfile, f"-- {comment}\n")
|
|
156
|
+
filewriter_write(outfile, sql)
|
|
157
|
+
filewriter_close(outfile)
|
|
158
|
+
else:
|
|
159
|
+
if comment:
|
|
160
|
+
_state.output.write(f"-- {comment}\n")
|
|
161
|
+
_state.output.write(f"{sql}\n")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def x_write_create_table_alias(**kwargs: Any) -> None:
|
|
165
|
+
alias = kwargs["alias"].lower()
|
|
166
|
+
schema = kwargs["schema"]
|
|
167
|
+
table = kwargs["table"]
|
|
168
|
+
comment = kwargs["comment"]
|
|
169
|
+
outfile = kwargs["filename"]
|
|
170
|
+
if alias not in _state.dbs.aliases():
|
|
171
|
+
raise ErrInfo(
|
|
172
|
+
type="cmd",
|
|
173
|
+
command_text=kwargs["metacommandline"],
|
|
174
|
+
other_msg=f"Unrecognized database alias: {alias}.",
|
|
175
|
+
)
|
|
176
|
+
db = _state.dbs.aliased_as(alias)
|
|
177
|
+
tbl = db.schema_qualified_table_name(schema, table)
|
|
178
|
+
try:
|
|
179
|
+
if not db.table_exists(table, schema):
|
|
180
|
+
raise ErrInfo(
|
|
181
|
+
type="cmd",
|
|
182
|
+
command_text=kwargs["metacommandline"],
|
|
183
|
+
other_msg=f"Table {tbl} does not exist",
|
|
184
|
+
)
|
|
185
|
+
except Exception:
|
|
186
|
+
pass # Best-effort check; some adapters lack information_schema.
|
|
187
|
+
select_stmt = f"select * from {tbl};"
|
|
188
|
+
try:
|
|
189
|
+
hdrs, rows = db.select_rowsource(select_stmt)
|
|
190
|
+
except ErrInfo:
|
|
191
|
+
raise
|
|
192
|
+
except Exception as e:
|
|
193
|
+
raise ErrInfo("db", select_stmt, exception_msg=exception_desc()) from e
|
|
194
|
+
tablespec = DataTable(hdrs, rows)
|
|
195
|
+
sql = tablespec.create_table(_state.dbs.current().type, kwargs["schema1"], kwargs["table1"], pretty=True)
|
|
196
|
+
if outfile:
|
|
197
|
+
if comment:
|
|
198
|
+
filewriter_write(outfile, f"-- {comment}\n")
|
|
199
|
+
filewriter_write(outfile, sql)
|
|
200
|
+
filewriter_close(outfile)
|
|
201
|
+
else:
|
|
202
|
+
if comment:
|
|
203
|
+
_state.output.write(f"-- {comment}\n")
|
|
204
|
+
_state.output.write(f"{sql}\n")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def x_write_prefix(**kwargs: Any) -> None:
|
|
208
|
+
pf = kwargs["prefix"]
|
|
209
|
+
if pf.lower() == "clear":
|
|
210
|
+
_state.conf.write_prefix = None
|
|
211
|
+
else:
|
|
212
|
+
_state.conf.write_prefix = pf
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def x_write_suffix(**kwargs: Any) -> None:
|
|
217
|
+
sf = kwargs["suffix"]
|
|
218
|
+
if sf.lower() == "clear":
|
|
219
|
+
_state.conf.write_suffix = None
|
|
220
|
+
else:
|
|
221
|
+
_state.conf.write_suffix = sf
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def x_writescript(**kwargs: Any) -> None:
|
|
226
|
+
script_id = kwargs["script_id"]
|
|
227
|
+
output_dest = kwargs["filename"]
|
|
228
|
+
append = kwargs["append"]
|
|
229
|
+
|
|
230
|
+
def write(txt: str) -> None:
|
|
231
|
+
if output_dest is None or output_dest == "stdout":
|
|
232
|
+
_state.output.write(txt)
|
|
233
|
+
else:
|
|
234
|
+
filewriter_write(output_dest, txt)
|
|
235
|
+
|
|
236
|
+
if output_dest is not None and output_dest != "stdout":
|
|
237
|
+
check_dir(output_dest)
|
|
238
|
+
if not append:
|
|
239
|
+
filewriter_open_as_new(output_dest)
|
|
240
|
+
script = _state.savedscripts[script_id]
|
|
241
|
+
if script.paramnames is not None and len(script.paramnames) > 0:
|
|
242
|
+
write(f"BEGIN SCRIPT {script_id} ({', '.join(script.paramnames)})\n")
|
|
243
|
+
else:
|
|
244
|
+
write(f"BEGIN SCRIPT {script_id}\n")
|
|
245
|
+
lines = [c.commandline() for c in script.cmdlist]
|
|
246
|
+
for line in lines:
|
|
247
|
+
write(f"{line}\n")
|
|
248
|
+
write(f"END SCRIPT {script_id}\n")
|
execsql/metacommands/prompt.py
CHANGED
|
@@ -29,7 +29,6 @@ from typing import Any
|
|
|
29
29
|
import execsql.state as _state
|
|
30
30
|
from execsql.script import current_script_line
|
|
31
31
|
from execsql.utils.errors import exception_desc, exit_now
|
|
32
|
-
from execsql.utils.fileio import EncodedFile, check_dir
|
|
33
32
|
from execsql.utils.gui import (
|
|
34
33
|
ActionSpec,
|
|
35
34
|
EntrySpec,
|
|
@@ -38,7 +37,6 @@ from execsql.utils.gui import (
|
|
|
38
37
|
GUI_DIRECTORY,
|
|
39
38
|
GUI_DISPLAY,
|
|
40
39
|
GUI_ENTRY,
|
|
41
|
-
GUI_HALT,
|
|
42
40
|
GUI_MAP,
|
|
43
41
|
GUI_MSG,
|
|
44
42
|
GUI_OPENFILE,
|
|
@@ -210,12 +208,12 @@ def x_prompt_entryform(**kwargs: Any) -> None:
|
|
|
210
208
|
initial_value = str(str(v["initial_value"]).lower() in ("yes", "true", "on", "1"))
|
|
211
209
|
else:
|
|
212
210
|
initial_value = str(v["initial_value"])
|
|
213
|
-
except Exception:
|
|
211
|
+
except Exception as e:
|
|
214
212
|
raise ErrInfo(
|
|
215
213
|
type="cmd",
|
|
216
214
|
command_text=kwargs["metacommandline"],
|
|
217
215
|
other_msg=f"The initial value of {v['initial_value']} can't be used.",
|
|
218
|
-
)
|
|
216
|
+
) from e
|
|
219
217
|
if "lookup_table" in colhdrs:
|
|
220
218
|
lt = v["lookup_table"]
|
|
221
219
|
if lt:
|
|
@@ -226,23 +224,23 @@ def x_prompt_entryform(**kwargs: Any) -> None:
|
|
|
226
224
|
if entry_width:
|
|
227
225
|
try:
|
|
228
226
|
entry_width = int(entry_width)
|
|
229
|
-
except Exception:
|
|
227
|
+
except Exception as e:
|
|
230
228
|
raise ErrInfo(
|
|
231
229
|
type="cmd",
|
|
232
230
|
command_text=kwargs["metacommandline"],
|
|
233
231
|
other_msg=f"Entry width {entry_width} is not an integer",
|
|
234
|
-
)
|
|
232
|
+
) from e
|
|
235
233
|
if "height" in colhdrs:
|
|
236
234
|
entry_height = v.get("height")
|
|
237
235
|
if entry_height:
|
|
238
236
|
try:
|
|
239
237
|
entry_height = int(entry_height)
|
|
240
|
-
except Exception:
|
|
238
|
+
except Exception as e:
|
|
241
239
|
raise ErrInfo(
|
|
242
240
|
type="cmd",
|
|
243
241
|
command_text=kwargs["metacommandline"],
|
|
244
242
|
other_msg=f"Entry height {entry_height} is not an integer",
|
|
245
|
-
)
|
|
243
|
+
) from e
|
|
246
244
|
if entry_height < 1:
|
|
247
245
|
entry_height = 1
|
|
248
246
|
if "form_column" in colhdrs:
|
|
@@ -250,12 +248,12 @@ def x_prompt_entryform(**kwargs: Any) -> None:
|
|
|
250
248
|
if entry_col:
|
|
251
249
|
try:
|
|
252
250
|
entry_col = int(entry_col)
|
|
253
|
-
except Exception:
|
|
251
|
+
except Exception as e:
|
|
254
252
|
raise ErrInfo(
|
|
255
253
|
type="cmd",
|
|
256
254
|
command_text=kwargs["metacommandline"],
|
|
257
255
|
other_msg=f"Entry column {entry_col} is not an integer",
|
|
258
|
-
)
|
|
256
|
+
) from e
|
|
259
257
|
if entry_col < 1:
|
|
260
258
|
entry_col = 1
|
|
261
259
|
subvarset = _state.subvars if subvar[0] != "~" else _state.commandliststack[-1].localvars
|
|
@@ -363,15 +361,15 @@ def prompt_compare(button_list: list, **kwargs: Any) -> Any:
|
|
|
363
361
|
if alias1 is not None:
|
|
364
362
|
try:
|
|
365
363
|
db1 = _state.dbs.aliased_as(alias1)
|
|
366
|
-
except Exception:
|
|
367
|
-
raise ErrInfo(type="error", other_msg=badaliasmsg % alias1)
|
|
364
|
+
except Exception as e:
|
|
365
|
+
raise ErrInfo(type="error", other_msg=badaliasmsg % alias1) from e
|
|
368
366
|
else:
|
|
369
367
|
db1 = _state.dbs.current()
|
|
370
368
|
if alias2 is not None:
|
|
371
369
|
try:
|
|
372
370
|
db2 = _state.dbs.aliased_as(alias2)
|
|
373
|
-
except Exception:
|
|
374
|
-
raise ErrInfo(type="error", other_msg=badaliasmsg % alias2)
|
|
371
|
+
except Exception as e:
|
|
372
|
+
raise ErrInfo(type="error", other_msg=badaliasmsg % alias2) from e
|
|
375
373
|
else:
|
|
376
374
|
db2 = _state.dbs.current()
|
|
377
375
|
sq_name1 = db1.schema_qualified_table_name(schema1, table1)
|
|
@@ -666,8 +664,8 @@ def x_prompt_savefile(**kwargs: Any) -> None:
|
|
|
666
664
|
subvarset5.add_substitution(sub_name5, Path(fn).stem)
|
|
667
665
|
except (ErrInfo, SystemExit):
|
|
668
666
|
raise
|
|
669
|
-
except Exception:
|
|
670
|
-
raise ErrInfo(type="exception", exception_msg=exception_desc())
|
|
667
|
+
except Exception as e:
|
|
668
|
+
raise ErrInfo(type="exception", exception_msg=exception_desc()) from e
|
|
671
669
|
return None
|
|
672
670
|
|
|
673
671
|
|
|
@@ -733,8 +731,8 @@ def x_prompt_openfile(**kwargs: Any) -> None:
|
|
|
733
731
|
subvarset5.add_substitution(sub_name5, Path(fn).stem)
|
|
734
732
|
except (ErrInfo, SystemExit):
|
|
735
733
|
raise
|
|
736
|
-
except Exception:
|
|
737
|
-
raise ErrInfo(type="exception", exception_msg=exception_desc())
|
|
734
|
+
except Exception as e:
|
|
735
|
+
raise ErrInfo(type="exception", exception_msg=exception_desc()) from e
|
|
738
736
|
return None
|
|
739
737
|
|
|
740
738
|
|
|
@@ -769,8 +767,8 @@ def x_prompt_directory(**kwargs: Any) -> None:
|
|
|
769
767
|
)
|
|
770
768
|
except (ErrInfo, SystemExit):
|
|
771
769
|
raise
|
|
772
|
-
except Exception:
|
|
773
|
-
raise ErrInfo(type="exception", exception_msg=exception_desc())
|
|
770
|
+
except Exception as e:
|
|
771
|
+
raise ErrInfo(type="exception", exception_msg=exception_desc()) from e
|
|
774
772
|
return None
|
|
775
773
|
|
|
776
774
|
|
|
@@ -787,15 +785,15 @@ def prompt_select_rows(button_list: list, **kwargs: Any) -> Any:
|
|
|
787
785
|
if alias1 is not None:
|
|
788
786
|
try:
|
|
789
787
|
db1 = _state.dbs.aliased_as(alias1)
|
|
790
|
-
except Exception:
|
|
791
|
-
raise ErrInfo(type="error", other_msg=badaliasmsg % alias1)
|
|
788
|
+
except Exception as e:
|
|
789
|
+
raise ErrInfo(type="error", other_msg=badaliasmsg % alias1) from e
|
|
792
790
|
else:
|
|
793
791
|
db1 = _state.dbs.current()
|
|
794
792
|
if alias2 is not None:
|
|
795
793
|
try:
|
|
796
794
|
db2 = _state.dbs.aliased_as(alias2)
|
|
797
|
-
except Exception:
|
|
798
|
-
raise ErrInfo(type="error", other_msg=badaliasmsg % alias2)
|
|
795
|
+
except Exception as e:
|
|
796
|
+
raise ErrInfo(type="error", other_msg=badaliasmsg % alias2) from e
|
|
799
797
|
else:
|
|
800
798
|
db2 = _state.dbs.current()
|
|
801
799
|
sq_name1 = db1.schema_qualified_table_name(schema1, table1)
|
|
@@ -942,48 +940,6 @@ def x_pause(**kwargs: Any) -> None:
|
|
|
942
940
|
return None
|
|
943
941
|
|
|
944
942
|
|
|
945
|
-
def x_halt_msg(**kwargs: Any) -> None:
|
|
946
|
-
errmsg = kwargs["errmsg"]
|
|
947
|
-
tee = kwargs["tee"]
|
|
948
|
-
tee = bool(tee)
|
|
949
|
-
outf = kwargs["filename"]
|
|
950
|
-
errlevel = kwargs["errorlevel"]
|
|
951
|
-
if errlevel:
|
|
952
|
-
errlevel = int(errlevel)
|
|
953
|
-
else:
|
|
954
|
-
errlevel = 3
|
|
955
|
-
conf = _state.conf
|
|
956
|
-
if outf:
|
|
957
|
-
check_dir(outf)
|
|
958
|
-
of = EncodedFile(outf, conf.output_encoding).open("a")
|
|
959
|
-
of.write(f"{errmsg}\n")
|
|
960
|
-
of.close()
|
|
961
|
-
schema = kwargs.get("schema")
|
|
962
|
-
table = kwargs.get("table")
|
|
963
|
-
if table:
|
|
964
|
-
db = _state.dbs.current()
|
|
965
|
-
db_obj = db.schema_qualified_table_name(schema, table)
|
|
966
|
-
sql = f"select * from {db_obj};"
|
|
967
|
-
headers, rows = db.select_data(sql)
|
|
968
|
-
else:
|
|
969
|
-
headers, rows = None, None
|
|
970
|
-
enable_gui()
|
|
971
|
-
return_queue = _queue.Queue()
|
|
972
|
-
gui_args = {
|
|
973
|
-
"title": "HALT",
|
|
974
|
-
"message": errmsg,
|
|
975
|
-
"button_list": [("OK", 1, "<Return>")],
|
|
976
|
-
"no_cancel": True,
|
|
977
|
-
"column_headers": headers,
|
|
978
|
-
"rowset": rows,
|
|
979
|
-
"help_url": None,
|
|
980
|
-
}
|
|
981
|
-
_state.gui_manager_queue.put(GuiSpec(GUI_HALT, gui_args, return_queue))
|
|
982
|
-
return_queue.get(block=True)
|
|
983
|
-
_state.exec_log.log_exit_halt(*current_script_line(), msg=errmsg)
|
|
984
|
-
exit_now(errlevel, None)
|
|
985
|
-
|
|
986
|
-
|
|
987
943
|
def x_msg(**kwargs: Any) -> None:
|
|
988
944
|
message = kwargs["message"]
|
|
989
945
|
current_script_line()
|
execsql/metacommands/system.py
CHANGED
|
@@ -89,6 +89,11 @@ def x_log_datavars(**kwargs: Any) -> None:
|
|
|
89
89
|
_state.conf.log_datavars = setting in ("yes", "on", "true", "1")
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
def x_log_sql(**kwargs: Any) -> None:
|
|
93
|
+
setting = kwargs["setting"].lower()
|
|
94
|
+
_state.conf.log_sql = setting in ("yes", "on", "true", "1")
|
|
95
|
+
|
|
96
|
+
|
|
92
97
|
def x_console(**kwargs: Any) -> None:
|
|
93
98
|
onoff = kwargs["onoff"].lower()
|
|
94
99
|
if onoff == "on":
|
|
@@ -251,6 +256,6 @@ def x_execute(**kwargs: Any) -> None:
|
|
|
251
256
|
db.commit()
|
|
252
257
|
except ErrInfo:
|
|
253
258
|
raise
|
|
254
|
-
except Exception:
|
|
255
|
-
raise ErrInfo("db", command_text=sql, exception_msg=exception_desc())
|
|
259
|
+
except Exception as e:
|
|
260
|
+
raise ErrInfo("db", command_text=sql, exception_msg=exception_desc()) from e
|
|
256
261
|
return None
|
execsql/models.py
CHANGED
|
@@ -40,6 +40,13 @@ from execsql.types import (
|
|
|
40
40
|
DT_Varchar,
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
+
__all__ = [
|
|
44
|
+
"Column",
|
|
45
|
+
"DataTable",
|
|
46
|
+
"JsonDatatype",
|
|
47
|
+
"to_json_type",
|
|
48
|
+
]
|
|
49
|
+
|
|
43
50
|
|
|
44
51
|
class Column:
|
|
45
52
|
# Column objects are used to compile information about the data types that a set of data
|
execsql/parser.py
CHANGED
|
@@ -27,6 +27,16 @@ from typing import Any
|
|
|
27
27
|
|
|
28
28
|
from execsql.exceptions import CondParserError, NumericParserError
|
|
29
29
|
|
|
30
|
+
__all__ = [
|
|
31
|
+
"SourceString",
|
|
32
|
+
"CondTokens",
|
|
33
|
+
"NumTokens",
|
|
34
|
+
"CondAstNode",
|
|
35
|
+
"NumericAstNode",
|
|
36
|
+
"CondParser",
|
|
37
|
+
"NumericParser",
|
|
38
|
+
]
|
|
39
|
+
|
|
30
40
|
|
|
31
41
|
class SourceString:
|
|
32
42
|
def __init__(self, source_string: str) -> None:
|
execsql/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Core script-execution engine for execsql.
|
|
5
|
+
|
|
6
|
+
This module contains the data structures and functions that load, parse, and
|
|
7
|
+
drive execution of execsql ``.sql`` script files. It is the heart of the
|
|
8
|
+
runtime.
|
|
9
|
+
|
|
10
|
+
Key classes:
|
|
11
|
+
|
|
12
|
+
- :class:`BatchLevels` — tracks which databases are used in nested BEGIN/END
|
|
13
|
+
BATCH blocks for commit/rollback handling.
|
|
14
|
+
- :class:`IfItem` / :class:`IfLevels` — stack-based IF/ELSE/ENDIF nesting.
|
|
15
|
+
- :class:`CounterVars` — named integer counters (``@NAME``).
|
|
16
|
+
- :class:`SubVarSet` — global ``!!$VAR!!`` substitution-variable store, plus
|
|
17
|
+
``&ENV``, ``@COUNTER``, ``~LOCAL``, and ``#ARG`` prefixes.
|
|
18
|
+
- :class:`LocalSubVarSet` / :class:`ScriptArgSubVarSet` — per-script-scope
|
|
19
|
+
variable overlays.
|
|
20
|
+
- :class:`MetaCommand` — one entry in the metacommand dispatch table (regex +
|
|
21
|
+
handler function + flags).
|
|
22
|
+
- :class:`MetaCommandList` — ordered list of :class:`MetaCommand` entries;
|
|
23
|
+
``get_match()`` finds the first matching entry for a given line.
|
|
24
|
+
- :class:`SqlStmt` — wraps a single SQL string; ``run()`` executes it via the
|
|
25
|
+
active database connection.
|
|
26
|
+
- :class:`MetacommandStmt` — wraps a metacommand line; ``run()`` dispatches
|
|
27
|
+
through :attr:`execsql.state.metacommandlist`.
|
|
28
|
+
- :class:`ScriptCmd` — pairs a statement with its source-file location.
|
|
29
|
+
- :class:`CommandList` — ordered list of :class:`ScriptCmd` objects plus an
|
|
30
|
+
execution cursor; ``run_next()`` drives one step of execution.
|
|
31
|
+
- :class:`CommandListWhileLoop` / :class:`CommandListUntilLoop` — loop
|
|
32
|
+
variants of :class:`CommandList` that re-evaluate a condition each pass.
|
|
33
|
+
- :class:`ScriptFile` — reads and tokenises a ``.sql`` file into
|
|
34
|
+
:class:`ScriptCmd` objects.
|
|
35
|
+
- :class:`ScriptExecSpec` — specification for deferred script execution.
|
|
36
|
+
|
|
37
|
+
Key functions:
|
|
38
|
+
|
|
39
|
+
- :func:`set_system_vars` — populates built-in ``$VARNAME`` system variables.
|
|
40
|
+
- :func:`substitute_vars` — performs ``!!$VAR!!`` and ``!{$var}!`` expansion.
|
|
41
|
+
- :func:`runscripts` — central execution loop; pops the top
|
|
42
|
+
:class:`CommandList` from ``_state.commandliststack`` and drives
|
|
43
|
+
``run_next()`` until the stack is empty.
|
|
44
|
+
- :func:`current_script_line` — returns the source location of the currently
|
|
45
|
+
executing command.
|
|
46
|
+
- :func:`read_sqlfile` — parses a SQL script file into a new
|
|
47
|
+
:class:`CommandList` and pushes it onto ``_state.commandliststack``.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
from execsql.script.control import BatchLevels, IfItem, IfLevels
|
|
51
|
+
from execsql.script.engine import (
|
|
52
|
+
CommandList,
|
|
53
|
+
CommandListUntilLoop,
|
|
54
|
+
CommandListWhileLoop,
|
|
55
|
+
MetaCommand,
|
|
56
|
+
MetaCommandList,
|
|
57
|
+
MetacommandStmt,
|
|
58
|
+
ScriptCmd,
|
|
59
|
+
ScriptExecSpec,
|
|
60
|
+
ScriptFile,
|
|
61
|
+
SqlStmt,
|
|
62
|
+
current_script_line,
|
|
63
|
+
read_sqlfile,
|
|
64
|
+
read_sqlstring,
|
|
65
|
+
runscripts,
|
|
66
|
+
set_system_vars,
|
|
67
|
+
substitute_vars,
|
|
68
|
+
)
|
|
69
|
+
from execsql.script.variables import CounterVars, LocalSubVarSet, ScriptArgSubVarSet, SubVarSet
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
"BatchLevels",
|
|
73
|
+
"IfItem",
|
|
74
|
+
"IfLevels",
|
|
75
|
+
"CounterVars",
|
|
76
|
+
"SubVarSet",
|
|
77
|
+
"LocalSubVarSet",
|
|
78
|
+
"ScriptArgSubVarSet",
|
|
79
|
+
"MetaCommand",
|
|
80
|
+
"MetaCommandList",
|
|
81
|
+
"SqlStmt",
|
|
82
|
+
"MetacommandStmt",
|
|
83
|
+
"ScriptCmd",
|
|
84
|
+
"CommandList",
|
|
85
|
+
"CommandListWhileLoop",
|
|
86
|
+
"CommandListUntilLoop",
|
|
87
|
+
"ScriptFile",
|
|
88
|
+
"ScriptExecSpec",
|
|
89
|
+
"set_system_vars",
|
|
90
|
+
"substitute_vars",
|
|
91
|
+
"runscripts",
|
|
92
|
+
"current_script_line",
|
|
93
|
+
"read_sqlfile",
|
|
94
|
+
"read_sqlstring",
|
|
95
|
+
]
|