execsql2 2.11.0__tar.gz → 2.12.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 (301) hide show
  1. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/project_context.md +27 -8
  2. {execsql2-2.11.0 → execsql2-2.12.0}/CHANGELOG.md +40 -0
  3. {execsql2-2.11.0 → execsql2-2.12.0}/CLAUDE.md +2 -2
  4. {execsql2-2.11.0 → execsql2-2.12.0}/PKG-INFO +5 -2
  5. {execsql2-2.11.0 → execsql2-2.12.0}/README.md +4 -1
  6. {execsql2-2.11.0 → execsql2-2.12.0}/docs/about/divergence.md +49 -8
  7. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/debugging.md +41 -0
  8. {execsql2-2.11.0 → execsql2-2.12.0}/pyproject.toml +3 -3
  9. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/cli/__init__.py +6 -0
  10. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/cli/lint.py +1 -1
  11. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/cli/run.py +18 -12
  12. execsql2-2.12.0/src/execsql/debug/__init__.py +6 -0
  13. execsql2-2.12.0/src/execsql/debug/repl.py +472 -0
  14. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/xlsx.py +5 -0
  15. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/yaml.py +2 -0
  16. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/__init__.py +1 -1
  17. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/control.py +4 -10
  18. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/debug.py +1 -1
  19. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/dispatch.py +1 -1
  20. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/script/engine.py +15 -5
  21. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/state.py +2 -2
  22. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/gui.py +26 -4
  23. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/test_profile.py +52 -1
  24. {execsql2-2.11.0 → execsql2-2.12.0}/tests/gui/test_backends.py +422 -0
  25. execsql2-2.12.0/tests/importers/test_csv_importer.py +436 -0
  26. execsql2-2.12.0/tests/metacommands/test_breakpoint.py +980 -0
  27. execsql2-2.12.0/tests/metacommands/test_io_export.py +1377 -0
  28. execsql2-2.12.0/tests/metacommands/test_io_import.py +1585 -0
  29. execsql2-2.12.0/tests/metacommands/test_metacommands_data.py +802 -0
  30. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_extended.py +362 -3
  31. {execsql2-2.11.0 → execsql2-2.12.0}/uv.lock +1 -1
  32. execsql2-2.11.0/src/execsql/metacommands/debug_repl.py +0 -288
  33. execsql2-2.11.0/tests/importers/test_csv_importer.py +0 -193
  34. execsql2-2.11.0/tests/metacommands/test_breakpoint.py +0 -524
  35. execsql2-2.11.0/tests/metacommands/test_metacommands_data.py +0 -381
  36. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/dba.md +0 -0
  37. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/herald.md +0 -0
  38. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/inspector.md +0 -0
  39. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/oracle.md +0 -0
  40. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/patcher.md +0 -0
  41. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/qa.md +0 -0
  42. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/agents/scribe.md +0 -0
  43. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/commands/code-oracle.md +0 -0
  44. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/commands/migrate.md +0 -0
  45. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/commands/review-changes.md +0 -0
  46. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/commands/test-module.md +0 -0
  47. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/commands/update-changelog.md +0 -0
  48. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/commands/where-is.md +0 -0
  49. {execsql2-2.11.0 → execsql2-2.12.0}/.claude/state/status.md +0 -0
  50. {execsql2-2.11.0 → execsql2-2.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  51. {execsql2-2.11.0 → execsql2-2.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  52. {execsql2-2.11.0 → execsql2-2.12.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  53. {execsql2-2.11.0 → execsql2-2.12.0}/.github/workflows/ci-cd.yml +0 -0
  54. {execsql2-2.11.0 → execsql2-2.12.0}/.gitignore +0 -0
  55. {execsql2-2.11.0 → execsql2-2.12.0}/.pre-commit-config.yaml +0 -0
  56. {execsql2-2.11.0 → execsql2-2.12.0}/.pre-commit-hooks.yaml +0 -0
  57. {execsql2-2.11.0 → execsql2-2.12.0}/.python-version +0 -0
  58. {execsql2-2.11.0 → execsql2-2.12.0}/.readthedocs.yaml +0 -0
  59. {execsql2-2.11.0 → execsql2-2.12.0}/CONTRIBUTING.md +0 -0
  60. {execsql2-2.11.0 → execsql2-2.12.0}/LICENSE.txt +0 -0
  61. {execsql2-2.11.0 → execsql2-2.12.0}/NOTICE +0 -0
  62. {execsql2-2.11.0 → execsql2-2.12.0}/SECURITY.md +0 -0
  63. {execsql2-2.11.0 → execsql2-2.12.0}/docs/about/contributors.md +0 -0
  64. {execsql2-2.11.0 → execsql2-2.12.0}/docs/about/copyright.md +0 -0
  65. {execsql2-2.11.0 → execsql2-2.12.0}/docs/api/cli.md +0 -0
  66. {execsql2-2.11.0 → execsql2-2.12.0}/docs/api/db.md +0 -0
  67. {execsql2-2.11.0 → execsql2-2.12.0}/docs/api/exporters.md +0 -0
  68. {execsql2-2.11.0 → execsql2-2.12.0}/docs/api/importers.md +0 -0
  69. {execsql2-2.11.0 → execsql2-2.12.0}/docs/api/index.md +0 -0
  70. {execsql2-2.11.0 → execsql2-2.12.0}/docs/api/metacommands.md +0 -0
  71. {execsql2-2.11.0 → execsql2-2.12.0}/docs/dev/adding_db_adapters.md +0 -0
  72. {execsql2-2.11.0 → execsql2-2.12.0}/docs/dev/adding_exporters.md +0 -0
  73. {execsql2-2.11.0 → execsql2-2.12.0}/docs/dev/adding_importers.md +0 -0
  74. {execsql2-2.11.0 → execsql2-2.12.0}/docs/dev/adding_metacommands.md +0 -0
  75. {execsql2-2.11.0 → execsql2-2.12.0}/docs/dev/architecture.md +0 -0
  76. {execsql2-2.11.0 → execsql2-2.12.0}/docs/getting-started/installation.md +0 -0
  77. {execsql2-2.11.0 → execsql2-2.12.0}/docs/getting-started/requirements.md +0 -0
  78. {execsql2-2.11.0 → execsql2-2.12.0}/docs/getting-started/syntax.md +0 -0
  79. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/documentation.md +0 -0
  80. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/encoding.md +0 -0
  81. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/examples.md +0 -0
  82. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/formatter.md +0 -0
  83. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/logging.md +0 -0
  84. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/sql_syntax.md +0 -0
  85. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/usage.md +0 -0
  86. {execsql2-2.11.0 → execsql2-2.12.0}/docs/guides/using_scripts.md +0 -0
  87. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/Compare_planets.png +0 -0
  88. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/actions.png +0 -0
  89. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/actions2.png +0 -0
  90. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/checkboxes.png +0 -0
  91. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/connect.b64 +0 -0
  92. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/connect.png +0 -0
  93. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/create_conf.png +0 -0
  94. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/data_error1_screenshot.jpg +0 -0
  95. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/entry_form.png +0 -0
  96. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/execsql_console.png +0 -0
  97. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/execsql_logo_01.png +0 -0
  98. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/fatals.png +0 -0
  99. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/logo_small.png +0 -0
  100. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/pause_terminal.png +0 -0
  101. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/pause_terminal_sm.b64 +0 -0
  102. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/pause_terminal_sm.png +0 -0
  103. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/prompt_compare.png +0 -0
  104. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/set_build_commands.jpg +0 -0
  105. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/unit_conversions.b64 +0 -0
  106. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/unit_conversions_029.png +0 -0
  107. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/unmatched.png +0 -0
  108. {execsql2-2.11.0 → execsql2-2.12.0}/docs/images/vim_execsql_highlight.png +0 -0
  109. {execsql2-2.11.0 → execsql2-2.12.0}/docs/index.md +0 -0
  110. {execsql2-2.11.0 → execsql2-2.12.0}/docs/reference/configuration.md +0 -0
  111. {execsql2-2.11.0 → execsql2-2.12.0}/docs/reference/metacommands.md +0 -0
  112. {execsql2-2.11.0 → execsql2-2.12.0}/docs/reference/security.md +0 -0
  113. {execsql2-2.11.0 → execsql2-2.12.0}/docs/reference/substitution_vars.md +0 -0
  114. {execsql2-2.11.0 → execsql2-2.12.0}/extras/vscode-execsql/README.md +0 -0
  115. {execsql2-2.11.0 → execsql2-2.12.0}/extras/vscode-execsql/package.json +0 -0
  116. {execsql2-2.11.0 → execsql2-2.12.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
  117. {execsql2-2.11.0 → execsql2-2.12.0}/justfile +0 -0
  118. {execsql2-2.11.0 → execsql2-2.12.0}/scripts/generate_vscode_grammar.py +0 -0
  119. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/__init__.py +0 -0
  120. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/__main__.py +0 -0
  121. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/cli/dsn.py +0 -0
  122. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/cli/help.py +0 -0
  123. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/config.py +0 -0
  124. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/constants.py +0 -0
  125. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/__init__.py +0 -0
  126. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/access.py +0 -0
  127. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/base.py +0 -0
  128. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/dsn.py +0 -0
  129. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/duckdb.py +0 -0
  130. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/factory.py +0 -0
  131. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/firebird.py +0 -0
  132. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/mysql.py +0 -0
  133. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/oracle.py +0 -0
  134. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/postgres.py +0 -0
  135. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/sqlite.py +0 -0
  136. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/db/sqlserver.py +0 -0
  137. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exceptions.py +0 -0
  138. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/__init__.py +0 -0
  139. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/base.py +0 -0
  140. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/delimited.py +0 -0
  141. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/duckdb.py +0 -0
  142. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/feather.py +0 -0
  143. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/html.py +0 -0
  144. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/json.py +0 -0
  145. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/latex.py +0 -0
  146. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/markdown.py +0 -0
  147. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/ods.py +0 -0
  148. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/parquet.py +0 -0
  149. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/pretty.py +0 -0
  150. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/protocol.py +0 -0
  151. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/raw.py +0 -0
  152. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/sqlite.py +0 -0
  153. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/templates.py +0 -0
  154. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/values.py +0 -0
  155. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/xls.py +0 -0
  156. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/xml.py +0 -0
  157. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/exporters/zip.py +0 -0
  158. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/format.py +0 -0
  159. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/gui/__init__.py +0 -0
  160. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/gui/base.py +0 -0
  161. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/gui/console.py +0 -0
  162. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/gui/desktop.py +0 -0
  163. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/gui/tui.py +0 -0
  164. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/importers/__init__.py +0 -0
  165. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/importers/base.py +0 -0
  166. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/importers/csv.py +0 -0
  167. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/importers/feather.py +0 -0
  168. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/importers/ods.py +0 -0
  169. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/importers/xls.py +0 -0
  170. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/conditions.py +1 -1
  171. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/connect.py +0 -0
  172. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/data.py +0 -0
  173. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/io.py +0 -0
  174. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/io_export.py +0 -0
  175. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/io_fileops.py +0 -0
  176. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/io_import.py +0 -0
  177. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/io_write.py +0 -0
  178. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/prompt.py +0 -0
  179. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/script_ext.py +0 -0
  180. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/metacommands/system.py +0 -0
  181. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/models.py +0 -0
  182. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/parser.py +0 -0
  183. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/py.typed +0 -0
  184. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/script/__init__.py +0 -0
  185. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/script/control.py +0 -0
  186. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/script/variables.py +0 -0
  187. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/types.py +0 -0
  188. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/__init__.py +0 -0
  189. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/auth.py +0 -0
  190. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/crypto.py +0 -0
  191. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/datetime.py +0 -0
  192. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/errors.py +0 -0
  193. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/fileio.py +0 -0
  194. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/mail.py +0 -0
  195. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/numeric.py +0 -0
  196. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/regex.py +0 -0
  197. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/strings.py +0 -0
  198. {execsql2-2.11.0 → execsql2-2.12.0}/src/execsql/utils/timer.py +0 -0
  199. {execsql2-2.11.0 → execsql2-2.12.0}/templates/README.md +0 -0
  200. {execsql2-2.11.0 → execsql2-2.12.0}/templates/config_settings.sqlite +0 -0
  201. {execsql2-2.11.0 → execsql2-2.12.0}/templates/example_config_prompt.sql +0 -0
  202. {execsql2-2.11.0 → execsql2-2.12.0}/templates/execsql.conf +0 -0
  203. {execsql2-2.11.0 → execsql2-2.12.0}/templates/make_config_db.sql +0 -0
  204. {execsql2-2.11.0 → execsql2-2.12.0}/templates/md_compare.sql +0 -0
  205. {execsql2-2.11.0 → execsql2-2.12.0}/templates/md_glossary.sql +0 -0
  206. {execsql2-2.11.0 → execsql2-2.12.0}/templates/md_upsert.sql +0 -0
  207. {execsql2-2.11.0 → execsql2-2.12.0}/templates/pg_compare.sql +0 -0
  208. {execsql2-2.11.0 → execsql2-2.12.0}/templates/pg_glossary.sql +0 -0
  209. {execsql2-2.11.0 → execsql2-2.12.0}/templates/pg_upsert.sql +0 -0
  210. {execsql2-2.11.0 → execsql2-2.12.0}/templates/script_template.sql +0 -0
  211. {execsql2-2.11.0 → execsql2-2.12.0}/templates/ss_compare.sql +0 -0
  212. {execsql2-2.11.0 → execsql2-2.12.0}/templates/ss_glossary.sql +0 -0
  213. {execsql2-2.11.0 → execsql2-2.12.0}/templates/ss_upsert.sql +0 -0
  214. {execsql2-2.11.0 → execsql2-2.12.0}/tests/__init__.py +0 -0
  215. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/__init__.py +0 -0
  216. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/test_cli.py +0 -0
  217. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/test_cli_e2e.py +0 -0
  218. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/test_cli_run.py +0 -0
  219. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/test_lint.py +0 -0
  220. {execsql2-2.11.0 → execsql2-2.12.0}/tests/cli/test_ping.py +0 -0
  221. {execsql2-2.11.0 → execsql2-2.12.0}/tests/conftest.py +0 -0
  222. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/__init__.py +0 -0
  223. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/test_base.py +0 -0
  224. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/test_duckdb.py +0 -0
  225. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/test_factory.py +0 -0
  226. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/test_postgres.py +0 -0
  227. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/test_sqlite.py +0 -0
  228. {execsql2-2.11.0 → execsql2-2.12.0}/tests/db/test_sqlite_extra.py +0 -0
  229. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/__init__.py +0 -0
  230. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_base.py +0 -0
  231. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_db.py +0 -0
  232. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_delimited.py +0 -0
  233. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_duckdb_exporter.py +0 -0
  234. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_exporters.py +0 -0
  235. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_feather.py +0 -0
  236. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_html_latex.py +0 -0
  237. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_json.py +0 -0
  238. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_markdown.py +0 -0
  239. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_ods.py +0 -0
  240. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_parquet.py +0 -0
  241. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_sqlite_exporter.py +0 -0
  242. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_templates.py +0 -0
  243. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_xls_xlsx.py +0 -0
  244. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_xlsx.py +0 -0
  245. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_xml.py +0 -0
  246. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_yaml.py +0 -0
  247. {execsql2-2.11.0 → execsql2-2.12.0}/tests/exporters/test_zip.py +0 -0
  248. {execsql2-2.11.0 → execsql2-2.12.0}/tests/gui/__init__.py +0 -0
  249. {execsql2-2.11.0 → execsql2-2.12.0}/tests/importers/__init__.py +0 -0
  250. {execsql2-2.11.0 → execsql2-2.12.0}/tests/importers/test_feather_importer.py +0 -0
  251. {execsql2-2.11.0 → execsql2-2.12.0}/tests/importers/test_ods_importer.py +0 -0
  252. {execsql2-2.11.0 → execsql2-2.12.0}/tests/importers/test_xls_importer.py +0 -0
  253. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/__init__.py +0 -0
  254. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/conftest.py +0 -0
  255. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/test_dsn.py +0 -0
  256. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/test_duckdb.py +0 -0
  257. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/test_mysql.py +0 -0
  258. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/test_postgres.py +0 -0
  259. {execsql2-2.11.0 → execsql2-2.12.0}/tests/integration/test_sqlite.py +0 -0
  260. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/__init__.py +0 -0
  261. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_assert.py +0 -0
  262. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_connect.py +0 -0
  263. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands.py +0 -0
  264. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_connect.py +0 -0
  265. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  266. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_io.py +0 -0
  267. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  268. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  269. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_system.py +0 -0
  270. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  271. {execsql2-2.11.0 → execsql2-2.12.0}/tests/metacommands/test_row_count.py +0 -0
  272. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_config.py +0 -0
  273. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_config_data.py +0 -0
  274. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_constants.py +0 -0
  275. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_engine.py +0 -0
  276. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_error_messages.py +0 -0
  277. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_exceptions.py +0 -0
  278. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_format.py +0 -0
  279. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_mail.py +0 -0
  280. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_models.py +0 -0
  281. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_package.py +0 -0
  282. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_parser.py +0 -0
  283. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_registry.py +0 -0
  284. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_script.py +0 -0
  285. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_state.py +0 -0
  286. {execsql2-2.11.0 → execsql2-2.12.0}/tests/test_types.py +0 -0
  287. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/__init__.py +0 -0
  288. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_auth.py +0 -0
  289. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_auth_extra.py +0 -0
  290. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_crypto.py +0 -0
  291. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_datetime.py +0 -0
  292. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_errors.py +0 -0
  293. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_errors_extra.py +0 -0
  294. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_fileio.py +0 -0
  295. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_fileio_extra.py +0 -0
  296. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_numeric.py +0 -0
  297. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_regex.py +0 -0
  298. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_strings.py +0 -0
  299. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_timer.py +0 -0
  300. {execsql2-2.11.0 → execsql2-2.12.0}/tests/utils/test_timer_extra.py +0 -0
  301. {execsql2-2.11.0 → execsql2-2.12.0}/zensical.toml +0 -0
@@ -67,6 +67,9 @@ src/execsql/ # active codebase — all new work goes here
67
67
  importers/
68
68
  __init__.py
69
69
  base.py / csv.py / ods.py / xls.py / feather.py
70
+ debug/
71
+ __init__.py # debug package
72
+ repl.py # x_breakpoint, _debug_repl, REPL helpers (BREAKPOINT metacommand)
70
73
  metacommands/
71
74
  __init__.py # DISPATCH_TABLE, format/db constants, re-exports all handlers
72
75
  dispatch.py # build_dispatch_table() — all mcl.add() regex registrations
@@ -189,10 +192,10 @@ Triggered on: push to `main`, any tag `v*.*.*`, pull requests.
189
192
 
190
193
  ## Versioning
191
194
 
192
- `bump-my-version` manages versions. Current: `2.5.0`. Bump commands:
195
+ `bump-my-version` manages versions. Current: `2.11.0`. Bump commands:
193
196
 
194
- - `just bump-patch` → 2.5.0 → 2.5.1
195
- - `just bump-minor` → 2.5.0 → 2.6.0
197
+ - `just bump-patch` → 2.11.0 → 2.11.1
198
+ - `just bump-minor` → 2.11.0 → 2.12.0
196
199
  Bumps commit + tag. Pre-commit hook runs `uv lock` + stages `uv.lock`.
197
200
 
198
201
  ## Ruff Config
@@ -254,16 +257,32 @@ the foreseeable future.
254
257
  | Dispatch optimization already in place (verified) | 2.5.0 | 2026-04 |
255
258
  | PostgreSQL integration tests (9 tests, CI Docker) | 2.5.0 | 2026-04 |
256
259
  | MySQL integration tests (9 tests, CI Docker) | 2.5.0 | 2026-04 |
260
+ | `RuntimeContext` refactor + module proxy | 2.6.0 | 2026-04 |
261
+ | `noqa` cleanup (180 suppressions removed) | 2.6.0 | 2026-04 |
262
+ | Coverage raised to 86% (3010 tests) | 2.6.0 | 2026-04 |
263
+ | Markdown, YAML, XLSX export formats | 2.7.0 | 2026-04 |
264
+ | `ASSERT` metacommand | 2.8.0 | 2026-04 |
265
+ | `--dry-run` variable expansion | 2.8.0 | 2026-04 |
266
+ | `--profile` per-statement timing | 2.8.0 | 2026-04 |
267
+ | `--ping` connectivity test | 2.9.0 | 2026-04 |
268
+ | `--lint` static script analysis | 2.9.0 | 2026-04 |
269
+ | Configuration docs audit (6 fixes) | 2.9.0 | 2026-04 |
270
+ | `ROW_COUNT_GT/GTE/EQ/LT` conditionals | 2.10.0 | 2026-04 |
271
+ | `BREAKPOINT` debug REPL with step mode | 2.10.0 | 2026-04 |
272
+ | Error messages restored (script location, command) | 2.11.0 | 2026-04 |
273
+ | 16-item codebase analysis fix sweep | 2.11.x | 2026-04 |
257
274
 
258
275
  ______________________________________________________________________
259
276
 
260
- ### v2.6 — Architecture & Internal Quality
277
+ ### Completed milestone detail (v2.6–v2.11)
261
278
 
262
- - [x] **`state.py` → `RuntimeContext` refactor** 33 mutable globals consolidated into a slotted `RuntimeContext` class with transparent module proxy. `get_context()`/`set_context()` API added. Zero external call-site changes.
263
- - [x] **`noqa` cleanup in `metacommands/__init__.py`** — removed all 180 redundant `# noqa` comments; `__all__` already satisfies ruff F401.
264
- - [x] **Coverage push to 86%** — 403 new tests (3010 total) covering `db/base.py` (55→99%), `metacommands/connect.py` (36→100%), `script/engine.py` (77→95%), `exporters/delimited.py` (76→95%). Remaining gap to 90% is in GUI, ODS, and import handlers.
279
+ #### v2.6Architecture & Internal Quality
265
280
 
266
- ### v2.7 New Export/Import Formats
281
+ - [x] `state.py` `RuntimeContext` refactor
282
+ - [x] `noqa` cleanup in `metacommands/__init__.py`
283
+ - [x] Coverage push to 86%
284
+
285
+ #### v2.7 — New Export/Import Formats
267
286
 
268
287
  - [x] **Parquet import** — already existed as `IMPORT TO table FROM PARQUET file` (verified present).
269
288
  - [x] **YAML export** — `FORMAT YAML` via PyYAML, list-of-dicts with native type preservation.
@@ -13,6 +13,46 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.12.0] - 2026-04-01
17
+
18
+ ### Added
19
+
20
+ - Debug REPL `.where` / `.w` command — shows current script file, line number, and the upcoming statement text (truncated to 120 chars). The entry banner now includes the location (`[Breakpoint] myscript.sql:42`) and `_print_where()` is called automatically on REPL entry via `BREAKPOINT` or step mode.
21
+ - Debug REPL `.set VAR VAL` / `.s VAR VAL` command — sets or updates a substitution variable interactively during a `BREAKPOINT` session. Prints a confirmation line (`VAR = VAL`) on success; prints an error if substitution variables are not initialised.
22
+ - Debug REPL ANSI color output — horizontal rule separators, colored labels ("Breakpoint"/"Step" in bold yellow, filename:line in cyan, type tags in dim green), cyan variable names, dim `=` signs, red error messages, bold SQL column headers, and dim row-count and table borders. Color is auto-detected via TTY and suppressed when `NO_COLOR` or `EXECSQL_NO_COLOR` environment variables are set. Falls back to plain text in non-interactive contexts (CI, piped output). Help text is also colorized with cyan command names and consistent column alignment.
23
+ - Debug REPL shortcut aliases — `.h` for `.help`, `.v` for `.vars`, `.v all` for `.vars all`.
24
+ - Debug REPL step mode banner — when the REPL is re-entered via `.next` / step mode, the entry banner now shows "Step" instead of "Breakpoint" to make it clear the pause is from stepping rather than an explicit `BREAKPOINT` metacommand.
25
+ - `--profile-limit N` CLI option — controls how many top statements appear in the `--profile` timing summary (default: 20). The "not shown" footer message now includes the active limit for clarity.
26
+ - Test coverage raised from 86% to 91% — 274 new tests across `metacommands/io_import.py`, `metacommands/io_export.py`, `metacommands/control.py`, `metacommands/data.py`, `importers/csv.py`, and `gui/console.py`. Coverage floor raised from 85% to 90% in `pyproject.toml`.
27
+
28
+ ### Changed
29
+
30
+ - `execsql.debug.repl` is now a dedicated package (`src/execsql/debug/repl.py`); previously the REPL lived at `execsql.metacommands.debug_repl`. Internal import paths have been updated throughout. No public API change.
31
+
32
+ ______________________________________________________________________
33
+
34
+ ## [2.11.1] - 2026-04-01
35
+
36
+ ### Fixed
37
+
38
+ - `x_assert` crash when `exec_log` is None — added null guard on `log_user_msg()` call.
39
+ - `--ping` version-query loop exiting prematurely — `break` was at wrong indentation, skipping fallback queries when the first query returned no rows.
40
+ - `CONSOLE SET WIDTH/HEIGHT` crash — `gui_console_width()`/`gui_console_height()` restored as setter functions with GUI console propagation.
41
+ - `$ERROR_MESSAGE` now contains full `errmsg()` (with script location and timestamp) for non-halting errors.
42
+ - Non-halting SQL and metacommand errors now logged to exec_log.
43
+ - `x_debug_log_subvars` log format — was printing full tuple instead of name/value for local variables.
44
+ - Dead `endloop()` removed from `control.py` — `state.endloop()` is canonical.
45
+ - YAML `append=True` now emits `---` document separator for valid multi-document streams.
46
+ - REPL dot-command parsing consistency between dispatcher and exit-check.
47
+ - `__delattr__` on state proxy uses cached `_DEFAULT_CTX` instead of allocating per call.
48
+ - `write_query_to_xlsx` single-sheet now updates `export_metadata`.
49
+ - `isinstance()` used instead of `type()` equality in `MetaCommandList.add()`.
50
+ - Module docstrings in `conditions.py` and `control.py` moved before imports.
51
+ - FEATHER divergence doc corrected — `polars` only, not `polars + pyarrow`.
52
+ - README pre-commit rev updated to `v2.11.0`; options table completed.
53
+
54
+ ______________________________________________________________________
55
+
16
56
  ## [2.11.0] - 2026-04-01
17
57
 
18
58
  ### Added
@@ -37,13 +37,13 @@ A multi-agent system where specialized agents collaborate to improve, extend, de
37
37
  1. **Research** — Oracle investigates codebase, finds relevant code paths and impact
38
38
  1. **Plan** — DBA synthesizes research into implementation approach, aligns with human
39
39
  1. **Implement** — Patcher writes code, Oracle advises on architecture
40
- 1. **Test** — QA writes/runs tests, verifies coverage stays above 80%
40
+ 1. **Test** — QA writes/runs tests, verifies coverage stays above 90%
41
41
  1. **Document** — Scribe updates docs, Herald updates changelog
42
42
  1. **Review** — Inspector does final code review before human merge
43
43
 
44
44
  ## Constraints
45
45
 
46
- - Coverage floor (80%) must be maintained — QA blocks any change that drops it
46
+ - Coverage floor (90%) must be maintained — QA blocks any change that drops it
47
47
  - Backwards compatibility with upstream execsql v1.130.1 unless explicitly approved
48
48
  - No destructive git operations without human approval
49
49
  - Agents should always read `.claude/project_context.md` before starting work
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.11.0
3
+ Version: 2.12.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: Repository, https://github.com/geocoug/execsql
6
6
  Project-URL: Issues, https://github.com/geocoug/execsql/issues
@@ -220,8 +220,11 @@ execsql script.sql # read connection from config file
220
220
  | `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
221
221
  | `-w` | Skip password prompt when a username is supplied |
222
222
  | `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
223
+ | `--output-dir DIR` | Default base directory for EXPORT output files |
223
224
  | `--dry-run` | Parse the script and report commands without executing |
224
225
  | `--lint` | Static analysis: check structure and warn on issues (no DB) |
226
+ | `--ping` | Test database connectivity and exit |
227
+ | `--profile` | Show per-statement timing summary after execution |
225
228
  | `--progress` | Show a progress bar for long-running IMPORT operations |
226
229
  | `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
227
230
  | `--dump-keywords` | Print metacommand keywords as JSON and exit |
@@ -298,7 +301,7 @@ execsql-format --check scripts/
298
301
  ```yaml
299
302
  repos:
300
303
  - repo: https://github.com/geocoug/execsql
301
- rev: v2.4.4
304
+ rev: v2.11.0
302
305
  hooks:
303
306
  - id: execsql-format
304
307
  args: [--in-place]
@@ -110,8 +110,11 @@ execsql script.sql # read connection from config file
110
110
  | `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
111
111
  | `-w` | Skip password prompt when a username is supplied |
112
112
  | `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
113
+ | `--output-dir DIR` | Default base directory for EXPORT output files |
113
114
  | `--dry-run` | Parse the script and report commands without executing |
114
115
  | `--lint` | Static analysis: check structure and warn on issues (no DB) |
116
+ | `--ping` | Test database connectivity and exit |
117
+ | `--profile` | Show per-statement timing summary after execution |
115
118
  | `--progress` | Show a progress bar for long-running IMPORT operations |
116
119
  | `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
117
120
  | `--dump-keywords` | Print metacommand keywords as JSON and exit |
@@ -188,7 +191,7 @@ execsql-format --check scripts/
188
191
  ```yaml
189
192
  repos:
190
193
  - repo: https://github.com/geocoug/execsql
191
- rev: v2.4.4
194
+ rev: v2.11.0
192
195
  hooks:
193
196
  - id: execsql-format
194
197
  args: [--in-place]
@@ -24,7 +24,8 @@ ______________________________________________________________________
24
24
  | `--gui-framework` | Select GUI backend: `tkinter` (default) or `textual` (terminal UI). |
25
25
  | `--debug` | Start in step-through debug mode. The debug REPL pauses before each statement, as if `BREAKPOINT` were at the top with `.next` always active. |
26
26
  | `--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. |
27
- | `--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. Top 20 slowest statements are shown. |
27
+ | `--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. |
28
+ | `--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. |
28
29
  | `--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. |
29
30
  | `--lint` | Parse the script and perform static analysis without connecting to a database. Reports unmatched IF/ENDIF, LOOP/END LOOP, and BEGIN BATCH/END BATCH blocks (errors), potentially undefined `!!$VAR!!` references (warnings), and missing INCLUDE file targets (warnings). Exits 0 if no errors, 1 if errors found. |
30
31
 
@@ -33,19 +34,19 @@ ______________________________________________________________________
33
34
  | Format | Description |
34
35
  | ----------------- | -------------------------------------------------------------------------------------------------------------------- |
35
36
  | `PARQUET` | Export query or table results to Apache Parquet via `polars`. |
36
- | `FEATHER` | Export to Apache Feather/IPC via `polars` + `pyarrow` (upstream used `pandas`). |
37
+ | `FEATHER` | Export to Apache Feather/IPC via `polars` (upstream used `pandas` + `pyarrow`). |
37
38
  | `YAML` | Export query or table results as a YAML sequence of mappings via `PyYAML`. |
38
39
  | `MARKDOWN` / `MD` | Export query or table results as a GitHub-Flavored Markdown (GFM) pipe table. Pure Python, no optional dependencies. |
39
40
  | `XLSX` | Export query or table results to an Excel XLSX workbook via `openpyxl` (single or multi-sheet). |
40
41
 
41
42
  ### Metacommands
42
43
 
43
- | Metacommand | Description |
44
- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
45
- | `ASSERT` | Evaluate a condition and raise an error (halting the script) if it is false. Supports all IF conditions. Optional quoted failure message. Skipped in false IF blocks. |
46
- | `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. Inspect variables, run ad-hoc SQL, and step through the script. Silently skipped in non-TTY (CI) environments. |
47
- | `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime. |
48
- | `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file. |
44
+ | Metacommand | Description |
45
+ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | `ASSERT` | Evaluate a condition and raise an error (halting the script) if it is false. Supports all IF conditions. Optional quoted failure message. Skipped in false IF blocks. |
47
+ | `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. See [Debugging](#debugging) below for full details. |
48
+ | `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime. |
49
+ | `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file. |
49
50
 
50
51
  ### Conditional Tests
51
52
 
@@ -106,6 +107,46 @@ New options in `execsql.conf`:
106
107
  | `py.typed` marker | PEP 561 marker enabling downstream static type checking. |
107
108
  | Structured keyword registry | `--dump-keywords` introspects the dispatch table and outputs JSON used by the grammar generator and test suite. |
108
109
 
110
+ ### Debugging { #debugging }
111
+
112
+ execsql2 adds a full interactive debugging system that has no equivalent in upstream execsql.
113
+
114
+ **`BREAKPOINT` metacommand** — insert `-- !x! BREAKPOINT` anywhere in a script to pause execution and drop into a debug REPL. The REPL provides a `execsql debug>` prompt where you can inspect state and interact with the database before resuming.
115
+
116
+ **`--debug` CLI flag** — start the script in step-through mode, pausing before every statement as if `BREAKPOINT` were inserted at the top with `.next` always active.
117
+
118
+ **REPL commands** (all dot-prefixed to avoid collisions with variable names and SQL):
119
+
120
+ | Command | Description |
121
+ | ---------------------- | ------------------------------------------------------------------------- |
122
+ | `.continue` / `.c` | Resume normal script execution |
123
+ | `.abort` / `.q` | Halt the script with exit status 1 |
124
+ | `.vars` / `.v` | List all user, system, local, and counter variables (grouped by type) |
125
+ | `.vars all` / `.v all` | Include environment variables (`&`) in the listing |
126
+ | `.next` / `.n` | Execute the next statement, then pause again (step mode) |
127
+ | `.where` / `.w` | Show the current script file, line number, and upcoming statement text |
128
+ | `.stack` | Show the command-list stack (script name, cursor position, nesting depth) |
129
+ | `.set VAR VAL` / `.s` | Set or update a substitution variable; prints confirmation on success |
130
+ | `.help` / `.h` | Show available commands |
131
+
132
+ **Non-prefixed input** is interpreted as either a variable lookup or ad-hoc SQL:
133
+
134
+ | Input | Behavior |
135
+ | ------------------------------ | -------------------------------------------------------------------------------- |
136
+ | `logfile` | Print the value of the `logfile` substitution variable |
137
+ | `$ARG_1` | Print the value of a system/built-in variable |
138
+ | `SELECT count(*) FROM orders;` | Execute SQL against the current database and pretty-print the results in a table |
139
+
140
+ **Key behaviors:**
141
+
142
+ - **Non-interactive safety** — `BREAKPOINT` is silently skipped when `sys.stdin` is not a TTY (CI pipelines, piped input, cron). Scripts never hang in automation.
143
+ - **Ad-hoc SQL** — any input ending with `;` is executed against the current database connection using the same cursor and transaction state as the script. Queries return a formatted table; DML statements (INSERT, UPDATE, DELETE) execute and commit/rollback according to the current autocommit and batch settings.
144
+ - **Variable inspection** — bare names look up user-defined variables (e.g., `logfile`); sigil-prefixed names look up system (`$`), environment (`&`), local (`~`), or counter (`@`) variables. If a `$`-prefixed name isn't found, the REPL strips the sigil and retries (since `SUB` stores keys without a prefix).
145
+ - **Step mode** — `.next` executes exactly one statement then re-enters the REPL. When stepping, the entry banner shows "Step" instead of "Breakpoint" to distinguish stepping from an explicit `BREAKPOINT`. Combined with `.where`, `.vars`, and SQL queries, this allows line-by-line script debugging with full state visibility.
146
+ - **Location display** — on entry to the REPL (via `BREAKPOINT` or step mode) the banner shows a horizontal rule with the label ("Breakpoint" or "Step"), the current filename and line number, and the upcoming statement. Use `.where` (or `.w`) at any time to re-display this information.
147
+ - **ANSI color output** — the REPL uses ANSI color on TTY outputs: bold yellow for section labels, cyan for filenames and variable names, dim for separators and `=` signs, red for error messages, bold for SQL column headers, and dim italic for `NULL` values. Color is suppressed when `NO_COLOR` or `EXECSQL_NO_COLOR` environment variables are set, or when the output stream is not a TTY.
148
+ - **Readline support** — on platforms where `readline` is available (macOS, Linux), the REPL supports arrow-key history navigation and line editing.
149
+
109
150
  ______________________________________________________________________
110
151
 
111
152
  ## Changed Behavior
@@ -38,6 +38,47 @@ DEBUG WRITE <script_name> [[APPEND] TO <filename>]
38
38
 
39
39
  This is an alias for the [WRITE SCRIPT](../reference/metacommands.md#write_script) metacommand.
40
40
 
41
+ # Interactive Debug REPL (BREAKPOINT)
42
+
43
+ Insert `-- !x! BREAKPOINT` anywhere in a script to pause execution and drop into the interactive debug REPL:
44
+
45
+ ```sql
46
+ -- !x! BREAKPOINT
47
+ SELECT * FROM orders WHERE status = 'pending';
48
+ ```
49
+
50
+ The REPL prints the current file name, line number, and the upcoming statement when it opens:
51
+
52
+ ```
53
+ [Breakpoint] myscript.sql:42 — Script paused. Type '.help' for commands, '.c' to resume.
54
+ myscript.sql:42 (sql)
55
+ → SELECT * FROM orders WHERE status = 'pending';
56
+ execsql debug>
57
+ ```
58
+
59
+ **Available commands:**
60
+
61
+ | Command | Shortcut | Description |
62
+ | -------------- | -------- | ------------------------------------------------------------------------- |
63
+ | `.continue` | `.c` | Resume normal script execution |
64
+ | `.abort` | `.q` | Halt the script with exit status 1 |
65
+ | `.vars` | `.v` | List user, system, local, and counter substitution variables |
66
+ | `.vars all` | `.v all` | Include environment variables (`&`) in the listing |
67
+ | `.next` | `.n` | Execute the next statement, then pause again (step mode) |
68
+ | `.where` | `.w` | Re-display the current script location and upcoming statement |
69
+ | `.stack` | | Show the command-list stack (script name, cursor position, nesting depth) |
70
+ | `.set VAR VAL` | `.s` | Set or update a substitution variable |
71
+ | `.help` | `.h` | Show available commands |
72
+
73
+ Anything not starting with `.` is treated as a variable lookup or SQL:
74
+
75
+ - A bare name (e.g. `logfile`) prints the value of that substitution variable.
76
+ - Any input ending with `;` is executed as SQL against the current database (expects columns returned, e.g. SELECT).
77
+
78
+ The `--debug` CLI flag starts execution in step mode, pausing before every statement.
79
+
80
+ In non-interactive environments (CI, piped input) `BREAKPOINT` is silently skipped so automated pipelines are never blocked.
81
+
41
82
  The ON ERROR_HALT metacommands allow custom reporting (or cleanup) actions to be taken when errors occur.
42
83
 
43
84
  Setting the configuration setting [write_warnings](../reference/configuration.md#write_warnings) to "Yes" can also assist with debugging by displaying conditions that may result from errors in the script.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execsql2"
7
- version = "2.11.0"
7
+ version = "2.12.0"
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" }
@@ -158,7 +158,7 @@ skip-magic-trailing-comma = false
158
158
  line-ending = "auto"
159
159
 
160
160
  [tool.bumpversion]
161
- current_version = "2.11.0"
161
+ current_version = "2.12.0"
162
162
  commit = true
163
163
  commit_args = "--no-verify"
164
164
  tag = true
@@ -182,7 +182,7 @@ addopts = [
182
182
  "--cov=execsql",
183
183
  "--cov-branch",
184
184
  "--cov-report=xml",
185
- "--cov-fail-under=85",
185
+ "--cov-fail-under=90",
186
186
  "--strict-markers",
187
187
  "--strict-config",
188
188
  "--color=yes",
@@ -278,6 +278,11 @@ def main(
278
278
  "--profile",
279
279
  help="Record per-statement execution times and print a timing summary after the script completes.",
280
280
  ),
281
+ profile_limit: int = typer.Option(
282
+ 20,
283
+ "--profile-limit",
284
+ help="Number of top statements to show in the --profile timing summary (default: 20).",
285
+ ),
281
286
  debug: bool = typer.Option(
282
287
  False,
283
288
  "--debug",
@@ -449,6 +454,7 @@ def main(
449
454
  output_dir=output_dir,
450
455
  progress=progress,
451
456
  profile=profile,
457
+ profile_limit=profile_limit,
452
458
  ping=ping,
453
459
  lint=lint,
454
460
  debug=debug,
@@ -194,7 +194,7 @@ def _lint_cmdlist(
194
194
  for cmd in cmdlist.cmdlist:
195
195
  src = cmd.source
196
196
  lno = cmd.line_no
197
- stmt = cmd.command.statement if cmd.command_type == "sql" else cmd.command.statement
197
+ stmt = cmd.command.statement
198
198
 
199
199
  if cmd.command_type == "sql":
200
200
  # SQL statements: check for variable references only
@@ -8,6 +8,7 @@ and drives the main execution loop. Separated from argument parsing
8
8
  from __future__ import annotations
9
9
 
10
10
  import atexit
11
+ from typing import Any
11
12
  import datetime
12
13
  import getpass
13
14
  import os
@@ -70,12 +71,14 @@ def _print_dry_run(cmdlist: object) -> None:
70
71
  # ---------------------------------------------------------------------------
71
72
 
72
73
 
73
- def _print_profile(profile_data: list[tuple]) -> None:
74
+ def _print_profile(profile_data: list[tuple], limit: int = 20) -> None:
74
75
  """Print a per-statement timing summary to stdout.
75
76
 
76
77
  Args:
77
78
  profile_data: List of ``(source, line_no, command_type, elapsed_secs,
78
79
  command_text_preview)`` tuples collected during execution.
80
+ limit: Maximum number of top statements to display (default: 20).
81
+ All statements are included in totals regardless of this value.
79
82
  """
80
83
  if not profile_data:
81
84
  _console.print("[dim]Profile: no statements recorded.[/dim]")
@@ -84,9 +87,9 @@ def _print_profile(profile_data: list[tuple]) -> None:
84
87
  total_secs = sum(row[3] for row in profile_data)
85
88
  n = len(profile_data)
86
89
 
87
- # Sort descending by elapsed time; show top 20 (or all if <= 20).
90
+ # Sort descending by elapsed time; show top `limit` (or all if <= limit).
88
91
  sorted_data = sorted(profile_data, key=lambda r: r[3], reverse=True)
89
- display = sorted_data[:20]
92
+ display = sorted_data[:limit]
90
93
 
91
94
  _console.print()
92
95
  _console.print(f"[bold cyan]Profile:[/bold cyan] {n} statement{'s' if n != 1 else ''} in {total_secs:.3f}s")
@@ -114,10 +117,10 @@ def _print_profile(profile_data: list[tuple]) -> None:
114
117
  f"{preview_short}",
115
118
  )
116
119
 
117
- if len(sorted_data) > 20:
118
- omitted = len(sorted_data) - 20
120
+ if len(sorted_data) > limit:
121
+ omitted = len(sorted_data) - limit
119
122
  _console.print(
120
- f"[dim] ... {omitted} more statement{'s' if omitted != 1 else ''} not shown (top 20 by time)[/dim]",
123
+ f"[dim] ... {omitted} more statement{'s' if omitted != 1 else ''} not shown (top {limit} by time)[/dim]",
121
124
  )
122
125
 
123
126
  _console.print()
@@ -128,7 +131,7 @@ def _print_profile(profile_data: list[tuple]) -> None:
128
131
  # ---------------------------------------------------------------------------
129
132
 
130
133
 
131
- def _ping_db(db) -> None:
134
+ def _ping_db(db: Any) -> None:
132
135
  """Test connectivity for *db*, print connection details, and exit.
133
136
 
134
137
  Attempts to execute ``SELECT version()`` (or ``SELECT sqlite_version()``
@@ -156,7 +159,7 @@ def _ping_db(db) -> None:
156
159
  curs.close()
157
160
  if row and row[0]:
158
161
  version_str = str(row[0]).split("\n")[0].strip()
159
- break
162
+ break
160
163
  except Exception:
161
164
  continue
162
165
 
@@ -212,6 +215,7 @@ def _run(
212
215
  output_dir: str | None = None,
213
216
  progress: bool = False,
214
217
  profile: bool = False,
218
+ profile_limit: int = 20,
215
219
  ping: bool = False,
216
220
  lint: bool = False,
217
221
  debug: bool = False,
@@ -551,7 +555,7 @@ def _run(
551
555
  if debug:
552
556
  _state.step_mode = True
553
557
 
554
- _execute_script_direct(conf, profile=profile)
558
+ _execute_script_direct(conf, profile=profile, profile_limit=profile_limit)
555
559
 
556
560
 
557
561
  # ---------------------------------------------------------------------------
@@ -610,7 +614,7 @@ def _execute_script_textual_console(conf: ConfigData) -> None:
610
614
  _state.exec_log.log_exit_end()
611
615
 
612
616
 
613
- def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
617
+ def _execute_script_direct(conf: ConfigData, *, profile: bool = False, profile_limit: int = 20) -> None:
614
618
  """Run runscripts() in the current (main) thread — used when Textual is not active.
615
619
 
616
620
  Args:
@@ -618,6 +622,8 @@ def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
618
622
  profile: When ``True``, print a per-statement timing summary after the
619
623
  script completes. Timing data must already have been activated on
620
624
  ``_state.profile_data`` before this function is called.
625
+ profile_limit: Maximum number of top statements to display in the
626
+ profile summary (default: 20).
621
627
  """
622
628
  import execsql.state as _state
623
629
  import execsql.utils.gui as _gui
@@ -644,7 +650,7 @@ def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
644
650
  gui_console_off()
645
651
  _state.exec_log.log_status_info(f"{_state.cmds_run} commands run")
646
652
  if profile and _state.profile_data is not None:
647
- _print_profile(_state.profile_data)
653
+ _print_profile(_state.profile_data, limit=profile_limit)
648
654
  sys.exit(exc.code)
649
655
  except ConfigError:
650
656
  raise
@@ -672,7 +678,7 @@ def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
672
678
  gui_console_off()
673
679
  _state.exec_log.log_status_info(f"{_state.cmds_run} commands run")
674
680
  if profile and _state.profile_data is not None:
675
- _print_profile(_state.profile_data)
681
+ _print_profile(_state.profile_data, limit=profile_limit)
676
682
  _state.exec_log.log_exit_end()
677
683
 
678
684
 
@@ -0,0 +1,6 @@
1
+ """Debug utilities for execsql.
2
+
3
+ This package contains interactive debugging tools for execsql scripts:
4
+
5
+ - :mod:`execsql.debug.repl` — the ``BREAKPOINT`` metacommand REPL.
6
+ """