execsql2 2.0.1__py3-none-any.whl → 2.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- execsql/cli.py +322 -108
- execsql/config.py +134 -114
- execsql/db/access.py +89 -65
- execsql/db/base.py +97 -68
- execsql/db/dsn.py +45 -29
- execsql/db/duckdb.py +4 -5
- execsql/db/factory.py +27 -27
- execsql/db/firebird.py +30 -18
- execsql/db/mysql.py +38 -14
- execsql/db/oracle.py +58 -33
- execsql/db/postgres.py +68 -28
- execsql/db/sqlite.py +36 -27
- execsql/db/sqlserver.py +45 -30
- execsql/exceptions.py +68 -64
- execsql/exporters/__init__.py +1 -1
- execsql/exporters/base.py +42 -17
- execsql/exporters/delimited.py +60 -59
- execsql/exporters/duckdb.py +8 -12
- execsql/exporters/feather.py +32 -24
- execsql/exporters/html.py +33 -30
- execsql/exporters/json.py +18 -17
- execsql/exporters/latex.py +11 -13
- execsql/exporters/ods.py +50 -46
- execsql/exporters/parquet.py +32 -0
- execsql/exporters/pretty.py +16 -15
- execsql/exporters/raw.py +9 -11
- execsql/exporters/sqlite.py +38 -38
- execsql/exporters/templates.py +15 -72
- execsql/exporters/values.py +13 -12
- execsql/exporters/xls.py +26 -26
- execsql/exporters/xml.py +12 -12
- execsql/exporters/zip.py +0 -3
- execsql/gui/__init__.py +2 -2
- execsql/gui/console.py +0 -1
- execsql/gui/desktop.py +6 -7
- execsql/gui/tui.py +8 -14
- execsql/importers/base.py +6 -9
- execsql/importers/csv.py +10 -17
- execsql/importers/feather.py +16 -22
- execsql/importers/ods.py +3 -4
- execsql/importers/xls.py +5 -6
- execsql/metacommands/__init__.py +8 -8
- execsql/metacommands/conditions.py +41 -33
- execsql/metacommands/connect.py +113 -99
- execsql/metacommands/control.py +38 -26
- execsql/metacommands/data.py +35 -33
- execsql/metacommands/debug.py +13 -9
- execsql/metacommands/io.py +288 -229
- execsql/metacommands/prompt.py +179 -157
- execsql/metacommands/script_ext.py +11 -9
- execsql/metacommands/system.py +44 -25
- execsql/models.py +9 -16
- execsql/parser.py +10 -10
- execsql/script.py +183 -157
- execsql/state.py +170 -208
- execsql/types.py +46 -81
- execsql/utils/auth.py +114 -14
- execsql/utils/crypto.py +31 -4
- execsql/utils/datetime.py +7 -7
- execsql/utils/errors.py +34 -29
- execsql/utils/fileio.py +90 -55
- execsql/utils/gui.py +22 -23
- execsql/utils/mail.py +15 -17
- execsql/utils/numeric.py +2 -3
- execsql/utils/regex.py +9 -12
- execsql/utils/strings.py +10 -12
- execsql/utils/timer.py +0 -2
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/execsql.conf +1 -1
- execsql2-2.1.2.dist-info/METADATA +300 -0
- execsql2-2.1.2.dist-info/RECORD +96 -0
- execsql2-2.0.1.dist-info/METADATA +0 -406
- execsql2-2.0.1.dist-info/RECORD +0 -95
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/READ_ME.rst +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.0.1.data → execsql2-2.1.2.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/WHEEL +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/entry_points.txt +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.0.1.dist-info → execsql2-2.1.2.dist-info}/licenses/NOTICE +0 -0
execsql/metacommands/data.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from execsql.exceptions import ErrInfo
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
Data import and export metacommand handlers for execsql.
|
|
@@ -14,11 +15,17 @@ import math
|
|
|
14
15
|
from typing import Any
|
|
15
16
|
|
|
16
17
|
import execsql.state as _state
|
|
18
|
+
from execsql.parser import NumericParser
|
|
19
|
+
from execsql.script import current_script_line
|
|
20
|
+
from execsql.utils.crypto import Encrypt
|
|
21
|
+
from execsql.utils.errors import exception_desc, exit_now
|
|
22
|
+
from execsql.utils.gui import GUI_SELECTSUB, GuiSpec, enable_gui
|
|
23
|
+
from execsql.utils.strings import get_subvarset, unquoted
|
|
17
24
|
|
|
18
25
|
|
|
19
26
|
def x_sub(**kwargs: Any) -> None:
|
|
20
27
|
varname = kwargs["match"]
|
|
21
|
-
subvarset, varname =
|
|
28
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
22
29
|
subvarset.add_substitution(varname, kwargs["repl"])
|
|
23
30
|
return None
|
|
24
31
|
|
|
@@ -26,21 +33,21 @@ def x_sub(**kwargs: Any) -> None:
|
|
|
26
33
|
def x_sub_add(**kwargs: Any) -> None:
|
|
27
34
|
varname = kwargs["match"]
|
|
28
35
|
increment_expr = kwargs["increment"]
|
|
29
|
-
subvarset, varname =
|
|
30
|
-
subvarset.increment_by(varname,
|
|
36
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
37
|
+
subvarset.increment_by(varname, NumericParser(increment_expr).parse().eval())
|
|
31
38
|
return None
|
|
32
39
|
|
|
33
40
|
|
|
34
41
|
def x_sub_append(**kwargs: Any) -> None:
|
|
35
42
|
varname = kwargs["match"]
|
|
36
|
-
subvarset, varname =
|
|
43
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
37
44
|
subvarset.append_substitution(varname, kwargs["repl"])
|
|
38
45
|
return None
|
|
39
46
|
|
|
40
47
|
|
|
41
48
|
def x_sub_empty(**kwargs: Any) -> None:
|
|
42
49
|
varname = kwargs["match"]
|
|
43
|
-
subvarset, varname =
|
|
50
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
44
51
|
subvarset.add_substitution(varname, "")
|
|
45
52
|
return None
|
|
46
53
|
|
|
@@ -62,7 +69,7 @@ def x_sub_local(**kwargs: Any) -> None:
|
|
|
62
69
|
|
|
63
70
|
def x_sub_tempfile(**kwargs: Any) -> None:
|
|
64
71
|
varname = kwargs["match"]
|
|
65
|
-
subvarset, varname =
|
|
72
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
66
73
|
subvarset.add_substitution(varname, _state.tempfiles.new_temp_fn())
|
|
67
74
|
return None
|
|
68
75
|
|
|
@@ -78,7 +85,7 @@ def x_sub_ini(**kwargs: Any) -> None:
|
|
|
78
85
|
varsect = cp.items(ini_sect)
|
|
79
86
|
for sub, repl in varsect:
|
|
80
87
|
if not _state.subvars.var_name_ok(sub):
|
|
81
|
-
raise
|
|
88
|
+
raise ErrInfo(type="error", other_msg=f"Invalid variable name in SUB_INI file: {sub}")
|
|
82
89
|
_state.subvars.add_substitution(sub, repl)
|
|
83
90
|
|
|
84
91
|
|
|
@@ -94,15 +101,15 @@ def x_sub_querystring(**kwargs: Any) -> None:
|
|
|
94
101
|
|
|
95
102
|
def x_sub_encrypt(**kwargs: Any) -> None:
|
|
96
103
|
varname = kwargs["match"]
|
|
97
|
-
subvarset, varname =
|
|
98
|
-
subvarset.add_substitution(varname,
|
|
104
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
105
|
+
subvarset.add_substitution(varname, Encrypt().encrypt(kwargs["plaintext"]))
|
|
99
106
|
return None
|
|
100
107
|
|
|
101
108
|
|
|
102
109
|
def x_sub_decrypt(**kwargs: Any) -> None:
|
|
103
110
|
varname = kwargs["match"]
|
|
104
|
-
subvarset, varname =
|
|
105
|
-
subvarset.add_substitution(varname,
|
|
111
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
112
|
+
subvarset.add_substitution(varname, Encrypt().decrypt(kwargs["crypttext"]))
|
|
106
113
|
return None
|
|
107
114
|
|
|
108
115
|
|
|
@@ -110,21 +117,16 @@ def x_subdata(**kwargs: Any) -> None:
|
|
|
110
117
|
varname = kwargs["match"]
|
|
111
118
|
sql = f"select * from {kwargs['datasource']};"
|
|
112
119
|
db = _state.dbs.current()
|
|
113
|
-
|
|
114
|
-
errmsg = (
|
|
115
|
-
f"There are no data in {kwargs['datasource']} to use with the SUBDATA metacommand "
|
|
116
|
-
f"(script {script}, line {line_no})."
|
|
117
|
-
)
|
|
118
|
-
subvarset, varname = _state.get_subvarset(varname, kwargs["metacommandline"])
|
|
120
|
+
subvarset, varname = get_subvarset(varname, kwargs["metacommandline"])
|
|
119
121
|
subvarset.remove_substitution(varname)
|
|
120
122
|
try:
|
|
121
|
-
|
|
122
|
-
except
|
|
123
|
+
_, rec = db.select_rowsource(sql)
|
|
124
|
+
except ErrInfo:
|
|
123
125
|
raise
|
|
124
126
|
except Exception:
|
|
125
|
-
raise
|
|
127
|
+
raise ErrInfo(
|
|
126
128
|
type="exception",
|
|
127
|
-
exception_msg=
|
|
129
|
+
exception_msg=exception_desc(),
|
|
128
130
|
other_msg=f"Can't get headers and rows from {sql}.",
|
|
129
131
|
)
|
|
130
132
|
try:
|
|
@@ -144,19 +146,19 @@ def x_subdata(**kwargs: Any) -> None:
|
|
|
144
146
|
def x_selectsub(**kwargs: Any) -> None:
|
|
145
147
|
sql = f"select * from {kwargs['datasource']};"
|
|
146
148
|
db = _state.dbs.current()
|
|
147
|
-
script, line_no =
|
|
149
|
+
script, line_no = current_script_line()
|
|
148
150
|
nodatamsg = (
|
|
149
151
|
f"There are no data in {kwargs['datasource']} to use with the SELECT_SUB metacommand "
|
|
150
152
|
f"(script {script}, line {line_no})."
|
|
151
153
|
)
|
|
152
154
|
try:
|
|
153
155
|
hdrs, rec = db.select_rowsource(sql)
|
|
154
|
-
except
|
|
156
|
+
except ErrInfo:
|
|
155
157
|
raise
|
|
156
158
|
except Exception:
|
|
157
|
-
raise
|
|
159
|
+
raise ErrInfo(
|
|
158
160
|
type="exception",
|
|
159
|
-
exception_msg=
|
|
161
|
+
exception_msg=exception_desc(),
|
|
160
162
|
other_msg=f"Can't get headers and rows from {sql}.",
|
|
161
163
|
)
|
|
162
164
|
for subvar in hdrs:
|
|
@@ -170,7 +172,7 @@ def x_selectsub(**kwargs: Any) -> None:
|
|
|
170
172
|
except StopIteration:
|
|
171
173
|
row1 = None
|
|
172
174
|
except Exception:
|
|
173
|
-
raise
|
|
175
|
+
raise ErrInfo(type="exception", exception_msg=exception_desc(), other_msg=nodatamsg)
|
|
174
176
|
if row1:
|
|
175
177
|
for i, item in enumerate(row1):
|
|
176
178
|
if item is None:
|
|
@@ -194,20 +196,20 @@ def x_prompt_selectsub(**kwargs: Any) -> None:
|
|
|
194
196
|
table = kwargs["table"]
|
|
195
197
|
msg = kwargs["msg"]
|
|
196
198
|
cont = kwargs["cont"]
|
|
197
|
-
help_url =
|
|
199
|
+
help_url = unquoted(kwargs["help"])
|
|
198
200
|
db = _state.dbs.current()
|
|
199
201
|
sq_name = db.schema_qualified_table_name(schema, table)
|
|
200
202
|
sql = f"select * from {sq_name};"
|
|
201
203
|
hdrs, rows = db.select_data(sql)
|
|
202
204
|
if len(rows) == 0:
|
|
203
|
-
raise
|
|
205
|
+
raise ErrInfo(type="error", other_msg=f"The table {sq_name} has no rows to display.")
|
|
204
206
|
for subvar in hdrs:
|
|
205
207
|
subvar = "@" + subvar
|
|
206
208
|
_state.subvars.remove_substitution(subvar)
|
|
207
209
|
btns = [("OK", 1, "O")]
|
|
208
210
|
if cont:
|
|
209
211
|
btns.append(("Continue", 2, "<Return>"))
|
|
210
|
-
|
|
212
|
+
enable_gui()
|
|
211
213
|
return_queue = _queue.Queue()
|
|
212
214
|
gui_args = {
|
|
213
215
|
"title": "Select data",
|
|
@@ -217,14 +219,14 @@ def x_prompt_selectsub(**kwargs: Any) -> None:
|
|
|
217
219
|
"rowset": rows,
|
|
218
220
|
"help_url": help_url,
|
|
219
221
|
}
|
|
220
|
-
_state.gui_manager_queue.put(
|
|
222
|
+
_state.gui_manager_queue.put(GuiSpec(GUI_SELECTSUB, gui_args, return_queue))
|
|
221
223
|
user_response = return_queue.get(block=True)
|
|
222
224
|
btn_val = user_response["button"]
|
|
223
225
|
return_val = user_response["return_value"]
|
|
224
226
|
selected_row = None
|
|
225
227
|
if btn_val and btn_val == 1:
|
|
226
228
|
selected_row = rows[int(return_val[0])]
|
|
227
|
-
script, line_no =
|
|
229
|
+
script, line_no = current_script_line()
|
|
228
230
|
if btn_val is None or (btn_val == 1 and selected_row is None):
|
|
229
231
|
if _state.status.cancel_halt:
|
|
230
232
|
_state.exec_log.log_exit_halt(
|
|
@@ -232,7 +234,7 @@ def x_prompt_selectsub(**kwargs: Any) -> None:
|
|
|
232
234
|
line_no,
|
|
233
235
|
msg=f"Halted from prompt for row of {sq_name} on line {line_no} of {script}",
|
|
234
236
|
)
|
|
235
|
-
|
|
237
|
+
exit_now(2, None)
|
|
236
238
|
else:
|
|
237
239
|
if btn_val == 1:
|
|
238
240
|
for i, item in enumerate(selected_row):
|
|
@@ -347,7 +349,7 @@ def x_reset_counters(**kwargs: Any) -> None:
|
|
|
347
349
|
def x_set_counter(**kwargs: Any) -> None:
|
|
348
350
|
ctr_no = int(kwargs["counter_no"])
|
|
349
351
|
ctr_expr = kwargs["value"]
|
|
350
|
-
_state.counters.set_counter(ctr_no, int(math.floor(
|
|
352
|
+
_state.counters.set_counter(ctr_no, int(math.floor(NumericParser(ctr_expr).parse().eval())))
|
|
351
353
|
|
|
352
354
|
|
|
353
355
|
def x_max_int(**kwargs: Any) -> None:
|
execsql/metacommands/debug.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from execsql.utils.errors import fatal_error
|
|
2
3
|
|
|
3
4
|
"""
|
|
4
5
|
Debug metacommand handlers for execsql.
|
|
@@ -11,6 +12,7 @@ metacommand list to the log/console for troubleshooting.
|
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
13
14
|
import execsql.state as _state
|
|
15
|
+
from execsql.utils.fileio import EncodedFile, filewriter_open_as_new, filewriter_write
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def x_debug_write_metacommands(**kwargs: Any) -> None:
|
|
@@ -18,7 +20,7 @@ def x_debug_write_metacommands(**kwargs: Any) -> None:
|
|
|
18
20
|
if output_dest is None or output_dest == "stdout":
|
|
19
21
|
ofile = _state.output
|
|
20
22
|
else:
|
|
21
|
-
ofile =
|
|
23
|
+
ofile = EncodedFile(output_dest, _state.conf.output_encoding).open("w")
|
|
22
24
|
for m in _state.metacommandlist:
|
|
23
25
|
ofile.write(f"({m.hitcount}) {m.rx.pattern}\n")
|
|
24
26
|
|
|
@@ -38,7 +40,9 @@ def x_debug_iflevels(**kwargs: Any) -> None:
|
|
|
38
40
|
if len(_state.if_stack.if_levels) == 0:
|
|
39
41
|
_state.output.write("If levels: None\n")
|
|
40
42
|
else:
|
|
41
|
-
_state.output.write(
|
|
43
|
+
_state.output.write(
|
|
44
|
+
"If levels: [{}]\n".format(",".join([str(tf.tf_value) for tf in _state.if_stack.if_levels])),
|
|
45
|
+
)
|
|
42
46
|
return None
|
|
43
47
|
|
|
44
48
|
|
|
@@ -46,7 +50,7 @@ def x_debug_write_odbc_drivers(**kwargs: Any) -> None:
|
|
|
46
50
|
try:
|
|
47
51
|
import pyodbc
|
|
48
52
|
except Exception:
|
|
49
|
-
|
|
53
|
+
fatal_error("The pyodbc module is required.")
|
|
50
54
|
output_dest = kwargs["filename"]
|
|
51
55
|
append = kwargs["append"]
|
|
52
56
|
|
|
@@ -55,8 +59,8 @@ def x_debug_write_odbc_drivers(**kwargs: Any) -> None:
|
|
|
55
59
|
_state.output.write(txt)
|
|
56
60
|
else:
|
|
57
61
|
if not append:
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
filewriter_open_as_new(output_dest)
|
|
63
|
+
filewriter_write(output_dest, txt)
|
|
60
64
|
|
|
61
65
|
for d in pyodbc.drivers():
|
|
62
66
|
write(f"{d}\n")
|
|
@@ -126,13 +130,13 @@ def x_debug_write_subvars(**kwargs: Any) -> None:
|
|
|
126
130
|
user = kwargs["user"]
|
|
127
131
|
local = kwargs["local"]
|
|
128
132
|
if output_dest is not None and output_dest != "stdout" and append is None:
|
|
129
|
-
|
|
133
|
+
filewriter_open_as_new(output_dest)
|
|
130
134
|
|
|
131
135
|
def write(txt: str) -> None:
|
|
132
136
|
if output_dest is None or output_dest == "stdout":
|
|
133
137
|
_state.output.write(txt)
|
|
134
138
|
else:
|
|
135
|
-
|
|
139
|
+
filewriter_write(output_dest, txt)
|
|
136
140
|
|
|
137
141
|
for s in _state.commandliststack[-1].localvars.substitutions:
|
|
138
142
|
write(f"Substitution [{s[0]}] = [{s[1]}]\n")
|
|
@@ -153,13 +157,13 @@ def x_debug_write_config(**kwargs: Any) -> None:
|
|
|
153
157
|
f"Config; GUI level = {conf.gui_level}",
|
|
154
158
|
]
|
|
155
159
|
if output_dest is not None and output_dest != "stdout" and append is None:
|
|
156
|
-
|
|
160
|
+
filewriter_open_as_new(output_dest)
|
|
157
161
|
|
|
158
162
|
def write(txt: str) -> None:
|
|
159
163
|
if output_dest is None or output_dest == "stdout":
|
|
160
164
|
_state.output.write(txt)
|
|
161
165
|
else:
|
|
162
|
-
|
|
166
|
+
filewriter_write(output_dest, txt)
|
|
163
167
|
|
|
164
168
|
for line in lines:
|
|
165
169
|
write(f"{line}\n")
|