execsql2 2.19.2__tar.gz → 2.20.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.
- {execsql2-2.19.2 → execsql2-2.20.0}/.github/workflows/ci-cd.yml +21 -9
- {execsql2-2.19.2 → execsql2-2.20.0}/.pre-commit-hooks.yaml +1 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/.readthedocs.yaml +1 -1
- {execsql2-2.19.2 → execsql2-2.20.0}/CHANGELOG.md +27 -8
- {execsql2-2.19.2 → execsql2-2.20.0}/PKG-INFO +54 -56
- {execsql2-2.19.2 → execsql2-2.20.0}/README.md +2 -3
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/about/divergence.md +16 -7
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/dev/architecture.md +26 -0
- execsql2-2.20.0/docs/dev/releasing.md +124 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/getting-started/requirements.md +16 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/formatter.md +11 -11
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/reference/security.md +19 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/pyproject.toml +40 -22
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/cli/run.py +28 -11
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/access.py +5 -1
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/base.py +8 -2
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/dsn.py +3 -1
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/firebird.py +10 -4
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/mysql.py +6 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/sqlserver.py +4 -1
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/format.py +172 -16
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/mail.py +7 -4
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/README.md +1 -1
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/test_cli.py +10 -7
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_db_adapters_mocked.py +13 -10
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_db_adapters_mocked_extra.py +9 -3
- execsql2-2.20.0/tests/security/test_log_redaction.py +187 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_format.py +242 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_mail.py +3 -3
- {execsql2-2.19.2 → execsql2-2.20.0}/uv.lock +29 -43
- {execsql2-2.19.2 → execsql2-2.20.0}/zensical.toml +2 -1
- execsql2-2.19.2/templates/execsql.conf +0 -359
- execsql2-2.19.2/tests/security/test_log_redaction.py +0 -74
- {execsql2-2.19.2 → execsql2-2.20.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/.github/dependabot.yml +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/.gitignore +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/.pre-commit-config.yaml +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/.python-version +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/CONTRIBUTING.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/LICENSE.txt +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/NOTICE +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/SECURITY.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/about/contributors.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/about/copyright.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/api/cli.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/api/db.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/api/exporters.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/api/importers.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/api/index.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/api/metacommands.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/getting-started/installation.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/debugging.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/documentation.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/encoding.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/examples.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/logging.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/usage.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/actions.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/actions2.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/checkboxes.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/connect.b64 +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/connect.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/create_conf.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/entry_form.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/execsql_console.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/fatals.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/logo_small.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/unmatched.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/index.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/reference/configuration.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/reference/metacommands.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/plugin-template/README.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/justfile +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/__main__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/api.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/cli/help.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/config.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/factory.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exceptions.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/gui/base.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/gui/console.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/base.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/json.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/models.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/parser.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/plugins.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/py.typed +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/ast.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/control.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/engine.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/executor.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/parser.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/script/variables.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/state.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/types.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/config_settings.sqlite +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/make_config_db.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/md_compare.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/md_glossary.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/md_upsert.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/pg_compare.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/pg_glossary.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/pg_upsert.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/script_template.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/ss_compare.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/ss_glossary.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/templates/ss_upsert.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/test_lint.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/test_ping.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/cli/test_profile.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/conftest.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_base.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_dsn.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_factory.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_postgres.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_postgres_inprocess.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_base.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_db.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_json.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_backends.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/conftest.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_conditions_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_prompt.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_api.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_api_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_ast.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_ast_parser.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_config.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_config_data.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_config_extended.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_debug_repl.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_engine.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_error_messages.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_exceptions.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_executor.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_models.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_package.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_parser.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_parser_params.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_plugins.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_registry.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_script.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_state.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/test_types.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/__init__.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_auth.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_errors.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_regex.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_strings.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_timer.py +0 -0
- {execsql2-2.19.2 → execsql2-2.20.0}/tests/utils/test_timer_extra.py +0 -0
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
permissions:
|
|
21
21
|
contents: read
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
24
24
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
25
25
|
with:
|
|
26
26
|
python-version: "3.13"
|
|
@@ -48,7 +48,7 @@ jobs:
|
|
|
48
48
|
contents: read
|
|
49
49
|
steps:
|
|
50
50
|
- name: Check out repository code
|
|
51
|
-
uses: actions/checkout@
|
|
51
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
52
52
|
- name: Setup Python ${{ matrix.python-version }}
|
|
53
53
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
54
54
|
with:
|
|
@@ -139,7 +139,7 @@ jobs:
|
|
|
139
139
|
--health-interval 10s --health-timeout 5s --health-retries 20
|
|
140
140
|
steps:
|
|
141
141
|
- name: Check out repository code
|
|
142
|
-
uses: actions/checkout@
|
|
142
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
143
143
|
- name: Setup Python ${{ matrix.python-version }}
|
|
144
144
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
145
145
|
with:
|
|
@@ -175,9 +175,10 @@ jobs:
|
|
|
175
175
|
access-tests-windows:
|
|
176
176
|
name: access-tests-windows
|
|
177
177
|
runs-on: windows-latest
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
|
|
178
|
+
# The Access engine install step is best-effort (the redistributable
|
|
179
|
+
# download from Microsoft can be flaky on hosted runners). The
|
|
180
|
+
# downstream test step IS gating — a real Access regression should
|
|
181
|
+
# block the merge.
|
|
181
182
|
strategy:
|
|
182
183
|
fail-fast: false
|
|
183
184
|
matrix:
|
|
@@ -186,11 +187,13 @@ jobs:
|
|
|
186
187
|
permissions:
|
|
187
188
|
contents: read
|
|
188
189
|
steps:
|
|
189
|
-
- uses: actions/checkout@
|
|
190
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
190
191
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
191
192
|
with:
|
|
192
193
|
python-version: ${{ matrix.python-version }}
|
|
193
194
|
- name: Install Microsoft Access Database Engine 2016 (x64)
|
|
195
|
+
id: install_access
|
|
196
|
+
continue-on-error: true
|
|
194
197
|
# The chocolatey `accessdatabaseengine-x64` package was removed
|
|
195
198
|
# from the community feed; download the redistributable directly
|
|
196
199
|
# from Microsoft and run it silently instead.
|
|
@@ -206,12 +209,21 @@ jobs:
|
|
|
206
209
|
# 0 = success, 3010 = success but reboot pending. Anything else is a failure.
|
|
207
210
|
if ($proc.ExitCode -ne 0 -and $proc.ExitCode -ne 3010) { exit $proc.ExitCode }
|
|
208
211
|
shell: pwsh
|
|
212
|
+
- name: Annotate skipped Access tests
|
|
213
|
+
if: steps.install_access.outcome != 'success'
|
|
214
|
+
run: |
|
|
215
|
+
Write-Host "::warning::Access Database Engine install failed; Access real-driver tests skipped this run."
|
|
216
|
+
shell: pwsh
|
|
209
217
|
- name: Install Python dependencies
|
|
218
|
+
if: steps.install_access.outcome == 'success'
|
|
210
219
|
run: |
|
|
211
220
|
python -m pip install --upgrade pip
|
|
212
221
|
python -m pip install ".[dev,odbc]" pywin32
|
|
213
222
|
shell: pwsh
|
|
214
223
|
- name: Run Access real-driver tests
|
|
224
|
+
# Gating: a real regression in src/execsql/db/access.py blocks
|
|
225
|
+
# the merge. Only runs when the install above succeeded.
|
|
226
|
+
if: steps.install_access.outcome == 'success'
|
|
215
227
|
env:
|
|
216
228
|
DISPLAY: ""
|
|
217
229
|
run: |
|
|
@@ -226,7 +238,7 @@ jobs:
|
|
|
226
238
|
permissions:
|
|
227
239
|
contents: read
|
|
228
240
|
steps:
|
|
229
|
-
- uses: actions/checkout@
|
|
241
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
230
242
|
- name: Set up Python
|
|
231
243
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
232
244
|
with:
|
|
@@ -276,7 +288,7 @@ jobs:
|
|
|
276
288
|
contents: write
|
|
277
289
|
steps:
|
|
278
290
|
- name: Checkout
|
|
279
|
-
uses: actions/checkout@
|
|
291
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
280
292
|
- name: Download dist
|
|
281
293
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
282
294
|
with:
|
|
@@ -13,11 +13,36 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.20.0] - 2026-06-11
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `execsql-format --encoding NAME` flag controls the text encoding used to read and write SQL files (default `utf-8`). Files saved by editors that emit cp1252, latin-1, shift_jis, etc. can now be formatted directly. A `UnicodeDecodeError` reports the file path and suggests `--encoding` instead of dumping a traceback.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Published `execsql-format` pre-commit hook now defaults to `args: [--in-place]`. Downstream configs that listed `- id: execsql-format` with no `args:` previously got a silent no-op (the CLI's default is stdout, which pre-commit doesn't capture); they will now reformat files in place. Override with `args: [--check]` for check-only mode.
|
|
25
|
+
- Pre-commit `rev:` snippets in `README.md` and `docs/guides/formatter.md` now point at `v2.19.2` and are rewritten automatically by `bump-my-version` on every version bump, so they cannot drift.
|
|
26
|
+
- Env-var redaction filter expanded to skip mainstream secret-naming conventions (`AWS_ACCESS_KEY_ID`, `STRIPE_KEY`, `OPENAI_API_KEY`, `SENTRY_DSN`, `SLACK_WEBHOOK`, etc.). `-a` assignment log lines now always show `{***}` instead of the raw value. See `docs/reference/security.md#env_redaction` for the full list and known gaps (`DATABASE_URL`, `GITHUB_PAT`).
|
|
27
|
+
- The duplicate `execsql.conf` reference file shipped at `<sys.prefix>/execsql2_extras/execsql.conf` has been removed. The canonical template still ships inside the package and is loaded via `importlib.resources`; bootstrap a project conf with `execsql --init-config > execsql.conf` (the documented path).
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `[firebird]` extra now connects: the adapter imports `firebird.driver` (provided by `firebird-driver`) instead of the missing `fdb` module. Users who installed `pip install execsql2[firebird]` previously hit `ModuleNotFoundError: No module named 'fdb'` at connect time.
|
|
32
|
+
- MySQL, SQL Server (pyodbc), MS Access (pyodbc), DSN (pyodbc), Firebird, and SMTP connections now use a 30-second connect timeout, matching the existing Postgres default. A silently-dropped peer no longer hangs a script (or a CI run) indefinitely.
|
|
33
|
+
- `execsql-format` now recognizes tagged `$body$ … $body$` / `$func$ … $func$` PL/pgSQL dollar quotes and skips sqlglot inside them, matching the existing `$$` behaviour. Tagged function bodies previously had their `IF / END IF / LOOP / RETURN` rewritten or collapsed.
|
|
34
|
+
- `execsql-format` now uppercases the second word of multi-word metacommands like `PROMPT MAP`, `PROMPT SAVEFILE`, `PROMPT OPENFILE`, `PROMPT CREDENTIALS`, `PROMPT SELECT_ROWS`, `PROMPT ASK COMPARE`, `APPEND SCRIPT`, `PG_UPSERT CHECK`/`QA`, `RESET COUNTER`/`DIALOG_CANCELED`, `SET COUNTER`, `WRITE CREATE_TABLE`/`SCRIPT`. Previously only the first word was uppercased, leaving the rest as the user typed.
|
|
35
|
+
- `execsql-format` no longer short-circuits on the first unreadable file; remaining files are still checked or formatted, and the run exits 1 at the end if any read error or pending change was found.
|
|
36
|
+
- `execsql-format --leading-comma` is now idempotent on SQL with mid-statement comments. The flag is no longer threaded through to sqlglot (which migrates inline `/* marker */` comments between passes); instead the formatter normalizes any leading-comma input to trailing-comma before sqlglot sees it, and applies leading commas as a textual post-pass on the assembled output.
|
|
37
|
+
|
|
38
|
+
______________________________________________________________________
|
|
39
|
+
|
|
16
40
|
## [2.19.2] - 2026-06-03
|
|
17
41
|
|
|
18
42
|
### Fixed
|
|
19
43
|
|
|
20
|
-
- `pre-commit` hook `execsql-format` now installs `sqlglot` into its isolated env
|
|
44
|
+
- `pre-commit` hook `execsql-format` now installs `sqlglot` into its isolated env (previously crashed with `ModuleNotFoundError: No module named 'sqlglot'`).
|
|
45
|
+
- After upgrading, re-run `pre-commit clean && pre-commit install --install-hooks` to rebuild the hook env.
|
|
21
46
|
|
|
22
47
|
______________________________________________________________________
|
|
23
48
|
|
|
@@ -49,13 +74,7 @@ ______________________________________________________________________
|
|
|
49
74
|
|
|
50
75
|
## [2.18.1] - 2026-05-28
|
|
51
76
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- Internal: `execsql.cli.lint_ast` has been folded into `execsql.cli.lint`; the AST walker entry point is now `execsql.cli.lint.lint()`. Code that imported `execsql.cli.lint_ast.lint_ast` should import `execsql.cli.lint.lint` instead.
|
|
55
|
-
|
|
56
|
-
### Removed
|
|
57
|
-
|
|
58
|
-
- Internal: the unused `run_when_false` and `run_in_batch` flags on `MetaCommand` (and the matching keyword arguments on `MetaCommandList.add()`) — neither has been consulted since v2.16.0. Code that still passes either kwarg to `mcl.add()` will raise `TypeError`.
|
|
77
|
+
- Internal: `execsql.cli.lint_ast` is now `execsql.cli.lint.lint`; deprecated `MetaCommand.run_when_false` / `run_in_batch` kwargs removed.
|
|
59
78
|
|
|
60
79
|
______________________________________________________________________
|
|
61
80
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.20.0
|
|
4
4
|
Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
|
|
5
5
|
Project-URL: Homepage, https://execsql2.readthedocs.io
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -44,88 +44,87 @@ Requires-Dist: rich>=13.0
|
|
|
44
44
|
Requires-Dist: textual>=1.0
|
|
45
45
|
Requires-Dist: typer>=0.12
|
|
46
46
|
Provides-Extra: all
|
|
47
|
-
Requires-Dist: defusedxml; extra == 'all'
|
|
48
|
-
Requires-Dist: duckdb; extra == 'all'
|
|
49
|
-
Requires-Dist: firebird-driver; extra == 'all'
|
|
50
|
-
Requires-Dist: jinja2; extra == 'all'
|
|
51
|
-
Requires-Dist: keyring; extra == 'all'
|
|
52
|
-
Requires-Dist: odfpy; extra == 'all'
|
|
53
|
-
Requires-Dist: openpyxl; extra == 'all'
|
|
54
|
-
Requires-Dist: oracledb; extra == 'all'
|
|
47
|
+
Requires-Dist: defusedxml>=0.7; extra == 'all'
|
|
48
|
+
Requires-Dist: duckdb>=1.0; extra == 'all'
|
|
49
|
+
Requires-Dist: firebird-driver>=1.10; extra == 'all'
|
|
50
|
+
Requires-Dist: jinja2>=3.1; extra == 'all'
|
|
51
|
+
Requires-Dist: keyring>=25.0; extra == 'all'
|
|
52
|
+
Requires-Dist: odfpy>=1.4; extra == 'all'
|
|
53
|
+
Requires-Dist: openpyxl>=3.1; extra == 'all'
|
|
54
|
+
Requires-Dist: oracledb>=3.0; extra == 'all'
|
|
55
55
|
Requires-Dist: pg-upsert>=1.22.1; extra == 'all'
|
|
56
|
-
Requires-Dist: polars; extra == 'all'
|
|
57
|
-
Requires-Dist: psycopg2-binary; extra == 'all'
|
|
58
|
-
Requires-Dist: pymysql; extra == 'all'
|
|
59
|
-
Requires-Dist: pyodbc; extra == 'all'
|
|
60
|
-
Requires-Dist: pyyaml; extra == 'all'
|
|
56
|
+
Requires-Dist: polars>=1.0; extra == 'all'
|
|
57
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'all'
|
|
58
|
+
Requires-Dist: pymysql>=1.1; extra == 'all'
|
|
59
|
+
Requires-Dist: pyodbc>=5.0; extra == 'all'
|
|
60
|
+
Requires-Dist: pyyaml>=6.0; extra == 'all'
|
|
61
61
|
Requires-Dist: sqlglot>=25.0; extra == 'all'
|
|
62
|
-
Requires-Dist: tables; extra == 'all'
|
|
62
|
+
Requires-Dist: tables>=3.10; extra == 'all'
|
|
63
63
|
Requires-Dist: tkintermapview>=1.29; extra == 'all'
|
|
64
|
-
Requires-Dist: xlrd; extra == 'all'
|
|
64
|
+
Requires-Dist: xlrd>=2.0; extra == 'all'
|
|
65
65
|
Provides-Extra: all-db
|
|
66
|
-
Requires-Dist: duckdb; extra == 'all-db'
|
|
67
|
-
Requires-Dist: firebird-driver; extra == 'all-db'
|
|
68
|
-
Requires-Dist: oracledb; extra == 'all-db'
|
|
69
|
-
Requires-Dist: psycopg2-binary; extra == 'all-db'
|
|
70
|
-
Requires-Dist: pymysql; extra == 'all-db'
|
|
71
|
-
Requires-Dist: pyodbc; extra == 'all-db'
|
|
66
|
+
Requires-Dist: duckdb>=1.0; extra == 'all-db'
|
|
67
|
+
Requires-Dist: firebird-driver>=1.10; extra == 'all-db'
|
|
68
|
+
Requires-Dist: oracledb>=3.0; extra == 'all-db'
|
|
69
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'all-db'
|
|
70
|
+
Requires-Dist: pymysql>=1.1; extra == 'all-db'
|
|
71
|
+
Requires-Dist: pyodbc>=5.0; extra == 'all-db'
|
|
72
72
|
Provides-Extra: auth
|
|
73
|
-
Requires-Dist: keyring; extra == 'auth'
|
|
73
|
+
Requires-Dist: keyring>=25.0; extra == 'auth'
|
|
74
74
|
Provides-Extra: auth-encrypted
|
|
75
|
-
Requires-Dist: keyring; extra == 'auth-encrypted'
|
|
76
|
-
Requires-Dist: keyrings-alt; extra == 'auth-encrypted'
|
|
77
|
-
Requires-Dist: pycryptodome; extra == 'auth-encrypted'
|
|
75
|
+
Requires-Dist: keyring>=25.0; extra == 'auth-encrypted'
|
|
76
|
+
Requires-Dist: keyrings-alt>=5.0; extra == 'auth-encrypted'
|
|
77
|
+
Requires-Dist: pycryptodome>=3.20; extra == 'auth-encrypted'
|
|
78
78
|
Provides-Extra: auth-plaintext
|
|
79
|
-
Requires-Dist: keyring; extra == 'auth-plaintext'
|
|
80
|
-
Requires-Dist: keyrings-alt; extra == 'auth-plaintext'
|
|
79
|
+
Requires-Dist: keyring>=25.0; extra == 'auth-plaintext'
|
|
80
|
+
Requires-Dist: keyrings-alt>=5.0; extra == 'auth-plaintext'
|
|
81
81
|
Provides-Extra: dev
|
|
82
82
|
Requires-Dist: build>=1.2.2.post1; extra == 'dev'
|
|
83
83
|
Requires-Dist: bump-my-version>=1.2.7; extra == 'dev'
|
|
84
|
-
Requires-Dist: defusedxml; extra == 'dev'
|
|
85
|
-
Requires-Dist: jinja2; extra == 'dev'
|
|
86
|
-
Requires-Dist: markdown-include>=0.8; extra == 'dev'
|
|
84
|
+
Requires-Dist: defusedxml>=0.7; extra == 'dev'
|
|
85
|
+
Requires-Dist: jinja2>=3.1; extra == 'dev'
|
|
87
86
|
Requires-Dist: mkdocstrings-python>=2.0.3; extra == 'dev'
|
|
88
87
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
89
|
-
Requires-Dist: odfpy; extra == 'dev'
|
|
90
|
-
Requires-Dist: openpyxl; extra == 'dev'
|
|
91
|
-
Requires-Dist: polars; extra == 'dev'
|
|
88
|
+
Requires-Dist: odfpy>=1.4; extra == 'dev'
|
|
89
|
+
Requires-Dist: openpyxl>=3.1; extra == 'dev'
|
|
90
|
+
Requires-Dist: polars>=1.0; extra == 'dev'
|
|
92
91
|
Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
|
|
93
92
|
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
94
|
-
Requires-Dist: pyyaml; extra == 'dev'
|
|
93
|
+
Requires-Dist: pyyaml>=6.0; extra == 'dev'
|
|
95
94
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
96
95
|
Requires-Dist: sqlglot>=25.0; extra == 'dev'
|
|
97
|
-
Requires-Dist: tables; extra == 'dev'
|
|
96
|
+
Requires-Dist: tables>=3.10; extra == 'dev'
|
|
98
97
|
Requires-Dist: tox-uv>=1.13.1; extra == 'dev'
|
|
99
98
|
Requires-Dist: twine>=6.1.0; extra == 'dev'
|
|
100
|
-
Requires-Dist: xlrd; extra == 'dev'
|
|
101
|
-
Requires-Dist: zensical
|
|
99
|
+
Requires-Dist: xlrd>=2.0; extra == 'dev'
|
|
100
|
+
Requires-Dist: zensical==0.0.28; extra == 'dev'
|
|
102
101
|
Provides-Extra: duckdb
|
|
103
|
-
Requires-Dist: duckdb; extra == 'duckdb'
|
|
102
|
+
Requires-Dist: duckdb>=1.0; extra == 'duckdb'
|
|
104
103
|
Provides-Extra: firebird
|
|
105
|
-
Requires-Dist: firebird-driver; extra == 'firebird'
|
|
104
|
+
Requires-Dist: firebird-driver>=1.10; extra == 'firebird'
|
|
106
105
|
Provides-Extra: formats
|
|
107
|
-
Requires-Dist: defusedxml; extra == 'formats'
|
|
108
|
-
Requires-Dist: jinja2; extra == 'formats'
|
|
109
|
-
Requires-Dist: odfpy; extra == 'formats'
|
|
110
|
-
Requires-Dist: openpyxl; extra == 'formats'
|
|
111
|
-
Requires-Dist: polars; extra == 'formats'
|
|
112
|
-
Requires-Dist: pyyaml; extra == 'formats'
|
|
113
|
-
Requires-Dist: tables; extra == 'formats'
|
|
114
|
-
Requires-Dist: xlrd; extra == 'formats'
|
|
106
|
+
Requires-Dist: defusedxml>=0.7; extra == 'formats'
|
|
107
|
+
Requires-Dist: jinja2>=3.1; extra == 'formats'
|
|
108
|
+
Requires-Dist: odfpy>=1.4; extra == 'formats'
|
|
109
|
+
Requires-Dist: openpyxl>=3.1; extra == 'formats'
|
|
110
|
+
Requires-Dist: polars>=1.0; extra == 'formats'
|
|
111
|
+
Requires-Dist: pyyaml>=6.0; extra == 'formats'
|
|
112
|
+
Requires-Dist: tables>=3.10; extra == 'formats'
|
|
113
|
+
Requires-Dist: xlrd>=2.0; extra == 'formats'
|
|
115
114
|
Provides-Extra: formatter
|
|
116
115
|
Requires-Dist: sqlglot>=25.0; extra == 'formatter'
|
|
117
116
|
Provides-Extra: map
|
|
118
117
|
Requires-Dist: tkintermapview>=1.29; extra == 'map'
|
|
119
118
|
Provides-Extra: mssql
|
|
120
|
-
Requires-Dist: pyodbc; extra == 'mssql'
|
|
119
|
+
Requires-Dist: pyodbc>=5.0; extra == 'mssql'
|
|
121
120
|
Provides-Extra: mysql
|
|
122
|
-
Requires-Dist: pymysql; extra == 'mysql'
|
|
121
|
+
Requires-Dist: pymysql>=1.1; extra == 'mysql'
|
|
123
122
|
Provides-Extra: odbc
|
|
124
|
-
Requires-Dist: pyodbc; extra == 'odbc'
|
|
123
|
+
Requires-Dist: pyodbc>=5.0; extra == 'odbc'
|
|
125
124
|
Provides-Extra: oracle
|
|
126
|
-
Requires-Dist: oracledb; extra == 'oracle'
|
|
125
|
+
Requires-Dist: oracledb>=3.0; extra == 'oracle'
|
|
127
126
|
Provides-Extra: postgres
|
|
128
|
-
Requires-Dist: psycopg2-binary; extra == 'postgres'
|
|
127
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
|
|
129
128
|
Provides-Extra: upsert
|
|
130
129
|
Requires-Dist: pg-upsert>=1.22.1; extra == 'upsert'
|
|
131
130
|
Description-Content-Type: text/markdown
|
|
@@ -404,13 +403,12 @@ execsql-format --no-sql --in-place scripts/
|
|
|
404
403
|
```yaml
|
|
405
404
|
repos:
|
|
406
405
|
- repo: https://github.com/geocoug/execsql
|
|
407
|
-
rev: v2.
|
|
406
|
+
rev: v2.20.0
|
|
408
407
|
hooks:
|
|
409
408
|
- id: execsql-format
|
|
410
|
-
args: [--in-place]
|
|
411
409
|
```
|
|
412
410
|
|
|
413
|
-
See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for
|
|
411
|
+
The hook rewrites `*.sql` files in place by default. See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for `--check`, `--indent`, and other options.
|
|
414
412
|
|
|
415
413
|
# VS Code Syntax Highlighting
|
|
416
414
|
|
|
@@ -272,13 +272,12 @@ execsql-format --no-sql --in-place scripts/
|
|
|
272
272
|
```yaml
|
|
273
273
|
repos:
|
|
274
274
|
- repo: https://github.com/geocoug/execsql
|
|
275
|
-
rev: v2.
|
|
275
|
+
rev: v2.20.0
|
|
276
276
|
hooks:
|
|
277
277
|
- id: execsql-format
|
|
278
|
-
args: [--in-place]
|
|
279
278
|
```
|
|
280
279
|
|
|
281
|
-
See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for
|
|
280
|
+
The hook rewrites `*.sql` files in place by default. See the [formatter documentation](https://execsql2.readthedocs.io/en/latest/guides/formatter/) for `--check`, `--indent`, and other options.
|
|
282
281
|
|
|
283
282
|
# VS Code Syntax Highlighting
|
|
284
283
|
|
|
@@ -102,9 +102,15 @@ New options in `execsql.conf`:
|
|
|
102
102
|
|
|
103
103
|
### Tools
|
|
104
104
|
|
|
105
|
-
| Tool | Description
|
|
106
|
-
| ---------------- |
|
|
107
|
-
| `execsql-format` | Standalone CLI for normalizing metacommand indentation and uppercasing SQL keywords. Supports `--check`, `--in-place`, `--indent N` (controls both metacommand and SQL indentation), and `--leading-comma` (commas at start of lines) modes. Also available as a [pre-commit hook](../guides/formatter.md). SQL reformatting (the optional sqlglot pass) requires the `[formatter]` extra as of 2.19.0; `--no-sql` works without it. |
|
|
105
|
+
| Tool | Description |
|
|
106
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `execsql-format` | Standalone CLI for normalizing metacommand indentation and uppercasing SQL keywords. Supports `--check`, `--in-place`, `--indent N` (controls both metacommand and SQL indentation), and `--leading-comma` (commas at start of lines) modes. Also available as a [pre-commit hook](../guides/formatter.md) — the published hook defaults to `args: [--in-place]` so downstream consumers get formatting on commit without extra wiring. SQL reformatting (the optional sqlglot pass) requires the `[formatter]` extra as of 2.19.0; `--no-sql` works without it. |
|
|
108
|
+
|
|
109
|
+
Formatter correctness notes added in the 2.19.x line:
|
|
110
|
+
|
|
111
|
+
- **2.19.1** — All four substitution-variable forms (`!!var!!`, `!'!var!'!`, `!"!var!"!`, and the deferred form `!{var}!`) are hidden from the sqlglot pass. Previously, `!'!var!'!` and `!"!var!"!` were parsed as `NOT` operators and rewritten — e.g. `!'!myvar!'!` → `NOT NOT '!myvar!'`. Scripts using quoted substitution variables are now safe to run through `execsql-format`.
|
|
112
|
+
- **2.19.2** — The published pre-commit hook declares `sqlglot` as an isolated-env dependency. The 2.19.0 move of `sqlglot` to the `[formatter]` extra meant the hook env (which installs the bare package) crashed with `ModuleNotFoundError: No module named 'sqlglot'` on first run. After upgrading, downstream users should run `pre-commit clean && pre-commit install --install-hooks` once to rebuild the env.
|
|
113
|
+
- **Tagged dollar quotes** — Tagged PL/pgSQL function bodies (`$body$ … $body$`, `$func$ … $func$`, any `$tag$`) are now recognized and skipped over by the formatter. Previously the tracker only saw untagged `$$`, so tagged function bodies were sent to sqlglot which mangled `IF / END IF / LOOP / RETURN`.
|
|
108
114
|
|
|
109
115
|
### GUI
|
|
110
116
|
|
|
@@ -256,10 +262,11 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
256
262
|
|
|
257
263
|
### Credential and Logging Safety
|
|
258
264
|
|
|
259
|
-
| Area | Fix
|
|
260
|
-
| ---------------------------- |
|
|
261
|
-
| ODBC password redaction | Connection strings in log output have `Pwd=***` substituted before logging.
|
|
262
|
-
| `enc_password` documentation | Prominent warnings that XOR encryption is obfuscation only — keys are hardcoded in source.
|
|
265
|
+
| Area | Fix |
|
|
266
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
267
|
+
| ODBC password redaction | Connection strings in log output have `Pwd=***` substituted before logging. |
|
|
268
|
+
| `enc_password` documentation | Prominent warnings that XOR encryption is obfuscation only — keys are hardcoded in source. |
|
|
269
|
+
| Env-var secret denylist | Extended `_SENSITIVE_SUBSTRINGS` to cover mainstream cloud / payment / observability / VCS conventions (`*_KEY`, `APIKEY`, `API_KEY`, `DSN`, `WEBHOOK`). `-a` positional assignment log lines now redact unconditionally (`{***}` instead of the raw value). |
|
|
263
270
|
|
|
264
271
|
### Bug Fixes
|
|
265
272
|
|
|
@@ -297,6 +304,8 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
297
304
|
| Temp file creation TOCTOU race | `TempFileMgr.new_temp_fn()` discarded the `NamedTemporaryFile` handle, creating a race window. Now uses `tempfile.mkstemp()` for secure creation. |
|
|
298
305
|
| `shlex.split` on Windows incorrect mode | Called without `posix=False` on Windows, mishandling backslash-heavy paths in SHELL commands. |
|
|
299
306
|
| AST executor `~`/`+` variable scoping broken | The AST executor passed `localvars` through function parameters but never pushed `CommandList` frames onto `commandliststack`. Legacy metacommand handlers (`x_sub`, `x_rm_sub`, `xf_sub_defined`, `SUB_LOCAL`, prompt handlers, REPL) access `commandliststack[-1]` for `~` local and `+` outer-scope variables. This caused `~` vars to be invisible to SQL, `SUB_DEFINED(~var)` to always return false, and the REPL `.vars`/`.stack` to show empty state. Fixed by pushing/popping `CommandList` frames in `execute()` and `_execute_script_native()`. |
|
|
307
|
+
| Firebird adapter import | Adapter imported `fdb` (the unmaintained legacy driver) while the `[firebird]` extra declared `firebird-driver`. `pip install execsql2[firebird]` followed by a Firebird connect raised `ModuleNotFoundError: No module named 'fdb'`. Adapter now imports `firebird.driver`, matching the declared dependency. |
|
|
308
|
+
| Connect timeouts on network adapters | MySQL, SQL Server / Access / DSN (pyodbc), Firebird, and SMTP all now apply a 30 s connect timeout matching the existing PostgreSQL default. A silently-dropped network peer (firewall rule, dead VM) used to hang scripts indefinitely; now the connect fails fast. |
|
|
300
309
|
| AST parser INCLUDE quoted paths broken | The AST parser captured the full INCLUDE target including surrounding quotes (`"path"`), but the legacy dispatch regex stripped them. Quoted INCLUDE paths failed with "File does not exist" even when the file was present. Fixed by stripping matched quote pairs in the parser. |
|
|
301
310
|
| AST parser `BEGIN SCRIPT name(params)` rejected | The regex required whitespace between the script name and parameter list. `BEGIN SCRIPT foo(a,b)` (no space before `(`) silently failed to match, causing the matching `END SCRIPT` to raise "Unmatched END SCRIPT metacommand." Fixed by allowing optional whitespace before the parameter expression. |
|
|
302
311
|
| AST executor forward SCRIPT references broken | The legacy engine registered all `BEGIN SCRIPT` blocks at parse time (two-pass), so `EXECUTE SCRIPT foo` could appear before the `BEGIN SCRIPT foo` definition. The AST executor walked the tree in a single pass, so forward references failed with "There is no SCRIPT named foo." Fixed by adding a pre-registration scan of all SCRIPT blocks before execution begins. |
|
|
@@ -118,6 +118,32 @@ Use `--parse-tree` to print the AST without executing.
|
|
|
118
118
|
|
|
119
119
|
`RuntimeContext` (in `state.py`) holds the per-run mutable state. `execute()` accepts an explicit `ctx`; the `active_context()` context manager installs one as the active thread-local so metacommand handlers and database adapters resolve against it automatically. Each thread gets its own context via `threading.local()`, enabling concurrent `execsql.run()` calls. The context carries the AST script registry (`ast_scripts`), the include cycle detector (`include_chain`), and the unified execution stack (`ast_exec_stack`) — a list of `ExecFrame` records describing every active scope, IF/LOOP/BATCH block, and INCLUDE'd file. Scope frames (`kind="main"` / `kind="script"`) hold the active `localvars` and `paramvals`; block frames cache a `scope_ref` to the enclosing scope for O(1) variable lookup. `current_script_line()` reads `ctx.last_command`, which the executor updates per statement.
|
|
120
120
|
|
|
121
|
+
### Dispatch vs. AST executor — the stub seam
|
|
122
|
+
|
|
123
|
+
A handful of keywords appear in **both** the metacommand dispatch table and the AST grammar. The dispatch entries are intentional stubs that raise `ErrInfo` at runtime if reached, and exist only so the keyword tables and tooling stay complete.
|
|
124
|
+
|
|
125
|
+
| Keyword | Source |
|
|
126
|
+
| ----------------------------------------------------- | ------------------------------------------------------------ |
|
|
127
|
+
| `IF` / `ANDIF` / `ORIF` / `ELSEIF` / `ELSE` / `ENDIF` | `metacommands/control.py` raises `_ast_only_stub("IF")` etc. |
|
|
128
|
+
| `LOOP` / `BREAK` | same |
|
|
129
|
+
| `BEGIN BATCH` / `END BATCH` | same |
|
|
130
|
+
|
|
131
|
+
What the stubs are for:
|
|
132
|
+
|
|
133
|
+
- **`--dump-keywords`** walks the dispatch table to emit the canonical keyword list. The VS Code grammar in `extras/vscode-execsql/syntaxes/execsql.tmLanguage.json` is regenerated from `--dump-keywords` output (see `scripts/generate_vscode_grammar.py`). Removing the dispatch entries would silently shrink that grammar and lose highlighting for the affected keywords.
|
|
134
|
+
- **Reachability is impossible at runtime.** The AST parser owns these constructs and they never bottom out in `_exec_metacommand()`. If a stub *does* raise, that means the parser missed a structural case — file a bug rather than implementing the dispatch path.
|
|
135
|
+
|
|
136
|
+
A new contributor who greps for `ErrInfo: AST-only` lands in `metacommands/control.py:_ast_only_stub`; the same logic applies to any future keyword that the AST parser owns.
|
|
137
|
+
|
|
138
|
+
### Legacy `commandliststack` residue
|
|
139
|
+
|
|
140
|
+
`state.py` still exposes `commandliststack` (and `if_stack`, `savedscripts`) as `RuntimeContext` attributes for backwards compatibility with two surfaces:
|
|
141
|
+
|
|
142
|
+
- **`metacommands/debug.py`** — the `x_debug_commandliststack` REPL helper and a few diagnostic prints read `_state.commandliststack[-1]` to surface the active local-variable frame.
|
|
143
|
+
- **`state.py` proxy** — kept as a slot on `RuntimeContext` so external code that imported `from execsql.state import commandliststack` keeps importing.
|
|
144
|
+
|
|
145
|
+
These are **read-only diagnostic surfaces.** New control-flow or scope code must go through `ctx.ast_exec_stack` / `ExecFrame`; the legacy stack is no longer the source of truth and is not pushed/popped by the AST executor in the way the old flat-command-list engine did. Treat `commandliststack` as a debug-only window onto `ctx.localvars` / `ctx.paramvals`.
|
|
146
|
+
|
|
121
147
|
______________________________________________________________________
|
|
122
148
|
|
|
123
149
|
## Plugin System
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
This page documents the release workflow for `execsql2`. The release is
|
|
4
|
+
fully automated — the version-bump commit (plus its companion tag) is
|
|
5
|
+
the trigger; `.github/workflows/ci-cd.yml` does the rest.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- Working tree clean on `main` (or whatever branch you are releasing
|
|
10
|
+
from).
|
|
11
|
+
- Local tests green: `just check`.
|
|
12
|
+
- `CHANGELOG.md` `[Unreleased]` section reviewed and tightened to the
|
|
13
|
+
Keep-a-Changelog voice rule (one idea per bullet, user-facing tone,
|
|
14
|
+
no internal-only refactor notes). See `CLAUDE.md` for the full rule.
|
|
15
|
+
- `docs/about/divergence.md` updated for any new feature, changed
|
|
16
|
+
behavior, security fix, or removed functionality that diverges from
|
|
17
|
+
upstream `execsql` v1.130.1.
|
|
18
|
+
- You have `gh` authenticated against the `geocoug/execsql` repository
|
|
19
|
+
(`gh auth status` shows you're logged in).
|
|
20
|
+
|
|
21
|
+
## Choose the bump level
|
|
22
|
+
|
|
23
|
+
| Recipe | Use when |
|
|
24
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
25
|
+
| `just bump-patch` | Bug fixes, doc fixes, dependency hygiene, anything user-invisible. Most releases. |
|
|
26
|
+
| `just bump-minor` | New features, new metacommands / flags / formats, backwards-compatible behavior changes. |
|
|
27
|
+
| `just bump-major` | Breaking changes. Public-API signature changes, removed metacommands, dropped Python versions. |
|
|
28
|
+
| `just bump-pre 2.20.0a1` | Cut an explicit pre-release (alpha / beta / rc). Doesn't fire the publish workflow unless `--allow-dirty` and a tag-style argument trigger it. |
|
|
29
|
+
|
|
30
|
+
What each one does (per `[tool.bumpversion]` in `pyproject.toml`):
|
|
31
|
+
|
|
32
|
+
1. Rewrites the version in `pyproject.toml`, `CHANGELOG.md` (turns
|
|
33
|
+
`[Unreleased]` into the new dated section), `README.md`'s pre-commit
|
|
34
|
+
`rev:` snippet, and `docs/guides/formatter.md`'s pre-commit `rev:`
|
|
35
|
+
snippet.
|
|
36
|
+
1. Runs `uv lock` (refreshes the lockfile against the new version) and
|
|
37
|
+
`git add uv.lock`.
|
|
38
|
+
1. Creates a commit `Bump version: 2.X.Y → 2.X.Y+1`.
|
|
39
|
+
1. Creates an annotated tag `v2.X.Y+1` on that commit.
|
|
40
|
+
|
|
41
|
+
The working tree is now clean.
|
|
42
|
+
|
|
43
|
+
## Push and watch CI
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git push && git push --tags # commit AND tag, both required
|
|
47
|
+
gh run list --limit 1 # confirm the workflow fired
|
|
48
|
+
gh run watch <RUN_ID> --exit-status # block until it finishes (red on failure)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`gh run watch --exit-status` returns non-zero if any job in the workflow
|
|
52
|
+
fails. Stay on the command until it exits.
|
|
53
|
+
|
|
54
|
+
### What runs on a tag push
|
|
55
|
+
|
|
56
|
+
| Job | Gating? | Purpose |
|
|
57
|
+
| ---------------------- | ---------------------------------- | ---------------------------------------------------------------- |
|
|
58
|
+
| `lint` | yes | ruff check + ruff format check |
|
|
59
|
+
| `tests` (matrix) | yes | py3.10–3.14 × {ubuntu, macos, windows} |
|
|
60
|
+
| `integration-tests` | yes | PostgreSQL, MySQL, MSSQL service containers |
|
|
61
|
+
| `access-tests-windows` | yes (when Access install succeeds) | Real Access driver on `windows-latest` |
|
|
62
|
+
| `build` | yes | `python -m build` produces sdist + wheel |
|
|
63
|
+
| `publish` | yes (tag-gated) | OIDC trusted-publisher PyPI upload |
|
|
64
|
+
| `generate-release` | yes (tag-gated) | Creates the GitHub release with auto-extracted CHANGELOG section |
|
|
65
|
+
|
|
66
|
+
Build / publish / generate-release run only on tag refs
|
|
67
|
+
(`if: startsWith(github.ref, 'refs/tags/v')`), so a non-bump push to
|
|
68
|
+
`main` doesn't publish.
|
|
69
|
+
|
|
70
|
+
## When something goes wrong
|
|
71
|
+
|
|
72
|
+
### A test failed after the tag push (PyPI not yet published)
|
|
73
|
+
|
|
74
|
+
The `build` job won't run if any earlier job is red, so `publish` won't
|
|
75
|
+
fire. Fix:
|
|
76
|
+
|
|
77
|
+
1. Identify the failure: `gh run view <RUN_ID> --log-failed`.
|
|
78
|
+
1. Fix it on `main`.
|
|
79
|
+
1. Re-bump: `just bump-patch` (this creates a *new* tag at the next patch
|
|
80
|
+
level; do NOT delete the old tag and retag the new commit — once a
|
|
81
|
+
tag has been visible publicly, treat it as immutable).
|
|
82
|
+
1. Push and re-watch.
|
|
83
|
+
|
|
84
|
+
### `publish` failed but the tag is live
|
|
85
|
+
|
|
86
|
+
This is the worst case: PyPI is unaware, GitHub thinks the release
|
|
87
|
+
happened. Fix:
|
|
88
|
+
|
|
89
|
+
1. `gh run view <RUN_ID> --log-failed` and read the publish job log.
|
|
90
|
+
1. Common cause: OIDC trust-policy drift, transient PyPI 5xx, or a name
|
|
91
|
+
collision with the pre-release tag.
|
|
92
|
+
1. If transient: `gh run rerun --failed <RUN_ID>` and watch again.
|
|
93
|
+
1. If structural (trust policy changed, package name conflict): delete
|
|
94
|
+
the GitHub release **but not the tag**, fix the cause, and re-trigger
|
|
95
|
+
the workflow manually via the Actions UI. The tag is the source of
|
|
96
|
+
truth for the version; don't reassign it.
|
|
97
|
+
|
|
98
|
+
### `generate-release` succeeded but the CHANGELOG section is wrong
|
|
99
|
+
|
|
100
|
+
The release-notes step in the workflow extracts the CHANGELOG section
|
|
101
|
+
matching `## [<version>]`. Edit the GitHub release body directly through
|
|
102
|
+
the UI or `gh release edit v2.X.Y --notes-file <path>`. Then fix the
|
|
103
|
+
voice in `CHANGELOG.md` on `main` so future releases don't repeat the
|
|
104
|
+
mistake.
|
|
105
|
+
|
|
106
|
+
### `uv lock` produced an unrelated diff
|
|
107
|
+
|
|
108
|
+
`bump-my-version` runs `uv lock` as a pre-commit hook to refresh the
|
|
109
|
+
lockfile. If unrelated packages also moved (because they updated since
|
|
110
|
+
your last sync), that's expected — `uv` is doing the right thing. If the
|
|
111
|
+
diff is large enough to be worrying, abort the bump (`git restore .`),
|
|
112
|
+
run `uv lock` manually, review, commit the lock update on its own, then
|
|
113
|
+
re-bump.
|
|
114
|
+
|
|
115
|
+
## Post-release sanity check
|
|
116
|
+
|
|
117
|
+
After `gh run watch` exits green:
|
|
118
|
+
|
|
119
|
+
- `pip install execsql2==2.X.Y` in a throwaway venv — confirms the
|
|
120
|
+
wheel landed on PyPI.
|
|
121
|
+
- Browse the new GitHub release page; check the CHANGELOG section
|
|
122
|
+
matches what's on `main`.
|
|
123
|
+
- `git pull` locally to retrieve the bump commit (you already had it
|
|
124
|
+
locally if you bumped yourself, but other contributors will sync).
|
|
@@ -93,3 +93,19 @@ Connections to SQLite databases use Python's standard library and require no add
|
|
|
93
93
|
## Additional System Requirements { #system_requirements }
|
|
94
94
|
|
|
95
95
|
To use MS Access, SQL Server, or an ODBC DSN, an appropriate ODBC driver must be installed on the system (e.g., the [Microsoft Access Database Engine](https://www.microsoft.com/en-US/download/details.aspx?id=13255) for MS Access, or the [ODBC Driver for SQL Server](https://learn.microsoft.com/en-us/sql/connect/odbc/download-odbc-driver-for-sql-server)).
|
|
96
|
+
|
|
97
|
+
### MS Access on Windows
|
|
98
|
+
|
|
99
|
+
In addition to the ODBC engine above, the MS Access adapter also needs [pywin32](https://pypi.org/project/pywin32/) to read certain Access-specific value types via the COM bridge. It is not declared in the `[mssql]` or any `execsql2` extra (the package is Windows-only and would noise up non-Windows installs); install it explicitly when you set up the rest of your Access stack:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
pip install "execsql2[mssql]" pywin32
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Oracle thin vs thick mode
|
|
106
|
+
|
|
107
|
+
[oracledb](https://pypi.org/project/oracledb/) defaults to **thin** mode (pure Python, no client libraries required) and works out of the box for most workloads. Switch to **thick** mode by installing the Oracle Instant Client (or a full Oracle client) on the host and calling `oracledb.init_oracle_client()` at startup. Thick mode is required for features the thin driver doesn't implement (Advanced Queuing, some XML features, legacy networking options). See the [oracledb docs](https://python-oracledb.readthedocs.io/en/latest/user_guide/initialization.html) for the full feature matrix.
|
|
108
|
+
|
|
109
|
+
### Firebird client library
|
|
110
|
+
|
|
111
|
+
[firebird-driver](https://pypi.org/project/firebird-driver/) is the Python bindings; the actual Firebird C client library (`fbclient.dll` on Windows, `libfbclient.so` on Linux, `libfbclient.dylib` on macOS) is loaded at runtime. Install it via your Firebird server distribution or the standalone [Firebird ODBC / client packages](https://firebirdsql.org/en/firebird-client-installer/), and make sure it's on the OS loader path (`PATH` on Windows, `LD_LIBRARY_PATH` on Linux, `DYLD_LIBRARY_PATH` on macOS) before invoking execsql.
|
|
@@ -30,14 +30,15 @@ By default, formatted output is written to stdout. Use `--in-place` to overwrite
|
|
|
30
30
|
|
|
31
31
|
### Options { #options }
|
|
32
32
|
|
|
33
|
-
| Option | Default | Description
|
|
34
|
-
| ------------------ | -------- |
|
|
35
|
-
| `FILE_OR_DIR` | required | One or more files or directories to format. Directories are searched recursively for `*.sql` files.
|
|
36
|
-
| `--check` | off | Exit with code 1 if any file would be reformatted. Does not write any changes. Useful in CI.
|
|
37
|
-
| `-i`, `--in-place` | off | Modify files in place instead of writing to stdout.
|
|
38
|
-
| `--no-sql` | off | Skip SQL reformatting via sqlglot. Only normalizes metacommand indentation and keyword casing.
|
|
39
|
-
| `--indent N` | `4` | Spaces per indent level. Controls both metacommand block depth and SQL indentation (columns, subqueries, etc).
|
|
40
|
-
| `--leading-comma` | off | Place commas at the start of lines instead of the end (e.g. ` , col2` instead of `col1,`).
|
|
33
|
+
| Option | Default | Description |
|
|
34
|
+
| ------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `FILE_OR_DIR` | required | One or more files or directories to format. Directories are searched recursively for `*.sql` files. |
|
|
36
|
+
| `--check` | off | Exit with code 1 if any file would be reformatted. Does not write any changes. Useful in CI. |
|
|
37
|
+
| `-i`, `--in-place` | off | Modify files in place instead of writing to stdout. |
|
|
38
|
+
| `--no-sql` | off | Skip SQL reformatting via sqlglot. Only normalizes metacommand indentation and keyword casing. |
|
|
39
|
+
| `--indent N` | `4` | Spaces per indent level. Controls both metacommand block depth and SQL indentation (columns, subqueries, etc). |
|
|
40
|
+
| `--leading-comma` | off | Place commas at the start of lines instead of the end (e.g. ` , col2` instead of `col1,`). |
|
|
41
|
+
| `--encoding NAME` | `utf-8` | Text encoding used to read and write SQL files. Pass `cp1252`, `latin-1`, `shift_jis`, etc. for files saved by non-UTF-8 editors. |
|
|
41
42
|
|
|
42
43
|
## What Gets Formatted { #what-gets-formatted }
|
|
43
44
|
|
|
@@ -159,13 +160,12 @@ select id,name,created_at from users where active = true order by name;
|
|
|
159
160
|
```yaml
|
|
160
161
|
repos:
|
|
161
162
|
- repo: https://github.com/geocoug/execsql
|
|
162
|
-
rev: v2.
|
|
163
|
+
rev: v2.20.0
|
|
163
164
|
hooks:
|
|
164
165
|
- id: execsql-format
|
|
165
|
-
args: [--in-place]
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
The hook runs on `*.sql` files
|
|
168
|
+
The hook runs on `*.sql` files and rewrites them in place by default (`args: [--in-place]` is baked into the published hook). To run in CI-style check-only mode that fails without modifying files, override with `args: [--check]`. To combine in-place rewriting with a custom indent width, use `args: [--in-place, --indent, "2"]` — note that any explicit `args:` you supply replaces the default, so include `--in-place` when adding more flags if you still want in-place behavior. Run `pre-commit autoupdate` periodically to bump the `rev`.
|
|
169
169
|
|
|
170
170
|
## Exit Codes { #exit-codes }
|
|
171
171
|
|