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.
Files changed (94) hide show
  1. execsql/cli/__init__.py +436 -0
  2. execsql/cli/dsn.py +86 -0
  3. execsql/cli/help.py +140 -0
  4. execsql/{cli.py → cli/run.py} +14 -589
  5. execsql/config.py +65 -1
  6. execsql/db/access.py +27 -15
  7. execsql/db/base.py +328 -215
  8. execsql/db/dsn.py +10 -5
  9. execsql/db/duckdb.py +6 -2
  10. execsql/db/factory.py +21 -0
  11. execsql/db/firebird.py +27 -19
  12. execsql/db/mysql.py +12 -7
  13. execsql/db/oracle.py +15 -11
  14. execsql/db/postgres.py +31 -16
  15. execsql/db/sqlite.py +15 -11
  16. execsql/db/sqlserver.py +16 -5
  17. execsql/exceptions.py +25 -7
  18. execsql/exporters/base.py +12 -1
  19. execsql/exporters/delimited.py +80 -35
  20. execsql/exporters/duckdb.py +6 -2
  21. execsql/exporters/feather.py +10 -6
  22. execsql/exporters/html.py +89 -69
  23. execsql/exporters/json.py +52 -45
  24. execsql/exporters/latex.py +37 -27
  25. execsql/exporters/ods.py +32 -11
  26. execsql/exporters/parquet.py +5 -2
  27. execsql/exporters/pretty.py +16 -9
  28. execsql/exporters/raw.py +22 -16
  29. execsql/exporters/sqlite.py +6 -2
  30. execsql/exporters/templates.py +39 -21
  31. execsql/exporters/values.py +26 -20
  32. execsql/exporters/xls.py +30 -11
  33. execsql/exporters/xml.py +31 -13
  34. execsql/exporters/zip.py +15 -0
  35. execsql/importers/base.py +6 -4
  36. execsql/importers/csv.py +8 -6
  37. execsql/importers/feather.py +6 -4
  38. execsql/importers/ods.py +6 -4
  39. execsql/importers/xls.py +6 -4
  40. execsql/metacommands/__init__.py +208 -1548
  41. execsql/metacommands/conditions.py +101 -27
  42. execsql/metacommands/control.py +8 -4
  43. execsql/metacommands/data.py +6 -6
  44. execsql/metacommands/debug.py +6 -2
  45. execsql/metacommands/dispatch.py +2011 -0
  46. execsql/metacommands/io.py +67 -1310
  47. execsql/metacommands/io_export.py +442 -0
  48. execsql/metacommands/io_fileops.py +287 -0
  49. execsql/metacommands/io_import.py +398 -0
  50. execsql/metacommands/io_write.py +248 -0
  51. execsql/metacommands/prompt.py +22 -66
  52. execsql/metacommands/system.py +7 -2
  53. execsql/models.py +7 -0
  54. execsql/parser.py +10 -0
  55. execsql/py.typed +0 -0
  56. execsql/script/__init__.py +95 -0
  57. execsql/script/control.py +162 -0
  58. execsql/{script.py → script/engine.py} +184 -402
  59. execsql/script/variables.py +281 -0
  60. execsql/types.py +49 -20
  61. execsql/utils/auth.py +2 -0
  62. execsql/utils/crypto.py +4 -6
  63. execsql/utils/datetime.py +1 -0
  64. execsql/utils/errors.py +11 -0
  65. execsql/utils/fileio.py +33 -8
  66. execsql/utils/gui.py +46 -0
  67. execsql/utils/mail.py +7 -17
  68. execsql/utils/numeric.py +2 -0
  69. execsql/utils/regex.py +9 -0
  70. execsql/utils/strings.py +16 -0
  71. execsql/utils/timer.py +2 -0
  72. execsql2-2.4.0.data/data/execsql2_extras/README.md +65 -0
  73. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/execsql.conf +1 -1
  74. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/METADATA +13 -6
  75. execsql2-2.4.0.dist-info/RECORD +108 -0
  76. execsql2-2.1.2.data/data/execsql2_extras/READ_ME.rst +0 -127
  77. execsql2-2.1.2.dist-info/RECORD +0 -96
  78. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/config_settings.sqlite +0 -0
  79. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/example_config_prompt.sql +0 -0
  80. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/make_config_db.sql +0 -0
  81. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_compare.sql +0 -0
  82. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_glossary.sql +0 -0
  83. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/md_upsert.sql +0 -0
  84. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_compare.sql +0 -0
  85. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_glossary.sql +0 -0
  86. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/pg_upsert.sql +0 -0
  87. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/script_template.sql +0 -0
  88. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_compare.sql +0 -0
  89. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_glossary.sql +0 -0
  90. {execsql2-2.1.2.data → execsql2-2.4.0.data}/data/execsql2_extras/ss_upsert.sql +0 -0
  91. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/WHEEL +0 -0
  92. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/entry_points.txt +0 -0
  93. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/licenses/LICENSE.txt +0 -0
  94. {execsql2-2.1.2.dist-info → execsql2-2.4.0.dist-info}/licenses/NOTICE +0 -0
@@ -1,1313 +1,70 @@
1
- from __future__ import annotations
2
- from execsql.exceptions import ErrInfo
3
-
1
+ """Input/output metacommand handlers for execsql.
2
+
3
+ This module is a **re-export façade**: the actual implementations live in
4
+ the sibling submodules listed below. All public names are imported here so
5
+ that existing ``from execsql.metacommands.io import x_*`` paths continue to
6
+ work without changes.
7
+
8
+ Submodules
9
+ ----------
10
+ - :mod:`~execsql.metacommands.io_export` — EXPORT handlers
11
+ - :mod:`~execsql.metacommands.io_import` — IMPORT handlers
12
+ - :mod:`~execsql.metacommands.io_write` — WRITE / WRITESCRIPT handlers
13
+ - :mod:`~execsql.metacommands.io_fileops` — file ops, COPY, ZIP, INCLUDE, SERVE, etc.
4
14
  """
5
- Input/output metacommand handlers for execsql.
6
-
7
- Implements the ``x_*`` handler functions for all output and I/O-related
8
- metacommands:
9
-
10
- - ``x_write`` / ``x_writeln`` — WRITE messages to log or console
11
- - ``x_copy_file`` — COPY FILE
12
- - ``x_delete_file`` / ``x_rename_file`` — file management
13
- - ``x_pause`` — PAUSE execution until user input
14
- - ``x_tee_log`` — TEE LOG on/off
15
- - ``x_log`` — LOG to file
16
- - ``x_set_write_output`` — redirect WRITE output
17
- """
18
-
19
- import os
20
- import sys
21
- from pathlib import Path
22
- from shutil import copyfileobj
23
- from typing import Any
24
-
25
- import execsql.state as _state
26
- from execsql.exporters.base import ExportRecord
27
- from execsql.exporters.delimited import CsvFile, write_delimited_file
28
- from execsql.exporters.duckdb import write_query_to_duckdb
29
- from execsql.exporters.feather import write_query_to_feather, write_query_to_hdf5
30
- from execsql.exporters.parquet import write_query_to_parquet
31
- from execsql.exporters.html import write_query_to_cgi_html, write_query_to_html
32
- from execsql.exporters.json import write_query_to_json, write_query_to_json_ts
33
- from execsql.exporters.latex import write_query_to_latex
34
- from execsql.exporters.ods import OdsFile, write_queries_to_ods, write_query_to_ods
35
- from execsql.exporters.pretty import prettyprint_query, prettyprint_rowset
36
- from execsql.exporters.raw import write_query_b64, write_query_raw
37
- from execsql.exporters.sqlite import write_query_to_sqlite
38
- from execsql.exporters.templates import report_query
39
- from execsql.exporters.values import write_query_to_values
40
- from execsql.exporters.xls import XlsFile, XlsxFile
41
- from execsql.exporters.xml import write_query_to_xml
42
- from execsql.importers.base import import_data_table
43
- from execsql.importers.csv import importfile, importtable
44
- from execsql.importers.feather import import_feather, import_parquet
45
- from execsql.importers.ods import importods, ods_data
46
- from execsql.importers.xls import importxls, xls_data
47
- from execsql.models import DataTable
48
- from execsql.script import current_script_line, read_sqlfile, substitute_vars
49
- from execsql.types import dbt_firebird
50
- from execsql.utils.errors import exception_desc
51
- from execsql.utils.fileio import check_dir, filewriter_close, filewriter_open_as_new, filewriter_write
52
- from execsql.utils.gui import ConsoleUIError
53
- from execsql.utils.strings import clean_words, fold_words, unquoted
54
-
55
-
56
- def _apply_output_dir(path: str) -> str:
57
- """Prepend the configured --output-dir to *path* if it is a relative path.
58
-
59
- If ``conf.export_output_dir`` is set and *path* is not absolute (and not
60
- ``stdout``), the base directory is joined to *path* so that all EXPORT
61
- output lands in the same directory without requiring scripts to hard-code
62
- absolute paths.
63
- """
64
- output_dir = getattr(_state.conf, "export_output_dir", None)
65
- if not output_dir:
66
- return path
67
- if path.lower() == "stdout":
68
- return path
69
- if Path(path).is_absolute():
70
- return path
71
- # Windows drive-letter paths are also absolute
72
- if len(path) > 1 and path[1] == ":":
73
- return path
74
- return str(Path(output_dir) / path)
75
-
76
-
77
- def x_export(**kwargs: Any) -> None:
78
- schema = kwargs["schema"]
79
- table = kwargs["table"]
80
- queryname = _state.dbs.current().schema_qualified_table_name(schema, table)
81
- select_stmt = f"select * from {queryname};"
82
- outfile = _apply_output_dir(kwargs["filename"])
83
- description = kwargs["description"]
84
- tee = kwargs["tee"]
85
- tee = bool(tee)
86
- append = kwargs["append"]
87
- append = bool(append)
88
- filefmt = kwargs["format"].lower()
89
- zipfilename = _apply_output_dir(kwargs["zipfilename"]) if kwargs["zipfilename"] else None
90
- if zipfilename is not None:
91
- if outfile.lower() == "stdout":
92
- raise ErrInfo("error", other_msg="Cannot write stdout to a zipfile.")
93
- elif len(outfile) > 1 and outfile[1] == ":":
94
- raise ErrInfo("error", other_msg="Cannot use a drive letter for a file path within a zipfile.")
95
- if filefmt == "duckdb":
96
- raise ErrInfo("error", other_msg="Cannot export to the DuckDB format within a zipfile.")
97
- if filefmt == "sqlite":
98
- raise ErrInfo("error", other_msg="Cannot export to the SQLite format within a zipfile.")
99
- if filefmt == "latex":
100
- raise ErrInfo("error", other_msg="Cannot export to the LaTeX format within a zipfile.")
101
- if filefmt == "feather":
102
- raise ErrInfo("error", other_msg="Cannot export to the feather format within a zipfile.")
103
- if filefmt == "parquet":
104
- raise ErrInfo("error", other_msg="Cannot export to the parquet format within a zipfile.")
105
- if filefmt == "hdf5":
106
- raise ErrInfo("error", other_msg="Cannot export to the HDF5 format within a zipfile.")
107
- if filefmt == "ods":
108
- raise ErrInfo("error", other_msg="Cannot export to an ODS workbook within a zipfile.")
109
- notype = bool(kwargs.get("notype"))
110
- if zipfilename is not None:
111
- check_dir(zipfilename)
112
- else:
113
- check_dir(outfile)
114
- if tee and outfile.lower() != "stdout":
115
- prettyprint_query(select_stmt, _state.dbs.current(), "stdout", False, desc=description)
116
- if filefmt in ("txt", "text"):
117
- prettyprint_query(
118
- select_stmt,
119
- _state.dbs.current(),
120
- outfile,
121
- append,
122
- desc=description,
123
- zipfile=zipfilename,
124
- )
125
- elif filefmt in ("txt-and", "text-and"):
126
- prettyprint_query(
127
- select_stmt,
128
- _state.dbs.current(),
129
- outfile,
130
- append,
131
- and_val="AND",
132
- desc=description,
133
- zipfile=zipfilename,
134
- )
135
- elif filefmt == "ods":
136
- write_query_to_ods(
137
- select_stmt,
138
- _state.dbs.current(),
139
- outfile,
140
- append,
141
- sheetname=queryname,
142
- desc=description,
143
- )
144
- elif filefmt == "duckdb":
145
- write_query_to_duckdb(select_stmt, _state.dbs.current(), outfile, append, tablename=queryname)
146
- elif filefmt == "sqlite":
147
- write_query_to_sqlite(select_stmt, _state.dbs.current(), outfile, append, tablename=queryname)
148
- elif filefmt == "xml":
149
- write_query_to_xml(
150
- select_stmt,
151
- table,
152
- _state.dbs.current(),
153
- outfile,
154
- append,
155
- desc=description,
156
- zipfile=zipfilename,
157
- )
158
- elif filefmt == "json":
159
- write_query_to_json(
160
- select_stmt,
161
- _state.dbs.current(),
162
- outfile,
163
- append,
164
- desc=description,
165
- zipfile=zipfilename,
166
- )
167
- elif filefmt in ("json_ts", "json_tableschema"):
168
- write_query_to_json_ts(
169
- select_stmt,
170
- _state.dbs.current(),
171
- outfile,
172
- append,
173
- not notype,
174
- desc=description,
175
- zipfile=zipfilename,
176
- )
177
- elif filefmt == "values":
178
- write_query_to_values(
179
- select_stmt,
180
- _state.dbs.current(),
181
- outfile,
182
- append,
183
- desc=description,
184
- zipfile=zipfilename,
185
- )
186
- elif filefmt == "html":
187
- write_query_to_html(
188
- select_stmt,
189
- _state.dbs.current(),
190
- outfile,
191
- append,
192
- desc=description,
193
- zipfile=zipfilename,
194
- )
195
- elif filefmt == "cgi-html":
196
- write_query_to_cgi_html(
197
- select_stmt,
198
- _state.dbs.current(),
199
- outfile,
200
- append,
201
- desc=description,
202
- zipfile=zipfilename,
203
- )
204
- elif filefmt == "latex":
205
- write_query_to_latex(
206
- select_stmt,
207
- _state.dbs.current(),
208
- outfile,
209
- append,
210
- desc=description,
211
- zipfile=zipfilename,
212
- )
213
- elif filefmt == "hdf5":
214
- write_query_to_hdf5(table, select_stmt, _state.dbs.current(), outfile, append, desc=description)
215
- else:
216
- try:
217
- hdrs, rows = _state.dbs.current().select_rowsource(select_stmt)
218
- except ErrInfo:
219
- raise
220
- except Exception:
221
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
222
- if filefmt == "raw":
223
- write_query_raw(outfile, rows, _state.dbs.current().encoding, append, zipfile=zipfilename)
224
- elif filefmt == "b64":
225
- write_query_b64(outfile, rows, append)
226
- elif filefmt == "feather":
227
- write_query_to_feather(outfile, hdrs, rows)
228
- elif filefmt == "parquet":
229
- write_query_to_parquet(outfile, hdrs, rows)
230
- else:
231
- write_delimited_file(outfile, filefmt, hdrs, rows, _state.conf.output_encoding, append, zipfilename)
232
- _state.export_metadata.add(ExportRecord(queryname, outfile, zipfilename, description))
233
- return None
234
-
235
-
236
- def x_export_query(**kwargs: Any) -> None:
237
- select_stmt = kwargs["query"]
238
- outfile = kwargs["filename"]
239
- description = kwargs["description"]
240
- tee = kwargs["tee"]
241
- tee = bool(tee)
242
- append = kwargs["append"]
243
- append = bool(append)
244
- filefmt = kwargs["format"].lower()
245
- zipfilename = kwargs["zipfilename"]
246
- if zipfilename is not None:
247
- if outfile == "stdout":
248
- raise ErrInfo("error", other_msg="Cannot write stdout to a zipfile.")
249
- elif len(outfile) > 1 and outfile[1] == ":":
250
- raise ErrInfo("error", other_msg="Cannot use a drive letter for a file path within a zipfile.")
251
- if filefmt == "latex":
252
- raise ErrInfo("error", other_msg="Cannot export to the LaTeX format within a zipfile.")
253
- if filefmt == "feather":
254
- raise ErrInfo("error", other_msg="Cannot export to the feather format within a zipfile.")
255
- if filefmt == "parquet":
256
- raise ErrInfo("error", other_msg="Cannot export to the parquet format within a zipfile.")
257
- if filefmt == "hdf5":
258
- raise ErrInfo("error", other_msg="Cannot export to the HDF5 format within a zipfile.")
259
- if filefmt == "ods":
260
- raise ErrInfo("error", other_msg="Cannot export to an ODS workbook within a zipfile.")
261
- notype = bool(kwargs.get("notype"))
262
- check_dir(outfile)
263
- if tee and outfile.lower() != "stdout":
264
- prettyprint_query(select_stmt, _state.dbs.current(), "stdout", False, desc=description)
265
- if filefmt in ("txt", "text"):
266
- prettyprint_query(
267
- select_stmt,
268
- _state.dbs.current(),
269
- outfile,
270
- append,
271
- desc=description,
272
- zipfile=zipfilename,
273
- )
274
- elif filefmt in ("txt-and", "text-and"):
275
- prettyprint_query(
276
- select_stmt,
277
- _state.dbs.current(),
278
- outfile,
279
- append,
280
- and_val="AND",
281
- desc=description,
282
- zipfile=zipfilename,
283
- )
284
- elif filefmt == "ods":
285
- script_name, lno = current_script_line()
286
- write_query_to_ods(
287
- select_stmt,
288
- _state.dbs.current(),
289
- outfile,
290
- append,
291
- sheetname=f"Query_{lno}",
292
- desc=description,
293
- )
294
- elif filefmt == "json":
295
- write_query_to_json(
296
- select_stmt,
297
- _state.dbs.current(),
298
- outfile,
299
- append,
300
- desc=description,
301
- zipfile=zipfilename,
302
- )
303
- elif filefmt in ("json_ts", "json_tableschema"):
304
- write_query_to_json_ts(
305
- select_stmt,
306
- _state.dbs.current(),
307
- outfile,
308
- append,
309
- not notype,
310
- desc=description,
311
- zipfile=zipfilename,
312
- )
313
- elif filefmt == "values":
314
- write_query_to_values(
315
- select_stmt,
316
- _state.dbs.current(),
317
- outfile,
318
- append,
319
- desc=description,
320
- zipfile=zipfilename,
321
- )
322
- elif filefmt == "html":
323
- write_query_to_html(
324
- select_stmt,
325
- _state.dbs.current(),
326
- outfile,
327
- append,
328
- desc=description,
329
- zipfile=zipfilename,
330
- )
331
- elif filefmt == "cgi-html":
332
- write_query_to_cgi_html(
333
- select_stmt,
334
- _state.dbs.current(),
335
- outfile,
336
- append,
337
- desc=description,
338
- zipfile=zipfilename,
339
- )
340
- elif filefmt == "latex":
341
- write_query_to_latex(
342
- select_stmt,
343
- _state.dbs.current(),
344
- outfile,
345
- append,
346
- desc=description,
347
- zipfile=zipfilename,
348
- )
349
- else:
350
- try:
351
- hdrs, rows = _state.dbs.current().select_rowsource(select_stmt)
352
- except ErrInfo:
353
- raise
354
- except Exception:
355
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
356
- if filefmt == "raw":
357
- write_query_raw(outfile, rows, _state.dbs.current().encoding, append, zipfile=zipfilename)
358
- elif filefmt == "b64":
359
- write_query_b64(outfile, rows, append, zipfile=zipfilename)
360
- elif filefmt == "feather":
361
- write_query_to_feather(outfile, hdrs, rows)
362
- elif filefmt == "parquet":
363
- write_query_to_parquet(outfile, hdrs, rows)
364
- else:
365
- write_delimited_file(
366
- outfile,
367
- filefmt,
368
- hdrs,
369
- rows,
370
- _state.conf.output_encoding,
371
- append,
372
- zipfile=zipfilename,
373
- )
374
- _state.export_metadata.add(ExportRecord(select_stmt, outfile, zipfilename, description))
375
- return None
376
-
377
-
378
- def x_export_query_with_template(**kwargs: Any) -> None:
379
- select_stmt = kwargs["query"]
380
- outfile = kwargs["filename"]
381
- template_file = kwargs["template"]
382
- tee = kwargs["tee"]
383
- tee = bool(tee)
384
- append = kwargs["append"]
385
- append = bool(append)
386
- zipfilename = kwargs["zipfilename"]
387
- check_dir(outfile)
388
- if tee and outfile.lower() != "stdout":
389
- prettyprint_query(select_stmt, _state.dbs.current(), "stdout", False)
390
- report_query(select_stmt, _state.dbs.current(), outfile, template_file, append, zipfile=zipfilename)
391
- _state.export_metadata.add(ExportRecord(select_stmt, outfile, zipfilename))
392
- return None
393
-
394
-
395
- def x_export_with_template(**kwargs: Any) -> None:
396
- schema = kwargs["schema"]
397
- table = kwargs["table"]
398
- queryname = _state.dbs.current().schema_qualified_table_name(schema, table)
399
- select_stmt = f"select * from {queryname};"
400
- outfile = kwargs["filename"]
401
- template_file = kwargs["template"]
402
- tee = kwargs["tee"]
403
- tee = bool(tee)
404
- append = kwargs["append"]
405
- append = bool(append)
406
- zipfilename = kwargs["zipfilename"]
407
- check_dir(outfile)
408
- if tee and outfile.lower() != "stdout":
409
- prettyprint_query(select_stmt, _state.dbs.current(), "stdout", False)
410
- report_query(select_stmt, _state.dbs.current(), outfile, template_file, append, zipfile=zipfilename)
411
- _state.export_metadata.add(ExportRecord(queryname, outfile, zipfilename))
412
- return None
413
-
414
-
415
- def x_export_ods_multiple(**kwargs: Any) -> None:
416
- table_list = kwargs["tables"]
417
- outfile = kwargs["filename"]
418
- description = kwargs["description"]
419
- tee = kwargs["tee"]
420
- tee = bool(tee)
421
- append = kwargs["append"]
422
- append = append is not None
423
- check_dir(outfile)
424
- write_queries_to_ods(table_list, _state.dbs.current(), outfile, append, tee, desc=description)
425
-
426
-
427
- def x_export_metadata(**kwargs: Any) -> None:
428
- outfile = kwargs["filename"]
429
- append = kwargs["append"] is not None
430
- xall = kwargs["all"] is not None
431
- zipfilename = kwargs["zipfilename"]
432
- filefmt = kwargs["format"].lower()
433
- if xall:
434
- hdrs, rows = _state.export_metadata.get_all()
435
- else:
436
- hdrs, rows = _state.export_metadata.get()
437
- if outfile.lower() != "stdout":
438
- check_dir(outfile)
439
- if filefmt in ("txt", "text"):
440
- prettyprint_rowset(hdrs, rows, outfile, append, and_val="", zipfile=zipfilename)
441
- else:
442
- write_delimited_file(outfile, filefmt, hdrs, rows, _state.conf.output_encoding, append, zipfilename)
443
-
444
-
445
- def x_export_metadata_table(**kwargs: Any) -> None:
446
- xall = kwargs["all"] is not None
447
- schemaname = kwargs["schema"]
448
- tablename = kwargs["table"]
449
- newstr = kwargs["new"]
450
- if newstr:
451
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
452
- else:
453
- is_new = 0
454
- if xall:
455
- hdrs, rows = _state.export_metadata.get_all()
456
- else:
457
- hdrs, rows = _state.export_metadata.get()
458
- import_data_table(_state.dbs.current(), schemaname, tablename, is_new, hdrs, rows)
459
-
460
-
461
- def x_import(**kwargs: Any) -> None:
462
- newstr = kwargs["new"]
463
- if newstr:
464
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
465
- else:
466
- is_new = 0
467
- schemaname = kwargs["schema"]
468
- tablename = kwargs["table"]
469
- filename = kwargs["filename"]
470
- if len(filename) > 1 and filename[0] == "~" and filename[1] == os.sep:
471
- filename = str(Path.home() / filename[2:])
472
- if not Path(filename).exists():
473
- raise ErrInfo(
474
- type="cmd",
475
- command_text=kwargs["metacommandline"],
476
- other_msg=f"Input file {filename} does not exist",
477
- )
478
- quotechar = kwargs["quotechar"]
479
- if quotechar:
480
- quotechar = quotechar.lower()
481
- delimchar = kwargs["delimchar"]
482
- if delimchar:
483
- if delimchar.lower() == "tab":
484
- delimchar = chr(9)
485
- elif delimchar.lower() in ("unitsep", "us"):
486
- delimchar = chr(31)
487
- enc = kwargs["encoding"]
488
- junk_hdrs = kwargs["skip"]
489
- if not junk_hdrs:
490
- junk_hdrs = 0
491
- else:
492
- junk_hdrs = int(junk_hdrs)
493
- from execsql.metacommands.conditions import file_size_date
494
-
495
- sz, dt = file_size_date(filename)
496
- _state.exec_log.log_status_info(f"IMPORTing {filename} ({sz}, {dt})")
497
- try:
498
- importtable(
499
- _state.dbs.current(),
500
- schemaname,
501
- tablename,
502
- filename,
503
- is_new,
504
- skip_header_line=True,
505
- quotechar=quotechar,
506
- delimchar=delimchar,
507
- encoding=enc,
508
- junk_header_lines=junk_hdrs,
509
- )
510
- except ErrInfo:
511
- raise
512
- except Exception:
513
- raise ErrInfo(
514
- "exception",
515
- exception_msg=exception_desc(),
516
- other_msg=f"Can't import data from tabular text file {filename}",
517
- )
518
- return None
519
-
520
-
521
- def x_import_file(**kwargs: Any) -> None:
522
- schemaname = kwargs["schema"]
523
- tablename = kwargs["table"]
524
- columnname = kwargs["columnname"]
525
- filename = kwargs["filename"]
526
- if not Path(filename).exists():
527
- raise ErrInfo(
528
- type="cmd",
529
- command_text=kwargs["metacommandline"],
530
- other_msg=f"Input file {filename} does not exist",
531
- )
532
- from execsql.metacommands.conditions import file_size_date
533
-
534
- sz, dt = file_size_date(filename)
535
- _state.exec_log.log_status_info(f"IMPORTing_FILE {filename} ({sz}, {dt})")
536
- try:
537
- importfile(_state.dbs.current(), schemaname, tablename, columnname, filename)
538
- except ErrInfo:
539
- raise
540
- except Exception:
541
- raise ErrInfo(
542
- "exception",
543
- exception_msg=exception_desc(),
544
- other_msg=f"Can't import file {filename}",
545
- )
546
- return None
547
-
548
-
549
- def x_import_ods(**kwargs: Any) -> None:
550
- newstr = kwargs["new"]
551
- if newstr:
552
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
553
- else:
554
- is_new = 0
555
- schemaname = kwargs["schema"]
556
- tablename = kwargs["table"]
557
- filename = kwargs["filename"]
558
- sheetname = kwargs["sheetname"]
559
- hdr_rows = kwargs["skip"]
560
- if not hdr_rows:
561
- hdr_rows = 0
562
- else:
563
- hdr_rows = int(hdr_rows)
564
- if not Path(filename).exists():
565
- raise ErrInfo(
566
- type="cmd",
567
- command_text=kwargs["metacommandline"],
568
- other_msg="Input file does not exist",
569
- )
570
- try:
571
- importods(_state.dbs.current(), schemaname, tablename, is_new, filename, sheetname, hdr_rows)
572
- except ErrInfo:
573
- raise
574
- except Exception:
575
- raise ErrInfo(
576
- "exception",
577
- exception_msg=exception_desc(),
578
- other_msg=f"Can't import data from ODS file {filename}",
579
- )
580
- return None
581
-
582
-
583
- def x_import_ods_pattern(**kwargs: Any) -> None:
584
- import re
585
-
586
- newstr = kwargs["new"]
587
- if newstr:
588
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
589
- else:
590
- is_new = 0
591
- schemaname = kwargs["schema"]
592
- filename = kwargs["filename"]
593
- rx = re.compile(kwargs["patn"], re.I)
594
- hdr_rows = kwargs["skip"]
595
- if not hdr_rows:
596
- hdr_rows = 0
597
- else:
598
- hdr_rows = int(hdr_rows)
599
- if not Path(filename).exists():
600
- raise ErrInfo(
601
- type="cmd",
602
- command_text=kwargs["metacommandline"],
603
- other_msg="Input file does not exist",
604
- )
605
- wbk = OdsFile()
606
- try:
607
- wbk.open(filename)
608
- except Exception:
609
- raise ErrInfo(type="cmd", other_msg=f"{filename} is not a valid OpenDocument spreadsheet.")
610
- sheets = wbk.sheetnames()
611
- impsheets = [s for s in sheets if rx.search(s)]
612
- tables = list(impsheets)
613
- if _state.conf.clean_col_hdrs:
614
- tables = clean_words(tables)
615
- if _state.conf.fold_col_hdrs != "no":
616
- tables = fold_words(tables, _state.conf.fold_col_hdrs)
617
- for ix in range(len(impsheets)):
618
- sheetname = impsheets[ix]
619
- tablename = tables[ix]
620
- try:
621
- importods(_state.dbs.current(), schemaname, tablename, is_new, filename, sheetname, hdr_rows)
622
- except ErrInfo:
623
- raise
624
- except Exception:
625
- raise ErrInfo(
626
- "exception",
627
- exception_msg=exception_desc(),
628
- other_msg=f"Can't import data from ODS file {filename}",
629
- )
630
- _state.subvars.add_substitution("$SHEETS_IMPORTED", ",".join(impsheets))
631
- _state.subvars.add_substitution("$SHEETS_TABLES", ",".join(tables))
632
- if schemaname is None:
633
- _state.subvars.add_substitution("$SHEETS_TABLES_VALUES", ",".join([f"('{t}')" for t in tables]))
634
- else:
635
- _state.subvars.add_substitution("$SHEETS_TABLES_VALUES", ",".join([f"('{schemaname}.{t}')" for t in tables]))
636
- return None
637
-
638
-
639
- def x_import_xls(**kwargs: Any) -> None:
640
- newstr = kwargs["new"]
641
- if newstr:
642
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
643
- else:
644
- is_new = 0
645
- schemaname = kwargs["schema"]
646
- tablename = kwargs["table"]
647
- filename = kwargs["filename"]
648
- sheetname = kwargs["sheetname"]
649
- junk_hdrs = kwargs["skip"]
650
- encoding = kwargs["encoding"]
651
- if not junk_hdrs:
652
- junk_hdrs = 0
653
- else:
654
- junk_hdrs = int(junk_hdrs)
655
- if not Path(filename).exists():
656
- raise ErrInfo(
657
- type="cmd",
658
- command_text=kwargs["metacommandline"],
659
- other_msg="Input file does not exist",
660
- )
661
- try:
662
- importxls(_state.dbs.current(), schemaname, tablename, is_new, filename, sheetname, junk_hdrs, encoding)
663
- except ErrInfo:
664
- raise
665
- except Exception:
666
- raise ErrInfo(
667
- "exception",
668
- exception_msg=exception_desc(),
669
- other_msg=f"Can't import data from Excel file {filename}",
670
- )
671
- return None
672
-
673
-
674
- def x_import_xls_pattern(**kwargs: Any) -> None:
675
- import re
676
-
677
- newstr = kwargs["new"]
678
- if newstr:
679
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
680
- else:
681
- is_new = 0
682
- schemaname = kwargs["schema"]
683
- filename = kwargs["filename"]
684
- rx = re.compile(kwargs["patn"], re.I)
685
- hdr_rows = kwargs["skip"]
686
- encoding = kwargs["encoding"]
687
- if not hdr_rows:
688
- hdr_rows = 0
689
- else:
690
- hdr_rows = int(hdr_rows)
691
- if not Path(filename).exists():
692
- raise ErrInfo(
693
- type="cmd",
694
- command_text=kwargs["metacommandline"],
695
- other_msg="Input file does not exist",
696
- )
697
- if len(filename) < 4:
698
- raise ErrInfo(type="cmd", other_msg=f"{filename} is not a recognizable Excel spreadsheet name.")
699
- ext3 = filename[-3:].lower()
700
- if ext3 == "xls":
701
- wbk = XlsFile()
702
- elif ext3 == "lsx":
703
- wbk = XlsxFile()
704
- else:
705
- raise ErrInfo(type="cmd", other_msg=f"{filename} is not a recognizable Excel spreadsheet name.")
706
- try:
707
- wbk.open(filename, encoding, read_only=True)
708
- except Exception:
709
- raise ErrInfo(type="cmd", other_msg=f"{filename} is not a valid Excel spreadsheet.")
710
- sheets = wbk.sheetnames()
711
- impsheets = [s for s in sheets if rx.search(s)]
712
- tables = list(impsheets)
713
- if _state.conf.clean_col_hdrs:
714
- tables = clean_words(tables)
715
- if _state.conf.fold_col_hdrs != "no":
716
- tables = fold_words(tables, _state.conf.fold_col_hdrs)
717
- for ix in range(len(impsheets)):
718
- sheetname = impsheets[ix]
719
- tablename = tables[ix]
720
- try:
721
- importxls(
722
- _state.dbs.current(),
723
- schemaname,
724
- tablename,
725
- is_new,
726
- filename,
727
- sheetname,
728
- hdr_rows,
729
- encoding,
730
- )
731
- except ErrInfo:
732
- raise
733
- except Exception:
734
- raise ErrInfo(
735
- "exception",
736
- exception_msg=exception_desc(),
737
- other_msg=f"Can't import data from ODS file {filename}",
738
- )
739
- _state.subvars.add_substitution("$SHEETS_IMPORTED", ",".join(impsheets))
740
- _state.subvars.add_substitution("$SHEETS_TABLES", ",".join(tables))
741
- if schemaname is None:
742
- _state.subvars.add_substitution("$SHEETS_TABLES_VALUES", ",".join([f"('{t}')" for t in tables]))
743
- else:
744
- _state.subvars.add_substitution("$SHEETS_TABLES_VALUES", ",".join([f"('{schemaname}.{t}')" for t in tables]))
745
- return None
746
-
747
-
748
- def x_import_parquet(**kwargs: Any) -> None:
749
- newstr = kwargs["new"]
750
- if newstr:
751
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
752
- else:
753
- is_new = 0
754
- schemaname = kwargs["schema"]
755
- tablename = kwargs["table"]
756
- filename = kwargs["filename"]
757
- if len(filename) > 1 and filename[0] == "~" and filename[1] == os.sep:
758
- filename = str(Path.home() / filename[2:])
759
- if not Path(filename).exists():
760
- raise ErrInfo(
761
- type="cmd",
762
- command_text=kwargs["metacommandline"],
763
- other_msg=f"Input file {filename} does not exist",
764
- )
765
- from execsql.metacommands.conditions import file_size_date
766
-
767
- sz, dt = file_size_date(filename)
768
- _state.exec_log.log_status_info(f"IMPORTing from Parquet file {filename} ({sz}, {dt})")
769
- try:
770
- import_parquet(_state.dbs.current(), schemaname, tablename, filename, is_new)
771
- except ErrInfo:
772
- raise
773
- except Exception:
774
- raise ErrInfo(
775
- "exception",
776
- exception_msg=exception_desc(),
777
- other_msg=f"Can't import data from Parquet data file {filename}",
778
- )
779
- return None
780
-
781
-
782
- def x_import_feather(**kwargs: Any) -> None:
783
- newstr = kwargs["new"]
784
- if newstr:
785
- is_new = 1 + ["new", "replacement"].index(newstr.lower())
786
- else:
787
- is_new = 0
788
- schemaname = kwargs["schema"]
789
- tablename = kwargs["table"]
790
- filename = kwargs["filename"]
791
- if len(filename) > 1 and filename[0] == "~" and filename[1] == os.sep:
792
- filename = str(Path.home() / filename[2:])
793
- if not Path(filename).exists():
794
- raise ErrInfo(
795
- type="cmd",
796
- command_text=kwargs["metacommandline"],
797
- other_msg=f"Input file {filename} does not exist",
798
- )
799
- from execsql.metacommands.conditions import file_size_date
800
-
801
- sz, dt = file_size_date(filename)
802
- _state.exec_log.log_status_info(f"IMPORTing from Feather file {filename} ({sz}, {dt})")
803
- try:
804
- import_feather(_state.dbs.current(), schemaname, tablename, filename, is_new)
805
- except ErrInfo:
806
- raise
807
- except Exception:
808
- raise ErrInfo(
809
- "exception",
810
- exception_msg=exception_desc(),
811
- other_msg=f"Can't import data from Feather data file {filename}",
812
- )
813
- return None
814
-
815
-
816
- def x_import_row_buffer(**kwargs: Any) -> None:
817
- rows = kwargs["rows"]
818
- _state.conf.import_row_buffer = int(rows)
819
-
820
-
821
- def x_export_row_buffer(**kwargs: Any) -> None:
822
- rows = kwargs["rows"]
823
- _state.conf.export_row_buffer = int(rows)
824
-
825
-
826
- def x_write(**kwargs: Any) -> None:
827
- msg = f"{kwargs['text']}\n"
828
- tee = kwargs["tee"]
829
- tee = bool(tee)
830
- outf = kwargs["filename"]
831
- if _state.conf.write_prefix is not None:
832
- msg = substitute_vars(_state.conf.write_prefix) + " " + msg
833
- if _state.conf.write_suffix is not None:
834
- msg = msg[:-1] + " " + substitute_vars(_state.conf.write_suffix) + "\n"
835
- if outf:
836
- check_dir(outf)
837
- filewriter_write(outf, msg)
838
- if (not outf) or tee:
839
- try:
840
- _state.output.write(msg)
841
- except TypeError:
842
- raise ErrInfo(
843
- type="other",
844
- command_text=kwargs["metacommandline"],
845
- other_msg="TypeError in 'write' metacommand.",
846
- )
847
- except ConsoleUIError as e:
848
- _state.output.reset()
849
- _state.exec_log.log_status_info(f"Console UI write failed (message {{{e.value}}}); output reset to stdout.")
850
- _state.output.write(msg.encode(_state.conf.output_encoding))
851
- if _state.conf.tee_write_log:
852
- _state.exec_log.log_user_msg(msg)
853
- return None
854
-
855
-
856
- def x_write_create_table(**kwargs: Any) -> None:
857
- filename = kwargs["filename"]
858
- if not Path(filename).exists():
859
- raise ErrInfo(
860
- type="cmd",
861
- command_text=kwargs["metacommandline"],
862
- other_msg="Input file does not exist",
863
- )
864
- quotechar = kwargs["quotechar"]
865
- delimchar = kwargs["delimchar"]
866
- encoding = kwargs["encoding"]
867
- if delimchar:
868
- if delimchar.lower() == "tab":
869
- delimchar = chr(9)
870
- elif delimchar.lower() in ("unitsep", "us"):
871
- delimchar = chr(31)
872
- junk_hdrs = kwargs["skip"]
873
- if not junk_hdrs:
874
- junk_hdrs = 0
875
- else:
876
- junk_hdrs = int(junk_hdrs)
877
- enc = encoding if encoding else _state.conf.import_encoding
878
- inf = CsvFile(filename, enc, junk_header_lines=junk_hdrs)
879
- if quotechar and delimchar:
880
- inf.lineformat(delimchar, quotechar, None)
881
- inf.evaluate_column_types()
882
- sql = inf.create_table(_state.dbs.current().type, kwargs["schema"], kwargs["table"], pretty=True)
883
- inf.close()
884
- comment = kwargs["comment"]
885
- outfile = kwargs["outfile"]
886
-
887
- def write(txt: str) -> None:
888
- if outfile is None or outfile == "stdout":
889
- _state.output.write(txt)
890
- else:
891
- filewriter_write(outfile, txt)
892
-
893
- if outfile:
894
- check_dir(outfile)
895
- if comment:
896
- write(f"-- {comment}\n")
897
- write(f"{sql}\n")
898
-
899
-
900
- def x_write_create_table_ods(**kwargs: Any) -> None:
901
- schemaname = kwargs["schema"]
902
- tablename = kwargs["table"]
903
- filename = kwargs["filename"]
904
- sheetname = kwargs["sheet"]
905
- hdr_rows = kwargs["skip"]
906
- if not hdr_rows:
907
- hdr_rows = 0
908
- else:
909
- hdr_rows = int(hdr_rows)
910
- comment = kwargs["comment"]
911
- outfile = kwargs["outfile"]
912
- if not Path(filename).exists():
913
- raise ErrInfo(
914
- type="cmd",
915
- command_text=kwargs["metacommandline"],
916
- other_msg="Input file does not exist",
917
- )
918
- hdrs, data = ods_data(filename, sheetname, hdr_rows)
919
- tablespec = DataTable(hdrs, data)
920
- sql = tablespec.create_table(_state.dbs.current().type, schemaname, tablename, pretty=True)
921
- if outfile:
922
- if comment:
923
- filewriter_write(outfile, f"-- {comment}\n")
924
- filewriter_write(outfile, sql)
925
- filewriter_close(outfile)
926
- else:
927
- if comment:
928
- _state.output.write(f"-- {comment}\n")
929
- _state.output.write(f"{sql}\n")
930
-
931
-
932
- def x_write_create_table_xls(**kwargs: Any) -> None:
933
- schemaname = kwargs["schema"]
934
- tablename = kwargs["table"]
935
- filename = kwargs["filename"]
936
- sheetname = kwargs["sheet"]
937
- junk_hdrs = kwargs["skip"]
938
- encoding = kwargs["encoding"]
939
- enc = encoding if encoding else _state.conf.import_encoding
940
- if not junk_hdrs:
941
- junk_hdrs = 0
942
- else:
943
- junk_hdrs = int(junk_hdrs)
944
- comment = kwargs["comment"]
945
- outfile = kwargs["outfile"]
946
- if not Path(filename).exists():
947
- raise ErrInfo(
948
- type="cmd",
949
- command_text=kwargs["metacommandline"],
950
- other_msg="Input file does not exist",
951
- )
952
- hdrs, data = xls_data(filename, sheetname, junk_hdrs, enc)
953
- tablespec = DataTable(hdrs, data)
954
- sql = tablespec.create_table(_state.dbs.current().type, schemaname, tablename, pretty=True)
955
- if outfile:
956
- if comment:
957
- filewriter_write(outfile, f"-- {comment}\n")
958
- filewriter_write(outfile, sql)
959
- filewriter_close(outfile)
960
- else:
961
- if comment:
962
- _state.output.write(f"-- {comment}\n")
963
- _state.output.write(f"{sql}\n")
964
-
965
-
966
- def x_write_create_table_alias(**kwargs: Any) -> None:
967
- alias = kwargs["alias"].lower()
968
- schema = kwargs["schema"]
969
- table = kwargs["table"]
970
- comment = kwargs["comment"]
971
- outfile = kwargs["filename"]
972
- if alias not in _state.dbs.aliases():
973
- raise ErrInfo(
974
- type="cmd",
975
- command_text=kwargs["metacommandline"],
976
- other_msg=f"Unrecognized database alias: {alias}.",
977
- )
978
- db = _state.dbs.aliased_as(alias)
979
- tbl = db.schema_qualified_table_name(schema, table)
980
- try:
981
- if not db.table_exists(table, schema):
982
- raise ErrInfo(
983
- type="cmd",
984
- command_text=kwargs["metacommandline"],
985
- other_msg=f"Table {tbl} does not exist",
986
- )
987
- except Exception:
988
- pass # Best-effort check; some adapters lack information_schema.
989
- select_stmt = f"select * from {tbl};"
990
- try:
991
- hdrs, rows = db.select_rowsource(select_stmt)
992
- except ErrInfo:
993
- raise
994
- except Exception:
995
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
996
- tablespec = DataTable(hdrs, rows)
997
- sql = tablespec.create_table(_state.dbs.current().type, kwargs["schema1"], kwargs["table1"], pretty=True)
998
- if outfile:
999
- if comment:
1000
- filewriter_write(outfile, f"-- {comment}\n")
1001
- filewriter_write(outfile, sql)
1002
- filewriter_close(outfile)
1003
- else:
1004
- if comment:
1005
- _state.output.write(f"-- {comment}\n")
1006
- _state.output.write(f"{sql}\n")
1007
-
1008
-
1009
- def x_write_prefix(**kwargs: Any) -> None:
1010
- pf = kwargs["prefix"]
1011
- if pf.lower() == "clear":
1012
- _state.conf.write_prefix = None
1013
- else:
1014
- _state.conf.write_prefix = pf
1015
- return None
1016
-
1017
-
1018
- def x_write_suffix(**kwargs: Any) -> None:
1019
- sf = kwargs["suffix"]
1020
- if sf.lower() == "clear":
1021
- _state.conf.write_suffix = None
1022
- else:
1023
- _state.conf.write_suffix = sf
1024
- return None
1025
-
1026
-
1027
- def x_writescript(**kwargs: Any) -> None:
1028
- script_id = kwargs["script_id"]
1029
- output_dest = kwargs["filename"]
1030
- append = kwargs["append"]
1031
-
1032
- def write(txt: str) -> None:
1033
- if output_dest is None or output_dest == "stdout":
1034
- _state.output.write(txt)
1035
- else:
1036
- filewriter_write(output_dest, txt)
1037
-
1038
- if output_dest is not None and output_dest != "stdout":
1039
- check_dir(output_dest)
1040
- if not append:
1041
- filewriter_open_as_new(output_dest)
1042
- script = _state.savedscripts[script_id]
1043
- if script.paramnames is not None and len(script.paramnames) > 0:
1044
- write(f"BEGIN SCRIPT {script_id} ({', '.join(script.paramnames)})\n")
1045
- else:
1046
- write(f"BEGIN SCRIPT {script_id}\n")
1047
- lines = [c.commandline() for c in script.cmdlist]
1048
- for line in lines:
1049
- write(f"{line}\n")
1050
- write(f"END SCRIPT {script_id}\n")
1051
-
1052
-
1053
- def x_include(**kwargs: Any) -> None:
1054
- filename = kwargs["filename"]
1055
- if len(filename) > 1 and filename[0] == "~" and filename[1] == os.sep:
1056
- filename = str(Path.home() / filename[2:])
1057
- exists = kwargs["exists"]
1058
- if exists is not None:
1059
- if Path(filename).is_file():
1060
- read_sqlfile(filename)
1061
- else:
1062
- if not Path(filename).is_file():
1063
- raise ErrInfo(type="error", other_msg=f"File {filename} does not exist.")
1064
- read_sqlfile(filename)
1065
- return None
1066
-
1067
-
1068
- def x_copy(**kwargs: Any) -> None:
1069
- alias1 = kwargs["alias1"].lower()
1070
- schema1 = kwargs["schema1"]
1071
- table1 = kwargs["table1"]
1072
- new = kwargs["new"]
1073
- new_tbl2 = new.lower() if new else None
1074
- alias2 = kwargs["alias2"].lower()
1075
- schema2 = kwargs["schema2"]
1076
- table2 = kwargs["table2"]
1077
- if alias1 not in _state.dbs.aliases():
1078
- raise ErrInfo(
1079
- type="cmd",
1080
- command_text=kwargs["metacommandline"],
1081
- other_msg=f"Unrecognized database alias: {alias1}.",
1082
- )
1083
- if alias2 not in _state.dbs.aliases():
1084
- raise ErrInfo(
1085
- type="cmd",
1086
- command_text=kwargs["metacommandline"],
1087
- other_msg=f"Unrecognized database alias: {alias2}.",
1088
- )
1089
- db1 = _state.dbs.aliased_as(alias1)
1090
- db2 = _state.dbs.aliased_as(alias2)
1091
- tbl1 = db1.schema_qualified_table_name(schema1, table1)
1092
- tbl2 = db2.schema_qualified_table_name(schema2, table2)
1093
- try:
1094
- if not db1.table_exists(table1, schema1):
1095
- raise ErrInfo(
1096
- type="cmd",
1097
- command_text=kwargs["metacommandline"],
1098
- other_msg=f"Table {tbl1} does not exist",
1099
- )
1100
- except Exception:
1101
- pass # Best-effort check; some adapters lack information_schema.
1102
- if new_tbl2 and new_tbl2 == "new":
1103
- try:
1104
- if db2.table_exists(table2, schema2):
1105
- raise ErrInfo(
1106
- type="cmd",
1107
- command_text=kwargs["metacommandline"],
1108
- other_msg=f"Table {tbl2} already exists",
1109
- )
1110
- except Exception:
1111
- pass # Best-effort check; some adapters lack information_schema.
1112
- select_stmt = f"select * from {tbl1};"
1113
-
1114
- def get_ts() -> DataTable:
1115
- if get_ts.tablespec is None:
1116
- hdrs, rows = db1.select_rowsource(select_stmt)
1117
- get_ts.tablespec = DataTable(hdrs, rows)
1118
- return get_ts.tablespec
1119
-
1120
- get_ts.tablespec = None
1121
-
1122
- if new_tbl2:
1123
- tbl_desc = get_ts()
1124
- create_tbl = tbl_desc.create_table(db2.type, schema2, table2)
1125
- if new_tbl2 == "replacement":
1126
- try:
1127
- db2.drop_table(tbl2)
1128
- except Exception:
1129
- _state.exec_log.log_status_info(f"Could not drop existing table ({tbl2}) for COPY metacommand")
1130
- db2.execute(create_tbl)
1131
- if db2.type == dbt_firebird:
1132
- db2.execute("COMMIT;")
1133
- try:
1134
- hdrs, rows = db1.select_rowsource(select_stmt)
1135
- except ErrInfo:
1136
- raise
1137
- except Exception:
1138
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
1139
- try:
1140
- db2.populate_table(schema2, table2, rows, hdrs, get_ts)
1141
- db2.commit()
1142
- except ErrInfo:
1143
- raise
1144
- except Exception:
1145
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
1146
-
1147
-
1148
- def x_copy_query(**kwargs: Any) -> None:
1149
- alias1 = kwargs["alias1"].lower()
1150
- select_stmt = kwargs["query"]
1151
- new = kwargs["new"]
1152
- new_tbl2 = new.lower() if new else None
1153
- alias2 = kwargs["alias2"].lower()
1154
- schema2 = kwargs["schema"]
1155
- table2 = kwargs["table"]
1156
- if alias1 not in _state.dbs.aliases():
1157
- raise ErrInfo(
1158
- type="cmd",
1159
- command_text=kwargs["metacommandline"],
1160
- other_msg=f"Unrecognized database alias: {alias1}.",
1161
- )
1162
- if alias2 not in _state.dbs.aliases():
1163
- raise ErrInfo(
1164
- type="cmd",
1165
- command_text=kwargs["metacommandline"],
1166
- other_msg=f"Unrecognized database alias: {alias2}.",
1167
- )
1168
- db1 = _state.dbs.aliased_as(alias1)
1169
- db2 = _state.dbs.aliased_as(alias2)
1170
- tbl2 = db2.schema_qualified_table_name(schema2, table2)
1171
- if new_tbl2 and new_tbl2 == "new":
1172
- try:
1173
- if db2.table_exists(table2, schema2):
1174
- raise ErrInfo(
1175
- type="cmd",
1176
- command_text=kwargs["metacommandline"],
1177
- other_msg=f"Table {tbl2} already exists",
1178
- )
1179
- except Exception:
1180
- pass # Best-effort check; some adapters lack information_schema.
1181
-
1182
- def get_ts() -> DataTable:
1183
- if not get_ts.tablespec:
1184
- hdrs, rows = db1.select_rowsource(select_stmt)
1185
- get_ts.tablespec = DataTable(hdrs, rows)
1186
- return get_ts.tablespec
1187
-
1188
- get_ts.tablespec = None
1189
-
1190
- if new_tbl2:
1191
- try:
1192
- hdrs, rows = db1.select_rowsource(select_stmt)
1193
- except ErrInfo:
1194
- raise
1195
- except Exception:
1196
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
1197
- get_ts.tablespec = DataTable(hdrs, rows)
1198
- tbl_desc = get_ts.tablespec
1199
- create_tbl = tbl_desc.create_table(db2.type, schema2, table2)
1200
- if new_tbl2 == "replacement":
1201
- try:
1202
- db2.drop_table(tbl2)
1203
- except Exception:
1204
- _state.exec_log.log_status_info(f"Could not drop existing table ({tbl2}) for COPY metacommand")
1205
- db2.execute(create_tbl)
1206
- if db2.type == dbt_firebird:
1207
- db2.execute("COMMIT;")
1208
- try:
1209
- hdrs, rows = db1.select_rowsource(select_stmt)
1210
- except ErrInfo:
1211
- raise
1212
- except Exception:
1213
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
1214
- try:
1215
- db2.populate_table(schema2, table2, rows, hdrs, get_ts)
1216
- db2.commit()
1217
- except ErrInfo:
1218
- raise
1219
- except Exception:
1220
- raise ErrInfo("db", select_stmt, exception_msg=exception_desc())
1221
-
1222
-
1223
- def x_zip(**kwargs: Any) -> None:
1224
- import zipfile as _zipfile
1225
- import glob as _glob
1226
-
1227
- files = kwargs["filename"].strip(' "')
1228
- zipfile_name = kwargs["zipfilename"].strip(' "')
1229
- append = kwargs["append"]
1230
- zmode = "a" if append is not None else "w"
1231
- zf = _zipfile.ZipFile(zipfile_name, mode=zmode, compression=_zipfile.ZIP_BZIP2, compresslevel=9)
1232
- fnlist = _glob.glob(files)
1233
- for f in fnlist:
1234
- if Path(f).is_file():
1235
- zf.write(f)
1236
- zf.close()
1237
-
1238
-
1239
- def x_zip_buffer_mb(**kwargs: Any) -> None:
1240
- size_mb = kwargs["size"]
1241
- _state.conf.zip_buffer_mb = int(size_mb)
1242
-
1243
-
1244
- def x_rm_file(**kwargs: Any) -> None:
1245
- import glob as _glob
1246
-
1247
- fn = kwargs["filename"].strip(' "')
1248
- fnlist = _glob.glob(fn)
1249
- for f in fnlist:
1250
- if Path(f).is_file():
1251
- filewriter_close(f)
1252
- os.unlink(f)
1253
-
1254
-
1255
- def x_make_export_dirs(**kwargs: Any) -> None:
1256
- setting = kwargs["setting"].lower()
1257
- _state.conf.make_export_dirs = setting in ("yes", "on", "true", "1")
1258
-
1259
-
1260
- def x_cd(**kwargs: Any) -> None:
1261
- new_dir = unquoted(kwargs["dir"])
1262
- if not Path(new_dir).is_dir():
1263
- raise ErrInfo(
1264
- type="cmd",
1265
- command_text=kwargs["metacommandline"],
1266
- other_msg="Directory does not exist",
1267
- )
1268
- os.chdir(new_dir)
1269
- script, lno = current_script_line()
1270
- _state.exec_log.log_status_info(f"Current directory changed to {new_dir} at line {lno} of {script}")
1271
- return None
1272
-
1273
-
1274
- def x_scan_lines(**kwargs: Any) -> None:
1275
- _state.conf.scan_lines = int(kwargs["scanlines"])
1276
-
1277
-
1278
- def x_hdf5_text_len(**kwargs: Any) -> None:
1279
- _state.conf.hdf5_text_len = int(kwargs["textlen"])
1280
15
 
16
+ from __future__ import annotations
1281
17
 
1282
- def x_serve(**kwargs: Any) -> None:
1283
- infname = kwargs["filename"]
1284
- fmt = kwargs["format"].lower()
1285
- if not Path(infname).is_file():
1286
- raise ErrInfo(
1287
- type="cmd",
1288
- command_text=kwargs["metacommandline"],
1289
- other_msg=f"Input file {infname} does not exist",
1290
- )
1291
- fname = Path(infname).name
1292
- if fmt == "binary":
1293
- contenttype = "application/octet-stream"
1294
- elif fmt == "csv":
1295
- contenttype = "text/csv"
1296
- elif fmt in ("txt", "text"):
1297
- contenttype = "text/plain"
1298
- elif fmt == "ods":
1299
- contenttype = "application/vnd.oasis.opendocument.spreadsheet"
1300
- elif fmt == "json":
1301
- contenttype = "application/json"
1302
- elif fmt == "html":
1303
- contenttype = "text/html"
1304
- elif fmt == "pdf":
1305
- contenttype = "application/pdf"
1306
- elif fmt == "zip":
1307
- contenttype = "application/zip"
1308
- else:
1309
- contenttype = "application/octet-stream"
1310
- print(f"Content-Type: {contenttype}")
1311
- print(f"Content-Disposition: attachment; filename={fname}\n")
1312
- with open(infname, "rb") as f:
1313
- copyfileobj(f, sys.stdout.buffer)
18
+ # -- export handlers ---------------------------------------------------------
19
+ from execsql.metacommands.io_export import ( # noqa: F401
20
+ _apply_output_dir,
21
+ x_export,
22
+ x_export_metadata,
23
+ x_export_metadata_table,
24
+ x_export_ods_multiple,
25
+ x_export_query,
26
+ x_export_query_with_template,
27
+ x_export_row_buffer,
28
+ x_export_with_template,
29
+ )
30
+
31
+ # -- import handlers ---------------------------------------------------------
32
+ from execsql.metacommands.io_import import ( # noqa: F401
33
+ x_import,
34
+ x_import_feather,
35
+ x_import_file,
36
+ x_import_ods,
37
+ x_import_ods_pattern,
38
+ x_import_parquet,
39
+ x_import_row_buffer,
40
+ x_import_xls,
41
+ x_show_progress,
42
+ x_import_xls_pattern,
43
+ )
44
+
45
+ # -- write handlers ----------------------------------------------------------
46
+ from execsql.metacommands.io_write import ( # noqa: F401
47
+ x_write,
48
+ x_write_create_table,
49
+ x_write_create_table_alias,
50
+ x_write_create_table_ods,
51
+ x_write_create_table_xls,
52
+ x_write_prefix,
53
+ x_write_suffix,
54
+ x_writescript,
55
+ )
56
+
57
+ # -- file / system operation handlers ----------------------------------------
58
+ from execsql.metacommands.io_fileops import ( # noqa: F401
59
+ x_cd,
60
+ x_copy,
61
+ x_copy_query,
62
+ x_hdf5_text_len,
63
+ x_include,
64
+ x_make_export_dirs,
65
+ x_rm_file,
66
+ x_scan_lines,
67
+ x_serve,
68
+ x_zip,
69
+ x_zip_buffer_mb,
70
+ )