execsql2 2.32.0__tar.gz → 2.33.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.32.0 → execsql2-2.33.0}/CHANGELOG.md +14 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/PKG-INFO +2 -2
- {execsql2-2.32.0 → execsql2-2.33.0}/README.md +1 -1
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/about/divergence.md +10 -10
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/formatter.md +1 -1
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/reference/lint.md +1 -1
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/reference/metacommands.md +6 -4
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/package-lock.json +2 -2
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/package.json +1 -1
- {execsql2-2.32.0 → execsql2-2.33.0}/pyproject.toml +2 -2
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/reference.py +10 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/upsert.py +13 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/engine.py +5 -7
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/executor.py +3 -1
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/test_postgres.py +49 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_pg_upsert.py +34 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_engine.py +22 -17
- execsql2-2.33.0/tests/test_metacommand_errors.py +98 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_script.py +3 -3
- {execsql2-2.32.0 → execsql2-2.33.0}/uv.lock +1 -1
- {execsql2-2.32.0 → execsql2-2.33.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.github/dependabot.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.gitignore +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.pre-commit-config.yaml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.python-version +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/.readthedocs.yaml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/CONTRIBUTING.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/LICENSE.txt +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/NOTICE +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/SECURITY.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/about/contributors.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/about/copyright.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/api/cli.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/api/db.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/api/exporters.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/api/importers.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/api/index.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/api/metacommands.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/dev/architecture.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/dev/releasing.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/getting-started/installation.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/debugging.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/documentation.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/editors.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/encoding.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/examples.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/logging.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/usage.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/actions.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/actions2.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/checkboxes.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/connect.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/fatals.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/unmatched.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/index.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/reference/configuration.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/reference/security.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/templates/compare.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/templates/glossary.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/templates/index.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/docs/templates/upsert.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/plugin-template/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/.vscodeignore +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/LICENSE.txt +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/extension.js +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/icon.png +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/justfile +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/scripts/benchmarks/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/scripts/benchmarks/results/.gitignore +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/scripts/benchmarks/run_benchmarks.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/scripts/release_notes.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/__main__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/api.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/application.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/config.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/format.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/init.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/lint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/listing.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/lsp.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/commands/run.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/dispatch.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/help.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/options.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/cli/run.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/config.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/config_validation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/data/script_header.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/access.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/factory.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/odbc.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/db/tiers.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exceptions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/format.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/gui/base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/gui/console.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/interactive.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/_util.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/cache.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/code_actions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/completion.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/diagnostics.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/document.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/folding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/formatting.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/hover.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/navigation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/rename.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/lsp/server.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/manifest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/effects.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/models.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/plugins.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/py.typed +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/ast.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/control.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/script/variables.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/state.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/state.pyi +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/types.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/color.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/paths.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/config_settings.sqlite +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/make_config_db.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/md_compare.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/md_glossary.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/md_upsert.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/pg_compare.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/pg_glossary.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/script_template.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/ss_compare.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/ss_glossary.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/templates/ss_upsert.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_cli.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_cli_surface.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_commands.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_config_validate.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_dispatch.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_error_location.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_init.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_lint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_lint_fixtures.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_log_exit_record.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_manifest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_ping.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_profile.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_sigterm.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/cli/test_undeclared_imports.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/formatter_shapes/comment_anchoring.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/formatter_shapes/escapes_and_literals.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/formatter_shapes/substitution_and_metacommands.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/formatter_shapes/unparsable_sql.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/lint/conditions_and_blocks.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/lint/does_not_parse.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/lint/every_metacommand.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/lint/invalid.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/ods/libreoffice.fods +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/data/ods/libreoffice.ods +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_bulk_inserts.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_copy_new.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_db_adapters_mocked_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_factory.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_factory_postgres_encoding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_import_atomic.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_import_replacement_quoted.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_import_short_rows.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_import_timezones.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_mysql_trailing_blank_lines.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_new_table_checks.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_odbc_fresh_sessions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_oracle_driver.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_postgres.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_postgres_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_quoted_substitution_roundtrip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_replacement_missing_table.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_roundtrip_adapters.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_sqlserver_driver18.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_tiers.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/db/test_transactions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_db.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_columnar.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_dbfile.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_delimited.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_latex_xml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_markup.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_pretty_raw.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_template.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_values.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_roundtrip_xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_values_roundtrip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_backends.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_format_table.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_csv_blank_lines.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_ods_values.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_roundtrip_csv.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/live_db.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_cache.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_code_actions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_completion.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_diagnostics.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_document.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_folding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_formatting.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_hover.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_lsp_command.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_navigation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_rename.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/lsp/test_server.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_conditions_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_prompt.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_serve_output.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_log_redaction.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_log_secrets.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_network_paths.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_odbc_connection_strings.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/templates/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/templates/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/templates/test_compare.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/templates/test_glossary.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/templates/test_static.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/templates/test_upsert.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_api.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_api_errors.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_api_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_api_filewriter.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_api_isolation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_api_subprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_ast.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_ast_parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_config.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_config_data.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_config_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_date_inference.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_debug_repl.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_error_messages.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_exceptions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_executor.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_format.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_interactive.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_mail.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_models.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_package.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_parser_params.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_plugins.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_reference.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_registry.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_release_notes.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_state.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_tooling_versions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/test_types.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_auth.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_errors.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_keyring_rules.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_mail_tls.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_regex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_strings.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_timer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.33.0}/zensical.toml +0 -0
|
@@ -13,6 +13,20 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.33.0] - 2026-10-16
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **`$PG_UPSERT_STATUS`** says how a `PG_UPSERT` ended (`committed`, `dry_run`, `no_changes`, `qa_failed`, `cancelled`, `db_error`), and **`$PG_UPSERT_ERROR`** holds the database error message. A run with nothing to load and a cancelled run used to look the same in the other variables.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- **A failed metacommand sets `$LAST_ERROR` and `$ERROR_MESSAGE`** with `METACOMMAND_ERROR_HALT OFF`, so a script can tell what failed, not only that `METACOMMAND_ERROR()` is true; an `ON ERROR_HALT` handler (including `ON ERROR_HALT WRITE "…!{$LAST_ERROR}!"`) now gets `$LAST_ERROR` too. The error is also written to `execsql.log`.
|
|
25
|
+
- **`execsql.run()` reports a metacommand that failed under `METACOMMAND_ERROR_HALT OFF`** in `result.errors`, with `success` False, as it already did for a SQL error under `ERROR_HALT OFF`.
|
|
26
|
+
- **`PG_UPSERT` cancelled from a prompt** no longer leaves an earlier run's `$PG_UPSERT_*` values (such as `$PG_UPSERT_COMMITTED` `TRUE`) in place.
|
|
27
|
+
|
|
28
|
+
______________________________________________________________________
|
|
29
|
+
|
|
16
30
|
## [2.32.0] - 2026-10-16
|
|
17
31
|
|
|
18
32
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.33.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
|
|
@@ -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.
|
|
444
|
+
rev: v2.33.0
|
|
445
445
|
hooks:
|
|
446
446
|
- id: execsql-format
|
|
447
447
|
- id: execsql-lint
|
|
@@ -48,15 +48,15 @@ ______________________________________________________________________
|
|
|
48
48
|
|
|
49
49
|
### Metacommands
|
|
50
50
|
|
|
51
|
-
| Metacommand | Description
|
|
52
|
-
| ----------------------- |
|
|
53
|
-
| `ASSERT` | Evaluate a condition and raise an error (halting the script) if it is false. Supports all IF conditions. Optional quoted failure message. Skipped in false IF blocks.
|
|
54
|
-
| `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. See [Debugging](#debugging) below for full details.
|
|
55
|
-
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime.
|
|
56
|
-
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file.
|
|
57
|
-
| `PG_UPSERT` | QA-checked, FK-dependency-ordered upserts from staging to base schema on PostgreSQL via the optional [pg-upsert](https://pg-upsert.readthedocs.io/) dependency. Modes: `PG_UPSERT` (full pipeline), `PG_UPSERT QA` (checks only), `PG_UPSERT CHECK` (schema only). QA checks only the rows the given `METHOD` would write. QA failure does not raise — the result is reported via `$PG_UPSERT_QA_PASSED` so the caller's `IF` / `ASSERT` decides control flow. |
|
|
58
|
-
| `IMPORT … FROM JSON` | Import a JSON file (array of objects or [JSON Lines](https://jsonlines.org/)) into a database table. Nested objects are flattened with dot-separated column names; arrays are stored as JSON strings.
|
|
59
|
-
| `SHOW SCRIPTS [<name>]` | Without a name, lists all registered SCRIPT definitions with parameter signatures and source locations. With a name, shows detail including parameters (with defaults), source file/line range, and docstring.
|
|
51
|
+
| Metacommand | Description |
|
|
52
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `ASSERT` | Evaluate a condition and raise an error (halting the script) if it is false. Supports all IF conditions. Optional quoted failure message. Skipped in false IF blocks. |
|
|
54
|
+
| `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. See [Debugging](#debugging) below for full details. |
|
|
55
|
+
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime. |
|
|
56
|
+
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file. |
|
|
57
|
+
| `PG_UPSERT` | QA-checked, FK-dependency-ordered upserts from staging to base schema on PostgreSQL via the optional [pg-upsert](https://pg-upsert.readthedocs.io/) dependency. Modes: `PG_UPSERT` (full pipeline), `PG_UPSERT QA` (checks only), `PG_UPSERT CHECK` (schema only). QA checks only the rows the given `METHOD` would write. QA failure does not raise — the result is reported via `$PG_UPSERT_QA_PASSED` so the caller's `IF` / `ASSERT` decides control flow; `$PG_UPSERT_STATUS` says how the run ended. |
|
|
58
|
+
| `IMPORT … FROM JSON` | Import a JSON file (array of objects or [JSON Lines](https://jsonlines.org/)) into a database table. Nested objects are flattened with dot-separated column names; arrays are stored as JSON strings. |
|
|
59
|
+
| `SHOW SCRIPTS [<name>]` | Without a name, lists all registered SCRIPT definitions with parameter signatures and source locations. With a name, shows detail including parameters (with defaults), source file/line range, and docstring. |
|
|
60
60
|
|
|
61
61
|
### SCRIPT Enhancements
|
|
62
62
|
|
|
@@ -349,7 +349,7 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
349
349
|
| `WRITE` after `CD` | A relative `WRITE ... TO` path is resolved against the working directory at the time of the `WRITE`, so it follows an earlier `CD` the way `EXPORT` and `IMPORT` already did. Upstream wrote it relative to the directory execsql started in, because its writer subprocess never saw the `CD`. |
|
|
350
350
|
| Substitution variable cycles | 100-iteration limit prevents infinite loops on cyclic variable references. |
|
|
351
351
|
| Script location in error messages | `ErrInfo.script_file` and `script_line_no` are now populated via `stamp_errinfo()` so error output includes "Line N of script foo.sql" context — restoring behavior present in the monolith. |
|
|
352
|
-
| `$ERROR_MESSAGE` not updated | `$ERROR_MESSAGE` is now set on every error path: `exit_now()`, non-halting SQL errors, and non-halting metacommand errors. Previously it was initialized to `""` and never changed.
|
|
352
|
+
| `$ERROR_MESSAGE` not updated | `$ERROR_MESSAGE` is now set on every error path: `exit_now()`, non-halting SQL errors, and non-halting metacommand errors. Previously it was initialized to `""` and never changed. `$LAST_ERROR` also holds a failed metacommand, with `METACOMMAND_ERROR_HALT OFF` and in an `ON ERROR_HALT` handler, as the documentation describes; upstream set it only for SQL errors and unknown metacommands. |
|
|
353
353
|
| Metacommand error message lost | When `halt_on_metacommand_err` is `ON`, the original handler `ErrInfo` is now re-raised; the generic "Unknown metacommand" message no longer replaces the specific error from the handler. |
|
|
354
354
|
| Empty script name in error msg | `_execute_script_direct()` and `_execute_script_textual_console()` no longer append "in script , line 0" to uncaught-exception messages when `current_script_line()` returns an empty string. |
|
|
355
355
|
| `PROMPT COMPARE` diff comparison | Diff engine uses native Python equality instead of string comparison — numeric types, Decimals, and booleans compare correctly. `None` is distinguished from empty string. Columns are matched by name (not position), key columns are excluded from comparison, and duplicate PKs keep the first row. |
|
|
@@ -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.
|
|
275
|
+
rev: v2.33.0
|
|
276
276
|
hooks:
|
|
277
277
|
- id: execsql-format
|
|
278
278
|
- id: execsql-lint # optional: fail the commit on lint errors
|
|
@@ -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.
|
|
161
|
+
rev: v2.33.0
|
|
162
162
|
hooks:
|
|
163
163
|
- id: execsql-lint
|
|
164
164
|
args: [--ignore, V002] # optional: any execsql lint option
|
|
@@ -1766,7 +1766,7 @@ Evaluates whether or not the specified value is equal to zero. If the value is n
|
|
|
1766
1766
|
METACOMMAND_ERROR()
|
|
1767
1767
|
```
|
|
1768
1768
|
|
|
1769
|
-
Evaluates whether the previous metacommand generated an error. This test for SQL errors will only be effective if the [METACOMMAND_ERROR_HALT OFF](#metacommanderrorhalt) metacommand has previously been issued. This conditional must be used in the first metacommand after any metacommand that might have encountered an error.
|
|
1769
|
+
Evaluates whether the previous metacommand generated an error. This test for SQL errors will only be effective if the [METACOMMAND_ERROR_HALT OFF](#metacommanderrorhalt) metacommand has previously been issued. This conditional must be used in the first metacommand after any metacommand that might have encountered an error: any metacommand that succeeds, even a WRITE, clears the flag. The error itself stays available until the next error: [$LAST_ERROR](substitution_vars.md#system_vars) holds the metacommand that failed and [$ERROR_MESSAGE](substitution_vars.md#system_vars) its message.
|
|
1770
1770
|
|
|
1771
1771
|
|
|
1772
1772
|
### *NEWER_DATE*
|
|
@@ -2109,7 +2109,7 @@ The [BREAK](#break) metacommand can be used to exit from a loop at any time.
|
|
|
2109
2109
|
METACOMMAND_ERROR_HALT ON|OFF
|
|
2110
2110
|
```
|
|
2111
2111
|
|
|
2112
|
-
When METACOMMAND_ERROR_HALT is set to ON, which is the default, any errors that occur during execution of a metacommand will cause an error message to be displayed immediately, and *execsql* to halt. When METACOMMAND_ERROR_HALT is set to OFF, then metacommand errors will be ignored, but can be evaluated with the [IF METACOMMAND_ERROR](#metacommanderror) conditional.
|
|
2112
|
+
When METACOMMAND_ERROR_HALT is set to ON, which is the default, any errors that occur during execution of a metacommand will cause an error message to be displayed immediately, and *execsql* to halt. When METACOMMAND_ERROR_HALT is set to OFF, then metacommand errors will be ignored, but can be evaluated with the [IF METACOMMAND_ERROR](#metacommanderror) conditional; `$LAST_ERROR` and `$ERROR_MESSAGE` hold the failed metacommand and its message.
|
|
2113
2113
|
|
|
2114
2114
|
The METACOMMAND_ERROR_HALT metacommand does not itself set or reset the internal flag that indicates whether a metacommand has encountered an error. This is so that a "METACOMMAND_ERROR_HALT Off" command can be used immediately after a potentially-failing metacommand and not alter the error flag that is set by the previous command. Specifically, constructions like this:
|
|
2115
2115
|
|
|
@@ -2356,6 +2356,8 @@ Set after every `PG_UPSERT` execution:
|
|
|
2356
2356
|
| `$PG_UPSERT_ROWS_UPDATED` | integer | Total rows updated across all tables |
|
|
2357
2357
|
| `$PG_UPSERT_ROWS_INSERTED` | integer | Total rows inserted across all tables |
|
|
2358
2358
|
| `$PG_UPSERT_COMMITTED` | TRUE/FALSE | Were changes committed? |
|
|
2359
|
+
| `$PG_UPSERT_STATUS` | string | How the run ended: `committed`, `dry_run` (changes made, then rolled back because `COMMIT` was not given), `no_changes` (QA passed, nothing to insert or update), `qa_failed`, `cancelled` or `db_error`. Empty after `PG_UPSERT QA` and `PG_UPSERT CHECK`, which do not upsert. |
|
|
2360
|
+
| `$PG_UPSERT_ERROR` | string | The database error message when `$PG_UPSERT_STATUS` is `db_error`; otherwise empty |
|
|
2359
2361
|
| `$PG_UPSERT_STAGING_SCHEMA` | string | Staging schema name used |
|
|
2360
2362
|
| `$PG_UPSERT_BASE_SCHEMA` | string | Base schema name used |
|
|
2361
2363
|
| `$PG_UPSERT_TABLES` | string | Comma-separated list of table names processed |
|
|
@@ -2380,7 +2382,7 @@ Set after every `PG_UPSERT` execution:
|
|
|
2380
2382
|
|
|
2381
2383
|
### QA failure behavior
|
|
2382
2384
|
|
|
2383
|
-
`PG_UPSERT`, `PG_UPSERT QA`, and `PG_UPSERT CHECK` do **not** raise an error when QA checks fail. The outcome is reported via `$PG_UPSERT_QA_PASSED` (and the per-table `$PG_UPSERT_TABLE_QA_PASSED`), so the script controls flow with `IF` or with execsql's standard error-halt configuration. When QA fails, the metacommand still runs `EXPORT_FAILURES`, populates all `$PG_UPSERT_*` substitution variables, and skips the upsert (no commit). Pair with `ASSERT` to halt explicitly:
|
|
2385
|
+
`PG_UPSERT`, `PG_UPSERT QA`, and `PG_UPSERT CHECK` do **not** raise an error when QA checks fail. The outcome is reported via `$PG_UPSERT_QA_PASSED` (and the per-table `$PG_UPSERT_TABLE_QA_PASSED`), so the script controls flow with `IF` or with execsql's standard error-halt configuration. When QA fails, the metacommand still runs `EXPORT_FAILURES`, populates all `$PG_UPSERT_*` substitution variables, and skips the upsert (no commit). To tell the ways a run can end apart, branch on `$PG_UPSERT_STATUS` rather than combining `$PG_UPSERT_QA_PASSED` with `$PG_UPSERT_COMMITTED`: a run with nothing to load (`no_changes`) and one the user cancelled (`cancelled`) both leave QA passed and nothing committed. Pair with `ASSERT` to halt explicitly:
|
|
2384
2386
|
|
|
2385
2387
|
```sql
|
|
2386
2388
|
-- Soft failure: branch on the result, keep running.
|
|
@@ -2398,7 +2400,7 @@ Set after every `PG_UPSERT` execution:
|
|
|
2398
2400
|
|
|
2399
2401
|
### Database errors
|
|
2400
2402
|
|
|
2401
|
-
A database error during the run (a deadlock, a lock timeout, a constraint or trigger error the QA checks did not catch) is different from a QA failure. pg-upsert rolls the run back, and `PG_UPSERT` fails like any other metacommand: the driver's message is in `$ERROR_MESSAGE`, and the script halts unless `METACOMMAND_ERROR_HALT` is off. The `$PG_UPSERT_*` variables describe the failed run (`$PG_UPSERT_COMMITTED` is `FALSE` and the row counts are `0`), not an earlier `PG_UPSERT`.
|
|
2403
|
+
A database error during the run (a deadlock, a lock timeout, a constraint or trigger error the QA checks did not catch) is different from a QA failure. pg-upsert rolls the run back, and `PG_UPSERT` fails like any other metacommand: the driver's message is in `$ERROR_MESSAGE`, and the script halts unless `METACOMMAND_ERROR_HALT` is off. The `$PG_UPSERT_*` variables describe the failed run (`$PG_UPSERT_STATUS` is `db_error`, `$PG_UPSERT_ERROR` holds the message, `$PG_UPSERT_COMMITTED` is `FALSE` and the row counts are `0`), not an earlier `PG_UPSERT`. Cancelling at one of pg-upsert's own prompts is not an error: `PG_UPSERT` returns, with `$PG_UPSERT_STATUS` `cancelled`.
|
|
2402
2404
|
|
|
2403
2405
|
The rollback ends the connection's transaction, so under `AUTOCOMMIT OFF` or inside `BEGIN BATCH`, statements run earlier on the same connection are rolled back with it.
|
|
2404
2406
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "execsql-syntax",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.33.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "execsql-syntax",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.33.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.
|
|
5
|
+
"version": "2.33.0",
|
|
6
6
|
"publisher": "geocoug",
|
|
7
7
|
"license": "GPL-3.0-or-later",
|
|
8
8
|
"icon": "icon.png",
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.33.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.
|
|
191
|
+
current_version = "2.33.0"
|
|
192
192
|
commit = true
|
|
193
193
|
tag = true
|
|
194
194
|
tag_name = "v{new_version}"
|
|
@@ -1364,6 +1364,16 @@ _SYSTEM_VARIABLES: tuple[SystemVariable, ...] = (
|
|
|
1364
1364
|
"TRUE or FALSE: whether the changes were committed.",
|
|
1365
1365
|
"metacommands.md#pg_upsert",
|
|
1366
1366
|
),
|
|
1367
|
+
SystemVariable(
|
|
1368
|
+
"$PG_UPSERT_STATUS",
|
|
1369
|
+
"How the run ended: committed, dry_run, no_changes, qa_failed, cancelled or db_error.",
|
|
1370
|
+
"metacommands.md#pg_upsert",
|
|
1371
|
+
),
|
|
1372
|
+
SystemVariable(
|
|
1373
|
+
"$PG_UPSERT_ERROR",
|
|
1374
|
+
"The database error message when $PG_UPSERT_STATUS is db_error; otherwise empty.",
|
|
1375
|
+
"metacommands.md#pg_upsert",
|
|
1376
|
+
),
|
|
1367
1377
|
SystemVariable("$PG_UPSERT_STAGING_SCHEMA", "Staging schema name used.", "metacommands.md#pg_upsert"),
|
|
1368
1378
|
SystemVariable("$PG_UPSERT_BASE_SCHEMA", "Base schema name used.", "metacommands.md#pg_upsert"),
|
|
1369
1379
|
SystemVariable("$PG_UPSERT_TABLES", "Comma-separated list of table names processed.", "metacommands.md#pg_upsert"),
|
|
@@ -185,6 +185,11 @@ def _set_subvars(result: Any) -> None:
|
|
|
185
185
|
sv("$PG_UPSERT_ROWS_UPDATED", str(result.total_updated))
|
|
186
186
|
sv("$PG_UPSERT_ROWS_INSERTED", str(result.total_inserted))
|
|
187
187
|
sv("$PG_UPSERT_COMMITTED", str(result.committed).upper())
|
|
188
|
+
# How the run ended: committed, dry_run, no_changes, qa_failed, cancelled
|
|
189
|
+
# or db_error. Empty for QA and CHECK, which do not run an upsert.
|
|
190
|
+
status = getattr(result, "status", None)
|
|
191
|
+
sv("$PG_UPSERT_STATUS", status.value if status is not None else "")
|
|
192
|
+
sv("$PG_UPSERT_ERROR", getattr(result, "error", None) or "")
|
|
188
193
|
sv("$PG_UPSERT_STAGING_SCHEMA", result.staging_schema)
|
|
189
194
|
sv("$PG_UPSERT_BASE_SCHEMA", result.base_schema)
|
|
190
195
|
sv("$PG_UPSERT_TABLES", ", ".join(t.table_name for t in result.tables))
|
|
@@ -426,6 +431,14 @@ def _handle_pg_upsert_errors(fn: Any, metacommandline: str | None) -> Any:
|
|
|
426
431
|
try:
|
|
427
432
|
return fn()
|
|
428
433
|
except UserCancelledError as exc:
|
|
434
|
+
# No result describes a run cancelled outside pg-upsert's own run();
|
|
435
|
+
# say what is known rather than leave an earlier run's values.
|
|
436
|
+
sv = _state.subvars.add_substitution
|
|
437
|
+
sv("$PG_UPSERT_STATUS", "cancelled")
|
|
438
|
+
sv("$PG_UPSERT_ERROR", "")
|
|
439
|
+
sv("$PG_UPSERT_COMMITTED", "FALSE")
|
|
440
|
+
sv("$PG_UPSERT_ROWS_UPDATED", "0")
|
|
441
|
+
sv("$PG_UPSERT_ROWS_INSERTED", "0")
|
|
429
442
|
raise ErrInfo(
|
|
430
443
|
"cmd",
|
|
431
444
|
command_text=metacommandline,
|
|
@@ -108,6 +108,7 @@ class MetaCommand:
|
|
|
108
108
|
"""Match *cmd_str* against this entry's regex and, if it matches, invoke the handler.
|
|
109
109
|
|
|
110
110
|
Returns ``(True, return_value)`` on a match, ``(False, None)`` otherwise.
|
|
111
|
+
Raises :class:`ErrInfo` when the handler fails.
|
|
111
112
|
"""
|
|
112
113
|
# Runs the metacommand if the command string matches the regex.
|
|
113
114
|
m = self.rx.match(cmd_str.strip())
|
|
@@ -125,13 +126,10 @@ class MetaCommand:
|
|
|
125
126
|
er = ErrInfo("cmd", command_text=cmd_str, exception_msg=exception_desc())
|
|
126
127
|
er.__cause__ = exc
|
|
127
128
|
if er:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if self.set_error_flag:
|
|
133
|
-
_state.status.metacommand_error = True
|
|
134
|
-
return True, None
|
|
129
|
+
# The caller records the error ($LAST_ERROR, $ERROR_MESSAGE,
|
|
130
|
+
# the log, the error history) and halts or carries on.
|
|
131
|
+
if self.set_error_flag or _state.status.halt_on_metacommand_err:
|
|
132
|
+
raise er
|
|
135
133
|
else:
|
|
136
134
|
if self.set_error_flag:
|
|
137
135
|
_state.status.metacommand_error = False
|
|
@@ -350,7 +350,9 @@ def _exec_metacommand(
|
|
|
350
350
|
if ctx.exec_log is not None:
|
|
351
351
|
ctx.exec_log.log_status_info(f"Metacommand error: {logged_error(e)}")
|
|
352
352
|
if status.halt_on_metacommand_err:
|
|
353
|
-
|
|
353
|
+
exit_now(1, e)
|
|
354
|
+
if (manifest := _manifest.current()) is not None:
|
|
355
|
+
manifest.record_metacommand(cmd, source, line_no, metacommandlist)
|
|
354
356
|
status.error_history.append((source, line_no, cmd, e.errmsg()))
|
|
355
357
|
return None
|
|
356
358
|
# No handler matched — truly unknown metacommand
|
|
@@ -567,6 +567,55 @@ def _upsert_outcomes(tmp_path, metacommands: str) -> dict[str, str]:
|
|
|
567
567
|
|
|
568
568
|
|
|
569
569
|
class TestPgUpsert:
|
|
570
|
+
@pytest.mark.parametrize(
|
|
571
|
+
("staging_rows", "commit", "expected"),
|
|
572
|
+
[
|
|
573
|
+
("(1, 'new'), (2, 'two')", "COMMIT", "committed"),
|
|
574
|
+
("(1, 'new'), (2, 'two')", "", "dry_run"),
|
|
575
|
+
("", "COMMIT", "no_changes"),
|
|
576
|
+
("(2, NULL)", "COMMIT", "qa_failed"),
|
|
577
|
+
],
|
|
578
|
+
ids=["committed", "dry_run", "no_changes", "qa_failed"],
|
|
579
|
+
)
|
|
580
|
+
def test_status_says_how_the_run_ended(self, tmp_path, upsert_schemas, staging_rows, commit, expected):
|
|
581
|
+
if staging_rows:
|
|
582
|
+
_exec_pg(f"INSERT INTO ups_stg.books VALUES {staging_rows}")
|
|
583
|
+
outcomes = _upsert_outcomes(
|
|
584
|
+
tmp_path,
|
|
585
|
+
f"""\
|
|
586
|
+
-- !x! PG_UPSERT FROM ups_stg TO ups_base TABLES books {commit}
|
|
587
|
+
-- !x! WRITE "status=!!$PG_UPSERT_STATUS!!" TO OUT
|
|
588
|
+
-- !x! WRITE "error=!!$PG_UPSERT_ERROR!!" TO OUT
|
|
589
|
+
""",
|
|
590
|
+
)
|
|
591
|
+
assert outcomes == {"status": expected, "error": ""}
|
|
592
|
+
|
|
593
|
+
def test_a_database_error_is_status_db_error_with_its_message(self, tmp_path, upsert_schemas):
|
|
594
|
+
_exec_pg("INSERT INTO ups_stg.books VALUES (2, 'two')")
|
|
595
|
+
_exec_pg(
|
|
596
|
+
"CREATE FUNCTION ups_base.refuse() RETURNS trigger LANGUAGE plpgsql AS "
|
|
597
|
+
"$$ BEGIN RAISE EXCEPTION 'books are read-only'; END $$",
|
|
598
|
+
)
|
|
599
|
+
_exec_pg(
|
|
600
|
+
"CREATE TRIGGER refuse BEFORE INSERT ON ups_base.books FOR EACH ROW EXECUTE FUNCTION ups_base.refuse()",
|
|
601
|
+
)
|
|
602
|
+
_write_conf(tmp_path)
|
|
603
|
+
out, err = tmp_path / "out.txt", tmp_path / "error.txt"
|
|
604
|
+
script = write_script(
|
|
605
|
+
tmp_path,
|
|
606
|
+
f"""\
|
|
607
|
+
-- !x! METACOMMAND_ERROR_HALT OFF
|
|
608
|
+
-- !x! PG_UPSERT FROM ups_stg TO ups_base TABLES books COMMIT
|
|
609
|
+
-- !x! WRITE "status=!!$PG_UPSERT_STATUS!! committed=!!$PG_UPSERT_COMMITTED!!" TO {out}
|
|
610
|
+
-- !x! WRITE `!!$PG_UPSERT_ERROR!!` TO {err}
|
|
611
|
+
""",
|
|
612
|
+
)
|
|
613
|
+
result = _run_execsql_pg(tmp_path, script, timeout=60)
|
|
614
|
+
assert result.returncode == 0, f"stderr: {result.stderr}"
|
|
615
|
+
assert out.read_text().strip() == "status=db_error committed=FALSE"
|
|
616
|
+
assert "books are read-only" in err.read_text()
|
|
617
|
+
assert _query_pg("SELECT id FROM ups_base.books ORDER BY id") == [(1,)]
|
|
618
|
+
|
|
570
619
|
def test_qa_follows_method(self, tmp_path, upsert_schemas):
|
|
571
620
|
"""A NULL title on a new row fails QA for upsert, but update never writes that row."""
|
|
572
621
|
_exec_pg("INSERT INTO ups_stg.books VALUES (1, 'new'), (2, NULL)")
|
|
@@ -893,6 +893,40 @@ class TestUserCancelled:
|
|
|
893
893
|
metacommandline="PG_UPSERT FROM staging TO public TABLES books INTERACTIVE",
|
|
894
894
|
)
|
|
895
895
|
assert "cancelled" in str(exc_info.value)
|
|
896
|
+
# No earlier run's values are left behind: nothing was committed.
|
|
897
|
+
calls = {c[0][0]: c[0][1] for c in state.subvars.add_substitution.call_args_list}
|
|
898
|
+
assert calls["$PG_UPSERT_STATUS"] == "cancelled"
|
|
899
|
+
assert calls["$PG_UPSERT_ERROR"] == ""
|
|
900
|
+
assert calls["$PG_UPSERT_COMMITTED"] == "FALSE"
|
|
901
|
+
assert (calls["$PG_UPSERT_ROWS_UPDATED"], calls["$PG_UPSERT_ROWS_INSERTED"]) == ("0", "0")
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
class TestRunStatus:
|
|
905
|
+
"""$PG_UPSERT_STATUS and $PG_UPSERT_ERROR come from the result's status and error."""
|
|
906
|
+
|
|
907
|
+
def test_status_and_error_from_the_result(self, mock_state):
|
|
908
|
+
import enum
|
|
909
|
+
|
|
910
|
+
from execsql.metacommands.upsert import _set_subvars
|
|
911
|
+
|
|
912
|
+
class RunStatus(enum.Enum):
|
|
913
|
+
DB_ERROR = "db_error"
|
|
914
|
+
|
|
915
|
+
state, _ = mock_state
|
|
916
|
+
result = FakeUpsertResult()
|
|
917
|
+
result.status = RunStatus.DB_ERROR
|
|
918
|
+
result.error = "trigger said no"
|
|
919
|
+
_set_subvars(result)
|
|
920
|
+
calls = {c[0][0]: c[0][1] for c in state.subvars.add_substitution.call_args_list}
|
|
921
|
+
assert (calls["$PG_UPSERT_STATUS"], calls["$PG_UPSERT_ERROR"]) == ("db_error", "trigger said no")
|
|
922
|
+
|
|
923
|
+
def test_no_status_is_empty(self, mock_state):
|
|
924
|
+
from execsql.metacommands.upsert import _set_subvars
|
|
925
|
+
|
|
926
|
+
state, _ = mock_state
|
|
927
|
+
_set_subvars(FakeUpsertResult()) # QA and CHECK build a result without one
|
|
928
|
+
calls = {c[0][0]: c[0][1] for c in state.subvars.add_substitution.call_args_list}
|
|
929
|
+
assert (calls["$PG_UPSERT_STATUS"], calls["$PG_UPSERT_ERROR"]) == ("", "")
|
|
896
930
|
|
|
897
931
|
|
|
898
932
|
# ---------------------------------------------------------------------------
|
|
@@ -100,17 +100,16 @@ class TestMetaCommandRun:
|
|
|
100
100
|
assert mc.hitcount == 0
|
|
101
101
|
|
|
102
102
|
def test_handler_raises_errinf_no_halt(self, engine_state):
|
|
103
|
-
"""ErrInfo from handler
|
|
103
|
+
"""ErrInfo from the handler goes to the caller, which records it ($LAST_ERROR, history)."""
|
|
104
104
|
_state.status.halt_on_metacommand_err = False
|
|
105
105
|
|
|
106
106
|
def bad_handler(**kw):
|
|
107
107
|
raise ErrInfo("error", other_msg="oops")
|
|
108
108
|
|
|
109
109
|
mc = MetaCommand(re.compile(r"^\s*FAIL\s*$", re.I), bad_handler, set_error_flag=True)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
assert
|
|
113
|
-
assert _state.status.metacommand_error is True
|
|
110
|
+
with pytest.raises(ErrInfo, match="oops"):
|
|
111
|
+
mc.run("FAIL")
|
|
112
|
+
assert mc.hitcount == 0
|
|
114
113
|
|
|
115
114
|
def test_handler_raises_general_exception_no_halt(self, engine_state):
|
|
116
115
|
"""Generic exception from handler is converted to ErrInfo."""
|
|
@@ -120,10 +119,19 @@ class TestMetaCommandRun:
|
|
|
120
119
|
raise ValueError("boom")
|
|
121
120
|
|
|
122
121
|
mc = MetaCommand(re.compile(r"^\s*BOOM\s*$", re.I), bad_handler, set_error_flag=True)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
assert
|
|
126
|
-
|
|
122
|
+
with pytest.raises(ErrInfo) as excinfo:
|
|
123
|
+
mc.run("BOOM")
|
|
124
|
+
assert isinstance(excinfo.value.__cause__, ValueError)
|
|
125
|
+
|
|
126
|
+
def test_set_error_flag_false_error_without_halt_is_no_match(self, engine_state):
|
|
127
|
+
"""As upstream: a failing METACOMMAND_ERROR_HALT-style command reports no match."""
|
|
128
|
+
_state.status.halt_on_metacommand_err = False
|
|
129
|
+
|
|
130
|
+
def bad_handler(**kw):
|
|
131
|
+
raise ErrInfo("error", other_msg="nope")
|
|
132
|
+
|
|
133
|
+
mc = MetaCommand(re.compile(r"^\s*NOFLAG\s*$", re.I), bad_handler, set_error_flag=False)
|
|
134
|
+
assert mc.run("NOFLAG") == (False, None)
|
|
127
135
|
|
|
128
136
|
def test_successful_run_clears_metacommand_error(self, engine_state):
|
|
129
137
|
_state.status.metacommand_error = True
|
|
@@ -154,20 +162,17 @@ class TestMetaCommandRun:
|
|
|
154
162
|
with pytest.raises(SystemExit):
|
|
155
163
|
mc.run("EXIT")
|
|
156
164
|
|
|
157
|
-
def
|
|
158
|
-
"""
|
|
165
|
+
def test_halting_is_left_to_the_caller(self, engine_state):
|
|
166
|
+
"""With halt_on_metacommand_err, the error is raised; the executor records it and halts."""
|
|
159
167
|
_state.status.halt_on_metacommand_err = True
|
|
160
168
|
|
|
161
169
|
def bad_handler(**kw):
|
|
162
170
|
raise ErrInfo("error", other_msg="halt me")
|
|
163
171
|
|
|
164
172
|
mc = MetaCommand(re.compile(r"^\s*HALTNOW\s*$", re.I), bad_handler)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
with pytest.raises(SystemExit):
|
|
169
|
-
mc.run("HALTNOW")
|
|
170
|
-
mock_exit.assert_called_once()
|
|
173
|
+
with patch("execsql.utils.errors.exit_now") as mock_exit, pytest.raises(ErrInfo, match="halt me"):
|
|
174
|
+
mc.run("HALTNOW")
|
|
175
|
+
mock_exit.assert_not_called()
|
|
171
176
|
|
|
172
177
|
def test_repr_contains_pattern(self):
|
|
173
178
|
mc = MetaCommand(re.compile(r"^\s*ABC\s*$", re.I), lambda **kw: None, "test cmd")
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""What a failed metacommand leaves for the script: $LAST_ERROR, $ERROR_MESSAGE, METACOMMAND_ERROR().
|
|
2
|
+
|
|
3
|
+
With METACOMMAND_ERROR_HALT OFF the script carries on and can read both
|
|
4
|
+
variables; with it ON, an ON ERROR_HALT handler can. The flag clears on the
|
|
5
|
+
next metacommand that succeeds; the variables keep their values until the next
|
|
6
|
+
error.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
from execsql import run
|
|
15
|
+
|
|
16
|
+
_FAILING = '-- !x! IMPORT TO NEW nofile FROM "does_not_exist.csv"\n'
|
|
17
|
+
_FAILED_COMMAND = 'IMPORT TO NEW nofile FROM "does_not_exist.csv"'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _run(tmp_path, monkeypatch, sql: str):
|
|
21
|
+
monkeypatch.chdir(tmp_path)
|
|
22
|
+
result = run(sql=sql, dsn=f"sqlite:///{tmp_path / 't.db'}", new_db=True)
|
|
23
|
+
out = tmp_path / "out.txt"
|
|
24
|
+
return result, out.read_text().splitlines() if out.exists() else []
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_without_halting_the_script_can_read_the_error(tmp_path, monkeypatch):
|
|
28
|
+
result, lines = _run(
|
|
29
|
+
tmp_path,
|
|
30
|
+
monkeypatch,
|
|
31
|
+
"-- !x! METACOMMAND_ERROR_HALT OFF\n" + _FAILING + "-- !x! IF (METACOMMAND_ERROR())\n"
|
|
32
|
+
"-- !x! WRITE `last=[!!$LAST_ERROR!!]` TO out.txt\n"
|
|
33
|
+
"-- !x! WRITE `message=[!!$ERROR_MESSAGE!!]` TO out.txt\n"
|
|
34
|
+
"-- !x! ENDIF\n",
|
|
35
|
+
)
|
|
36
|
+
assert lines[0] == f"last=[{_FAILED_COMMAND}]"
|
|
37
|
+
assert "does_not_exist.csv does not exist" in "\n".join(lines[1:])
|
|
38
|
+
# The failure is one of the run's errors, as a non-halting SQL error is.
|
|
39
|
+
assert not result.success
|
|
40
|
+
assert [e.sql for e in result.errors] == [_FAILED_COMMAND]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_the_flag_clears_on_success_and_the_variables_keep_their_values(tmp_path, monkeypatch):
|
|
44
|
+
_, lines = _run(
|
|
45
|
+
tmp_path,
|
|
46
|
+
monkeypatch,
|
|
47
|
+
"-- !x! METACOMMAND_ERROR_HALT OFF\n" + _FAILING + "-- !x! WRITE `next` TO out.txt\n"
|
|
48
|
+
"-- !x! IF (NOT METACOMMAND_ERROR())\n"
|
|
49
|
+
"-- !x! WRITE `cleared; last=[!!$LAST_ERROR!!]` TO out.txt\n"
|
|
50
|
+
"-- !x! ENDIF\n",
|
|
51
|
+
)
|
|
52
|
+
assert lines == ["next", f"cleared; last=[{_FAILED_COMMAND}]"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_an_on_error_halt_handler_can_read_the_error(tmp_path, monkeypatch):
|
|
56
|
+
result, lines = _run(
|
|
57
|
+
tmp_path,
|
|
58
|
+
monkeypatch,
|
|
59
|
+
"-- !x! BEGIN SCRIPT on_err\n"
|
|
60
|
+
"-- !x! WRITE `handler last=[!!$LAST_ERROR!!]` TO out.txt\n"
|
|
61
|
+
"-- !x! END SCRIPT\n"
|
|
62
|
+
"-- !x! ON ERROR_HALT EXECUTE SCRIPT on_err\n" + _FAILING + "-- !x! WRITE `not reached` TO out.txt\n",
|
|
63
|
+
)
|
|
64
|
+
assert lines == [f"handler last=[{_FAILED_COMMAND}]"]
|
|
65
|
+
assert not result.success
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_the_documented_on_error_halt_write_example(tmp_path, monkeypatch):
|
|
69
|
+
_, lines = _run(
|
|
70
|
+
tmp_path,
|
|
71
|
+
monkeypatch,
|
|
72
|
+
'-- !x! ON ERROR_HALT WRITE "Error in line: !{$LAST_ERROR}!" TO out.txt\n' + _FAILING,
|
|
73
|
+
)
|
|
74
|
+
assert lines == [f"Error in line: {_FAILED_COMMAND}"]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _cli(tmp_path, script: str) -> subprocess.CompletedProcess:
|
|
78
|
+
(tmp_path / "s.sql").write_text(script)
|
|
79
|
+
return subprocess.run(
|
|
80
|
+
[sys.executable, "-m", "execsql", "run", "-tl", "-n", "s.sql", "t.db"],
|
|
81
|
+
cwd=tmp_path,
|
|
82
|
+
capture_output=True,
|
|
83
|
+
text=True,
|
|
84
|
+
timeout=120,
|
|
85
|
+
check=False,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_the_cli_logs_a_non_halting_error_and_exits_zero(tmp_path):
|
|
90
|
+
proc = _cli(tmp_path, "-- !x! METACOMMAND_ERROR_HALT OFF\n" + _FAILING)
|
|
91
|
+
assert proc.returncode == 0, proc.stderr
|
|
92
|
+
assert "Metacommand error:" in (tmp_path / "execsql.log").read_text()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_the_cli_still_halts_with_status_1(tmp_path):
|
|
96
|
+
proc = _cli(tmp_path, _FAILING + "-- !x! WRITE `not reached`\n")
|
|
97
|
+
assert proc.returncode == 1
|
|
98
|
+
assert "not reached" not in proc.stdout
|
|
@@ -807,15 +807,15 @@ class TestMetaCommand:
|
|
|
807
807
|
assert captured["varname"] == "myvar"
|
|
808
808
|
assert captured["value"] == "hello"
|
|
809
809
|
|
|
810
|
-
def
|
|
810
|
+
def test_run_raises_the_handler_error(self):
|
|
811
811
|
rx = re.compile(r".*", re.I)
|
|
812
812
|
|
|
813
813
|
def bad_handler(**kw):
|
|
814
814
|
raise ValueError("oops")
|
|
815
815
|
|
|
816
816
|
mc = MetaCommand(rx, bad_handler, set_error_flag=True)
|
|
817
|
-
|
|
818
|
-
|
|
817
|
+
with pytest.raises(ErrInfo):
|
|
818
|
+
mc.run("anything")
|
|
819
819
|
|
|
820
820
|
def test_run_clears_error_flag_on_success(self):
|
|
821
821
|
_state.status.metacommand_error = True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|