execsql2 2.25.1__tar.gz → 2.26.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 (456) hide show
  1. {execsql2-2.25.1 → execsql2-2.26.0}/.github/dependabot.yml +5 -0
  2. {execsql2-2.25.1 → execsql2-2.26.0}/.github/workflows/ci-cd.yml +1 -1
  3. {execsql2-2.25.1 → execsql2-2.26.0}/.pre-commit-hooks.yaml +4 -1
  4. {execsql2-2.25.1 → execsql2-2.26.0}/CHANGELOG.md +13 -0
  5. {execsql2-2.25.1 → execsql2-2.26.0}/PKG-INFO +5 -5
  6. {execsql2-2.25.1 → execsql2-2.26.0}/README.md +1 -1
  7. {execsql2-2.25.1 → execsql2-2.26.0}/docs/about/divergence.md +8 -8
  8. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/formatter.md +29 -1
  9. {execsql2-2.25.1 → execsql2-2.26.0}/docs/reference/configuration.md +3 -0
  10. {execsql2-2.25.1 → execsql2-2.26.0}/docs/reference/lint.md +1 -1
  11. {execsql2-2.25.1 → execsql2-2.26.0}/pyproject.toml +3 -3
  12. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/format.py +10 -0
  13. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/config.py +2 -0
  14. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/data/execsql.conf.template +3 -0
  15. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/format.py +106 -21
  16. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_cli_surface.py +1 -0
  17. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_commands.py +8 -0
  18. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_format.py +54 -0
  19. execsql2-2.26.0/tests/test_tooling_versions.py +65 -0
  20. {execsql2-2.25.1 → execsql2-2.26.0}/uv.lock +2 -2
  21. execsql2-2.25.1/tests/test_tooling_versions.py +0 -38
  22. {execsql2-2.25.1 → execsql2-2.26.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  23. {execsql2-2.25.1 → execsql2-2.26.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  24. {execsql2-2.25.1 → execsql2-2.26.0}/.gitignore +0 -0
  25. {execsql2-2.25.1 → execsql2-2.26.0}/.pre-commit-config.yaml +0 -0
  26. {execsql2-2.25.1 → execsql2-2.26.0}/.python-version +0 -0
  27. {execsql2-2.25.1 → execsql2-2.26.0}/.readthedocs.yaml +0 -0
  28. {execsql2-2.25.1 → execsql2-2.26.0}/CONTRIBUTING.md +0 -0
  29. {execsql2-2.25.1 → execsql2-2.26.0}/LICENSE.txt +0 -0
  30. {execsql2-2.25.1 → execsql2-2.26.0}/NOTICE +0 -0
  31. {execsql2-2.25.1 → execsql2-2.26.0}/SECURITY.md +0 -0
  32. {execsql2-2.25.1 → execsql2-2.26.0}/docs/about/contributors.md +0 -0
  33. {execsql2-2.25.1 → execsql2-2.26.0}/docs/about/copyright.md +0 -0
  34. {execsql2-2.25.1 → execsql2-2.26.0}/docs/api/cli.md +0 -0
  35. {execsql2-2.25.1 → execsql2-2.26.0}/docs/api/db.md +0 -0
  36. {execsql2-2.25.1 → execsql2-2.26.0}/docs/api/exporters.md +0 -0
  37. {execsql2-2.25.1 → execsql2-2.26.0}/docs/api/importers.md +0 -0
  38. {execsql2-2.25.1 → execsql2-2.26.0}/docs/api/index.md +0 -0
  39. {execsql2-2.25.1 → execsql2-2.26.0}/docs/api/metacommands.md +0 -0
  40. {execsql2-2.25.1 → execsql2-2.26.0}/docs/dev/adding_db_adapters.md +0 -0
  41. {execsql2-2.25.1 → execsql2-2.26.0}/docs/dev/adding_exporters.md +0 -0
  42. {execsql2-2.25.1 → execsql2-2.26.0}/docs/dev/adding_importers.md +0 -0
  43. {execsql2-2.25.1 → execsql2-2.26.0}/docs/dev/adding_metacommands.md +0 -0
  44. {execsql2-2.25.1 → execsql2-2.26.0}/docs/dev/architecture.md +0 -0
  45. {execsql2-2.25.1 → execsql2-2.26.0}/docs/dev/releasing.md +0 -0
  46. {execsql2-2.25.1 → execsql2-2.26.0}/docs/getting-started/installation.md +0 -0
  47. {execsql2-2.25.1 → execsql2-2.26.0}/docs/getting-started/requirements.md +0 -0
  48. {execsql2-2.25.1 → execsql2-2.26.0}/docs/getting-started/syntax.md +0 -0
  49. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/debugging.md +0 -0
  50. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/documentation.md +0 -0
  51. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/encoding.md +0 -0
  52. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/examples.md +0 -0
  53. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/logging.md +0 -0
  54. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/sql_syntax.md +0 -0
  55. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/usage.md +0 -0
  56. {execsql2-2.25.1 → execsql2-2.26.0}/docs/guides/using_scripts.md +0 -0
  57. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/Compare_planets.png +0 -0
  58. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/actions.png +0 -0
  59. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/actions2.png +0 -0
  60. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/checkboxes.png +0 -0
  61. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/connect.b64 +0 -0
  62. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/connect.png +0 -0
  63. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/create_conf.png +0 -0
  64. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/data_error1_screenshot.jpg +0 -0
  65. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/entry_form.png +0 -0
  66. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/execsql_console.png +0 -0
  67. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/execsql_logo_01.png +0 -0
  68. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/fatals.png +0 -0
  69. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/logo_small.png +0 -0
  70. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/pause_terminal.png +0 -0
  71. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/pause_terminal_sm.b64 +0 -0
  72. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/pause_terminal_sm.png +0 -0
  73. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/prompt_compare.png +0 -0
  74. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/set_build_commands.jpg +0 -0
  75. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/unit_conversions.b64 +0 -0
  76. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/unit_conversions_029.png +0 -0
  77. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/unmatched.png +0 -0
  78. {execsql2-2.25.1 → execsql2-2.26.0}/docs/images/vim_execsql_highlight.png +0 -0
  79. {execsql2-2.25.1 → execsql2-2.26.0}/docs/index.md +0 -0
  80. {execsql2-2.25.1 → execsql2-2.26.0}/docs/reference/metacommands.md +0 -0
  81. {execsql2-2.25.1 → execsql2-2.26.0}/docs/reference/security.md +0 -0
  82. {execsql2-2.25.1 → execsql2-2.26.0}/docs/reference/substitution_vars.md +0 -0
  83. {execsql2-2.25.1 → execsql2-2.26.0}/extras/plugin-template/README.md +0 -0
  84. {execsql2-2.25.1 → execsql2-2.26.0}/extras/plugin-template/pyproject.toml +0 -0
  85. {execsql2-2.25.1 → execsql2-2.26.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
  86. {execsql2-2.25.1 → execsql2-2.26.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
  87. {execsql2-2.25.1 → execsql2-2.26.0}/extras/vscode-execsql/.vscodeignore +0 -0
  88. {execsql2-2.25.1 → execsql2-2.26.0}/extras/vscode-execsql/LICENSE.txt +0 -0
  89. {execsql2-2.25.1 → execsql2-2.26.0}/extras/vscode-execsql/README.md +0 -0
  90. {execsql2-2.25.1 → execsql2-2.26.0}/extras/vscode-execsql/icon.png +0 -0
  91. {execsql2-2.25.1 → execsql2-2.26.0}/extras/vscode-execsql/package.json +0 -0
  92. {execsql2-2.25.1 → execsql2-2.26.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
  93. {execsql2-2.25.1 → execsql2-2.26.0}/justfile +0 -0
  94. {execsql2-2.25.1 → execsql2-2.26.0}/scripts/benchmarks/README.md +0 -0
  95. {execsql2-2.25.1 → execsql2-2.26.0}/scripts/benchmarks/results/.gitignore +0 -0
  96. {execsql2-2.25.1 → execsql2-2.26.0}/scripts/benchmarks/run_benchmarks.py +0 -0
  97. {execsql2-2.25.1 → execsql2-2.26.0}/scripts/generate_vscode_grammar.py +0 -0
  98. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/__init__.py +0 -0
  99. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/__main__.py +0 -0
  100. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/api.py +0 -0
  101. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/__init__.py +0 -0
  102. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/application.py +0 -0
  103. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/__init__.py +0 -0
  104. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/config.py +0 -0
  105. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/init.py +0 -0
  106. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/lint.py +0 -0
  107. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/listing.py +0 -0
  108. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/commands/run.py +0 -0
  109. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/dispatch.py +0 -0
  110. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/dsn.py +0 -0
  111. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/help.py +0 -0
  112. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/lint.py +0 -0
  113. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/options.py +0 -0
  114. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/cli/run.py +0 -0
  115. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/config_validation.py +0 -0
  116. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/data/__init__.py +0 -0
  117. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/data/script_header.sql +0 -0
  118. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/__init__.py +0 -0
  119. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/access.py +0 -0
  120. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/base.py +0 -0
  121. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/dsn.py +0 -0
  122. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/duckdb.py +0 -0
  123. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/factory.py +0 -0
  124. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/firebird.py +0 -0
  125. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/mysql.py +0 -0
  126. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/oracle.py +0 -0
  127. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/postgres.py +0 -0
  128. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/sqlite.py +0 -0
  129. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/sqlserver.py +0 -0
  130. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/db/tiers.py +0 -0
  131. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/debug/__init__.py +0 -0
  132. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/debug/repl.py +0 -0
  133. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exceptions.py +0 -0
  134. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/__init__.py +0 -0
  135. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/base.py +0 -0
  136. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/delimited.py +0 -0
  137. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/duckdb.py +0 -0
  138. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/feather.py +0 -0
  139. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/html.py +0 -0
  140. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/json.py +0 -0
  141. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/latex.py +0 -0
  142. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/markdown.py +0 -0
  143. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/ods.py +0 -0
  144. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/parquet.py +0 -0
  145. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/pretty.py +0 -0
  146. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/protocol.py +0 -0
  147. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/raw.py +0 -0
  148. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/sqlite.py +0 -0
  149. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/templates.py +0 -0
  150. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/values.py +0 -0
  151. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/xls.py +0 -0
  152. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/xlsx.py +0 -0
  153. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/xml.py +0 -0
  154. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/yaml.py +0 -0
  155. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/exporters/zip.py +0 -0
  156. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/gui/__init__.py +0 -0
  157. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/gui/base.py +0 -0
  158. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/gui/console.py +0 -0
  159. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/gui/desktop.py +0 -0
  160. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/gui/tui.py +0 -0
  161. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/__init__.py +0 -0
  162. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/base.py +0 -0
  163. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/csv.py +0 -0
  164. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/feather.py +0 -0
  165. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/json.py +0 -0
  166. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/ods.py +0 -0
  167. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/importers/xls.py +0 -0
  168. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/interactive.py +0 -0
  169. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/manifest.py +0 -0
  170. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/__init__.py +0 -0
  171. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/conditions.py +0 -0
  172. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/connect.py +0 -0
  173. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/control.py +0 -0
  174. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/data.py +0 -0
  175. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/debug.py +0 -0
  176. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/dispatch.py +0 -0
  177. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/effects.py +0 -0
  178. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/io.py +0 -0
  179. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/io_export.py +0 -0
  180. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/io_fileops.py +0 -0
  181. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/io_import.py +0 -0
  182. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/io_write.py +0 -0
  183. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/prompt.py +0 -0
  184. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/script_ext.py +0 -0
  185. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/system.py +0 -0
  186. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/metacommands/upsert.py +0 -0
  187. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/models.py +0 -0
  188. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/parser.py +0 -0
  189. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/plugins.py +0 -0
  190. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/py.typed +0 -0
  191. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/__init__.py +0 -0
  192. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/ast.py +0 -0
  193. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/control.py +0 -0
  194. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/engine.py +0 -0
  195. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/executor.py +0 -0
  196. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/parser.py +0 -0
  197. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/script/variables.py +0 -0
  198. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/state.py +0 -0
  199. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/state.pyi +0 -0
  200. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/types.py +0 -0
  201. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/__init__.py +0 -0
  202. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/auth.py +0 -0
  203. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/color.py +0 -0
  204. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/crypto.py +0 -0
  205. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/datetime.py +0 -0
  206. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/errors.py +0 -0
  207. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/fileio.py +0 -0
  208. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/gui.py +0 -0
  209. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/mail.py +0 -0
  210. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/numeric.py +0 -0
  211. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/regex.py +0 -0
  212. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/strings.py +0 -0
  213. {execsql2-2.25.1 → execsql2-2.26.0}/src/execsql/utils/timer.py +0 -0
  214. {execsql2-2.25.1 → execsql2-2.26.0}/templates/README.md +0 -0
  215. {execsql2-2.25.1 → execsql2-2.26.0}/templates/config_settings.sqlite +0 -0
  216. {execsql2-2.25.1 → execsql2-2.26.0}/templates/example_config_prompt.sql +0 -0
  217. {execsql2-2.25.1 → execsql2-2.26.0}/templates/make_config_db.sql +0 -0
  218. {execsql2-2.25.1 → execsql2-2.26.0}/templates/md_compare.sql +0 -0
  219. {execsql2-2.25.1 → execsql2-2.26.0}/templates/md_glossary.sql +0 -0
  220. {execsql2-2.25.1 → execsql2-2.26.0}/templates/md_upsert.sql +0 -0
  221. {execsql2-2.25.1 → execsql2-2.26.0}/templates/pg_compare.sql +0 -0
  222. {execsql2-2.25.1 → execsql2-2.26.0}/templates/pg_glossary.sql +0 -0
  223. {execsql2-2.25.1 → execsql2-2.26.0}/templates/pg_upsert.sql +0 -0
  224. {execsql2-2.25.1 → execsql2-2.26.0}/templates/script_template.sql +0 -0
  225. {execsql2-2.25.1 → execsql2-2.26.0}/templates/ss_compare.sql +0 -0
  226. {execsql2-2.25.1 → execsql2-2.26.0}/templates/ss_glossary.sql +0 -0
  227. {execsql2-2.25.1 → execsql2-2.26.0}/templates/ss_upsert.sql +0 -0
  228. {execsql2-2.25.1 → execsql2-2.26.0}/tests/__init__.py +0 -0
  229. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/__init__.py +0 -0
  230. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_cli.py +0 -0
  231. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_cli_e2e.py +0 -0
  232. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_cli_run.py +0 -0
  233. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_config_validate.py +0 -0
  234. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_dispatch.py +0 -0
  235. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_error_location.py +0 -0
  236. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_init.py +0 -0
  237. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_lint.py +0 -0
  238. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_lint_fixtures.py +0 -0
  239. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_log_exit_record.py +0 -0
  240. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_manifest.py +0 -0
  241. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_ping.py +0 -0
  242. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_profile.py +0 -0
  243. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_sigterm.py +0 -0
  244. {execsql2-2.25.1 → execsql2-2.26.0}/tests/cli/test_undeclared_imports.py +0 -0
  245. {execsql2-2.25.1 → execsql2-2.26.0}/tests/conftest.py +0 -0
  246. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/formatter_shapes/comment_anchoring.sql +0 -0
  247. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/formatter_shapes/escapes_and_literals.sql +0 -0
  248. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/formatter_shapes/substitution_and_metacommands.sql +0 -0
  249. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/formatter_shapes/unparsable_sql.sql +0 -0
  250. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/lint/conditions_and_blocks.sql +0 -0
  251. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/lint/does_not_parse.sql +0 -0
  252. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/lint/every_metacommand.sql +0 -0
  253. {execsql2-2.25.1 → execsql2-2.26.0}/tests/data/lint/invalid.sql +0 -0
  254. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/__init__.py +0 -0
  255. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_access_windows.py +0 -0
  256. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_base.py +0 -0
  257. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_copy_new.py +0 -0
  258. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_db_adapters_mocked.py +0 -0
  259. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_db_adapters_mocked_extra.py +0 -0
  260. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_dsn.py +0 -0
  261. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_duckdb.py +0 -0
  262. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_factory.py +0 -0
  263. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_factory_postgres_encoding.py +0 -0
  264. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_import_atomic.py +0 -0
  265. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_import_replacement_quoted.py +0 -0
  266. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_import_short_rows.py +0 -0
  267. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_import_timezones.py +0 -0
  268. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_mysql_case_folding.py +0 -0
  269. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_mysql_inprocess.py +0 -0
  270. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_new_table_checks.py +0 -0
  271. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_oracle_driver.py +0 -0
  272. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_postgres.py +0 -0
  273. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_postgres_inprocess.py +0 -0
  274. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_roundtrip_adapters.py +0 -0
  275. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_sqlite.py +0 -0
  276. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_sqlite_extra.py +0 -0
  277. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_sqlserver_inprocess.py +0 -0
  278. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_tiers.py +0 -0
  279. {execsql2-2.25.1 → execsql2-2.26.0}/tests/db/test_transactions.py +0 -0
  280. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/__init__.py +0 -0
  281. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/conftest.py +0 -0
  282. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_base.py +0 -0
  283. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_db.py +0 -0
  284. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_delimited.py +0 -0
  285. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_duckdb_exporter.py +0 -0
  286. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_exporters.py +0 -0
  287. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_feather.py +0 -0
  288. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_html_extended.py +0 -0
  289. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_html_latex.py +0 -0
  290. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_json.py +0 -0
  291. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_json_extended.py +0 -0
  292. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_latex_extended.py +0 -0
  293. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_markdown.py +0 -0
  294. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_ods.py +0 -0
  295. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_ods_export.py +0 -0
  296. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_parquet.py +0 -0
  297. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_pretty_extended.py +0 -0
  298. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_raw_extended.py +0 -0
  299. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_columnar.py +0 -0
  300. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_dbfile.py +0 -0
  301. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_delimited.py +0 -0
  302. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_json.py +0 -0
  303. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_latex_xml.py +0 -0
  304. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_markup.py +0 -0
  305. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_ods.py +0 -0
  306. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_pretty_raw.py +0 -0
  307. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_template.py +0 -0
  308. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_values.py +0 -0
  309. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_roundtrip_xlsx.py +0 -0
  310. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_sqlite_exporter.py +0 -0
  311. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_templates.py +0 -0
  312. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_templates_extended.py +0 -0
  313. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_values_extended.py +0 -0
  314. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_xls_xlsx.py +0 -0
  315. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_xlsx.py +0 -0
  316. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_xml.py +0 -0
  317. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_yaml.py +0 -0
  318. {execsql2-2.25.1 → execsql2-2.26.0}/tests/exporters/test_zip.py +0 -0
  319. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/__init__.py +0 -0
  320. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_backends.py +0 -0
  321. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_backends_extended.py +0 -0
  322. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_compare_stats.py +0 -0
  323. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_compute_row_diffs.py +0 -0
  324. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_desktop_dialogs.py +0 -0
  325. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_format_table.py +0 -0
  326. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_tui_pilot.py +0 -0
  327. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_tui_pilot_complex.py +0 -0
  328. {execsql2-2.25.1 → execsql2-2.26.0}/tests/gui/test_utils_gui_extended.py +0 -0
  329. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/__init__.py +0 -0
  330. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/conftest.py +0 -0
  331. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_base_extended.py +0 -0
  332. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_csv_blank_lines.py +0 -0
  333. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_csv_edge_cases.py +0 -0
  334. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_csv_importer.py +0 -0
  335. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_feather_importer.py +0 -0
  336. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_json_importer.py +0 -0
  337. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_ods_importer.py +0 -0
  338. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_roundtrip_csv.py +0 -0
  339. {execsql2-2.25.1 → execsql2-2.26.0}/tests/importers/test_xls_importer.py +0 -0
  340. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/__init__.py +0 -0
  341. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/conftest.py +0 -0
  342. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/test_dsn.py +0 -0
  343. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/test_duckdb.py +0 -0
  344. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/test_mysql.py +0 -0
  345. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/test_postgres.py +0 -0
  346. {execsql2-2.25.1 → execsql2-2.26.0}/tests/integration/test_sqlite.py +0 -0
  347. {execsql2-2.25.1 → execsql2-2.26.0}/tests/live_db.py +0 -0
  348. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/__init__.py +0 -0
  349. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_assert.py +0 -0
  350. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_breakpoint.py +0 -0
  351. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_conditions_extra.py +0 -0
  352. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_connect.py +0 -0
  353. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_io_export.py +0 -0
  354. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_io_import.py +0 -0
  355. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands.py +0 -0
  356. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_connect.py +0 -0
  357. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_data.py +0 -0
  358. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_extended.py +0 -0
  359. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  360. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_io.py +0 -0
  361. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  362. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  363. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_system.py +0 -0
  364. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  365. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_pg_upsert.py +0 -0
  366. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_prompt.py +0 -0
  367. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_row_count.py +0 -0
  368. {execsql2-2.25.1 → execsql2-2.26.0}/tests/metacommands/test_show_scripts.py +0 -0
  369. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/__init__.py +0 -0
  370. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
  371. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
  372. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
  373. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/audit_smoke.sql +0 -0
  374. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
  375. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/control_flow.sql +0 -0
  376. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
  377. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/error_handling.sql +0 -0
  378. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
  379. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/import_options.sql +0 -0
  380. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
  381. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/includes.sql +0 -0
  382. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/io_formats.sql +0 -0
  383. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
  384. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/multi_db.sql +0 -0
  385. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
  386. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
  387. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/predicates.sql +0 -0
  388. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/scripts.sql +0 -0
  389. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/smoke.sql +0 -0
  390. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
  391. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
  392. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
  393. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/template_export.sql +0 -0
  394. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/timer.sql +0 -0
  395. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/transactions.sql +0 -0
  396. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
  397. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
  398. {execsql2-2.25.1 → execsql2-2.26.0}/tests/scripts/test_sql_scripts.py +0 -0
  399. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/__init__.py +0 -0
  400. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_env_detection.py +0 -0
  401. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_expansion_bomb.py +0 -0
  402. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_import_regex_hardening.py +0 -0
  403. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_log_redaction.py +0 -0
  404. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_log_secrets.py +0 -0
  405. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_odbc_injection.py +0 -0
  406. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_path_containment.py +0 -0
  407. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_substitution_injection.py +0 -0
  408. {execsql2-2.25.1 → execsql2-2.26.0}/tests/security/test_zip_bomb.py +0 -0
  409. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_api.py +0 -0
  410. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_api_errors.py +0 -0
  411. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_api_extended.py +0 -0
  412. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_api_filewriter.py +0 -0
  413. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_api_isolation.py +0 -0
  414. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_api_subprocess.py +0 -0
  415. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_ast.py +0 -0
  416. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_ast_parser.py +0 -0
  417. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_bundled_templates.py +0 -0
  418. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_config.py +0 -0
  419. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_config_data.py +0 -0
  420. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_config_extended.py +0 -0
  421. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_date_inference.py +0 -0
  422. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_debug_repl.py +0 -0
  423. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_engine.py +0 -0
  424. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_error_messages.py +0 -0
  425. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_exceptions.py +0 -0
  426. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_executor.py +0 -0
  427. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_executor_inprocess.py +0 -0
  428. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_interactive.py +0 -0
  429. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_mail.py +0 -0
  430. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_models.py +0 -0
  431. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_package.py +0 -0
  432. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_parser.py +0 -0
  433. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_parser_params.py +0 -0
  434. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_plugins.py +0 -0
  435. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_registry.py +0 -0
  436. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_script.py +0 -0
  437. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_state.py +0 -0
  438. {execsql2-2.25.1 → execsql2-2.26.0}/tests/test_types.py +0 -0
  439. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/__init__.py +0 -0
  440. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_auth.py +0 -0
  441. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_auth_extra.py +0 -0
  442. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_crypto.py +0 -0
  443. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_datetime.py +0 -0
  444. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_encodedfile_context.py +0 -0
  445. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_errors.py +0 -0
  446. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_errors_extra.py +0 -0
  447. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_fileio.py +0 -0
  448. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_fileio_extra.py +0 -0
  449. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_keyring_rules.py +0 -0
  450. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_mail_tls.py +0 -0
  451. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_numeric.py +0 -0
  452. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_regex.py +0 -0
  453. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_strings.py +0 -0
  454. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_timer.py +0 -0
  455. {execsql2-2.25.1 → execsql2-2.26.0}/tests/utils/test_timer_extra.py +0 -0
  456. {execsql2-2.25.1 → execsql2-2.26.0}/zensical.toml +0 -0
@@ -28,8 +28,13 @@ updates:
28
28
  directory: /
29
29
  schedule:
30
30
  interval: weekly
31
+ # sqlglot is pinned (pyproject formatter extra, .pre-commit-hooks.yaml):
32
+ # its output changes between releases, so a bump is deliberate. Bump all
33
+ # three together, `uv lock --upgrade-package sqlglot==<new>`, and run the
34
+ # formatter corpus (CLAUDE.md) before merging.
31
35
  ignore:
32
36
  - dependency-name: ruff
37
+ - dependency-name: sqlglot
33
38
  groups:
34
39
  python-minor-and-patch:
35
40
  patterns:
@@ -57,7 +57,7 @@ jobs:
57
57
  permissions:
58
58
  contents: read
59
59
  steps:
60
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
60
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
61
61
  - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
62
62
  with:
63
63
  python-version: "3.13"
@@ -6,7 +6,10 @@
6
6
  types: [file]
7
7
  files: \.sql$
8
8
  args: [--in-place]
9
- additional_dependencies: ["sqlglot>=25.0,<31"]
9
+ # One sqlglot: its output changes between releases, so an unpinned hook
10
+ # formats differently depending on when its env was built (#72). Keep equal
11
+ # to the formatter extra and uv.lock (tests/test_tooling_versions.py).
12
+ additional_dependencies: ["sqlglot==30.21.0"]
10
13
 
11
14
  - id: execsql-lint
12
15
  name: execsql-lint
@@ -13,6 +13,19 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.26.0] - 2026-10-09
17
+
18
+ ### Added
19
+
20
+ - **`execsql format --rewrite-sql`** (`[format] rewrite_sql`): accept sqlglot's rewrites of SQL text, such as `x::int` as `CAST(x AS INT)`.
21
+
22
+ ### Changed
23
+
24
+ - **`execsql format` changes only layout and keyword case by default:** a statement sqlglot would rewrite (`x::int` as `CAST(x AS INT)`, an alias gaining `AS`, `BTRIM` as `TRIM`) is kept as written. Pass `--rewrite-sql`, or set `rewrite_sql = Yes` under `[format]`, for the previous behavior.
25
+ - **`execsql format` and the `execsql-format` pre-commit hook use one sqlglot release:** the `[formatter]` extra requires sqlglot 30.21.x and the hook installs 30.21.0, so a script formats the same everywhere. The first run after upgrading can reformat files formatted with an older sqlglot, such as `BTRIM(x)` written as `TRIM(x)`; see [sqlglot version](https://execsql2.readthedocs.io/en/latest/guides/formatter/#sqlglot-version).
26
+
27
+ ______________________________________________________________________
28
+
16
29
  ## [2.25.1] - 2026-10-09
17
30
 
18
31
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execsql2
3
- Version: 2.25.1
3
+ Version: 2.26.0
4
4
  Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
5
5
  Project-URL: Homepage, https://execsql2.readthedocs.io
6
6
  Project-URL: Repository, https://github.com/geocoug/execsql
@@ -58,7 +58,7 @@ Requires-Dist: psycopg[binary]<4,>=3.1; extra == 'all'
58
58
  Requires-Dist: pymysql>=1.1; extra == 'all'
59
59
  Requires-Dist: pyodbc>=5.0; extra == 'all'
60
60
  Requires-Dist: pyyaml>=6.0; extra == 'all'
61
- Requires-Dist: sqlglot>=25.0; extra == 'all'
61
+ Requires-Dist: sqlglot<30.22,>=30.21.0; extra == 'all'
62
62
  Requires-Dist: tables>=3.10; extra == 'all'
63
63
  Requires-Dist: tkintermapview>=1.29; extra == 'all'
64
64
  Requires-Dist: xlrd>=2.0; extra == 'all'
@@ -92,7 +92,7 @@ Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
92
92
  Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
93
93
  Requires-Dist: pyyaml>=6.0; extra == 'dev'
94
94
  Requires-Dist: ruff>=0.4; extra == 'dev'
95
- Requires-Dist: sqlglot>=25.0; extra == 'dev'
95
+ Requires-Dist: sqlglot<30.22,>=30.21.0; extra == 'dev'
96
96
  Requires-Dist: tables>=3.10; extra == 'dev'
97
97
  Requires-Dist: tox-uv>=1.13.1; extra == 'dev'
98
98
  Requires-Dist: twine>=6.1.0; extra == 'dev'
@@ -112,7 +112,7 @@ Requires-Dist: pyyaml>=6.0; extra == 'formats'
112
112
  Requires-Dist: tables>=3.10; extra == 'formats'
113
113
  Requires-Dist: xlrd>=2.0; extra == 'formats'
114
114
  Provides-Extra: formatter
115
- Requires-Dist: sqlglot>=25.0; extra == 'formatter'
115
+ Requires-Dist: sqlglot<30.22,>=30.21.0; extra == 'formatter'
116
116
  Provides-Extra: map
117
117
  Requires-Dist: tkintermapview>=1.29; extra == 'map'
118
118
  Provides-Extra: mssql
@@ -451,7 +451,7 @@ execsql format --no-sql --in-place scripts/
451
451
  ```yaml
452
452
  repos:
453
453
  - repo: https://github.com/geocoug/execsql
454
- rev: v2.25.1
454
+ rev: v2.26.0
455
455
  hooks:
456
456
  - id: execsql-format
457
457
  - id: execsql-lint
@@ -320,7 +320,7 @@ execsql format --no-sql --in-place scripts/
320
320
  ```yaml
321
321
  repos:
322
322
  - repo: https://github.com/geocoug/execsql
323
- rev: v2.25.1
323
+ rev: v2.26.0
324
324
  hooks:
325
325
  - id: execsql-format
326
326
  - id: execsql-lint
@@ -128,14 +128,14 @@ command, naming their replacement (`execsql run --ping` for `--ping`). The one e
128
128
  today — a script named exactly like a command — is recorded under
129
129
  [Changed Behavior](#cli-interface).
130
130
 
131
- | Command | Description |
132
- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
- | `execsql run` | The explicit spelling of the upstream positional form. Identical behavior. |
134
- | `execsql format` | The formatter, reached through the main command. It replaced the standalone `execsql-format` console script, which was removed. The published pre-commit hook **id** is still `execsql-format`, so downstream `.pre-commit-config.yaml` files need no change — only the hook's internal `entry` moved. Formatter for normalizing metacommand indentation and uppercasing SQL keywords. Supports `--check`, `--in-place`, `--indent N` (controls both metacommand and SQL indentation), `--leading-comma` (commas at start of lines), `--no-sql` (skip SQL reformatting), `-f`/`--script-encoding` (formerly `--encoding`, still accepted), and `--config`; `-` formats stdin to stdout. Also available as a [pre-commit hook](../guides/formatter.md) — the published hook defaults to `args: [--in-place]` so downstream consumers get formatting on commit without extra wiring. SQL reformatting (the optional sqlglot pass) requires the `[formatter]` extra as of 2.19.0; `--no-sql` works without it. |
135
- | `execsql lint` | Static checks over files *and directories*, walking directories recursively for `*.sql`; `-` reads stdin. Scripts are read with `-f`/`--script-encoding` or `[encoding] script` from config, like `run`. Every issue carries a [rule code](../reference/lint.md#rules); `--select` / `--ignore` choose rules, `--output-format json` feeds tools, and `--statistics` counts issues per rule. It replaced the `--lint` flag, which checked only the one script it was given. |
136
- | `execsql config` | List every config option with its current value, default, and the file that set it, resolved as a run would (pass a script to include its directory's `execsql.conf`). Passwords are masked. `--init` prints the template (replaces `--init-config`). `--validate` reports every problem in every config file a run reads, with file and line: invalid values and unparsable files as errors, unknown or misplaced sections and keys as warnings — upstream stopped at the first bad value and ignored unknown names silently. |
137
- | `execsql list` | `metacommands`, `encodings`, `plugins` or `keywords`, as text or JSON. Replaces `-m`, `-y`, `--list-plugins` and `--dump-keywords`; `-m` and `-y` are upstream flags and keep working as hidden aliases, deprecated when used without a command. |
138
- | `execsql init` | Set up a project: `execsql.conf` from the template, a script with a header (from the upstream script template), and the `execsql-format` / `execsql-lint` pre-commit hooks. Never overwrites without `--force`, and never rewrites an existing `.pre-commit-config.yaml`. |
131
+ | Command | Description |
132
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
133
+ | `execsql run` | The explicit spelling of the upstream positional form. Identical behavior. |
134
+ | `execsql format` | The formatter, reached through the main command. It replaced the standalone `execsql-format` console script, which was removed. The published pre-commit hook **id** is still `execsql-format`, so downstream `.pre-commit-config.yaml` files need no change — only the hook's internal `entry` moved. Formatter for normalizing metacommand indentation and uppercasing SQL keywords. Supports `--check`, `--in-place`, `--indent N` (controls both metacommand and SQL indentation), `--leading-comma` (commas at start of lines), `--no-sql` (skip SQL reformatting), `--rewrite-sql` (accept sqlglot's rewrites of SQL text; off by default, so only layout changes), `-f`/`--script-encoding` (formerly `--encoding`, still accepted), and `--config`; `-` formats stdin to stdout. Also available as a [pre-commit hook](../guides/formatter.md) — the published hook defaults to `args: [--in-place]` so downstream consumers get formatting on commit without extra wiring. SQL reformatting (the optional sqlglot pass) requires the `[formatter]` extra as of 2.19.0; `--no-sql` works without it. |
135
+ | `execsql lint` | Static checks over files *and directories*, walking directories recursively for `*.sql`; `-` reads stdin. Scripts are read with `-f`/`--script-encoding` or `[encoding] script` from config, like `run`. Every issue carries a [rule code](../reference/lint.md#rules); `--select` / `--ignore` choose rules, `--output-format json` feeds tools, and `--statistics` counts issues per rule. It replaced the `--lint` flag, which checked only the one script it was given. |
136
+ | `execsql config` | List every config option with its current value, default, and the file that set it, resolved as a run would (pass a script to include its directory's `execsql.conf`). Passwords are masked. `--init` prints the template (replaces `--init-config`). `--validate` reports every problem in every config file a run reads, with file and line: invalid values and unparsable files as errors, unknown or misplaced sections and keys as warnings — upstream stopped at the first bad value and ignored unknown names silently. |
137
+ | `execsql list` | `metacommands`, `encodings`, `plugins` or `keywords`, as text or JSON. Replaces `-m`, `-y`, `--list-plugins` and `--dump-keywords`; `-m` and `-y` are upstream flags and keep working as hidden aliases, deprecated when used without a command. |
138
+ | `execsql init` | Set up a project: `execsql.conf` from the template, a script with a header (from the upstream script template), and the `execsql-format` / `execsql-lint` pre-commit hooks. Never overwrites without `--force`, and never rewrites an existing `.pre-commit-config.yaml`. |
139
139
 
140
140
  Formatter correctness notes added in the 2.19.x line:
141
141
 
@@ -80,6 +80,7 @@ one run.
80
80
  | `--sql`, `--no-sql` | `[format] sql`, else `--sql` | Reformat SQL via sqlglot, or skip it and only normalize metacommand indentation and keyword casing. `--no-sql` works without the `[formatter]` extra. |
81
81
  | `--indent N` | `[format] indent`, else `4` | Spaces per indent level. Controls both metacommand block depth and SQL indentation (columns, subqueries, etc). |
82
82
  | `--leading-comma`, `--no-leading-comma` | `[format] leading_comma`, else off | Place commas at the start of lines instead of the end (e.g. ` , col2` instead of `col1,`). |
83
+ | `--rewrite-sql`, `--no-rewrite-sql` | `[format] rewrite_sql`, else off | Also let sqlglot rewrite SQL text (`x::int` as `CAST(x AS INT)`, an alias with `AS`). Off, a statement sqlglot would rewrite is kept as written. See [Layout only](#layout-only). |
83
84
  | `-f`, `--script-encoding NAME` | config, else `utf-8` | Text encoding used to read and write SQL files. Pass `cp1252`, `latin-1`, `shift_jis`, etc. for files saved by non-UTF-8 editors. Without it, `[encoding] script` from a [config file](../reference/configuration.md#configuration) applies. The old spelling `--encoding` still works. |
84
85
  | `--config FILE` | — | An extra config file to read `[encoding] script` from. Config files are read once, from the system, user and working-directory locations plus this file — not from each script's directory. |
85
86
 
@@ -121,6 +122,33 @@ SQL statements between metacommands are re-indented to match the current block d
121
122
 
122
123
  The `--indent` flag controls SQL indentation in addition to metacommand depth. For example, `--indent 4` (the default) produces 4-space indented column lists, subqueries, and CASE branches. `--indent 2` gives a more compact style.
123
124
 
125
+ #### sqlglot version { #sqlglot-version }
126
+
127
+ The SQL layout comes from sqlglot, and sqlglot's output changes between its releases. So that the same script formats the same way on every machine, in CI and in the pre-commit hook, execsql2 pins it: the `[formatter]` extra requires sqlglot 30.21.x and the `execsql-format` hook installs exactly 30.21.0. A new execsql2 release may move the pin; its changelog says so, and the first run after upgrading can reformat files whose content did not change.
128
+
129
+ Conditions joined by `AND` / `OR` go on one line when their combined text is at most 120 characters, and one per line otherwise. sqlglot measures the conditions without the indentation and the spaces between them, so a joined line can run a few characters past 120.
130
+
131
+ #### Layout only, or rewrite { #layout-only }
132
+
133
+ sqlglot does more than lay SQL out: it writes the SQL it parsed in its own preferred form. By default the formatter changes only layout and keyword case. A statement sqlglot would also rewrite is kept exactly as written, while the statements around it are still formatted.
134
+
135
+ | You wrote | sqlglot writes |
136
+ | --------------------------- | ------------------------------------- |
137
+ | `x::int` | `CAST(x AS INT)` |
138
+ | `count(*) n` | `COUNT(*) AS n` |
139
+ | `integer`, `numeric(10,2)` | `INT`, `DECIMAL(10, 2)` |
140
+ | `BTRIM(x)`, `LTRIM(x, 'a')` | `TRIM(x)`, `TRIM(LEADING 'a' FROM x)` |
141
+ | `a != b` | `a <> b` |
142
+
143
+ Each pair means the same to PostgreSQL. To accept sqlglot's form, so every statement is formatted, pass `--rewrite-sql` or set it in a config file:
144
+
145
+ ```ini
146
+ [format]
147
+ rewrite_sql = Yes
148
+ ```
149
+
150
+ With it on, the rewrites a statement gets can change when the pinned sqlglot changes (see above). Turning it off later does not undo rewrites already written; it only stops new ones.
151
+
124
152
  #### Comment handling
125
153
 
126
154
  Comments interleaved within SQL statements (e.g. `--` comments between SELECT columns, or inside CASE expressions) are preserved through formatting using a marker-based round-trip:
@@ -233,7 +261,7 @@ select id,name,created_at from users where active = true order by name;
233
261
  ```yaml
234
262
  repos:
235
263
  - repo: https://github.com/geocoug/execsql
236
- rev: v2.25.1
264
+ rev: v2.26.0
237
265
  hooks:
238
266
  - id: execsql-format
239
267
  - id: execsql-lint # optional: fail the commit on lint errors
@@ -405,6 +405,9 @@ the command line always wins. A run ignores this section.
405
405
  `sql`
406
406
  : Reformat SQL with sqlglot (needs the `[formatter]` extra). `No` normalizes metacommands only. Default: Yes. Equivalent to `--sql` / `--no-sql`.
407
407
 
408
+ `rewrite_sql`
409
+ : Also let sqlglot rewrite SQL text, such as `x::int` as `CAST(x AS INT)` or an alias with `AS`. With `No`, a statement sqlglot would rewrite is kept as written (see [Layout only](../guides/formatter.md#layout-only)). Default: No. Equivalent to `--rewrite-sql` / `--no-rewrite-sql`.
410
+
408
411
  ## Section `lint` { #config_lint }
409
412
 
410
413
  Rule selection for [`execsql lint`](lint.md#config). A run ignores this section.
@@ -158,7 +158,7 @@ The `execsql-lint` [pre-commit](https://pre-commit.com/) hook runs `execsql lint
158
158
  ```yaml
159
159
  repos:
160
160
  - repo: https://github.com/geocoug/execsql
161
- rev: v2.25.1
161
+ rev: v2.26.0
162
162
  hooks:
163
163
  - id: execsql-lint
164
164
  args: [--ignore, V002] # optional: any execsql lint option
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execsql2"
7
- version = "2.25.1"
7
+ version = "2.26.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" }
@@ -71,7 +71,7 @@ auth-plaintext = ["keyring>=25.0", "keyrings.alt>=5.0"]
71
71
  auth-encrypted = ["keyring>=25.0", "keyrings.alt>=5.0", "pycryptodome>=3.20"]
72
72
  upsert = ["pg-upsert>=1.25.0"]
73
73
  map = ["tkintermapview>=1.29"]
74
- formatter = ["sqlglot>=25.0"]
74
+ formatter = ["sqlglot>=30.21.0,<30.22"] # pinned: output changes between sqlglot releases (#72)
75
75
  # Convenience groups
76
76
  all-db = [
77
77
  "psycopg[binary]>=3.1,<4",
@@ -177,7 +177,7 @@ skip-magic-trailing-comma = false
177
177
  line-ending = "auto"
178
178
 
179
179
  [tool.bumpversion]
180
- current_version = "2.25.1"
180
+ current_version = "2.26.0"
181
181
  commit = true
182
182
  tag = true
183
183
  tag_name = "v{new_version}"
@@ -51,6 +51,15 @@ def format_cmd(
51
51
  help="Commas at the start of lines instead of the end. Default: [format] leading_comma, else off.",
52
52
  show_default=False,
53
53
  ),
54
+ rewrite_sql: bool | None = typer.Option(
55
+ None,
56
+ "--rewrite-sql/--no-rewrite-sql",
57
+ help=(
58
+ "Also let sqlglot rewrite SQL text (x::int as CAST(x AS INT), BTRIM as TRIM, an alias with AS); "
59
+ "without it such a statement is kept as written. Default: [format] rewrite_sql, else off."
60
+ ),
61
+ show_default=False,
62
+ ),
54
63
  script_encoding: ScriptEncodingOpt = None,
55
64
  # The old spelling of --script-encoding, kept working but out of --help.
56
65
  encoding: str | None = typer.Option(None, "--encoding", metavar="NAME", hidden=True),
@@ -92,6 +101,7 @@ def format_cmd(
92
101
  no_sql=no_sql,
93
102
  indent=conf.format_indent if indent is None else indent,
94
103
  leading_comma=conf.format_leading_comma if leading_comma is None else leading_comma,
104
+ rewrite_sql=conf.format_rewrite_sql if rewrite_sql is None else rewrite_sql,
95
105
  encoding=script_encoding or encoding or _configured_script_encoding(conf) or "utf-8",
96
106
  ),
97
107
  )
@@ -379,6 +379,7 @@ class ConfigData:
379
379
  self._get_int(cp, self._FORMAT_SECTION, "indent", "format_indent")
380
380
  self._get_bool(cp, self._FORMAT_SECTION, "leading_comma", "format_leading_comma")
381
381
  self._get_bool(cp, self._FORMAT_SECTION, "sql", "format_sql")
382
+ self._get_bool(cp, self._FORMAT_SECTION, "rewrite_sql", "format_rewrite_sql")
382
383
  self._get_str(cp, self._LINT_SECTION, "select", "lint_select")
383
384
  self._get_str(cp, self._LINT_SECTION, "ignore", "lint_ignore")
384
385
  self._get_bool(cp, self._LINT_SECTION, "strict", "lint_strict")
@@ -555,6 +556,7 @@ class ConfigData:
555
556
  self.format_indent = 4
556
557
  self.format_leading_comma = False
557
558
  self.format_sql = True
559
+ self.format_rewrite_sql = False
558
560
  self.lint_select: str | None = None
559
561
  self.lint_ignore: str | None = None
560
562
  self.lint_strict = False
@@ -369,6 +369,9 @@
369
369
  #leading_comma=No
370
370
  # Reformat SQL with sqlglot (needs the [formatter] extra). No = metacommands only.
371
371
  #sql=Yes
372
+ # Also let sqlglot rewrite SQL text: x::int as CAST(x AS INT), BTRIM as TRIM,
373
+ # an alias with AS. No = a statement it would rewrite is kept as written.
374
+ #rewrite_sql=No
372
375
 
373
376
 
374
377
  [lint]
@@ -16,6 +16,7 @@ import io
16
16
  import re
17
17
  from collections.abc import Iterator
18
18
  from pathlib import Path
19
+ from types import ModuleType
19
20
 
20
21
  __all__ = ["collect_paths", "format_file", "open_dollar_quote", "parse_keyword", "run_formatter"]
21
22
 
@@ -509,10 +510,47 @@ def _is_comment_line(line: str, in_block: bool) -> tuple[bool, bool]:
509
510
  return False, False
510
511
 
511
512
 
513
+ def _statement_sources(sqlglot: ModuleType, sql: str) -> list[str]:
514
+ """The text of each statement in *sql*, split at the semicolons sqlglot's tokenizer sees.
515
+
516
+ Comments before a statement belong to it; a semicolon inside a string or
517
+ a quoted name is not a split. A part holding no tokens (``;;``) is not a
518
+ statement, matching the ``None`` sqlglot's parser returns for it.
519
+ """
520
+ sources: list[str] = []
521
+ start = 0
522
+ has_tokens = False
523
+ for token in sqlglot.tokenize(sql, read="postgres"):
524
+ if token.token_type.name == "SEMICOLON":
525
+ if has_tokens:
526
+ sources.append(sql[start : token.start])
527
+ start, has_tokens = token.end + 1, False
528
+ else:
529
+ has_tokens = True
530
+ if has_tokens:
531
+ sources.append(sql[start:])
532
+ return sources
533
+
534
+
535
+ def _layout_tokens(sqlglot: ModuleType, sql: str) -> list[str]:
536
+ """*sql*'s tokens with everything the formatter may change taken out.
537
+
538
+ Whitespace and comments are not tokens. Keywords and unquoted names are
539
+ compared without case; strings and quoted names exactly.
540
+ """
541
+ return [
542
+ token.text
543
+ if token.token_type.name.endswith("STRING") or token.token_type.name == "IDENTIFIER"
544
+ else token.text.upper()
545
+ for token in sqlglot.tokenize(sql, read="postgres")
546
+ ]
547
+
548
+
512
549
  def _sqlglot_format(
513
550
  sql_lines: list[str],
514
551
  sql_indent: int = 4,
515
552
  leading_comma: bool = False, # noqa: ARG001 — leading-comma layout is applied as a textual post-pass on the assembled output; sqlglot's own `leading_comma=True` is non-idempotent under inline comments.
553
+ rewrite_sql: bool = False,
516
554
  ) -> list[str]:
517
555
  """Format a list of SQL-only lines (no comment-only lines) via sqlglot.
518
556
 
@@ -521,6 +559,12 @@ def _sqlglot_format(
521
559
  ``format_file``. sqlglot's own ``leading_comma=True`` reshuffles inline
522
560
  comments and is therefore non-idempotent on SQL with mid-statement
523
561
  comments, which is the dominant real-world case.
562
+
563
+ sqlglot also rewrites SQL into its preferred form (``x::int`` ->
564
+ ``CAST(x AS INT)``, ``BTRIM`` -> ``TRIM``, an alias gains ``AS``), and
565
+ which rewrites it makes changes between its releases (#72). Unless
566
+ *rewrite_sql*, a statement whose tokens would change beyond keyword case
567
+ is kept as written.
524
568
  """
525
569
  sqlglot = _require_sqlglot()
526
570
  import sqlglot.errors as sqlglot_errors
@@ -551,24 +595,28 @@ def _sqlglot_format(
551
595
  # understood the whole statement, so ask it: input that does not
552
596
  # parse is left exactly as written.
553
597
  ast = sqlglot.parse(protected, read="postgres", error_level=sqlglot_errors.ErrorLevel.RAISE)
598
+ nodes = [node for node in ast if node is not None]
599
+ sources = None if rewrite_sql else _statement_sources(sqlglot, protected)
600
+ if sources is not None and len(sources) != len(nodes):
601
+ return sql_lines
554
602
  statements: list[str] = []
555
- for node in ast:
556
- if node is None:
557
- continue
603
+ for i, node in enumerate(nodes):
558
604
  # For Command nodes (psql backslash commands, ERROR:, etc.)
559
605
  # use unpretty output to avoid mangling.
560
606
  if type(node).__name__ == "Command":
561
- statements.append(node.sql(dialect="postgres"))
607
+ generated = node.sql(dialect="postgres")
562
608
  else:
563
- statements.append(
564
- node.sql(
565
- dialect="postgres",
566
- pretty=True,
567
- pad=sql_indent,
568
- indent=sql_indent,
569
- max_text_width=120,
570
- ),
609
+ generated = node.sql(
610
+ dialect="postgres",
611
+ pretty=True,
612
+ pad=sql_indent,
613
+ indent=sql_indent,
614
+ max_text_width=120,
571
615
  )
616
+ generated = re.sub(r"\bINTO TEMPORARY\b(?!\s+TABLE)", "INTO TEMPORARY TABLE", generated)
617
+ if sources is not None and _layout_tokens(sqlglot, generated) != _layout_tokens(sqlglot, sources[i]):
618
+ generated = sources[i].lstrip("\n").rstrip() # layout only: keep it as written
619
+ statements.append(generated)
572
620
  stmts = [s for s in statements if s]
573
621
  if not stmts:
574
622
  return sql_lines
@@ -613,7 +661,6 @@ def _sqlglot_format(
613
661
  if set(_sql_literal_texts(protected)) - set(_sql_literal_texts(joined)):
614
662
  return sql_lines
615
663
 
616
- joined = re.sub(r"\bINTO TEMPORARY\b(?!\s+TABLE)", "INTO TEMPORARY TABLE", joined)
617
664
  restored = _restore_estrings(joined, estrings)
618
665
  if restored is None:
619
666
  return sql_lines
@@ -701,6 +748,7 @@ def _format_preserving_comments(
701
748
  lines: list[str],
702
749
  sql_indent: int = 4,
703
750
  leading_comma: bool = False,
751
+ rewrite_sql: bool = False,
704
752
  ) -> list[str]:
705
753
  """Format SQL with interleaved comments via marker-based round-tripping.
706
754
 
@@ -744,8 +792,12 @@ def _format_preserving_comments(
744
792
  pending_markers.append(mid)
745
793
  else:
746
794
  # SQL line — prepend any pending markers as inline comments
795
+ # After the line's indentation, not before it: a statement that
796
+ # comes back as written (unparsable, or kept by layout-only) has
797
+ # its indentation read from this line once the marker is removed.
747
798
  prefix = " ".join(f"/* {m} */" for m in pending_markers)
748
- processed.append(f"{prefix} {line}" if prefix else line)
799
+ body = line.lstrip()
800
+ processed.append(f"{line[: len(line) - len(body)]}{prefix} {body}" if prefix else line)
749
801
  for m in pending_markers:
750
802
  anchor_sql[m] = stripped
751
803
  anchor_line[m] = len(processed) - 1
@@ -760,7 +812,7 @@ def _format_preserving_comments(
760
812
  anchor_depth: dict[str, int] = {m: in_depths[i] for m, i in anchor_line.items()}
761
813
 
762
814
  # ---- Step 2: format through sqlglot ---------------------------------
763
- formatted = _sqlglot_format(processed, sql_indent=sql_indent, leading_comma=leading_comma)
815
+ formatted = _sqlglot_format(processed, sql_indent=sql_indent, leading_comma=leading_comma, rewrite_sql=rewrite_sql)
764
816
 
765
817
  # ---- Step 3: restore surviving markers to comment lines -------------
766
818
  found_markers: set[str] = set()
@@ -908,6 +960,7 @@ def format_sql_block(
908
960
  indent: int,
909
961
  use_sql: bool,
910
962
  leading_comma: bool = False,
963
+ rewrite_sql: bool = False,
911
964
  ) -> list[str]:
912
965
  """Re-indent a SQL block to the current depth, optionally formatting via sqlglot."""
913
966
  if not lines:
@@ -940,7 +993,12 @@ def format_sql_block(
940
993
  # which preserves both comments AND sqlglot formatting. When all
941
994
  # comments are between statements, the simpler segmentation works.
942
995
  if _has_mid_statement_comments(rebased):
943
- formatted_lines = _format_preserving_comments(rebased, sql_indent=indent, leading_comma=leading_comma)
996
+ formatted_lines = _format_preserving_comments(
997
+ rebased,
998
+ sql_indent=indent,
999
+ leading_comma=leading_comma,
1000
+ rewrite_sql=rewrite_sql,
1001
+ )
944
1002
  return [target_prefix + line if line.strip() else "" for line in formatted_lines]
945
1003
 
946
1004
  result: list[str] = []
@@ -954,7 +1012,7 @@ def format_sql_block(
954
1012
  if seg_is_comment:
955
1013
  result.extend(seg)
956
1014
  else:
957
- result.extend(_sqlglot_format(seg, sql_indent=indent, leading_comma=leading_comma))
1015
+ result.extend(_sqlglot_format(seg, sql_indent=indent, leading_comma=leading_comma, rewrite_sql=rewrite_sql))
958
1016
  seg.clear()
959
1017
 
960
1018
  for line in rebased:
@@ -1076,8 +1134,19 @@ def _apply_leading_comma(text: str) -> str:
1076
1134
  return "\n".join(lines)
1077
1135
 
1078
1136
 
1079
- def format_file(source: str, indent: int = 4, use_sql: bool = True, leading_comma: bool = False) -> str:
1080
- """Format the source text of an execsql script and return the result."""
1137
+ def format_file(
1138
+ source: str,
1139
+ indent: int = 4,
1140
+ use_sql: bool = True,
1141
+ leading_comma: bool = False,
1142
+ rewrite_sql: bool = False,
1143
+ ) -> str:
1144
+ """Format the source text of an execsql script and return the result.
1145
+
1146
+ SQL is laid out by sqlglot. A statement sqlglot would also rewrite
1147
+ (``x::int`` as ``CAST(x AS INT)``, ``BTRIM`` as ``TRIM``, an alias with
1148
+ ``AS``) is kept as written unless *rewrite_sql*.
1149
+ """
1081
1150
  # Normalize any leading-comma SQL in the source to trailing commas so
1082
1151
  # that sqlglot always sees the same comma shape regardless of how
1083
1152
  # the user saved the file. The post-pass at the bottom of this
@@ -1124,7 +1193,16 @@ def format_file(source: str, indent: int = 4, use_sql: bool = True, leading_comm
1124
1193
  # `sql_acc_contains_dollar_quote` catches the case where the region
1125
1194
  # already closed (`... $$;`) so `in_dollar_quote` is False at flush.
1126
1195
  safe_for_sqlglot = use_sql and not in_dollar_quote and not sql_acc_contains_dollar_quote
1127
- output.extend(format_sql_block(sql_acc, depth, indent, safe_for_sqlglot, leading_comma=leading_comma))
1196
+ output.extend(
1197
+ format_sql_block(
1198
+ sql_acc,
1199
+ depth,
1200
+ indent,
1201
+ safe_for_sqlglot,
1202
+ leading_comma=leading_comma,
1203
+ rewrite_sql=rewrite_sql,
1204
+ ),
1205
+ )
1128
1206
  sql_acc.clear()
1129
1207
  in_sql_statement = False
1130
1208
  sql_acc_contains_dollar_quote = False
@@ -1243,6 +1321,7 @@ def run_formatter(
1243
1321
  no_sql: bool = False,
1244
1322
  indent: int = 4,
1245
1323
  leading_comma: bool = False,
1324
+ rewrite_sql: bool = False,
1246
1325
  encoding: str = "utf-8",
1247
1326
  diff: bool = False,
1248
1327
  ) -> int:
@@ -1290,7 +1369,13 @@ def run_formatter(
1290
1369
  any_errors = True
1291
1370
  continue
1292
1371
 
1293
- formatted = format_file(source, indent=indent, use_sql=use_sql, leading_comma=leading_comma)
1372
+ formatted = format_file(
1373
+ source,
1374
+ indent=indent,
1375
+ use_sql=use_sql,
1376
+ leading_comma=leading_comma,
1377
+ rewrite_sql=rewrite_sql,
1378
+ )
1294
1379
 
1295
1380
  if diff:
1296
1381
  if formatted != source:
@@ -86,6 +86,7 @@ SURFACE: dict[str, dict[str, object]] = {
86
86
  "--diff",
87
87
  "--indent",
88
88
  "--leading-comma --no-leading-comma",
89
+ "--rewrite-sql --no-rewrite-sql",
89
90
  "--sql --no-sql",
90
91
  "-f --script-encoding",
91
92
  "-i --in-place",
@@ -436,6 +436,14 @@ class TestFormatConfig:
436
436
  assert "\n , b" in with_config
437
437
  assert "\n , b" not in overridden
438
438
 
439
+ def test_rewrite_sql_from_config_and_turned_off_by_flag(self, isolated):
440
+ pytest.importorskip("sqlglot")
441
+ (isolated / "s.sql").write_text("select x::int from t;\n")
442
+ assert runner.invoke(app, ["format", "s.sql"]).output == "select x::int from t;\n" # kept as written
443
+ (isolated / "execsql.conf").write_text("[format]\nrewrite_sql = yes\n")
444
+ assert "CAST(x AS INT)" in runner.invoke(app, ["format", "s.sql"]).output
445
+ assert runner.invoke(app, ["format", "--no-rewrite-sql", "s.sql"]).output == "select x::int from t;\n"
446
+
439
447
  def test_config_option_file(self, isolated, tmp_path):
440
448
  conf = tmp_path / "ci.conf"
441
449
  conf.write_text("[format]\nindent = 2\nsql = no\n")
@@ -2804,3 +2804,57 @@ class TestRegressions:
2804
2804
  result = format_file(source, use_sql=True)
2805
2805
  for col in ["id", "name", "value"]:
2806
2806
  assert col in result, f"Column '{col}' dropped from INSERT"
2807
+
2808
+
2809
+ class TestLayoutOnly:
2810
+ """By default only layout and keyword case change; sqlglot's rewrites need rewrite_sql (#72)."""
2811
+
2812
+ @pytest.fixture(autouse=True)
2813
+ def _sqlglot(self):
2814
+ pytest.importorskip("sqlglot")
2815
+
2816
+ @pytest.mark.parametrize(
2817
+ "statement",
2818
+ [
2819
+ "select x::int from t;",
2820
+ "select count(*) n from t;",
2821
+ "create table t (a integer, b numeric(10,2));",
2822
+ "select btrim(name) from t;",
2823
+ "select a from t where a != b;",
2824
+ ],
2825
+ )
2826
+ def test_a_statement_sqlglot_would_rewrite_is_kept_as_written(self, statement):
2827
+ assert format_file(statement + "\n") == statement + "\n"
2828
+
2829
+ def test_rewrite_sql_accepts_the_rewrite(self):
2830
+ assert "CAST(x AS INT)" in format_file("select x::int from t;\n", rewrite_sql=True)
2831
+
2832
+ def test_only_the_rewritten_statement_is_kept(self):
2833
+ out = format_file("select a, b from t;\nselect btrim(n) from u;\nselect c from v;\n")
2834
+ assert out == "SELECT\n a,\n b\nFROM t;\nselect btrim(n) from u;\nSELECT\n c\nFROM v;\n"
2835
+
2836
+ def test_a_statement_already_in_sqlglot_form_is_formatted(self):
2837
+ assert format_file("select cast(x as int) as n from t;\n") == "SELECT\n CAST(x AS INT) AS n\nFROM t;\n"
2838
+
2839
+ def test_keyword_case_alone_is_not_a_rewrite(self):
2840
+ assert format_file("select a from t where b = 'Mixed Case';\n") == (
2841
+ "SELECT\n a\nFROM t\nWHERE\n b = 'Mixed Case';\n"
2842
+ )
2843
+
2844
+ def test_a_kept_statement_keeps_its_comments_and_is_stable(self):
2845
+ source = "select\n x::int, -- the id\n -- the name\n y\nfrom t;\n"
2846
+ once = format_file(source)
2847
+ assert once == source
2848
+ assert format_file(once) == once
2849
+
2850
+ def test_inside_a_block_the_kept_statement_is_stable(self):
2851
+ source = "-- !x! IF(True)\nselect x::int from t;\nselect a from u;\n-- !x! ENDIF\n"
2852
+ once = format_file(source)
2853
+ assert "select x::int from t;" in once
2854
+ assert format_file(once) == once
2855
+
2856
+ @pytest.mark.parametrize("rewrite_sql", [False, True])
2857
+ def test_both_modes_are_idempotent(self, rewrite_sql):
2858
+ source = "select x::int, count(*) n from t where a != b;\nselect a, b from u;\n"
2859
+ once = format_file(source, rewrite_sql=rewrite_sql)
2860
+ assert format_file(once, rewrite_sql=rewrite_sql) == once