execsql2 2.15.7__tar.gz → 2.15.11__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.15.7 → execsql2-2.15.11}/.gitignore +2 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/CHANGELOG.md +61 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/PKG-INFO +4 -3
- {execsql2-2.15.7 → execsql2-2.15.11}/README.md +1 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/about/divergence.md +10 -8
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/reference/configuration.md +6 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/reference/substitution_vars.md +3 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/justfile +45 -10
- {execsql2-2.15.7 → execsql2-2.15.11}/pyproject.toml +3 -3
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/__init__.py +4 -3
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/cli/__init__.py +17 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/cli/run.py +4 -1
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/config.py +20 -4
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/base.py +4 -1
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/debug/repl.py +27 -10
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/gui/tui.py +59 -2
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/conditions.py +20 -2
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/upsert.py +20 -10
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/gui.py +139 -17
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/test_cli.py +51 -0
- execsql2-2.15.11/tests/db/test_db_adapters_mocked.py +547 -0
- execsql2-2.15.11/tests/db/test_dsn.py +366 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/gui/test_backends.py +16 -5
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_csv_edge_cases.py +97 -1
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_breakpoint.py +7 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_pg_upsert.py +46 -23
- execsql2-2.15.11/tests/scripts/fixtures/control_flow.sql +173 -0
- execsql2-2.15.11/tests/scripts/fixtures/io_roundtrip.sql +135 -0
- execsql2-2.15.11/tests/scripts/fixtures/smoke.sql +138 -0
- execsql2-2.15.11/tests/scripts/test_sql_scripts.py +67 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_config_data.py +61 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_debug_repl.py +7 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_format.py +183 -0
- execsql2-2.15.11/tests/test_parser.py +814 -0
- execsql2-2.15.11/tests/utils/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/uv.lock +5 -5
- execsql2-2.15.7/tests/test_parser.py +0 -446
- {execsql2-2.15.7 → execsql2-2.15.11}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.pre-commit-config.yaml +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.python-version +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/.readthedocs.yaml +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/CONTRIBUTING.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/LICENSE.txt +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/NOTICE +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/SECURITY.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/about/contributors.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/about/copyright.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/api/cli.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/api/db.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/api/exporters.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/api/importers.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/api/index.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/api/metacommands.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/dev/architecture.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/getting-started/installation.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/debugging.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/documentation.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/encoding.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/examples.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/formatter.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/logging.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/usage.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/actions.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/actions2.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/checkboxes.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/connect.b64 +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/connect.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/create_conf.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/entry_form.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/execsql_console.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/fatals.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/logo_small.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/unmatched.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/index.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/reference/metacommands.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/docs/reference/security.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/__main__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/cli/help.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/access.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/factory.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exceptions.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/format.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/gui/base.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/gui/console.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/base.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/json.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/models.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/parser.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/py.typed +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/script/control.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/script/engine.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/script/variables.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/state.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/types.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/README.md +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/config_settings.sqlite +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/execsql.conf +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/make_config_db.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/md_compare.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/md_glossary.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/md_upsert.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/pg_compare.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/pg_glossary.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/pg_upsert.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/script_template.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/ss_compare.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/ss_glossary.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/templates/ss_upsert.sql +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/test_lint.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/test_ping.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/cli/test_profile.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/conftest.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/test_base.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/test_factory.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/test_postgres.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_base.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_db.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_json.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/gui/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/conftest.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.15.7/tests/utils → execsql2-2.15.11/tests/scripts}/__init__.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_config.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_config_extended.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_engine.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_error_messages.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_exceptions.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_mail.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_models.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_package.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_registry.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_script.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_state.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/test_types.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_auth.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_errors.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_regex.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_strings.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_timer.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.15.7 → execsql2-2.15.11}/zensical.toml +0 -0
|
@@ -13,6 +13,67 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.15.11] - 2026-04-27
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- `PAUSE` console-mode fallback now checks `sys.platform` before attempting POSIX terminal imports, preventing hangs on Windows when stdin reports as a TTY.
|
|
21
|
+
|
|
22
|
+
______________________________________________________________________
|
|
23
|
+
|
|
24
|
+
## [2.15.10] - 2026-04-27
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `--config FILE` CLI flag to specify an explicit configuration file. The file is loaded after the implicit search paths (system, user, script-dir, working-dir) so its values take precedence, while CLI arguments still override everything.
|
|
29
|
+
- `$HOSTNAME` system substitution variable — the network name of the machine running execsql, useful for log messages and environment detection.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Config file chaining no longer mutates a list during iteration; uses a deque for safe, predictable processing order.
|
|
34
|
+
- REPL `_use_color()` result is now cached instead of re-checking environment variables and TTY status on every colorized output.
|
|
35
|
+
- `DatabasePool.closeall()` no longer calls `self.__init__()` to reset state; fields are reset directly to avoid the re-initialization anti-pattern.
|
|
36
|
+
- `PAUSE` console mode no longer crashes on Windows CI due to unconditional `import termios`; POSIX-only imports are now guarded by the TTY fallback check.
|
|
37
|
+
- `HAS_ROWS()`, `ROW_COUNT_GT()`, `ROW_COUNT_GTE()`, `ROW_COUNT_EQ()`, and `ROW_COUNT_LT()` condition predicates now quote table names with standard SQL identifier quoting, preventing potential SQL injection when table names originate from substitution variables.
|
|
38
|
+
- Corrected `__init__.py` module docstring that incorrectly described the CLI entry point as `execsql2` (the command is `execsql`).
|
|
39
|
+
- Added note to configuration reference clarifying that `--output-dir` is a CLI-only option with no equivalent configuration file setting.
|
|
40
|
+
|
|
41
|
+
______________________________________________________________________
|
|
42
|
+
|
|
43
|
+
## [2.15.9] - 2026-04-27
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- Textual TUI now displays a progress bar and remaining-time countdown for `PROMPT PAUSE` and `PAUSE` dialogs when the `CONTINUE AFTER` or `HALT AFTER` keywords specify a timed duration (matching existing Tkinter behavior).
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- `PAUSE` metacommand in console mode (no `-v`) now responds to single keypresses (Enter to continue, Esc to quit) instead of requiring Enter after every key. Uses raw-mode terminal reading on POSIX and `msvcrt` polling on Windows.
|
|
52
|
+
- `PAUSE` with `CONTINUE AFTER`/`HALT AFTER` in console mode now displays a live SIGALRM-driven progress bar showing time remaining, matching the documented behavior and terminal screenshot.
|
|
53
|
+
- `PAUSE` progress bar output no longer bleeds into subsequent script output — the progress line is cleared before returning.
|
|
54
|
+
- Fixed double minutes-to-seconds conversion in the console `PAUSE` path that caused a 1-minute pause to sleep for 60 minutes.
|
|
55
|
+
|
|
56
|
+
______________________________________________________________________
|
|
57
|
+
|
|
58
|
+
## [2.15.8] - 2026-04-20
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- `PG_UPSERT` / `PG_UPSERT QA` / `PG_UPSERT CHECK` now support the `STRICT_COLUMNS` keyword. When present, all missing columns in staging tables are treated as errors (not just PK and NOT NULL/no-default columns). Maps to pg-upsert's `strict_columns=True` parameter.
|
|
63
|
+
- New substitution variable `$PG_UPSERT_QA_WARNINGS` — a comma-separated list of table names that received WARNING-level QA findings. Scripts can use this to react to warnings without parsing `$PG_UPSERT_RESULT_JSON`.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- `$PG_UPSERT_RESULT_JSON` now includes a `qa_warnings` array per table (previously only `qa_errors` was present). This reflects pg-upsert v1.22's severity-aware QA model.
|
|
68
|
+
- Minimum pg-upsert version bumped from `>=1.21.0` to `>=1.22.0`.
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- `PG_UPSERT QA` and `PG_UPSERT CHECK` now capture all QA findings (errors + warnings) instead of only errors, so `$PG_UPSERT_RESULT_JSON` includes the full picture.
|
|
73
|
+
- Fixed compatibility with pg-upsert v1.22.0 where `TableResult.qa_errors` became a read-only property (now writes to `_qa_findings` field).
|
|
74
|
+
|
|
75
|
+
______________________________________________________________________
|
|
76
|
+
|
|
16
77
|
## [2.15.7] - 2026-04-20
|
|
17
78
|
|
|
18
79
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.15.
|
|
3
|
+
Version: 2.15.11
|
|
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
|
|
@@ -52,7 +52,7 @@ Requires-Dist: keyring; extra == 'all'
|
|
|
52
52
|
Requires-Dist: odfpy; extra == 'all'
|
|
53
53
|
Requires-Dist: openpyxl; extra == 'all'
|
|
54
54
|
Requires-Dist: oracledb; extra == 'all'
|
|
55
|
-
Requires-Dist: pg-upsert>=1.
|
|
55
|
+
Requires-Dist: pg-upsert>=1.22.0; extra == 'all'
|
|
56
56
|
Requires-Dist: polars; extra == 'all'
|
|
57
57
|
Requires-Dist: psycopg2-binary; extra == 'all'
|
|
58
58
|
Requires-Dist: pymysql; extra == 'all'
|
|
@@ -117,7 +117,7 @@ Requires-Dist: oracledb; extra == 'oracle'
|
|
|
117
117
|
Provides-Extra: postgres
|
|
118
118
|
Requires-Dist: psycopg2-binary; extra == 'postgres'
|
|
119
119
|
Provides-Extra: upsert
|
|
120
|
-
Requires-Dist: pg-upsert>=1.
|
|
120
|
+
Requires-Dist: pg-upsert>=1.22.0; extra == 'upsert'
|
|
121
121
|
Description-Content-Type: text/markdown
|
|
122
122
|
|
|
123
123
|
> [!NOTE]
|
|
@@ -240,6 +240,7 @@ execsql script.sql # read connection from config file
|
|
|
240
240
|
| `--ping` | Test database connectivity and exit |
|
|
241
241
|
| `--profile` | Show per-statement timing summary after execution |
|
|
242
242
|
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
243
|
+
| `--config FILE` | Load an explicit config file (highest priority after CLI args) |
|
|
243
244
|
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
244
245
|
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
245
246
|
| `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
|
|
@@ -118,6 +118,7 @@ execsql script.sql # read connection from config file
|
|
|
118
118
|
| `--ping` | Test database connectivity and exit |
|
|
119
119
|
| `--profile` | Show per-statement timing summary after execution |
|
|
120
120
|
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
121
|
+
| `--config FILE` | Load an explicit config file (highest priority after CLI args) |
|
|
121
122
|
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
122
123
|
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
123
124
|
| `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
|
|
@@ -21,6 +21,7 @@ ______________________________________________________________________
|
|
|
21
21
|
| `--output-dir` | Set a default base directory for export output files. |
|
|
22
22
|
| `--progress` | Show a Rich progress bar during long-running IMPORT operations. |
|
|
23
23
|
| `--dump-keywords` | Emit all metacommand keywords, conditionals, config options, and export formats as structured JSON. |
|
|
24
|
+
| `--config FILE` | Load an explicit configuration file after the implicit search paths. Its values take precedence over system, user, script-dir, and working-dir config files; CLI arguments still override everything. The file may chain additional configs via its `[config]` section. |
|
|
24
25
|
| `--gui-framework` | Select GUI backend: `tkinter` (default) or `textual` (terminal UI). |
|
|
25
26
|
| `--debug` | Start in step-through debug mode. The debug REPL pauses before each statement, as if `BREAKPOINT` were at the top with `.next` always active. |
|
|
26
27
|
| `--dry-run` | Parse the script and print the full command list without connecting to a database or executing anything. Substitution variables already populated at parse time (env vars, `--assign-arg` values, built-in start-time vars) are expanded in the output; execution-time variables (`$DB_NAME`, `$CURRENT_TIME`, etc.) remain unexpanded. |
|
|
@@ -41,14 +42,14 @@ ______________________________________________________________________
|
|
|
41
42
|
|
|
42
43
|
### Metacommands
|
|
43
44
|
|
|
44
|
-
| Metacommand | Description
|
|
45
|
-
| ---------------------- |
|
|
46
|
-
| `ASSERT` | Evaluate a condition and raise an error (halting the script) if it is false. Supports all IF conditions. Optional quoted failure message. Skipped in false IF blocks.
|
|
47
|
-
| `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. See [Debugging](#debugging) below for full details.
|
|
48
|
-
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime.
|
|
49
|
-
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file.
|
|
50
|
-
| `PG_UPSERT` | QA-checked, FK-dependency-ordered upserts from staging to base schema on PostgreSQL. Integrates [pg-upsert](https://pg-upsert.readthedocs.io/) as an optional dependency. Three modes: full pipeline, QA-only, and schema check. Supports `EXPORT_FAILURES`, `EXPORT_FORMAT`, and `
|
|
51
|
-
| `IMPORT … FROM JSON` | Import a JSON file (array of objects or NDJSON) into a database table. Nested objects are flattened with dot-separated column names; arrays are stored as JSON strings.
|
|
45
|
+
| Metacommand | Description |
|
|
46
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
47
|
+
| `ASSERT` | Evaluate a condition and raise an error (halting the script) if it is false. Supports all IF conditions. Optional quoted failure message. Skipped in false IF blocks. |
|
|
48
|
+
| `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL. See [Debugging](#debugging) below for full details. |
|
|
49
|
+
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime. |
|
|
50
|
+
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file. |
|
|
51
|
+
| `PG_UPSERT` | QA-checked, FK-dependency-ordered upserts from staging to base schema on PostgreSQL. Integrates [pg-upsert](https://pg-upsert.readthedocs.io/) as an optional dependency. Three modes: full pipeline, QA-only, and schema check. Supports `EXPORT_FAILURES`, `EXPORT_FORMAT`, `EXPORT_MAX_ROWS`, and `STRICT_COLUMNS` keywords. `STRICT_COLUMNS` forces all missing columns to be errors (requires `pg-upsert>=1.22.0`). |
|
|
52
|
+
| `IMPORT … FROM JSON` | Import a JSON file (array of objects or NDJSON) into a database table. Nested objects are flattened with dot-separated column names; arrays are stored as JSON strings. |
|
|
52
53
|
|
|
53
54
|
### Conditional Tests
|
|
54
55
|
|
|
@@ -175,6 +176,7 @@ All 33 mutable runtime globals in `state.py` have been consolidated into a `Runt
|
|
|
175
176
|
|
|
176
177
|
### Substitution Variables
|
|
177
178
|
|
|
179
|
+
- **`$HOSTNAME`** — Network name of the machine running execsql (`platform.node()`). Useful for log messages and environment detection.
|
|
178
180
|
- **`$SYSTEM_CMD_PID`** — New system variable set to the PID of the background process when `SHELL … CONTINUE` is used.
|
|
179
181
|
- **Cycle detection** — `substitute_vars()` raises an error after 100 iterations to prevent infinite loops when variables reference each other cyclically. Upstream had no protection.
|
|
180
182
|
- **O(1) substitution** — Variable substitution uses a single combined regex and dict lookup instead of O(V) per-variable regex passes. Behavior is identical; performance is improved.
|
|
@@ -13,6 +13,8 @@ The name of the configuration file, in all locations, is `execsql.conf`.
|
|
|
13
13
|
|
|
14
14
|
Configuration data is read from these files in the order listed above. Information in later files may augment or replace information in earlier files. Options and arguments specified on the command line will further augment or override information specified in the configuration files.
|
|
15
15
|
|
|
16
|
+
An explicit configuration file can also be specified with the `--config FILE` command-line option. This file is loaded **after** all four implicit search paths, so its values take precedence over system, user, script-directory, and working-directory config files. CLI arguments still override everything. The `--config` file may chain additional configs via its `[config]` section, just like any other config file.
|
|
17
|
+
|
|
16
18
|
In addition, *execsql* will read additional configuration files if they are specified in any of the standard configuration files ([see below](#config_config)).
|
|
17
19
|
|
|
18
20
|
Configuration files use the [INI](https://en.wikipedia.org/wiki/INI_file) file format. Section names are case sensitive and must be all in lowercase. Property names are not case sensitive. Property values are read as-is and may or may not be case sensitive, depending on their use. Comments can be included in configuration files; each comment line must start with the "#" character.
|
|
@@ -200,6 +202,10 @@ The section and property names that may be used in a configuration file are list
|
|
|
200
202
|
`zip_buffer_mb` { #zip_buffer_mb }
|
|
201
203
|
: The size of the internal buffer used when the [EXPORT](metacommands.md#export) metacommand exports data to a zipfile, in Mb. The default value is 10. The buffer should be at least as large as the largest data row to be exported. This value typically has little effect on performance, and only affects memory usage.
|
|
202
204
|
|
|
205
|
+
!!! note "CLI-only output options"
|
|
206
|
+
|
|
207
|
+
The [`--output-dir`](../getting-started/syntax.md) option sets a default base directory for EXPORT output files. Relative paths in EXPORT metacommands are automatically joined to this directory; absolute paths and `stdout` are unaffected. This option is **only available on the command line** — there is no equivalent configuration file setting.
|
|
208
|
+
|
|
203
209
|
## Section `interface`
|
|
204
210
|
|
|
205
211
|
`console_height`
|
|
@@ -138,6 +138,9 @@ $ERROR_HALT_STATE
|
|
|
138
138
|
$ERROR_MESSAGE
|
|
139
139
|
: The message generated by any error, as it would be printed on the terminal by default. This is initially an empty string, and is set by any SQL error or metacommand error. If an error occurs, the error message is only accessible if the [ERROR_HALT OFF](metacommands.md#error_halt) or [METACOMMAND_ERROR_HALT OFF](metacommands.md#metacommanderrorhalt) metacommand has been used, or in an [ON ERROR_HALT EMAIL](metacommands.md#error_halt_email), [ON ERROR_HALT WRITE](metacommands.md#error_halt_write), or [ON ERROR_HALT EXECUTE SCRIPT](metacommands.md#error_halt_exec) metacommand.
|
|
140
140
|
|
|
141
|
+
$HOSTNAME
|
|
142
|
+
: The network name of the machine running execsql, as returned by Python's `platform.node()`. Useful for log messages, environment detection, and multi-host deployment scripts.
|
|
143
|
+
|
|
141
144
|
$LAST_ERROR
|
|
142
145
|
: The text of the last SQL statement or metacommand that caused an error. This value will only be available if the [ERROR_HALT OFF](metacommands.md#error_halt) or [METACOMMAND_ERROR_HALT OFF](metacommands.md#metacommanderrorhalt) metacommand has been used, or in an [ON ERROR_HALT EMAIL](metacommands.md#error_halt_email), [ON ERROR_HALT WRITE](metacommands.md#error_halt_write), or [ON ERROR_HALT EXECUTE SCRIPT](metacommands.md#error_halt_exec) metacommand.
|
|
143
146
|
|
|
@@ -1,42 +1,65 @@
|
|
|
1
1
|
# Install: brew install just | cargo install just | uv tool install rust-just
|
|
2
2
|
# Usage: just <recipe>
|
|
3
3
|
|
|
4
|
+
set quiet
|
|
5
|
+
set unstable
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
|
|
8
|
+
[private]
|
|
6
9
|
default:
|
|
7
|
-
@just --list
|
|
10
|
+
@just --list --unsorted
|
|
8
11
|
|
|
9
12
|
# ── Dependencies ──────────────────────────────────────────────────────────────
|
|
10
13
|
|
|
11
14
|
# Sync dependencies from lockfile
|
|
15
|
+
[group('deps')]
|
|
12
16
|
sync:
|
|
13
17
|
uv sync --all-extras
|
|
14
18
|
|
|
15
19
|
# Update pre-commit hooks
|
|
20
|
+
[group('deps')]
|
|
16
21
|
update-hooks:
|
|
17
22
|
uv run pre-commit autoupdate
|
|
18
23
|
|
|
19
24
|
|
|
20
25
|
# ── Code Quality ──────────────────────────────────────────────────────────────
|
|
21
26
|
|
|
22
|
-
#
|
|
27
|
+
# Lint, spell-check, and test
|
|
28
|
+
[group('quality')]
|
|
29
|
+
check: format lint test
|
|
30
|
+
|
|
31
|
+
# Run linter
|
|
32
|
+
[group('quality')]
|
|
23
33
|
lint:
|
|
24
34
|
uv run ruff check .
|
|
35
|
+
|
|
36
|
+
# Run formatter
|
|
37
|
+
[group('quality')]
|
|
38
|
+
format:
|
|
25
39
|
uv run ruff format .
|
|
26
40
|
|
|
27
41
|
# Run pre-commit hooks on all files
|
|
42
|
+
[group('quality')]
|
|
28
43
|
pre-commit:
|
|
29
44
|
uv run pre-commit run --all-files
|
|
30
45
|
|
|
31
46
|
# Run tests
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
[group('quality')]
|
|
48
|
+
test *ARGS:
|
|
49
|
+
uv run tox -e py -- {{ ARGS }}
|
|
34
50
|
|
|
35
51
|
# Run tests across all supported Python versions
|
|
52
|
+
[group('quality')]
|
|
36
53
|
test-all:
|
|
37
54
|
uv run tox run-parallel
|
|
38
55
|
|
|
56
|
+
# Run tests with coverage report printed to terminal
|
|
57
|
+
[group('quality')]
|
|
58
|
+
coverage:
|
|
59
|
+
uv run pytest --cov-report=term-missing
|
|
60
|
+
|
|
39
61
|
# Clean up Python build artifacts and caches
|
|
62
|
+
[group('quality')]
|
|
40
63
|
clean:
|
|
41
64
|
find . -type d -name "__pycache__" -exec rm -rf {} +
|
|
42
65
|
find . -type d -name ".pytest_cache" -exec rm -rf {} +
|
|
@@ -48,11 +71,14 @@ clean:
|
|
|
48
71
|
find . -type f -name "*.pyc" -exec rm -f {} +
|
|
49
72
|
find . -type f -name "*.pyo" -exec rm -f {} +
|
|
50
73
|
find . -type f -name ".coverage" -exec rm -rf {} +
|
|
74
|
+
find . -type f -name "coverage.xml" -exec rm -rf {} +
|
|
75
|
+
find . -type f -name "execsql.log" -exec rm -f {} +
|
|
51
76
|
|
|
52
77
|
|
|
53
78
|
# ── VS Code Extension ────────────────────────────────────────────────────────
|
|
54
79
|
|
|
55
|
-
#
|
|
80
|
+
# Regenerate grammar and install VS Code extension via symlink
|
|
81
|
+
[group('vscode')]
|
|
56
82
|
install-vscode:
|
|
57
83
|
uv run python scripts/generate_vscode_grammar.py
|
|
58
84
|
ln -sfn "$(pwd)/extras/vscode-execsql" ~/.vscode/extensions/execsql-syntax
|
|
@@ -61,31 +87,40 @@ install-vscode:
|
|
|
61
87
|
|
|
62
88
|
# ── Documentation──────────────────────────────────────────────────────────────
|
|
63
89
|
|
|
64
|
-
#
|
|
65
|
-
|
|
90
|
+
# Copy CHANGELOG into docs source tree
|
|
91
|
+
[private]
|
|
92
|
+
_sync-changelog:
|
|
66
93
|
cp CHANGELOG.md docs/about/change_log.md
|
|
94
|
+
|
|
95
|
+
# Build documentation
|
|
96
|
+
[group('docs')]
|
|
97
|
+
docs: _sync-changelog
|
|
67
98
|
uv run zensical build
|
|
68
99
|
|
|
69
100
|
# Serve documentation locally
|
|
70
|
-
docs
|
|
71
|
-
|
|
101
|
+
[group('docs')]
|
|
102
|
+
docs-serve: _sync-changelog
|
|
72
103
|
uv run zensical serve
|
|
73
104
|
|
|
74
105
|
|
|
75
106
|
# ── Versioning ────────────────────────────────────────────────────────────────
|
|
76
107
|
|
|
77
108
|
# List the current version
|
|
109
|
+
[group('version')]
|
|
78
110
|
bump:
|
|
79
111
|
uv run bump-my-version show-bump
|
|
80
112
|
|
|
81
113
|
# Bump patch version (e.g. 1.2.3 → 1.2.4)
|
|
114
|
+
[group('version')]
|
|
82
115
|
bump-patch:
|
|
83
116
|
uv run bump-my-version bump patch
|
|
84
117
|
|
|
85
118
|
# Bump minor version (e.g. 1.2.3 → 1.3.0)
|
|
119
|
+
[group('version')]
|
|
86
120
|
bump-minor:
|
|
87
121
|
uv run bump-my-version bump minor
|
|
88
122
|
|
|
89
123
|
# Bump major version (e.g. 1.2.3 → 2.0.0)
|
|
124
|
+
[group('version')]
|
|
90
125
|
bump-major:
|
|
91
126
|
uv run bump-my-version bump major
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.15.
|
|
7
|
+
version = "2.15.11"
|
|
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" }
|
|
@@ -61,7 +61,7 @@ formats = ["odfpy", "xlrd", "openpyxl", "Jinja2", "polars", "tables", "PyYAML"]
|
|
|
61
61
|
auth = ["keyring"]
|
|
62
62
|
auth-plaintext = ["keyring", "keyrings.alt"]
|
|
63
63
|
auth-encrypted = ["keyring", "keyrings.alt", "pycryptodome"]
|
|
64
|
-
upsert = ["pg-upsert>=1.
|
|
64
|
+
upsert = ["pg-upsert>=1.22.0"]
|
|
65
65
|
# Convenience groups
|
|
66
66
|
all-db = [
|
|
67
67
|
"psycopg2-binary",
|
|
@@ -164,7 +164,7 @@ skip-magic-trailing-comma = false
|
|
|
164
164
|
line-ending = "auto"
|
|
165
165
|
|
|
166
166
|
[tool.bumpversion]
|
|
167
|
-
current_version = "2.15.
|
|
167
|
+
current_version = "2.15.11"
|
|
168
168
|
commit = true
|
|
169
169
|
commit_args = "--no-verify"
|
|
170
170
|
tag = true
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
2
|
execsql — a maintained fork of the execsql SQL scripting tool.
|
|
3
3
|
|
|
4
|
-
This package provides the ``
|
|
5
|
-
``
|
|
6
|
-
version; all public
|
|
4
|
+
This package provides the ``execsql`` CLI command (distributed as the
|
|
5
|
+
``execsql2`` package on PyPI) and the ``execsql`` importable module.
|
|
6
|
+
The top-level package exposes only the package version; all public
|
|
7
|
+
functionality lives in sub-modules.
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from __future__ import annotations
|
|
@@ -283,6 +283,16 @@ def main(
|
|
|
283
283
|
"--profile-limit",
|
|
284
284
|
help="Number of top statements to show in the --profile timing summary (default: 20).",
|
|
285
285
|
),
|
|
286
|
+
config_file: str | None = typer.Option(
|
|
287
|
+
None,
|
|
288
|
+
"--config",
|
|
289
|
+
metavar="FILE",
|
|
290
|
+
help=(
|
|
291
|
+
"Path to an execsql configuration file. "
|
|
292
|
+
"Loaded after the implicit search paths so its values take precedence. "
|
|
293
|
+
"The file may chain additional configs via its [cyan][config][/cyan] section."
|
|
294
|
+
),
|
|
295
|
+
),
|
|
286
296
|
debug: bool = typer.Option(
|
|
287
297
|
False,
|
|
288
298
|
"--debug",
|
|
@@ -376,6 +386,12 @@ def main(
|
|
|
376
386
|
webbrowser.open("https://execsql2.readthedocs.io/en/latest/", new=2, autoraise=True)
|
|
377
387
|
raise typer.Exit()
|
|
378
388
|
|
|
389
|
+
if config_file and not Path(config_file).is_file():
|
|
390
|
+
_err_console.print(
|
|
391
|
+
f"[bold red]Error:[/bold red] Config file [cyan]{config_file!r}[/cyan] does not exist.",
|
|
392
|
+
)
|
|
393
|
+
raise typer.Exit(code=2)
|
|
394
|
+
|
|
379
395
|
positional = args or []
|
|
380
396
|
if command is not None:
|
|
381
397
|
script_name = None # inline mode — no script file
|
|
@@ -458,6 +474,7 @@ def main(
|
|
|
458
474
|
ping=ping,
|
|
459
475
|
lint=lint,
|
|
460
476
|
debug=debug,
|
|
477
|
+
config_file=config_file,
|
|
461
478
|
)
|
|
462
479
|
|
|
463
480
|
|
|
@@ -12,6 +12,7 @@ from typing import Any
|
|
|
12
12
|
import datetime
|
|
13
13
|
import getpass
|
|
14
14
|
import os
|
|
15
|
+
import platform
|
|
15
16
|
import sys
|
|
16
17
|
import traceback
|
|
17
18
|
from pathlib import Path
|
|
@@ -219,6 +220,7 @@ def _run(
|
|
|
219
220
|
ping: bool = False,
|
|
220
221
|
lint: bool = False,
|
|
221
222
|
debug: bool = False,
|
|
223
|
+
config_file: str | None = None,
|
|
222
224
|
) -> None:
|
|
223
225
|
"""Initialise state, connect to the database, load the script, and run it.
|
|
224
226
|
|
|
@@ -276,13 +278,14 @@ def _run(
|
|
|
276
278
|
elif osys.startswith("win"):
|
|
277
279
|
osys = "windows"
|
|
278
280
|
_state.subvars.add_substitution("$OS", osys)
|
|
281
|
+
_state.subvars.add_substitution("$HOSTNAME", platform.node())
|
|
279
282
|
_state.subvars.add_substitution("$PYTHON_EXECUTABLE", sys.executable)
|
|
280
283
|
|
|
281
284
|
# ------------------------------------------------------------------
|
|
282
285
|
# Read configuration file
|
|
283
286
|
# ------------------------------------------------------------------
|
|
284
287
|
script_path = str(Path(script_name).resolve().parent) if script_name else os.getcwd()
|
|
285
|
-
_state.conf = ConfigData(script_path, _state.subvars)
|
|
288
|
+
_state.conf = ConfigData(script_path, _state.subvars, config_file=config_file)
|
|
286
289
|
conf = _state.conf
|
|
287
290
|
|
|
288
291
|
# ------------------------------------------------------------------
|
|
@@ -198,7 +198,13 @@ class ConfigData:
|
|
|
198
198
|
raise ConfigError(f"Invalid {key}: {val}; must be >= {min_val}.")
|
|
199
199
|
setattr(self, attr, val)
|
|
200
200
|
|
|
201
|
-
def __init__(
|
|
201
|
+
def __init__(
|
|
202
|
+
self,
|
|
203
|
+
script_path: str,
|
|
204
|
+
variable_pool: object,
|
|
205
|
+
*,
|
|
206
|
+
config_file: str | None = None,
|
|
207
|
+
) -> None:
|
|
202
208
|
"""Load and merge all discoverable execsql.conf files for the given script path.
|
|
203
209
|
|
|
204
210
|
Args:
|
|
@@ -207,6 +213,10 @@ class ConfigData:
|
|
|
207
213
|
variable_pool: Substitution-variable registry used to expand
|
|
208
214
|
``config_file`` path values and to populate ``[variables]``
|
|
209
215
|
sections.
|
|
216
|
+
config_file: Optional explicit config file path (from ``--config``).
|
|
217
|
+
Loaded after the implicit search paths so its values take
|
|
218
|
+
precedence over system, user, script, and working-directory
|
|
219
|
+
config files.
|
|
210
220
|
"""
|
|
211
221
|
self.db_type = "a"
|
|
212
222
|
self.server = None
|
|
@@ -290,9 +300,15 @@ class ConfigData:
|
|
|
290
300
|
config_files = [sys_config_file, user_config_file, script_config_file, startdir_config_file]
|
|
291
301
|
else:
|
|
292
302
|
config_files = [sys_config_file, user_config_file, startdir_config_file]
|
|
303
|
+
if config_file:
|
|
304
|
+
config_files.append(str(Path(config_file).resolve()))
|
|
305
|
+
from collections import deque
|
|
306
|
+
|
|
293
307
|
_MAX_CONFIG_CHAIN = 20 # Guard against circular config_file references.
|
|
308
|
+
config_queue: deque[str] = deque(config_files)
|
|
294
309
|
self.files_read: list = []
|
|
295
|
-
|
|
310
|
+
while config_queue:
|
|
311
|
+
configfile = config_queue.popleft()
|
|
296
312
|
if len(self.files_read) >= _MAX_CONFIG_CHAIN:
|
|
297
313
|
break
|
|
298
314
|
if configfile not in self.files_read and Path(configfile).is_file():
|
|
@@ -425,7 +441,7 @@ class ConfigData:
|
|
|
425
441
|
conffile = str(Path(conffile) / self.config_file_name)
|
|
426
442
|
if Path(conffile).is_file():
|
|
427
443
|
# Silently ignore a non-existent file, for cross-OS compatibility.
|
|
428
|
-
|
|
444
|
+
config_queue.appendleft(conffile)
|
|
429
445
|
# OS-specific additional config files.
|
|
430
446
|
_os_config_key: str | None = None
|
|
431
447
|
if sys.platform == "linux" and cp.has_option(self._CONFIG_SECTION, "linux_config_file"):
|
|
@@ -445,7 +461,7 @@ class ConfigData:
|
|
|
445
461
|
if not Path(conffile).is_file():
|
|
446
462
|
conffile = str(Path(conffile) / self.config_file_name)
|
|
447
463
|
if Path(conffile).is_file():
|
|
448
|
-
|
|
464
|
+
config_queue.appendleft(conffile)
|
|
449
465
|
self._get_bool(cp, self._CONFIG_SECTION, "user_logfile", "user_logfile")
|
|
450
466
|
# dao_flush_delay_secs has a specific error message — keep inline
|
|
451
467
|
if cp.has_option(self._CONFIG_SECTION, "dao_flush_delay_secs"):
|
|
@@ -45,23 +45,39 @@ _YELLOW = "\033[33m"
|
|
|
45
45
|
_CYAN = "\033[36m"
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
_color_cache: bool | None = None
|
|
49
|
+
|
|
50
|
+
|
|
48
51
|
def _use_color() -> bool:
|
|
49
52
|
"""Return True if the output stream supports ANSI color.
|
|
50
53
|
|
|
51
54
|
Checks ``NO_COLOR`` and ``EXECSQL_NO_COLOR`` environment variables first
|
|
52
55
|
(either set → color off). Then tests whether the active output stream
|
|
53
56
|
reports itself as a TTY.
|
|
57
|
+
|
|
58
|
+
The result is cached after the first call; call ``_reset_color_cache()``
|
|
59
|
+
to force re-evaluation (e.g. when entering the REPL).
|
|
54
60
|
"""
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
global _color_cache # noqa: PLW0603
|
|
62
|
+
if _color_cache is not None:
|
|
63
|
+
return _color_cache
|
|
64
|
+
if os.environ.get("NO_COLOR") is not None or os.environ.get("EXECSQL_NO_COLOR") is not None:
|
|
65
|
+
_color_cache = False
|
|
66
|
+
else:
|
|
67
|
+
output = _state.output
|
|
68
|
+
if output is not None and hasattr(output, "isatty"):
|
|
69
|
+
_color_cache = output.isatty()
|
|
70
|
+
else:
|
|
71
|
+
# WriteHooks (the default _state.output) has no isatty — fall through
|
|
72
|
+
# to check the underlying stream it would write to.
|
|
73
|
+
_color_cache = sys.stdout.isatty()
|
|
74
|
+
return _color_cache
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _reset_color_cache() -> None:
|
|
78
|
+
"""Clear the cached color decision so it is re-evaluated on next use."""
|
|
79
|
+
global _color_cache # noqa: PLW0603
|
|
80
|
+
_color_cache = None
|
|
65
81
|
|
|
66
82
|
|
|
67
83
|
def _c(code: str, text: str) -> str:
|
|
@@ -169,6 +185,7 @@ def _debug_repl(*, step: bool = False) -> None:
|
|
|
169
185
|
step: When ``True``, the entry banner says "Step" instead of
|
|
170
186
|
"Breakpoint" to indicate the REPL was re-entered via step mode.
|
|
171
187
|
"""
|
|
188
|
+
_reset_color_cache()
|
|
172
189
|
try:
|
|
173
190
|
import readline as _readline # noqa: F401 — side-effect: enables history/arrow keys
|
|
174
191
|
except ImportError:
|