execsql2 2.15.5__tar.gz → 2.15.6__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 (296) hide show
  1. {execsql2-2.15.5 → execsql2-2.15.6}/CHANGELOG.md +8 -0
  2. {execsql2-2.15.5 → execsql2-2.15.6}/PKG-INFO +1 -1
  3. {execsql2-2.15.5 → execsql2-2.15.6}/pyproject.toml +2 -2
  4. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/script/variables.py +36 -0
  5. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_script.py +91 -0
  6. {execsql2-2.15.5 → execsql2-2.15.6}/uv.lock +1 -1
  7. {execsql2-2.15.5 → execsql2-2.15.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  8. {execsql2-2.15.5 → execsql2-2.15.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  9. {execsql2-2.15.5 → execsql2-2.15.6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  10. {execsql2-2.15.5 → execsql2-2.15.6}/.github/workflows/ci-cd.yml +0 -0
  11. {execsql2-2.15.5 → execsql2-2.15.6}/.gitignore +0 -0
  12. {execsql2-2.15.5 → execsql2-2.15.6}/.pre-commit-config.yaml +0 -0
  13. {execsql2-2.15.5 → execsql2-2.15.6}/.pre-commit-hooks.yaml +0 -0
  14. {execsql2-2.15.5 → execsql2-2.15.6}/.python-version +0 -0
  15. {execsql2-2.15.5 → execsql2-2.15.6}/.readthedocs.yaml +0 -0
  16. {execsql2-2.15.5 → execsql2-2.15.6}/CONTRIBUTING.md +0 -0
  17. {execsql2-2.15.5 → execsql2-2.15.6}/LICENSE.txt +0 -0
  18. {execsql2-2.15.5 → execsql2-2.15.6}/NOTICE +0 -0
  19. {execsql2-2.15.5 → execsql2-2.15.6}/README.md +0 -0
  20. {execsql2-2.15.5 → execsql2-2.15.6}/SECURITY.md +0 -0
  21. {execsql2-2.15.5 → execsql2-2.15.6}/docs/about/contributors.md +0 -0
  22. {execsql2-2.15.5 → execsql2-2.15.6}/docs/about/copyright.md +0 -0
  23. {execsql2-2.15.5 → execsql2-2.15.6}/docs/about/divergence.md +0 -0
  24. {execsql2-2.15.5 → execsql2-2.15.6}/docs/api/cli.md +0 -0
  25. {execsql2-2.15.5 → execsql2-2.15.6}/docs/api/db.md +0 -0
  26. {execsql2-2.15.5 → execsql2-2.15.6}/docs/api/exporters.md +0 -0
  27. {execsql2-2.15.5 → execsql2-2.15.6}/docs/api/importers.md +0 -0
  28. {execsql2-2.15.5 → execsql2-2.15.6}/docs/api/index.md +0 -0
  29. {execsql2-2.15.5 → execsql2-2.15.6}/docs/api/metacommands.md +0 -0
  30. {execsql2-2.15.5 → execsql2-2.15.6}/docs/dev/adding_db_adapters.md +0 -0
  31. {execsql2-2.15.5 → execsql2-2.15.6}/docs/dev/adding_exporters.md +0 -0
  32. {execsql2-2.15.5 → execsql2-2.15.6}/docs/dev/adding_importers.md +0 -0
  33. {execsql2-2.15.5 → execsql2-2.15.6}/docs/dev/adding_metacommands.md +0 -0
  34. {execsql2-2.15.5 → execsql2-2.15.6}/docs/dev/architecture.md +0 -0
  35. {execsql2-2.15.5 → execsql2-2.15.6}/docs/getting-started/installation.md +0 -0
  36. {execsql2-2.15.5 → execsql2-2.15.6}/docs/getting-started/requirements.md +0 -0
  37. {execsql2-2.15.5 → execsql2-2.15.6}/docs/getting-started/syntax.md +0 -0
  38. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/debugging.md +0 -0
  39. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/documentation.md +0 -0
  40. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/encoding.md +0 -0
  41. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/examples.md +0 -0
  42. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/formatter.md +0 -0
  43. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/logging.md +0 -0
  44. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/sql_syntax.md +0 -0
  45. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/usage.md +0 -0
  46. {execsql2-2.15.5 → execsql2-2.15.6}/docs/guides/using_scripts.md +0 -0
  47. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/Compare_planets.png +0 -0
  48. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/actions.png +0 -0
  49. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/actions2.png +0 -0
  50. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/checkboxes.png +0 -0
  51. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/connect.b64 +0 -0
  52. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/connect.png +0 -0
  53. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/create_conf.png +0 -0
  54. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/data_error1_screenshot.jpg +0 -0
  55. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/entry_form.png +0 -0
  56. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/execsql_console.png +0 -0
  57. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/execsql_logo_01.png +0 -0
  58. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/fatals.png +0 -0
  59. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/logo_small.png +0 -0
  60. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/pause_terminal.png +0 -0
  61. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/pause_terminal_sm.b64 +0 -0
  62. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/pause_terminal_sm.png +0 -0
  63. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/prompt_compare.png +0 -0
  64. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/set_build_commands.jpg +0 -0
  65. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/unit_conversions.b64 +0 -0
  66. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/unit_conversions_029.png +0 -0
  67. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/unmatched.png +0 -0
  68. {execsql2-2.15.5 → execsql2-2.15.6}/docs/images/vim_execsql_highlight.png +0 -0
  69. {execsql2-2.15.5 → execsql2-2.15.6}/docs/index.md +0 -0
  70. {execsql2-2.15.5 → execsql2-2.15.6}/docs/reference/configuration.md +0 -0
  71. {execsql2-2.15.5 → execsql2-2.15.6}/docs/reference/metacommands.md +0 -0
  72. {execsql2-2.15.5 → execsql2-2.15.6}/docs/reference/security.md +0 -0
  73. {execsql2-2.15.5 → execsql2-2.15.6}/docs/reference/substitution_vars.md +0 -0
  74. {execsql2-2.15.5 → execsql2-2.15.6}/extras/vscode-execsql/README.md +0 -0
  75. {execsql2-2.15.5 → execsql2-2.15.6}/extras/vscode-execsql/package.json +0 -0
  76. {execsql2-2.15.5 → execsql2-2.15.6}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
  77. {execsql2-2.15.5 → execsql2-2.15.6}/justfile +0 -0
  78. {execsql2-2.15.5 → execsql2-2.15.6}/scripts/generate_vscode_grammar.py +0 -0
  79. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/__init__.py +0 -0
  80. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/__main__.py +0 -0
  81. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/cli/__init__.py +0 -0
  82. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/cli/dsn.py +0 -0
  83. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/cli/help.py +0 -0
  84. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/cli/lint.py +0 -0
  85. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/cli/run.py +0 -0
  86. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/config.py +0 -0
  87. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/__init__.py +0 -0
  88. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/access.py +0 -0
  89. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/base.py +0 -0
  90. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/dsn.py +0 -0
  91. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/duckdb.py +0 -0
  92. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/factory.py +0 -0
  93. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/firebird.py +0 -0
  94. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/mysql.py +0 -0
  95. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/oracle.py +0 -0
  96. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/postgres.py +0 -0
  97. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/sqlite.py +0 -0
  98. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/db/sqlserver.py +0 -0
  99. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/debug/__init__.py +0 -0
  100. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/debug/repl.py +0 -0
  101. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exceptions.py +0 -0
  102. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/__init__.py +0 -0
  103. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/base.py +0 -0
  104. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/delimited.py +0 -0
  105. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/duckdb.py +0 -0
  106. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/feather.py +0 -0
  107. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/html.py +0 -0
  108. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/json.py +0 -0
  109. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/latex.py +0 -0
  110. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/markdown.py +0 -0
  111. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/ods.py +0 -0
  112. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/parquet.py +0 -0
  113. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/pretty.py +0 -0
  114. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/protocol.py +0 -0
  115. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/raw.py +0 -0
  116. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/sqlite.py +0 -0
  117. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/templates.py +0 -0
  118. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/values.py +0 -0
  119. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/xls.py +0 -0
  120. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/xlsx.py +0 -0
  121. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/xml.py +0 -0
  122. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/yaml.py +0 -0
  123. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/exporters/zip.py +0 -0
  124. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/format.py +0 -0
  125. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/gui/__init__.py +0 -0
  126. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/gui/base.py +0 -0
  127. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/gui/console.py +0 -0
  128. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/gui/desktop.py +0 -0
  129. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/gui/tui.py +0 -0
  130. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/__init__.py +0 -0
  131. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/base.py +0 -0
  132. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/csv.py +0 -0
  133. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/feather.py +0 -0
  134. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/json.py +0 -0
  135. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/ods.py +0 -0
  136. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/importers/xls.py +0 -0
  137. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/__init__.py +0 -0
  138. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/conditions.py +0 -0
  139. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/connect.py +0 -0
  140. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/control.py +0 -0
  141. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/data.py +0 -0
  142. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/debug.py +0 -0
  143. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/dispatch.py +0 -0
  144. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/io.py +0 -0
  145. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/io_export.py +0 -0
  146. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/io_fileops.py +0 -0
  147. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/io_import.py +0 -0
  148. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/io_write.py +0 -0
  149. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/prompt.py +0 -0
  150. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/script_ext.py +0 -0
  151. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/system.py +0 -0
  152. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/metacommands/upsert.py +0 -0
  153. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/models.py +0 -0
  154. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/parser.py +0 -0
  155. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/py.typed +0 -0
  156. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/script/__init__.py +0 -0
  157. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/script/control.py +0 -0
  158. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/script/engine.py +0 -0
  159. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/state.py +0 -0
  160. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/types.py +0 -0
  161. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/__init__.py +0 -0
  162. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/auth.py +0 -0
  163. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/crypto.py +0 -0
  164. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/datetime.py +0 -0
  165. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/errors.py +0 -0
  166. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/fileio.py +0 -0
  167. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/gui.py +0 -0
  168. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/mail.py +0 -0
  169. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/numeric.py +0 -0
  170. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/regex.py +0 -0
  171. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/strings.py +0 -0
  172. {execsql2-2.15.5 → execsql2-2.15.6}/src/execsql/utils/timer.py +0 -0
  173. {execsql2-2.15.5 → execsql2-2.15.6}/templates/README.md +0 -0
  174. {execsql2-2.15.5 → execsql2-2.15.6}/templates/config_settings.sqlite +0 -0
  175. {execsql2-2.15.5 → execsql2-2.15.6}/templates/example_config_prompt.sql +0 -0
  176. {execsql2-2.15.5 → execsql2-2.15.6}/templates/execsql.conf +0 -0
  177. {execsql2-2.15.5 → execsql2-2.15.6}/templates/make_config_db.sql +0 -0
  178. {execsql2-2.15.5 → execsql2-2.15.6}/templates/md_compare.sql +0 -0
  179. {execsql2-2.15.5 → execsql2-2.15.6}/templates/md_glossary.sql +0 -0
  180. {execsql2-2.15.5 → execsql2-2.15.6}/templates/md_upsert.sql +0 -0
  181. {execsql2-2.15.5 → execsql2-2.15.6}/templates/pg_compare.sql +0 -0
  182. {execsql2-2.15.5 → execsql2-2.15.6}/templates/pg_glossary.sql +0 -0
  183. {execsql2-2.15.5 → execsql2-2.15.6}/templates/pg_upsert.sql +0 -0
  184. {execsql2-2.15.5 → execsql2-2.15.6}/templates/script_template.sql +0 -0
  185. {execsql2-2.15.5 → execsql2-2.15.6}/templates/ss_compare.sql +0 -0
  186. {execsql2-2.15.5 → execsql2-2.15.6}/templates/ss_glossary.sql +0 -0
  187. {execsql2-2.15.5 → execsql2-2.15.6}/templates/ss_upsert.sql +0 -0
  188. {execsql2-2.15.5 → execsql2-2.15.6}/tests/__init__.py +0 -0
  189. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/__init__.py +0 -0
  190. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/test_cli.py +0 -0
  191. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/test_cli_e2e.py +0 -0
  192. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/test_cli_run.py +0 -0
  193. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/test_lint.py +0 -0
  194. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/test_ping.py +0 -0
  195. {execsql2-2.15.5 → execsql2-2.15.6}/tests/cli/test_profile.py +0 -0
  196. {execsql2-2.15.5 → execsql2-2.15.6}/tests/conftest.py +0 -0
  197. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/__init__.py +0 -0
  198. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/test_base.py +0 -0
  199. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/test_duckdb.py +0 -0
  200. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/test_factory.py +0 -0
  201. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/test_postgres.py +0 -0
  202. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/test_sqlite.py +0 -0
  203. {execsql2-2.15.5 → execsql2-2.15.6}/tests/db/test_sqlite_extra.py +0 -0
  204. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/__init__.py +0 -0
  205. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_base.py +0 -0
  206. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_db.py +0 -0
  207. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_delimited.py +0 -0
  208. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_duckdb_exporter.py +0 -0
  209. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_exporters.py +0 -0
  210. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_feather.py +0 -0
  211. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_html_extended.py +0 -0
  212. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_html_latex.py +0 -0
  213. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_json.py +0 -0
  214. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_json_extended.py +0 -0
  215. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_latex_extended.py +0 -0
  216. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_markdown.py +0 -0
  217. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_ods.py +0 -0
  218. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_parquet.py +0 -0
  219. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_pretty_extended.py +0 -0
  220. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_raw_extended.py +0 -0
  221. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_sqlite_exporter.py +0 -0
  222. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_templates.py +0 -0
  223. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_templates_extended.py +0 -0
  224. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_values_extended.py +0 -0
  225. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_xls_xlsx.py +0 -0
  226. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_xlsx.py +0 -0
  227. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_xml.py +0 -0
  228. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_yaml.py +0 -0
  229. {execsql2-2.15.5 → execsql2-2.15.6}/tests/exporters/test_zip.py +0 -0
  230. {execsql2-2.15.5 → execsql2-2.15.6}/tests/gui/__init__.py +0 -0
  231. {execsql2-2.15.5 → execsql2-2.15.6}/tests/gui/test_backends.py +0 -0
  232. {execsql2-2.15.5 → execsql2-2.15.6}/tests/gui/test_compare_stats.py +0 -0
  233. {execsql2-2.15.5 → execsql2-2.15.6}/tests/gui/test_compute_row_diffs.py +0 -0
  234. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/__init__.py +0 -0
  235. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_base_extended.py +0 -0
  236. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_csv_edge_cases.py +0 -0
  237. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_csv_importer.py +0 -0
  238. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_feather_importer.py +0 -0
  239. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_json_importer.py +0 -0
  240. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_ods_importer.py +0 -0
  241. {execsql2-2.15.5 → execsql2-2.15.6}/tests/importers/test_xls_importer.py +0 -0
  242. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/__init__.py +0 -0
  243. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/conftest.py +0 -0
  244. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/test_dsn.py +0 -0
  245. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/test_duckdb.py +0 -0
  246. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/test_mysql.py +0 -0
  247. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/test_postgres.py +0 -0
  248. {execsql2-2.15.5 → execsql2-2.15.6}/tests/integration/test_sqlite.py +0 -0
  249. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/__init__.py +0 -0
  250. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_assert.py +0 -0
  251. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_breakpoint.py +0 -0
  252. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_connect.py +0 -0
  253. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_io_export.py +0 -0
  254. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_io_import.py +0 -0
  255. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands.py +0 -0
  256. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_connect.py +0 -0
  257. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_data.py +0 -0
  258. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_extended.py +0 -0
  259. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  260. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_io.py +0 -0
  261. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  262. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  263. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_system.py +0 -0
  264. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  265. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_pg_upsert.py +0 -0
  266. {execsql2-2.15.5 → execsql2-2.15.6}/tests/metacommands/test_row_count.py +0 -0
  267. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_config.py +0 -0
  268. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_config_data.py +0 -0
  269. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_config_extended.py +0 -0
  270. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_debug_repl.py +0 -0
  271. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_engine.py +0 -0
  272. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_error_messages.py +0 -0
  273. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_exceptions.py +0 -0
  274. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_format.py +0 -0
  275. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_mail.py +0 -0
  276. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_models.py +0 -0
  277. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_package.py +0 -0
  278. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_parser.py +0 -0
  279. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_registry.py +0 -0
  280. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_state.py +0 -0
  281. {execsql2-2.15.5 → execsql2-2.15.6}/tests/test_types.py +0 -0
  282. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/__init__.py +0 -0
  283. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_auth.py +0 -0
  284. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_auth_extra.py +0 -0
  285. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_crypto.py +0 -0
  286. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_datetime.py +0 -0
  287. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_errors.py +0 -0
  288. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_errors_extra.py +0 -0
  289. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_fileio.py +0 -0
  290. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_fileio_extra.py +0 -0
  291. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_numeric.py +0 -0
  292. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_regex.py +0 -0
  293. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_strings.py +0 -0
  294. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_timer.py +0 -0
  295. {execsql2-2.15.5 → execsql2-2.15.6}/tests/utils/test_timer_extra.py +0 -0
  296. {execsql2-2.15.5 → execsql2-2.15.6}/zensical.toml +0 -0
@@ -13,6 +13,14 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.15.6] - 2026-04-16
17
+
18
+ ### Fixed
19
+
20
+ - Nested substitution variable names (e.g., `!!N_!!CHECK_GROUP!!_CHECKS!!`) now resolve correctly, matching original execsql behavior. The single-pass token regex introduced in 2.15.0 could not find inner `!!var!!` tokens embedded within an outer variable name; a per-variable substring fallback now handles this edge case.
21
+
22
+ ______________________________________________________________________
23
+
16
24
  ## [2.15.5] - 2026-04-15
17
25
 
18
26
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: execsql2
3
- Version: 2.15.5
3
+ Version: 2.15.6
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execsql2"
7
- version = "2.15.5"
7
+ version = "2.15.6"
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" }
@@ -164,7 +164,7 @@ skip-magic-trailing-comma = false
164
164
  line-ending = "auto"
165
165
 
166
166
  [tool.bumpversion]
167
- current_version = "2.15.5"
167
+ current_version = "2.15.6"
168
168
  commit = true
169
169
  commit_args = "--no-verify"
170
170
  tag = true
@@ -224,6 +224,11 @@ class SubVarSet:
224
224
  dict. This is O(1) per call instead of O(V) where V is the number of
225
225
  defined variables.
226
226
 
227
+ Falls back to a per-variable substring scan when ``_TOKEN_RX`` finds no
228
+ match — this handles nested variable names like
229
+ ``!!N_!!CHECK_GROUP!!_CHECKS!!`` where the inner ``!!CHECK_GROUP!!``
230
+ must be resolved first.
231
+
227
232
  Returns ``(modified_string, True)`` if a substitution was made, or
228
233
  ``(original_string, False)`` if no variable pattern matched.
229
234
  """
@@ -249,6 +254,37 @@ class SubVarSet:
249
254
  return command_str[: m.start()] + sub + command_str[m.end() :], True
250
255
  # Token found but variable not defined — skip it and keep searching.
251
256
  m = self._TOKEN_RX.search(command_str, m.end())
257
+ # Fallback: per-variable substring scan for nested tokens like
258
+ # !!N_!!CHECK_GROUP!!_CHECKS!! where _TOKEN_RX cannot find the inner
259
+ # variable. Matches original monolith behavior.
260
+ return self._substitute_nested(command_str)
261
+
262
+ def _substitute_nested(self, command_str: str) -> tuple:
263
+ """Scan for any defined variable as a substring — handles nested tokens."""
264
+ for varname, sub in self._subs_dict.items():
265
+ if sub is None:
266
+ sub = ""
267
+ sub = str(sub)
268
+ if os.name != "posix":
269
+ sub = sub.replace("\\", "\\\\")
270
+ pat = re.compile(re.escape(f"!!{varname}!!"), re.I)
271
+ m = pat.search(command_str)
272
+ if m:
273
+ return command_str[: m.start()] + sub + command_str[m.end() :], True
274
+ patq = re.compile(re.escape(f"!'!{varname}!'!"), re.I)
275
+ mq = patq.search(command_str)
276
+ if mq:
277
+ return (
278
+ command_str[: mq.start()] + sub.replace("'", "''") + command_str[mq.end() :],
279
+ True,
280
+ )
281
+ patdq = re.compile(re.escape(f'!"!{varname}!"!'), re.I)
282
+ mdq = patdq.search(command_str)
283
+ if mdq:
284
+ return (
285
+ command_str[: mdq.start()] + '"' + sub + '"' + command_str[mdq.end() :],
286
+ True,
287
+ )
252
288
  return command_str, False
253
289
 
254
290
  def substitute_all(self, any_text: str) -> tuple:
@@ -666,6 +666,97 @@ class TestSubVarSetTokenOptimization:
666
666
  assert changed is True
667
667
  assert result == "resolved"
668
668
 
669
+ def test_nested_variable_name_basic(self):
670
+ """Inner !!VAR!! inside an outer token name resolves correctly."""
671
+ sv = SubVarSet()
672
+ sv.add_substitution("check_group", "Initial")
673
+ sv.add_substitution("n_initial_checks", "12")
674
+ result, changed = sv.substitute_all("!!N_!!CHECK_GROUP!!_CHECKS!!")
675
+ assert changed is True
676
+ assert result == "12"
677
+
678
+ def test_nested_variable_name_dollar_prefix(self):
679
+ """Nested name with $-prefixed variables."""
680
+ sv = SubVarSet()
681
+ sv.add_substitution("$prefix", "Initial")
682
+ sv.add_substitution("$n_initial_checks", "42")
683
+ result, changed = sv.substitute_all("!!$N_!!$PREFIX!!_CHECKS!!")
684
+ assert changed is True
685
+ assert result == "42"
686
+
687
+ def test_nested_variable_name_at_prefix(self):
688
+ """Nested name with @-prefixed variables."""
689
+ sv = SubVarSet()
690
+ sv.add_substitution("@group", "east")
691
+ sv.add_substitution("@region_east_id", "99")
692
+ result, changed = sv.substitute_all("!!@REGION_!!@GROUP!!_ID!!")
693
+ assert changed is True
694
+ assert result == "99"
695
+
696
+ def test_nested_variable_name_no_prefix(self):
697
+ """Nested name with unprefixed variables."""
698
+ sv = SubVarSet()
699
+ sv.add_substitution("part", "mid")
700
+ sv.add_substitution("sec_mid_val", "xyz")
701
+ result, changed = sv.substitute_all("!!SEC_!!PART!!_VAL!!")
702
+ assert changed is True
703
+ assert result == "xyz"
704
+
705
+ def test_nested_variable_name_mixed_context(self):
706
+ """Nested name alongside a normal variable in the same string."""
707
+ sv = SubVarSet()
708
+ sv.add_substitution("idx", "3")
709
+ sv.add_substitution("col_3_name", "addr")
710
+ sv.add_substitution("table", "users")
711
+ result, changed = sv.substitute_all("SELECT !!COL_!!IDX!!_NAME!! FROM !!TABLE!!")
712
+ assert changed is True
713
+ assert result == "SELECT addr FROM users"
714
+
715
+ def test_nested_variable_name_ampersand_prefix(self):
716
+ """Nested name with &-prefixed variables."""
717
+ sv = SubVarSet()
718
+ sv.add_substitution("&slot", "A")
719
+ sv.add_substitution("&val_a_out", "done")
720
+ result, changed = sv.substitute_all("!!&VAL_!!&SLOT!!_OUT!!")
721
+ assert changed is True
722
+ assert result == "done"
723
+
724
+ def test_nested_variable_name_single_quote_form(self):
725
+ """Nested name inside single-quoted form applies apostrophe escaping."""
726
+ sv = SubVarSet()
727
+ sv.add_substitution("group", "east")
728
+ sv.add_substitution("n_east_val", "it's done")
729
+ result, changed = sv.substitute_all("!'!N_!!GROUP!!_VAL!'!")
730
+ assert changed is True
731
+ assert result == "it''s done"
732
+
733
+ def test_nested_variable_name_double_quote_form(self):
734
+ """Nested name inside double-quoted form wraps value in quotes."""
735
+ sv = SubVarSet()
736
+ sv.add_substitution("group", "east")
737
+ sv.add_substitution("n_east_val", "hello")
738
+ result, changed = sv.substitute_all('!"!N_!!GROUP!!_VAL!"!')
739
+ assert changed is True
740
+ assert result == '"hello"'
741
+
742
+ def test_nested_variable_name_undefined_inner(self):
743
+ """Nested name with undefined inner variable stays unchanged."""
744
+ sv = SubVarSet()
745
+ sv.add_substitution("n_something_x", "12")
746
+ result, changed = sv.substitute_all("!!N_!!UNDEFINED!!_X!!")
747
+ assert changed is False
748
+ assert result == "!!N_!!UNDEFINED!!_X!!"
749
+
750
+ def test_nested_variable_name_triple(self):
751
+ """Triple nesting resolves inside-out."""
752
+ sv = SubVarSet()
753
+ sv.add_substitution("c", "X")
754
+ sv.add_substitution("b_x_d", "Y")
755
+ sv.add_substitution("a_y_e", "final")
756
+ result, changed = sv.substitute_all("!!A_!!B_!!C!!_D!!_E!!")
757
+ assert changed is True
758
+ assert result == "final"
759
+
669
760
  def test_non_string_input_returns_unchanged(self):
670
761
  sv = SubVarSet()
671
762
  sv.add_substitution("$x", "val")
@@ -648,7 +648,7 @@ wheels = [
648
648
 
649
649
  [[package]]
650
650
  name = "execsql2"
651
- version = "2.15.5"
651
+ version = "2.15.6"
652
652
  source = { editable = "." }
653
653
  dependencies = [
654
654
  { 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