execsql2 2.28.0__tar.gz → 2.30.0__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 (492) hide show
  1. execsql2-2.30.0/.github/ISSUE_TEMPLATE/bug_report.yml +57 -0
  2. execsql2-2.30.0/.github/ISSUE_TEMPLATE/feature_request.yml +28 -0
  3. {execsql2-2.28.0 → execsql2-2.30.0}/.github/workflows/ci-cd.yml +3 -21
  4. {execsql2-2.28.0 → execsql2-2.30.0}/CHANGELOG.md +45 -0
  5. {execsql2-2.28.0 → execsql2-2.30.0}/PKG-INFO +2 -4
  6. {execsql2-2.28.0 → execsql2-2.30.0}/README.md +1 -1
  7. {execsql2-2.28.0 → execsql2-2.30.0}/docs/about/divergence.md +16 -11
  8. {execsql2-2.28.0 → execsql2-2.30.0}/docs/api/index.md +1 -1
  9. {execsql2-2.28.0 → execsql2-2.30.0}/docs/dev/architecture.md +73 -36
  10. {execsql2-2.28.0 → execsql2-2.30.0}/docs/getting-started/syntax.md +3 -3
  11. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/editors.md +57 -9
  12. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/examples.md +20 -10
  13. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/formatter.md +2 -2
  14. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/sql_syntax.md +4 -0
  15. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/usage.md +2 -0
  16. {execsql2-2.28.0 → execsql2-2.30.0}/docs/reference/configuration.md +6 -2
  17. {execsql2-2.28.0 → execsql2-2.30.0}/docs/reference/lint.md +3 -1
  18. {execsql2-2.28.0 → execsql2-2.30.0}/docs/reference/metacommands.md +38 -15
  19. {execsql2-2.28.0 → execsql2-2.30.0}/docs/reference/security.md +5 -3
  20. {execsql2-2.28.0 → execsql2-2.30.0}/docs/reference/substitution_vars.md +5 -2
  21. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/README.md +14 -10
  22. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/extension.js +71 -25
  23. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/package.json +22 -0
  24. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +1 -1
  25. {execsql2-2.28.0 → execsql2-2.30.0}/justfile +2 -2
  26. {execsql2-2.28.0 → execsql2-2.30.0}/pyproject.toml +2 -4
  27. {execsql2-2.28.0 → execsql2-2.30.0}/scripts/generate_vscode_grammar.py +1 -26
  28. execsql2-2.30.0/src/execsql/__main__.py +12 -0
  29. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/api.py +5 -0
  30. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/__init__.py +3 -14
  31. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/application.py +5 -3
  32. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/lint.py +4 -1
  33. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/lsp.py +42 -0
  34. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/dsn.py +3 -1
  35. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/lint.py +14 -6
  36. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/run.py +17 -9
  37. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/config.py +32 -6
  38. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/base.py +6 -9
  39. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/duckdb.py +96 -14
  40. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/mysql.py +19 -3
  41. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/postgres.py +1 -1
  42. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/xls.py +3 -2
  43. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/gui/__init__.py +3 -31
  44. execsql2-2.30.0/src/execsql/lsp/_util.py +21 -0
  45. execsql2-2.30.0/src/execsql/lsp/cache.py +73 -0
  46. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/code_actions.py +10 -14
  47. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/completion.py +8 -15
  48. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/diagnostics.py +14 -2
  49. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/document.py +139 -41
  50. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/hover.py +5 -13
  51. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/navigation.py +7 -10
  52. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/server.py +111 -29
  53. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/manifest.py +29 -9
  54. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/__init__.py +0 -2
  55. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/io_fileops.py +5 -0
  56. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/executor.py +2 -21
  57. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/variables.py +42 -10
  58. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/state.py +6 -3
  59. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/state.pyi +2 -0
  60. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/types.py +8 -0
  61. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/errors.py +19 -36
  62. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/fileio.py +44 -48
  63. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/regex.py +0 -5
  64. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_cli.py +13 -18
  65. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_cli_e2e.py +0 -33
  66. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_cli_run.py +0 -27
  67. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_cli_surface.py +1 -1
  68. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_commands.py +3 -1
  69. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_dispatch.py +20 -2
  70. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_lint.py +20 -0
  71. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_manifest.py +23 -0
  72. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_duckdb.py +65 -0
  73. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_postgres_inprocess.py +3 -0
  74. execsql2-2.30.0/tests/db/test_quoted_substitution_roundtrip.py +40 -0
  75. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_transactions.py +0 -1
  76. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/conftest.py +0 -9
  77. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_exporters.py +1 -1
  78. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_ods_export.py +1 -1
  79. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_values.py +0 -5
  80. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_xlsx.py +1 -1
  81. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_backends.py +0 -64
  82. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_xls_importer.py +32 -0
  83. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/test_mysql.py +56 -0
  84. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/test_postgres.py +56 -0
  85. execsql2-2.30.0/tests/lsp/test_cache.py +130 -0
  86. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_document.py +7 -0
  87. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_server.py +264 -15
  88. execsql2-2.30.0/tests/metacommands/conftest.py +22 -0
  89. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands.py +6 -46
  90. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_extended.py +0 -7
  91. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_script_ext.py +0 -4
  92. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_row_count.py +3 -31
  93. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/audit_smoke.sql +3 -3
  94. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_env_detection.py +2 -2
  95. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_log_redaction.py +1 -1
  96. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_log_secrets.py +48 -0
  97. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_substitution_injection.py +64 -20
  98. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_api_isolation.py +28 -0
  99. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_config_data.py +50 -0
  100. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_executor_inprocess.py +0 -8
  101. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_reference.py +1 -1
  102. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_registry.py +5 -5
  103. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_state.py +1 -1
  104. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_tooling_versions.py +10 -0
  105. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_types.py +17 -0
  106. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_errors.py +21 -36
  107. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_errors_extra.py +17 -15
  108. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_fileio.py +0 -20
  109. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_fileio_extra.py +1 -9
  110. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_regex.py +0 -25
  111. {execsql2-2.28.0 → execsql2-2.30.0}/uv.lock +1 -140
  112. execsql2-2.28.0/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  113. execsql2-2.28.0/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  114. execsql2-2.28.0/docs/images/connect.b64 +0 -1
  115. execsql2-2.28.0/docs/images/create_conf.png +0 -0
  116. execsql2-2.28.0/docs/images/entry_form.png +0 -0
  117. execsql2-2.28.0/docs/images/execsql_console.png +0 -0
  118. execsql2-2.28.0/docs/images/logo_small.png +0 -0
  119. execsql2-2.28.0/docs/images/pause_terminal.png +0 -0
  120. execsql2-2.28.0/docs/images/pause_terminal_sm.b64 +0 -1
  121. execsql2-2.28.0/docs/images/prompt_compare.png +0 -0
  122. execsql2-2.28.0/docs/images/unit_conversions.b64 +0 -1
  123. execsql2-2.28.0/src/execsql/__main__.py +0 -13
  124. {execsql2-2.28.0 → execsql2-2.30.0}/.github/dependabot.yml +0 -0
  125. {execsql2-2.28.0 → execsql2-2.30.0}/.gitignore +0 -0
  126. {execsql2-2.28.0 → execsql2-2.30.0}/.pre-commit-config.yaml +0 -0
  127. {execsql2-2.28.0 → execsql2-2.30.0}/.pre-commit-hooks.yaml +0 -0
  128. {execsql2-2.28.0 → execsql2-2.30.0}/.python-version +0 -0
  129. {execsql2-2.28.0 → execsql2-2.30.0}/.readthedocs.yaml +0 -0
  130. {execsql2-2.28.0 → execsql2-2.30.0}/CONTRIBUTING.md +0 -0
  131. {execsql2-2.28.0 → execsql2-2.30.0}/LICENSE.txt +0 -0
  132. {execsql2-2.28.0 → execsql2-2.30.0}/NOTICE +0 -0
  133. {execsql2-2.28.0 → execsql2-2.30.0}/SECURITY.md +0 -0
  134. {execsql2-2.28.0 → execsql2-2.30.0}/docs/about/contributors.md +0 -0
  135. {execsql2-2.28.0 → execsql2-2.30.0}/docs/about/copyright.md +0 -0
  136. {execsql2-2.28.0 → execsql2-2.30.0}/docs/api/cli.md +0 -0
  137. {execsql2-2.28.0 → execsql2-2.30.0}/docs/api/db.md +0 -0
  138. {execsql2-2.28.0 → execsql2-2.30.0}/docs/api/exporters.md +0 -0
  139. {execsql2-2.28.0 → execsql2-2.30.0}/docs/api/importers.md +0 -0
  140. {execsql2-2.28.0 → execsql2-2.30.0}/docs/api/metacommands.md +0 -0
  141. {execsql2-2.28.0 → execsql2-2.30.0}/docs/dev/adding_db_adapters.md +0 -0
  142. {execsql2-2.28.0 → execsql2-2.30.0}/docs/dev/adding_exporters.md +0 -0
  143. {execsql2-2.28.0 → execsql2-2.30.0}/docs/dev/adding_importers.md +0 -0
  144. {execsql2-2.28.0 → execsql2-2.30.0}/docs/dev/adding_metacommands.md +0 -0
  145. {execsql2-2.28.0 → execsql2-2.30.0}/docs/dev/releasing.md +0 -0
  146. {execsql2-2.28.0 → execsql2-2.30.0}/docs/getting-started/installation.md +0 -0
  147. {execsql2-2.28.0 → execsql2-2.30.0}/docs/getting-started/requirements.md +0 -0
  148. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/debugging.md +0 -0
  149. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/documentation.md +0 -0
  150. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/encoding.md +0 -0
  151. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/logging.md +0 -0
  152. {execsql2-2.28.0 → execsql2-2.30.0}/docs/guides/using_scripts.md +0 -0
  153. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/Compare_planets.png +0 -0
  154. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/actions.png +0 -0
  155. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/actions2.png +0 -0
  156. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/checkboxes.png +0 -0
  157. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/connect.png +0 -0
  158. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/data_error1_screenshot.jpg +0 -0
  159. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/execsql_logo_01.png +0 -0
  160. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/fatals.png +0 -0
  161. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/pause_terminal_sm.png +0 -0
  162. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/set_build_commands.jpg +0 -0
  163. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/unit_conversions_029.png +0 -0
  164. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/unmatched.png +0 -0
  165. {execsql2-2.28.0 → execsql2-2.30.0}/docs/images/vim_execsql_highlight.png +0 -0
  166. {execsql2-2.28.0 → execsql2-2.30.0}/docs/index.md +0 -0
  167. {execsql2-2.28.0 → execsql2-2.30.0}/extras/plugin-template/README.md +0 -0
  168. {execsql2-2.28.0 → execsql2-2.30.0}/extras/plugin-template/pyproject.toml +0 -0
  169. {execsql2-2.28.0 → execsql2-2.30.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
  170. {execsql2-2.28.0 → execsql2-2.30.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
  171. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/.vscodeignore +0 -0
  172. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/LICENSE.txt +0 -0
  173. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/icon.png +0 -0
  174. {execsql2-2.28.0 → execsql2-2.30.0}/extras/vscode-execsql/package-lock.json +0 -0
  175. {execsql2-2.28.0 → execsql2-2.30.0}/scripts/benchmarks/README.md +0 -0
  176. {execsql2-2.28.0 → execsql2-2.30.0}/scripts/benchmarks/results/.gitignore +0 -0
  177. {execsql2-2.28.0 → execsql2-2.30.0}/scripts/benchmarks/run_benchmarks.py +0 -0
  178. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/__init__.py +0 -0
  179. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/__init__.py +0 -0
  180. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/config.py +0 -0
  181. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/format.py +0 -0
  182. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/init.py +0 -0
  183. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/listing.py +0 -0
  184. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/commands/run.py +0 -0
  185. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/dispatch.py +0 -0
  186. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/help.py +0 -0
  187. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/cli/options.py +0 -0
  188. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/config_validation.py +0 -0
  189. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/data/__init__.py +0 -0
  190. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/data/execsql.conf.template +0 -0
  191. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/data/script_header.sql +0 -0
  192. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/__init__.py +0 -0
  193. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/access.py +0 -0
  194. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/dsn.py +0 -0
  195. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/factory.py +0 -0
  196. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/firebird.py +0 -0
  197. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/oracle.py +0 -0
  198. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/sqlite.py +0 -0
  199. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/sqlserver.py +0 -0
  200. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/db/tiers.py +0 -0
  201. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/debug/__init__.py +0 -0
  202. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/debug/repl.py +0 -0
  203. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exceptions.py +0 -0
  204. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/__init__.py +0 -0
  205. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/base.py +0 -0
  206. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/delimited.py +0 -0
  207. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/duckdb.py +0 -0
  208. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/feather.py +0 -0
  209. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/html.py +0 -0
  210. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/json.py +0 -0
  211. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/latex.py +0 -0
  212. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/markdown.py +0 -0
  213. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/ods.py +0 -0
  214. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/parquet.py +0 -0
  215. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/pretty.py +0 -0
  216. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/protocol.py +0 -0
  217. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/raw.py +0 -0
  218. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/sqlite.py +0 -0
  219. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/templates.py +0 -0
  220. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/values.py +0 -0
  221. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/xlsx.py +0 -0
  222. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/xml.py +0 -0
  223. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/yaml.py +0 -0
  224. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/exporters/zip.py +0 -0
  225. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/format.py +0 -0
  226. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/gui/base.py +0 -0
  227. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/gui/console.py +0 -0
  228. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/gui/desktop.py +0 -0
  229. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/gui/tui.py +0 -0
  230. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/__init__.py +0 -0
  231. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/base.py +0 -0
  232. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/csv.py +0 -0
  233. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/feather.py +0 -0
  234. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/json.py +0 -0
  235. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/ods.py +0 -0
  236. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/importers/xls.py +0 -0
  237. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/interactive.py +0 -0
  238. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/__init__.py +0 -0
  239. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/folding.py +0 -0
  240. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/formatting.py +0 -0
  241. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/lsp/rename.py +0 -0
  242. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/conditions.py +0 -0
  243. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/connect.py +0 -0
  244. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/control.py +0 -0
  245. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/data.py +0 -0
  246. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/debug.py +0 -0
  247. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/dispatch.py +0 -0
  248. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/effects.py +0 -0
  249. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/io.py +0 -0
  250. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/io_export.py +0 -0
  251. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/io_import.py +0 -0
  252. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/io_write.py +0 -0
  253. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/prompt.py +0 -0
  254. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/reference.py +0 -0
  255. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/script_ext.py +0 -0
  256. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/system.py +0 -0
  257. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/metacommands/upsert.py +0 -0
  258. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/models.py +0 -0
  259. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/parser.py +0 -0
  260. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/plugins.py +0 -0
  261. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/py.typed +0 -0
  262. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/__init__.py +0 -0
  263. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/ast.py +0 -0
  264. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/control.py +0 -0
  265. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/engine.py +0 -0
  266. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/script/parser.py +0 -0
  267. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/__init__.py +0 -0
  268. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/auth.py +0 -0
  269. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/color.py +0 -0
  270. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/crypto.py +0 -0
  271. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/datetime.py +0 -0
  272. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/gui.py +0 -0
  273. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/mail.py +0 -0
  274. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/numeric.py +0 -0
  275. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/strings.py +0 -0
  276. {execsql2-2.28.0 → execsql2-2.30.0}/src/execsql/utils/timer.py +0 -0
  277. {execsql2-2.28.0 → execsql2-2.30.0}/templates/README.md +0 -0
  278. {execsql2-2.28.0 → execsql2-2.30.0}/templates/config_settings.sqlite +0 -0
  279. {execsql2-2.28.0 → execsql2-2.30.0}/templates/example_config_prompt.sql +0 -0
  280. {execsql2-2.28.0 → execsql2-2.30.0}/templates/make_config_db.sql +0 -0
  281. {execsql2-2.28.0 → execsql2-2.30.0}/templates/md_compare.sql +0 -0
  282. {execsql2-2.28.0 → execsql2-2.30.0}/templates/md_glossary.sql +0 -0
  283. {execsql2-2.28.0 → execsql2-2.30.0}/templates/md_upsert.sql +0 -0
  284. {execsql2-2.28.0 → execsql2-2.30.0}/templates/pg_compare.sql +0 -0
  285. {execsql2-2.28.0 → execsql2-2.30.0}/templates/pg_glossary.sql +0 -0
  286. {execsql2-2.28.0 → execsql2-2.30.0}/templates/pg_upsert.sql +0 -0
  287. {execsql2-2.28.0 → execsql2-2.30.0}/templates/script_template.sql +0 -0
  288. {execsql2-2.28.0 → execsql2-2.30.0}/templates/ss_compare.sql +0 -0
  289. {execsql2-2.28.0 → execsql2-2.30.0}/templates/ss_glossary.sql +0 -0
  290. {execsql2-2.28.0 → execsql2-2.30.0}/templates/ss_upsert.sql +0 -0
  291. {execsql2-2.28.0 → execsql2-2.30.0}/tests/__init__.py +0 -0
  292. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/__init__.py +0 -0
  293. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_config_validate.py +0 -0
  294. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_error_location.py +0 -0
  295. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_init.py +0 -0
  296. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_lint_fixtures.py +0 -0
  297. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_log_exit_record.py +0 -0
  298. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_ping.py +0 -0
  299. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_profile.py +0 -0
  300. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_sigterm.py +0 -0
  301. {execsql2-2.28.0 → execsql2-2.30.0}/tests/cli/test_undeclared_imports.py +0 -0
  302. {execsql2-2.28.0 → execsql2-2.30.0}/tests/conftest.py +0 -0
  303. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/formatter_shapes/comment_anchoring.sql +0 -0
  304. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/formatter_shapes/escapes_and_literals.sql +0 -0
  305. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/formatter_shapes/substitution_and_metacommands.sql +0 -0
  306. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/formatter_shapes/unparsable_sql.sql +0 -0
  307. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/lint/conditions_and_blocks.sql +0 -0
  308. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/lint/does_not_parse.sql +0 -0
  309. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/lint/every_metacommand.sql +0 -0
  310. {execsql2-2.28.0 → execsql2-2.30.0}/tests/data/lint/invalid.sql +0 -0
  311. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/__init__.py +0 -0
  312. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_access_windows.py +0 -0
  313. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_base.py +0 -0
  314. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_copy_new.py +0 -0
  315. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_db_adapters_mocked.py +0 -0
  316. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_db_adapters_mocked_extra.py +0 -0
  317. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_dsn.py +0 -0
  318. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_factory.py +0 -0
  319. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_factory_postgres_encoding.py +0 -0
  320. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_import_atomic.py +0 -0
  321. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_import_replacement_quoted.py +0 -0
  322. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_import_short_rows.py +0 -0
  323. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_import_timezones.py +0 -0
  324. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_mysql_case_folding.py +0 -0
  325. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_mysql_inprocess.py +0 -0
  326. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_new_table_checks.py +0 -0
  327. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_oracle_driver.py +0 -0
  328. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_postgres.py +0 -0
  329. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_roundtrip_adapters.py +0 -0
  330. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_sqlite.py +0 -0
  331. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_sqlite_extra.py +0 -0
  332. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_sqlserver_inprocess.py +0 -0
  333. {execsql2-2.28.0 → execsql2-2.30.0}/tests/db/test_tiers.py +0 -0
  334. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/__init__.py +0 -0
  335. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_base.py +0 -0
  336. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_db.py +0 -0
  337. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_delimited.py +0 -0
  338. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_duckdb_exporter.py +0 -0
  339. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_feather.py +0 -0
  340. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_html_extended.py +0 -0
  341. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_html_latex.py +0 -0
  342. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_json.py +0 -0
  343. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_json_extended.py +0 -0
  344. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_latex_extended.py +0 -0
  345. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_markdown.py +0 -0
  346. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_ods.py +0 -0
  347. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_parquet.py +0 -0
  348. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_pretty_extended.py +0 -0
  349. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_raw_extended.py +0 -0
  350. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_columnar.py +0 -0
  351. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_dbfile.py +0 -0
  352. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_delimited.py +0 -0
  353. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_json.py +0 -0
  354. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_latex_xml.py +0 -0
  355. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_markup.py +0 -0
  356. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_ods.py +0 -0
  357. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_pretty_raw.py +0 -0
  358. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_template.py +0 -0
  359. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_roundtrip_xlsx.py +0 -0
  360. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_sqlite_exporter.py +0 -0
  361. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_templates.py +0 -0
  362. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_templates_extended.py +0 -0
  363. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_values_extended.py +0 -0
  364. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_xls_xlsx.py +0 -0
  365. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_xml.py +0 -0
  366. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_yaml.py +0 -0
  367. {execsql2-2.28.0 → execsql2-2.30.0}/tests/exporters/test_zip.py +0 -0
  368. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/__init__.py +0 -0
  369. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_backends_extended.py +0 -0
  370. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_compare_stats.py +0 -0
  371. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_compute_row_diffs.py +0 -0
  372. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_desktop_dialogs.py +0 -0
  373. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_format_table.py +0 -0
  374. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_tui_pilot.py +0 -0
  375. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_tui_pilot_complex.py +0 -0
  376. {execsql2-2.28.0 → execsql2-2.30.0}/tests/gui/test_utils_gui_extended.py +0 -0
  377. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/__init__.py +0 -0
  378. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/conftest.py +0 -0
  379. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_base_extended.py +0 -0
  380. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_csv_blank_lines.py +0 -0
  381. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_csv_edge_cases.py +0 -0
  382. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_csv_importer.py +0 -0
  383. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_feather_importer.py +0 -0
  384. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_json_importer.py +0 -0
  385. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_ods_importer.py +0 -0
  386. {execsql2-2.28.0 → execsql2-2.30.0}/tests/importers/test_roundtrip_csv.py +0 -0
  387. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/__init__.py +0 -0
  388. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/conftest.py +0 -0
  389. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/test_dsn.py +0 -0
  390. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/test_duckdb.py +0 -0
  391. {execsql2-2.28.0 → execsql2-2.30.0}/tests/integration/test_sqlite.py +0 -0
  392. {execsql2-2.28.0 → execsql2-2.30.0}/tests/live_db.py +0 -0
  393. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/__init__.py +0 -0
  394. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_code_actions.py +0 -0
  395. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_completion.py +0 -0
  396. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_diagnostics.py +0 -0
  397. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_folding.py +0 -0
  398. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_formatting.py +0 -0
  399. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_hover.py +0 -0
  400. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_lsp_command.py +0 -0
  401. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_navigation.py +0 -0
  402. {execsql2-2.28.0 → execsql2-2.30.0}/tests/lsp/test_rename.py +0 -0
  403. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/__init__.py +0 -0
  404. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_assert.py +0 -0
  405. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_breakpoint.py +0 -0
  406. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_conditions_extra.py +0 -0
  407. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_connect.py +0 -0
  408. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_io_export.py +0 -0
  409. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_io_import.py +0 -0
  410. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_connect.py +0 -0
  411. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_data.py +0 -0
  412. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  413. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_io.py +0 -0
  414. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  415. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_system.py +0 -0
  416. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  417. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_pg_upsert.py +0 -0
  418. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_prompt.py +0 -0
  419. {execsql2-2.28.0 → execsql2-2.30.0}/tests/metacommands/test_show_scripts.py +0 -0
  420. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/__init__.py +0 -0
  421. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
  422. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
  423. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
  424. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
  425. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/control_flow.sql +0 -0
  426. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
  427. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/error_handling.sql +0 -0
  428. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
  429. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/import_options.sql +0 -0
  430. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
  431. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/includes.sql +0 -0
  432. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/io_formats.sql +0 -0
  433. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
  434. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/multi_db.sql +0 -0
  435. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
  436. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
  437. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/predicates.sql +0 -0
  438. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/scripts.sql +0 -0
  439. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/smoke.sql +0 -0
  440. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
  441. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
  442. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
  443. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/template_export.sql +0 -0
  444. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/timer.sql +0 -0
  445. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/transactions.sql +0 -0
  446. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
  447. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
  448. {execsql2-2.28.0 → execsql2-2.30.0}/tests/scripts/test_sql_scripts.py +0 -0
  449. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/__init__.py +0 -0
  450. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_expansion_bomb.py +0 -0
  451. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_import_regex_hardening.py +0 -0
  452. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_odbc_injection.py +0 -0
  453. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_path_containment.py +0 -0
  454. {execsql2-2.28.0 → execsql2-2.30.0}/tests/security/test_zip_bomb.py +0 -0
  455. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_api.py +0 -0
  456. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_api_errors.py +0 -0
  457. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_api_extended.py +0 -0
  458. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_api_filewriter.py +0 -0
  459. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_api_subprocess.py +0 -0
  460. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_ast.py +0 -0
  461. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_ast_parser.py +0 -0
  462. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_bundled_templates.py +0 -0
  463. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_config.py +0 -0
  464. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_config_extended.py +0 -0
  465. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_date_inference.py +0 -0
  466. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_debug_repl.py +0 -0
  467. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_engine.py +0 -0
  468. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_error_messages.py +0 -0
  469. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_exceptions.py +0 -0
  470. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_executor.py +0 -0
  471. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_format.py +0 -0
  472. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_interactive.py +0 -0
  473. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_mail.py +0 -0
  474. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_models.py +0 -0
  475. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_package.py +0 -0
  476. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_parser.py +0 -0
  477. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_parser_params.py +0 -0
  478. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_plugins.py +0 -0
  479. {execsql2-2.28.0 → execsql2-2.30.0}/tests/test_script.py +0 -0
  480. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/__init__.py +0 -0
  481. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_auth.py +0 -0
  482. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_auth_extra.py +0 -0
  483. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_crypto.py +0 -0
  484. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_datetime.py +0 -0
  485. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_encodedfile_context.py +0 -0
  486. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_keyring_rules.py +0 -0
  487. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_mail_tls.py +0 -0
  488. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_numeric.py +0 -0
  489. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_strings.py +0 -0
  490. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_timer.py +0 -0
  491. {execsql2-2.28.0 → execsql2-2.30.0}/tests/utils/test_timer_extra.py +0 -0
  492. {execsql2-2.28.0 → execsql2-2.30.0}/zensical.toml +0 -0
@@ -0,0 +1,57 @@
1
+ name: Bug report
2
+ description: Something execsql does wrong, from the command line, the library, or an editor.
3
+ labels: [bug]
4
+ body:
5
+ - type: textarea
6
+ id: what
7
+ attributes:
8
+ label: What happened
9
+ description: What you ran, what you expected, and what happened instead.
10
+ placeholder: |
11
+ `execsql run load.sql prod.db -t l` stopped at line 12 with ...
12
+ I expected ...
13
+ validations:
14
+ required: true
15
+ - type: textarea
16
+ id: script
17
+ attributes:
18
+ label: A script that shows it
19
+ description: The smallest script that reproduces the problem, if you can share one. Leave out data and credentials.
20
+ render: sql
21
+ - type: textarea
22
+ id: output
23
+ attributes:
24
+ label: Error output
25
+ description: The full message or traceback, and the end of `execsql.log` if the run wrote one.
26
+ render: text
27
+ - type: input
28
+ id: version
29
+ attributes:
30
+ label: execsql version
31
+ description: The output of `execsql --version`.
32
+ placeholder: execsql 2.30.0
33
+ validations:
34
+ required: true
35
+ - type: input
36
+ id: install
37
+ attributes:
38
+ label: How execsql is installed
39
+ description: The command, with its extras.
40
+ placeholder: uv tool install "execsql2[postgres,lsp]"
41
+ validations:
42
+ required: true
43
+ - type: input
44
+ id: environment
45
+ attributes:
46
+ label: Operating system, Python and database
47
+ placeholder: macOS 15, Python 3.12, PostgreSQL 16
48
+ validations:
49
+ required: true
50
+ - type: textarea
51
+ id: editor
52
+ attributes:
53
+ label: Editor and language server (only for a problem in an editor)
54
+ description: |
55
+ The editor and the execsql extension's version, the output of `execsql lsp --check`, and the server's log
56
+ at `info` (VS Code: View → Output → execsql). See https://execsql2.readthedocs.io/en/latest/guides/editors/#troubleshooting
57
+ render: text
@@ -0,0 +1,28 @@
1
+ name: Feature request
2
+ description: A new feature, metacommand or option, or a change to how something works.
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: The problem
9
+ description: What you are trying to do, and what gets in the way today.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: What you would like
16
+ description: For a new metacommand or CLI option, the syntax you would expect.
17
+ placeholder: "-- !x! NEW_METACOMMAND ..."
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: alternatives
22
+ attributes:
23
+ label: Alternatives and workarounds
24
+ description: What you tried instead, if anything.
25
+ - type: textarea
26
+ id: context
27
+ attributes:
28
+ label: Anything else
@@ -51,25 +51,6 @@ jobs:
51
51
  - run: uv sync --locked --all-extras
52
52
  - run: uv run --no-sync mypy src/execsql/
53
53
 
54
- formatter-corpus:
55
- name: formatter-corpus
56
- runs-on: ubuntu-latest
57
- permissions:
58
- contents: read
59
- steps:
60
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
61
- - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
62
- with:
63
- python-version: "3.13"
64
- cache: "pip"
65
- - run: python -m pip install --upgrade pip
66
- - run: python -m pip install ".[dev]"
67
- # Every formatter bug to date came from real SQL containing a
68
- # construct the project's own scripts lack. This gate runs those
69
- # checks under their own name so a corpus failure is legible
70
- # without reading a 6,000-test log.
71
- - run: pytest tests/test_format.py -k corpus --no-cov -q
72
-
73
54
  typer-floor:
74
55
  name: typer-floor
75
56
  runs-on: ubuntu-latest
@@ -155,7 +136,8 @@ jobs:
155
136
  - run: python -m pip install --upgrade pip
156
137
  - run: python -m pip install ".[dev]"
157
138
  - run: cp CHANGELOG.md docs/about/change_log.md
158
- - run: zensical build
139
+ # --strict: a warning, such as a link to an #anchor that does not exist, fails the job.
140
+ - run: zensical build --strict
159
141
 
160
142
  tests:
161
143
  strategy:
@@ -369,7 +351,7 @@ jobs:
369
351
  build:
370
352
  name: Build distribution 📦
371
353
  runs-on: ubuntu-latest
372
- needs: [ lint, typecheck, pre-commit, formatter-corpus, typer-floor, docs, tests, integration-tests, vscode-extension ]
354
+ needs: [ lint, typecheck, pre-commit, typer-floor, docs, tests, integration-tests, vscode-extension ]
373
355
  # Deliberately NOT gated on tags: packaging breaks must surface on a PR,
374
356
  # not at release time. Only publish/generate-release below are tag-gated.
375
357
  permissions:
@@ -13,6 +13,51 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.30.0] - 2026-10-14
17
+
18
+ ### Added
19
+
20
+ - **`execsql lsp --log-level` and `--log-file`** (or `EXECSQL_LSP_LOG_LEVEL`): at `info` the server logs its version, the Python running it, whether the formatter is installed, the workspace folders and the config files each folder read; `debug` adds every protocol message. See [Troubleshooting](https://execsql2.readthedocs.io/en/latest/guides/editors/#troubleshooting) for what to include in a bug report.
21
+ - **VS Code extension:** the **execsql** Output channel shows the server's log (setting `execsql.server.logLevel`, default `info`) and why the server did not start; `execsql.trace.server` traces the protocol there.
22
+
23
+ ### Fixed
24
+
25
+ - **`execsql lint` checks every file** when one has an `INCLUDE` path the operating system rejects (such as a name too long for it): that file gets an I001 finding instead of the whole run stopping with "Uncaught exception", and a script lint cannot check at all is reported as one P001 error.
26
+ - **The language server shows "execsql could not check this script"** when checking a script fails unexpectedly, instead of keeping its old findings; hover, completion and navigation keep working for a script with such an `INCLUDE`.
27
+ - **The language server answers quickly in a script with many or large `INCLUDE`s:** completion, hover, go-to-definition and the other features no longer reread and reparse every included file on each request (50 included 3,000-line files: about 0.55 s per keystroke before, under 20 ms now). A changed included file is read again.
28
+ - **The language server lints less after a save:** saving a script, or a change to a `.sql` file on disk, lints again only the open scripts that `INCLUDE` that file, once, instead of every open script two or three times.
29
+ - **`execsql --help` shows each command's whole summary**, wrapped to the terminal, instead of cutting it off with `...` on a narrow one.
30
+ - **DuckDB temporary tables last for the whole run:** a table created with `CREATE TEMPORARY TABLE` is visible to the statements and metacommands after it (`INSERT`, `EXPORT`, `COPY`, conditions); before, it was gone by the next statement.
31
+ - **A `--dsn` / `dsn=` value without a scheme** (`//user:password@host/db`) is reported with its password as `***` instead of in full.
32
+ - **`--manifest` masks passwords and tokens:** a password, `SUB_DECRYPT` result or token (a URL's password, `password=...`, an API key) that reaches a file path, connection target or error message is written as `***`. `-a` and `--var` values are still written where a path uses them.
33
+ - **A registered secret no longer reaches `execsql.log` through a `HALT MESSAGE`** (the `exit` record of a halted run), an exported file name (`action` records) or a connection name (`connect` records); these now show `***` like every other log record.
34
+ - **`execsql.run()` returns the caller to their working directory** after a script that ran `CD`, including when the script fails; before, the caller was left in the script's last directory.
35
+ - **Language server settings for a workspace folder added after the first folder was deleted** are read; before, every later settings reload failed and the folder linted with stale rules. An unsaved script's relative `INCLUDE` resolves against the workspace root.
36
+ - **VS Code extension:** after `CONFIG`, only options `CONFIG` accepts are highlighted; config-file-only keys such as `DB_FILE`, `CONSOLE_HEIGHT` or `CSS_FILE` (which `CONFIG` rejects) no longer look valid.
37
+ - **VS Code extension:** turning `execsql.server.enabled` on or off, or changing `execsql.server.path`, takes effect without reloading the window; **execsql: Restart Language Server** works (and says so) when the server is off, and two quick restarts no longer leave a second server running.
38
+
39
+ ### Removed
40
+
41
+ - **Unused helpers:** `execsql.gui.get_backend()` (the GUI picks its backend in `enable_gui()`), `ins_quoted_rx()` (re-exported from `execsql.metacommands`; nothing built a metacommand with it) `execsql.utils.errors.exception_info()` (use `exception_desc()`), and the `execsql.cli.main` re-export (the run command's own module, `execsql.cli.commands.run`, keeps it).
42
+ - **`execsql.utils.fileio`:** `filewriter_closeall()`, `filewriter_shutdown()` and `list_encodings()`, which nothing called; use `filewriter_end()` to stop a writer and `execsql list encodings` for the encodings. `FileWriter.ping()` is gone too.
43
+
44
+ ______________________________________________________________________
45
+
46
+ ## [2.29.0] - 2026-10-13
47
+
48
+ ### Changed
49
+
50
+ - **`!'!var!'!` stores backslashes as written** on every DBMS: a value such as `C:\data\new` was stored as `C:\\data\\new` on SQLite, PostgreSQL, DuckDB, SQL Server, Oracle, Firebird and Access. MySQL/MariaDB (which read `\` as an escape) and PostgreSQL escape strings written as `E!'!var!'!` still get backslashes doubled, so both forms store the same value and the `E` workaround is no longer needed. Scripts that removed the doubled backslashes some other way should stop.
51
+
52
+ ### Fixed
53
+
54
+ - **Oracle IMPORT and COPY with a time column** create it as text (`VARCHAR2(15)`, holding `HH:MM:SS`); `TO NEW` stopped with "Oracle DBMS type has no specification for data type time".
55
+ - **MySQL IMPORT accepts a header with capital letters** (`Id,Name`): it stopped with "columns that are not in table" (leaving the new table behind), and with `IMPORT_COMMON_COLUMNS_ONLY` it loaded those columns as NULL.
56
+ - **PostgreSQL IMPORT honors `SKIP n`** for quoted CSV files, which load through PostgreSQL's COPY: it skipped only the header line, loading the lines before it as data or failing on them.
57
+ - **Spreadsheet IMPORT reads every data row:** an XLSX row whose values are all `0` or FALSE no longer ends the data (it and every row after it were dropped), and an XLS sheet imported with `SKIP n` no longer loses its last *n* rows.
58
+
59
+ ______________________________________________________________________
60
+
16
61
  ## [2.28.0] - 2026-10-13
17
62
 
18
63
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execsql2
3
- Version: 2.28.0
3
+ Version: 2.30.0
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
@@ -83,7 +83,6 @@ Provides-Extra: auth-plaintext
83
83
  Requires-Dist: keyring>=25.0; extra == 'auth-plaintext'
84
84
  Requires-Dist: keyrings-alt>=5.0; extra == 'auth-plaintext'
85
85
  Provides-Extra: dev
86
- Requires-Dist: build>=1.2.2.post1; extra == 'dev'
87
86
  Requires-Dist: bump-my-version>=1.2.7; extra == 'dev'
88
87
  Requires-Dist: defusedxml>=0.7; extra == 'dev'
89
88
  Requires-Dist: jinja2>=3.1; extra == 'dev'
@@ -104,7 +103,6 @@ Requires-Dist: ruff>=0.4; extra == 'dev'
104
103
  Requires-Dist: sqlglot<30.22,>=30.21.0; extra == 'dev'
105
104
  Requires-Dist: tables>=3.10; extra == 'dev'
106
105
  Requires-Dist: tox-uv>=1.13.1; extra == 'dev'
107
- Requires-Dist: twine>=6.1.0; extra == 'dev'
108
106
  Requires-Dist: xlrd>=2.0; extra == 'dev'
109
107
  Requires-Dist: zensical==0.0.67; extra == 'dev'
110
108
  Provides-Extra: duckdb
@@ -466,7 +464,7 @@ execsql format --no-sql --in-place scripts/
466
464
  ```yaml
467
465
  repos:
468
466
  - repo: https://github.com/geocoug/execsql
469
- rev: v2.28.0
467
+ rev: v2.30.0
470
468
  hooks:
471
469
  - id: execsql-format
472
470
  - id: execsql-lint
@@ -321,7 +321,7 @@ execsql format --no-sql --in-place scripts/
321
321
  ```yaml
322
322
  repos:
323
323
  - repo: https://github.com/geocoug/execsql
324
- rev: v2.28.0
324
+ rev: v2.30.0
325
325
  hooks:
326
326
  - id: execsql-format
327
327
  - id: execsql-lint
@@ -28,7 +28,7 @@ ______________________________________________________________________
28
28
  | `--dry-run` | Parse the script and print the full command list without connecting to a database or executing anything. Substitution variables already populated at parse time (env vars, `--assign-arg` values, built-in start-time vars) are expanded in the output; execution-time variables (`$DB_NAME`, `$CURRENT_TIME`, etc.) remain unexpanded. |
29
29
  | `--profile` | Record wall-clock time for each SQL and metacommand statement. After the script completes, print a summary table sorted by elapsed time (descending), showing time, percentage of total, source location, command type, and a command preview. |
30
30
  | `--profile-limit N` | Number of top statements to display in the `--profile` summary (default: 20). Remaining statements are counted and noted in the output footer. |
31
- | `--manifest FILE` | Write a JSON record of the run when it ends, even on failure: script, database, statement counts, files read, written and deleted (paths after substitution), connections, errors. Variable values and passwords are never written. |
31
+ | `--manifest FILE` | Write a JSON record of the run when it ends, even on failure: script, database, statement counts, files read, written and deleted (paths after substitution), connections, errors. Variable values are never listed; passwords, decrypted values and token shapes are masked as `***` wherever they appear. |
32
32
  | `--ping` | Test database connectivity and exit. Connects using the supplied connection parameters, queries for the server version, and prints a one-line success message (exit 0) or the error (exit 1). No script file argument is required. |
33
33
  | `--lint` | Removed. Replaced by `execsql lint`, which takes files and directories and adds rule selection and JSON output. `execsql run --lint` now exits 2 with `run --lint was removed; use execsql lint` and never runs the script. |
34
34
  | `--parse-tree` | Parse the script into an Abstract Syntax Tree and print a visual tree showing block nesting (IF/LOOP/BATCH/SCRIPT), source line ranges, compound conditions (ANDIF/ORIF), and all metacommands. Does not connect to a database or execute anything. Useful for understanding script structure and verifying the parser handles a script correctly. |
@@ -205,7 +205,7 @@ result = run(
205
205
  result.raise_on_error()
206
206
  ```
207
207
 
208
- Accepts `dsn=` or a pre-built `connection=` `Database` object. Each call runs in its own thread-local `RuntimeContext` with its own file-writer thread, so concurrent calls from different threads are safe and a halt in one does not stop another's file output. `halt_on_error=False` collects errors instead of raising.
208
+ Accepts `dsn=` or a pre-built `connection=` `Database` object. Each call runs in its own thread-local `RuntimeContext` with its own file-writer thread, so concurrent calls from different threads are safe and a halt in one does not stop another's file output. `halt_on_error=False` collects errors instead of raising. A script's `CD` lasts until `run()` returns, which then changes back to the caller's directory.
209
209
 
210
210
  ______________________________________________________________________
211
211
 
@@ -267,7 +267,8 @@ All 33 mutable runtime globals in `state.py` have been consolidated into a `Runt
267
267
  - **DuckDB temporal types** — `TIMESTAMPTZ`, `TIMESTAMP`, `DATE`, `TIME` now map to native DuckDB types instead of `TEXT`.
268
268
  - **SQLite `DT_Long` mapping** — `DT_Long` maps to `"hugeint"` in the SQLite type table. SQLite does not have a native `HUGEINT` type; the value receives `TEXT` affinity. In practice this is harmless because SQLite's type affinity system handles large integers transparently, but the mapping name differs from upstream.
269
269
  - **`Database.auto_commits_ddl()` not provided** — 2.18.0 shipped this capability hook on the base `Database` class with `True` overrides on Oracle, MySQL, SQL Server, and MS Access. No call site ever consumed the hook, so 2.19.0 removed it. Its replacement is `Database.implicit_commit_rx`: MySQL and Oracle name the statements their server commits on its own (DDL), and a warning is written when one runs while `AUTOCOMMIT` is `OFF` or a batch is open. SQL Server holds DDL in a transaction like any other statement. (The companion hook `Database.needs_explicit_commit_after_ddl()`, used by the Firebird import path, remains.)
270
- - **Statements are held until commit on every adapter** — while `AUTOCOMMIT` is `OFF` or a batch is open, `Database.before_statement()` makes sure each statement is inside a transaction. Most drivers already opened one; SQLite's (Python's `sqlite3`) opened one only before `INSERT`/`UPDATE`/`DELETE`/`REPLACE`, and DuckDB's never did, so upstream committed SQLite DDL, and every DuckDB statement, at once. SQLite now begins a transaction before other statements (except `VACUUM`, `ATTACH`, `DETACH`, `PRAGMA`); DuckDB begins one itself, tracks it, and runs statements on the connection rather than on cursors while it is open, since a DuckDB cursor is a separate connection with its own transaction.
270
+ - **Statements are held until commit on every adapter** — while `AUTOCOMMIT` is `OFF` or a batch is open, `Database.before_statement()` makes sure each statement is inside a transaction. Most drivers already opened one; SQLite's (Python's `sqlite3`) opened one only before `INSERT`/`UPDATE`/`DELETE`/`REPLACE`, and DuckDB's never did, so upstream committed SQLite DDL, and every DuckDB statement, at once. SQLite now begins a transaction before other statements (except `VACUUM`, `ATTACH`, `DETACH`, `PRAGMA`); DuckDB begins one itself and tracks it.
271
+ - **DuckDB statements share one connection** — a DuckDB cursor is a separate connection with its own transaction and its own temporary tables, and upstream ran each statement on a new cursor, so a `CREATE TEMPORARY TABLE` was gone by the next statement and a held transaction did not cover them. Every statement now runs on the database's one connection. That connection holds one result at a time, so when a statement runs while another query's rows are still being read (`COPY` scans its source while reading it), the unread rows are fetched into memory first; an `EXPORT` still streams.
271
272
  - **DuckDB `$LAST_ROWCOUNT`** — the duckdb driver's `rowcount` is always `-1`; DuckDB answers an `INSERT`, `UPDATE` or `DELETE` with a one-column `Count` result instead. The DuckDB adapter reads that result, so `$LAST_ROWCOUNT` holds the rows changed, as on the other adapters. Upstream set it to `-1`.
272
273
 
273
274
  ### Execution Engine
@@ -279,7 +280,6 @@ The legacy flat command-list engine (`_parse_script_lines` / `runscripts` / `Com
279
280
  - **BREAK outside LOOP is an error** — `BREAK` outside a loop block now raises an error (exit 1) instead of being silently ignored. This catches script bugs that upstream would not report.
280
281
  - **File writer is a thread, one per run** — `WRITE`, `TEE` and the other text-file metacommands hand their output to a background thread owned by the run's `RuntimeContext`, which `execsql.run()` stops before returning. Upstream uses one `multiprocessing` subprocess for the whole program; under the `spawn` start method (macOS, Windows) that re-imported the calling program, so a library caller without an `if __name__ == "__main__":` guard ran its script twice.
281
282
  - **Instance-scoped script registry** — Named SCRIPT blocks are stored on the `RuntimeContext` instance instead of a module-level dict, preventing cross-execution contamination.
282
- - **Script source directory resolution** — `ScriptCmd` resolves `source_dir` at construction time (when the command is parsed) rather than per-statement at execution time. This is functionally equivalent for all normal usage since the script file does not move between parse and execution.
283
283
 
284
284
  ### Error Handling
285
285
 
@@ -322,13 +322,13 @@ These are behavioral changes driven by security or correctness issues in the ups
322
322
 
323
323
  ### Credential and Logging Safety
324
324
 
325
- | Area | Fix |
326
- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
327
- | ODBC password redaction | Connection strings in log output have `Pwd=***` substituted before logging. |
328
- | `enc_password` documentation | Prominent warnings that XOR encryption is obfuscation only — keys are hardcoded in source. |
329
- | Env-var secret denylist | Extended `_SENSITIVE_SUBSTRINGS` to cover mainstream cloud / payment / observability / VCS conventions (`*_KEY`, `APIKEY`, `API_KEY`, `DSN`, `WEBHOOK`). `-a` positional assignment log lines now redact unconditionally (`{***}` instead of the raw value). |
330
- | Secrets registered at source | The `run` record lists a count of `-a` values instead of the values (upstream wrote them in clear). DSN and SMTP passwords, prompted and keyring database passwords, `PROMPT ENTER_SUB ... PASSWORD` and `PROMPT CREDENTIALS` values, and `SUB_DECRYPT` results are registered for redaction, so SQL logged with `LOG_SQL ON`, `SYSTEM_CMD` echoes and error messages show `***`. |
331
- | SMTP certificate verification | `EMAIL` over `use_ssl` or `use_tls` verifies the server certificate (upstream accepted any certificate, so the SMTP password could go to an impostor). New `[email]` settings: `ca_file` to trust an internal CA, `verify_certificate = No` to turn the check off. |
325
+ | Area | Fix |
326
+ | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
327
+ | ODBC password redaction | Connection strings in log output have `Pwd=***` substituted before logging. |
328
+ | `enc_password` documentation | Prominent warnings that XOR encryption is obfuscation only — keys are hardcoded in source. |
329
+ | Env-var secret denylist | Extended `_SENSITIVE_SUBSTRINGS` to cover mainstream cloud / payment / observability / VCS conventions (`*_KEY`, `APIKEY`, `API_KEY`, `DSN`, `WEBHOOK`). `-a` positional assignment log lines now redact unconditionally (`{***}` instead of the raw value). |
330
+ | Secrets registered at source | The `run` record lists a count of `-a` values instead of the values (upstream wrote them in clear). DSN and SMTP passwords, prompted and keyring database passwords, `PROMPT ENTER_SUB ... PASSWORD` and `PROMPT CREDENTIALS` values, and `SUB_DECRYPT` results are registered for redaction, so SQL logged with `LOG_SQL ON`, `SYSTEM_CMD` echoes, error messages, the `exit` record (including a `HALT MESSAGE`), `action` export records and `connect` records show `***`. |
331
+ | SMTP certificate verification | `EMAIL` over `use_ssl` or `use_tls` verifies the server certificate (upstream accepted any certificate, so the SMTP password could go to an impostor). New `[email]` settings: `ca_file` to trust an internal CA, `verify_certificate = No` to turn the check off. |
332
332
 
333
333
  ### Bug Fixes
334
334
 
@@ -362,6 +362,11 @@ These are behavioral changes driven by security or correctness issues in the ups
362
362
  | Partial dates inferred as timestamps | `dateutil` fills fields missing from a value with today's date, so `1-2`, `5/6`, `March 5`, `Jan` and `Monday` were inferred as timestamps and, on DuckDB, SQL Server, MySQL, Oracle, Firebird, Access and every ODS/XLSX/JSON/Parquet/Feather import, stored as dates in the current year. Inference now requires a four-digit year (or a month-first `m/d/yy` or `m-d-yy`) and a month and day, which restores upstream's behaviour for these values. `m-d-yy` is accepted in addition to upstream's `m/d/yy`. |
363
363
  | Offset values stored as plain timestamps | With `dateutil`, a value with a UTC offset (`2024-01-01 10:00+05:00`) also counted as a plain timestamp, so a column mixing offset and plain values was created as `timestamp` and the driver converted the offset values with the client's time zone: the stored value depended on the machine that ran the import. A value with an offset is again not a plain timestamp, as with upstream's offset-free format list, so a mixed column is text and keeps each value as written. A column where every value has an offset is still `timestamptz`. |
364
364
  | Binary columns in `COPY` | Every value a source cursor returned as bytes was decoded to text (undecodable bytes as `\xNN` escapes) before `COPY` wrote it, and `TO NEW` typed the column as text. Upstream did the same for SQLite BLOBs; with psycopg2 a PostgreSQL `bytea` arrived as a `memoryview` and escaped the decode, so for PostgreSQL this was an execsql2 regression. `COPY` now moves bytes unchanged and creates binary columns. Exports still write the decoded text. |
365
+ | Backslashes in `!'!var!'!` | Upstream doubled backslashes on Windows only, to protect `re.sub`'s replacement template; execsql2 2.x doubled them on every platform as SQL escaping, which corrupted values on every DBMS but MySQL. A backslash is now doubled only where it is an escape: on a MySQL/MariaDB connection, and in a PostgreSQL escape string written as `E!'!var!'!`. Apostrophes are doubled everywhere. |
366
+ | Time columns on Oracle | `IMPORT` / `COPY ... TO NEW` of a column of times stopped with "Oracle DBMS type has no specification for data type time" (a `DT_Time_Oracle` type was registered but inference never produced it). A time column is now created as `VARCHAR2(15)` holding the ISO time text. |
367
+ | Capitalized headers on MySQL | MySQL compared the file's headers with the table's lower-cased column names, so `Id` did not match `id`: IMPORT stopped (after `TO NEW` had created the table), and with `IMPORT_COMMON_COLUMNS_ONLY` such columns were skipped. The comparison now ignores case, as on PostgreSQL and the other DBMSs. |
368
+ | `SKIP` on PostgreSQL's COPY path | The PostgreSQL fast path (a quoted CSV file) skipped one line, the header, whatever `SKIP` said, so the skipped lines were loaded as data. It now starts after the `SKIP` lines like every other reader. |
369
+ | Spreadsheet rows on import | XLSX reading stopped at the first row with no truthy value, so a row of `0` / FALSE values ended the data; XLS reading with `SKIP n` stopped *n* rows before the end of the sheet. Both now read to the first row of empty cells. |
365
370
  | Short data lines on import | A line with fewer values than the header was padded with NULLs only on PostgreSQL and MySQL. SQLite stopped with "Too few values", DuckDB and the other DBMSs with a driver parameter-count error. With `TO NEW` / `REPLACEMENT`, type inference skipped the missing values, so the column could be created `NOT NULL` and then reject the padded line, on PostgreSQL too. Missing trailing values are now NULL on every DBMS, and inference counts them as NULL. |
366
371
  | `COPY` re-typed text values | Upstream described the destination table by re-reading the source's text values as if they came from a file, so a text column of `1`/`0` became `boolean` and one of dates became `timestamp` with `NEW` / `REPLACEMENT`, and values copied into an existing text column were converted on the way in (`1` stored as `true`). Text from the source database now stays text. Upstream also ran the source query twice, once to describe the table and once to copy it; execsql2 runs it once and holds the rows in a temporary file in between. |
367
372
  | `REPLACEMENT` of a quoted or schema-qualified table | The SQLite, SQL Server, Oracle, Firebird and Access adapters quoted the table name a second time before dropping it, so `IMPORT` / `COPY ... TO REPLACEMENT "my tbl"` dropped a table named `"my tbl"` (quotes included) that did not exist and then failed to create the table, and on SQL Server, Oracle and Firebird `REPLACEMENT staging.t` tried to drop one table named `staging.t`. The name is now quoted once. |
@@ -80,7 +80,7 @@ Each run has its own [file writer](#file-output), so two runs writing files at t
80
80
 
81
81
  Three things are still shared by every thread in the process, so keep them apart yourself:
82
82
 
83
- - **The working directory.** `CD` calls `os.chdir()`, which moves every thread. Use absolute paths in scripts that run concurrently, or don't use `CD` in them.
83
+ - **The working directory.** `CD` calls `os.chdir()`, which moves every thread while the script runs. Use absolute paths in scripts that run concurrently, or don't use `CD` in them. When a script that ran `CD` finishes, `run()` changes back to the directory it was in before that `CD`, so the caller ends where it started; a run without `CD` leaves the directory as it finds it.
84
84
  - **The same output file.** Two runs writing one file at the same time can interleave their lines. Give each run its own output paths.
85
85
  - **Interactive prompts.** `PROMPT` and the other GUI metacommands are not meant to be driven from several threads at once.
86
86
 
@@ -56,6 +56,8 @@ flowchart LR
56
56
  UTILS["utils/<br/>auth, crypto,<br/>fileio, mail,<br/>regex, strings"]
57
57
  PARSER["parser.py<br/>CondParser,<br/>NumericParser"]
58
58
  DEBUG["debug/<br/>REPL debugger"]
59
+ LSP["lsp/<br/>execsql lsp,<br/>editor features"]
60
+ REF["metacommands/<br/>reference.py<br/>syntax registry"]
59
61
 
60
62
  CLI --> CONFIG
61
63
  CLI --> STATE
@@ -75,35 +77,66 @@ flowchart LR
75
77
  EXPORT --> STATE
76
78
  GUI --> STATE
77
79
  SCRIPT --> PARSER
80
+ CLI --> LSP
81
+ LSP --> SCRIPT
82
+ LSP --> REF
83
+ LSP --> CLI
78
84
  ```
79
85
 
80
86
  ### Package summary
81
87
 
82
- | Package | Purpose |
83
- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
84
- | `cli/` | Typer app and help rendering, one module per command under `cli/commands/`, shared options, `_run()` orchestration, DSN URL parsing |
85
- | `api.py` | Public `execsql.run()` Python entry point for notebooks, pipelines, and library use |
86
- | `config.py` | `ConfigData` (INI merging), `StatObj` (runtime flags), `WriteHooks` (stdout/stderr redirection) |
87
- | `state.py` | Thread-local runtime store — all shared mutable state lives here, isolated per-thread |
88
- | `script/` | AST node types, parser, `MetaCommandList`, `SubVarSet`, `BatchLevels`, `ScriptExecSpec`, `set_system_vars()` |
89
- | `metacommands/` | `build_dispatch_table()`, all `x_*` handlers, `build_conditional_table()`, all `xf_*` predicates |
90
- | `db/` | `Database` ABC, `DatabasePool`, 9 adapter modules (postgres, sqlite, duckdb, mysql, sqlserver, oracle, firebird, access, dsn) |
91
- | `exporters/` | `ExportRecord`, `ExportMetadata`, `WriteSpec`, 20+ format writers (CSV, JSON, XML, HTML, etc.) |
92
- | `importers/` | `CsvFile`, `OdsFile`, `XlsFile`, `FeatherFile` — data import backends |
93
- | `gui/` | `GuiBackend` ABC, `TkinterBackend`, `TextualBackend`, `ConsoleBackend` |
94
- | `utils/` | Shared utilities: file I/O, encryption, mail, regex helpers, string manipulation, timers |
95
- | `parser.py` | Recursive-descent parsers for conditional (`IF`) and arithmetic (`SET`) expressions |
96
- | `types.py` | `DataType` subclasses and `DbType` per-DBMS type dialect mappings |
97
- | `models.py` | `Column`, `DataTable`, `JsonDatatype` |
98
- | `format.py` | `execsql format` formatter — opinionated formatter for execsql scripts |
99
- | `exceptions.py` | `ExecSqlError` base, `ErrInfo`, `ConfigError`, `DataTypeError`, `DbTypeError`, etc. |
100
- | `plugins.py` | Entry-point plugin discovery for metacommands, exporters, and importers |
101
- | `debug/` | Interactive REPL debugger for stepping through script execution |
102
- | `interactive.py` | Engine behind the debug REPL: input typed at a BREAKPOINT runs as the next lines of the paused script via `executor.execute_input`; errors return to the prompt |
103
- | `data/` | Bundled package data (`execsql.conf.template` powers `execsql config --init`) |
88
+ | Package | Purpose |
89
+ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
90
+ | `cli/` | Typer app and help rendering, one module per command under `cli/commands/`, shared options, `_run()` orchestration, DSN URL parsing |
91
+ | `api.py` | Public `execsql.run()` Python entry point for notebooks, pipelines, and library use |
92
+ | `config.py` | `ConfigData` (INI merging), `StatObj` (runtime flags), `WriteHooks` (stdout/stderr redirection) |
93
+ | `state.py` | Thread-local runtime store — all shared mutable state lives here, isolated per-thread |
94
+ | `script/` | AST node types, parser, `MetaCommandList`, `SubVarSet`, `BatchLevels`, `ScriptExecSpec`, `set_system_vars()` |
95
+ | `metacommands/` | `build_dispatch_table()`, all `x_*` handlers, `build_conditional_table()`, all `xf_*` predicates |
96
+ | `db/` | `Database` ABC, `DatabasePool`, 9 adapter modules (postgres, sqlite, duckdb, mysql, sqlserver, oracle, firebird, access, dsn) |
97
+ | `exporters/` | `ExportRecord`, `ExportMetadata`, `WriteSpec`, 20+ format writers (CSV, JSON, XML, HTML, etc.) |
98
+ | `importers/` | `CsvFile`, `OdsFile`, `XlsFile`, `FeatherFile` — data import backends |
99
+ | `gui/` | `GuiBackend` ABC, `TkinterBackend`, `TextualBackend`, `ConsoleBackend` |
100
+ | `utils/` | Shared utilities: file I/O, encryption, mail, regex helpers, string manipulation, timers |
101
+ | `parser.py` | Recursive-descent parsers for conditional (`IF`) and arithmetic (`SET`) expressions |
102
+ | `types.py` | `DataType` subclasses and `DbType` per-DBMS type dialect mappings |
103
+ | `models.py` | `Column`, `DataTable`, `JsonDatatype` |
104
+ | `format.py` | `execsql format` formatter — opinionated formatter for execsql scripts |
105
+ | `exceptions.py` | `ExecSqlError` base, `ErrInfo`, `ConfigError`, `DataTypeError`, `DbTypeError`, etc. |
106
+ | `plugins.py` | Entry-point plugin discovery for metacommands, exporters, and importers |
107
+ | `debug/` | Interactive REPL debugger for stepping through script execution |
108
+ | `interactive.py` | Engine behind the debug REPL: input typed at a BREAKPOINT runs as the next lines of the paused script via `executor.execute_input`; errors return to the prompt |
109
+ | `lsp/` | `execsql lsp`, the language server editors start (pygls 2): diagnostics, completion, hover, navigation, rename, folding, quick fixes, Format Document. See [Language Server](#language-server) |
110
+ | `manifest.py` | `RunManifest`: the JSON record `--manifest FILE` writes when a run ends (script, database, statements, files read/written/deleted, connections, outcome) |
111
+ | `config_validation.py` | `execsql config --validate`: reads the same config files as a run and reports every problem with its file and line |
112
+ | `data/` | Bundled package data (`execsql.conf.template` powers `execsql config --init`) |
104
113
 
105
114
  ______________________________________________________________________
106
115
 
116
+ ## Language Server { #language-server }
117
+
118
+ `execsql lsp` (`cli/commands/lsp.py` → `lsp/server.py:create_server()`) is a pygls 2 server on stdin/stdout. It never runs a script or connects to a database: it reuses the static tools.
119
+
120
+ | Module | Feature | Reuses |
121
+ | ------------------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------ |
122
+ | `server.py` | handlers, settings, debounced lint, watched files | `cli.run._tool_config` |
123
+ | `diagnostics.py` | lint findings as LSP diagnostics | `cli.lint.lint_source` |
124
+ | `document.py` | `ScriptIndex`: definitions, references, `INCLUDE` graph | `script.parser`, `cli.lint.variables_defined_by` |
125
+ | `cache.py` | each text's `FileFacts`, included files' text | `document` |
126
+ | `_util.py` | `span()`, `markdown()`, the metacommand-line regex | `lsprotocol` |
127
+ | `completion.py`, `hover.py` | completion and hover | `metacommands.reference` |
128
+ | `navigation.py`, `rename.py`, `folding.py` | definition, references, outline, links, rename, folding | `ScriptIndex`, the parser |
129
+ | `code_actions.py` | quick fixes for P002, P003, P004, V001 | the dispatch table (each fix is re-validated before it is offered) |
130
+ | `formatting.py` | Format Document | `format.format_file` with the `[format]` settings |
131
+
132
+ **Settings per workspace folder.** `ConfigData(start_dir=...)` (through `_tool_config(None, start_dir=folder)`) reads config as a run started in that directory would: its `execsql.conf`, and relative `config_file` chains resolved against it. `load_settings()` reads each folder's `[lint]` / `[format]` settings that way and keeps them by folder; a document uses its innermost folder's settings, a document in no folder those of the workspace root (`base_dir()`, or the server's working directory when there is no root or it is gone). The server never changes its working directory. An unsaved script's relative `INCLUDE` resolves against `base_dir()` too, for both lint (`lint_source(base_dir=...)`) and the index.
133
+
134
+ **Index lifetime.** A `ScriptIndex` is assembled on every request that needs one (hover, completion, definition, references, rename, links, code actions) from per-file `FileFacts`: what one file defines, calls, includes and refers to, independent of the files it includes. `lsp/cache.py` keeps each text's facts and each included file's text (by path, size and modification time, dropped when the client reports the file changed), so a request reparses only the edited buffer; a file with a `SUB_INI` is reparsed every time, since its INI file can change on its own. `INCLUDE`d files are read too (the editor's unsaved copy when the file is open), up to 200 files, 10 MiB each and 20 MiB together, regular files only; `SUB_INI` files at most 1 MiB, cached by path, size and modification time.
135
+
136
+ **Linting.** On open, and 200 ms after the last edit (`LINT_DELAY_SECONDS`), lint runs on the document and the result is published with the document's version. A change to a watched `**/execsql.conf` reloads the settings and relints every open document. Saving a script, or a change to a watched `**/*.sql`, relints only the open documents that are that file or `INCLUDE` it directly: lint reads no included file's text, it only checks that each exists. Both go through the same debounce, so the editor's `didSave` and its watcher's report of the same save lint once.
137
+
138
+ **Trust.** The server reads the files the workspace names, so the VS Code client (`extras/vscode-execsql/extension.js`) starts it only in a trusted workspace, resolves `execsql` through `PATH` (never from the open folder) and runs it from the home directory. Plugins load from the server's own Python environment, not from the workspace.
139
+
107
140
  ## AST Parser and Executor
108
141
 
109
142
  execsql2 parses scripts into an AST and walks the tree to execute. There is no separate flat-command-list engine; the AST executor is the only path.
@@ -157,6 +190,10 @@ ______________________________________________________________________
157
190
 
158
191
  Metacommands are lines in SQL scripts prefixed with `-- !x!`. At import time, `metacommands/__init__.py` calls `build_dispatch_table()`, which populates a `MetaCommandList` with all `mcl.add()` registrations (~225 regex patterns). This singleton is stored as `_state.metacommandlist`.
159
192
 
193
+ ### The syntax registry
194
+
195
+ `metacommands/reference.py` describes every metacommand a second way: the syntax forms as the reference docs write them, a one-line summary, a category and the docs anchor. The regex dispatch table decides what *runs*; the registry is what people and editors *read*. `execsql list metacommands`, the language server's completion snippets and hover text come from it. `tests/test_reference.py` checks that every registry form is accepted by the dispatch table, that every dispatch keyword has an entry, and that each anchor exists in `docs/reference/metacommands.md`. A new metacommand therefore needs both: an `mcl.add()` registration and a registry entry.
196
+
160
197
  ### How dispatch works
161
198
 
162
199
  For each `MetaCommandStatement` AST node, the executor calls `_exec_metacommand()` in `script/executor.py`, which delegates to `_state.metacommandlist.eval(cmd_str)`. The dispatcher extracts the leading keyword, narrows ~225 entries to a small candidate set via a keyword index, then tests each candidate's compiled regex against the full command. The matched handler is called with the regex's named groups as keyword arguments plus `metacommandline` (the original unmodified line), and its hit counter is incremented.
@@ -303,19 +340,19 @@ This import pattern (always as `_state`, always accessed inside function/method
303
340
 
304
341
  ### Key state variables
305
342
 
306
- | Variable | Type | Purpose |
307
- | ----------------- | ------------------------ | ---------------------------------------------------------------- |
308
- | `conf` | `ConfigData` | Merged configuration |
309
- | `subvars` | `SubVarSet` | Global substitution variables |
310
- | `ast_exec_stack` | `list[ExecFrame]` | Unified execution stack (scopes + IF/LOOP/BATCH/INCLUDE) |
311
- | `ast_scripts` | `dict[str, ScriptBlock]` | Named scripts from `BEGIN/END SCRIPT` (AST registry) |
312
- | `last_command` | `ScriptCmd \| None` | Most-recently-executed statement; powers `current_script_line()` |
313
- | `dbs` | `DatabasePool` | Open database connections |
314
- | `metacommandlist` | `MetaCommandList` | Metacommand dispatch table |
315
- | `conditionallist` | `MetaCommandList` | Conditional predicate dispatch table |
316
- | `counters` | `CounterVars` | Auto-incrementing counters |
317
- | `filewriter` | `FileWriter` | This run's background file-writing thread |
318
- | `exec_log` | `Logger` | Execution log |
343
+ | Variable | Type | Purpose |
344
+ | ----------------- | ---------------------------- | ---------------------------------------------------------------- |
345
+ | `conf` | `ConfigData` | Merged configuration |
346
+ | `subvars` | `SubVarSet` | Global substitution variables |
347
+ | `ast_exec_stack` | `list[ExecFrame]` | Unified execution stack (scopes + IF/LOOP/BATCH/INCLUDE) |
348
+ | `ast_scripts` | `dict[str, ScriptBlock]` | Named scripts from `BEGIN/END SCRIPT` (AST registry) |
349
+ | `last_command` | `ExecutingStatement \| None` | Most-recently-executed statement; powers `current_script_line()` |
350
+ | `dbs` | `DatabasePool` | Open database connections |
351
+ | `metacommandlist` | `MetaCommandList` | Metacommand dispatch table |
352
+ | `conditionallist` | `MetaCommandList` | Conditional predicate dispatch table |
353
+ | `counters` | `CounterVars` | Auto-incrementing counters |
354
+ | `filewriter` | `FileWriter` | This run's background file-writing thread |
355
+ | `exec_log` | `Logger` | Execution log |
319
356
 
320
357
  ______________________________________________________________________
321
358
 
@@ -328,7 +365,7 @@ ______________________________________________________________________
328
365
  1. System-wide (platform-dependent)
329
366
  1. User home directory
330
367
  1. Script file directory
331
- 1. Current working directory
368
+ 1. Current working directory (or `start_dir`, when the caller passes one)
332
369
 
333
370
  It exposes all recognized options as attributes (`db_type`, `server`, `db`, `db_file`, `username`, `script_encoding`, `output_encoding`, etc.).
334
371
 
@@ -300,8 +300,8 @@ The options below belong to `run`: `execsql run -tl script.sql mydb.sqlite`.
300
300
  `--version` and `-o`/`--online-help` work on their own (`execsql --version`)
301
301
  and anywhere on a `run` command line.
302
302
  `format` has its own options, described in the [formatter guide](../guides/formatter.md);
303
- `lint`'s are in the [lint rules reference](../reference/lint.md); `lsp`'s (`--check`) in
304
- [Editor Support](../guides/editors.md#install); `config`, `list` and `init` are described
303
+ `lint`'s are in the [lint rules reference](../reference/lint.md); `lsp`'s (`--check`,
304
+ `--log-level`, `--log-file`) in [Editor Support](../guides/editors.md#troubleshooting); `config`, `list` and `init` are described
305
305
  [above](#config_command).
306
306
 
307
307
  ### Connection options
@@ -479,7 +479,7 @@ Valid encoding names can be displayed with `execsql list encodings`. See also [C
479
479
  }
480
480
  ```
481
481
 
482
- Files and connections are recorded as each metacommand succeeds, after variable substitution, so paths are the ones used (`out/west.csv`, not `out/!!region!!.csv`); `INCLUDE`d scripts count as files read. When a run fails, `exit_status` is its exit code and `errors` holds the message, its `type`, `source`, `line` and the failing `command`. `variables` lists the names set with `-a` and `--var`; their values are never written, for the same reason the log hides them, and neither are passwords.
482
+ Files and connections are recorded as each metacommand succeeds, after variable substitution, so paths are the ones used (`out/west.csv`, not `out/!!region!!.csv`); `INCLUDE`d scripts count as files read. When a run fails, `exit_status` is its exit code and `errors` holds the message, its `type`, `source`, `line` and the failing `command`. `variables` lists the names set with `-a` and `--var`, not their values. A password (from `--dsn`, a prompt or the keyring), a `SUB_DECRYPT` result, or a common secret shape (a URL's password, `password=...`, an API token) that ends up in a path, a connection target or an error message is written as `***`. Values given with `-a` and `--var` are kept where a path uses them, as in `out/west.csv`, although the log masks them: keep a secret out of `-a` and `--var` if the manifest is collected.
483
483
 
484
484
  The path is checked before the run starts: if *FILE* cannot be written (its folder is a file, or not writable), `execsql run` exits 2 before any SQL runs.
485
485