execsql2 2.22.1__tar.gz → 2.22.3__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.22.1 → execsql2-2.22.3}/.gitignore +3 -1
- {execsql2-2.22.1 → execsql2-2.22.3}/CHANGELOG.md +16 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/PKG-INFO +2 -2
- {execsql2-2.22.1 → execsql2-2.22.3}/README.md +1 -1
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/about/divergence.md +1 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/formatter.md +1 -1
- {execsql2-2.22.1 → execsql2-2.22.3}/pyproject.toml +2 -2
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/format.py +12 -1
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/gui.py +15 -6
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_backends.py +15 -5
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_prompt.py +27 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_format.py +96 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/uv.lock +1 -1
- {execsql2-2.22.1 → execsql2-2.22.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.github/dependabot.yml +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.pre-commit-config.yaml +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.python-version +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/.readthedocs.yaml +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/CONTRIBUTING.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/LICENSE.txt +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/NOTICE +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/SECURITY.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/about/contributors.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/about/copyright.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/api/cli.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/api/db.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/api/exporters.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/api/importers.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/api/index.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/api/metacommands.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/dev/architecture.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/dev/releasing.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/getting-started/installation.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/debugging.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/documentation.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/encoding.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/examples.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/logging.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/usage.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/actions.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/actions2.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/checkboxes.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/connect.b64 +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/connect.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/create_conf.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/entry_form.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/execsql_console.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/fatals.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/logo_small.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/unmatched.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/index.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/reference/configuration.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/reference/metacommands.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/reference/security.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/plugin-template/README.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/justfile +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/scripts/benchmarks/README.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/scripts/benchmarks/results/.gitignore +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/scripts/benchmarks/run_benchmarks.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/__main__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/api.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/cli/help.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/cli/run.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/config.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/access.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/base.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/factory.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exceptions.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/gui/base.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/gui/console.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/base.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/json.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/models.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/parser.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/plugins.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/py.typed +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/ast.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/control.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/engine.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/executor.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/parser.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/script/variables.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/state.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/state.pyi +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/types.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/README.md +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/config_settings.sqlite +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/make_config_db.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/md_compare.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/md_glossary.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/md_upsert.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/pg_compare.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/pg_glossary.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/pg_upsert.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/script_template.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/ss_compare.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/ss_glossary.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/templates/ss_upsert.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/test_cli.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/test_lint.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/test_ping.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/cli/test_profile.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/conftest.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_base.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_db_adapters_mocked_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_dsn.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_factory.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_postgres.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_postgres_inprocess.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_base.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_db.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_json.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/conftest.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_conditions_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_log_redaction.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_api.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_api_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_ast.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_ast_parser.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_config.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_config_data.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_config_extended.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_debug_repl.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_engine.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_error_messages.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_exceptions.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_executor.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_mail.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_models.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_package.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_parser.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_parser_params.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_plugins.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_registry.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_script.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_state.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/test_types.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/__init__.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_auth.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_errors.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_regex.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_strings.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_timer.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.22.1 → execsql2-2.22.3}/zensical.toml +0 -0
|
@@ -13,6 +13,22 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.22.3] - 2026-07-23
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- `execsql-format` no longer sends a completed dollar-quoted PL/pgSQL body to sqlglot. A `CREATE FUNCTION … $$ … $$;` or `DO $$ … $$;` whose quote closed before the block was flushed was reformatted (signature exploded, `LANGUAGE … AS` reordered, body types uppercased) and drifted — gaining one space of indentation on every run, so `--check` never converged. Such statements are now kept verbatim.
|
|
21
|
+
|
|
22
|
+
______________________________________________________________________
|
|
23
|
+
|
|
24
|
+
## [2.22.2] - 2026-07-13
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- `ASK … SUB` now stores the actual answer ("Yes" or "No"); it previously stored "No" for every response, so `IF(IS_TRUE(…))` and `IF(…)` branches keyed on the answer never took the yes path. Pressing `Esc` at the prompt again quits the script.
|
|
29
|
+
|
|
30
|
+
______________________________________________________________________
|
|
31
|
+
|
|
16
32
|
## [2.22.1] - 2026-07-04
|
|
17
33
|
|
|
18
34
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.22.
|
|
3
|
+
Version: 2.22.3
|
|
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
|
|
@@ -384,7 +384,7 @@ execsql-format --no-sql --in-place scripts/
|
|
|
384
384
|
```yaml
|
|
385
385
|
repos:
|
|
386
386
|
- repo: https://github.com/geocoug/execsql
|
|
387
|
-
rev: v2.22.
|
|
387
|
+
rev: v2.22.3
|
|
388
388
|
hooks:
|
|
389
389
|
- id: execsql-format
|
|
390
390
|
```
|
|
@@ -111,6 +111,7 @@ Formatter correctness notes added in the 2.19.x line:
|
|
|
111
111
|
- **2.19.1** — All four substitution-variable forms (`!!var!!`, `!'!var!'!`, `!"!var!"!`, and the deferred form `!{var}!`) are hidden from the sqlglot pass. Previously, `!'!var!'!` and `!"!var!"!` were parsed as `NOT` operators and rewritten — e.g. `!'!myvar!'!` → `NOT NOT '!myvar!'`. Scripts using quoted substitution variables are now safe to run through `execsql-format`.
|
|
112
112
|
- **2.19.2** — The published pre-commit hook declares `sqlglot` as an isolated-env dependency. The 2.19.0 move of `sqlglot` to the `[formatter]` extra meant the hook env (which installs the bare package) crashed with `ModuleNotFoundError: No module named 'sqlglot'` on first run. After upgrading, downstream users should run `pre-commit clean && pre-commit install --install-hooks` once to rebuild the env.
|
|
113
113
|
- **Tagged dollar quotes** — Tagged PL/pgSQL function bodies (`$body$ … $body$`, `$func$ … $func$`, any `$tag$`) are now recognized and skipped over by the formatter. Previously the tracker only saw untagged `$$`, so tagged function bodies were sent to sqlglot which mangled `IF / END IF / LOOP / RETURN`.
|
|
114
|
+
- **Closed dollar quotes** — Any statement that *contains* a dollar-quoted region is now opaque to sqlglot, tracked with a sticky flag rather than the parser's live `in_dollar_quote` state. A complete `CREATE FUNCTION … $$ … $$;` or `DO $$ … $$;` had its quote already closed by the time the block was flushed, so the guard passed and the whole body was reformatted — and, because dropped comments were re-indented onto the nearest output line each pass, the body drifted (gaining a space of indentation) on every run, so `--check` never converged. A side effect is that a single-line `SELECT $$literal$$;` also skips the sqlglot pass (no keyword uppercasing), which is the safe, conservative behavior.
|
|
114
115
|
|
|
115
116
|
### GUI
|
|
116
117
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.22.
|
|
7
|
+
version = "2.22.3"
|
|
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.22.
|
|
181
|
+
current_version = "2.22.3"
|
|
182
182
|
commit = true
|
|
183
183
|
tag = true
|
|
184
184
|
tag_name = "v{new_version}"
|
|
@@ -642,6 +642,12 @@ def format_file(source: str, indent: int = 4, use_sql: bool = True, leading_comm
|
|
|
642
642
|
# "body" for `$body$`, etc.). Nested markers with a different tag
|
|
643
643
|
# are ignored — only a matching close marker re-opens us.
|
|
644
644
|
current_dq_tag: str | None = None
|
|
645
|
+
# Sticky flag: True once the current accumulator has seen ANY dollar-quote
|
|
646
|
+
# marker. `in_dollar_quote` alone is False by the time a complete
|
|
647
|
+
# `CREATE FUNCTION ... $$ ... $$;` is flushed (the closing `$$` reset it),
|
|
648
|
+
# which would wrongly hand the PL/pgSQL body to sqlglot. This flag keeps the
|
|
649
|
+
# whole containing statement opaque to sqlglot. Reset in flush_sql().
|
|
650
|
+
sql_acc_contains_dollar_quote = False
|
|
645
651
|
in_block_comment = False
|
|
646
652
|
# Track whether we are inside an open SQL statement (last SQL line
|
|
647
653
|
# did not end with ';'). Blank lines mid-statement should NOT flush
|
|
@@ -655,15 +661,19 @@ def format_file(source: str, indent: int = 4, use_sql: bool = True, leading_comm
|
|
|
655
661
|
|
|
656
662
|
def flush_sql() -> None:
|
|
657
663
|
nonlocal in_dollar_quote, current_dq_tag, in_sql_statement
|
|
664
|
+
nonlocal sql_acc_contains_dollar_quote
|
|
658
665
|
if sql_acc:
|
|
659
666
|
# If any line in the accumulated block is inside a $$-delimited
|
|
660
667
|
# region, skip sqlglot formatting entirely. PL/pgSQL function
|
|
661
668
|
# bodies contain IF/END IF, LOOP, RETURN, etc. that sqlglot does
|
|
662
669
|
# not understand and will corrupt (e.g., rewriting to COMMIT).
|
|
663
|
-
|
|
670
|
+
# `sql_acc_contains_dollar_quote` catches the case where the region
|
|
671
|
+
# already closed (`... $$;`) so `in_dollar_quote` is False at flush.
|
|
672
|
+
safe_for_sqlglot = use_sql and not in_dollar_quote and not sql_acc_contains_dollar_quote
|
|
664
673
|
output.extend(format_sql_block(sql_acc, depth, indent, safe_for_sqlglot, leading_comma=leading_comma))
|
|
665
674
|
sql_acc.clear()
|
|
666
675
|
in_sql_statement = False
|
|
676
|
+
sql_acc_contains_dollar_quote = False
|
|
667
677
|
|
|
668
678
|
for raw_line in source.expandtabs(4).splitlines():
|
|
669
679
|
stripped_line = raw_line.strip()
|
|
@@ -727,6 +737,7 @@ def format_file(source: str, indent: int = 4, use_sql: bool = True, leading_comm
|
|
|
727
737
|
# PL/pgSQL. Walk every dollar-quote marker on the line; toggle
|
|
728
738
|
# state only when we hit the matching open or close.
|
|
729
739
|
for m in _DOLLAR_QUOTE_RE.finditer(raw_line):
|
|
740
|
+
sql_acc_contains_dollar_quote = True
|
|
730
741
|
tag = m.group(1) or ""
|
|
731
742
|
if not in_dollar_quote:
|
|
732
743
|
in_dollar_quote = True
|
|
@@ -540,18 +540,27 @@ def gui_credentials(
|
|
|
540
540
|
# ---------------------------------------------------------------------------
|
|
541
541
|
|
|
542
542
|
|
|
543
|
-
def get_yn(prompt: str) ->
|
|
544
|
-
"""Prompt for a yes/no answer on the terminal.
|
|
543
|
+
def get_yn(prompt: str) -> str:
|
|
544
|
+
"""Prompt for a yes/no answer on the terminal.
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
``"y"`` or ``"n"``, or ``chr(27)`` (ESC) if the user aborts via EOF
|
|
548
|
+
(Ctrl-D / piped stdin closed). Matches the upstream v1.130.1
|
|
549
|
+
``get_yn`` character contract expected by callers such as ``x_ask``.
|
|
550
|
+
"""
|
|
545
551
|
while True:
|
|
546
|
-
|
|
552
|
+
try:
|
|
553
|
+
answer = input(f"{prompt} [y/n]: ").strip().lower()
|
|
554
|
+
except EOFError:
|
|
555
|
+
return chr(27)
|
|
547
556
|
if answer in ("y", "yes"):
|
|
548
|
-
return
|
|
557
|
+
return "y"
|
|
549
558
|
if answer in ("n", "no"):
|
|
550
|
-
return
|
|
559
|
+
return "n"
|
|
551
560
|
print("Please enter y or n.", file=sys.stderr)
|
|
552
561
|
|
|
553
562
|
|
|
554
|
-
def get_yn_win(prompt: str) ->
|
|
563
|
+
def get_yn_win(prompt: str) -> str:
|
|
555
564
|
"""GUI yes/no dialog — falls back to terminal in headless mode."""
|
|
556
565
|
return get_yn(prompt)
|
|
557
566
|
|
|
@@ -786,27 +786,37 @@ class TestGuiPublicAPI:
|
|
|
786
786
|
from execsql.utils.gui import get_yn
|
|
787
787
|
|
|
788
788
|
monkeypatch.setattr("builtins.input", lambda *a: "y")
|
|
789
|
-
assert get_yn("Continue?")
|
|
789
|
+
assert get_yn("Continue?") == "y"
|
|
790
790
|
|
|
791
791
|
def test_get_yn_no(self, monkeypatch):
|
|
792
792
|
from execsql.utils.gui import get_yn
|
|
793
793
|
|
|
794
794
|
monkeypatch.setattr("builtins.input", lambda *a: "n")
|
|
795
|
-
assert get_yn("Continue?")
|
|
795
|
+
assert get_yn("Continue?") == "n"
|
|
796
796
|
|
|
797
797
|
def test_get_yn_full_words(self, monkeypatch):
|
|
798
798
|
from execsql.utils.gui import get_yn
|
|
799
799
|
|
|
800
800
|
inputs = iter(["yes"])
|
|
801
801
|
monkeypatch.setattr("builtins.input", lambda *a: next(inputs))
|
|
802
|
-
assert get_yn("?")
|
|
802
|
+
assert get_yn("?") == "y"
|
|
803
803
|
|
|
804
804
|
def test_get_yn_retry_on_bad_input(self, monkeypatch):
|
|
805
805
|
from execsql.utils.gui import get_yn
|
|
806
806
|
|
|
807
807
|
inputs = iter(["maybe", "no"])
|
|
808
808
|
monkeypatch.setattr("builtins.input", lambda *a: next(inputs))
|
|
809
|
-
assert get_yn("?")
|
|
809
|
+
assert get_yn("?") == "n"
|
|
810
|
+
|
|
811
|
+
def test_get_yn_eof_returns_esc(self, monkeypatch):
|
|
812
|
+
"""Ctrl-D / piped EOF is treated as the quit sentinel, matching upstream."""
|
|
813
|
+
from execsql.utils.gui import get_yn
|
|
814
|
+
|
|
815
|
+
def _raise_eof(*a):
|
|
816
|
+
raise EOFError
|
|
817
|
+
|
|
818
|
+
monkeypatch.setattr("builtins.input", _raise_eof)
|
|
819
|
+
assert get_yn("?") == chr(27)
|
|
810
820
|
|
|
811
821
|
def test_gui_credentials_console_fallback(self, monkeypatch):
|
|
812
822
|
"""gui_credentials must not call auth.get_password (which needs
|
|
@@ -912,7 +922,7 @@ class TestGuiPublicAPI:
|
|
|
912
922
|
from execsql.utils.gui import get_yn_win
|
|
913
923
|
|
|
914
924
|
monkeypatch.setattr("builtins.input", lambda *a: "y")
|
|
915
|
-
assert get_yn_win("?")
|
|
925
|
+
assert get_yn_win("?") == "y"
|
|
916
926
|
|
|
917
927
|
def test_gui_console_on_off_updates_isrunning(self):
|
|
918
928
|
"""gui_console_on/off should be reflected by gui_console_isrunning()."""
|
|
@@ -499,6 +499,33 @@ class TestXAsk:
|
|
|
499
499
|
os_mock.name = "posix"
|
|
500
500
|
_prompt.x_ask(**self._kwargs())
|
|
501
501
|
|
|
502
|
+
def test_real_get_yn_yes_stores_yes(self, fake_state, monkeypatch) -> None:
|
|
503
|
+
"""Regression: ASK ... SUB var must store "Yes" when the user answers y.
|
|
504
|
+
|
|
505
|
+
Exercises the real (unmocked) execsql.utils.gui.get_yn through x_ask,
|
|
506
|
+
driven by a patched builtins.input, instead of mocking get_yn itself.
|
|
507
|
+
This is what caught the bool-vs-char-contract regression where
|
|
508
|
+
get_yn() started returning True/False instead of "y"/"n"/chr(27),
|
|
509
|
+
making x_ask's `resp == "y"` comparison always false.
|
|
510
|
+
"""
|
|
511
|
+
s = fake_state()
|
|
512
|
+
_state.gui_console = None
|
|
513
|
+
monkeypatch.setattr("builtins.input", lambda *a: "y")
|
|
514
|
+
with patch("execsql.metacommands.prompt.os") as os_mock:
|
|
515
|
+
os_mock.name = "posix"
|
|
516
|
+
_prompt.x_ask(**self._kwargs())
|
|
517
|
+
s.subvars.add_substitution.assert_called_with("ans", "Yes")
|
|
518
|
+
|
|
519
|
+
def test_real_get_yn_no_stores_no(self, fake_state, monkeypatch) -> None:
|
|
520
|
+
"""Regression: ASK ... SUB var must store "No" when the user answers n."""
|
|
521
|
+
s = fake_state()
|
|
522
|
+
_state.gui_console = None
|
|
523
|
+
monkeypatch.setattr("builtins.input", lambda *a: "n")
|
|
524
|
+
with patch("execsql.metacommands.prompt.os") as os_mock:
|
|
525
|
+
os_mock.name = "posix"
|
|
526
|
+
_prompt.x_ask(**self._kwargs())
|
|
527
|
+
s.subvars.add_substitution.assert_called_with("ans", "No")
|
|
528
|
+
|
|
502
529
|
|
|
503
530
|
# ---------------------------------------------------------------------------
|
|
504
531
|
# x_pause
|
|
@@ -6,6 +6,7 @@ import subprocess
|
|
|
6
6
|
import sys
|
|
7
7
|
from unittest.mock import patch
|
|
8
8
|
|
|
9
|
+
import pytest
|
|
9
10
|
|
|
10
11
|
from execsql.format import (
|
|
11
12
|
_is_comment_line,
|
|
@@ -1825,6 +1826,101 @@ class TestDollarQuotedStrings:
|
|
|
1825
1826
|
assert "EXIT WHEN n >= 3" in result
|
|
1826
1827
|
assert "END LOOP" in result
|
|
1827
1828
|
|
|
1829
|
+
def test_closed_dollar_quote_body_not_sent_to_sqlglot(self):
|
|
1830
|
+
"""A *complete* `CREATE FUNCTION ... $$ ... $$;` must stay opaque.
|
|
1831
|
+
|
|
1832
|
+
The closing `$$` resets `in_dollar_quote` to False before the block is
|
|
1833
|
+
flushed, so a guard that only checked `in_dollar_quote` handed the whole
|
|
1834
|
+
procedural body to sqlglot — exploding the arg list, reordering
|
|
1835
|
+
`LANGUAGE ... AS`, and uppercasing the body's types. The sticky
|
|
1836
|
+
`sql_acc_contains_dollar_quote` flag keeps the statement verbatim.
|
|
1837
|
+
"""
|
|
1838
|
+
source = (
|
|
1839
|
+
"CREATE FUNCTION check_case(val text) RETURNS boolean AS $$\n"
|
|
1840
|
+
"BEGIN\n"
|
|
1841
|
+
" -- compare lowercased value\n"
|
|
1842
|
+
" IF lower(val) = val THEN\n"
|
|
1843
|
+
" RETURN true;\n"
|
|
1844
|
+
" END IF;\n"
|
|
1845
|
+
" RETURN false;\n"
|
|
1846
|
+
"END;\n"
|
|
1847
|
+
"$$ LANGUAGE plpgsql;\n"
|
|
1848
|
+
)
|
|
1849
|
+
result = format_file(source, use_sql=True)
|
|
1850
|
+
# Body preserved byte-for-byte: signature not exploded, types not
|
|
1851
|
+
# uppercased, LANGUAGE clause not reordered, indentation intact.
|
|
1852
|
+
assert "CREATE FUNCTION check_case(val text) RETURNS boolean AS $$" in result
|
|
1853
|
+
assert " -- compare lowercased value" in result
|
|
1854
|
+
assert " RETURN true;" in result
|
|
1855
|
+
assert "$$ LANGUAGE plpgsql;" in result
|
|
1856
|
+
|
|
1857
|
+
@pytest.mark.parametrize(
|
|
1858
|
+
"source",
|
|
1859
|
+
[
|
|
1860
|
+
# Untagged $$ with a comment inside the body.
|
|
1861
|
+
(
|
|
1862
|
+
"CREATE FUNCTION check_case(val text) RETURNS boolean AS $$\n"
|
|
1863
|
+
"BEGIN\n"
|
|
1864
|
+
" -- compare lowercased value\n"
|
|
1865
|
+
" IF lower(val) = val THEN\n"
|
|
1866
|
+
" RETURN true;\n"
|
|
1867
|
+
" END IF;\n"
|
|
1868
|
+
" RETURN false;\n"
|
|
1869
|
+
"END;\n"
|
|
1870
|
+
"$$ LANGUAGE plpgsql;\n"
|
|
1871
|
+
),
|
|
1872
|
+
# Tagged body ($body$ ... $body$).
|
|
1873
|
+
(
|
|
1874
|
+
"CREATE FUNCTION add_one(x int) RETURNS int AS $body$\n"
|
|
1875
|
+
"BEGIN\n"
|
|
1876
|
+
" -- add one\n"
|
|
1877
|
+
" IF x IS NULL THEN\n"
|
|
1878
|
+
" RETURN 0;\n"
|
|
1879
|
+
" END IF;\n"
|
|
1880
|
+
" RETURN x + 1;\n"
|
|
1881
|
+
"END\n"
|
|
1882
|
+
"$body$ LANGUAGE plpgsql;\n"
|
|
1883
|
+
),
|
|
1884
|
+
# DO $$ ... $$;
|
|
1885
|
+
(
|
|
1886
|
+
"DO $$\n"
|
|
1887
|
+
"DECLARE\n"
|
|
1888
|
+
" n int := 0;\n"
|
|
1889
|
+
"BEGIN\n"
|
|
1890
|
+
" -- loop three times\n"
|
|
1891
|
+
" LOOP\n"
|
|
1892
|
+
" EXIT WHEN n >= 3;\n"
|
|
1893
|
+
" n := n + 1;\n"
|
|
1894
|
+
" END LOOP;\n"
|
|
1895
|
+
"END\n"
|
|
1896
|
+
"$$;\n"
|
|
1897
|
+
),
|
|
1898
|
+
# Dollar-quoted body wrapped in an explicit BEGIN SQL / END SQL block.
|
|
1899
|
+
(
|
|
1900
|
+
"-- !x! BEGIN SQL\n"
|
|
1901
|
+
"CREATE FUNCTION add_one(x int) RETURNS int AS $$\n"
|
|
1902
|
+
"BEGIN\n"
|
|
1903
|
+
" -- add one\n"
|
|
1904
|
+
" RETURN x + 1;\n"
|
|
1905
|
+
"END;\n"
|
|
1906
|
+
"$$ LANGUAGE plpgsql;\n"
|
|
1907
|
+
"-- !x! END SQL\n"
|
|
1908
|
+
),
|
|
1909
|
+
],
|
|
1910
|
+
ids=["untagged", "tagged-body", "do-block", "in-begin-sql"],
|
|
1911
|
+
)
|
|
1912
|
+
def test_dollar_quoted_body_idempotent(self, source):
|
|
1913
|
+
"""Formatting a dollar-quoted body must be idempotent (no drift).
|
|
1914
|
+
|
|
1915
|
+
Prior to the fix, each SQL-enabled pass re-indented the body's comment
|
|
1916
|
+
and control-flow lines by one more space, so `execsql-format --check`
|
|
1917
|
+
never converged. Assert `first == second == third`.
|
|
1918
|
+
"""
|
|
1919
|
+
first = format_file(source, use_sql=True)
|
|
1920
|
+
second = format_file(first, use_sql=True)
|
|
1921
|
+
third = format_file(second, use_sql=True)
|
|
1922
|
+
assert first == second == third
|
|
1923
|
+
|
|
1828
1924
|
def test_if_inline_regex_agrees_with_parser(self):
|
|
1829
1925
|
"""`_IF_INLINE_RE` (formatter) and `_IF_INLINE_RX` (AST parser) must
|
|
1830
1926
|
recognise the same inline-IF payloads. They are intentionally kept
|
|
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.22.1 → execsql2-2.22.3}/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
|