execsql2 2.21.0__tar.gz → 2.21.2__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.21.0 → execsql2-2.21.2}/.github/workflows/ci-cd.yml +13 -13
- {execsql2-2.21.0 → execsql2-2.21.2}/CHANGELOG.md +16 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/PKG-INFO +2 -2
- {execsql2-2.21.0 → execsql2-2.21.2}/README.md +1 -1
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/about/divergence.md +1 -1
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/formatter.md +1 -1
- {execsql2-2.21.0 → execsql2-2.21.2}/pyproject.toml +2 -2
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/postgres.py +8 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/feather.py +1 -1
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/parquet.py +1 -1
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_postgres.py +46 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_parquet.py +8 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/test_postgres.py +51 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/uv.lock +1 -1
- {execsql2-2.21.0 → execsql2-2.21.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.github/dependabot.yml +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.gitignore +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.pre-commit-config.yaml +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.python-version +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/.readthedocs.yaml +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/CONTRIBUTING.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/LICENSE.txt +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/NOTICE +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/SECURITY.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/about/contributors.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/about/copyright.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/api/cli.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/api/db.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/api/exporters.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/api/importers.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/api/index.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/api/metacommands.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/dev/architecture.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/dev/releasing.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/getting-started/installation.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/debugging.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/documentation.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/encoding.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/examples.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/logging.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/usage.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/actions.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/actions2.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/checkboxes.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/connect.b64 +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/connect.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/create_conf.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/entry_form.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/execsql_console.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/fatals.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/logo_small.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/unmatched.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/index.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/reference/configuration.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/reference/metacommands.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/reference/security.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/plugin-template/README.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/justfile +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/__main__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/api.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/cli/help.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/cli/run.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/config.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/access.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/base.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/factory.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exceptions.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/format.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/gui/base.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/gui/console.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/base.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/json.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/models.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/parser.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/plugins.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/py.typed +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/ast.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/control.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/engine.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/executor.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/parser.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/script/variables.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/state.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/types.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/README.md +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/config_settings.sqlite +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/make_config_db.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/md_compare.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/md_glossary.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/md_upsert.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/pg_compare.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/pg_glossary.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/pg_upsert.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/script_template.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/ss_compare.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/ss_glossary.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/templates/ss_upsert.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/test_cli.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/test_lint.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/test_ping.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/cli/test_profile.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/conftest.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_base.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_db_adapters_mocked_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_dsn.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_factory.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_postgres_inprocess.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_base.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_db.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_json.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_backends.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/conftest.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_conditions_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_prompt.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_log_redaction.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_api.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_api_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_ast.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_ast_parser.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_config.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_config_data.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_config_extended.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_debug_repl.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_engine.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_error_messages.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_exceptions.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_executor.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_format.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_mail.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_models.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_package.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_parser.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_parser_params.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_plugins.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_registry.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_script.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_state.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/test_types.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/__init__.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_auth.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_errors.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_regex.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_strings.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_timer.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.21.0 → execsql2-2.21.2}/zensical.toml +0 -0
|
@@ -20,8 +20,8 @@ jobs:
|
|
|
20
20
|
permissions:
|
|
21
21
|
contents: read
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
24
|
-
- uses: actions/setup-python@
|
|
23
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
24
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
25
25
|
with:
|
|
26
26
|
python-version: "3.13"
|
|
27
27
|
- run: python -m pip install ruff
|
|
@@ -48,13 +48,13 @@ jobs:
|
|
|
48
48
|
contents: read
|
|
49
49
|
steps:
|
|
50
50
|
- name: Check out repository code
|
|
51
|
-
uses: actions/checkout@
|
|
51
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
52
52
|
- name: Setup Python ${{ matrix.python-version }}
|
|
53
|
-
uses: actions/setup-python@
|
|
53
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
54
54
|
with:
|
|
55
55
|
python-version: ${{ matrix.python-version }}
|
|
56
56
|
- name: Cache pip dependencies
|
|
57
|
-
uses: actions/cache@
|
|
57
|
+
uses: actions/cache@2c8a9bd7457de244a408f35966fab2fb45fda9c8 # v6.0.0
|
|
58
58
|
with:
|
|
59
59
|
path: ~/.cache/pip
|
|
60
60
|
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{
|
|
@@ -139,9 +139,9 @@ jobs:
|
|
|
139
139
|
--health-interval 10s --health-timeout 5s --health-retries 20
|
|
140
140
|
steps:
|
|
141
141
|
- name: Check out repository code
|
|
142
|
-
uses: actions/checkout@
|
|
142
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
143
143
|
- name: Setup Python ${{ matrix.python-version }}
|
|
144
|
-
uses: actions/setup-python@
|
|
144
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
145
145
|
with:
|
|
146
146
|
python-version: ${{ matrix.python-version }}
|
|
147
147
|
- name: Install tkinter (Ubuntu)
|
|
@@ -187,8 +187,8 @@ jobs:
|
|
|
187
187
|
permissions:
|
|
188
188
|
contents: read
|
|
189
189
|
steps:
|
|
190
|
-
- uses: actions/checkout@
|
|
191
|
-
- uses: actions/setup-python@
|
|
190
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
191
|
+
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
192
192
|
with:
|
|
193
193
|
python-version: ${{ matrix.python-version }}
|
|
194
194
|
- name: Install Microsoft Access Database Engine 2016 (x64)
|
|
@@ -238,9 +238,9 @@ jobs:
|
|
|
238
238
|
permissions:
|
|
239
239
|
contents: read
|
|
240
240
|
steps:
|
|
241
|
-
- uses: actions/checkout@
|
|
241
|
+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
242
242
|
- name: Set up Python
|
|
243
|
-
uses: actions/setup-python@
|
|
243
|
+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
|
244
244
|
with:
|
|
245
245
|
python-version: "3.13"
|
|
246
246
|
cache: "pip"
|
|
@@ -288,14 +288,14 @@ jobs:
|
|
|
288
288
|
contents: write
|
|
289
289
|
steps:
|
|
290
290
|
- name: Checkout
|
|
291
|
-
uses: actions/checkout@
|
|
291
|
+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
292
292
|
- name: Download dist
|
|
293
293
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
294
294
|
with:
|
|
295
295
|
name: ${{ github.event.repository.name }}
|
|
296
296
|
path: dist/
|
|
297
297
|
- name: Create release
|
|
298
|
-
uses: softprops/action-gh-release@
|
|
298
|
+
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
|
|
299
299
|
with:
|
|
300
300
|
draft: false
|
|
301
301
|
prerelease: false
|
|
@@ -13,6 +13,22 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.21.2] - 2026-06-30
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fixed `EXPORT … AS PARQUET` and `EXPORT … AS FEATHER` `ComputeError` when a result column has more than 100 leading `NULL` values followed by non-null numeric values.
|
|
21
|
+
|
|
22
|
+
______________________________________________________________________
|
|
23
|
+
|
|
24
|
+
## [2.21.1] - 2026-06-24
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- PostgreSQL: `EXPORT` and other repeated queries against objects a script drops/recreates or alters no longer fail with `FeatureNotSupported: cached plan must not change result type`. psycopg3's automatic server-side prepared statements are now disabled (`prepare_threshold=None`), restoring the psycopg2 behavior.
|
|
29
|
+
|
|
30
|
+
______________________________________________________________________
|
|
31
|
+
|
|
16
32
|
## [2.21.0] - 2026-06-24
|
|
17
33
|
|
|
18
34
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.21.
|
|
3
|
+
Version: 2.21.2
|
|
4
4
|
Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
|
|
5
5
|
Project-URL: Homepage, https://execsql2.readthedocs.io
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -403,7 +403,7 @@ execsql-format --no-sql --in-place scripts/
|
|
|
403
403
|
```yaml
|
|
404
404
|
repos:
|
|
405
405
|
- repo: https://github.com/geocoug/execsql
|
|
406
|
-
rev: v2.21.
|
|
406
|
+
rev: v2.21.2
|
|
407
407
|
hooks:
|
|
408
408
|
- id: execsql-format
|
|
409
409
|
```
|
|
@@ -210,7 +210,7 @@ All 33 mutable runtime globals in `state.py` have been consolidated into a `Runt
|
|
|
210
210
|
|
|
211
211
|
### Database Adapters
|
|
212
212
|
|
|
213
|
-
- **PostgreSQL driver is psycopg3** — the PostgreSQL adapter uses `psycopg` (psycopg3, installed via the `psycopg[binary]` extra) instead of upstream's `psycopg2`. The connection now passes the libpq `dbname` keyword (psycopg3 rejects psycopg2's `database`), `VACUUM` toggles `conn.autocommit` instead of the removed `set_session()`, server-side `COPY` import uses psycopg3's `cursor.copy()` context manager instead of `copy_expert()`, and binary `IMPORT` sends raw `bytes` instead of `psycopg2.Binary()`. This also makes `db.conn` directly usable by the `PG_UPSERT` metacommand's pg-upsert ≥1.23.0 dependency, which itself requires a psycopg3 connection.
|
|
213
|
+
- **PostgreSQL driver is psycopg3** — the PostgreSQL adapter uses `psycopg` (psycopg3, installed via the `psycopg[binary]` extra) instead of upstream's `psycopg2`. The connection now passes the libpq `dbname` keyword (psycopg3 rejects psycopg2's `database`), `VACUUM` toggles `conn.autocommit` instead of the removed `set_session()`, server-side `COPY` import uses psycopg3's `cursor.copy()` context manager instead of `copy_expert()`, and binary `IMPORT` sends raw `bytes` instead of `psycopg2.Binary()`. This also makes `db.conn` directly usable by the `PG_UPSERT` metacommand's pg-upsert ≥1.23.0 dependency, which itself requires a psycopg3 connection. The connection sets `prepare_threshold=None` to disable psycopg3's automatic server-side prepared statements, which psycopg2 never used; without this, re-running a query (e.g. via `EXPORT`) against an object the script drops/recreates or alters raises `FeatureNotSupported: cached plan must not change result type`.
|
|
214
214
|
- **`Database` is an ABC** — `open_db()` and `exec_cmd()` are abstract methods. Subclasses that omit them raise `TypeError` at instantiation instead of at call time.
|
|
215
215
|
- **Connection timeouts** — PostgreSQL and SQLite adapters accept a connection timeout parameter (default 30 seconds).
|
|
216
216
|
- **DuckDB temporal types** — `TIMESTAMPTZ`, `TIMESTAMP`, `DATE`, `TIME` now map to native DuckDB types instead of `TEXT`.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.21.
|
|
7
|
+
version = "2.21.2"
|
|
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" }
|
|
@@ -178,7 +178,7 @@ skip-magic-trailing-comma = false
|
|
|
178
178
|
line-ending = "auto"
|
|
179
179
|
|
|
180
180
|
[tool.bumpversion]
|
|
181
|
-
current_version = "2.21.
|
|
181
|
+
current_version = "2.21.2"
|
|
182
182
|
commit = true
|
|
183
183
|
tag = true
|
|
184
184
|
tag_name = "v{new_version}"
|
|
@@ -78,6 +78,12 @@ class PostgresDatabase(Database):
|
|
|
78
78
|
|
|
79
79
|
def db_conn(db: PostgresDatabase, db_name: str):
|
|
80
80
|
try:
|
|
81
|
+
# prepare_threshold=None disables psycopg3's automatic server-side
|
|
82
|
+
# prepared statements. execsql re-runs the same query text (e.g. via
|
|
83
|
+
# EXPORT) against objects that scripts may drop/recreate or alter
|
|
84
|
+
# between runs; a cached plan whose result type then changes triggers
|
|
85
|
+
# PostgreSQL's "cached plan must not change result type" error. psycopg2
|
|
86
|
+
# never auto-prepared, so this preserves backwards-compatible behavior.
|
|
81
87
|
if db.user and db.password:
|
|
82
88
|
return psycopg.connect(
|
|
83
89
|
host=str(db.server_name),
|
|
@@ -86,6 +92,7 @@ class PostgresDatabase(Database):
|
|
|
86
92
|
user=db.user,
|
|
87
93
|
password=db.password,
|
|
88
94
|
connect_timeout=db.connect_timeout,
|
|
95
|
+
prepare_threshold=None,
|
|
89
96
|
)
|
|
90
97
|
else:
|
|
91
98
|
return psycopg.connect(
|
|
@@ -93,6 +100,7 @@ class PostgresDatabase(Database):
|
|
|
93
100
|
dbname=db_name,
|
|
94
101
|
port=db.port,
|
|
95
102
|
connect_timeout=db.connect_timeout,
|
|
103
|
+
prepare_threshold=None,
|
|
96
104
|
)
|
|
97
105
|
except Exception as e:
|
|
98
106
|
msg = (
|
|
@@ -33,7 +33,7 @@ def write_query_to_feather(outfile: str, headers: list[str], rows: Any) -> None:
|
|
|
33
33
|
) from e
|
|
34
34
|
rows_list = list(rows)
|
|
35
35
|
if rows_list:
|
|
36
|
-
df = pl.DataFrame(rows_list, schema=headers, orient="row")
|
|
36
|
+
df = pl.DataFrame(rows_list, schema=headers, orient="row", infer_schema_length=None)
|
|
37
37
|
else:
|
|
38
38
|
df = pl.DataFrame({h: [] for h in headers})
|
|
39
39
|
filewriter_close(outfile)
|
|
@@ -28,7 +28,7 @@ def write_query_to_parquet(outfile: str, headers: list[str], rows: Any) -> None:
|
|
|
28
28
|
) from e
|
|
29
29
|
rows_list = list(rows)
|
|
30
30
|
if rows_list:
|
|
31
|
-
df = pl.DataFrame(rows_list, schema=headers, orient="row")
|
|
31
|
+
df = pl.DataFrame(rows_list, schema=headers, orient="row", infer_schema_length=None)
|
|
32
32
|
else:
|
|
33
33
|
df = pl.DataFrame({h: [] for h in headers})
|
|
34
34
|
filewriter_close(outfile)
|
|
@@ -93,3 +93,49 @@ class TestPostgresConnectTimeout:
|
|
|
93
93
|
# Credential-less path still uses ``dbname``.
|
|
94
94
|
assert call_kwargs["dbname"] == "testdb"
|
|
95
95
|
assert "database" not in call_kwargs
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# Regression: psycopg3 auto-prepared statements must stay disabled
|
|
100
|
+
#
|
|
101
|
+
# psycopg3 promotes a query to a server-side prepared statement after the same
|
|
102
|
+
# query text runs ``prepare_threshold`` times (default 5). Scripts that re-run
|
|
103
|
+
# a query (e.g. via EXPORT) against an object they drop/recreate or alter then
|
|
104
|
+
# hit "cached plan must not change result type". psycopg2 never auto-prepared,
|
|
105
|
+
# so the adapter passes ``prepare_threshold=None`` to restore that behavior.
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TestPostgresPrepareThresholdDisabled:
|
|
110
|
+
@patch.dict("sys.modules", {"psycopg": _make_mock_psycopg()})
|
|
111
|
+
def test_prepare_threshold_disabled_with_credentials(self):
|
|
112
|
+
import sys
|
|
113
|
+
|
|
114
|
+
mock_psycopg = sys.modules["psycopg"]
|
|
115
|
+
from execsql.db.postgres import PostgresDatabase
|
|
116
|
+
|
|
117
|
+
PostgresDatabase(
|
|
118
|
+
server_name="localhost",
|
|
119
|
+
db_name="testdb",
|
|
120
|
+
user_name="user",
|
|
121
|
+
password="pass",
|
|
122
|
+
)
|
|
123
|
+
call_kwargs = mock_psycopg.connect.call_args[1]
|
|
124
|
+
assert "prepare_threshold" in call_kwargs
|
|
125
|
+
assert call_kwargs["prepare_threshold"] is None
|
|
126
|
+
|
|
127
|
+
@patch.dict("sys.modules", {"psycopg": _make_mock_psycopg()})
|
|
128
|
+
def test_prepare_threshold_disabled_without_credentials(self):
|
|
129
|
+
import sys
|
|
130
|
+
|
|
131
|
+
mock_psycopg = sys.modules["psycopg"]
|
|
132
|
+
from execsql.db.postgres import PostgresDatabase
|
|
133
|
+
|
|
134
|
+
PostgresDatabase(
|
|
135
|
+
server_name="localhost",
|
|
136
|
+
db_name="testdb",
|
|
137
|
+
user_name=None,
|
|
138
|
+
)
|
|
139
|
+
call_kwargs = mock_psycopg.connect.call_args[1]
|
|
140
|
+
assert "prepare_threshold" in call_kwargs
|
|
141
|
+
assert call_kwargs["prepare_threshold"] is None
|
|
@@ -60,3 +60,11 @@ class TestWriteQueryToParquet:
|
|
|
60
60
|
assert len(result["id"]) == 100
|
|
61
61
|
assert result["id"][0] == 0
|
|
62
62
|
assert result["label"][99] == "item_99"
|
|
63
|
+
|
|
64
|
+
def test_leading_nulls_then_float(self, noop_filewriter_close, tmp_path):
|
|
65
|
+
out = str(tmp_path / "out.parquet")
|
|
66
|
+
rows = [(None,)] * 100 + [(12.5,), (5.2,)]
|
|
67
|
+
write_query_to_parquet(out, ["val"], iter(rows))
|
|
68
|
+
result = pl.read_parquet(out).to_dict(as_series=False)
|
|
69
|
+
assert result["val"][100] == pytest.approx(12.5)
|
|
70
|
+
assert result["val"][101] == pytest.approx(5.2)
|
|
@@ -412,3 +412,54 @@ class TestExportJSON:
|
|
|
412
412
|
assert data[1]["label"] == "beta"
|
|
413
413
|
|
|
414
414
|
_exec_pg("DROP TABLE IF EXISTS items")
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
# ---------------------------------------------------------------------------
|
|
418
|
+
# Regression: psycopg3 cached-plan / auto-prepared statements
|
|
419
|
+
#
|
|
420
|
+
# psycopg3 promotes a query to a server-side prepared statement after the same
|
|
421
|
+
# query text runs prepare_threshold times (default 5). Re-EXPORTing a view that
|
|
422
|
+
# the script drops/recreates with a different result type used to raise
|
|
423
|
+
# "FeatureNotSupported: cached plan must not change result type". The adapter
|
|
424
|
+
# now connects with prepare_threshold=None; this exercises the full pipeline to
|
|
425
|
+
# prove the fix end-to-end. See CHANGELOG 2.21.1.
|
|
426
|
+
# ---------------------------------------------------------------------------
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
class TestCachedPlanRegression:
|
|
430
|
+
def test_repeated_export_after_view_result_type_change(self, tmp_path):
|
|
431
|
+
"""EXPORT the same view past the prepare threshold, then recreate it with
|
|
432
|
+
a different column type and EXPORT again — must not error."""
|
|
433
|
+
_exec_pg("DROP VIEW IF EXISTS val_summ")
|
|
434
|
+
_exec_pg("DROP TABLE IF EXISTS val_src")
|
|
435
|
+
_write_conf(tmp_path)
|
|
436
|
+
out = tmp_path / "summ.txt"
|
|
437
|
+
|
|
438
|
+
# Six exports of identical query text cross psycopg3's default
|
|
439
|
+
# prepare_threshold (5); the view is then recreated returning a TEXT
|
|
440
|
+
# column instead of an INTEGER one and exported again.
|
|
441
|
+
first_exports = "\n".join(f"-- !x! EXPORT val_summ TO {out} AS TXT" for _ in range(6))
|
|
442
|
+
second_exports = "\n".join(f"-- !x! EXPORT val_summ TO {out} AS TXT" for _ in range(6))
|
|
443
|
+
script = write_script(
|
|
444
|
+
tmp_path,
|
|
445
|
+
f"""\
|
|
446
|
+
CREATE TABLE val_src (id INTEGER, name TEXT);
|
|
447
|
+
INSERT INTO val_src VALUES (1, 'alpha');
|
|
448
|
+
|
|
449
|
+
CREATE VIEW val_summ AS SELECT id AS c FROM val_src;
|
|
450
|
+
{first_exports}
|
|
451
|
+
|
|
452
|
+
DROP VIEW val_summ;
|
|
453
|
+
CREATE VIEW val_summ AS SELECT name AS c FROM val_src;
|
|
454
|
+
{second_exports}
|
|
455
|
+
""",
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
result = _run_execsql_pg(tmp_path, script)
|
|
459
|
+
assert result.returncode == 0, f"stderr: {result.stderr}"
|
|
460
|
+
assert "cached plan must not change result type" not in result.stderr
|
|
461
|
+
# The final export reflects the recreated (TEXT) view.
|
|
462
|
+
assert "alpha" in out.read_text()
|
|
463
|
+
|
|
464
|
+
_exec_pg("DROP VIEW IF EXISTS val_summ")
|
|
465
|
+
_exec_pg("DROP TABLE IF EXISTS val_src")
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{execsql2-2.21.0 → execsql2-2.21.2}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|