execsql2 2.15.8__py3-none-any.whl → 2.16.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/__init__.py +8 -3
- execsql/api.py +580 -0
- execsql/cli/__init__.py +123 -0
- execsql/cli/lint_ast.py +439 -0
- execsql/cli/run.py +113 -102
- execsql/config.py +29 -4
- execsql/db/access.py +1 -0
- execsql/db/base.py +4 -1
- execsql/db/dsn.py +3 -2
- execsql/db/duckdb.py +1 -1
- execsql/db/factory.py +3 -0
- execsql/db/firebird.py +2 -1
- execsql/db/mysql.py +2 -1
- execsql/db/oracle.py +2 -1
- execsql/db/postgres.py +2 -1
- execsql/db/sqlite.py +1 -1
- execsql/db/sqlserver.py +3 -2
- execsql/debug/repl.py +27 -10
- execsql/exporters/base.py +6 -4
- execsql/exporters/delimited.py +11 -3
- execsql/exporters/pretty.py +9 -12
- execsql/gui/tui.py +59 -2
- execsql/metacommands/__init__.py +3 -0
- execsql/metacommands/conditions.py +20 -2
- execsql/metacommands/connect.py +1 -1
- execsql/metacommands/control.py +8 -14
- execsql/metacommands/debug.py +6 -4
- execsql/metacommands/io_export.py +117 -315
- execsql/metacommands/io_fileops.py +7 -13
- execsql/metacommands/io_write.py +1 -1
- execsql/metacommands/script_ext.py +8 -5
- execsql/metacommands/upsert.py +40 -0
- execsql/models.py +8 -12
- execsql/plugins.py +414 -0
- execsql/script/__init__.py +36 -12
- execsql/script/ast.py +562 -0
- execsql/script/engine.py +59 -368
- execsql/script/executor.py +833 -0
- execsql/script/parser.py +663 -0
- execsql/script/variables.py +11 -0
- execsql/state.py +55 -2
- execsql/utils/crypto.py +14 -10
- execsql/utils/errors.py +31 -8
- execsql/utils/gui.py +139 -17
- execsql/utils/mail.py +15 -12
- {execsql2-2.15.8.dist-info → execsql2-2.16.0.dist-info}/METADATA +59 -1
- {execsql2-2.15.8.dist-info → execsql2-2.16.0.dist-info}/RECORD +66 -60
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/README.md +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/execsql.conf +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/md_compare.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/script_template.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
- {execsql2-2.15.8.data → execsql2-2.16.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
- {execsql2-2.15.8.dist-info → execsql2-2.16.0.dist-info}/WHEEL +0 -0
- {execsql2-2.15.8.dist-info → execsql2-2.16.0.dist-info}/entry_points.txt +0 -0
- {execsql2-2.15.8.dist-info → execsql2-2.16.0.dist-info}/licenses/LICENSE.txt +0 -0
- {execsql2-2.15.8.dist-info → execsql2-2.16.0.dist-info}/licenses/NOTICE +0 -0
execsql/plugins.py
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
"""Plugin discovery and registration for execsql.
|
|
2
|
+
|
|
3
|
+
Discovers and loads plugins via Python `entry_points`_. Plugins can
|
|
4
|
+
provide custom metacommands, export formats, and import formats.
|
|
5
|
+
|
|
6
|
+
Entry point groups:
|
|
7
|
+
|
|
8
|
+
- ``execsql.metacommands`` — custom metacommand handlers
|
|
9
|
+
- ``execsql.exporters`` — custom export formats
|
|
10
|
+
- ``execsql.importers`` — custom import formats
|
|
11
|
+
|
|
12
|
+
Each entry point should reference a **registration function** that
|
|
13
|
+
receives the appropriate registry and adds its entries.
|
|
14
|
+
|
|
15
|
+
Metacommand plugin example
|
|
16
|
+
--------------------------
|
|
17
|
+
|
|
18
|
+
In the plugin package's ``pyproject.toml``::
|
|
19
|
+
|
|
20
|
+
[project.entry-points."execsql.metacommands"]
|
|
21
|
+
my_plugin = "my_package.execsql_plugin:register"
|
|
22
|
+
|
|
23
|
+
The registration function receives a
|
|
24
|
+
:class:`~execsql.script.engine.MetaCommandList` and adds entries::
|
|
25
|
+
|
|
26
|
+
def register(mcl):
|
|
27
|
+
mcl.add(
|
|
28
|
+
r"^\\s*MY_COMMAND\\s+(?P<arg>.+)\\s*$",
|
|
29
|
+
my_handler,
|
|
30
|
+
description="MY_COMMAND",
|
|
31
|
+
category="action",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def my_handler(**kwargs):
|
|
35
|
+
import execsql.state as _state
|
|
36
|
+
arg = kwargs["arg"]
|
|
37
|
+
# ... do work ...
|
|
38
|
+
|
|
39
|
+
Exporter plugin example
|
|
40
|
+
-----------------------
|
|
41
|
+
|
|
42
|
+
In ``pyproject.toml``::
|
|
43
|
+
|
|
44
|
+
[project.entry-points."execsql.exporters"]
|
|
45
|
+
my_format = "my_package.execsql_export:register"
|
|
46
|
+
|
|
47
|
+
The registration function receives an :class:`ExporterRegistry` and
|
|
48
|
+
adds entries::
|
|
49
|
+
|
|
50
|
+
def register(registry):
|
|
51
|
+
registry.add(
|
|
52
|
+
format_name="myformat",
|
|
53
|
+
query_fn=my_query_exporter,
|
|
54
|
+
table_fn=my_table_exporter, # optional
|
|
55
|
+
description="My custom format",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
.. _entry_points: https://packaging.python.org/en/latest/specifications/entry-points/
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
from __future__ import annotations
|
|
62
|
+
|
|
63
|
+
import logging
|
|
64
|
+
from collections.abc import Callable
|
|
65
|
+
from importlib.metadata import entry_points
|
|
66
|
+
from typing import Any
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"ExporterEntry",
|
|
70
|
+
"ExporterRegistry",
|
|
71
|
+
"ImporterEntry",
|
|
72
|
+
"ImporterRegistry",
|
|
73
|
+
"discover_metacommand_plugins",
|
|
74
|
+
"discover_exporter_plugins",
|
|
75
|
+
"discover_importer_plugins",
|
|
76
|
+
"discover_all_plugins",
|
|
77
|
+
"get_exporter_registry",
|
|
78
|
+
"get_importer_registry",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
_log = logging.getLogger(__name__)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# Entry point group names
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
METACOMMAND_GROUP = "execsql.metacommands"
|
|
89
|
+
EXPORTER_GROUP = "execsql.exporters"
|
|
90
|
+
IMPORTER_GROUP = "execsql.importers"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Exporter registry
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ExporterEntry:
|
|
99
|
+
"""A registered export format.
|
|
100
|
+
|
|
101
|
+
Attributes:
|
|
102
|
+
format_name: The format keyword (e.g. ``"csv"``, ``"myformat"``).
|
|
103
|
+
query_fn: Function for exporting query results.
|
|
104
|
+
table_fn: Function for exporting full tables (optional).
|
|
105
|
+
description: Human-readable description.
|
|
106
|
+
plugin_name: Name of the plugin that registered this entry, or
|
|
107
|
+
``"built-in"`` for formats shipped with execsql.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
__slots__ = ("format_name", "query_fn", "table_fn", "description", "plugin_name")
|
|
111
|
+
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
format_name: str,
|
|
115
|
+
query_fn: Callable | None = None,
|
|
116
|
+
table_fn: Callable | None = None,
|
|
117
|
+
description: str = "",
|
|
118
|
+
plugin_name: str = "built-in",
|
|
119
|
+
) -> None:
|
|
120
|
+
self.format_name = format_name.upper()
|
|
121
|
+
self.query_fn = query_fn
|
|
122
|
+
self.table_fn = table_fn
|
|
123
|
+
self.description = description
|
|
124
|
+
self.plugin_name = plugin_name
|
|
125
|
+
|
|
126
|
+
def __repr__(self) -> str:
|
|
127
|
+
return f"ExporterEntry({self.format_name!r}, plugin={self.plugin_name!r})"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class ExporterRegistry:
|
|
131
|
+
"""Registry of available export formats.
|
|
132
|
+
|
|
133
|
+
Built-in formats are registered during initialization. Plugins add
|
|
134
|
+
their formats via :meth:`add`.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
def __init__(self) -> None:
|
|
138
|
+
self._entries: dict[str, ExporterEntry] = {}
|
|
139
|
+
|
|
140
|
+
def add(
|
|
141
|
+
self,
|
|
142
|
+
format_name: str,
|
|
143
|
+
query_fn: Callable | None = None,
|
|
144
|
+
table_fn: Callable | None = None,
|
|
145
|
+
description: str = "",
|
|
146
|
+
plugin_name: str = "built-in",
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Register an export format.
|
|
149
|
+
|
|
150
|
+
If a format with the same name already exists, the new entry
|
|
151
|
+
overwrites it (plugins can override built-in formats).
|
|
152
|
+
"""
|
|
153
|
+
key = format_name.upper()
|
|
154
|
+
if key in self._entries and self._entries[key].plugin_name != plugin_name:
|
|
155
|
+
_log.info(
|
|
156
|
+
"Plugin %r overrides export format %r (was %r)",
|
|
157
|
+
plugin_name,
|
|
158
|
+
key,
|
|
159
|
+
self._entries[key].plugin_name,
|
|
160
|
+
)
|
|
161
|
+
self._entries[key] = ExporterEntry(
|
|
162
|
+
format_name=key,
|
|
163
|
+
query_fn=query_fn,
|
|
164
|
+
table_fn=table_fn,
|
|
165
|
+
description=description,
|
|
166
|
+
plugin_name=plugin_name,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def get(self, format_name: str) -> ExporterEntry | None:
|
|
170
|
+
"""Look up an export format by name (case-insensitive)."""
|
|
171
|
+
return self._entries.get(format_name.upper())
|
|
172
|
+
|
|
173
|
+
def formats(self) -> list[str]:
|
|
174
|
+
"""Return sorted list of registered format names."""
|
|
175
|
+
return sorted(self._entries)
|
|
176
|
+
|
|
177
|
+
def entries(self) -> list[ExporterEntry]:
|
|
178
|
+
"""Return all registered entries."""
|
|
179
|
+
return list(self._entries.values())
|
|
180
|
+
|
|
181
|
+
def __contains__(self, format_name: str) -> bool:
|
|
182
|
+
return format_name.upper() in self._entries
|
|
183
|
+
|
|
184
|
+
def __len__(self) -> int:
|
|
185
|
+
return len(self._entries)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
# Importer registry
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class ImporterEntry:
|
|
194
|
+
"""A registered import format.
|
|
195
|
+
|
|
196
|
+
Attributes:
|
|
197
|
+
format_name: The format keyword (e.g. ``"csv"``, ``"json"``).
|
|
198
|
+
import_fn: Function for importing data.
|
|
199
|
+
description: Human-readable description.
|
|
200
|
+
plugin_name: Name of the plugin that registered this entry.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
__slots__ = ("format_name", "import_fn", "description", "plugin_name")
|
|
204
|
+
|
|
205
|
+
def __init__(
|
|
206
|
+
self,
|
|
207
|
+
format_name: str,
|
|
208
|
+
import_fn: Callable | None = None,
|
|
209
|
+
description: str = "",
|
|
210
|
+
plugin_name: str = "built-in",
|
|
211
|
+
) -> None:
|
|
212
|
+
self.format_name = format_name.upper()
|
|
213
|
+
self.import_fn = import_fn
|
|
214
|
+
self.description = description
|
|
215
|
+
self.plugin_name = plugin_name
|
|
216
|
+
|
|
217
|
+
def __repr__(self) -> str:
|
|
218
|
+
return f"ImporterEntry({self.format_name!r}, plugin={self.plugin_name!r})"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class ImporterRegistry:
|
|
222
|
+
"""Registry of available import formats."""
|
|
223
|
+
|
|
224
|
+
def __init__(self) -> None:
|
|
225
|
+
self._entries: dict[str, ImporterEntry] = {}
|
|
226
|
+
|
|
227
|
+
def add(
|
|
228
|
+
self,
|
|
229
|
+
format_name: str,
|
|
230
|
+
import_fn: Callable | None = None,
|
|
231
|
+
description: str = "",
|
|
232
|
+
plugin_name: str = "built-in",
|
|
233
|
+
) -> None:
|
|
234
|
+
"""Register an import format."""
|
|
235
|
+
key = format_name.upper()
|
|
236
|
+
if key in self._entries and self._entries[key].plugin_name != plugin_name:
|
|
237
|
+
_log.info(
|
|
238
|
+
"Plugin %r overrides import format %r (was %r)",
|
|
239
|
+
plugin_name,
|
|
240
|
+
key,
|
|
241
|
+
self._entries[key].plugin_name,
|
|
242
|
+
)
|
|
243
|
+
self._entries[key] = ImporterEntry(
|
|
244
|
+
format_name=key,
|
|
245
|
+
import_fn=import_fn,
|
|
246
|
+
description=description,
|
|
247
|
+
plugin_name=plugin_name,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
def get(self, format_name: str) -> ImporterEntry | None:
|
|
251
|
+
return self._entries.get(format_name.upper())
|
|
252
|
+
|
|
253
|
+
def formats(self) -> list[str]:
|
|
254
|
+
return sorted(self._entries)
|
|
255
|
+
|
|
256
|
+
def entries(self) -> list[ImporterEntry]:
|
|
257
|
+
return list(self._entries.values())
|
|
258
|
+
|
|
259
|
+
def __contains__(self, format_name: str) -> bool:
|
|
260
|
+
return format_name.upper() in self._entries
|
|
261
|
+
|
|
262
|
+
def __len__(self) -> int:
|
|
263
|
+
return len(self._entries)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ---------------------------------------------------------------------------
|
|
267
|
+
# Module-level singletons
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
_exporter_registry: ExporterRegistry | None = None
|
|
271
|
+
_importer_registry: ImporterRegistry | None = None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_exporter_registry() -> ExporterRegistry:
|
|
275
|
+
"""Return the global exporter registry, creating it on first access."""
|
|
276
|
+
global _exporter_registry
|
|
277
|
+
if _exporter_registry is None:
|
|
278
|
+
_exporter_registry = ExporterRegistry()
|
|
279
|
+
return _exporter_registry
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_importer_registry() -> ImporterRegistry:
|
|
283
|
+
"""Return the global importer registry, creating it on first access."""
|
|
284
|
+
global _importer_registry
|
|
285
|
+
if _importer_registry is None:
|
|
286
|
+
_importer_registry = ImporterRegistry()
|
|
287
|
+
return _importer_registry
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# ---------------------------------------------------------------------------
|
|
291
|
+
# Discovery functions
|
|
292
|
+
# ---------------------------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _load_entry_points(group: str) -> list[tuple[str, Any]]:
|
|
296
|
+
"""Load all entry points for a group, returning (name, loaded_object) pairs.
|
|
297
|
+
|
|
298
|
+
Errors during loading are logged and skipped — a broken plugin should
|
|
299
|
+
not prevent execsql from starting.
|
|
300
|
+
"""
|
|
301
|
+
results = []
|
|
302
|
+
try:
|
|
303
|
+
eps = entry_points(group=group)
|
|
304
|
+
except Exception:
|
|
305
|
+
_log.warning("Failed to query entry points for group %r", group, exc_info=True)
|
|
306
|
+
return results
|
|
307
|
+
|
|
308
|
+
for ep in eps:
|
|
309
|
+
try:
|
|
310
|
+
obj = ep.load()
|
|
311
|
+
results.append((ep.name, obj))
|
|
312
|
+
except Exception:
|
|
313
|
+
_log.warning(
|
|
314
|
+
"Failed to load plugin %r from group %r: %s",
|
|
315
|
+
ep.name,
|
|
316
|
+
group,
|
|
317
|
+
ep.value,
|
|
318
|
+
exc_info=True,
|
|
319
|
+
)
|
|
320
|
+
return results
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def discover_metacommand_plugins(mcl: Any) -> int:
|
|
324
|
+
"""Discover and register metacommand plugins.
|
|
325
|
+
|
|
326
|
+
Each entry point should be a callable that receives a
|
|
327
|
+
:class:`~execsql.script.engine.MetaCommandList` and calls ``mcl.add()``
|
|
328
|
+
to register its commands.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
mcl: The metacommand dispatch table to register into.
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Number of plugins successfully loaded.
|
|
335
|
+
"""
|
|
336
|
+
loaded = 0
|
|
337
|
+
for name, register_fn in _load_entry_points(METACOMMAND_GROUP):
|
|
338
|
+
try:
|
|
339
|
+
register_fn(mcl)
|
|
340
|
+
_log.info("Loaded metacommand plugin: %s", name)
|
|
341
|
+
loaded += 1
|
|
342
|
+
except Exception:
|
|
343
|
+
_log.warning("Metacommand plugin %r failed during registration", name, exc_info=True)
|
|
344
|
+
return loaded
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def discover_exporter_plugins(registry: ExporterRegistry | None = None) -> int:
|
|
348
|
+
"""Discover and register exporter plugins.
|
|
349
|
+
|
|
350
|
+
Each entry point should be a callable that receives an
|
|
351
|
+
:class:`ExporterRegistry` and calls ``registry.add()`` to register
|
|
352
|
+
its formats.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
registry: The exporter registry. Defaults to the global singleton.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Number of plugins successfully loaded.
|
|
359
|
+
"""
|
|
360
|
+
if registry is None:
|
|
361
|
+
registry = get_exporter_registry()
|
|
362
|
+
loaded = 0
|
|
363
|
+
for name, register_fn in _load_entry_points(EXPORTER_GROUP):
|
|
364
|
+
try:
|
|
365
|
+
register_fn(registry)
|
|
366
|
+
_log.info("Loaded exporter plugin: %s", name)
|
|
367
|
+
loaded += 1
|
|
368
|
+
except Exception:
|
|
369
|
+
_log.warning("Exporter plugin %r failed during registration", name, exc_info=True)
|
|
370
|
+
return loaded
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def discover_importer_plugins(registry: ImporterRegistry | None = None) -> int:
|
|
374
|
+
"""Discover and register importer plugins.
|
|
375
|
+
|
|
376
|
+
Each entry point should be a callable that receives an
|
|
377
|
+
:class:`ImporterRegistry` and calls ``registry.add()`` to register
|
|
378
|
+
its formats.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
registry: The importer registry. Defaults to the global singleton.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Number of plugins successfully loaded.
|
|
385
|
+
"""
|
|
386
|
+
if registry is None:
|
|
387
|
+
registry = get_importer_registry()
|
|
388
|
+
loaded = 0
|
|
389
|
+
for name, register_fn in _load_entry_points(IMPORTER_GROUP):
|
|
390
|
+
try:
|
|
391
|
+
register_fn(registry)
|
|
392
|
+
_log.info("Loaded importer plugin: %s", name)
|
|
393
|
+
loaded += 1
|
|
394
|
+
except Exception:
|
|
395
|
+
_log.warning("Importer plugin %r failed during registration", name, exc_info=True)
|
|
396
|
+
return loaded
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def discover_all_plugins(mcl: Any = None) -> dict[str, int]:
|
|
400
|
+
"""Discover and load all plugin types.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
mcl: The metacommand dispatch table. If ``None``, metacommand
|
|
404
|
+
plugins are skipped.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
Dict of ``{group_name: count}`` for each plugin type loaded.
|
|
408
|
+
"""
|
|
409
|
+
results = {}
|
|
410
|
+
if mcl is not None:
|
|
411
|
+
results[METACOMMAND_GROUP] = discover_metacommand_plugins(mcl)
|
|
412
|
+
results[EXPORTER_GROUP] = discover_exporter_plugins()
|
|
413
|
+
results[IMPORTER_GROUP] = discover_importer_plugins()
|
|
414
|
+
return results
|
execsql/script/__init__.py
CHANGED
|
@@ -50,24 +50,36 @@ Key functions:
|
|
|
50
50
|
from execsql.script.control import BatchLevels, IfItem, IfLevels
|
|
51
51
|
from execsql.script.engine import (
|
|
52
52
|
CommandList,
|
|
53
|
-
CommandListUntilLoop,
|
|
54
|
-
CommandListWhileLoop,
|
|
55
53
|
MetaCommand,
|
|
56
54
|
MetaCommandList,
|
|
57
55
|
MetacommandStmt,
|
|
58
56
|
ScriptCmd,
|
|
59
57
|
ScriptExecSpec,
|
|
60
|
-
ScriptFile,
|
|
61
58
|
SqlStmt,
|
|
62
59
|
current_script_line,
|
|
63
|
-
read_sqlfile,
|
|
64
|
-
read_sqlstring,
|
|
65
|
-
runscripts,
|
|
66
60
|
set_dynamic_system_vars,
|
|
67
61
|
set_static_system_vars,
|
|
68
62
|
set_system_vars,
|
|
69
63
|
substitute_vars,
|
|
70
64
|
)
|
|
65
|
+
from execsql.script.ast import (
|
|
66
|
+
BatchBlock,
|
|
67
|
+
Comment,
|
|
68
|
+
ConditionModifier,
|
|
69
|
+
ElseIfClause,
|
|
70
|
+
IfBlock,
|
|
71
|
+
IncludeDirective,
|
|
72
|
+
LoopBlock,
|
|
73
|
+
MetaCommandStatement as AstMetaCommand,
|
|
74
|
+
Node,
|
|
75
|
+
Script,
|
|
76
|
+
ScriptBlock,
|
|
77
|
+
SourceSpan,
|
|
78
|
+
SqlBlock,
|
|
79
|
+
SqlStatement as AstSqlStatement,
|
|
80
|
+
format_tree,
|
|
81
|
+
)
|
|
82
|
+
from execsql.script.parser import parse_script, parse_string
|
|
71
83
|
from execsql.script.variables import CounterVars, LocalSubVarSet, ScriptArgSubVarSet, SubVarSet
|
|
72
84
|
|
|
73
85
|
__all__ = [
|
|
@@ -84,16 +96,28 @@ __all__ = [
|
|
|
84
96
|
"MetacommandStmt",
|
|
85
97
|
"ScriptCmd",
|
|
86
98
|
"CommandList",
|
|
87
|
-
"CommandListWhileLoop",
|
|
88
|
-
"CommandListUntilLoop",
|
|
89
|
-
"ScriptFile",
|
|
90
99
|
"ScriptExecSpec",
|
|
91
100
|
"set_dynamic_system_vars",
|
|
92
101
|
"set_static_system_vars",
|
|
93
102
|
"set_system_vars",
|
|
94
103
|
"substitute_vars",
|
|
95
|
-
"runscripts",
|
|
96
104
|
"current_script_line",
|
|
97
|
-
|
|
98
|
-
"
|
|
105
|
+
# AST nodes and parser
|
|
106
|
+
"Node",
|
|
107
|
+
"SourceSpan",
|
|
108
|
+
"AstSqlStatement",
|
|
109
|
+
"AstMetaCommand",
|
|
110
|
+
"Comment",
|
|
111
|
+
"ConditionModifier",
|
|
112
|
+
"ElseIfClause",
|
|
113
|
+
"IfBlock",
|
|
114
|
+
"LoopBlock",
|
|
115
|
+
"BatchBlock",
|
|
116
|
+
"ScriptBlock",
|
|
117
|
+
"SqlBlock",
|
|
118
|
+
"IncludeDirective",
|
|
119
|
+
"Script",
|
|
120
|
+
"format_tree",
|
|
121
|
+
"parse_script",
|
|
122
|
+
"parse_string",
|
|
99
123
|
]
|