execsql2 2.32.0__tar.gz → 2.34.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.34.0}/CHANGELOG.md +34 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/PKG-INFO +3 -3
- {execsql2-2.32.0 → execsql2-2.34.0}/README.md +2 -2
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/about/divergence.md +20 -10
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/api/db.md +6 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/dev/adding_db_adapters.md +18 -17
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/dev/architecture.md +2 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/formatter.md +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/sql_syntax.md +20 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/index.md +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/reference/lint.md +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/reference/metacommands.md +28 -7
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/package-lock.json +2 -2
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/package.json +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/pyproject.toml +2 -2
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/base.py +71 -8
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/duckdb.py +6 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/mysql.py +54 -2
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/oracle.py +36 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/postgres.py +17 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/sqlite.py +7 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/sqlserver.py +24 -0
- execsql2-2.34.0/src/execsql/db/target_types.py +277 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/json.py +51 -7
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/protocol.py +1 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/base.py +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/csv.py +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/__init__.py +17 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/io_export.py +3 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/io_fileops.py +2 -2
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/reference.py +10 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/upsert.py +13 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/engine.py +5 -7
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/executor.py +3 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/variables.py +4 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/types.py +2 -2
- execsql2-2.34.0/tests/data/existing_table_loads/changes.json +5618 -0
- execsql2-2.34.0/tests/data/existing_table_loads/duckdb.json +1112 -0
- execsql2-2.34.0/tests/data/existing_table_loads/mysql.json +1782 -0
- execsql2-2.34.0/tests/data/existing_table_loads/mysql_files.json +2104 -0
- execsql2-2.34.0/tests/data/existing_table_loads/postgres.json +1166 -0
- execsql2-2.34.0/tests/data/existing_table_loads/postgres_files.json +1228 -0
- execsql2-2.34.0/tests/data/existing_table_loads/sqlite.json +1090 -0
- execsql2-2.34.0/tests/data/existing_table_loads/sqlserver.json +1307 -0
- execsql2-2.34.0/tests/db/test_existing_table_loads.py +357 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_postgres_inprocess.py +13 -1
- execsql2-2.34.0/tests/db/test_target_types.py +520 -0
- execsql2-2.34.0/tests/exporters/test_jsonl.py +101 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/test_mysql.py +76 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/test_postgres.py +49 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/live_db.py +23 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_pg_upsert.py +34 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_engine.py +22 -17
- execsql2-2.34.0/tests/test_metacommand_errors.py +98 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_script.py +16 -3
- {execsql2-2.32.0 → execsql2-2.34.0}/uv.lock +1 -1
- {execsql2-2.32.0 → execsql2-2.34.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.github/dependabot.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.gitignore +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.pre-commit-config.yaml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.python-version +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/.readthedocs.yaml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/CONTRIBUTING.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/LICENSE.txt +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/NOTICE +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/SECURITY.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/about/contributors.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/about/copyright.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/api/cli.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/api/exporters.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/api/importers.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/api/index.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/api/metacommands.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/dev/releasing.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/getting-started/installation.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/debugging.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/documentation.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/editors.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/encoding.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/examples.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/logging.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/usage.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/actions.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/actions2.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/checkboxes.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/connect.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/fatals.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/unmatched.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/reference/configuration.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/reference/security.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/templates/compare.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/templates/glossary.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/templates/index.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/docs/templates/upsert.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/plugin-template/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/.vscodeignore +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/LICENSE.txt +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/extension.js +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/extras/vscode-execsql/icon.png +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/justfile +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/scripts/benchmarks/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/scripts/benchmarks/results/.gitignore +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/scripts/benchmarks/run_benchmarks.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/scripts/release_notes.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/__main__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/api.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/application.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/config.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/format.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/init.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/lint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/listing.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/lsp.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/commands/run.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/dispatch.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/help.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/options.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/cli/run.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/config.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/config_validation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/data/script_header.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/access.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/factory.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/odbc.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/db/tiers.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exceptions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/format.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/gui/base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/gui/console.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/interactive.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/_util.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/cache.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/code_actions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/completion.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/diagnostics.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/document.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/folding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/formatting.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/hover.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/navigation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/rename.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/lsp/server.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/manifest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/effects.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/models.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/plugins.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/py.typed +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/ast.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/control.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/script/parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/state.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/state.pyi +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/color.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/paths.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/README.md +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/config_settings.sqlite +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/make_config_db.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/md_compare.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/md_glossary.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/md_upsert.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/pg_compare.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/pg_glossary.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/script_template.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/ss_compare.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/ss_glossary.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/templates/ss_upsert.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_cli.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_cli_surface.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_commands.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_config_validate.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_dispatch.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_error_location.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_init.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_lint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_lint_fixtures.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_log_exit_record.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_manifest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_ping.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_profile.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_sigterm.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/cli/test_undeclared_imports.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/formatter_shapes/comment_anchoring.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/formatter_shapes/escapes_and_literals.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/formatter_shapes/substitution_and_metacommands.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/formatter_shapes/unparsable_sql.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/lint/conditions_and_blocks.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/lint/does_not_parse.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/lint/every_metacommand.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/lint/invalid.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/ods/libreoffice.fods +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/data/ods/libreoffice.ods +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_bulk_inserts.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_copy_new.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_db_adapters_mocked_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_factory.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_factory_postgres_encoding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_import_atomic.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_import_replacement_quoted.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_import_short_rows.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_import_timezones.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_mysql_trailing_blank_lines.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_new_table_checks.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_odbc_fresh_sessions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_oracle_driver.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_postgres.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_quoted_substitution_roundtrip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_replacement_missing_table.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_roundtrip_adapters.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_sqlserver_driver18.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_tiers.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/db/test_transactions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_base.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_db.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_columnar.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_dbfile.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_delimited.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_json.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_latex_xml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_markup.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_ods.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_pretty_raw.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_template.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_values.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_values_roundtrip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_backends.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_format_table.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_csv_blank_lines.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_ods_values.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_roundtrip_csv.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_cache.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_code_actions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_completion.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_diagnostics.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_document.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_folding.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_formatting.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_hover.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_lsp_command.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_navigation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_rename.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/lsp/test_server.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_conditions_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_prompt.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_serve_output.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_log_redaction.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_log_secrets.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_network_paths.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_odbc_connection_strings.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/templates/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/templates/conftest.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/templates/test_compare.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/templates/test_glossary.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/templates/test_static.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/templates/test_upsert.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_api.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_api_errors.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_api_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_api_filewriter.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_api_isolation.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_api_subprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_ast.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_ast_parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_config.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_config_data.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_config_extended.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_date_inference.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_debug_repl.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_error_messages.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_exceptions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_executor.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_format.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_interactive.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_mail.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_models.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_package.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_parser.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_parser_params.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_plugins.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_reference.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_registry.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_release_notes.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_state.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_tooling_versions.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/test_types.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/__init__.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_auth.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_errors.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_keyring_rules.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_mail_tls.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_regex.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_strings.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_timer.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.32.0 → execsql2-2.34.0}/zensical.toml +0 -0
|
@@ -13,6 +13,40 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.34.0] - 2026-10-18
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **`EXPORT … AS JSONL` and `EXPORT QUERY … AS JSONL`** write [JSON Lines](https://jsonlines.org/): one object per row, one row per line. `APPEND` adds rows to the file, which stays valid; `IMPORT … FROM JSON` reads it back.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **A value with a fraction loaded into an integer column** (`INTEGER`, `BIGINT`, `DECIMAL(10,0)`, …) of an existing table by `IMPORT` or `COPY` stops the load with an error that names the value and the column, and nothing is loaded. The database used to round it (`1.5` stored as `2`), or on SQL Server truncate it. A delimited file loaded into PostgreSQL or MySQL is not affected: the server reads it as written. Round the values in the source, or load them into a `DECIMAL` column with places for the fraction.
|
|
25
|
+
- **MySQL / MariaDB sessions run in strict mode**: a SQL statement, `IMPORT` or `COPY` that gives a column a value MySQL can't store as given stops with MySQL's error, and nothing is loaded. MySQL used to store a changed value with a warning (`3000000000` as `2147483647`, `2024-02-30` as `0000-00-00`, `abcdef` in a `VARCHAR(3)` as `abc`). A timestamp ending in `Z` is one such value: write it with `+00:00`. To keep the old behavior in a script, run `SET SESSION sql_mode = 'ANSI';` after connecting.
|
|
26
|
+
- **Scripts with many substitution variables run faster**: a statement or metacommand with no `!!`, `!'!` or `!"!` in it is no longer checked against every defined variable. With 500 variables each such line took about 0.1 ms longer; a 1,000-pass `LOOP` over `EXECUTE SCRIPT` with 200 variables now runs in half the time.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`IMPORT` and `COPY` into a table that already exists** convert each value by the type of the column it goes into, not the type guessed from the source data. A text column gets the value as written: `yes`, `1`, `01/05/2024`, `1e3` and `" 7 "` were stored as `true`, `true`, `2024-01-05 00:00:00`, `1000` and `7`. On MySQL a missing value in such a column is NULL, not `''`. `TO NEW` and `TO REPLACEMENT` tables are created and filled as before.
|
|
31
|
+
- **`COPY` of a column of 0s and 1s into a PostgreSQL integer, numeric or float column** loads the numbers; it failed with "column … is of type integer but expression is of type boolean". The same goes for true/false values (`yes`/`no`, `t`/`f`) copied, or imported from a spreadsheet, JSON or Feather file, into such a column: they load as 1 and 0, as on the other databases.
|
|
32
|
+
- **A missing date or timestamp loaded into MySQL by `IMPORT` or `COPY`** is stored as NULL; it was stored as `0000-00-00` (or `0` in a number column). Delimited files were not affected.
|
|
33
|
+
|
|
34
|
+
______________________________________________________________________
|
|
35
|
+
|
|
36
|
+
## [2.33.0] - 2026-10-16
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **`$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.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- **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`.
|
|
45
|
+
- **`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`.
|
|
46
|
+
- **`PG_UPSERT` cancelled from a prompt** no longer leaves an earlier run's `$PG_UPSERT_*` values (such as `$PG_UPSERT_COMMITTED` `TRUE`) in place.
|
|
47
|
+
|
|
48
|
+
______________________________________________________________________
|
|
49
|
+
|
|
16
50
|
## [2.32.0] - 2026-10-16
|
|
17
51
|
|
|
18
52
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.34.0
|
|
4
4
|
Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
|
|
5
5
|
Project-URL: Homepage, https://execsql2.readthedocs.io/en/stable/
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -318,7 +318,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/stable/getti
|
|
|
318
318
|
**Data movement**
|
|
319
319
|
|
|
320
320
|
- Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
|
|
321
|
-
- Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
321
|
+
- Export query results in 20+ formats including CSV, TSV, JSON, JSON Lines, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
322
322
|
- Copy data between databases, including across different DBMS types.
|
|
323
323
|
|
|
324
324
|
**Script logic**
|
|
@@ -441,7 +441,7 @@ execsql format --no-sql --in-place scripts/
|
|
|
441
441
|
```yaml
|
|
442
442
|
repos:
|
|
443
443
|
- repo: https://github.com/geocoug/execsql
|
|
444
|
-
rev: v2.
|
|
444
|
+
rev: v2.34.0
|
|
445
445
|
hooks:
|
|
446
446
|
- id: execsql-format
|
|
447
447
|
- id: execsql-lint
|
|
@@ -198,7 +198,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/stable/getti
|
|
|
198
198
|
**Data movement**
|
|
199
199
|
|
|
200
200
|
- Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
|
|
201
|
-
- Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
201
|
+
- Export query results in 20+ formats including CSV, TSV, JSON, JSON Lines, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
202
202
|
- Copy data between databases, including across different DBMS types.
|
|
203
203
|
|
|
204
204
|
**Script logic**
|
|
@@ -321,7 +321,7 @@ execsql format --no-sql --in-place scripts/
|
|
|
321
321
|
```yaml
|
|
322
322
|
repos:
|
|
323
323
|
- repo: https://github.com/geocoug/execsql
|
|
324
|
-
rev: v2.
|
|
324
|
+
rev: v2.34.0
|
|
325
325
|
hooks:
|
|
326
326
|
- id: execsql-format
|
|
327
327
|
- id: execsql-lint
|
|
@@ -43,20 +43,21 @@ ______________________________________________________________________
|
|
|
43
43
|
| `PARQUET` | Export query or table results to Apache Parquet via `polars`. |
|
|
44
44
|
| `FEATHER` | Export to Apache Feather/IPC via `polars` (upstream used `pandas` + `pyarrow`). |
|
|
45
45
|
| `YAML` | Export query or table results as a YAML sequence of mappings via `PyYAML`. |
|
|
46
|
+
| `JSONL` | Export query or table results as JSON Lines: one object per row, one row per line; `APPEND` adds rows (#7). |
|
|
46
47
|
| `MARKDOWN` / `MD` | Export query or table results as a GitHub-Flavored Markdown (GFM) pipe table. Pure Python, no optional dependencies. |
|
|
47
48
|
| `XLSX` | Export query or table results to an Excel XLSX workbook via `openpyxl` (single or multi-sheet). |
|
|
48
49
|
|
|
49
50
|
### Metacommands
|
|
50
51
|
|
|
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.
|
|
52
|
+
| Metacommand | Description |
|
|
53
|
+
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| `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. |
|
|
55
|
+
| `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. See [Debugging](#debugging) below for full details. |
|
|
56
|
+
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime. |
|
|
57
|
+
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file. |
|
|
58
|
+
| `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. |
|
|
59
|
+
| `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. |
|
|
60
|
+
| `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
61
|
|
|
61
62
|
### SCRIPT Enhancements
|
|
62
63
|
|
|
@@ -228,6 +229,12 @@ Upstream opened MySQL and MariaDB connections as `latin1`, and execsql2 did the
|
|
|
228
229
|
|
|
229
230
|
The default is now `utf8mb4`. This does not change stored data or column charsets: the connection charset governs only how text moves between client and server, and MySQL transcodes to and from each column's own charset. A latin1 column read over a utf8mb4 connection returns the same string it always did. `-e latin1` restores the upstream behavior.
|
|
230
231
|
|
|
232
|
+
### MySQL Strict Mode
|
|
233
|
+
|
|
234
|
+
Upstream set `sql_mode='ANSI'` on every MySQL and MariaDB connection. That replaces the server's whole mode, and `ANSI` leaves out `STRICT_TRANS_TABLES`, so MySQL stored a changed value with a warning instead of raising an error: `3000000000` in an `INTEGER` column became `2147483647`, `2024-02-30` became `0000-00-00`, and `abcdef` in a `VARCHAR(3)` became `abc`. That applied to plain SQL statements, `IMPORT` and `COPY` alike (#101).
|
|
235
|
+
|
|
236
|
+
The mode is now `ANSI,STRICT_TRANS_TABLES`, so such a value is an error, as in the `mysql` client with the server's default settings. The mode stays fixed rather than added to the server's own, because execsql's quoting of literals relies on backslash escapes being on (no `NO_BACKSLASH_ESCAPES`). `LOAD DATA LOCAL`, which `IMPORT` uses for a delimited file, ignores strict mode, so `IMPORT` checks its warnings and rolls the load back if MySQL changed a value. `SET SESSION sql_mode = 'ANSI';` in a script restores the upstream behavior for that session, `LOAD DATA` included.
|
|
237
|
+
|
|
231
238
|
### Database Support Tiers
|
|
232
239
|
|
|
233
240
|
Upstream documented all nine DBMS adapters with equal confidence. execsql2 splits them by what is actually verified: **Supported** (PostgreSQL, MySQL/MariaDB, MS SQL Server, SQLite, DuckDB) run against a live server or a real database file on every CI run, while **best effort** (MS Access, Firebird, Oracle, ODBC DSN) are carried forward from the monolith and are exercised nowhere in CI.
|
|
@@ -349,7 +356,7 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
349
356
|
| `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
357
|
| Substitution variable cycles | 100-iteration limit prevents infinite loops on cyclic variable references. |
|
|
351
358
|
| 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.
|
|
359
|
+
| `$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
360
|
| 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
361
|
| 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
362
|
| `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. |
|
|
@@ -436,6 +443,9 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
436
443
|
| XLSX export wrote `numeric` columns as text | `_cell_value()` passed `int` and `float` straight to openpyxl but let every other type fall through to `str()`. PostgreSQL, MySQL, and DuckDB all return `Decimal` for `numeric`/`decimal` columns, so measurement values arrived in Excel as text cells — invisible in a screenshot, useless in a formula, a sort, or a chart. openpyxl lists `Decimal` in its own `NUMERIC_TYPES` and stores it as a real numeric cell, so the conversion was never needed. Found by exporting from a live PostgreSQL container and checking `cell.data_type` rather than `cell.value`. |
|
|
437
444
|
| `EMAIL` SMTP login without a password | Upstream calls `smtplib.login()` with only a username when `[email] password` and `enc_password` are both unset, which raises `TypeError` from the stdlib — `login()` has no single-argument form. execsql2 reports a configuration error naming the missing password instead. |
|
|
438
445
|
| Type scan of large integers | Working out a column's type tested each integer against the binary type by building `bytearray(n)`, n zero bytes, so a column of large integers (epoch milliseconds: about a terabyte per value) hung or ran out of memory. The binary test now answers for an integer without building it; the inferred types are unchanged. |
|
|
446
|
+
| Values loaded into an existing table | `IMPORT` and `COPY` converted every value to the type guessed from the source data, even when the table was already there with its own column types, so a text column got a different string than the source held (`yes` and `1` stored as `true`, `01/05/2024` as `2024-01-05 00:00:00`, `1e3` as `1000`), and on PostgreSQL a column of 0s and 1s copied into an integer column failed (the guess was boolean). For a table that exists, the target column's type now decides: a text column gets the value as written, a number column gets true/false values as 1 and 0, and other columns are converted as before. Each adapter reads the column types from its catalog (`pg_attribute`, `SHOW COLUMNS`, `sys.columns`) or its driver's description of the table. `TO NEW` / `REPLACEMENT` are unchanged. |
|
|
447
|
+
| Missing dates loaded into MySQL | `IMPORT` and `COPY` sent a missing value in a column scanned as a date or timestamp as `''`, so MySQL stored `0000-00-00` (or `0` in a number column) instead of NULL. It is sent as NULL. |
|
|
448
|
+
| Fractions loaded into an integer column | A value with a fraction going into an integer column (including `DECIMAL(p,0)`, `NUMERIC(p)` and Oracle `NUMBER(p)`) of an existing table is an error naming the value and the column. Upstream sent the number and the database rounded it (`1.5` → `2`; SQL Server truncated it to `1`). |
|
|
439
449
|
|
|
440
450
|
______________________________________________________________________
|
|
441
451
|
|
|
@@ -8,6 +8,12 @@ If you are adding support for a new database, start with the [Adding Database Ad
|
|
|
8
8
|
|
|
9
9
|
::: execsql.db.base
|
|
10
10
|
|
|
11
|
+
## Target column types
|
|
12
|
+
|
|
13
|
+
`IMPORT` and `COPY` into a table that already exists convert each value by the type of the column it goes into (`Database.column_kinds()`), not by the type scanned from the source data.
|
|
14
|
+
|
|
15
|
+
::: execsql.db.target_types
|
|
16
|
+
|
|
11
17
|
## Database factory
|
|
12
18
|
|
|
13
19
|
Convenience constructors used internally to create typed `Database` instances. The CLI calls the appropriate factory function based on the `-t` flag value. Each factory validates its arguments (e.g., checking that a file exists) before constructing and returning the adapter.
|
|
@@ -133,23 +133,24 @@ class MyDBDatabase(Database):
|
|
|
133
133
|
|
|
134
134
|
These are the instance attributes and methods you must configure correctly:
|
|
135
135
|
|
|
136
|
-
| Attribute / Method | Type | Purpose
|
|
137
|
-
| ------------------ | ----------------- |
|
|
138
|
-
| `self.type` | `DbType` | DBMS type token (e.g., `dbt_sqlite`). Controls quoting and type-mapping.
|
|
139
|
-
| `self.paramstr` | `str` | SQL parameter placeholder: `"?"` (most drivers) or `"%s"` (psycopg, PyMySQL).
|
|
140
|
-
| `self.encoding` | `str` | Database character encoding. Detect from the database on connect if possible.
|
|
141
|
-
| `self.autocommit` | `bool` | `True` means the driver commits automatically; `False` requires explicit commits.
|
|
142
|
-
| `self.conn` | driver connection | Set in `open_db()`.
|
|
143
|
-
| `open_db()` | method | **Must override.** Establish the connection and assign `self.conn`.
|
|
144
|
-
| `table_exists()` | method | **Must override.** Query the DBMS catalog. Use parameterized queries.
|
|
145
|
-
| `column_exists()` | method | **Must override.** Check column presence.
|
|
146
|
-
| `table_columns()` | method | **Must override.** Return column names for a table.
|
|
147
|
-
| `view_exists()` | method | **Must override.** Check view presence.
|
|
148
|
-
| `schema_exists()` | method | **Must override.** Return `False` if schemas are not supported.
|
|
149
|
-
| `drop_table()` | method | **Must override.** Drop a table (used by IMPORT when creating fresh).
|
|
150
|
-
| `populate_table()` | method | **Must override.** Bulk-load rows from a generator (used by IMPORT).
|
|
151
|
-
| `row_inserter()` | method | Override for a faster bulk path that stores exactly what `executemany()` stores.
|
|
152
|
-
| `
|
|
136
|
+
| Attribute / Method | Type | Purpose |
|
|
137
|
+
| ------------------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
138
|
+
| `self.type` | `DbType` | DBMS type token (e.g., `dbt_sqlite`). Controls quoting and type-mapping. |
|
|
139
|
+
| `self.paramstr` | `str` | SQL parameter placeholder: `"?"` (most drivers) or `"%s"` (psycopg, PyMySQL). |
|
|
140
|
+
| `self.encoding` | `str` | Database character encoding. Detect from the database on connect if possible. |
|
|
141
|
+
| `self.autocommit` | `bool` | `True` means the driver commits automatically; `False` requires explicit commits. |
|
|
142
|
+
| `self.conn` | driver connection | Set in `open_db()`. |
|
|
143
|
+
| `open_db()` | method | **Must override.** Establish the connection and assign `self.conn`. |
|
|
144
|
+
| `table_exists()` | method | **Must override.** Query the DBMS catalog. Use parameterized queries. |
|
|
145
|
+
| `column_exists()` | method | **Must override.** Check column presence. |
|
|
146
|
+
| `table_columns()` | method | **Must override.** Return column names for a table. |
|
|
147
|
+
| `view_exists()` | method | **Must override.** Check view presence. |
|
|
148
|
+
| `schema_exists()` | method | **Must override.** Return `False` if schemas are not supported. |
|
|
149
|
+
| `drop_table()` | method | **Must override.** Drop a table (used by IMPORT when creating fresh). |
|
|
150
|
+
| `populate_table()` | method | **Must override.** Bulk-load rows from a generator (used by IMPORT). |
|
|
151
|
+
| `row_inserter()` | method | Override for a faster bulk path that stores exactly what `executemany()` stores. |
|
|
152
|
+
| `column_kinds()` | method | Override if the driver's description of a table does not tell its column types apart (see `db/target_types.py`); IMPORT and COPY into an existing table convert each value by them. |
|
|
153
|
+
| `exec_cmd()` | method | Override if the DBMS can execute stored procedures or views as commands. |
|
|
153
154
|
|
|
154
155
|
Methods inherited from `Database` that you get for free include `execute()`, `cursor()`, `close()`, `commit()`, `rollback()`, `select_rowsource()`, `select_data()`, `schema_qualified_table_name()`, `quote_identifier()`, and `paramsubs()`.
|
|
155
156
|
|
|
@@ -264,6 +264,8 @@ ______________________________________________________________________
|
|
|
264
264
|
|
|
265
265
|
The base class provides shared implementations for `execute()`, `select_data()`, `select_rowsource()`, `select_rowdict()`, `commit()`, `rollback()`, and `populate_table()`.
|
|
266
266
|
|
|
267
|
+
`populate_table()` converts each value to the type scanned from the source (`models.DataTable`). For a table that was already there (`existing_table=True`, passed by IMPORT and COPY), `column_kinds()` reports the target columns' types and `db/target_types.py` picks each column's conversion instead: a text column gets the value as given, an integer column whole numbers or an error. `column_kinds()` reads the driver's description of `select * from <table> where 1=0` by default; PostgreSQL, MySQL and SQL Server read their catalogs, where the description cannot tell some types apart. `tests/db/test_existing_table_loads.py` compares what every value type stores in every column type with the record in `tests/data/existing_table_loads/`.
|
|
268
|
+
|
|
267
269
|
### DatabasePool
|
|
268
270
|
|
|
269
271
|
`DatabasePool` is a dict-like container mapping string aliases to open `Database` instances. It is the canonical `_state.dbs` object. Key operations:
|
|
@@ -272,7 +272,7 @@ select id,name,created_at from users where active = true order by name;
|
|
|
272
272
|
```yaml
|
|
273
273
|
repos:
|
|
274
274
|
- repo: https://github.com/geocoug/execsql
|
|
275
|
-
rev: v2.
|
|
275
|
+
rev: v2.34.0
|
|
276
276
|
hooks:
|
|
277
277
|
- id: execsql-format
|
|
278
278
|
- id: execsql-lint # optional: fail the commit on lint errors
|
|
@@ -4,6 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
When execsql connects to a SQL Server or MySQL/MariaDB database, it automatically configures the DBMS to expect ANSI-compatible SQL, to allow the use of more standards-compliant, and thus consistent, SQL. In particular, for MySQL/MariaDB, note that the double-quote character, rather than the backtick character, must be used to quote table, schema, and column names, and only the apostrophe can be used to quote character data.
|
|
6
6
|
|
|
7
|
+
### MySQL strict mode { #mysql_strict_mode }
|
|
8
|
+
|
|
9
|
+
On MySQL/MariaDB, execsql sets the session's `sql_mode` to `ANSI,STRICT_TRANS_TABLES`. A value that MySQL can't store as given is an error, for a SQL statement, [IMPORT](../reference/metacommands.md#import) or [COPY](../reference/metacommands.md#copy):
|
|
10
|
+
|
|
11
|
+
```sql
|
|
12
|
+
create table m (n integer, d date, s varchar(3));
|
|
13
|
+
insert into m values (3000000000, '2024-02-30', 'abcdef');
|
|
14
|
+
-- ERROR 1264 (22003): Out of range value for column 'n' at row 1
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Without strict mode, MySQL would have stored `2147483647`, `0000-00-00` and `abc` with only a warning. This is how the `mysql` client behaves with the server's default settings. An IMPORT of a delimited file that MySQL reads with `LOAD DATA` is held to the same rule: if MySQL warns that it changed a value, the IMPORT stops and nothing is loaded.
|
|
18
|
+
|
|
19
|
+
MySQL in strict mode does not read a trailing `Z` as a time zone: write `2024-01-05T10:00:00Z` as `2024-01-05 10:00:00+00:00`. A decimal with more places than its column has (`1.2345` into `DECIMAL(10,3)`) is still rounded, as MySQL does in strict mode.
|
|
20
|
+
|
|
21
|
+
A script that relies on MySQL's changed values can turn strict mode off for the rest of its session:
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
SET SESSION sql_mode = 'ANSI';
|
|
25
|
+
```
|
|
26
|
+
|
|
7
27
|
## Implicit Commits
|
|
8
28
|
|
|
9
29
|
By default, execsql immediately commits all SQL statements. The [AUTOCOMMIT](../reference/metacommands.md#autocommit) metacommand can be used to turn off automatic commits, and the [BATCH](../reference/metacommands.md#batch) metacommand can be used to delay commits until the end of a batch. [IMPORT](../reference/metacommands.md#import) and [COPY](../reference/metacommands.md#copy) are the only metacommands that change data, and they also automatically commit their changes when complete (unless [AUTOCOMMIT](../reference/metacommands.md#autocommit) has been turned off). If a new table is created with either of these metacommands (through the use of the NEW or REPLACEMENT keywords), the CREATE TABLE statement will not be committed separately from the data addition, except when using Firebird. Thus, if an error occurs during addition of the data, the new target table will not exist---except when using Firebird.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
You can use *execsql* to:
|
|
10
10
|
|
|
11
11
|
- Import data from text files and spreadsheets into a database.
|
|
12
|
-
- Export tables and views to ~30 data formats including CSV, OpenDocument spreadsheets, XLSX, HTML, JSON, YAML, Markdown, LaTeX, XML, Parquet, Feather, HDF5, SQLite, and DuckDB.
|
|
12
|
+
- Export tables and views to ~30 data formats including CSV, OpenDocument spreadsheets, XLSX, HTML, JSON, JSON Lines, YAML, Markdown, LaTeX, XML, Parquet, Feather, HDF5, SQLite, and DuckDB.
|
|
13
13
|
- Copy data between different databases, even databases using different DBMSs.
|
|
14
14
|
- Display tables or views on the terminal or in a GUI dialog window.
|
|
15
15
|
- Export data using template processors to produce non-tabular output with customized format and contents.
|
|
@@ -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.34.0
|
|
162
162
|
hooks:
|
|
163
163
|
- id: execsql-lint
|
|
164
164
|
args: [--ignore, V002] # optional: any execsql lint option
|
|
@@ -889,6 +889,8 @@ Copies the data from a data table or view in one database to a data table in a s
|
|
|
889
889
|
|
|
890
890
|
The second (destination) table must have column names that are identical to the names of the columns in the first (source) table. The second table may have additional columns; if it does, they will not be affected and their names don't matter. The data types in the columns to be copied must be compatible, though not necessarily identical. The order of the columns in the two tables does not have to be identical.
|
|
891
891
|
|
|
892
|
+
When neither "NEW" nor "REPLACEMENT" is used, each value is converted for the destination column it goes into, as for [IMPORT into an existing table](#import_existing_table_types): a text column gets the value as the source database returned it (an integer `1` is stored as `1`, a date as `2024-01-05`), an integer column gets whole numbers and stops the COPY with an error on a value with a fraction instead of rounding it, and a number column gets a column of 0s and 1s, or of true/false values, as 1 and 0.
|
|
893
|
+
|
|
892
894
|
If the "NEW" keyword is used, the destination table will be automatically created with column names and data types that are compatible with the first (source) table. The data types used for the columns in the newly created table will be determined by a scan of all of the data in the first table, but may not exactly match those in the first table. A text column stays text, whatever its values look like: `00123`, `1`/`0` and `2024-01-05` are copied as written, not as numbers, booleans or dates. The source is read once; while its rows are scanned they are held in a temporary file, so a large copy does not need memory for the whole table. If the destination table already exists when the "NEW" keyword is used, the copy stops before creating anything, with the error `Table <name> already exists`; a name without a schema is looked for only in the schema the table would be created in (on MySQL, the current database)
|
|
893
895
|
|
|
894
896
|
If the "REPLACEMENT" keyword is used, the destination table will also be created to be compatible with the source table, but any existing destination table of the same name will be dropped first. This replacement is not atomic: if the drop succeeds but a later create or load step fails, the old destination table has already been removed. If the drop fails, *execsql* stops the COPY command before creating or loading the replacement table. *execsql* uses a "drop table" statement to drop an existing destination table, and this statement may not succeed if there are dependencies on that table (see the discussion of [implicit drop table
|
|
@@ -1165,6 +1167,15 @@ JSON
|
|
|
1165
1167
|
: [Javascript Object Notation](http://www.json.org/). The data table is represented as an array of JSON objects, where each object represents a row of the table. Each row is represented as a set of key:value pairs, with column names used as the keys. No description text will be included in the output even if it is provided.
|
|
1166
1168
|
|
|
1167
1169
|
|
|
1170
|
+
JSONL
|
|
1171
|
+
|
|
1172
|
+
: [JSON Lines](https://jsonlines.org/): one JSON object per row, one row per line, with no surrounding array. Values are written as for JSON (dates and times as text, NULL as `null`); a line break inside a value is written as `\n`, so each row stays on one line. If the "APPEND" keyword is used, the rows are added to the end of the file, which stays valid JSON Lines (appending JSON instead leaves two arrays in one file). A query that returns no rows writes an empty file. The file can be read back with [IMPORT … FROM JSON](#import). No description text will be included in the output even if it is provided:
|
|
1173
|
+
|
|
1174
|
+
```
|
|
1175
|
+
-- !x! EXPORT QUERY <<select * from events where day = current_date;>> APPEND TO events.jsonl AS JSONL
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
|
|
1168
1179
|
JSON_TS or JSON_TABLESCHEMA
|
|
1169
1180
|
|
|
1170
1181
|
: [JSON Table Schema](https://frictionlessdata.io/specs/table-schema/). The data themselves are not exported. Ordinarily this form would be used in conjunction with an additional export of the data to a CSV file. The column type descriptions in the output are derived by scanning the entire table to evaluate the data type of each column. If a "NOTYPE" keyword is included before the "DESCRIPTION" keyword, this scanning is not done, and no type information is included in the output file.:
|
|
@@ -1766,7 +1777,7 @@ Evaluates whether or not the specified value is equal to zero. If the value is n
|
|
|
1766
1777
|
METACOMMAND_ERROR()
|
|
1767
1778
|
```
|
|
1768
1779
|
|
|
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.
|
|
1780
|
+
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
1781
|
|
|
1771
1782
|
|
|
1772
1783
|
### *NEWER_DATE*
|
|
@@ -1945,7 +1956,7 @@ The syntax for importing data from a [JSON](https://www.json.org/) file is:
|
|
|
1945
1956
|
IMPORT TO [NEW|REPLACEMENT] <table_name> FROM JSON <file_name>
|
|
1946
1957
|
```
|
|
1947
1958
|
|
|
1948
|
-
The JSON file must contain either a JSON array of objects (`[{…}, …]`) or [JSON Lines](https://jsonlines.org/) (JSONL, one object per line). Nested objects are flattened with dot-separated column names (e.g., an object `{"address": {"city": "Portland"}}` produces column `address.city`). Nested arrays within objects are stored as JSON strings. Records with different keys produce a superset of columns — missing keys become NULL. The parser auto-detects which form is present by peeking at the first non-whitespace character (`[` → array, `{` → JSON Lines); the file extension is not consulted.
|
|
1959
|
+
The JSON file must contain either a JSON array of objects (`[{…}, …]`) or [JSON Lines](https://jsonlines.org/) (JSONL, one object per line). Nested objects are flattened with dot-separated column names (e.g., an object `{"address": {"city": "Portland"}}` produces column `address.city`). Nested arrays within objects are stored as JSON strings. Records with different keys produce a superset of columns — missing keys become NULL. The parser auto-detects which form is present by peeking at the first non-whitespace character (`[` → array, `{` → JSON Lines); the file extension is not consulted. A file written by [EXPORT … AS JSONL](#export) is read as JSON Lines.
|
|
1949
1960
|
|
|
1950
1961
|
Column names in the input must be valid for the DBMS in use.
|
|
1951
1962
|
|
|
@@ -1968,7 +1979,15 @@ A data line with fewer values than the header has NULL for the values left off t
|
|
|
1968
1979
|
|
|
1969
1980
|
The rows are loaded in one transaction: if the load fails part-way (a constraint violation, a value of the wrong type, a row with too many columns), none of the file's rows are kept, on every DBMS. The exception is an ODBC DSN whose driver can only autocommit, which keeps the rows loaded before the failure; *execsql* warns once on such a connection.
|
|
1970
1981
|
|
|
1971
|
-
|
|
1982
|
+
<a id="import_existing_table_types"></a>
|
|
1983
|
+
If neither the NEW or REPLACEMENT keywords are used, the table must exist, must have column names identical to those in the input data, and columns must have data types that are compatible with (though not necessarily identical to) those in the input data. Each value is converted for the column it goes into:
|
|
1984
|
+
|
|
1985
|
+
- A text column (`CHAR`, `VARCHAR`, `TEXT`, …) gets the value as written in the file: `yes`, `00701`, `01/05/2024` and `1e3` are stored as those strings. A missing value is NULL.
|
|
1986
|
+
- An integer column (`INTEGER`, `BIGINT`, `DECIMAL(10,0)`, `NUMERIC(10)`, Oracle `NUMBER(10)`, …) gets whole numbers: `7`, `+7`, `7.0` and `7e0` load as 7. A value with a fraction, such as `1.5`, stops the IMPORT with an error that names the value and the column, and nothing is loaded; it is not rounded.
|
|
1987
|
+
- A number column gets a column of true/false values (`yes`/`no`, `t`/`f`, `1`/`0`; see [BOOLEAN_INT](#boolean_int) and [BOOLEAN_WORDS](#boolean_words)) as 1 and 0.
|
|
1988
|
+
- Any other column (a date, a timestamp, a Boolean, a decimal with places for a fraction) gets the value converted to the data type found by scanning that column of the input, as with NEW, and the database converts it to the column's type.
|
|
1989
|
+
|
|
1990
|
+
On PostgreSQL and MySQL a delimited text file goes to the server's own loader (`COPY`, `LOAD DATA`, or on PostgreSQL an `INSERT` of each value as text), and the server converts each value as written; the rules above apply to the other file formats, and to delimited files on the other DBMSs. On MySQL a value that the server can't store as given (out of range, an invalid date, text longer than the column) stops the IMPORT with an error, and nothing is loaded; see [MySQL strict mode](../guides/sql_syntax.md#mysql_strict_mode).
|
|
1972
1991
|
|
|
1973
1992
|
If a table is scanned to determine data types, any column that is completely empty (all null) will be created with the text data type. This provides the greatest flexibility for subsequent addition of data to the table. However, if that column ought to have a different data type, and a WHERE clause is applied to that column assuming a different data type, the DBMS may report an error because of incomparable data types.
|
|
1974
1993
|
|
|
@@ -2032,7 +2051,7 @@ When MS-Excel saves an OpenDocument spreadsheet, it may create an additional emp
|
|
|
2032
2051
|
Because the data addition to the target table is always committed except when [AUTOCOMMIT](#autocommit) is off, the IMPORT metacommand should be used with care within transactions that are managed with explicit SQL statements or with [BATCHes](#batch).
|
|
2033
2052
|
|
|
2034
2053
|
|
|
2035
|
-
In general, if an error occurs while importing data, none of the new data should be in the target table (the operation is not committed).
|
|
2054
|
+
In general, if an error occurs while importing data, none of the new data should be in the target table (the operation is not committed). On MySQL/MariaDB a value that the server would change to store it is an error too, unless the script has turned off [strict mode](../guides/sql_syntax.md#mysql_strict_mode). If the NEW or REPLACEMENT keywords are used, depending on the DBMS and where the error occurred, the target table may be created even if the data are not loaded.
|
|
2036
2055
|
|
|
2037
2056
|
The name, size, and date of the IMPORTed file are written to the [execsql.log](../guides/logging.md#logging) file.
|
|
2038
2057
|
|
|
@@ -2109,7 +2128,7 @@ The [BREAK](#break) metacommand can be used to exit from a loop at any time.
|
|
|
2109
2128
|
METACOMMAND_ERROR_HALT ON|OFF
|
|
2110
2129
|
```
|
|
2111
2130
|
|
|
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.
|
|
2131
|
+
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
2132
|
|
|
2114
2133
|
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
2134
|
|
|
@@ -2356,6 +2375,8 @@ Set after every `PG_UPSERT` execution:
|
|
|
2356
2375
|
| `$PG_UPSERT_ROWS_UPDATED` | integer | Total rows updated across all tables |
|
|
2357
2376
|
| `$PG_UPSERT_ROWS_INSERTED` | integer | Total rows inserted across all tables |
|
|
2358
2377
|
| `$PG_UPSERT_COMMITTED` | TRUE/FALSE | Were changes committed? |
|
|
2378
|
+
| `$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. |
|
|
2379
|
+
| `$PG_UPSERT_ERROR` | string | The database error message when `$PG_UPSERT_STATUS` is `db_error`; otherwise empty |
|
|
2359
2380
|
| `$PG_UPSERT_STAGING_SCHEMA` | string | Staging schema name used |
|
|
2360
2381
|
| `$PG_UPSERT_BASE_SCHEMA` | string | Base schema name used |
|
|
2361
2382
|
| `$PG_UPSERT_TABLES` | string | Comma-separated list of table names processed |
|
|
@@ -2380,7 +2401,7 @@ Set after every `PG_UPSERT` execution:
|
|
|
2380
2401
|
|
|
2381
2402
|
### QA failure behavior
|
|
2382
2403
|
|
|
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:
|
|
2404
|
+
`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
2405
|
|
|
2385
2406
|
```sql
|
|
2386
2407
|
-- Soft failure: branch on the result, keep running.
|
|
@@ -2398,7 +2419,7 @@ Set after every `PG_UPSERT` execution:
|
|
|
2398
2419
|
|
|
2399
2420
|
### Database errors
|
|
2400
2421
|
|
|
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`.
|
|
2422
|
+
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
2423
|
|
|
2403
2424
|
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
2425
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "execsql-syntax",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.34.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.34.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.34.0",
|
|
6
6
|
"publisher": "geocoug",
|
|
7
7
|
"license": "GPL-3.0-or-later",
|
|
8
8
|
"icon": "icon.png",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
"export-formats": {
|
|
120
120
|
"comment": "Export/import format names",
|
|
121
|
-
"match": "(?i)\\b(json_tableschema|cgi-html|markdown|feather|json_ts|txt-and|unitsep|duckdb|sqlite|values|latex|plain|hdf5|html|json|tabq|tsvq|xlsx|yaml|b64|csv|ods|raw|tab|tsv|txt|xml|md|us)\\b",
|
|
121
|
+
"match": "(?i)\\b(json_tableschema|cgi-html|markdown|feather|json_ts|txt-and|unitsep|duckdb|sqlite|values|jsonl|latex|plain|hdf5|html|json|tabq|tsvq|xlsx|yaml|b64|csv|ods|raw|tab|tsv|txt|xml|md|us)\\b",
|
|
122
122
|
"name": "support.constant.execsql"
|
|
123
123
|
},
|
|
124
124
|
"secondary-operators": {
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.34.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.34.0"
|
|
192
192
|
commit = true
|
|
193
193
|
tag = true
|
|
194
194
|
tag_name = "v{new_version}"
|