execsql2 2.19.0__tar.gz → 2.19.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 (373) hide show
  1. {execsql2-2.19.0 → execsql2-2.19.1}/CHANGELOG.md +8 -0
  2. {execsql2-2.19.0 → execsql2-2.19.1}/PKG-INFO +1 -1
  3. {execsql2-2.19.0 → execsql2-2.19.1}/docs/about/divergence.md +1 -1
  4. {execsql2-2.19.0 → execsql2-2.19.1}/docs/getting-started/installation.md +3 -1
  5. {execsql2-2.19.0 → execsql2-2.19.1}/docs/getting-started/requirements.md +29 -2
  6. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/debugging.md +14 -7
  7. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/formatter.md +8 -2
  8. {execsql2-2.19.0 → execsql2-2.19.1}/pyproject.toml +2 -2
  9. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/format.py +9 -3
  10. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_format.py +24 -0
  11. {execsql2-2.19.0 → execsql2-2.19.1}/uv.lock +1 -1
  12. {execsql2-2.19.0 → execsql2-2.19.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {execsql2-2.19.0 → execsql2-2.19.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {execsql2-2.19.0 → execsql2-2.19.1}/.github/dependabot.yml +0 -0
  15. {execsql2-2.19.0 → execsql2-2.19.1}/.github/workflows/ci-cd.yml +0 -0
  16. {execsql2-2.19.0 → execsql2-2.19.1}/.gitignore +0 -0
  17. {execsql2-2.19.0 → execsql2-2.19.1}/.pre-commit-config.yaml +0 -0
  18. {execsql2-2.19.0 → execsql2-2.19.1}/.pre-commit-hooks.yaml +0 -0
  19. {execsql2-2.19.0 → execsql2-2.19.1}/.python-version +0 -0
  20. {execsql2-2.19.0 → execsql2-2.19.1}/.readthedocs.yaml +0 -0
  21. {execsql2-2.19.0 → execsql2-2.19.1}/CONTRIBUTING.md +0 -0
  22. {execsql2-2.19.0 → execsql2-2.19.1}/LICENSE.txt +0 -0
  23. {execsql2-2.19.0 → execsql2-2.19.1}/NOTICE +0 -0
  24. {execsql2-2.19.0 → execsql2-2.19.1}/README.md +0 -0
  25. {execsql2-2.19.0 → execsql2-2.19.1}/SECURITY.md +0 -0
  26. {execsql2-2.19.0 → execsql2-2.19.1}/docs/about/contributors.md +0 -0
  27. {execsql2-2.19.0 → execsql2-2.19.1}/docs/about/copyright.md +0 -0
  28. {execsql2-2.19.0 → execsql2-2.19.1}/docs/api/cli.md +0 -0
  29. {execsql2-2.19.0 → execsql2-2.19.1}/docs/api/db.md +0 -0
  30. {execsql2-2.19.0 → execsql2-2.19.1}/docs/api/exporters.md +0 -0
  31. {execsql2-2.19.0 → execsql2-2.19.1}/docs/api/importers.md +0 -0
  32. {execsql2-2.19.0 → execsql2-2.19.1}/docs/api/index.md +0 -0
  33. {execsql2-2.19.0 → execsql2-2.19.1}/docs/api/metacommands.md +0 -0
  34. {execsql2-2.19.0 → execsql2-2.19.1}/docs/dev/adding_db_adapters.md +0 -0
  35. {execsql2-2.19.0 → execsql2-2.19.1}/docs/dev/adding_exporters.md +0 -0
  36. {execsql2-2.19.0 → execsql2-2.19.1}/docs/dev/adding_importers.md +0 -0
  37. {execsql2-2.19.0 → execsql2-2.19.1}/docs/dev/adding_metacommands.md +0 -0
  38. {execsql2-2.19.0 → execsql2-2.19.1}/docs/dev/architecture.md +0 -0
  39. {execsql2-2.19.0 → execsql2-2.19.1}/docs/getting-started/syntax.md +0 -0
  40. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/documentation.md +0 -0
  41. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/encoding.md +0 -0
  42. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/examples.md +0 -0
  43. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/logging.md +0 -0
  44. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/sql_syntax.md +0 -0
  45. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/usage.md +0 -0
  46. {execsql2-2.19.0 → execsql2-2.19.1}/docs/guides/using_scripts.md +0 -0
  47. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/Compare_planets.png +0 -0
  48. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/actions.png +0 -0
  49. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/actions2.png +0 -0
  50. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/checkboxes.png +0 -0
  51. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/connect.b64 +0 -0
  52. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/connect.png +0 -0
  53. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/create_conf.png +0 -0
  54. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/data_error1_screenshot.jpg +0 -0
  55. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/entry_form.png +0 -0
  56. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/execsql_console.png +0 -0
  57. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/execsql_logo_01.png +0 -0
  58. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/fatals.png +0 -0
  59. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/logo_small.png +0 -0
  60. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/pause_terminal.png +0 -0
  61. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/pause_terminal_sm.b64 +0 -0
  62. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/pause_terminal_sm.png +0 -0
  63. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/prompt_compare.png +0 -0
  64. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/set_build_commands.jpg +0 -0
  65. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/unit_conversions.b64 +0 -0
  66. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/unit_conversions_029.png +0 -0
  67. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/unmatched.png +0 -0
  68. {execsql2-2.19.0 → execsql2-2.19.1}/docs/images/vim_execsql_highlight.png +0 -0
  69. {execsql2-2.19.0 → execsql2-2.19.1}/docs/index.md +0 -0
  70. {execsql2-2.19.0 → execsql2-2.19.1}/docs/reference/configuration.md +0 -0
  71. {execsql2-2.19.0 → execsql2-2.19.1}/docs/reference/metacommands.md +0 -0
  72. {execsql2-2.19.0 → execsql2-2.19.1}/docs/reference/security.md +0 -0
  73. {execsql2-2.19.0 → execsql2-2.19.1}/docs/reference/substitution_vars.md +0 -0
  74. {execsql2-2.19.0 → execsql2-2.19.1}/extras/plugin-template/README.md +0 -0
  75. {execsql2-2.19.0 → execsql2-2.19.1}/extras/plugin-template/pyproject.toml +0 -0
  76. {execsql2-2.19.0 → execsql2-2.19.1}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
  77. {execsql2-2.19.0 → execsql2-2.19.1}/extras/plugin-template/tests/test_plugin.py.example +0 -0
  78. {execsql2-2.19.0 → execsql2-2.19.1}/extras/vscode-execsql/README.md +0 -0
  79. {execsql2-2.19.0 → execsql2-2.19.1}/extras/vscode-execsql/package.json +0 -0
  80. {execsql2-2.19.0 → execsql2-2.19.1}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
  81. {execsql2-2.19.0 → execsql2-2.19.1}/justfile +0 -0
  82. {execsql2-2.19.0 → execsql2-2.19.1}/scripts/generate_vscode_grammar.py +0 -0
  83. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/__init__.py +0 -0
  84. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/__main__.py +0 -0
  85. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/api.py +0 -0
  86. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/cli/__init__.py +0 -0
  87. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/cli/dsn.py +0 -0
  88. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/cli/help.py +0 -0
  89. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/cli/lint.py +0 -0
  90. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/cli/run.py +0 -0
  91. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/config.py +0 -0
  92. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/data/__init__.py +0 -0
  93. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/data/execsql.conf.template +0 -0
  94. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/__init__.py +0 -0
  95. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/access.py +0 -0
  96. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/base.py +0 -0
  97. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/dsn.py +0 -0
  98. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/duckdb.py +0 -0
  99. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/factory.py +0 -0
  100. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/firebird.py +0 -0
  101. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/mysql.py +0 -0
  102. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/oracle.py +0 -0
  103. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/postgres.py +0 -0
  104. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/sqlite.py +0 -0
  105. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/db/sqlserver.py +0 -0
  106. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/debug/__init__.py +0 -0
  107. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/debug/repl.py +0 -0
  108. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exceptions.py +0 -0
  109. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/__init__.py +0 -0
  110. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/base.py +0 -0
  111. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/delimited.py +0 -0
  112. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/duckdb.py +0 -0
  113. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/feather.py +0 -0
  114. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/html.py +0 -0
  115. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/json.py +0 -0
  116. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/latex.py +0 -0
  117. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/markdown.py +0 -0
  118. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/ods.py +0 -0
  119. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/parquet.py +0 -0
  120. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/pretty.py +0 -0
  121. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/protocol.py +0 -0
  122. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/raw.py +0 -0
  123. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/sqlite.py +0 -0
  124. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/templates.py +0 -0
  125. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/values.py +0 -0
  126. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/xls.py +0 -0
  127. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/xlsx.py +0 -0
  128. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/xml.py +0 -0
  129. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/yaml.py +0 -0
  130. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/exporters/zip.py +0 -0
  131. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/gui/__init__.py +0 -0
  132. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/gui/base.py +0 -0
  133. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/gui/console.py +0 -0
  134. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/gui/desktop.py +0 -0
  135. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/gui/tui.py +0 -0
  136. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/__init__.py +0 -0
  137. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/base.py +0 -0
  138. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/csv.py +0 -0
  139. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/feather.py +0 -0
  140. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/json.py +0 -0
  141. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/ods.py +0 -0
  142. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/importers/xls.py +0 -0
  143. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/__init__.py +0 -0
  144. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/conditions.py +0 -0
  145. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/connect.py +0 -0
  146. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/control.py +0 -0
  147. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/data.py +0 -0
  148. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/debug.py +0 -0
  149. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/dispatch.py +0 -0
  150. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/io.py +0 -0
  151. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/io_export.py +0 -0
  152. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/io_fileops.py +0 -0
  153. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/io_import.py +0 -0
  154. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/io_write.py +0 -0
  155. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/prompt.py +0 -0
  156. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/script_ext.py +0 -0
  157. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/system.py +0 -0
  158. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/metacommands/upsert.py +0 -0
  159. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/models.py +0 -0
  160. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/parser.py +0 -0
  161. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/plugins.py +0 -0
  162. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/py.typed +0 -0
  163. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/__init__.py +0 -0
  164. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/ast.py +0 -0
  165. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/control.py +0 -0
  166. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/engine.py +0 -0
  167. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/executor.py +0 -0
  168. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/parser.py +0 -0
  169. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/script/variables.py +0 -0
  170. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/state.py +0 -0
  171. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/types.py +0 -0
  172. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/__init__.py +0 -0
  173. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/auth.py +0 -0
  174. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/crypto.py +0 -0
  175. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/datetime.py +0 -0
  176. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/errors.py +0 -0
  177. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/fileio.py +0 -0
  178. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/gui.py +0 -0
  179. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/mail.py +0 -0
  180. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/numeric.py +0 -0
  181. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/regex.py +0 -0
  182. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/strings.py +0 -0
  183. {execsql2-2.19.0 → execsql2-2.19.1}/src/execsql/utils/timer.py +0 -0
  184. {execsql2-2.19.0 → execsql2-2.19.1}/templates/README.md +0 -0
  185. {execsql2-2.19.0 → execsql2-2.19.1}/templates/config_settings.sqlite +0 -0
  186. {execsql2-2.19.0 → execsql2-2.19.1}/templates/example_config_prompt.sql +0 -0
  187. {execsql2-2.19.0 → execsql2-2.19.1}/templates/execsql.conf +0 -0
  188. {execsql2-2.19.0 → execsql2-2.19.1}/templates/make_config_db.sql +0 -0
  189. {execsql2-2.19.0 → execsql2-2.19.1}/templates/md_compare.sql +0 -0
  190. {execsql2-2.19.0 → execsql2-2.19.1}/templates/md_glossary.sql +0 -0
  191. {execsql2-2.19.0 → execsql2-2.19.1}/templates/md_upsert.sql +0 -0
  192. {execsql2-2.19.0 → execsql2-2.19.1}/templates/pg_compare.sql +0 -0
  193. {execsql2-2.19.0 → execsql2-2.19.1}/templates/pg_glossary.sql +0 -0
  194. {execsql2-2.19.0 → execsql2-2.19.1}/templates/pg_upsert.sql +0 -0
  195. {execsql2-2.19.0 → execsql2-2.19.1}/templates/script_template.sql +0 -0
  196. {execsql2-2.19.0 → execsql2-2.19.1}/templates/ss_compare.sql +0 -0
  197. {execsql2-2.19.0 → execsql2-2.19.1}/templates/ss_glossary.sql +0 -0
  198. {execsql2-2.19.0 → execsql2-2.19.1}/templates/ss_upsert.sql +0 -0
  199. {execsql2-2.19.0 → execsql2-2.19.1}/tests/__init__.py +0 -0
  200. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/__init__.py +0 -0
  201. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/test_cli.py +0 -0
  202. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/test_cli_e2e.py +0 -0
  203. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/test_cli_run.py +0 -0
  204. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/test_lint.py +0 -0
  205. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/test_ping.py +0 -0
  206. {execsql2-2.19.0 → execsql2-2.19.1}/tests/cli/test_profile.py +0 -0
  207. {execsql2-2.19.0 → execsql2-2.19.1}/tests/conftest.py +0 -0
  208. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/__init__.py +0 -0
  209. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_access_windows.py +0 -0
  210. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_base.py +0 -0
  211. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_db_adapters_mocked.py +0 -0
  212. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_db_adapters_mocked_extra.py +0 -0
  213. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_dsn.py +0 -0
  214. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_duckdb.py +0 -0
  215. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_factory.py +0 -0
  216. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_mysql_case_folding.py +0 -0
  217. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_mysql_inprocess.py +0 -0
  218. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_postgres.py +0 -0
  219. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_postgres_inprocess.py +0 -0
  220. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_sqlite.py +0 -0
  221. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_sqlite_extra.py +0 -0
  222. {execsql2-2.19.0 → execsql2-2.19.1}/tests/db/test_sqlserver_inprocess.py +0 -0
  223. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/__init__.py +0 -0
  224. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_base.py +0 -0
  225. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_db.py +0 -0
  226. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_delimited.py +0 -0
  227. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_duckdb_exporter.py +0 -0
  228. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_exporters.py +0 -0
  229. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_feather.py +0 -0
  230. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_html_extended.py +0 -0
  231. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_html_latex.py +0 -0
  232. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_json.py +0 -0
  233. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_json_extended.py +0 -0
  234. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_latex_extended.py +0 -0
  235. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_markdown.py +0 -0
  236. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_ods.py +0 -0
  237. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_ods_export.py +0 -0
  238. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_parquet.py +0 -0
  239. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_pretty_extended.py +0 -0
  240. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_raw_extended.py +0 -0
  241. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_sqlite_exporter.py +0 -0
  242. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_templates.py +0 -0
  243. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_templates_extended.py +0 -0
  244. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_values_extended.py +0 -0
  245. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_xls_xlsx.py +0 -0
  246. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_xlsx.py +0 -0
  247. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_xml.py +0 -0
  248. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_yaml.py +0 -0
  249. {execsql2-2.19.0 → execsql2-2.19.1}/tests/exporters/test_zip.py +0 -0
  250. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/__init__.py +0 -0
  251. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_backends.py +0 -0
  252. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_backends_extended.py +0 -0
  253. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_compare_stats.py +0 -0
  254. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_compute_row_diffs.py +0 -0
  255. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_desktop_dialogs.py +0 -0
  256. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_tui_pilot.py +0 -0
  257. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_tui_pilot_complex.py +0 -0
  258. {execsql2-2.19.0 → execsql2-2.19.1}/tests/gui/test_utils_gui_extended.py +0 -0
  259. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/__init__.py +0 -0
  260. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_base_extended.py +0 -0
  261. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_csv_edge_cases.py +0 -0
  262. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_csv_importer.py +0 -0
  263. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_feather_importer.py +0 -0
  264. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_json_importer.py +0 -0
  265. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_ods_importer.py +0 -0
  266. {execsql2-2.19.0 → execsql2-2.19.1}/tests/importers/test_xls_importer.py +0 -0
  267. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/__init__.py +0 -0
  268. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/conftest.py +0 -0
  269. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/test_dsn.py +0 -0
  270. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/test_duckdb.py +0 -0
  271. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/test_mysql.py +0 -0
  272. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/test_postgres.py +0 -0
  273. {execsql2-2.19.0 → execsql2-2.19.1}/tests/integration/test_sqlite.py +0 -0
  274. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/__init__.py +0 -0
  275. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_assert.py +0 -0
  276. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_breakpoint.py +0 -0
  277. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_conditions_extra.py +0 -0
  278. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_connect.py +0 -0
  279. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_io_export.py +0 -0
  280. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_io_import.py +0 -0
  281. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands.py +0 -0
  282. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_connect.py +0 -0
  283. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_data.py +0 -0
  284. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_extended.py +0 -0
  285. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  286. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_io.py +0 -0
  287. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  288. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  289. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_system.py +0 -0
  290. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  291. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_pg_upsert.py +0 -0
  292. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_prompt.py +0 -0
  293. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_row_count.py +0 -0
  294. {execsql2-2.19.0 → execsql2-2.19.1}/tests/metacommands/test_show_scripts.py +0 -0
  295. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/__init__.py +0 -0
  296. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
  297. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
  298. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
  299. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/audit_smoke.sql +0 -0
  300. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/config_runtime.sql +0 -0
  301. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/control_flow.sql +0 -0
  302. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
  303. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/error_handling.sql +0 -0
  304. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
  305. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/import_options.sql +0 -0
  306. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/includes/helper.sql +0 -0
  307. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/includes.sql +0 -0
  308. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/io_formats.sql +0 -0
  309. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
  310. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/multi_db.sql +0 -0
  311. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
  312. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
  313. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/predicates.sql +0 -0
  314. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/scripts.sql +0 -0
  315. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/smoke.sql +0 -0
  316. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
  317. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
  318. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
  319. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/template_export.sql +0 -0
  320. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/timer.sql +0 -0
  321. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/transactions.sql +0 -0
  322. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/write_create_table.sql +0 -0
  323. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
  324. {execsql2-2.19.0 → execsql2-2.19.1}/tests/scripts/test_sql_scripts.py +0 -0
  325. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/__init__.py +0 -0
  326. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_env_detection.py +0 -0
  327. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_expansion_bomb.py +0 -0
  328. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_import_regex_hardening.py +0 -0
  329. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_log_redaction.py +0 -0
  330. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_odbc_injection.py +0 -0
  331. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_path_containment.py +0 -0
  332. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_substitution_injection.py +0 -0
  333. {execsql2-2.19.0 → execsql2-2.19.1}/tests/security/test_zip_bomb.py +0 -0
  334. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_api.py +0 -0
  335. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_api_extended.py +0 -0
  336. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_ast.py +0 -0
  337. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_ast_parser.py +0 -0
  338. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_bundled_templates.py +0 -0
  339. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_config.py +0 -0
  340. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_config_data.py +0 -0
  341. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_config_extended.py +0 -0
  342. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_debug_repl.py +0 -0
  343. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_engine.py +0 -0
  344. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_error_messages.py +0 -0
  345. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_exceptions.py +0 -0
  346. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_executor.py +0 -0
  347. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_executor_inprocess.py +0 -0
  348. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_mail.py +0 -0
  349. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_models.py +0 -0
  350. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_package.py +0 -0
  351. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_parser.py +0 -0
  352. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_parser_params.py +0 -0
  353. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_plugins.py +0 -0
  354. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_registry.py +0 -0
  355. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_script.py +0 -0
  356. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_state.py +0 -0
  357. {execsql2-2.19.0 → execsql2-2.19.1}/tests/test_types.py +0 -0
  358. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/__init__.py +0 -0
  359. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_auth.py +0 -0
  360. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_auth_extra.py +0 -0
  361. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_crypto.py +0 -0
  362. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_datetime.py +0 -0
  363. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_encodedfile_context.py +0 -0
  364. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_errors.py +0 -0
  365. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_errors_extra.py +0 -0
  366. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_fileio.py +0 -0
  367. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_fileio_extra.py +0 -0
  368. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_numeric.py +0 -0
  369. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_regex.py +0 -0
  370. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_strings.py +0 -0
  371. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_timer.py +0 -0
  372. {execsql2-2.19.0 → execsql2-2.19.1}/tests/utils/test_timer_extra.py +0 -0
  373. {execsql2-2.19.0 → execsql2-2.19.1}/zensical.toml +0 -0
@@ -13,6 +13,14 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.19.1] - 2026-06-03
17
+
18
+ ### Fixed
19
+
20
+ - `execsql-format` no longer mangles quoted substitution variables: `!'!var!'!` and `!"!var!"!` were being parsed by sqlglot as `NOT` operators (e.g. `!'!myvar!'!` → `NOT NOT '!myvar!'`). All three quote forms plus deferred `!{var}!` are now hidden from SQL formatting.
21
+
22
+ ______________________________________________________________________
23
+
16
24
  ## [2.19.0] - 2026-06-02
17
25
 
18
26
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.19.0
3
+ Version: 2.19.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
@@ -147,7 +147,7 @@ New options in `execsql.conf`:
147
147
 
148
148
  ### Debugging { #debugging }
149
149
 
150
- execsql2 adds an interactive debug REPL (no upstream equivalent), triggered either by the `BREAKPOINT` metacommand or the `--debug` CLI flag (step-through mode). The REPL prompts with `execsql debug>` and accepts dot-prefixed commands (`.continue`, `.next`, `.vars`, `.where`, `.stack`, `.set`, `.scripts`, `.help`), bare variable names for inspection, and SQL terminated with `;` for ad-hoc queries against the live connection. `BREAKPOINT` is silently skipped when stdin is not a TTY so it does not hang CI. Full command list and behavior: [Debugging guide](../guides/debugging.md).
150
+ execsql2 adds an interactive debug REPL (no upstream equivalent), triggered either by the `BREAKPOINT` metacommand or the `--debug` CLI flag (step-through mode). The REPL prompts with `execsql debug>` and uses two-way dispatch (as of 2.19.0): input starting with `.` is a REPL command (`.continue`, `.quit`, `.next`, `.vars`, `.vars VAR`, `.where`, `.stack`, `.set`, `.scripts`, `.cancel`, `.help`); everything else is SQL, buffered across lines until terminated with `;` and executed against the live connection (DML reports rows affected, DDL / transaction-control reports `(statement executed)`, errors print inline without ending the session). `BREAKPOINT` is silently skipped when stdin is not a TTY so it does not hang CI. Full command list and behavior: [Debugging guide](../guides/debugging.md).
151
151
 
152
152
  ### Library API
153
153
 
@@ -27,12 +27,14 @@ pip install "execsql2[odbc]" # Generic ODBC DSN (pyodbc)
27
27
  pip install "execsql2[firebird]" # Firebird
28
28
  pip install "execsql2[oracle]" # Oracle
29
29
  pip install "execsql2[formats]" # ODS, Excel, Jinja2, Feather/Parquet, HDF5, YAML
30
+ pip install "execsql2[formatter]" # SQL pass for execsql-format (sqlglot)
30
31
  pip install "execsql2[auth]" # OS keyring integration (desktop/native)
31
32
  pip install "execsql2[auth-plaintext]" # Headless keyring (plaintext file backend)
32
33
  pip install "execsql2[auth-encrypted]" # Headless keyring (encrypted file backend)
33
34
  pip install "execsql2[upsert]" # PG_UPSERT metacommand (pg-upsert)
35
+ pip install "execsql2[map]" # PROMPT MAP widget (tkintermapview)
34
36
  pip install "execsql2[all-db]" # All database drivers
35
- pip install "execsql2[all]" # Everything (all-db + formats + auth + upsert)
37
+ pip install "execsql2[all]" # Everything (all-db + formats + formatter + auth + upsert + map)
36
38
  ```
37
39
 
38
40
  In addition to the *execsql* program itself, additional Python libraries may need to be installed to use *execsql* with specific types of DBMSs and spreadsheets. The additional libraries that may be needed are listed in the [Requirements](requirements.md#requirements) section.
@@ -18,11 +18,16 @@ pip install "execsql2[odbc]" # ODBC DSN (pyodbc)
18
18
 
19
19
  # Feature bundles
20
20
  pip install "execsql2[formats]" # ODS, Excel, Jinja2, Feather, Parquet, HDF5
21
- pip install "execsql2[auth]" # OS keyring integration
21
+ pip install "execsql2[formatter]" # SQL pass for execsql-format (sqlglot)
22
+ pip install "execsql2[upsert]" # PG_UPSERT metacommand (pg-upsert)
23
+ pip install "execsql2[map]" # PROMPT MAP widget (tkintermapview)
24
+ pip install "execsql2[auth]" # OS keyring integration (desktop / native)
25
+ pip install "execsql2[auth-plaintext]" # Headless keyring (plaintext file backend)
26
+ pip install "execsql2[auth-encrypted]" # Headless keyring (encrypted file backend)
22
27
 
23
28
  # Convenience
24
29
  pip install "execsql2[all-db]" # All database drivers
25
- pip install "execsql2[all]" # Everything
30
+ pip install "execsql2[all]" # Everything (all-db + formats + formatter + auth + upsert + map)
26
31
  ```
27
32
 
28
33
  Multiple extras can be combined: `pip install "execsql2[postgres,duckdb,formats]"`.
@@ -55,12 +60,34 @@ The specific libraries installed by each extra are:
55
60
  | [Parquet](https://parquet.apache.org/) files | [polars](https://pypi.org/project/polars/) |
56
61
  | [HDF5](https://www.hdfgroup.org/solutions/hdf5/) files | [tables](https://pypi.org/project/tables/) |
57
62
 
63
+ ### `formatter` extra
64
+
65
+ Required for the SQL-formatting pass of the `execsql-format` CLI. Without this extra, `execsql-format` still normalizes metacommand indentation and keyword casing (use `--no-sql` or import scripts without SQL); the SQL pretty-printing pass calls [sqlglot](https://sqlglot.com/) and raises `ModuleNotFoundError` if it isn't installed.
66
+
67
+ | Feature | Library |
68
+ | ------------------------------------- | -------------------------------------------- |
69
+ | SQL reformatting via `execsql-format` | [sqlglot](https://pypi.org/project/sqlglot/) |
70
+
71
+ ### `upsert` extra
72
+
73
+ | Feature | Library |
74
+ | ----------------------------------------- | ------------------------------------------------ |
75
+ | `PG_UPSERT` PostgreSQL upsert metacommand | [pg-upsert](https://pypi.org/project/pg-upsert/) |
76
+
77
+ ### `map` extra
78
+
79
+ | Feature | Library |
80
+ | ------------------------------------- | ---------------------------------------------------------- |
81
+ | `PROMPT MAP` lat/lon selection widget | [tkintermapview](https://pypi.org/project/tkintermapview/) |
82
+
58
83
  ### `auth` bundle
59
84
 
60
85
  | Feature | Library |
61
86
  | -------------------- | -------------------------------------------- |
62
87
  | OS keyring / secrets | [keyring](https://pypi.org/project/keyring/) |
63
88
 
89
+ The `auth-plaintext` and `auth-encrypted` variants add a fallback keyring backend for headless Linux: `auth-plaintext` adds [keyrings.alt](https://pypi.org/project/keyrings.alt/) (plaintext file storage — **secrets are not encrypted at rest**), and `auth-encrypted` adds `keyrings.alt` plus [pycryptodome](https://pypi.org/project/pycryptodome/) for an encrypted file backend. See [Keyring Platform Setup](../reference/security.md#keyring_setup).
90
+
64
91
  Connections to SQLite databases use Python's standard library and require no additional packages.
65
92
 
66
93
  ## Additional System Requirements { #system_requirements }
@@ -57,22 +57,29 @@ execsql debug>
57
57
 
58
58
  | Command | Shortcut | Description |
59
59
  | --------------- | -------- | ------------------------------------------------------------------------- |
60
- | `.continue` | `.c` | Resume normal script execution |
61
- | `.abort` | `.q` | Halt the script (exit 1) |
62
- | `.vars` | `.v` | List user, system, local, and counter substitution variables |
63
- | `.vars all` | `.v all` | Include environment variables (`&`) in the listing |
60
+ | `.continue` | `.c` | Resume script execution |
61
+ | `.quit` | `.q` | Halt the script (exit 1). `.abort` is accepted as an alias. |
62
+ | `.vars` | `.v` | List all execsql substitution variables |
63
+ | `.vars VAR` | `.v VAR` | Print the value of one variable (e.g. `.vars logfile`, `.vars $ARG_1`) |
64
64
  | `.next` | `.n` | Execute the next statement, then pause again (step mode) |
65
65
  | `.where` | `.w` | Re-display the current script location and upcoming statement |
66
66
  | `.stack` | | Show the command-list stack (script name, cursor position, nesting depth) |
67
67
  | `.set VAR VAL` | `.s` | Set or update a substitution variable |
68
68
  | `.scripts` | | List all registered SCRIPT definitions with parameters and source |
69
69
  | `.scripts NAME` | | Show detail for a specific SCRIPT (parameters, source file/line range) |
70
+ | `.cancel` | | Discard the current partial multi-line SQL buffer (also Ctrl-C / EOF) |
70
71
  | `.help` | `.h` | Show available commands |
71
72
 
72
- Anything not starting with `.` is treated as a variable lookup or SQL:
73
+ **Dispatch is two-way** (changed in 2.19.0): input starting with `.` is a REPL command, everything else is SQL. There is no bare-name variable lookup use `.vars VAR` to print a single variable.
73
74
 
74
- - A bare name (e.g. `logfile`) prints the value of that substitution variable.
75
- - Any input ending with `;` is executed as SQL against the current database (expects columns returned, e.g. SELECT).
75
+ **SQL execution:**
76
+
77
+ - Input is buffered as SQL until a line ends with `;`, at which point the buffered statement is sent to the live connection. Multi-line statements are accepted; the prompt switches to a continuation indicator while a partial statement is being entered.
78
+ - `SELECT` (and other row-returning statements) print the result rows in tabular form.
79
+ - DML (`INSERT` / `UPDATE` / `DELETE`) prints `(N rows affected)`.
80
+ - DDL and transaction-control statements (`CREATE`, `DROP`, `BEGIN`, `COMMIT`, `ROLLBACK`, …) print `(statement executed)`.
81
+ - A SQL error prints the database error inline and returns to the prompt — the REPL session is not terminated.
82
+ - `.cancel` (or Ctrl-C / EOF mid-statement) discards a partial buffer without executing it.
76
83
 
77
84
  The `--debug` CLI flag starts execution in step mode, pausing before every statement.
78
85
 
@@ -4,13 +4,19 @@
4
4
 
5
5
  ## Installation { #installation }
6
6
 
7
- `execsql-format` is installed automatically as part of the `execsql2` package no extra steps required.
7
+ The `execsql-format` command is installed automatically with the `execsql2` package and is available on your PATH after install:
8
8
 
9
9
  ```bash
10
10
  pip install execsql2
11
11
  ```
12
12
 
13
- After installation, the `execsql-format` command is available on your PATH.
13
+ The metacommand-indentation and keyword-casing passes work out of the box. **SQL reformatting** (the optional sqlglot pass) requires the `[formatter]` extra, as of execsql2 2.19.0:
14
+
15
+ ```bash
16
+ pip install "execsql2[formatter]"
17
+ ```
18
+
19
+ Without the extra, `execsql-format` works in `--no-sql` mode (metacommand indentation and keyword casing only); invoking the SQL pass without `[formatter]` installed raises `ModuleNotFoundError: No module named 'sqlglot'`.
14
20
 
15
21
  ## Usage { #usage }
16
22
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execsql2"
7
- version = "2.19.0"
7
+ version = "2.19.1"
8
8
  description = "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."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = { file = "LICENSE.txt" }
@@ -170,7 +170,7 @@ skip-magic-trailing-comma = false
170
170
  line-ending = "auto"
171
171
 
172
172
  [tool.bumpversion]
173
- current_version = "2.19.0"
173
+ current_version = "2.19.1"
174
174
  commit = true
175
175
  tag = true
176
176
  tag_name = "v{new_version}"
@@ -129,9 +129,15 @@ def parse_keyword(payload: str) -> str:
129
129
  # SQL block formatting helpers
130
130
  # ---------------------------------------------------------------------------
131
131
 
132
- # Matches execsql variable substitutions: !!varname!!, !!#param!!, !!@col!!, etc.
133
- # and deferred substitutions !{varname}!
134
- _EXECSQL_VAR_RE = re.compile(r"!!([^!\s][^!]*)!!|!\{[^}]+\}!")
132
+ # Matches execsql variable substitutions:
133
+ # !!varname!! verbatim
134
+ # !'!varname!'! single-quoted (apostrophes doubled at expansion)
135
+ # !"!varname!"! double-quoted (quotes doubled at expansion)
136
+ # !{varname}! deferred
137
+ # The bare/quoted forms mirror src/execsql/script/variables.py:_TOKEN_RX so
138
+ # every token the executor recognises is hidden from sqlglot — otherwise the
139
+ # `!` in a quoted form is parsed as a NOT operator (e.g. `!'!v!'!` -> `NOT NOT '!v!'`).
140
+ _EXECSQL_VAR_RE = re.compile(r"""!(['"]?)!([^!\s][^!]*)!\1!|!\{[^}]+\}!""")
135
141
 
136
142
 
137
143
  def _protect_variables(sql: str) -> tuple[str, list[tuple[str, str]]]:
@@ -279,6 +279,18 @@ class TestProtectVariables:
279
279
  assert len(replacements) == 1
280
280
  assert replacements[0][1] == "!{myvar}!"
281
281
 
282
+ def test_single_quoted_var(self):
283
+ protected, replacements = _protect_variables("WHERE a = !'!myvar!'!")
284
+ assert "!'!myvar!'!" not in protected
285
+ assert len(replacements) == 1
286
+ assert replacements[0][1] == "!'!myvar!'!"
287
+
288
+ def test_double_quoted_var(self):
289
+ protected, replacements = _protect_variables('WHERE a = !"!myvar!"!')
290
+ assert '!"!myvar!"!' not in protected
291
+ assert len(replacements) == 1
292
+ assert replacements[0][1] == '!"!myvar!"!'
293
+
282
294
  def test_multiple_vars(self):
283
295
  protected, replacements = _protect_variables("SELECT !!a!!, !!b!!")
284
296
  assert len(replacements) == 2
@@ -649,6 +661,18 @@ class TestFormatFileEdgeCases:
649
661
  assert "!!$my_var!!" in result
650
662
  assert "!!table_name!!" in result
651
663
 
664
+ def test_quoted_subvar_not_mangled_as_not(self):
665
+ # Regression: !'!var!'! used to be parsed by sqlglot as NOT NOT '!var!'.
666
+ source = "select * from documents where author = !'!myvar!'!;\n"
667
+ result = format_file(source)
668
+ assert "!'!myvar!'!" in result
669
+ assert "NOT" not in result
670
+
671
+ def test_double_quoted_subvar_preserved(self):
672
+ source = 'select * from t where col = !"!myvar!"!;\n'
673
+ result = format_file(source)
674
+ assert '!"!myvar!"!' in result
675
+
652
676
  def test_elseif_depth(self):
653
677
  source = "-- !x! IF 1=1\n-- !x! WRITE 'a'\n-- !x! ELSEIF 2=2\n-- !x! WRITE 'b'\n-- !x! ENDIF\n"
654
678
  result = format_file(source, use_sql=False)
@@ -722,7 +722,7 @@ wheels = [
722
722
 
723
723
  [[package]]
724
724
  name = "execsql2"
725
- version = "2.19.0"
725
+ version = "2.19.1"
726
726
  source = { editable = "." }
727
727
  dependencies = [
728
728
  { name = "python-dateutil" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes