execsql2 2.5.0__tar.gz → 2.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/project_context.md +107 -19
- {execsql2-2.5.0 → execsql2-2.6.0}/.gitignore +1 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/CHANGELOG.md +21 -1
- {execsql2-2.5.0 → execsql2-2.6.0}/CLAUDE.md +1 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/PKG-INFO +1 -1
- execsql2-2.6.0/docs/about/divergence.md +168 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/pyproject.toml +7 -3
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/gui/tui.py +132 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/__init__.py +180 -180
- execsql2-2.6.0/src/execsql/state.py +452 -0
- execsql2-2.6.0/tests/db/test_base.py +1953 -0
- execsql2-2.6.0/tests/exporters/test_delimited.py +1248 -0
- execsql2-2.6.0/tests/metacommands/test_connect.py +1807 -0
- execsql2-2.6.0/tests/test_engine.py +1305 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_state.py +109 -3
- {execsql2-2.5.0 → execsql2-2.6.0}/uv.lock +1 -1
- {execsql2-2.5.0 → execsql2-2.6.0}/zensical.toml +1 -0
- execsql2-2.5.0/src/execsql/state.py +0 -391
- execsql2-2.5.0/tests/db/test_base.py +0 -423
- execsql2-2.5.0/tests/exporters/test_delimited.py +0 -460
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/dba.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/herald.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/inspector.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/oracle.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/patcher.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/qa.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/agents/scribe.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/commands/code-oracle.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/commands/migrate.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/commands/review-changes.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/commands/test-module.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/commands/update-changelog.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/commands/where-is.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.claude/state/status.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.pre-commit-config.yaml +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.python-version +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/.readthedocs.yaml +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/CONTRIBUTING.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/LICENSE.txt +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/NOTICE +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/README.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/SECURITY.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/about/contributors.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/about/copyright.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/api/cli.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/api/db.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/api/exporters.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/api/importers.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/api/index.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/api/metacommands.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/dev/architecture.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/getting-started/installation.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/debugging.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/documentation.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/encoding.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/examples.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/formatter.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/logging.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/usage.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/actions.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/actions2.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/checkboxes.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/connect.b64 +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/connect.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/create_conf.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/entry_form.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/execsql_console.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/fatals.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/logo_small.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/unmatched.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/index.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/reference/configuration.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/reference/metacommands.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/reference/security.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/justfile +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/__main__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/cli/help.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/cli/run.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/config.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/constants.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/access.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/base.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/factory.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exceptions.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/format.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/gui/base.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/gui/console.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/importers/base.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/models.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/parser.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/py.typed +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/script/control.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/script/engine.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/script/variables.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/types.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/README.md +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/config_settings.sqlite +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/execsql.conf +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/make_config_db.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/md_compare.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/md_glossary.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/md_upsert.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/pg_compare.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/pg_glossary.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/pg_upsert.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/script_template.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/ss_compare.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/ss_glossary.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/templates/ss_upsert.sql +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/cli/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/cli/test_cli.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/conftest.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/db/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/db/test_factory.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/db/test_postgres.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_base.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_db.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_json.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/gui/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/gui/test_backends.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/importers/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/conftest.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_config.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_config_data.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_constants.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_exceptions.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_format.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_mail.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_models.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_package.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_parser.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_registry.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_script.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/test_types.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/__init__.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_auth.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_errors.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_regex.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_strings.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_timer.py +0 -0
- {execsql2-2.5.0 → execsql2-2.6.0}/tests/utils/test_timer_extra.py +0 -0
|
@@ -189,10 +189,10 @@ Triggered on: push to `main`, any tag `v*.*.*`, pull requests.
|
|
|
189
189
|
|
|
190
190
|
## Versioning
|
|
191
191
|
|
|
192
|
-
`bump-my-version` manages versions. Current: `2.
|
|
192
|
+
`bump-my-version` manages versions. Current: `2.5.0`. Bump commands:
|
|
193
193
|
|
|
194
|
-
- `just bump-patch` → 2.
|
|
195
|
-
- `just bump-minor` → 2.
|
|
194
|
+
- `just bump-patch` → 2.5.0 → 2.5.1
|
|
195
|
+
- `just bump-minor` → 2.5.0 → 2.6.0
|
|
196
196
|
Bumps commit + tag. Pre-commit hook runs `uv lock` + stages `uv.lock`.
|
|
197
197
|
|
|
198
198
|
## Ruff Config
|
|
@@ -243,44 +243,132 @@ the foreseeable future.
|
|
|
243
243
|
| Docs reorganized (getting-started/reference/guides/about) | 2.4.6 | 2026-03 |
|
|
244
244
|
| Pre-commit hook for `execsql-format` | 2.4.3 | 2026-03 |
|
|
245
245
|
| VS Code syntax highlighting extension | 2.4.x | 2026-03 |
|
|
246
|
+
| Docstring coverage 81% on public API | 2.5.0 | 2026-04 |
|
|
247
|
+
| Developer architecture guide with Mermaid diagrams | 2.5.0 | 2026-04 |
|
|
248
|
+
| Cursor context managers (exec_cmd + vacuum) | 2.5.0 | 2026-04 |
|
|
249
|
+
| Exporter Protocol types (QueryExporter, RowsetExporter) | 2.5.0 | 2026-04 |
|
|
250
|
+
| SubVarSet.merge() optimization (O(1) vs O(V)) | 2.5.0 | 2026-04 |
|
|
251
|
+
| GitHub Actions upgraded to Node.js 24 | 2.5.0 | 2026-04 |
|
|
252
|
+
| GitHub issue/PR templates + SECURITY.md | 2.5.0 | 2026-04 |
|
|
253
|
+
| Database ABC already in place (verified) | 2.5.0 | 2026-04 |
|
|
254
|
+
| Dispatch optimization already in place (verified) | 2.5.0 | 2026-04 |
|
|
255
|
+
| PostgreSQL integration tests (9 tests, CI Docker) | 2.5.0 | 2026-04 |
|
|
256
|
+
| MySQL integration tests (9 tests, CI Docker) | 2.5.0 | 2026-04 |
|
|
246
257
|
|
|
247
258
|
______________________________________________________________________
|
|
248
259
|
|
|
249
|
-
### v2.
|
|
260
|
+
### v2.6 — Architecture & Internal Quality
|
|
250
261
|
|
|
251
|
-
|
|
262
|
+
- [x] **`state.py` → `RuntimeContext` refactor** — 33 mutable globals consolidated into a slotted `RuntimeContext` class with transparent module proxy. `get_context()`/`set_context()` API added. Zero external call-site changes.
|
|
263
|
+
- [x] **`noqa` cleanup in `metacommands/__init__.py`** — removed all 180 redundant `# noqa` comments; `__all__` already satisfies ruff F401.
|
|
264
|
+
- [x] **Coverage push to 86%** — 403 new tests (3010 total) covering `db/base.py` (55→99%), `metacommands/connect.py` (36→100%), `script/engine.py` (77→95%), `exporters/delimited.py` (76→95%). Remaining gap to 90% is in GUI, ODS, and import handlers.
|
|
252
265
|
|
|
253
|
-
|
|
266
|
+
### v2.7 — New Export/Import Formats
|
|
254
267
|
|
|
255
|
-
|
|
268
|
+
- [ ] **Parquet import** — complement existing Parquet export and Feather import. Natural fit with DuckDB support.
|
|
269
|
+
- [ ] **YAML export** — popular for config-generation workflows; rounds out the format matrix.
|
|
270
|
+
- [ ] **Markdown (GFM) export** — GitHub-flavored markdown tables. Lightweight and useful for docs/reports.
|
|
271
|
+
- [ ] **Excel (XLSX) multi-sheet export** — multiple queries → multiple named sheets in one workbook, with basic formatting.
|
|
256
272
|
|
|
257
|
-
### v2.
|
|
273
|
+
### v2.8 — Scripting Power Features
|
|
258
274
|
|
|
259
|
-
|
|
275
|
+
- [ ] **`ASSERT` metacommand** — `-- !x! ASSERT <condition> "message"`. Data validation for CI pipelines and sanity checks.
|
|
276
|
+
- [ ] **`--dry-run` improvements** — show SQL with substitution variables expanded, not just raw metacommands.
|
|
277
|
+
- [ ] **Script profiling (`--profile`)** — per-statement execution times, summary report at end. Leverages existing `Timer` infrastructure.
|
|
278
|
+
- [ ] **Parallel execution blocks** — `PARALLEL BEGIN ... PARALLEL END` for independent statements. See design notes below.
|
|
260
279
|
|
|
261
|
-
|
|
262
|
-
- **Cursor context managers** — explicit cursor lifecycle in `execute()`, `select_data()`, `select_rowsource()`
|
|
263
|
-
- **Metacommand dispatch optimization** — consider dict/trie lookup instead of O(N) regex scan
|
|
264
|
-
- **Variable substitution optimization** — reduce O(V×D) complexity
|
|
265
|
-
- **Exporter protocol** — define a `Protocol` or ABC for exporters with a common interface
|
|
280
|
+
### v2.9 — Library API & Developer Experience
|
|
266
281
|
|
|
267
|
-
|
|
282
|
+
- [ ] **Programmatic Python API** — `execsql.run(script, db=...)` for notebook/pipeline usage. Depends on `RuntimeContext` refactor.
|
|
283
|
+
- [ ] **TOML configuration** — `execsql.toml` as modern alternative to legacy INI format (coexist initially).
|
|
284
|
+
|
|
285
|
+
### v2.10 — Testing & CI Hardening
|
|
286
|
+
|
|
287
|
+
- [ ] **Property-based testing (Hypothesis)** — for parsers, type inference, substitution variables.
|
|
288
|
+
- [ ] **Parser fuzzing** — `CondParser` and `NumericParser` handle arbitrary user input; fuzz for edge cases.
|
|
289
|
+
- [ ] **Nightly CI against latest DB driver versions** — catch upstream breakage in psycopg2, pymysql, duckdb, etc.
|
|
290
|
+
- [ ] **CI benchmarks** — track substitution variable and dispatch performance over time.
|
|
291
|
+
|
|
292
|
+
### v2.11 — Documentation & Community
|
|
293
|
+
|
|
294
|
+
- [ ] **Cookbook / recipes page** — real-world examples: ETL workflows, HTML reports, data validation pipelines.
|
|
295
|
+
- [ ] **Migration guide from upstream execsql** — what changed, what's new, how to switch.
|
|
296
|
+
- [ ] **Interactive tutorial** — guided walkthrough script against a bundled SQLite DB.
|
|
268
297
|
|
|
269
298
|
### v3.0+ — Future
|
|
270
299
|
|
|
271
|
-
- **Plugin system** —
|
|
300
|
+
- [ ] **Plugin system** — entry points for `execsql.exporters`, `execsql.importers`, `execsql.metacommands` allowing external packages to register new handlers.
|
|
301
|
+
- [ ] **LSP / language server** — for the VS Code extension: autocomplete metacommands, validate substitution variables, jump-to-definition for `INCLUDE`d scripts.
|
|
272
302
|
|
|
273
303
|
______________________________________________________________________
|
|
274
304
|
|
|
275
305
|
### Ongoing / No-milestone
|
|
276
306
|
|
|
277
|
-
- PostgreSQL integration tests (requires external server — CI docker service)
|
|
278
|
-
- MySQL integration tests (same)
|
|
279
|
-
- `savedscripts` memory pruning
|
|
280
307
|
- Textual TUI polish
|
|
281
308
|
|
|
282
309
|
______________________________________________________________________
|
|
283
310
|
|
|
311
|
+
### Design Notes: Parallel Execution Blocks
|
|
312
|
+
|
|
313
|
+
**Concept:** Allow users to declare groups of independent SQL statements that
|
|
314
|
+
can run concurrently, reducing wall-clock time for ETL scripts with
|
|
315
|
+
independent work.
|
|
316
|
+
|
|
317
|
+
**Syntax:**
|
|
318
|
+
```sql
|
|
319
|
+
-- !x! PARALLEL BEGIN [WORKERS=4]
|
|
320
|
+
INSERT INTO summary_a SELECT ... FROM raw_data;
|
|
321
|
+
INSERT INTO summary_b SELECT ... FROM raw_data;
|
|
322
|
+
INSERT INTO summary_c SELECT ... FROM raw_data;
|
|
323
|
+
-- !x! PARALLEL END
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**How it would work in the engine:**
|
|
327
|
+
|
|
328
|
+
1. When `runscripts()` encounters `PARALLEL BEGIN`, the engine enters a
|
|
329
|
+
"collecting" mode (similar to how `LOOP` compiles commands into a
|
|
330
|
+
`CommandList` before executing). Statements are accumulated but not run.
|
|
331
|
+
|
|
332
|
+
2. On `PARALLEL END`, the collected statements are dispatched to a
|
|
333
|
+
`concurrent.futures.ThreadPoolExecutor` (or `ProcessPoolExecutor`).
|
|
334
|
+
Each statement gets its own database cursor (or connection from
|
|
335
|
+
`DatabasePool`).
|
|
336
|
+
|
|
337
|
+
3. The main `runscripts()` loop blocks at the `PARALLEL END` until all
|
|
338
|
+
futures complete. Errors from any worker are collected and raised as
|
|
339
|
+
a combined `ErrInfo`.
|
|
340
|
+
|
|
341
|
+
**Key constraints and design decisions:**
|
|
342
|
+
|
|
343
|
+
- **No shared mutable state inside parallel blocks.** Substitution variable
|
|
344
|
+
writes (`SET`), `IF/ELSE`, `LOOP`, `INCLUDE`, and other control-flow
|
|
345
|
+
metacommands are **prohibited** inside `PARALLEL` blocks — only raw SQL
|
|
346
|
+
and simple export metacommands are allowed. The parser rejects anything
|
|
347
|
+
else at compile time.
|
|
348
|
+
|
|
349
|
+
- **Connection handling.** Each parallel worker needs its own cursor or
|
|
350
|
+
connection. `DatabasePool` already exists in `db/base.py` but currently
|
|
351
|
+
manages one connection per named alias. This would need a pool-per-alias
|
|
352
|
+
model (e.g., min/max connections) or each worker opens a fresh connection
|
|
353
|
+
from the same DSN.
|
|
354
|
+
|
|
355
|
+
- **Depends on `RuntimeContext` refactor.** The current `state.py` globals
|
|
356
|
+
(especially `commandliststack`, `if_stack`, `subvars`) are not
|
|
357
|
+
thread-safe. Workers would need isolated read-only snapshots of
|
|
358
|
+
substitution variables and their own cursor state. The `RuntimeContext`
|
|
359
|
+
work in v2.6 is a prerequisite.
|
|
360
|
+
|
|
361
|
+
- **`WORKERS=N`** defaults to `min(len(statements), os.cpu_count())`.
|
|
362
|
+
Configurable via the metacommand or `execsql.toml`.
|
|
363
|
+
|
|
364
|
+
- **Transaction semantics.** Each statement runs in its own implicit
|
|
365
|
+
transaction (autocommit). If the user needs atomicity across the whole
|
|
366
|
+
block, they wrap it in `BEGIN BATCH ... END BATCH` outside the parallel
|
|
367
|
+
block — but that negates parallelism for most backends, so this is
|
|
368
|
+
mainly useful for independent ETL loads.
|
|
369
|
+
|
|
370
|
+
______________________________________________________________________
|
|
371
|
+
|
|
284
372
|
## Open Design Questions
|
|
285
373
|
|
|
286
374
|
### Distribution / single-file invocation model
|
|
@@ -13,7 +13,27 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
-
## [2.
|
|
16
|
+
## [2.6.0] - 2026-04-01
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Textual TUI `console_save()` — writes console output to a file, matching Tkinter parity.
|
|
21
|
+
- Keyboard shortcut hints on Textual TUI dialog screens — Escape to cancel, Enter to submit, with `Footer` widget on all major dialog screens.
|
|
22
|
+
- `RuntimeContext` class in `state.py` — groups all 33 mutable runtime globals into a single slotted object. Enables isolated contexts for testing and future concurrent execution.
|
|
23
|
+
- `get_context()` / `set_context()` public API for programmatic access to the active runtime context.
|
|
24
|
+
- Divergence from Upstream documentation page (`docs/about/divergence.md`) listing all user-visible changes since the fork.
|
|
25
|
+
- Test coverage raised from 80% to 86% — 403 new tests across `db/base.py`, `metacommands/connect.py`, `script/engine.py`, and `exporters/delimited.py`.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- `state.py` module now uses a `types.ModuleType` subclass that transparently proxies attribute reads and writes to the active `RuntimeContext` instance. All existing `_state.foo` call sites continue working with zero changes.
|
|
30
|
+
- `reset()` simplified from 40 lines with 7 `global` statements to a clean context replacement (preserving `filewriter`).
|
|
31
|
+
- `initialize()` and `endloop()` rewritten to use `_ctx` directly instead of `global` statements.
|
|
32
|
+
- Removed 180 redundant `# noqa` suppressions from `metacommands/__init__.py` — the existing `__all__` already satisfies ruff F401.
|
|
33
|
+
|
|
34
|
+
______________________________________________________________________
|
|
35
|
+
|
|
36
|
+
## [2.5.0] - 2026-04-01
|
|
17
37
|
|
|
18
38
|
### Added
|
|
19
39
|
|
|
@@ -51,4 +51,5 @@ A multi-agent system where specialized agents collaborate to improve, extend, de
|
|
|
51
51
|
- All code must pass `ruff check` and target Python 3.10+
|
|
52
52
|
- **Every user-visible change must be reflected in `CHANGELOG.md`** under the `[Unreleased]` section using [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) categories: Added, Changed, Fixed, Removed. Do not leave changelog updates for later — include them in the same commit or PR as the code change.
|
|
53
53
|
- **Every user-visible change must also update documentation** — review and revise `README.md`, `docs/`, and any other relevant documentation to stay consistent with the code. New CLI options, features, or behavior changes must be reflected in the README Options table, feature list, and the corresponding docs page. Do not leave doc updates for later. Docs are organized into subdirectories: `docs/getting-started/`, `docs/reference/` (configuration, metacommands, substitution_vars, security), `docs/guides/`, `docs/about/`, `docs/api/`, `docs/dev/`.
|
|
54
|
+
- **Every change that diverges from upstream execsql v1.130.1 must update `docs/about/divergence.md`** — this includes new features, changed behavior, security fixes, and removed functionality. The divergence page is the canonical record of how execsql2 differs from the original monolith. Keep entries concise and organized under the existing section headings (Added Features, Changed Behavior, Security and Correctness Fixes, Removed Features).
|
|
54
55
|
- **After every version bump and push, monitor CI** — push with `git push && git push --tags` to include version tags, then run `gh run list --limit 1` to get the run ID, then `gh run watch <id> --exit-status` to block until it completes. Bump commits trigger PyPI publish and GitHub Release, so failures must be caught and fixed immediately.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
|
|
5
5
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
6
6
|
Project-URL: Issues, https://github.com/geocoug/execsql/issues
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Divergence from Upstream
|
|
2
|
+
|
|
3
|
+
execsql2 is a maintained fork of [execsql](https://execsql.readthedocs.io/)
|
|
4
|
+
v1.130.1 by R. Dreas Nielsen. This page documents all user-visible changes
|
|
5
|
+
since the fork was created: new features, changed behavior, security fixes,
|
|
6
|
+
and removed functionality.
|
|
7
|
+
|
|
8
|
+
For a chronological view, see the [Change Log](change_log.md).
|
|
9
|
+
|
|
10
|
+
______________________________________________________________________
|
|
11
|
+
|
|
12
|
+
## Added Features
|
|
13
|
+
|
|
14
|
+
### CLI Options
|
|
15
|
+
|
|
16
|
+
| Flag | Description |
|
|
17
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
18
|
+
| `--version` | Print version and exit (Rich-formatted). |
|
|
19
|
+
| `-c` / `--command` | Execute an inline SQL or metacommand string instead of a script file. |
|
|
20
|
+
| `--dsn` / `--connection-string` | Accept a standard database URL (e.g. `postgresql://user:pass@host/db`). Supports `postgresql`, `mysql`, `mssql`, `oracle`, `firebird`, `sqlite`, and `duckdb` schemes. |
|
|
21
|
+
| `--output-dir` | Set a default base directory for export output files. |
|
|
22
|
+
| `--progress` | Show a Rich progress bar during long-running IMPORT operations. |
|
|
23
|
+
| `--dump-keywords` | Emit all metacommand keywords, conditionals, config options, and export formats as structured JSON. |
|
|
24
|
+
| `--gui-framework` | Select GUI backend: `tkinter` (default) or `textual` (terminal UI). |
|
|
25
|
+
| `--dry-run` | Parse the script and print the full command list without connecting to a database or executing anything. |
|
|
26
|
+
|
|
27
|
+
### Export Formats
|
|
28
|
+
|
|
29
|
+
| Format | Description |
|
|
30
|
+
| --------- | ------------------------------------------------------------------------------- |
|
|
31
|
+
| `PARQUET` | Export query or table results to Apache Parquet via `polars`. |
|
|
32
|
+
| `FEATHER` | Export to Apache Feather/IPC via `polars` + `pyarrow` (upstream used `pandas`). |
|
|
33
|
+
|
|
34
|
+
### Metacommands
|
|
35
|
+
|
|
36
|
+
| Metacommand | Description |
|
|
37
|
+
| ---------------------- | --------------------------------------------------------------------- |
|
|
38
|
+
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime. |
|
|
39
|
+
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file. |
|
|
40
|
+
|
|
41
|
+
### Configuration Options
|
|
42
|
+
|
|
43
|
+
New options in `execsql.conf`:
|
|
44
|
+
|
|
45
|
+
| Option | Section | Description |
|
|
46
|
+
| -------------------------- | ----------- | ----------------------------------------------------------------- |
|
|
47
|
+
| `use_keyring` | `[connect]` | Use the OS keyring for credential storage (default: `yes`). |
|
|
48
|
+
| `show_progress` | `[input]` | Enable Rich progress bar for IMPORT (default: `no`). |
|
|
49
|
+
| `import_progress_interval` | `[input]` | Log a status line every N rows during IMPORT (default: `0`). |
|
|
50
|
+
| `log_sql` | `[config]` | Enable SQL audit logging (default: `no`). |
|
|
51
|
+
| `max_log_size_mb` | `[config]` | Rotate the log file at this size in MB (default: `0` = disabled). |
|
|
52
|
+
|
|
53
|
+
### Tools
|
|
54
|
+
|
|
55
|
+
| Tool | Description |
|
|
56
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `execsql-format` | Standalone CLI for normalizing metacommand indentation and uppercasing SQL keywords. Supports `--check` and `--in-place` modes. Also available as a [pre-commit hook](../guides/formatter.md). |
|
|
58
|
+
|
|
59
|
+
### GUI
|
|
60
|
+
|
|
61
|
+
| Feature | Description |
|
|
62
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
63
|
+
| Textual TUI backend | Full terminal-UI backend via the `textual` library. Provides all dialog types (password, pause, message, entry, compare, action, etc.) in the terminal. |
|
|
64
|
+
| Console fallback | Text-only backend that handles GUI calls in headless environments by printing to stdout. |
|
|
65
|
+
|
|
66
|
+
### Authentication
|
|
67
|
+
|
|
68
|
+
| Feature | Description |
|
|
69
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
70
|
+
| OS keyring integration | When the `keyring` package is installed, passwords are stored in and retrieved from the OS credential store (macOS Keychain, Windows Credential Manager, Linux SecretService). |
|
|
71
|
+
| Keyring retry on auth failure | If a stored password is rejected, the stale entry is deleted, the user is re-prompted, and the new password is saved automatically. |
|
|
72
|
+
|
|
73
|
+
### Logging Enhancements
|
|
74
|
+
|
|
75
|
+
| Feature | Description |
|
|
76
|
+
| ----------------------------- | ---------------------------------------------------------------------------------- |
|
|
77
|
+
| Per-event ISO 8601 timestamps | `status`, `connect`, `action`, and `user_msg` log entries include a timestamp. |
|
|
78
|
+
| Run duration in exit record | The `exit` log record includes elapsed wall-clock time. |
|
|
79
|
+
| Run ID millisecond precision | Run identifier format changed from `%Y%m%d_%H%M_%S` to `%Y%m%d_%H%M_%S_NNN`. |
|
|
80
|
+
| SQL audit log record | New `sql` record type containing DB name, line number, and query text. |
|
|
81
|
+
| Import progress log | Periodic row-count status lines during IMPORT when `import_progress_interval > 0`. |
|
|
82
|
+
|
|
83
|
+
### Developer / Packaging
|
|
84
|
+
|
|
85
|
+
| Feature | Description |
|
|
86
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| VS Code syntax highlighting | Auto-generated `tmLanguage.json` grammar from the dispatch table. |
|
|
88
|
+
| `py.typed` marker | PEP 561 marker enabling downstream static type checking. |
|
|
89
|
+
| Structured keyword registry | `--dump-keywords` introspects the dispatch table and outputs JSON used by the grammar generator and test suite. |
|
|
90
|
+
|
|
91
|
+
______________________________________________________________________
|
|
92
|
+
|
|
93
|
+
## Changed Behavior
|
|
94
|
+
|
|
95
|
+
### CLI Interface
|
|
96
|
+
|
|
97
|
+
The CLI framework changed from `optparse` to [Typer](https://typer.tiangolo.com/) with Rich-formatted help text. All original short flags (`-a` through `-z`) are preserved. The tool can be invoked as either `execsql` or `execsql2`.
|
|
98
|
+
|
|
99
|
+
### Internal State Management
|
|
100
|
+
|
|
101
|
+
All 33 mutable runtime globals in `state.py` have been consolidated into a `RuntimeContext` object. The module uses a transparent proxy so existing code is unaffected, but the architecture now supports isolated contexts for testing and future concurrent execution.
|
|
102
|
+
|
|
103
|
+
### Substitution Variables
|
|
104
|
+
|
|
105
|
+
- **Cycle detection** — `substitute_vars()` raises an error after 100 iterations to prevent infinite loops when variables reference each other cyclically. Upstream had no protection.
|
|
106
|
+
- **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.
|
|
107
|
+
|
|
108
|
+
### Database Adapters
|
|
109
|
+
|
|
110
|
+
- **`Database` is an ABC** — `open_db()` and `exec_cmd()` are abstract methods. Subclasses that omit them raise `TypeError` at instantiation instead of at call time.
|
|
111
|
+
- **Connection timeouts** — PostgreSQL and SQLite adapters accept a connection timeout parameter (default 30 seconds).
|
|
112
|
+
- **DuckDB temporal types** — `TIMESTAMPTZ`, `TIMESTAMP`, `DATE`, `TIME` now map to native DuckDB types instead of `TEXT`.
|
|
113
|
+
|
|
114
|
+
### Error Handling
|
|
115
|
+
|
|
116
|
+
- **Exception hierarchy** — All custom exceptions inherit from `ExecSqlError`, enabling `except ExecSqlError` to catch any execsql-originated error.
|
|
117
|
+
- **Exception chaining** — All `raise` statements inside `except` blocks preserve the original traceback via `from`.
|
|
118
|
+
|
|
119
|
+
______________________________________________________________________
|
|
120
|
+
|
|
121
|
+
## Security and Correctness Fixes
|
|
122
|
+
|
|
123
|
+
These are behavioral changes driven by security or correctness issues in the upstream code.
|
|
124
|
+
|
|
125
|
+
### Injection Fixes
|
|
126
|
+
|
|
127
|
+
| Area | Fix |
|
|
128
|
+
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
129
|
+
| Database metadata queries | `schema_exists()`, `table_exists()`, `column_exists()`, `table_columns()`, `view_exists()`, `role_exists()` across all 9 adapters now use parameterized queries. Upstream used string interpolation. |
|
|
130
|
+
| `import_entire_file()` | Column names are quoted with `quote_identifier()` instead of interpolated into INSERT statements. |
|
|
131
|
+
| PostgreSQL `CREATE DATABASE` | Database name and encoding are quoted. COPY delimiter and quote character are validated. |
|
|
132
|
+
| `$SHEETS_TABLES_VALUES` | Sheet names from ODS/XLS imports are escaped before embedding in SQL. |
|
|
133
|
+
| HTTP `Content-Disposition` | Filename is sanitized to prevent HTTP response splitting in SERVE. |
|
|
134
|
+
|
|
135
|
+
### Template and Export Safety
|
|
136
|
+
|
|
137
|
+
| Area | Fix |
|
|
138
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------ |
|
|
139
|
+
| Jinja2 sandboxing | Templates run in `SandboxedEnvironment` instead of the default `jinja2.Template`. |
|
|
140
|
+
| HTML export | Column headers and cell values are escaped with `html.escape()` to prevent XSS. |
|
|
141
|
+
| XML export | Values are escaped with `xml.sax.saxutils.escape()`. Invalid XML element name characters are replaced. |
|
|
142
|
+
| JSON export | The `description` field uses `json.dumps()` instead of string interpolation. |
|
|
143
|
+
|
|
144
|
+
### Credential and Logging Safety
|
|
145
|
+
|
|
146
|
+
| Area | Fix |
|
|
147
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------ |
|
|
148
|
+
| ODBC password redaction | Connection strings in log output have `Pwd=***` substituted before logging. |
|
|
149
|
+
| `enc_password` documentation | Prominent warnings that XOR encryption is obfuscation only — keys are hardcoded in source. |
|
|
150
|
+
|
|
151
|
+
### Bug Fixes
|
|
152
|
+
|
|
153
|
+
| Area | Fix |
|
|
154
|
+
| --------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| Oracle default port | Corrected from `5432` (PostgreSQL) to `1521`. |
|
|
156
|
+
| MySQL `LOAD DATA INFILE` encoding | Python encoding names are now mapped to MySQL charset names. |
|
|
157
|
+
| `dt_cast` type converters | Base `Database` class auto-populates 8 type converters that were previously left empty after the refactor. |
|
|
158
|
+
| `FileWriter` CPU busy-loop | Uses blocking `queue.get(timeout=0.1)` instead of `get_nowait()` in a tight loop. |
|
|
159
|
+
| Substitution variable cycles | 100-iteration limit prevents infinite loops on cyclic variable references. |
|
|
160
|
+
|
|
161
|
+
______________________________________________________________________
|
|
162
|
+
|
|
163
|
+
## Removed Features
|
|
164
|
+
|
|
165
|
+
| Feature | Reason |
|
|
166
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
167
|
+
| Airspeed template processor | The `airspeed` library (Velocity clone) is unmaintained since ~2018. Use `FORMAT jinja` instead. The `airspeed` value for `template_processor` in `execsql.conf` is no longer accepted. |
|
|
168
|
+
| Python 2 compatibility | All Python 2 constructs (`stringtypes`, `u""` literals, `optparse`, etc.) have been removed. execsql2 requires Python 3.10+. |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.6.0"
|
|
8
8
|
description = "Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = { file = "LICENSE.txt" }
|
|
@@ -147,6 +147,10 @@ ignore = [
|
|
|
147
147
|
"UP017", # Use datetime.UTC alias (not available in Python 3.10)
|
|
148
148
|
]
|
|
149
149
|
|
|
150
|
+
[tool.ruff.lint.per-file-ignores]
|
|
151
|
+
# F822: context attrs are proxied via __getattr__/__setattr__ on the module class, not defined as module-level variables
|
|
152
|
+
"src/execsql/state.py" = ["F822"]
|
|
153
|
+
|
|
150
154
|
[tool.ruff.format]
|
|
151
155
|
quote-style = "double"
|
|
152
156
|
indent-style = "space"
|
|
@@ -154,7 +158,7 @@ skip-magic-trailing-comma = false
|
|
|
154
158
|
line-ending = "auto"
|
|
155
159
|
|
|
156
160
|
[tool.bumpversion]
|
|
157
|
-
current_version = "2.
|
|
161
|
+
current_version = "2.6.0"
|
|
158
162
|
commit = true
|
|
159
163
|
commit_args = "--no-verify"
|
|
160
164
|
tag = true
|
|
@@ -178,7 +182,7 @@ addopts = [
|
|
|
178
182
|
"--cov=execsql",
|
|
179
183
|
"--cov-branch",
|
|
180
184
|
"--cov-report=xml",
|
|
181
|
-
"--cov-fail-under=
|
|
185
|
+
"--cov-fail-under=85",
|
|
182
186
|
"--strict-markers",
|
|
183
187
|
"--strict-config",
|
|
184
188
|
"--color=yes",
|