execsql2 2.21.2__tar.gz → 2.22.1__tar.gz

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 (377) hide show
  1. {execsql2-2.21.2 → execsql2-2.22.1}/.github/workflows/ci-cd.yml +50 -1
  2. {execsql2-2.21.2 → execsql2-2.22.1}/.gitignore +2 -1
  3. {execsql2-2.21.2 → execsql2-2.22.1}/.pre-commit-config.yaml +12 -0
  4. {execsql2-2.21.2 → execsql2-2.22.1}/CHANGELOG.md +24 -0
  5. {execsql2-2.21.2 → execsql2-2.22.1}/CONTRIBUTING.md +20 -8
  6. {execsql2-2.21.2 → execsql2-2.22.1}/PKG-INFO +114 -132
  7. {execsql2-2.21.2 → execsql2-2.22.1}/README.md +113 -131
  8. {execsql2-2.21.2 → execsql2-2.22.1}/docs/about/divergence.md +44 -40
  9. {execsql2-2.21.2 → execsql2-2.22.1}/docs/dev/architecture.md +1 -1
  10. {execsql2-2.21.2 → execsql2-2.22.1}/docs/getting-started/installation.md +2 -1
  11. {execsql2-2.21.2 → execsql2-2.22.1}/docs/getting-started/syntax.md +8 -0
  12. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/examples.md +130 -2
  13. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/formatter.md +1 -1
  14. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/sql_syntax.md +1 -1
  15. {execsql2-2.21.2 → execsql2-2.22.1}/docs/reference/configuration.md +3 -0
  16. {execsql2-2.21.2 → execsql2-2.22.1}/docs/reference/metacommands.md +9 -5
  17. {execsql2-2.21.2 → execsql2-2.22.1}/docs/reference/security.md +14 -2
  18. {execsql2-2.21.2 → execsql2-2.22.1}/justfile +2 -2
  19. {execsql2-2.21.2 → execsql2-2.22.1}/pyproject.toml +11 -2
  20. execsql2-2.22.1/scripts/benchmarks/README.md +66 -0
  21. execsql2-2.22.1/scripts/benchmarks/results/.gitignore +3 -0
  22. execsql2-2.22.1/scripts/benchmarks/run_benchmarks.py +317 -0
  23. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/api.py +29 -4
  24. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/cli/__init__.py +1 -1
  25. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/cli/run.py +37 -17
  26. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/config.py +40 -22
  27. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/data/execsql.conf.template +6 -0
  28. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/access.py +6 -5
  29. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/base.py +23 -5
  30. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/dsn.py +2 -0
  31. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/duckdb.py +1 -1
  32. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/mysql.py +6 -8
  33. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/sqlite.py +1 -1
  34. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exceptions.py +14 -11
  35. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/base.py +1 -1
  36. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/delimited.py +22 -16
  37. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/duckdb.py +6 -1
  38. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/feather.py +1 -1
  39. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/json.py +5 -3
  40. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/latex.py +2 -2
  41. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/markdown.py +1 -0
  42. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/ods.py +7 -3
  43. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/raw.py +8 -8
  44. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/sqlite.py +11 -1
  45. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/templates.py +2 -1
  46. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/xls.py +10 -10
  47. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/xml.py +1 -0
  48. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/yaml.py +1 -0
  49. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/zip.py +9 -7
  50. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/format.py +6 -6
  51. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/gui/console.py +3 -3
  52. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/gui/desktop.py +28 -10
  53. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/gui/tui.py +8 -6
  54. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/base.py +14 -8
  55. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/csv.py +8 -3
  56. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/xls.py +1 -0
  57. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/conditions.py +14 -14
  58. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/connect.py +2 -2
  59. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/control.py +1 -1
  60. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/data.py +2 -1
  61. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/io_export.py +4 -0
  62. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/io_fileops.py +85 -39
  63. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/io_import.py +1 -0
  64. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/io_write.py +1 -1
  65. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/prompt.py +24 -22
  66. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/system.py +14 -1
  67. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/models.py +56 -17
  68. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/parser.py +8 -8
  69. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/plugins.py +1 -1
  70. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/engine.py +4 -4
  71. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/executor.py +69 -28
  72. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/parser.py +13 -12
  73. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/variables.py +4 -2
  74. execsql2-2.22.1/src/execsql/state.pyi +122 -0
  75. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/types.py +31 -26
  76. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/auth.py +6 -2
  77. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/datetime.py +3 -3
  78. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/errors.py +7 -5
  79. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/fileio.py +80 -32
  80. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/gui.py +15 -11
  81. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/mail.py +9 -2
  82. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/numeric.py +1 -1
  83. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/regex.py +9 -9
  84. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/test_cli_run.py +45 -1
  85. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_mysql_case_folding.py +18 -1
  86. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_duckdb_exporter.py +29 -0
  87. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_feather.py +1 -1
  88. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_latex_extended.py +10 -7
  89. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_sqlite_exporter.py +38 -0
  90. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_base_extended.py +19 -0
  91. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_csv_importer.py +8 -25
  92. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_fileops_extra.py +102 -0
  93. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_system.py +66 -0
  94. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_log_redaction.py +45 -0
  95. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_api.py +52 -0
  96. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_config_data.py +26 -0
  97. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_models.py +106 -4
  98. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_types.py +2 -3
  99. {execsql2-2.21.2 → execsql2-2.22.1}/uv.lock +1 -1
  100. {execsql2-2.21.2 → execsql2-2.22.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  101. {execsql2-2.21.2 → execsql2-2.22.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  102. {execsql2-2.21.2 → execsql2-2.22.1}/.github/dependabot.yml +0 -0
  103. {execsql2-2.21.2 → execsql2-2.22.1}/.pre-commit-hooks.yaml +0 -0
  104. {execsql2-2.21.2 → execsql2-2.22.1}/.python-version +0 -0
  105. {execsql2-2.21.2 → execsql2-2.22.1}/.readthedocs.yaml +0 -0
  106. {execsql2-2.21.2 → execsql2-2.22.1}/LICENSE.txt +0 -0
  107. {execsql2-2.21.2 → execsql2-2.22.1}/NOTICE +0 -0
  108. {execsql2-2.21.2 → execsql2-2.22.1}/SECURITY.md +0 -0
  109. {execsql2-2.21.2 → execsql2-2.22.1}/docs/about/contributors.md +0 -0
  110. {execsql2-2.21.2 → execsql2-2.22.1}/docs/about/copyright.md +0 -0
  111. {execsql2-2.21.2 → execsql2-2.22.1}/docs/api/cli.md +0 -0
  112. {execsql2-2.21.2 → execsql2-2.22.1}/docs/api/db.md +0 -0
  113. {execsql2-2.21.2 → execsql2-2.22.1}/docs/api/exporters.md +0 -0
  114. {execsql2-2.21.2 → execsql2-2.22.1}/docs/api/importers.md +0 -0
  115. {execsql2-2.21.2 → execsql2-2.22.1}/docs/api/index.md +0 -0
  116. {execsql2-2.21.2 → execsql2-2.22.1}/docs/api/metacommands.md +0 -0
  117. {execsql2-2.21.2 → execsql2-2.22.1}/docs/dev/adding_db_adapters.md +0 -0
  118. {execsql2-2.21.2 → execsql2-2.22.1}/docs/dev/adding_exporters.md +0 -0
  119. {execsql2-2.21.2 → execsql2-2.22.1}/docs/dev/adding_importers.md +0 -0
  120. {execsql2-2.21.2 → execsql2-2.22.1}/docs/dev/adding_metacommands.md +0 -0
  121. {execsql2-2.21.2 → execsql2-2.22.1}/docs/dev/releasing.md +0 -0
  122. {execsql2-2.21.2 → execsql2-2.22.1}/docs/getting-started/requirements.md +0 -0
  123. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/debugging.md +0 -0
  124. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/documentation.md +0 -0
  125. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/encoding.md +0 -0
  126. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/logging.md +0 -0
  127. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/usage.md +0 -0
  128. {execsql2-2.21.2 → execsql2-2.22.1}/docs/guides/using_scripts.md +0 -0
  129. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/Compare_planets.png +0 -0
  130. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/actions.png +0 -0
  131. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/actions2.png +0 -0
  132. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/checkboxes.png +0 -0
  133. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/connect.b64 +0 -0
  134. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/connect.png +0 -0
  135. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/create_conf.png +0 -0
  136. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/data_error1_screenshot.jpg +0 -0
  137. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/entry_form.png +0 -0
  138. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/execsql_console.png +0 -0
  139. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/execsql_logo_01.png +0 -0
  140. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/fatals.png +0 -0
  141. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/logo_small.png +0 -0
  142. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/pause_terminal.png +0 -0
  143. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/pause_terminal_sm.b64 +0 -0
  144. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/pause_terminal_sm.png +0 -0
  145. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/prompt_compare.png +0 -0
  146. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/set_build_commands.jpg +0 -0
  147. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/unit_conversions.b64 +0 -0
  148. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/unit_conversions_029.png +0 -0
  149. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/unmatched.png +0 -0
  150. {execsql2-2.21.2 → execsql2-2.22.1}/docs/images/vim_execsql_highlight.png +0 -0
  151. {execsql2-2.21.2 → execsql2-2.22.1}/docs/index.md +0 -0
  152. {execsql2-2.21.2 → execsql2-2.22.1}/docs/reference/substitution_vars.md +0 -0
  153. {execsql2-2.21.2 → execsql2-2.22.1}/extras/plugin-template/README.md +0 -0
  154. {execsql2-2.21.2 → execsql2-2.22.1}/extras/plugin-template/pyproject.toml +0 -0
  155. {execsql2-2.21.2 → execsql2-2.22.1}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
  156. {execsql2-2.21.2 → execsql2-2.22.1}/extras/plugin-template/tests/test_plugin.py.example +0 -0
  157. {execsql2-2.21.2 → execsql2-2.22.1}/extras/vscode-execsql/README.md +0 -0
  158. {execsql2-2.21.2 → execsql2-2.22.1}/extras/vscode-execsql/package.json +0 -0
  159. {execsql2-2.21.2 → execsql2-2.22.1}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
  160. {execsql2-2.21.2 → execsql2-2.22.1}/scripts/generate_vscode_grammar.py +0 -0
  161. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/__init__.py +0 -0
  162. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/__main__.py +0 -0
  163. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/cli/dsn.py +0 -0
  164. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/cli/help.py +0 -0
  165. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/cli/lint.py +0 -0
  166. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/data/__init__.py +0 -0
  167. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/__init__.py +0 -0
  168. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/factory.py +0 -0
  169. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/firebird.py +0 -0
  170. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/oracle.py +0 -0
  171. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/postgres.py +0 -0
  172. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/db/sqlserver.py +0 -0
  173. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/debug/__init__.py +0 -0
  174. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/debug/repl.py +0 -0
  175. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/__init__.py +0 -0
  176. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/html.py +0 -0
  177. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/parquet.py +0 -0
  178. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/pretty.py +0 -0
  179. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/protocol.py +0 -0
  180. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/values.py +0 -0
  181. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/exporters/xlsx.py +0 -0
  182. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/gui/__init__.py +0 -0
  183. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/gui/base.py +0 -0
  184. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/__init__.py +0 -0
  185. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/feather.py +0 -0
  186. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/json.py +0 -0
  187. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/importers/ods.py +0 -0
  188. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/__init__.py +0 -0
  189. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/debug.py +0 -0
  190. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/dispatch.py +0 -0
  191. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/io.py +0 -0
  192. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/script_ext.py +0 -0
  193. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/metacommands/upsert.py +0 -0
  194. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/py.typed +0 -0
  195. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/__init__.py +0 -0
  196. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/ast.py +0 -0
  197. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/script/control.py +0 -0
  198. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/state.py +0 -0
  199. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/__init__.py +0 -0
  200. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/crypto.py +0 -0
  201. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/strings.py +0 -0
  202. {execsql2-2.21.2 → execsql2-2.22.1}/src/execsql/utils/timer.py +0 -0
  203. {execsql2-2.21.2 → execsql2-2.22.1}/templates/README.md +0 -0
  204. {execsql2-2.21.2 → execsql2-2.22.1}/templates/config_settings.sqlite +0 -0
  205. {execsql2-2.21.2 → execsql2-2.22.1}/templates/example_config_prompt.sql +0 -0
  206. {execsql2-2.21.2 → execsql2-2.22.1}/templates/make_config_db.sql +0 -0
  207. {execsql2-2.21.2 → execsql2-2.22.1}/templates/md_compare.sql +0 -0
  208. {execsql2-2.21.2 → execsql2-2.22.1}/templates/md_glossary.sql +0 -0
  209. {execsql2-2.21.2 → execsql2-2.22.1}/templates/md_upsert.sql +0 -0
  210. {execsql2-2.21.2 → execsql2-2.22.1}/templates/pg_compare.sql +0 -0
  211. {execsql2-2.21.2 → execsql2-2.22.1}/templates/pg_glossary.sql +0 -0
  212. {execsql2-2.21.2 → execsql2-2.22.1}/templates/pg_upsert.sql +0 -0
  213. {execsql2-2.21.2 → execsql2-2.22.1}/templates/script_template.sql +0 -0
  214. {execsql2-2.21.2 → execsql2-2.22.1}/templates/ss_compare.sql +0 -0
  215. {execsql2-2.21.2 → execsql2-2.22.1}/templates/ss_glossary.sql +0 -0
  216. {execsql2-2.21.2 → execsql2-2.22.1}/templates/ss_upsert.sql +0 -0
  217. {execsql2-2.21.2 → execsql2-2.22.1}/tests/__init__.py +0 -0
  218. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/__init__.py +0 -0
  219. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/test_cli.py +0 -0
  220. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/test_cli_e2e.py +0 -0
  221. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/test_lint.py +0 -0
  222. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/test_ping.py +0 -0
  223. {execsql2-2.21.2 → execsql2-2.22.1}/tests/cli/test_profile.py +0 -0
  224. {execsql2-2.21.2 → execsql2-2.22.1}/tests/conftest.py +0 -0
  225. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/__init__.py +0 -0
  226. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_access_windows.py +0 -0
  227. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_base.py +0 -0
  228. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_db_adapters_mocked.py +0 -0
  229. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_db_adapters_mocked_extra.py +0 -0
  230. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_dsn.py +0 -0
  231. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_duckdb.py +0 -0
  232. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_factory.py +0 -0
  233. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_mysql_inprocess.py +0 -0
  234. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_postgres.py +0 -0
  235. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_postgres_inprocess.py +0 -0
  236. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_sqlite.py +0 -0
  237. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_sqlite_extra.py +0 -0
  238. {execsql2-2.21.2 → execsql2-2.22.1}/tests/db/test_sqlserver_inprocess.py +0 -0
  239. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/__init__.py +0 -0
  240. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_base.py +0 -0
  241. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_db.py +0 -0
  242. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_delimited.py +0 -0
  243. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_exporters.py +0 -0
  244. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_html_extended.py +0 -0
  245. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_html_latex.py +0 -0
  246. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_json.py +0 -0
  247. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_json_extended.py +0 -0
  248. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_markdown.py +0 -0
  249. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_ods.py +0 -0
  250. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_ods_export.py +0 -0
  251. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_parquet.py +0 -0
  252. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_pretty_extended.py +0 -0
  253. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_raw_extended.py +0 -0
  254. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_templates.py +0 -0
  255. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_templates_extended.py +0 -0
  256. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_values_extended.py +0 -0
  257. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_xls_xlsx.py +0 -0
  258. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_xlsx.py +0 -0
  259. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_xml.py +0 -0
  260. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_yaml.py +0 -0
  261. {execsql2-2.21.2 → execsql2-2.22.1}/tests/exporters/test_zip.py +0 -0
  262. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/__init__.py +0 -0
  263. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_backends.py +0 -0
  264. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_backends_extended.py +0 -0
  265. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_compare_stats.py +0 -0
  266. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_compute_row_diffs.py +0 -0
  267. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_desktop_dialogs.py +0 -0
  268. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_tui_pilot.py +0 -0
  269. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_tui_pilot_complex.py +0 -0
  270. {execsql2-2.21.2 → execsql2-2.22.1}/tests/gui/test_utils_gui_extended.py +0 -0
  271. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/__init__.py +0 -0
  272. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_csv_edge_cases.py +0 -0
  273. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_feather_importer.py +0 -0
  274. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_json_importer.py +0 -0
  275. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_ods_importer.py +0 -0
  276. {execsql2-2.21.2 → execsql2-2.22.1}/tests/importers/test_xls_importer.py +0 -0
  277. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/__init__.py +0 -0
  278. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/conftest.py +0 -0
  279. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/test_dsn.py +0 -0
  280. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/test_duckdb.py +0 -0
  281. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/test_mysql.py +0 -0
  282. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/test_postgres.py +0 -0
  283. {execsql2-2.21.2 → execsql2-2.22.1}/tests/integration/test_sqlite.py +0 -0
  284. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/__init__.py +0 -0
  285. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_assert.py +0 -0
  286. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_breakpoint.py +0 -0
  287. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_conditions_extra.py +0 -0
  288. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_connect.py +0 -0
  289. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_io_export.py +0 -0
  290. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_io_import.py +0 -0
  291. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands.py +0 -0
  292. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_connect.py +0 -0
  293. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_data.py +0 -0
  294. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_extended.py +0 -0
  295. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_io.py +0 -0
  296. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  297. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  298. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  299. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_pg_upsert.py +0 -0
  300. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_prompt.py +0 -0
  301. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_row_count.py +0 -0
  302. {execsql2-2.21.2 → execsql2-2.22.1}/tests/metacommands/test_show_scripts.py +0 -0
  303. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/__init__.py +0 -0
  304. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
  305. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
  306. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
  307. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/audit_smoke.sql +0 -0
  308. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/config_runtime.sql +0 -0
  309. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/control_flow.sql +0 -0
  310. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
  311. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/error_handling.sql +0 -0
  312. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
  313. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/import_options.sql +0 -0
  314. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/includes/helper.sql +0 -0
  315. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/includes.sql +0 -0
  316. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/io_formats.sql +0 -0
  317. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
  318. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/multi_db.sql +0 -0
  319. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
  320. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
  321. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/predicates.sql +0 -0
  322. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/scripts.sql +0 -0
  323. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/smoke.sql +0 -0
  324. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
  325. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
  326. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
  327. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/template_export.sql +0 -0
  328. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/timer.sql +0 -0
  329. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/transactions.sql +0 -0
  330. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/write_create_table.sql +0 -0
  331. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
  332. {execsql2-2.21.2 → execsql2-2.22.1}/tests/scripts/test_sql_scripts.py +0 -0
  333. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/__init__.py +0 -0
  334. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_env_detection.py +0 -0
  335. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_expansion_bomb.py +0 -0
  336. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_import_regex_hardening.py +0 -0
  337. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_odbc_injection.py +0 -0
  338. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_path_containment.py +0 -0
  339. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_substitution_injection.py +0 -0
  340. {execsql2-2.21.2 → execsql2-2.22.1}/tests/security/test_zip_bomb.py +0 -0
  341. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_api_extended.py +0 -0
  342. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_ast.py +0 -0
  343. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_ast_parser.py +0 -0
  344. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_bundled_templates.py +0 -0
  345. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_config.py +0 -0
  346. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_config_extended.py +0 -0
  347. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_debug_repl.py +0 -0
  348. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_engine.py +0 -0
  349. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_error_messages.py +0 -0
  350. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_exceptions.py +0 -0
  351. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_executor.py +0 -0
  352. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_executor_inprocess.py +0 -0
  353. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_format.py +0 -0
  354. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_mail.py +0 -0
  355. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_package.py +0 -0
  356. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_parser.py +0 -0
  357. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_parser_params.py +0 -0
  358. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_plugins.py +0 -0
  359. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_registry.py +0 -0
  360. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_script.py +0 -0
  361. {execsql2-2.21.2 → execsql2-2.22.1}/tests/test_state.py +0 -0
  362. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/__init__.py +0 -0
  363. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_auth.py +0 -0
  364. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_auth_extra.py +0 -0
  365. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_crypto.py +0 -0
  366. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_datetime.py +0 -0
  367. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_encodedfile_context.py +0 -0
  368. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_errors.py +0 -0
  369. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_errors_extra.py +0 -0
  370. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_fileio.py +0 -0
  371. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_fileio_extra.py +0 -0
  372. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_numeric.py +0 -0
  373. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_regex.py +0 -0
  374. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_strings.py +0 -0
  375. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_timer.py +0 -0
  376. {execsql2-2.21.2 → execsql2-2.22.1}/tests/utils/test_timer_extra.py +0 -0
  377. {execsql2-2.21.2 → execsql2-2.22.1}/zensical.toml +0 -0
@@ -28,6 +28,55 @@ jobs:
28
28
  - run: ruff check src/ tests/
29
29
  - run: ruff format --check src/ tests/
30
30
 
31
+ typecheck:
32
+ name: typecheck
33
+ runs-on: ubuntu-latest
34
+ permissions:
35
+ contents: read
36
+ steps:
37
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
38
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
39
+ with:
40
+ python-version: "3.13"
41
+ cache: "pip"
42
+ - run: python -m pip install --upgrade pip
43
+ - run: python -m pip install ".[dev]"
44
+ - run: mypy src/execsql/
45
+
46
+ pre-commit:
47
+ name: pre-commit
48
+ runs-on: ubuntu-latest
49
+ permissions:
50
+ contents: read
51
+ steps:
52
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
53
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
54
+ with:
55
+ python-version: "3.13"
56
+ cache: "pip"
57
+ - run: python -m pip install --upgrade pip
58
+ - run: python -m pip install ".[dev]"
59
+ # mypy runs in the dedicated typecheck job; the local hook needs uv
60
+ - run: pre-commit run --all-files
61
+ env:
62
+ SKIP: mypy
63
+
64
+ docs:
65
+ name: docs
66
+ runs-on: ubuntu-latest
67
+ permissions:
68
+ contents: read
69
+ steps:
70
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
71
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
72
+ with:
73
+ python-version: "3.13"
74
+ cache: "pip"
75
+ - run: python -m pip install --upgrade pip
76
+ - run: python -m pip install ".[dev]"
77
+ - run: cp CHANGELOG.md docs/about/change_log.md
78
+ - run: zensical build
79
+
31
80
  tests:
32
81
  strategy:
33
82
  fail-fast: false
@@ -233,7 +282,7 @@ jobs:
233
282
  build:
234
283
  name: Build distribution 📦
235
284
  runs-on: ubuntu-latest
236
- needs: [ lint, tests, integration-tests ]
285
+ needs: [ lint, typecheck, pre-commit, docs, tests, integration-tests ]
237
286
  if: startsWith(github.ref, 'refs/tags/v')
238
287
  permissions:
239
288
  contents: read
@@ -34,8 +34,9 @@ coverage.xml
34
34
  .tox/
35
35
  htmlcov/
36
36
  execsql.log
37
+ scripts/*
37
38
  !scripts/generate_vscode_grammar.py
38
- scripts/
39
+ !scripts/benchmarks/
39
40
  !tests/scripts
40
41
 
41
42
  # Distribution
@@ -80,3 +80,15 @@ repos:
80
80
  rev: v0.25
81
81
  hooks:
82
82
  - id: validate-pyproject
83
+ # Static type checking — same command as `just typecheck`, run in the
84
+ # project environment so results match local and CI. Whole-program run
85
+ # (pass_filenames: false) because mypy cannot check files in isolation.
86
+ # The CI pre-commit job sets SKIP=mypy; CI has a dedicated typecheck gate.
87
+ - repo: local
88
+ hooks:
89
+ - id: mypy
90
+ name: mypy
91
+ entry: uv run mypy src/execsql/
92
+ language: system
93
+ pass_filenames: false
94
+ files: ^(src/execsql/.*\.pyi?|pyproject\.toml)$
@@ -13,6 +13,30 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.22.1] - 2026-07-04
17
+
18
+ ### Fixed
19
+
20
+ - Database factory functions (`db_SQLite(..., new_db=True)`, etc.) no longer crash with `AttributeError` when called before `run()` in library use.
21
+ - `run(halt_on_error=False)` now reports non-halting SQL and metacommand errors in `result.errors` and sets `result.success` to `False`; previously they were silently dropped.
22
+
23
+ ______________________________________________________________________
24
+
25
+ ## [2.22.0] - 2026-07-03
26
+
27
+ ### Added
28
+
29
+ - `run()` now accepts `allow_rm_file=False` and `allow_serve=False` to disable the `RM_FILE` and `SERVE` metacommands for library callers, matching the existing `allow_system_cmd` kwarg and the `--no-rm-file` / `--no-serve` CLI flags.
30
+ - `[config] system_cmd_timeout` can bound foreground `SYSTEM_CMD` execution; the default remains no timeout.
31
+
32
+ ### Fixed
33
+
34
+ - `EXPORT … FORMAT SQLITE` and `EXPORT … FORMAT DUCKDB` correctly preserve source column types when exporting from a typed database: text columns stay text (not re-inferred as TIMESTAMP or numeric from value shape), `decimal.Decimal` values bind without error (converted to their exact string representation for SQLite), and unsupported Python objects such as PostGIS geometry are stringified instead of raising a bind error.
35
+ - `EXPORT … FORMAT LATEX` into a ZIP archive no longer crashes with `AttributeError`.
36
+ - Connecting to a server-based database without a server or database name now stops with a clear error message instead of a driver traceback.
37
+
38
+ ______________________________________________________________________
39
+
16
40
  ## [2.21.2] - 2026-06-30
17
41
 
18
42
  ### Fixed
@@ -32,13 +32,19 @@ Linting and formatting are handled by [ruff](https://docs.astral.sh/ruff/):
32
32
  just lint
33
33
  ```
34
34
 
35
- [pre-commit](https://pre-commit.com/) hooks enforce additional checks (gitleaks, uv-lock, mdformat, markdownlint, typos, validate-pyproject) on every commit. To run them manually against all files:
35
+ Static type checking is handled by [mypy](https://mypy.readthedocs.io/) and must pass with zero errors:
36
+
37
+ ```bash
38
+ just typecheck
39
+ ```
40
+
41
+ [pre-commit](https://pre-commit.com/) hooks enforce additional checks (gitleaks, uv-lock, mdformat, markdownlint, typos, validate-pyproject, mypy) on every commit. To run them manually against all files:
36
42
 
37
43
  ```bash
38
44
  just pre-commit
39
45
  ```
40
46
 
41
- CI rejects PRs that fail linting or any pre-commit check.
47
+ CI rejects PRs that fail ruff lint/format, mypy type checking, the full pre-commit suite, docs build, or tests.
42
48
 
43
49
  ## Running Tests
44
50
 
@@ -96,12 +102,18 @@ Pre-releases are published to PyPI but are not installed by default — `pip ins
96
102
 
97
103
  Triggered on pushes to `main`, any `v*.*.*` tag, and pull requests.
98
104
 
99
- | Job | Trigger | What it does |
100
- | ------------------ | ------------------ | ----------------------------------------------- |
101
- | `tests` | all events | Runs the test matrix (3 OS × 5 Python versions) |
102
- | `build` | `v*.*.*` tags only | Builds sdist + wheel, checks with twine |
103
- | `publish` | `v*.*.*` tags only | Publishes to PyPI via OIDC trusted publishing |
104
- | `generate-release` | `v*.*.*` tags only | Creates a GitHub Release with dist artifacts |
105
+ | Job | Trigger | What it does |
106
+ | ---------------------- | ------------------ | ----------------------------------------------------- |
107
+ | `lint` | all events | Runs `ruff check` and `ruff format --check` |
108
+ | `typecheck` | all events | Runs `mypy src/execsql/` (zero-error gate) |
109
+ | `pre-commit` | all events | Runs the full pre-commit suite against all files |
110
+ | `docs` | all events | Builds the documentation site |
111
+ | `tests` | all events | Runs the test matrix (3 OS × 5 Python versions) |
112
+ | `integration-tests` | all events | Runs tests against live database service containers |
113
+ | `access-tests-windows` | all events | Runs MS Access driver tests on Windows (non-blocking) |
114
+ | `build` | `v*.*.*` tags only | Builds sdist + wheel, checks with twine |
115
+ | `publish` | `v*.*.*` tags only | Publishes to PyPI via OIDC trusted publishing |
116
+ | `generate-release` | `v*.*.*` tags only | Creates a GitHub Release with dist artifacts |
105
117
 
106
118
  ### PyPI trusted publishing setup
107
119
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.21.2
3
+ Version: 2.22.1
4
4
  Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
5
5
  Project-URL: Homepage, https://execsql2.readthedocs.io
6
6
  Project-URL: Repository, https://github.com/geocoug/execsql
@@ -130,10 +130,7 @@ Requires-Dist: pg-upsert>=1.23.0; extra == 'upsert'
130
130
  Description-Content-Type: text/markdown
131
131
 
132
132
  > [!NOTE]
133
- > **This is a maintained fork of [execsql](https://execsql.readthedocs.io/).**
134
- > The original monolith has been fully refactored into a modular package.
135
- > The CLI and configuration are backwards-compatible with upstream v1.130.1.
136
- > Report issues at [github.com/geocoug/execsql/issues](https://github.com/geocoug/execsql/issues).
133
+ > ***execsql2* is a maintained fork of [execsql](https://execsql.readthedocs.io/)**, originally authored by R.Dreas Nielsen and no longer actively maintained upstream. The monolith has been fully refactored into a modular package; the CLI and configuration remain backwards-compatible with upstream v1.130.1. Maintained by [Caleb Grant](https://github.com/geocoug) and distributed on PyPI as [`execsql2`](https://pypi.org/project/execsql2/). Report issues at [github.com/geocoug/execsql/issues](https://github.com/geocoug/execsql/issues).
137
134
 
138
135
  <div align="center">
139
136
 
@@ -155,48 +152,59 @@ Description-Content-Type: text/markdown
155
152
 
156
153
  </div>
157
154
 
158
- # Fork
155
+ ## Overview
159
156
 
160
- *execsql2* is a maintained fork of [execsql](https://execsql.readthedocs.io/) originally authored by R.Dreas Nielsen. The upstream project is no longer actively maintained. This fork is maintained by [Caleb Grant](https://github.com/geocoug) and is distributed on PyPI as `execsql2`. Complete documentation is at [execsql2.readthedocs.io](https://execsql2.readthedocs.io/).
157
+ *execsql* runs SQL scripts against PostgreSQL, MySQL/MariaDB, SQLite, DuckDB, MS-SQL-Server, MS-Access, Firebird, Oracle, or an ODBC DSN. In addition to standard SQL, it supports a set of metacommands (embedded in SQL comments) for importing and exporting data, copying data between databases, conditional execution, looping, substitution variables, and interactive prompts. Because metacommands live in SQL comments, scripts remain valid SQL and are ignored by other tools such as `psql` or `sqlcmd`.
161
158
 
162
- # Overview
159
+ ## Example
163
160
 
164
- *execsql* runs SQL scripts against PostgreSQL, MySQL/MariaDB, SQLite, DuckDB, MS-SQL-Server, MS-Access, Firebird, Oracle, or an ODBC DSN. In addition to standard SQL, it supports a set of metacommands (embedded in SQL comments) for importing and exporting data, copying data between databases, conditional execution, looping, substitution variables, and interactive prompts. Because metacommands live in SQL comments, scripts remain valid SQL and are ignored by other tools such as `psql` or `sqlcmd`.
161
+ Lines prefixed with `-- !x!` are metacommands; identifiers wrapped in `!!` are substitution variables:
165
162
 
166
- # Installation
163
+ ```sql
164
+ -- ==== Configuration ====
165
+ -- Put the (date-tagged) logfile name in the 'inputfile' substitution variable.
166
+ -- !x! SUB inputfile logs/errors_!!$date_tag!!
167
+ -- Ensure that the export directory will be created if necessary.
168
+ -- !x! CONFIG MAKE_EXPORT_DIRS Yes
167
169
 
168
- ```bash
169
- pip install execsql2
170
+ -- ==== Display Fatal Errors ====
171
+ -- !x! IF(file_exists(!!inputfile!!))
172
+ -- Import the data to a staging table.
173
+ -- !x! IMPORT TO REPLACEMENT staging.errorlog FROM !!inputfile!!
174
+ -- Create a view to display only fatal errors.
175
+ create temporary view fatals as
176
+ select user, run_time, process
177
+ from staging.errorlog
178
+ where severity = 'FATAL';
179
+ -- !x! IF(HASROWS(fatals))
180
+ -- Export the fatal errors to a dated report.
181
+ -- !x! EXPORT fatals TO reports/error_report_!!$date_tag!! AS CSV
182
+ -- Also display it to the user in a GUI.
183
+ -- !x! PROMPT MESSAGE "Fatal errors in !!inputfile!!:" DISPLAY fatals
184
+ -- !x! ELSE
185
+ -- !x! WRITE "There are no fatal errors."
186
+ -- !x! ENDIF
187
+ -- !x! ELSE
188
+ -- !x! WRITE "There is no error log."
189
+ -- !x! ENDIF
190
+ drop table if exists staging.errorlog cascade;
170
191
  ```
171
192
 
172
- Optional extras install database drivers and feature bundles:
193
+ The `PROMPT` metacommand produces a GUI display of the data:
194
+
195
+ ![PROMPT display of 'fatals' view](https://execsql2.readthedocs.io/en/latest/images/fatals.png)
196
+
197
+ ## Installation
173
198
 
174
199
  ```bash
175
- # Database drivers
176
- pip install execsql2[postgres] # PostgreSQL (psycopg2-binary)
177
- pip install execsql2[mysql] # MySQL / MariaDB (pymysql)
178
- pip install execsql2[mssql] # SQL Server (pyodbc)
179
- pip install execsql2[duckdb] # DuckDB
180
- pip install execsql2[firebird] # Firebird (firebird-driver)
181
- pip install execsql2[oracle] # Oracle (oracledb)
182
- pip install execsql2[odbc] # ODBC DSN (pyodbc)
183
-
184
- # Feature bundles
185
- pip install execsql2[formats] # ODS, Excel, Jinja2, Feather, Parquet, HDF5
186
- pip install execsql2[upsert] # pg-upsert for PostgreSQL upsert operations
187
- pip install execsql2[auth] # OS keyring integration
188
- pip install execsql2[auth-plaintext] # Keyring + plaintext file backend (headless Linux)
189
- pip install execsql2[auth-encrypted] # Keyring + encrypted file backend (headless Linux)
190
- pip install execsql2[map] # tkintermapview for PROMPT MAP
191
-
192
- # Convenience
193
- pip install execsql2[all-db] # All database drivers
194
- pip install execsql2[all] # Everything
200
+ pip install execsql2 # core — SQLite works with no extras
201
+ pip install "execsql2[postgres]" # add a driver: postgres, mysql, mssql, duckdb, firebird, oracle, odbc
202
+ pip install "execsql2[all]" # everything: all drivers plus all feature extras
195
203
  ```
196
204
 
197
- SQLite connections use Python's standard library and require no additional packages.
205
+ Feature extras cover spreadsheet and Parquet/Feather formats, keyring authentication, PostgreSQL upsert, and more see the [installation guide](https://execsql2.readthedocs.io/en/latest/getting-started/installation/) for the full list.
198
206
 
199
- # Usage
207
+ ## Usage
200
208
 
201
209
  ```text
202
210
  execsql [OPTIONS] SQL_SCRIPT [SERVER DATABASE | DATABASE_FILE]
@@ -214,7 +222,7 @@ execsql -to script.sql myserver myservice # Oracle
214
222
  execsql script.sql # read connection from config file
215
223
  ```
216
224
 
217
- ## Supported Databases
225
+ ### Supported Databases
218
226
 
219
227
  | Flag | Database |
220
228
  | ---- | --------------- |
@@ -228,67 +236,53 @@ execsql script.sql # read connection from config file
228
236
  | `o` | Oracle |
229
237
  | `d` | ODBC DSN |
230
238
 
231
- ## Options
232
-
233
- | Flag | Description |
234
- | ------------------------------------- | ------------------------------------------------------------------ |
235
- | `-t {p,m,s,l,k,a,f,o,d}` | Database type |
236
- | `-u USER` | Database username |
237
- | `-p PORT` | Server port |
238
- | `-a VALUE` | Set substitution variable `$ARG_x` |
239
- | `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values |
240
- | `-c SCRIPT` | Execute inline SQL or metacommand string |
241
- | `-d` | Auto-create export directories |
242
- | `-e ENCODING` / `--database-encoding` | Character encoding used in the database |
243
- | `-f ENCODING` | Script file encoding (default: UTF-8) |
244
- | `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output |
245
- | `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT |
246
- | `-l` | Write run log to `~/execsql.log` |
247
- | `-m` | List metacommands and exit |
248
- | `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
249
- | `-o` / `--online-help` | Open the online documentation in the default browser |
250
- | `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file) |
251
- | `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
252
- | `-w` | Skip password prompt when a username is supplied |
253
- | `-y` / `--encodings` | List available encoding names and exit |
254
- | `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32) |
255
- | `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
256
- | `--output-dir DIR` | Default base directory for EXPORT output files |
257
- | `--dry-run` | Parse the script and report commands without executing |
258
- | `--lint` | Static analysis: check structure and warn on issues (no DB) |
259
- | `--parse-tree` | Print the script's AST structure and exit (no DB) |
260
- | `--list-plugins` | List discovered plugins and exit |
261
- | `--ping` | Test database connectivity and exit |
262
- | `--profile` | Show per-statement timing summary after execution |
263
- | `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20) |
264
- | `--progress` | Show a progress bar for long-running IMPORT operations |
265
- | `--config FILE` | Load an explicit config file (highest priority after CLI args) |
266
- | `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs) |
267
- | `--no-rm-file` | Disable the `RM_FILE` metacommand (no script-driven file deletion) |
268
- | `--no-serve` | Disable the `SERVE` metacommand (no script-driven file streaming) |
269
- | `--init-config` | Print a default `execsql.conf` template to stdout and exit |
270
- | `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
271
- | `--dump-keywords` | Print metacommand keywords as JSON and exit |
272
- | `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
273
-
274
- Run `execsql --help` for the full option list, or `execsql -m` to list all metacommands.
275
-
276
- # Features
239
+ ### Common options
240
+
241
+ | Flag | Description |
242
+ | ------------------------------------------------- | --------------------------------------------------------------- |
243
+ | `-t {p,m,s,l,k,a,f,o,d}` | Database type |
244
+ | `-u USER` | Database username |
245
+ | `-p PORT` | Server port |
246
+ | `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
247
+ | `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
248
+ | `-c SCRIPT` | Execute inline SQL or metacommand string |
249
+ | `-a VALUE` | Set substitution variable `$ARG_x` |
250
+ | `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
251
+ | `--config FILE` | Load an explicit config file |
252
+ | `--dry-run` | Parse the script and report commands without executing |
253
+ | `--lint` | Static analysis: check structure and warn on issues (no DB) |
254
+ | `--ping` | Test database connectivity and exit |
255
+ | `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
256
+ | `--no-system-cmd` / `--no-rm-file` / `--no-serve` | Disable the `SYSTEM_CMD` / `RM_FILE` / `SERVE` metacommands |
257
+
258
+ See the [full options reference](https://execsql2.readthedocs.io/en/latest/getting-started/syntax/#options) or run `execsql --help` for the complete list, and `execsql -m` for all metacommands.
259
+
260
+ ## Features
261
+
262
+ **Data movement**
277
263
 
278
264
  - Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
279
265
  - Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
280
266
  - Copy data between databases, including across different DBMS types.
267
+
268
+ **Script logic**
269
+
281
270
  - Conditionally execute SQL and metacommands using `IF`/`ELSE`/`ENDIF` based on data values, DBMS type, or user input.
282
- - Validate data with `ASSERT` halt the script with a clear error message if a condition is false (ideal for CI pipelines).
283
- - Loop over blocks of SQL and metacommands using `LOOP`/`ENDLOOP`.
271
+ - Loop over blocks of SQL and metacommands using `LOOP`/`ENDLOOP`; include or chain scripts with `INCLUDE` and `SCRIPT`.
284
272
  - Use substitution variables (`SUB`, `$ARG_x`, built-in variables like `$date_tag`) to parameterize scripts.
285
- - Include or chain scripts with `INCLUDE` and `SCRIPT`.
273
+ - Validate data with `ASSERT` halt the script with a clear error message if a condition is false (ideal for CI pipelines).
274
+
275
+ **Interaction & observability**
276
+
286
277
  - Display query results in a GUI dialog; optionally prompt the user to select a row, enter a value, or submit a form.
287
278
  - Write status messages or tabular output to the console or a file during execution.
288
279
  - Automatically log each run, recording databases used, scripts executed, and user responses.
280
+
281
+ **Extensibility**
282
+
289
283
  - Extend with custom metacommands, exporters, and importers via the plugin system.
290
284
 
291
- # Library API
285
+ ## Library API
292
286
 
293
287
  execsql can be used as a Python library for programmatic script execution:
294
288
 
@@ -331,56 +325,43 @@ if not result.success:
331
325
  result.raise_on_error() # raises ExecSqlError
332
326
  ```
333
327
 
334
- Use a pre-existing database connection instead of a DSN:
328
+ Use a pre-existing database connection instead of a DSN — useful for reusing one connection across multiple `run()` calls, or when connection parameters come from your application's configuration rather than a URL:
335
329
 
336
330
  ```python
337
- from execsql.db.factory import db_SQLite
338
- conn = db_SQLite("my.db", new_db=True)
339
- result = run(sql="SELECT 1;", connection=conn)
340
- # run() does NOT close this connection you manage its lifecycle
341
- ```
342
-
343
- Each call to `run()` uses an isolated `RuntimeContext`, so multiple calls do not share state.
344
-
345
- # An Illustration
331
+ from execsql import run
332
+ from execsql.db.factory import db_Postgres, db_SQLite
333
+
334
+ # PostgreSQL connection object (psycopg3 under the hood)
335
+ conn = db_Postgres(
336
+ "db.example.com", # server
337
+ "warehouse", # database
338
+ user="etl",
339
+ port=5432, # optional, default 5432
340
+ password="s3cret", # omit to use keyring / interactive prompt
341
+ )
346
342
 
347
- The following script demonstrates metacommands and substitution variables. Lines prefixed with `-- !x!` are metacommands; identifiers wrapped in `!!` are substitution variables.
343
+ # Reuse the same connection across multiple calls
344
+ staged = run(script="stage.sql", connection=conn)
345
+ loaded = run(
346
+ script="load.sql",
347
+ connection=conn,
348
+ variables={"RUN_DATE": "2026-07-03"},
349
+ )
348
350
 
349
- ```sql
350
- -- ==== Configuration ====
351
- -- Put the (date-tagged) logfile name in the 'inputfile' substitution variable.
352
- -- !x! SUB inputfile logs/errors_!!$date_tag!!
353
- -- Ensure that the export directory will be created if necessary.
354
- -- !x! CONFIG MAKE_EXPORT_DIRS Yes
351
+ # run() does NOT close the connection — you manage its lifecycle
352
+ conn.close()
355
353
 
356
- -- ==== Display Fatal Errors ====
357
- -- !x! IF(file_exists(!!inputfile!!))
358
- -- Import the data to a staging table.
359
- -- !x! IMPORT TO REPLACEMENT staging.errorlog FROM !!inputfile!!
360
- -- Create a view to display only fatal errors.
361
- create temporary view fatals as
362
- select user, run_time, process
363
- from staging.errorlog
364
- where severity = 'FATAL';
365
- -- !x! IF(HASROWS(fatals))
366
- -- Export the fatal errors to a dated report.
367
- -- !x! EXPORT fatals TO reports/error_report_!!$date_tag!! AS CSV
368
- -- Also display it to the user in a GUI.
369
- -- !x! PROMPT MESSAGE "Fatal errors in !!inputfile!!:" DISPLAY fatals
370
- -- !x! ELSE
371
- -- !x! WRITE "There are no fatal errors."
372
- -- !x! ENDIF
373
- -- !x! ELSE
374
- -- !x! WRITE "There is no error log."
375
- -- !x! ENDIF
376
- drop table if exists staging.errorlog cascade;
354
+ # SQLite works the same way
355
+ conn = db_SQLite("my.db", new_db=True)
356
+ result = run(sql="SELECT 1;", connection=conn)
357
+ conn.close()
377
358
  ```
378
359
 
379
- The `PROMPT` metacommand produces a GUI display of the data:
360
+ Factory functions exist for every supported backend (`db_Postgres`, `db_MySQL`, `db_SQLite`, `db_DuckDB`, `db_SqlServer`, `db_Oracle`, `db_Firebird`, `db_Access`, `db_Dsn`) — see [`execsql.db.factory`](https://execsql2.readthedocs.io/en/latest/api/db/).
380
361
 
381
- ![PROMPT display of 'fatals' view](https://execsql2.readthedocs.io/en/latest/images/fatals.png)
362
+ Each call to `run()` uses an isolated `RuntimeContext`, so multiple calls do not share state.
382
363
 
383
- # Formatting Scripts
364
+ ## Formatting Scripts
384
365
 
385
366
  The `execsql-format` command normalizes execsql script files: it uppercases metacommand keywords, corrects block indentation, and optionally reformats SQL via `sqlglot`. The metacommand / indent / keyword reformatting is built into `execsql2`; SQL reformatting requires the `[formatter]` extra (or pass `--no-sql` to skip it):
386
367
 
@@ -403,14 +384,14 @@ execsql-format --no-sql --in-place scripts/
403
384
  ```yaml
404
385
  repos:
405
386
  - repo: https://github.com/geocoug/execsql
406
- rev: v2.21.2
387
+ rev: v2.22.1
407
388
  hooks:
408
389
  - id: execsql-format
409
390
  ```
410
391
 
411
392
  The hook rewrites `*.sql` files in place by default. See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for `--check`, `--indent`, and other options.
412
393
 
413
- # VS Code Syntax Highlighting
394
+ ## VS Code Syntax Highlighting
414
395
 
415
396
  A VS Code extension for execsql syntax highlighting is included in [`extras/vscode-execsql`](extras/vscode-execsql). It injects a TextMate grammar into `.sql` files, adding highlighting for `-- !x!` metacommand markers, keywords (control flow, block, action, directive), variable substitutions (`!!var!!`, `!{var}!`), built-in functions, export formats, and config options — all layered on top of standard SQL highlighting.
416
397
 
@@ -422,7 +403,7 @@ ln -s /path/to/execsql/extras/vscode-execsql ~/.vscode/extensions/execsql-syntax
422
403
 
423
404
  See the [extension README](extras/vscode-execsql/README.md) for Windows instructions, color customization, and troubleshooting.
424
405
 
425
- # Templates
406
+ ## Templates
426
407
 
427
408
  The `templates/` directory in this repository includes ready-to-use execsql scripts:
428
409
 
@@ -432,13 +413,14 @@ The `templates/` directory in this repository includes ready-to-use execsql scri
432
413
  - **`script_template.sql`**: A framework for new scripts with sections for configuration, logging, and error reporting.
433
414
  - **`execsql.conf`**: An annotated configuration file covering all available settings.
434
415
 
435
- # Documentation
416
+ ## Documentation
436
417
 
437
418
  Full documentation, including a complete metacommand reference and 30+ examples, is at [execsql2.readthedocs.io](https://execsql2.readthedocs.io/).
438
419
 
439
- # Copyright and License
420
+ ## Copyright and License
440
421
 
441
422
  Copyright (c) 2007-2025 R.Dreas Nielsen
423
+
442
424
  Copyright (c) 2026-present Caleb Grant
443
425
 
444
426
  This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [GNU General Public License](http://www.gnu.org/licenses/) for more details.