execsql2 2.33.0__tar.gz → 2.35.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 (584) hide show
  1. {execsql2-2.33.0 → execsql2-2.35.0}/CHANGELOG.md +36 -0
  2. {execsql2-2.33.0 → execsql2-2.35.0}/CONTRIBUTING.md +13 -0
  3. {execsql2-2.33.0 → execsql2-2.35.0}/PKG-INFO +3 -3
  4. {execsql2-2.33.0 → execsql2-2.35.0}/README.md +2 -2
  5. {execsql2-2.33.0 → execsql2-2.35.0}/docs/about/divergence.md +15 -0
  6. {execsql2-2.33.0 → execsql2-2.35.0}/docs/api/db.md +6 -0
  7. {execsql2-2.33.0 → execsql2-2.35.0}/docs/dev/adding_db_adapters.md +18 -17
  8. {execsql2-2.33.0 → execsql2-2.35.0}/docs/dev/architecture.md +2 -0
  9. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/formatter.md +1 -1
  10. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/sql_syntax.md +20 -0
  11. {execsql2-2.33.0 → execsql2-2.35.0}/docs/index.md +1 -1
  12. {execsql2-2.33.0 → execsql2-2.35.0}/docs/reference/configuration.md +4 -0
  13. {execsql2-2.33.0 → execsql2-2.35.0}/docs/reference/lint.md +1 -1
  14. {execsql2-2.33.0 → execsql2-2.35.0}/docs/reference/metacommands.md +37 -3
  15. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/package-lock.json +2 -2
  16. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/package.json +1 -1
  17. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +2 -2
  18. {execsql2-2.33.0 → execsql2-2.35.0}/pyproject.toml +2 -2
  19. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/api.py +1 -0
  20. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/config.py +2 -0
  21. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/data/execsql.conf.template +7 -0
  22. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/base.py +71 -8
  23. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/duckdb.py +6 -0
  24. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/mysql.py +54 -2
  25. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/oracle.py +36 -0
  26. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/postgres.py +17 -1
  27. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/sqlite.py +7 -1
  28. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/sqlserver.py +24 -0
  29. execsql2-2.35.0/src/execsql/db/target_types.py +277 -0
  30. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/json.py +51 -7
  31. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/protocol.py +1 -0
  32. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/base.py +1 -1
  33. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/csv.py +1 -1
  34. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/__init__.py +17 -1
  35. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/data.py +7 -1
  36. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/dispatch.py +7 -0
  37. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/io_export.py +3 -1
  38. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/io_fileops.py +2 -2
  39. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/reference.py +1 -0
  40. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/models.py +30 -4
  41. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/executor.py +10 -10
  42. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/variables.py +4 -0
  43. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/types.py +2 -2
  44. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/errors.py +2 -2
  45. {execsql2-2.33.0 → execsql2-2.35.0}/tests/conftest.py +1 -0
  46. execsql2-2.35.0/tests/corpus/cases/batch_and_autocommit/script.sql +16 -0
  47. execsql2-2.35.0/tests/corpus/cases/boolean_and_empty_config/data.csv +4 -0
  48. execsql2-2.35.0/tests/corpus/cases/boolean_and_empty_config/script.sql +13 -0
  49. execsql2-2.35.0/tests/corpus/cases/copy_between_databases/script.sql +17 -0
  50. execsql2-2.35.0/tests/corpus/cases/counters/script.sql +11 -0
  51. execsql2-2.35.0/tests/corpus/cases/decimal_digits/long.csv +3 -0
  52. execsql2-2.35.0/tests/corpus/cases/decimal_digits/script.sql +3 -0
  53. execsql2-2.35.0/tests/corpus/cases/decimal_digits/short.csv +4 -0
  54. execsql2-2.35.0/tests/corpus/cases/error_halt_off/script.sql +13 -0
  55. execsql2-2.35.0/tests/corpus/cases/error_stops_script/script.sql +7 -0
  56. execsql2-2.35.0/tests/corpus/cases/execute_script_args/script.sql +21 -0
  57. execsql2-2.35.0/tests/corpus/cases/export_query_append/script.sql +8 -0
  58. execsql2-2.35.0/tests/corpus/cases/export_structured_formats/script.sql +7 -0
  59. execsql2-2.35.0/tests/corpus/cases/export_text_formats/script.sql +9 -0
  60. execsql2-2.35.0/tests/corpus/cases/halt_exit_status/script.sql +5 -0
  61. execsql2-2.35.0/tests/corpus/cases/if_conditions/script.sql +42 -0
  62. execsql2-2.35.0/tests/corpus/cases/import_csv_existing/data.csv +3 -0
  63. execsql2-2.35.0/tests/corpus/cases/import_csv_existing/script.sql +6 -0
  64. execsql2-2.35.0/tests/corpus/cases/import_csv_new/data.csv +4 -0
  65. execsql2-2.35.0/tests/corpus/cases/import_csv_new/data.tsv +3 -0
  66. execsql2-2.35.0/tests/corpus/cases/import_csv_new/script.sql +4 -0
  67. execsql2-2.35.0/tests/corpus/cases/include_file/part.sql +3 -0
  68. execsql2-2.35.0/tests/corpus/cases/include_file/script.sql +6 -0
  69. execsql2-2.35.0/tests/corpus/cases/loop_while_until/script.sql +28 -0
  70. execsql2-2.35.0/tests/corpus/cases/on_error_halt_write/script.sql +7 -0
  71. execsql2-2.35.0/tests/corpus/cases/select_sub_subdata/script.sql +9 -0
  72. execsql2-2.35.0/tests/corpus/cases/sub_backslash/script.sql +7 -0
  73. execsql2-2.35.0/tests/corpus/cases/sub_basic/script.sql +18 -0
  74. execsql2-2.35.0/tests/corpus/cases/sub_quoting/script.sql +6 -0
  75. execsql2-2.35.0/tests/corpus/cases/sub_quoting_upstream_idiom/script.sql +6 -0
  76. execsql2-2.35.0/tests/corpus/cases/unsubstituted_warning/script.sql +7 -0
  77. execsql2-2.35.0/tests/corpus/cases/write_console/script.sql +6 -0
  78. execsql2-2.35.0/tests/corpus/cases/write_create_table/data.csv +3 -0
  79. execsql2-2.35.0/tests/corpus/cases/write_create_table/script.sql +2 -0
  80. execsql2-2.35.0/tests/corpus/changes.json +513 -0
  81. execsql2-2.35.0/tests/corpus/record.py +53 -0
  82. execsql2-2.35.0/tests/corpus/results/batch_and_autocommit.json +37 -0
  83. execsql2-2.35.0/tests/corpus/results/boolean_and_empty_config.json +389 -0
  84. execsql2-2.35.0/tests/corpus/results/copy_between_databases.json +355 -0
  85. execsql2-2.35.0/tests/corpus/results/counters.json +55 -0
  86. execsql2-2.35.0/tests/corpus/results/decimal_digits.json +10 -0
  87. execsql2-2.35.0/tests/corpus/results/error_halt_off.json +39 -0
  88. execsql2-2.35.0/tests/corpus/results/error_stops_script.json +27 -0
  89. execsql2-2.35.0/tests/corpus/results/execute_script_args.json +51 -0
  90. execsql2-2.35.0/tests/corpus/results/export_query_append.json +58 -0
  91. execsql2-2.35.0/tests/corpus/results/export_structured_formats.json +72 -0
  92. execsql2-2.35.0/tests/corpus/results/export_text_formats.json +92 -0
  93. execsql2-2.35.0/tests/corpus/results/halt_exit_status.json +25 -0
  94. execsql2-2.35.0/tests/corpus/results/if_conditions.json +33 -0
  95. execsql2-2.35.0/tests/corpus/results/import_csv_existing.json +113 -0
  96. execsql2-2.35.0/tests/corpus/results/import_csv_new.json +183 -0
  97. execsql2-2.35.0/tests/corpus/results/include_file.json +33 -0
  98. execsql2-2.35.0/tests/corpus/results/loop_while_until.json +51 -0
  99. execsql2-2.35.0/tests/corpus/results/on_error_halt_write.json +20 -0
  100. execsql2-2.35.0/tests/corpus/results/select_sub_subdata.json +45 -0
  101. execsql2-2.35.0/tests/corpus/results/sub_backslash.json +18 -0
  102. execsql2-2.35.0/tests/corpus/results/sub_basic.json +45 -0
  103. execsql2-2.35.0/tests/corpus/results/sub_quoting.json +22 -0
  104. execsql2-2.35.0/tests/corpus/results/sub_quoting_upstream_idiom.json +27 -0
  105. execsql2-2.35.0/tests/corpus/results/unsubstituted_warning.json +27 -0
  106. execsql2-2.35.0/tests/corpus/results/write_console.json +10 -0
  107. execsql2-2.35.0/tests/corpus/results/write_create_table.json +10 -0
  108. execsql2-2.35.0/tests/corpus/runner.py +138 -0
  109. execsql2-2.35.0/tests/corpus/test_script_corpus.py +74 -0
  110. execsql2-2.35.0/tests/data/existing_table_loads/changes.json +5618 -0
  111. execsql2-2.35.0/tests/data/existing_table_loads/duckdb.json +1112 -0
  112. execsql2-2.35.0/tests/data/existing_table_loads/mysql.json +1782 -0
  113. execsql2-2.35.0/tests/data/existing_table_loads/mysql_files.json +2104 -0
  114. execsql2-2.35.0/tests/data/existing_table_loads/postgres.json +1166 -0
  115. execsql2-2.35.0/tests/data/existing_table_loads/postgres_files.json +1228 -0
  116. execsql2-2.35.0/tests/data/existing_table_loads/sqlite.json +1090 -0
  117. execsql2-2.35.0/tests/data/existing_table_loads/sqlserver.json +1307 -0
  118. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/lint/every_metacommand.sql +1 -0
  119. execsql2-2.35.0/tests/db/test_existing_table_loads.py +357 -0
  120. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_postgres_inprocess.py +13 -1
  121. execsql2-2.35.0/tests/db/test_target_types.py +520 -0
  122. execsql2-2.35.0/tests/exporters/test_jsonl.py +101 -0
  123. {execsql2-2.33.0 → execsql2-2.35.0}/tests/integration/test_mysql.py +76 -0
  124. {execsql2-2.33.0 → execsql2-2.35.0}/tests/live_db.py +23 -1
  125. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_log_secrets.py +3 -1
  126. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_models.py +54 -32
  127. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_script.py +13 -0
  128. execsql2-2.35.0/tests/utils/__init__.py +0 -0
  129. {execsql2-2.33.0 → execsql2-2.35.0}/uv.lock +1 -1
  130. {execsql2-2.33.0 → execsql2-2.35.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  131. {execsql2-2.33.0 → execsql2-2.35.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  132. {execsql2-2.33.0 → execsql2-2.35.0}/.github/dependabot.yml +0 -0
  133. {execsql2-2.33.0 → execsql2-2.35.0}/.github/workflows/ci-cd.yml +0 -0
  134. {execsql2-2.33.0 → execsql2-2.35.0}/.gitignore +0 -0
  135. {execsql2-2.33.0 → execsql2-2.35.0}/.pre-commit-config.yaml +0 -0
  136. {execsql2-2.33.0 → execsql2-2.35.0}/.pre-commit-hooks.yaml +0 -0
  137. {execsql2-2.33.0 → execsql2-2.35.0}/.python-version +0 -0
  138. {execsql2-2.33.0 → execsql2-2.35.0}/.readthedocs.yaml +0 -0
  139. {execsql2-2.33.0 → execsql2-2.35.0}/LICENSE.txt +0 -0
  140. {execsql2-2.33.0 → execsql2-2.35.0}/NOTICE +0 -0
  141. {execsql2-2.33.0 → execsql2-2.35.0}/SECURITY.md +0 -0
  142. {execsql2-2.33.0 → execsql2-2.35.0}/docs/about/contributors.md +0 -0
  143. {execsql2-2.33.0 → execsql2-2.35.0}/docs/about/copyright.md +0 -0
  144. {execsql2-2.33.0 → execsql2-2.35.0}/docs/api/cli.md +0 -0
  145. {execsql2-2.33.0 → execsql2-2.35.0}/docs/api/exporters.md +0 -0
  146. {execsql2-2.33.0 → execsql2-2.35.0}/docs/api/importers.md +0 -0
  147. {execsql2-2.33.0 → execsql2-2.35.0}/docs/api/index.md +0 -0
  148. {execsql2-2.33.0 → execsql2-2.35.0}/docs/api/metacommands.md +0 -0
  149. {execsql2-2.33.0 → execsql2-2.35.0}/docs/dev/adding_exporters.md +0 -0
  150. {execsql2-2.33.0 → execsql2-2.35.0}/docs/dev/adding_importers.md +0 -0
  151. {execsql2-2.33.0 → execsql2-2.35.0}/docs/dev/adding_metacommands.md +0 -0
  152. {execsql2-2.33.0 → execsql2-2.35.0}/docs/dev/releasing.md +0 -0
  153. {execsql2-2.33.0 → execsql2-2.35.0}/docs/getting-started/installation.md +0 -0
  154. {execsql2-2.33.0 → execsql2-2.35.0}/docs/getting-started/requirements.md +0 -0
  155. {execsql2-2.33.0 → execsql2-2.35.0}/docs/getting-started/syntax.md +0 -0
  156. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/debugging.md +0 -0
  157. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/documentation.md +0 -0
  158. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/editors.md +0 -0
  159. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/encoding.md +0 -0
  160. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/examples.md +0 -0
  161. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/logging.md +0 -0
  162. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/usage.md +0 -0
  163. {execsql2-2.33.0 → execsql2-2.35.0}/docs/guides/using_scripts.md +0 -0
  164. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/Compare_planets.png +0 -0
  165. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/actions.png +0 -0
  166. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/actions2.png +0 -0
  167. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/checkboxes.png +0 -0
  168. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/connect.png +0 -0
  169. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/data_error1_screenshot.jpg +0 -0
  170. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/execsql_logo_01.png +0 -0
  171. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/fatals.png +0 -0
  172. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/pause_terminal_sm.png +0 -0
  173. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/set_build_commands.jpg +0 -0
  174. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/unit_conversions_029.png +0 -0
  175. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/unmatched.png +0 -0
  176. {execsql2-2.33.0 → execsql2-2.35.0}/docs/images/vim_execsql_highlight.png +0 -0
  177. {execsql2-2.33.0 → execsql2-2.35.0}/docs/reference/security.md +0 -0
  178. {execsql2-2.33.0 → execsql2-2.35.0}/docs/reference/substitution_vars.md +0 -0
  179. {execsql2-2.33.0 → execsql2-2.35.0}/docs/templates/compare.md +0 -0
  180. {execsql2-2.33.0 → execsql2-2.35.0}/docs/templates/glossary.md +0 -0
  181. {execsql2-2.33.0 → execsql2-2.35.0}/docs/templates/index.md +0 -0
  182. {execsql2-2.33.0 → execsql2-2.35.0}/docs/templates/upsert.md +0 -0
  183. {execsql2-2.33.0 → execsql2-2.35.0}/extras/plugin-template/README.md +0 -0
  184. {execsql2-2.33.0 → execsql2-2.35.0}/extras/plugin-template/pyproject.toml +0 -0
  185. {execsql2-2.33.0 → execsql2-2.35.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
  186. {execsql2-2.33.0 → execsql2-2.35.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
  187. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/.vscodeignore +0 -0
  188. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/LICENSE.txt +0 -0
  189. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/README.md +0 -0
  190. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/extension.js +0 -0
  191. {execsql2-2.33.0 → execsql2-2.35.0}/extras/vscode-execsql/icon.png +0 -0
  192. {execsql2-2.33.0 → execsql2-2.35.0}/justfile +0 -0
  193. {execsql2-2.33.0 → execsql2-2.35.0}/scripts/benchmarks/README.md +0 -0
  194. {execsql2-2.33.0 → execsql2-2.35.0}/scripts/benchmarks/results/.gitignore +0 -0
  195. {execsql2-2.33.0 → execsql2-2.35.0}/scripts/benchmarks/run_benchmarks.py +0 -0
  196. {execsql2-2.33.0 → execsql2-2.35.0}/scripts/generate_vscode_grammar.py +0 -0
  197. {execsql2-2.33.0 → execsql2-2.35.0}/scripts/release_notes.py +0 -0
  198. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/__init__.py +0 -0
  199. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/__main__.py +0 -0
  200. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/__init__.py +0 -0
  201. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/application.py +0 -0
  202. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/__init__.py +0 -0
  203. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/config.py +0 -0
  204. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/format.py +0 -0
  205. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/init.py +0 -0
  206. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/lint.py +0 -0
  207. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/listing.py +0 -0
  208. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/lsp.py +0 -0
  209. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/commands/run.py +0 -0
  210. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/dispatch.py +0 -0
  211. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/dsn.py +0 -0
  212. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/help.py +0 -0
  213. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/lint.py +0 -0
  214. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/options.py +0 -0
  215. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/cli/run.py +0 -0
  216. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/config_validation.py +0 -0
  217. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/data/__init__.py +0 -0
  218. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/data/script_header.sql +0 -0
  219. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/__init__.py +0 -0
  220. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/access.py +0 -0
  221. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/dsn.py +0 -0
  222. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/factory.py +0 -0
  223. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/firebird.py +0 -0
  224. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/odbc.py +0 -0
  225. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/db/tiers.py +0 -0
  226. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/debug/__init__.py +0 -0
  227. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/debug/repl.py +0 -0
  228. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exceptions.py +0 -0
  229. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/__init__.py +0 -0
  230. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/base.py +0 -0
  231. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/delimited.py +0 -0
  232. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/duckdb.py +0 -0
  233. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/feather.py +0 -0
  234. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/html.py +0 -0
  235. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/latex.py +0 -0
  236. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/markdown.py +0 -0
  237. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/ods.py +0 -0
  238. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/parquet.py +0 -0
  239. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/pretty.py +0 -0
  240. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/raw.py +0 -0
  241. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/sqlite.py +0 -0
  242. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/templates.py +0 -0
  243. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/values.py +0 -0
  244. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/xls.py +0 -0
  245. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/xlsx.py +0 -0
  246. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/xml.py +0 -0
  247. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/yaml.py +0 -0
  248. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/exporters/zip.py +0 -0
  249. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/format.py +0 -0
  250. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/gui/__init__.py +0 -0
  251. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/gui/base.py +0 -0
  252. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/gui/console.py +0 -0
  253. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/gui/desktop.py +0 -0
  254. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/gui/tui.py +0 -0
  255. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/__init__.py +0 -0
  256. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/feather.py +0 -0
  257. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/json.py +0 -0
  258. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/ods.py +0 -0
  259. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/importers/xls.py +0 -0
  260. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/interactive.py +0 -0
  261. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/__init__.py +0 -0
  262. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/_util.py +0 -0
  263. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/cache.py +0 -0
  264. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/code_actions.py +0 -0
  265. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/completion.py +0 -0
  266. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/diagnostics.py +0 -0
  267. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/document.py +0 -0
  268. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/folding.py +0 -0
  269. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/formatting.py +0 -0
  270. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/hover.py +0 -0
  271. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/navigation.py +0 -0
  272. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/rename.py +0 -0
  273. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/lsp/server.py +0 -0
  274. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/manifest.py +0 -0
  275. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/conditions.py +0 -0
  276. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/connect.py +0 -0
  277. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/control.py +0 -0
  278. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/debug.py +0 -0
  279. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/effects.py +0 -0
  280. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/io.py +0 -0
  281. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/io_import.py +0 -0
  282. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/io_write.py +0 -0
  283. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/prompt.py +0 -0
  284. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/script_ext.py +0 -0
  285. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/system.py +0 -0
  286. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/metacommands/upsert.py +0 -0
  287. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/parser.py +0 -0
  288. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/plugins.py +0 -0
  289. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/py.typed +0 -0
  290. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/__init__.py +0 -0
  291. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/ast.py +0 -0
  292. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/control.py +0 -0
  293. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/engine.py +0 -0
  294. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/script/parser.py +0 -0
  295. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/state.py +0 -0
  296. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/state.pyi +0 -0
  297. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/__init__.py +0 -0
  298. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/auth.py +0 -0
  299. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/color.py +0 -0
  300. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/crypto.py +0 -0
  301. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/datetime.py +0 -0
  302. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/fileio.py +0 -0
  303. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/gui.py +0 -0
  304. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/mail.py +0 -0
  305. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/numeric.py +0 -0
  306. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/paths.py +0 -0
  307. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/regex.py +0 -0
  308. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/strings.py +0 -0
  309. {execsql2-2.33.0 → execsql2-2.35.0}/src/execsql/utils/timer.py +0 -0
  310. {execsql2-2.33.0 → execsql2-2.35.0}/templates/README.md +0 -0
  311. {execsql2-2.33.0 → execsql2-2.35.0}/templates/config_settings.sqlite +0 -0
  312. {execsql2-2.33.0 → execsql2-2.35.0}/templates/example_config_prompt.sql +0 -0
  313. {execsql2-2.33.0 → execsql2-2.35.0}/templates/make_config_db.sql +0 -0
  314. {execsql2-2.33.0 → execsql2-2.35.0}/templates/md_compare.sql +0 -0
  315. {execsql2-2.33.0 → execsql2-2.35.0}/templates/md_glossary.sql +0 -0
  316. {execsql2-2.33.0 → execsql2-2.35.0}/templates/md_upsert.sql +0 -0
  317. {execsql2-2.33.0 → execsql2-2.35.0}/templates/pg_compare.sql +0 -0
  318. {execsql2-2.33.0 → execsql2-2.35.0}/templates/pg_glossary.sql +0 -0
  319. {execsql2-2.33.0 → execsql2-2.35.0}/templates/script_template.sql +0 -0
  320. {execsql2-2.33.0 → execsql2-2.35.0}/templates/ss_compare.sql +0 -0
  321. {execsql2-2.33.0 → execsql2-2.35.0}/templates/ss_glossary.sql +0 -0
  322. {execsql2-2.33.0 → execsql2-2.35.0}/templates/ss_upsert.sql +0 -0
  323. {execsql2-2.33.0 → execsql2-2.35.0}/tests/__init__.py +0 -0
  324. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/__init__.py +0 -0
  325. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_cli.py +0 -0
  326. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_cli_e2e.py +0 -0
  327. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_cli_run.py +0 -0
  328. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_cli_surface.py +0 -0
  329. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_commands.py +0 -0
  330. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_config_validate.py +0 -0
  331. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_dispatch.py +0 -0
  332. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_error_location.py +0 -0
  333. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_init.py +0 -0
  334. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_lint.py +0 -0
  335. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_lint_fixtures.py +0 -0
  336. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_log_exit_record.py +0 -0
  337. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_manifest.py +0 -0
  338. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_ping.py +0 -0
  339. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_profile.py +0 -0
  340. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_sigterm.py +0 -0
  341. {execsql2-2.33.0 → execsql2-2.35.0}/tests/cli/test_undeclared_imports.py +0 -0
  342. {execsql2-2.33.0/tests/db → execsql2-2.35.0/tests/corpus}/__init__.py +0 -0
  343. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/formatter_shapes/comment_anchoring.sql +0 -0
  344. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/formatter_shapes/escapes_and_literals.sql +0 -0
  345. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/formatter_shapes/substitution_and_metacommands.sql +0 -0
  346. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/formatter_shapes/unparsable_sql.sql +0 -0
  347. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/lint/conditions_and_blocks.sql +0 -0
  348. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/lint/does_not_parse.sql +0 -0
  349. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/lint/invalid.sql +0 -0
  350. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/ods/libreoffice.fods +0 -0
  351. {execsql2-2.33.0 → execsql2-2.35.0}/tests/data/ods/libreoffice.ods +0 -0
  352. {execsql2-2.33.0/tests/exporters → execsql2-2.35.0/tests/db}/__init__.py +0 -0
  353. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/conftest.py +0 -0
  354. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_access_windows.py +0 -0
  355. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_base.py +0 -0
  356. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_bulk_inserts.py +0 -0
  357. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_copy_new.py +0 -0
  358. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_db_adapters_mocked.py +0 -0
  359. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_db_adapters_mocked_extra.py +0 -0
  360. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_dsn.py +0 -0
  361. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_duckdb.py +0 -0
  362. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_factory.py +0 -0
  363. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_factory_postgres_encoding.py +0 -0
  364. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_import_atomic.py +0 -0
  365. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_import_replacement_quoted.py +0 -0
  366. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_import_short_rows.py +0 -0
  367. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_import_timezones.py +0 -0
  368. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_mysql_case_folding.py +0 -0
  369. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_mysql_inprocess.py +0 -0
  370. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_mysql_trailing_blank_lines.py +0 -0
  371. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_new_table_checks.py +0 -0
  372. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_odbc_fresh_sessions.py +0 -0
  373. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_oracle_driver.py +0 -0
  374. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_postgres.py +0 -0
  375. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_quoted_substitution_roundtrip.py +0 -0
  376. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_replacement_missing_table.py +0 -0
  377. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_roundtrip_adapters.py +0 -0
  378. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_sqlite.py +0 -0
  379. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_sqlite_extra.py +0 -0
  380. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_sqlserver_driver18.py +0 -0
  381. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_sqlserver_inprocess.py +0 -0
  382. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_tiers.py +0 -0
  383. {execsql2-2.33.0 → execsql2-2.35.0}/tests/db/test_transactions.py +0 -0
  384. {execsql2-2.33.0/tests/gui → execsql2-2.35.0/tests/exporters}/__init__.py +0 -0
  385. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/conftest.py +0 -0
  386. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_base.py +0 -0
  387. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_db.py +0 -0
  388. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_delimited.py +0 -0
  389. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_duckdb_exporter.py +0 -0
  390. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_exporters.py +0 -0
  391. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_feather.py +0 -0
  392. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_html_extended.py +0 -0
  393. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_html_latex.py +0 -0
  394. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_json.py +0 -0
  395. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_json_extended.py +0 -0
  396. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_latex_extended.py +0 -0
  397. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_markdown.py +0 -0
  398. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_ods.py +0 -0
  399. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_ods_export.py +0 -0
  400. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_parquet.py +0 -0
  401. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_pretty_extended.py +0 -0
  402. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_raw_extended.py +0 -0
  403. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_columnar.py +0 -0
  404. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_dbfile.py +0 -0
  405. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_delimited.py +0 -0
  406. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_json.py +0 -0
  407. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_latex_xml.py +0 -0
  408. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_markup.py +0 -0
  409. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_ods.py +0 -0
  410. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_pretty_raw.py +0 -0
  411. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_template.py +0 -0
  412. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_values.py +0 -0
  413. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_roundtrip_xlsx.py +0 -0
  414. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_sqlite_exporter.py +0 -0
  415. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_templates.py +0 -0
  416. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_templates_extended.py +0 -0
  417. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_values_extended.py +0 -0
  418. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_values_roundtrip.py +0 -0
  419. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_xls_xlsx.py +0 -0
  420. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_xlsx.py +0 -0
  421. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_xml.py +0 -0
  422. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_yaml.py +0 -0
  423. {execsql2-2.33.0 → execsql2-2.35.0}/tests/exporters/test_zip.py +0 -0
  424. {execsql2-2.33.0/tests/importers → execsql2-2.35.0/tests/gui}/__init__.py +0 -0
  425. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_backends.py +0 -0
  426. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_backends_extended.py +0 -0
  427. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_compare_stats.py +0 -0
  428. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_compute_row_diffs.py +0 -0
  429. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_desktop_dialogs.py +0 -0
  430. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_format_table.py +0 -0
  431. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_tui_pilot.py +0 -0
  432. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_tui_pilot_complex.py +0 -0
  433. {execsql2-2.33.0 → execsql2-2.35.0}/tests/gui/test_utils_gui_extended.py +0 -0
  434. {execsql2-2.33.0/tests/integration → execsql2-2.35.0/tests/importers}/__init__.py +0 -0
  435. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/conftest.py +0 -0
  436. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_base_extended.py +0 -0
  437. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_csv_blank_lines.py +0 -0
  438. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_csv_edge_cases.py +0 -0
  439. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_csv_importer.py +0 -0
  440. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_feather_importer.py +0 -0
  441. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_json_importer.py +0 -0
  442. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_ods_importer.py +0 -0
  443. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_ods_values.py +0 -0
  444. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_roundtrip_csv.py +0 -0
  445. {execsql2-2.33.0 → execsql2-2.35.0}/tests/importers/test_xls_importer.py +0 -0
  446. {execsql2-2.33.0/tests/lsp → execsql2-2.35.0/tests/integration}/__init__.py +0 -0
  447. {execsql2-2.33.0 → execsql2-2.35.0}/tests/integration/conftest.py +0 -0
  448. {execsql2-2.33.0 → execsql2-2.35.0}/tests/integration/test_dsn.py +0 -0
  449. {execsql2-2.33.0 → execsql2-2.35.0}/tests/integration/test_duckdb.py +0 -0
  450. {execsql2-2.33.0 → execsql2-2.35.0}/tests/integration/test_postgres.py +0 -0
  451. {execsql2-2.33.0 → execsql2-2.35.0}/tests/integration/test_sqlite.py +0 -0
  452. {execsql2-2.33.0/tests/metacommands → execsql2-2.35.0/tests/lsp}/__init__.py +0 -0
  453. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_cache.py +0 -0
  454. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_code_actions.py +0 -0
  455. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_completion.py +0 -0
  456. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_diagnostics.py +0 -0
  457. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_document.py +0 -0
  458. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_folding.py +0 -0
  459. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_formatting.py +0 -0
  460. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_hover.py +0 -0
  461. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_lsp_command.py +0 -0
  462. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_navigation.py +0 -0
  463. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_rename.py +0 -0
  464. {execsql2-2.33.0 → execsql2-2.35.0}/tests/lsp/test_server.py +0 -0
  465. {execsql2-2.33.0/tests/scripts → execsql2-2.35.0/tests/metacommands}/__init__.py +0 -0
  466. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/conftest.py +0 -0
  467. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_assert.py +0 -0
  468. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_breakpoint.py +0 -0
  469. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_conditions_extra.py +0 -0
  470. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_connect.py +0 -0
  471. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_io_export.py +0 -0
  472. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_io_import.py +0 -0
  473. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands.py +0 -0
  474. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_connect.py +0 -0
  475. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_data.py +0 -0
  476. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_extended.py +0 -0
  477. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
  478. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_io.py +0 -0
  479. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
  480. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
  481. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_system.py +0 -0
  482. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
  483. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_pg_upsert.py +0 -0
  484. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_prompt.py +0 -0
  485. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_row_count.py +0 -0
  486. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_serve_output.py +0 -0
  487. {execsql2-2.33.0 → execsql2-2.35.0}/tests/metacommands/test_show_scripts.py +0 -0
  488. {execsql2-2.33.0/tests/security → execsql2-2.35.0/tests/scripts}/__init__.py +0 -0
  489. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
  490. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
  491. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
  492. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/audit_smoke.sql +0 -0
  493. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
  494. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/control_flow.sql +0 -0
  495. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
  496. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/error_handling.sql +0 -0
  497. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
  498. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/import_options.sql +0 -0
  499. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
  500. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/includes.sql +0 -0
  501. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/io_formats.sql +0 -0
  502. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
  503. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/multi_db.sql +0 -0
  504. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
  505. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
  506. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/predicates.sql +0 -0
  507. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/scripts.sql +0 -0
  508. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/smoke.sql +0 -0
  509. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
  510. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
  511. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
  512. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/template_export.sql +0 -0
  513. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/timer.sql +0 -0
  514. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/transactions.sql +0 -0
  515. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
  516. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
  517. {execsql2-2.33.0 → execsql2-2.35.0}/tests/scripts/test_sql_scripts.py +0 -0
  518. {execsql2-2.33.0/tests/templates → execsql2-2.35.0/tests/security}/__init__.py +0 -0
  519. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_env_detection.py +0 -0
  520. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_expansion_bomb.py +0 -0
  521. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_import_regex_hardening.py +0 -0
  522. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_log_redaction.py +0 -0
  523. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_network_paths.py +0 -0
  524. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_odbc_connection_strings.py +0 -0
  525. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_odbc_injection.py +0 -0
  526. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_path_containment.py +0 -0
  527. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_substitution_injection.py +0 -0
  528. {execsql2-2.33.0 → execsql2-2.35.0}/tests/security/test_zip_bomb.py +0 -0
  529. {execsql2-2.33.0/tests/utils → execsql2-2.35.0/tests/templates}/__init__.py +0 -0
  530. {execsql2-2.33.0 → execsql2-2.35.0}/tests/templates/conftest.py +0 -0
  531. {execsql2-2.33.0 → execsql2-2.35.0}/tests/templates/test_compare.py +0 -0
  532. {execsql2-2.33.0 → execsql2-2.35.0}/tests/templates/test_glossary.py +0 -0
  533. {execsql2-2.33.0 → execsql2-2.35.0}/tests/templates/test_static.py +0 -0
  534. {execsql2-2.33.0 → execsql2-2.35.0}/tests/templates/test_upsert.py +0 -0
  535. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_api.py +0 -0
  536. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_api_errors.py +0 -0
  537. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_api_extended.py +0 -0
  538. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_api_filewriter.py +0 -0
  539. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_api_isolation.py +0 -0
  540. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_api_subprocess.py +0 -0
  541. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_ast.py +0 -0
  542. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_ast_parser.py +0 -0
  543. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_bundled_templates.py +0 -0
  544. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_config.py +0 -0
  545. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_config_data.py +0 -0
  546. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_config_extended.py +0 -0
  547. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_date_inference.py +0 -0
  548. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_debug_repl.py +0 -0
  549. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_engine.py +0 -0
  550. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_error_messages.py +0 -0
  551. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_exceptions.py +0 -0
  552. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_executor.py +0 -0
  553. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_executor_inprocess.py +0 -0
  554. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_format.py +0 -0
  555. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_interactive.py +0 -0
  556. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_mail.py +0 -0
  557. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_metacommand_errors.py +0 -0
  558. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_package.py +0 -0
  559. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_parser.py +0 -0
  560. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_parser_params.py +0 -0
  561. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_plugins.py +0 -0
  562. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_reference.py +0 -0
  563. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_registry.py +0 -0
  564. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_release_notes.py +0 -0
  565. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_state.py +0 -0
  566. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_tooling_versions.py +0 -0
  567. {execsql2-2.33.0 → execsql2-2.35.0}/tests/test_types.py +0 -0
  568. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_auth.py +0 -0
  569. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_auth_extra.py +0 -0
  570. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_crypto.py +0 -0
  571. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_datetime.py +0 -0
  572. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_encodedfile_context.py +0 -0
  573. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_errors.py +0 -0
  574. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_errors_extra.py +0 -0
  575. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_fileio.py +0 -0
  576. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_fileio_extra.py +0 -0
  577. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_keyring_rules.py +0 -0
  578. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_mail_tls.py +0 -0
  579. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_numeric.py +0 -0
  580. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_regex.py +0 -0
  581. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_strings.py +0 -0
  582. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_timer.py +0 -0
  583. {execsql2-2.33.0 → execsql2-2.35.0}/tests/utils/test_timer_extra.py +0 -0
  584. {execsql2-2.33.0 → execsql2-2.35.0}/zensical.toml +0 -0
@@ -13,6 +13,42 @@ ______________________________________________________________________
13
13
 
14
14
  ______________________________________________________________________
15
15
 
16
+ ## [2.35.0] - 2026-10-18
17
+
18
+ ### Added
19
+
20
+ - **`exact_decimals`** (`[input]`) and **`CONFIG EXACT_DECIMALS YES|NO`**: with Yes, `IMPORT` and `COPY … TO NEW` give a column of decimals with different numbers of places an exact `DECIMAL(p,s)` type instead of a floating-point one.
21
+
22
+ ### Changed
23
+
24
+ - **A column of decimals with different numbers of places** (`1.50`, `2`, `-3.25`) gets a floating-point type again when `IMPORT` or `COPY … TO NEW` creates a table (it was `DECIMAL(p,s)` from 2.22.0). A column with a value of more than 15 significant digits stays `DECIMAL(p,s)`, so no digit is lost. Set `exact_decimals` to keep every such column exact.
25
+
26
+ ### Fixed
27
+
28
+ - **The "potential un-substituted variable" warning** is printed only when `write_warnings` is on, to stderr as `**** Warning …`, as documented; it was printed to stdout for every command with an undefined `!!var!!`. It is always written to `execsql.log`.
29
+
30
+ ______________________________________________________________________
31
+
32
+ ## [2.34.0] - 2026-10-18
33
+
34
+ ### Added
35
+
36
+ - **`EXPORT … AS JSONL` and `EXPORT QUERY … AS JSONL`** write [JSON Lines](https://jsonlines.org/): one object per row, one row per line. `APPEND` adds rows to the file, which stays valid; `IMPORT … FROM JSON` reads it back.
37
+
38
+ ### Changed
39
+
40
+ - **A value with a fraction loaded into an integer column** (`INTEGER`, `BIGINT`, `DECIMAL(10,0)`, …) of an existing table by `IMPORT` or `COPY` stops the load with an error that names the value and the column, and nothing is loaded. The database used to round it (`1.5` stored as `2`), or on SQL Server truncate it. A delimited file loaded into PostgreSQL or MySQL is not affected: the server reads it as written. Round the values in the source, or load them into a `DECIMAL` column with places for the fraction.
41
+ - **MySQL / MariaDB sessions run in strict mode**: a SQL statement, `IMPORT` or `COPY` that gives a column a value MySQL can't store as given stops with MySQL's error, and nothing is loaded. MySQL used to store a changed value with a warning (`3000000000` as `2147483647`, `2024-02-30` as `0000-00-00`, `abcdef` in a `VARCHAR(3)` as `abc`). A timestamp ending in `Z` is one such value: write it with `+00:00`. To keep the old behavior in a script, run `SET SESSION sql_mode = 'ANSI';` after connecting.
42
+ - **Scripts with many substitution variables run faster**: a statement or metacommand with no `!!`, `!'!` or `!"!` in it is no longer checked against every defined variable. With 500 variables each such line took about 0.1 ms longer; a 1,000-pass `LOOP` over `EXECUTE SCRIPT` with 200 variables now runs in half the time.
43
+
44
+ ### Fixed
45
+
46
+ - **`IMPORT` and `COPY` into a table that already exists** convert each value by the type of the column it goes into, not the type guessed from the source data. A text column gets the value as written: `yes`, `1`, `01/05/2024`, `1e3` and `" 7 "` were stored as `true`, `true`, `2024-01-05 00:00:00`, `1000` and `7`. On MySQL a missing value in such a column is NULL, not `''`. `TO NEW` and `TO REPLACEMENT` tables are created and filled as before.
47
+ - **`COPY` of a column of 0s and 1s into a PostgreSQL integer, numeric or float column** loads the numbers; it failed with "column … is of type integer but expression is of type boolean". The same goes for true/false values (`yes`/`no`, `t`/`f`) copied, or imported from a spreadsheet, JSON or Feather file, into such a column: they load as 1 and 0, as on the other databases.
48
+ - **A missing date or timestamp loaded into MySQL by `IMPORT` or `COPY`** is stored as NULL; it was stored as `0000-00-00` (or `0` in a number column). Delimited files were not affected.
49
+
50
+ ______________________________________________________________________
51
+
16
52
  ## [2.33.0] - 2026-10-16
17
53
 
18
54
  ### Added
@@ -64,6 +64,19 @@ just test-templates-down # removes the containers
64
64
 
65
65
  SQL Server is included only when an ODBC driver for it is installed. A template bug the tests have found but not yet fixed is marked with `known_bug(...)`, a strict expected failure: the fix makes the test pass, which fails the run until the marker is removed with it.
66
66
 
67
+ ### Script behavior corpus
68
+
69
+ `tests/corpus/` checks that whole scripts do under execsql2 what they did under upstream execsql 1.130.1: the same exit status, console output, files written and table contents. Each case is a directory in `tests/corpus/cases/` holding `script.sql` and the files it reads. Upstream's results are recorded in `tests/corpus/results/`, so the normal test run compares against them without upstream installed. A variable a case checks is `WRITE`n to a file, because upstream has no way to read variables after a run.
70
+
71
+ To add a case, create its directory, then record what upstream does (this installs execsql 1.130.1 from PyPI under Python 3.12 into `~/.cache/execsql-corpus/` on first use):
72
+
73
+ ```bash
74
+ uv run python -m tests.corpus.record my_new_case
75
+ uv run pytest tests/corpus
76
+ ```
77
+
78
+ Use only syntax upstream accepts; read `_execsql/execsql.py` when unsure. A case must not open a GUI dialog (`HALT "message"`, `PROMPT`) or loop forever under upstream: both hang the recording. A difference from upstream fails the test unless `tests/corpus/changes.json` lists it with both values, a `status` (`accepted` for an upstream bug execsql2 fixed on purpose, `open` for one not yet decided) and the reason.
79
+
67
80
  ## Building the Docs
68
81
 
69
82
  ```bash
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: execsql2
3
- Version: 2.33.0
3
+ Version: 2.35.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/en/stable/
6
6
  Project-URL: Repository, https://github.com/geocoug/execsql
@@ -318,7 +318,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/stable/getti
318
318
  **Data movement**
319
319
 
320
320
  - Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
321
- - Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
321
+ - Export query results in 20+ formats including CSV, TSV, JSON, JSON Lines, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
322
322
  - Copy data between databases, including across different DBMS types.
323
323
 
324
324
  **Script logic**
@@ -441,7 +441,7 @@ execsql format --no-sql --in-place scripts/
441
441
  ```yaml
442
442
  repos:
443
443
  - repo: https://github.com/geocoug/execsql
444
- rev: v2.33.0
444
+ rev: v2.35.0
445
445
  hooks:
446
446
  - id: execsql-format
447
447
  - id: execsql-lint
@@ -198,7 +198,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/stable/getti
198
198
  **Data movement**
199
199
 
200
200
  - Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
201
- - Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
201
+ - Export query results in 20+ formats including CSV, TSV, JSON, JSON Lines, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
202
202
  - Copy data between databases, including across different DBMS types.
203
203
 
204
204
  **Script logic**
@@ -321,7 +321,7 @@ execsql format --no-sql --in-place scripts/
321
321
  ```yaml
322
322
  repos:
323
323
  - repo: https://github.com/geocoug/execsql
324
- rev: v2.33.0
324
+ rev: v2.35.0
325
325
  hooks:
326
326
  - id: execsql-format
327
327
  - id: execsql-lint
@@ -43,6 +43,7 @@ ______________________________________________________________________
43
43
  | `PARQUET` | Export query or table results to Apache Parquet via `polars`. |
44
44
  | `FEATHER` | Export to Apache Feather/IPC via `polars` (upstream used `pandas` + `pyarrow`). |
45
45
  | `YAML` | Export query or table results as a YAML sequence of mappings via `PyYAML`. |
46
+ | `JSONL` | Export query or table results as JSON Lines: one object per row, one row per line; `APPEND` adds rows (#7). |
46
47
  | `MARKDOWN` / `MD` | Export query or table results as a GitHub-Flavored Markdown (GFM) pipe table. Pure Python, no optional dependencies. |
47
48
  | `XLSX` | Export query or table results to an Excel XLSX workbook via `openpyxl` (single or multi-sheet). |
48
49
 
@@ -91,6 +92,7 @@ New options in `execsql.conf`:
91
92
  | -------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
92
93
  | `use_keyring` | `[connect]` | Use the OS keyring for credential storage (default: `yes`). |
93
94
  | `trust_server_certificate` | `[connect]` | SQL Server: accept a self-signed server certificate (ODBC Driver 18 verifies it by default); also `?trust_server_certificate=yes` on an `mssql://` URL. Default: `no`. |
95
+ | `exact_decimals` | `[input]` | Give a column of decimals with different numbers of places an exact `DECIMAL(p,s)` type instead of a float (default: `no`); also `CONFIG EXACT_DECIMALS`. |
94
96
  | `show_progress` | `[input]` | Enable Rich progress bar for IMPORT (default: `no`). |
95
97
  | `import_progress_interval` | `[input]` | Log a status line every N rows during IMPORT (default: `0`). |
96
98
  | `gui_framework` | `[interface]` | GUI backend: `tkinter` (default) or `textual` (terminal UI). |
@@ -228,6 +230,12 @@ Upstream opened MySQL and MariaDB connections as `latin1`, and execsql2 did the
228
230
 
229
231
  The default is now `utf8mb4`. This does not change stored data or column charsets: the connection charset governs only how text moves between client and server, and MySQL transcodes to and from each column's own charset. A latin1 column read over a utf8mb4 connection returns the same string it always did. `-e latin1` restores the upstream behavior.
230
232
 
233
+ ### MySQL Strict Mode
234
+
235
+ Upstream set `sql_mode='ANSI'` on every MySQL and MariaDB connection. That replaces the server's whole mode, and `ANSI` leaves out `STRICT_TRANS_TABLES`, so MySQL stored a changed value with a warning instead of raising an error: `3000000000` in an `INTEGER` column became `2147483647`, `2024-02-30` became `0000-00-00`, and `abcdef` in a `VARCHAR(3)` became `abc`. That applied to plain SQL statements, `IMPORT` and `COPY` alike (#101).
236
+
237
+ The mode is now `ANSI,STRICT_TRANS_TABLES`, so such a value is an error, as in the `mysql` client with the server's default settings. The mode stays fixed rather than added to the server's own, because execsql's quoting of literals relies on backslash escapes being on (no `NO_BACKSLASH_ESCAPES`). `LOAD DATA LOCAL`, which `IMPORT` uses for a delimited file, ignores strict mode, so `IMPORT` checks its warnings and rolls the load back if MySQL changed a value. `SET SESSION sql_mode = 'ANSI';` in a script restores the upstream behavior for that session, `LOAD DATA` included.
238
+
231
239
  ### Database Support Tiers
232
240
 
233
241
  Upstream documented all nine DBMS adapters with equal confidence. execsql2 splits them by what is actually verified: **Supported** (PostgreSQL, MySQL/MariaDB, MS SQL Server, SQLite, DuckDB) run against a live server or a real database file on every CI run, while **best effort** (MS Access, Firebird, Oracle, ODBC DSN) are carried forward from the monolith and are exercised nowhere in CI.
@@ -250,6 +258,7 @@ All 33 mutable runtime globals in `state.py` have been consolidated into a `Runt
250
258
 
251
259
  - **`$HOSTNAME`** — Network name of the machine running execsql (`platform.node()`). Useful for log messages and environment detection.
252
260
  - **`$SYSTEM_CMD_PID`** — New system variable set to the PID of the background process when `SHELL … CONTINUE` is used.
261
+ - **`!'!var!'!` adds the quotes** — `!'!name!'!` becomes a complete string literal: the value is wrapped in single quotes and its apostrophes doubled (`O'Brien` → `'O''Brien'`), so a script writes `where author = !'!name!'!`. Upstream only doubled the apostrophes, so its scripts wrote the quotes themselves (`'!'!name!'!'`); in execsql2 that form has two sets of quotes and fails. Remove the outer quotes when moving such a script.
253
262
  - **Cycle detection** — `substitute_vars()` raises an error after 100 iterations to prevent infinite loops when variables reference each other cyclically. Upstream had no protection.
254
263
  - **O(1) substitution** — Variable substitution uses a single combined regex and dict lookup instead of O(V) per-variable regex passes. Behavior is identical; performance is improved.
255
264
  - **Lazy `$RANDOM`/`$UUID`** — These system variables are now computed on first access rather than generated unconditionally for every statement. Behavior is identical when referenced; scripts that never reference them skip the computation entirely.
@@ -281,6 +290,7 @@ The legacy flat command-list engine (`_parse_script_lines` / `runscripts` / `Com
281
290
 
282
291
  - **Native INCLUDE handling** — The AST executor parses INCLUDE'd files with the AST parser and executes them through the tree-walking executor. Control flow structures (IF/LOOP/BATCH/SCRIPT) in included files are fully tree-driven, with correct deferred variable handling and source-span error reporting.
283
292
  - **Circular INCLUDE detection** — The executor tracks the full include chain and detects circular references (e.g. A includes B includes A), reporting the full chain in the error message. Upstream has no such detection.
293
+ - **LOOP conditions are read on every pass** — The variables in a `LOOP WHILE` / `LOOP UNTIL` condition are substituted afresh before each pass, so `LOOP WHILE (IS_GT(10, !!n!!))` with `SUB_ADD n 1` in the body stops when `n` reaches 10. Upstream substituted them once, when the `LOOP` line ran, so a condition on a variable already defined never changed: the loop ran until a `BREAK`, or forever. Upstream scripts got per-pass conditions only from variables not yet defined when the loop started ("phantom variables") or from deferred `!{var}!` references, and both still work.
284
294
  - **BREAK outside LOOP is an error** — `BREAK` outside a loop block now raises an error (exit 1) instead of being silently ignored. This catches script bugs that upstream would not report.
285
295
  - **File writer is a thread, one per run** — `WRITE`, `TEE` and the other text-file metacommands hand their output to a background thread owned by the run's `RuntimeContext`, which `execsql.run()` stops before returning. Upstream uses one `multiprocessing` subprocess for the whole program; under the `spawn` start method (macOS, Windows) that re-imported the calling program, so a library caller without an `if __name__ == "__main__":` guard ran its script twice.
286
296
  - **Instance-scoped script registry** — Named SCRIPT blocks are stored on the `RuntimeContext` instance instead of a module-level dict, preventing cross-execution contamination.
@@ -428,6 +438,8 @@ These are behavioral changes driven by security or correctness issues in the ups
428
438
  | DuckDB float columns were single precision | `dbt_duckdb` mapped `DT_Float` to `REAL`. `DT_Float` is documented as IEEE double precision and a Python float always is one, so every float written to DuckDB was narrowed: `1234.567` was stored and returned as `1234.5670166015625`. PostgreSQL, SQLite, SQL Server, MySQL, Access, Firebird and DSN all map `DT_Float` to a double; DuckDB was the only backend that did not, and is now `DOUBLE`. |
429
439
  | VALUES export wrote dates and timestamps unquoted | The literal renderer quoted `str` and emitted `NULL` for `None`, but sent everything else through `str()` bare. A `date` became `2026-09-24` in the generated `INSERT`, which is not a syntax error — it is a valid SQL expression evaluating to 1993, so PostgreSQL inserted that and reported nothing. A `timestamp` contains a space and failed outright. Quoting is now the default and only numbers are emitted bare, so a type the renderer has not been taught about is safe rather than silently wrong. |
430
440
  | LaTeX export escaped only the underscore | Cells were written as `str(c).replace("_", r"\_")`, leaving every other LaTeX special raw. `&` is the column separator this exporter itself emits, so an ampersand in a value added a column and the `.tex` no longer compiled; `%` commented out the rest of the row. All of `\ & % $ # _ { } ~ ^` are now escaped, in both the header and the body. |
441
+ | `ON ERROR_HALT WRITE … TO` a file wrote nothing | Upstream passed the `(text, flag)` pair that variable substitution returns on as the text, so writing the message failed after the file was opened and the file stayed empty. The message is written. |
442
+ | Mixed-scale decimals keep every digit | `IMPORT` / `COPY … TO NEW` gave a column of decimals with different numbers of places (`1.50`, `2`, `-3.25`) a floating-point type, which also held values a double cannot store exactly: `1234567890.1234567891` was stored as `1234567890.1234567`. Such a column is still a float, unless a value has more than 15 significant digits; then it is `DECIMAL(p,s)`. The `exact_decimals` setting makes every such column exact. |
431
443
  | LaTeX export wrote NULL as the literal "None" | `str(c)` was applied to every value including `None`, so a NULL appeared in the typeset table as the word None. It is now an empty cell, matching every other exporter. |
432
444
  | HTML export rendered zero as an empty cell | Cells were written as `html.escape(str(v)) if v else ''`. `0`, `False`, and `""` are all falsy, so a zero measurement rendered exactly like a NULL and the two could not be told apart in the output. The test is now `if v is not None`. Both `export_html` and `export_cgi_html` were affected. |
433
445
  | Markdown export broke the table on an embedded newline | `_cell()` escaped pipes but not newlines, and a GFM table row is a single line: a value containing one ended the row mid-cell and left the rest of that row's values on a stray line outside the table entirely. Newlines are now written as `<br>`, which is how GFM expresses a line break inside a cell. |
@@ -436,6 +448,9 @@ These are behavioral changes driven by security or correctness issues in the ups
436
448
  | XLSX export wrote `numeric` columns as text | `_cell_value()` passed `int` and `float` straight to openpyxl but let every other type fall through to `str()`. PostgreSQL, MySQL, and DuckDB all return `Decimal` for `numeric`/`decimal` columns, so measurement values arrived in Excel as text cells — invisible in a screenshot, useless in a formula, a sort, or a chart. openpyxl lists `Decimal` in its own `NUMERIC_TYPES` and stores it as a real numeric cell, so the conversion was never needed. Found by exporting from a live PostgreSQL container and checking `cell.data_type` rather than `cell.value`. |
437
449
  | `EMAIL` SMTP login without a password | Upstream calls `smtplib.login()` with only a username when `[email] password` and `enc_password` are both unset, which raises `TypeError` from the stdlib — `login()` has no single-argument form. execsql2 reports a configuration error naming the missing password instead. |
438
450
  | Type scan of large integers | Working out a column's type tested each integer against the binary type by building `bytearray(n)`, n zero bytes, so a column of large integers (epoch milliseconds: about a terabyte per value) hung or ran out of memory. The binary test now answers for an integer without building it; the inferred types are unchanged. |
451
+ | Values loaded into an existing table | `IMPORT` and `COPY` converted every value to the type guessed from the source data, even when the table was already there with its own column types, so a text column got a different string than the source held (`yes` and `1` stored as `true`, `01/05/2024` as `2024-01-05 00:00:00`, `1e3` as `1000`), and on PostgreSQL a column of 0s and 1s copied into an integer column failed (the guess was boolean). For a table that exists, the target column's type now decides: a text column gets the value as written, a number column gets true/false values as 1 and 0, and other columns are converted as before. Each adapter reads the column types from its catalog (`pg_attribute`, `SHOW COLUMNS`, `sys.columns`) or its driver's description of the table. `TO NEW` / `REPLACEMENT` are unchanged. |
452
+ | Missing dates loaded into MySQL | `IMPORT` and `COPY` sent a missing value in a column scanned as a date or timestamp as `''`, so MySQL stored `0000-00-00` (or `0` in a number column) instead of NULL. It is sent as NULL. |
453
+ | Fractions loaded into an integer column | A value with a fraction going into an integer column (including `DECIMAL(p,0)`, `NUMERIC(p)` and Oracle `NUMBER(p)`) of an existing table is an error naming the value and the column. Upstream sent the number and the database rounded it (`1.5` → `2`; SQL Server truncated it to `1`). |
439
454
 
440
455
  ______________________________________________________________________
441
456
 
@@ -8,6 +8,12 @@ If you are adding support for a new database, start with the [Adding Database Ad
8
8
 
9
9
  ::: execsql.db.base
10
10
 
11
+ ## Target column types
12
+
13
+ `IMPORT` and `COPY` into a table that already exists convert each value by the type of the column it goes into (`Database.column_kinds()`), not by the type scanned from the source data.
14
+
15
+ ::: execsql.db.target_types
16
+
11
17
  ## Database factory
12
18
 
13
19
  Convenience constructors used internally to create typed `Database` instances. The CLI calls the appropriate factory function based on the `-t` flag value. Each factory validates its arguments (e.g., checking that a file exists) before constructing and returning the adapter.
@@ -133,23 +133,24 @@ class MyDBDatabase(Database):
133
133
 
134
134
  These are the instance attributes and methods you must configure correctly:
135
135
 
136
- | Attribute / Method | Type | Purpose |
137
- | ------------------ | ----------------- | --------------------------------------------------------------------------------- |
138
- | `self.type` | `DbType` | DBMS type token (e.g., `dbt_sqlite`). Controls quoting and type-mapping. |
139
- | `self.paramstr` | `str` | SQL parameter placeholder: `"?"` (most drivers) or `"%s"` (psycopg, PyMySQL). |
140
- | `self.encoding` | `str` | Database character encoding. Detect from the database on connect if possible. |
141
- | `self.autocommit` | `bool` | `True` means the driver commits automatically; `False` requires explicit commits. |
142
- | `self.conn` | driver connection | Set in `open_db()`. |
143
- | `open_db()` | method | **Must override.** Establish the connection and assign `self.conn`. |
144
- | `table_exists()` | method | **Must override.** Query the DBMS catalog. Use parameterized queries. |
145
- | `column_exists()` | method | **Must override.** Check column presence. |
146
- | `table_columns()` | method | **Must override.** Return column names for a table. |
147
- | `view_exists()` | method | **Must override.** Check view presence. |
148
- | `schema_exists()` | method | **Must override.** Return `False` if schemas are not supported. |
149
- | `drop_table()` | method | **Must override.** Drop a table (used by IMPORT when creating fresh). |
150
- | `populate_table()` | method | **Must override.** Bulk-load rows from a generator (used by IMPORT). |
151
- | `row_inserter()` | method | Override for a faster bulk path that stores exactly what `executemany()` stores. |
152
- | `exec_cmd()` | method | Override if the DBMS can execute stored procedures or views as commands. |
136
+ | Attribute / Method | Type | Purpose |
137
+ | ------------------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
138
+ | `self.type` | `DbType` | DBMS type token (e.g., `dbt_sqlite`). Controls quoting and type-mapping. |
139
+ | `self.paramstr` | `str` | SQL parameter placeholder: `"?"` (most drivers) or `"%s"` (psycopg, PyMySQL). |
140
+ | `self.encoding` | `str` | Database character encoding. Detect from the database on connect if possible. |
141
+ | `self.autocommit` | `bool` | `True` means the driver commits automatically; `False` requires explicit commits. |
142
+ | `self.conn` | driver connection | Set in `open_db()`. |
143
+ | `open_db()` | method | **Must override.** Establish the connection and assign `self.conn`. |
144
+ | `table_exists()` | method | **Must override.** Query the DBMS catalog. Use parameterized queries. |
145
+ | `column_exists()` | method | **Must override.** Check column presence. |
146
+ | `table_columns()` | method | **Must override.** Return column names for a table. |
147
+ | `view_exists()` | method | **Must override.** Check view presence. |
148
+ | `schema_exists()` | method | **Must override.** Return `False` if schemas are not supported. |
149
+ | `drop_table()` | method | **Must override.** Drop a table (used by IMPORT when creating fresh). |
150
+ | `populate_table()` | method | **Must override.** Bulk-load rows from a generator (used by IMPORT). |
151
+ | `row_inserter()` | method | Override for a faster bulk path that stores exactly what `executemany()` stores. |
152
+ | `column_kinds()` | method | Override if the driver's description of a table does not tell its column types apart (see `db/target_types.py`); IMPORT and COPY into an existing table convert each value by them. |
153
+ | `exec_cmd()` | method | Override if the DBMS can execute stored procedures or views as commands. |
153
154
 
154
155
  Methods inherited from `Database` that you get for free include `execute()`, `cursor()`, `close()`, `commit()`, `rollback()`, `select_rowsource()`, `select_data()`, `schema_qualified_table_name()`, `quote_identifier()`, and `paramsubs()`.
155
156
 
@@ -264,6 +264,8 @@ ______________________________________________________________________
264
264
 
265
265
  The base class provides shared implementations for `execute()`, `select_data()`, `select_rowsource()`, `select_rowdict()`, `commit()`, `rollback()`, and `populate_table()`.
266
266
 
267
+ `populate_table()` converts each value to the type scanned from the source (`models.DataTable`). For a table that was already there (`existing_table=True`, passed by IMPORT and COPY), `column_kinds()` reports the target columns' types and `db/target_types.py` picks each column's conversion instead: a text column gets the value as given, an integer column whole numbers or an error. `column_kinds()` reads the driver's description of `select * from <table> where 1=0` by default; PostgreSQL, MySQL and SQL Server read their catalogs, where the description cannot tell some types apart. `tests/db/test_existing_table_loads.py` compares what every value type stores in every column type with the record in `tests/data/existing_table_loads/`.
268
+
267
269
  ### DatabasePool
268
270
 
269
271
  `DatabasePool` is a dict-like container mapping string aliases to open `Database` instances. It is the canonical `_state.dbs` object. Key operations:
@@ -272,7 +272,7 @@ select id,name,created_at from users where active = true order by name;
272
272
  ```yaml
273
273
  repos:
274
274
  - repo: https://github.com/geocoug/execsql
275
- rev: v2.33.0
275
+ rev: v2.35.0
276
276
  hooks:
277
277
  - id: execsql-format
278
278
  - id: execsql-lint # optional: fail the commit on lint errors
@@ -4,6 +4,26 @@
4
4
 
5
5
  When execsql connects to a SQL Server or MySQL/MariaDB database, it automatically configures the DBMS to expect ANSI-compatible SQL, to allow the use of more standards-compliant, and thus consistent, SQL. In particular, for MySQL/MariaDB, note that the double-quote character, rather than the backtick character, must be used to quote table, schema, and column names, and only the apostrophe can be used to quote character data.
6
6
 
7
+ ### MySQL strict mode { #mysql_strict_mode }
8
+
9
+ On MySQL/MariaDB, execsql sets the session's `sql_mode` to `ANSI,STRICT_TRANS_TABLES`. A value that MySQL can't store as given is an error, for a SQL statement, [IMPORT](../reference/metacommands.md#import) or [COPY](../reference/metacommands.md#copy):
10
+
11
+ ```sql
12
+ create table m (n integer, d date, s varchar(3));
13
+ insert into m values (3000000000, '2024-02-30', 'abcdef');
14
+ -- ERROR 1264 (22003): Out of range value for column 'n' at row 1
15
+ ```
16
+
17
+ Without strict mode, MySQL would have stored `2147483647`, `0000-00-00` and `abc` with only a warning. This is how the `mysql` client behaves with the server's default settings. An IMPORT of a delimited file that MySQL reads with `LOAD DATA` is held to the same rule: if MySQL warns that it changed a value, the IMPORT stops and nothing is loaded.
18
+
19
+ MySQL in strict mode does not read a trailing `Z` as a time zone: write `2024-01-05T10:00:00Z` as `2024-01-05 10:00:00+00:00`. A decimal with more places than its column has (`1.2345` into `DECIMAL(10,3)`) is still rounded, as MySQL does in strict mode.
20
+
21
+ A script that relies on MySQL's changed values can turn strict mode off for the rest of its session:
22
+
23
+ ```sql
24
+ SET SESSION sql_mode = 'ANSI';
25
+ ```
26
+
7
27
  ## Implicit Commits
8
28
 
9
29
  By default, execsql immediately commits all SQL statements. The [AUTOCOMMIT](../reference/metacommands.md#autocommit) metacommand can be used to turn off automatic commits, and the [BATCH](../reference/metacommands.md#batch) metacommand can be used to delay commits until the end of a batch. [IMPORT](../reference/metacommands.md#import) and [COPY](../reference/metacommands.md#copy) are the only metacommands that change data, and they also automatically commit their changes when complete (unless [AUTOCOMMIT](../reference/metacommands.md#autocommit) has been turned off). If a new table is created with either of these metacommands (through the use of the NEW or REPLACEMENT keywords), the CREATE TABLE statement will not be committed separately from the data addition, except when using Firebird. Thus, if an error occurs during addition of the data, the new target table will not exist---except when using Firebird.
@@ -9,7 +9,7 @@
9
9
  You can use *execsql* to:
10
10
 
11
11
  - Import data from text files and spreadsheets into a database.
12
- - Export tables and views to ~30 data formats including CSV, OpenDocument spreadsheets, XLSX, HTML, JSON, YAML, Markdown, LaTeX, XML, Parquet, Feather, HDF5, SQLite, and DuckDB.
12
+ - Export tables and views to ~30 data formats including CSV, OpenDocument spreadsheets, XLSX, HTML, JSON, JSON Lines, YAML, Markdown, LaTeX, XML, Parquet, Feather, HDF5, SQLite, and DuckDB.
13
13
  - Copy data between different databases, even databases using different DBMSs.
14
14
  - Display tables or views on the terminal or in a GUI dialog window.
15
15
  - Export data using template processors to produce non-tabular output with customized format and contents.
@@ -141,6 +141,10 @@ The section and property names that may be used in a configuration file are list
141
141
 
142
142
  : Determines whether empty strings in the input are preserved or, alternatively, will be replaced by NULL. The property value should be either "Yes" or "No". The default, "Yes", indicates that empty strings are allowed. A value of "No" will cause all empty strings to be replaced by NULL. When this is set to "No", a string value consisting of a sequence of zero or more space characters will be considered to be an empty string. There is no command-line option corresponding to this configuration parameter, but the metacommand [CONFIG EMPTY_STRINGS](metacommands.md#empty_strings) can also be used to change this configuration item.
143
143
 
144
+ `exact_decimals`
145
+
146
+ : Determines the data type of a column of decimal numbers with different numbers of decimal places, such as `1.50`, `2` and `-3.25`, when the [IMPORT](metacommands.md#import) or [COPY](metacommands.md#copy) metacommand creates a table. The default, "No", gives such a column a floating-point type (`double precision` on PostgreSQL, `double` on MySQL and DuckDB, `float` on SQL Server), unless a value has more than 15 significant digits, which a floating-point column cannot hold exactly: then the column is an exact decimal, so no digit is lost. "Yes" always gives such a column an exact decimal type wide enough for every value (`numeric(3,2)` for those values), for values such as money amounts that should be stored and compared exactly. A column whose values all have the same number of decimal places is an exact decimal either way. The metacommand [CONFIG EXACT_DECIMALS](metacommands.md#exact_decimals) can also be used to change this configuration item.
147
+
144
148
  `fold_column_headers` { #setting_fold_column_headers }
145
149
 
146
150
  : Whether or not to fold (convert) the case of all column headers to lowercase or uppercase, or to leave them unchanged when data are [IMPORTed](metacommands.md#import). Valid values are "No" (the default), "Lower", and "Upper". Case does not matter in the specification.
@@ -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.33.0
161
+ rev: v2.35.0
162
162
  hooks:
163
163
  - id: execsql-lint
164
164
  args: [--ignore, V002] # optional: any execsql lint option
@@ -597,6 +597,21 @@ CONFIG MAX_INT <integer_value>
597
597
  Specifies the threshold between integer and bigint data types that is used by the [IMPORT](#import) and [COPY](#copy) metacommands when creating a new table. Any column with integer values less than or equal to this value (max_int) and greater than or equal to -1 × max_int - 1 will be considered to have an integer type. Any column with values outside this range will be considered to have a bigint type. The default value for max_int is 2147483647. The max_int value can also be altered using a configuration option. The bare form `MAX_INT <integer_value>`, without `CONFIG`, does the same.
598
598
 
599
599
 
600
+ <a id="exact_decimals"></a>
601
+ ```
602
+ CONFIG EXACT_DECIMALS YES|NO
603
+ ```
604
+
605
+ Controls the data type the [IMPORT](#import) and [COPY](#copy) metacommands give a column of decimal numbers with different numbers of decimal places when they create a table. With "No" (the default), `1.50`, `2` and `-3.25` make a floating-point column; a column with a value of more than 15 significant digits, such as `1234567890.1234567891`, is an exact decimal even then, because a floating-point column would cut it. With "Yes" every such column is an exact decimal wide enough for every value, `numeric(3,2)` for the first example:
606
+
607
+ ```
608
+ -- !x! CONFIG EXACT_DECIMALS YES
609
+ -- !x! IMPORT TO NEW payments FROM payments.csv
610
+ ```
611
+
612
+ A column whose values all have the same number of decimal places is an exact decimal either way. This can also be set with the `exact_decimals` [configuration setting](configuration.md#config_input).
613
+
614
+
600
615
  ```
601
616
  CONFIG ONLY_STRINGS YES|NO
602
617
  ```
@@ -889,6 +904,8 @@ Copies the data from a data table or view in one database to a data table in a s
889
904
 
890
905
  The second (destination) table must have column names that are identical to the names of the columns in the first (source) table. The second table may have additional columns; if it does, they will not be affected and their names don't matter. The data types in the columns to be copied must be compatible, though not necessarily identical. The order of the columns in the two tables does not have to be identical.
891
906
 
907
+ When neither "NEW" nor "REPLACEMENT" is used, each value is converted for the destination column it goes into, as for [IMPORT into an existing table](#import_existing_table_types): a text column gets the value as the source database returned it (an integer `1` is stored as `1`, a date as `2024-01-05`), an integer column gets whole numbers and stops the COPY with an error on a value with a fraction instead of rounding it, and a number column gets a column of 0s and 1s, or of true/false values, as 1 and 0.
908
+
892
909
  If the "NEW" keyword is used, the destination table will be automatically created with column names and data types that are compatible with the first (source) table. The data types used for the columns in the newly created table will be determined by a scan of all of the data in the first table, but may not exactly match those in the first table. A text column stays text, whatever its values look like: `00123`, `1`/`0` and `2024-01-05` are copied as written, not as numbers, booleans or dates. The source is read once; while its rows are scanned they are held in a temporary file, so a large copy does not need memory for the whole table. If the destination table already exists when the "NEW" keyword is used, the copy stops before creating anything, with the error `Table <name> already exists`; a name without a schema is looked for only in the schema the table would be created in (on MySQL, the current database)
893
910
 
894
911
  If the "REPLACEMENT" keyword is used, the destination table will also be created to be compatible with the source table, but any existing destination table of the same name will be dropped first. This replacement is not atomic: if the drop succeeds but a later create or load step fails, the old destination table has already been removed. If the drop fails, *execsql* stops the COPY command before creating or loading the replacement table. *execsql* uses a "drop table" statement to drop an existing destination table, and this statement may not succeed if there are dependencies on that table (see the discussion of [implicit drop table
@@ -1165,6 +1182,15 @@ JSON
1165
1182
  : [Javascript Object Notation](http://www.json.org/). The data table is represented as an array of JSON objects, where each object represents a row of the table. Each row is represented as a set of key:value pairs, with column names used as the keys. No description text will be included in the output even if it is provided.
1166
1183
 
1167
1184
 
1185
+ JSONL
1186
+
1187
+ : [JSON Lines](https://jsonlines.org/): one JSON object per row, one row per line, with no surrounding array. Values are written as for JSON (dates and times as text, NULL as `null`); a line break inside a value is written as `\n`, so each row stays on one line. If the "APPEND" keyword is used, the rows are added to the end of the file, which stays valid JSON Lines (appending JSON instead leaves two arrays in one file). A query that returns no rows writes an empty file. The file can be read back with [IMPORT … FROM JSON](#import). No description text will be included in the output even if it is provided:
1188
+
1189
+ ```
1190
+ -- !x! EXPORT QUERY <<select * from events where day = current_date;>> APPEND TO events.jsonl AS JSONL
1191
+ ```
1192
+
1193
+
1168
1194
  JSON_TS or JSON_TABLESCHEMA
1169
1195
 
1170
1196
  : [JSON Table Schema](https://frictionlessdata.io/specs/table-schema/). The data themselves are not exported. Ordinarily this form would be used in conjunction with an additional export of the data to a CSV file. The column type descriptions in the output are derived by scanning the entire table to evaluate the data type of each column. If a "NOTYPE" keyword is included before the "DESCRIPTION" keyword, this scanning is not done, and no type information is included in the output file.:
@@ -1945,7 +1971,7 @@ The syntax for importing data from a [JSON](https://www.json.org/) file is:
1945
1971
  IMPORT TO [NEW|REPLACEMENT] <table_name> FROM JSON <file_name>
1946
1972
  ```
1947
1973
 
1948
- The JSON file must contain either a JSON array of objects (`[{…}, …]`) or [JSON Lines](https://jsonlines.org/) (JSONL, one object per line). Nested objects are flattened with dot-separated column names (e.g., an object `{"address": {"city": "Portland"}}` produces column `address.city`). Nested arrays within objects are stored as JSON strings. Records with different keys produce a superset of columns — missing keys become NULL. The parser auto-detects which form is present by peeking at the first non-whitespace character (`[` → array, `{` → JSON Lines); the file extension is not consulted.
1974
+ The JSON file must contain either a JSON array of objects (`[{…}, …]`) or [JSON Lines](https://jsonlines.org/) (JSONL, one object per line). Nested objects are flattened with dot-separated column names (e.g., an object `{"address": {"city": "Portland"}}` produces column `address.city`). Nested arrays within objects are stored as JSON strings. Records with different keys produce a superset of columns — missing keys become NULL. The parser auto-detects which form is present by peeking at the first non-whitespace character (`[` → array, `{` → JSON Lines); the file extension is not consulted. A file written by [EXPORT … AS JSONL](#export) is read as JSON Lines.
1949
1975
 
1950
1976
  Column names in the input must be valid for the DBMS in use.
1951
1977
 
@@ -1968,7 +1994,15 @@ A data line with fewer values than the header has NULL for the values left off t
1968
1994
 
1969
1995
  The rows are loaded in one transaction: if the load fails part-way (a constraint violation, a value of the wrong type, a row with too many columns), none of the file's rows are kept, on every DBMS. The exception is an ODBC DSN whose driver can only autocommit, which keeps the rows loaded before the failure; *execsql* warns once on such a connection.
1970
1996
 
1971
- If neither the NEW or REPLACEMENT keywords are used, the table must exist, must have column names identical to those in the input data, and columns must have data types that are compatible with (though not necessarily identical to) those in the input data. If neither the NEW or REPLACEMENT keywords are used, the input data is not scanned to determine the data type of each column.
1997
+ <a id="import_existing_table_types"></a>
1998
+ If neither the NEW or REPLACEMENT keywords are used, the table must exist, must have column names identical to those in the input data, and columns must have data types that are compatible with (though not necessarily identical to) those in the input data. Each value is converted for the column it goes into:
1999
+
2000
+ - A text column (`CHAR`, `VARCHAR`, `TEXT`, …) gets the value as written in the file: `yes`, `00701`, `01/05/2024` and `1e3` are stored as those strings. A missing value is NULL.
2001
+ - An integer column (`INTEGER`, `BIGINT`, `DECIMAL(10,0)`, `NUMERIC(10)`, Oracle `NUMBER(10)`, …) gets whole numbers: `7`, `+7`, `7.0` and `7e0` load as 7. A value with a fraction, such as `1.5`, stops the IMPORT with an error that names the value and the column, and nothing is loaded; it is not rounded.
2002
+ - A number column gets a column of true/false values (`yes`/`no`, `t`/`f`, `1`/`0`; see [BOOLEAN_INT](#boolean_int) and [BOOLEAN_WORDS](#boolean_words)) as 1 and 0.
2003
+ - Any other column (a date, a timestamp, a Boolean, a decimal with places for a fraction) gets the value converted to the data type found by scanning that column of the input, as with NEW, and the database converts it to the column's type.
2004
+
2005
+ On PostgreSQL and MySQL a delimited text file goes to the server's own loader (`COPY`, `LOAD DATA`, or on PostgreSQL an `INSERT` of each value as text), and the server converts each value as written; the rules above apply to the other file formats, and to delimited files on the other DBMSs. On MySQL a value that the server can't store as given (out of range, an invalid date, text longer than the column) stops the IMPORT with an error, and nothing is loaded; see [MySQL strict mode](../guides/sql_syntax.md#mysql_strict_mode).
1972
2006
 
1973
2007
  If a table is scanned to determine data types, any column that is completely empty (all null) will be created with the text data type. This provides the greatest flexibility for subsequent addition of data to the table. However, if that column ought to have a different data type, and a WHERE clause is applied to that column assuming a different data type, the DBMS may report an error because of incomparable data types.
1974
2008
 
@@ -2032,7 +2066,7 @@ When MS-Excel saves an OpenDocument spreadsheet, it may create an additional emp
2032
2066
  Because the data addition to the target table is always committed except when [AUTOCOMMIT](#autocommit) is off, the IMPORT metacommand should be used with care within transactions that are managed with explicit SQL statements or with [BATCHes](#batch).
2033
2067
 
2034
2068
 
2035
- In general, if an error occurs while importing data, none of the new data should be in the target table (the operation is not committed). However, MySQL/MariaDB may issue messages about data type incompatibility to the standard error device (ordinarily the terminal), yet load some or all of the data. If the NEW or REPLACEMENT keywords are used, depending on the DBMS and where the error occurred, the target table may be created even if the data are not loaded.
2069
+ In general, if an error occurs while importing data, none of the new data should be in the target table (the operation is not committed). On MySQL/MariaDB a value that the server would change to store it is an error too, unless the script has turned off [strict mode](../guides/sql_syntax.md#mysql_strict_mode). If the NEW or REPLACEMENT keywords are used, depending on the DBMS and where the error occurred, the target table may be created even if the data are not loaded.
2036
2070
 
2037
2071
  The name, size, and date of the IMPORTed file are written to the [execsql.log](../guides/logging.md#logging) file.
2038
2072
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "execsql-syntax",
3
- "version": "2.33.0",
3
+ "version": "2.35.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "execsql-syntax",
9
- "version": "2.33.0",
9
+ "version": "2.35.0",
10
10
  "license": "GPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "vscode-languageclient": "10.1.2"
@@ -2,7 +2,7 @@
2
2
  "name": "execsql-syntax",
3
3
  "displayName": "execsql",
4
4
  "description": "Syntax highlighting, lint findings, completion, hover, navigation, quick fixes and formatting for execsql scripts.",
5
- "version": "2.33.0",
5
+ "version": "2.35.0",
6
6
  "publisher": "geocoug",
7
7
  "license": "GPL-3.0-or-later",
8
8
  "icon": "icon.png",
@@ -113,12 +113,12 @@
113
113
  },
114
114
  "config-option-names": {
115
115
  "comment": "Config option names used after the config keyword",
116
- "match": "(?i)\\b(import_only_common_columns|console_wait_when_error|console_wait_when_done|create_column_headers|clean_column_headers|dao_flush_delay_secs|dedup_column_headers|delete_empty_columns|fold_column_headers|trim_column_headers|log_write_messages|export_row_buffer|import_row_buffer|make_export_dirs|replace_newlines|quote_all_text|write_warnings|boolean_words|empty_strings|hdf5_text_len|show_progress|zip_buffer_mb|log_datavars|only_strings|trim_strings|write_prefix|write_suffix|boolean_int|empty_rows|scan_lines|gui_level|log_sql|max_int)\\b",
116
+ "match": "(?i)\\b(import_only_common_columns|console_wait_when_error|console_wait_when_done|create_column_headers|clean_column_headers|dao_flush_delay_secs|dedup_column_headers|delete_empty_columns|fold_column_headers|trim_column_headers|log_write_messages|export_row_buffer|import_row_buffer|make_export_dirs|replace_newlines|exact_decimals|quote_all_text|write_warnings|boolean_words|empty_strings|hdf5_text_len|show_progress|zip_buffer_mb|log_datavars|only_strings|trim_strings|write_prefix|write_suffix|boolean_int|empty_rows|scan_lines|gui_level|log_sql|max_int)\\b",
117
117
  "name": "support.constant.config.execsql"
118
118
  },
119
119
  "export-formats": {
120
120
  "comment": "Export/import format names",
121
- "match": "(?i)\\b(json_tableschema|cgi-html|markdown|feather|json_ts|txt-and|unitsep|duckdb|sqlite|values|latex|plain|hdf5|html|json|tabq|tsvq|xlsx|yaml|b64|csv|ods|raw|tab|tsv|txt|xml|md|us)\\b",
121
+ "match": "(?i)\\b(json_tableschema|cgi-html|markdown|feather|json_ts|txt-and|unitsep|duckdb|sqlite|values|jsonl|latex|plain|hdf5|html|json|tabq|tsvq|xlsx|yaml|b64|csv|ods|raw|tab|tsv|txt|xml|md|us)\\b",
122
122
  "name": "support.constant.execsql"
123
123
  },
124
124
  "secondary-operators": {
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "execsql2"
7
- version = "2.33.0"
7
+ version = "2.35.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" }
@@ -188,7 +188,7 @@ line-ending = "auto"
188
188
  exclude = ["*.md"]
189
189
 
190
190
  [tool.bumpversion]
191
- current_version = "2.33.0"
191
+ current_version = "2.35.0"
192
192
  commit = true
193
193
  tag = true
194
194
  tag_name = "v{new_version}"
@@ -178,6 +178,7 @@ class _LibraryConfig:
178
178
  self.boolean_int = True
179
179
  self.boolean_words = False
180
180
  self.empty_strings = True
181
+ self.exact_decimals = False
181
182
  self.only_strings = False
182
183
  self.empty_rows = True
183
184
  self.del_empty_cols = False
@@ -300,6 +300,7 @@ class ConfigData:
300
300
  self._get_bool(cp, self._INPUT_SECTION, "boolean_int", "boolean_int")
301
301
  self._get_bool(cp, self._INPUT_SECTION, "boolean_words", "boolean_words")
302
302
  self._get_bool(cp, self._INPUT_SECTION, "empty_strings", "empty_strings")
303
+ self._get_bool(cp, self._INPUT_SECTION, "exact_decimals", "exact_decimals")
303
304
  self._get_bool(cp, self._INPUT_SECTION, "only_strings", "only_strings")
304
305
  self._get_bool(cp, self._INPUT_SECTION, "empty_rows", "empty_rows")
305
306
  self._get_bool(cp, self._INPUT_SECTION, "delete_empty_columns", "del_empty_cols")
@@ -503,6 +504,7 @@ class ConfigData:
503
504
  self.boolean_int = True
504
505
  self.boolean_words = False
505
506
  self.empty_strings = True
507
+ self.exact_decimals = False
506
508
  self.only_strings = False
507
509
  self.empty_rows = True
508
510
  self.del_empty_cols = False
@@ -115,6 +115,13 @@
115
115
  # Values: Yes or No. Default: Yes.
116
116
  #empty_strings=Yes
117
117
 
118
+ # Whether a column of decimal numbers with different numbers of decimal places
119
+ # (1.5, 2.25) gets an exact DECIMAL(precision, scale) type when IMPORT or COPY
120
+ # creates a table. With No it gets a floating-point type, unless a value has
121
+ # more than 15 significant digits (then it is DECIMAL, so no digit is lost).
122
+ # Values: Yes or No. Default: No.
123
+ #exact_decimals=No
124
+
118
125
  # Whether to fold column headers to lowercase, uppercase, or leave unchanged.
119
126
  # Values: No, Lower, or Upper. Default: No.
120
127
  #fold_column_headers=No