execsql2 2.11.1__tar.gz → 2.12.1__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.11.1 → execsql2-2.12.1}/.claude/project_context.md +3 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/CHANGELOG.md +34 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/CLAUDE.md +2 -2
- {execsql2-2.11.1 → execsql2-2.12.1}/PKG-INFO +1 -1
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/about/divergence.md +48 -7
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/debugging.md +41 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/pyproject.toml +3 -3
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/cli/__init__.py +6 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/cli/run.py +15 -10
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/base.py +37 -23
- execsql2-2.12.1/src/execsql/debug/__init__.py +6 -0
- execsql2-2.12.1/src/execsql/debug/repl.py +472 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/__init__.py +1 -1
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/dispatch.py +1 -1
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/script/engine.py +15 -6
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/script/variables.py +2 -25
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/test_profile.py +52 -1
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/test_base.py +5 -5
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/gui/test_backends.py +422 -0
- execsql2-2.12.1/tests/importers/test_csv_importer.py +436 -0
- execsql2-2.12.1/tests/metacommands/test_breakpoint.py +980 -0
- execsql2-2.12.1/tests/metacommands/test_io_export.py +1377 -0
- execsql2-2.12.1/tests/metacommands/test_io_import.py +1585 -0
- execsql2-2.12.1/tests/metacommands/test_metacommands_data.py +802 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_extended.py +361 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_engine.py +3 -4
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_script.py +12 -16
- {execsql2-2.11.1 → execsql2-2.12.1}/uv.lock +1 -1
- execsql2-2.11.1/src/execsql/metacommands/debug_repl.py +0 -289
- execsql2-2.11.1/tests/importers/test_csv_importer.py +0 -193
- execsql2-2.11.1/tests/metacommands/test_breakpoint.py +0 -524
- execsql2-2.11.1/tests/metacommands/test_metacommands_data.py +0 -381
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/dba.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/herald.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/inspector.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/oracle.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/patcher.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/qa.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/agents/scribe.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/commands/code-oracle.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/commands/migrate.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/commands/review-changes.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/commands/test-module.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/commands/update-changelog.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/commands/where-is.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.claude/state/status.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.gitignore +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.pre-commit-config.yaml +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.python-version +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/.readthedocs.yaml +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/CONTRIBUTING.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/LICENSE.txt +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/NOTICE +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/README.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/SECURITY.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/about/contributors.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/about/copyright.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/api/cli.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/api/db.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/api/exporters.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/api/importers.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/api/index.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/api/metacommands.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/dev/architecture.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/getting-started/installation.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/documentation.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/encoding.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/examples.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/formatter.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/logging.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/usage.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/actions.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/actions2.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/checkboxes.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/connect.b64 +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/connect.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/create_conf.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/entry_form.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/execsql_console.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/fatals.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/logo_small.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/unmatched.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/index.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/reference/configuration.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/reference/metacommands.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/reference/security.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/justfile +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/__main__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/cli/help.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/config.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/constants.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/access.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/factory.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exceptions.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/format.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/gui/base.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/gui/console.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/importers/base.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/models.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/parser.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/py.typed +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/script/control.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/state.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/types.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/README.md +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/config_settings.sqlite +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/execsql.conf +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/make_config_db.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/md_compare.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/md_glossary.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/md_upsert.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/pg_compare.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/pg_glossary.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/pg_upsert.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/script_template.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/ss_compare.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/ss_glossary.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/templates/ss_upsert.sql +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/test_cli.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/test_lint.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/cli/test_ping.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/conftest.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/test_factory.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/test_postgres.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_base.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_db.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_json.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/gui/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/importers/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/conftest.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_config.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_config_data.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_constants.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_error_messages.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_exceptions.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_format.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_mail.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_models.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_package.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_parser.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_registry.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_state.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/test_types.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/__init__.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_auth.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_errors.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_regex.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_strings.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_timer.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.11.1 → execsql2-2.12.1}/zensical.toml +0 -0
|
@@ -67,6 +67,9 @@ src/execsql/ # active codebase — all new work goes here
|
|
|
67
67
|
importers/
|
|
68
68
|
__init__.py
|
|
69
69
|
base.py / csv.py / ods.py / xls.py / feather.py
|
|
70
|
+
debug/
|
|
71
|
+
__init__.py # debug package
|
|
72
|
+
repl.py # x_breakpoint, _debug_repl, REPL helpers (BREAKPOINT metacommand)
|
|
70
73
|
metacommands/
|
|
71
74
|
__init__.py # DISPATCH_TABLE, format/db constants, re-exports all handlers
|
|
72
75
|
dispatch.py # build_dispatch_table() — all mcl.add() regex registrations
|
|
@@ -13,6 +13,40 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.12.1] - 2026-04-02
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Performance: removed dead `_compiled_patterns` dict from `SubVarSet` — eliminated 3 unused regex compilations per `add_substitution` call (~20 calls per statement in typical scripts).
|
|
21
|
+
- Performance: cached `source_dir` and `source_name` on `ScriptCmd` at construction time — eliminated per-statement `Path.resolve()` filesystem calls.
|
|
22
|
+
- Performance: `select_rowdict()` now uses batched `fetchmany()` instead of row-at-a-time `fetchone()`, matching `select_rowsource()` behavior for template exports.
|
|
23
|
+
- Performance: removed redundant `$CURRENT_TIME` set in `set_system_vars()` — now set once per statement in `run_and_increment()`.
|
|
24
|
+
- Performance: removed no-op `copy.copy()` on immutable string in `substitute_vars()`.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Fixed cursor leak in `select_rowsource()` — generator now closes the cursor in a `finally` block when exhausted or abandoned.
|
|
29
|
+
|
|
30
|
+
______________________________________________________________________
|
|
31
|
+
|
|
32
|
+
## [2.12.0] - 2026-04-01
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Debug REPL `.where` / `.w` command — shows current script file, line number, and the upcoming statement text (truncated to 120 chars). The entry banner now includes the location (`[Breakpoint] myscript.sql:42`) and `_print_where()` is called automatically on REPL entry via `BREAKPOINT` or step mode.
|
|
37
|
+
- Debug REPL `.set VAR VAL` / `.s VAR VAL` command — sets or updates a substitution variable interactively during a `BREAKPOINT` session. Prints a confirmation line (`VAR = VAL`) on success; prints an error if substitution variables are not initialised.
|
|
38
|
+
- Debug REPL ANSI color output — horizontal rule separators, colored labels ("Breakpoint"/"Step" in bold yellow, filename:line in cyan, type tags in dim green), cyan variable names, dim `=` signs, red error messages, bold SQL column headers, and dim row-count and table borders. Color is auto-detected via TTY and suppressed when `NO_COLOR` or `EXECSQL_NO_COLOR` environment variables are set. Falls back to plain text in non-interactive contexts (CI, piped output). Help text is also colorized with cyan command names and consistent column alignment.
|
|
39
|
+
- Debug REPL shortcut aliases — `.h` for `.help`, `.v` for `.vars`, `.v all` for `.vars all`.
|
|
40
|
+
- Debug REPL step mode banner — when the REPL is re-entered via `.next` / step mode, the entry banner now shows "Step" instead of "Breakpoint" to make it clear the pause is from stepping rather than an explicit `BREAKPOINT` metacommand.
|
|
41
|
+
- `--profile-limit N` CLI option — controls how many top statements appear in the `--profile` timing summary (default: 20). The "not shown" footer message now includes the active limit for clarity.
|
|
42
|
+
- Test coverage raised from 86% to 91% — 274 new tests across `metacommands/io_import.py`, `metacommands/io_export.py`, `metacommands/control.py`, `metacommands/data.py`, `importers/csv.py`, and `gui/console.py`. Coverage floor raised from 85% to 90% in `pyproject.toml`.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- `execsql.debug.repl` is now a dedicated package (`src/execsql/debug/repl.py`); previously the REPL lived at `execsql.metacommands.debug_repl`. Internal import paths have been updated throughout. No public API change.
|
|
47
|
+
|
|
48
|
+
______________________________________________________________________
|
|
49
|
+
|
|
16
50
|
## [2.11.1] - 2026-04-01
|
|
17
51
|
|
|
18
52
|
### Fixed
|
|
@@ -37,13 +37,13 @@ A multi-agent system where specialized agents collaborate to improve, extend, de
|
|
|
37
37
|
1. **Research** — Oracle investigates codebase, finds relevant code paths and impact
|
|
38
38
|
1. **Plan** — DBA synthesizes research into implementation approach, aligns with human
|
|
39
39
|
1. **Implement** — Patcher writes code, Oracle advises on architecture
|
|
40
|
-
1. **Test** — QA writes/runs tests, verifies coverage stays above
|
|
40
|
+
1. **Test** — QA writes/runs tests, verifies coverage stays above 90%
|
|
41
41
|
1. **Document** — Scribe updates docs, Herald updates changelog
|
|
42
42
|
1. **Review** — Inspector does final code review before human merge
|
|
43
43
|
|
|
44
44
|
## Constraints
|
|
45
45
|
|
|
46
|
-
- Coverage floor (
|
|
46
|
+
- Coverage floor (90%) must be maintained — QA blocks any change that drops it
|
|
47
47
|
- Backwards compatibility with upstream execsql v1.130.1 unless explicitly approved
|
|
48
48
|
- No destructive git operations without human approval
|
|
49
49
|
- Agents should always read `.claude/project_context.md` before starting work
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.1
|
|
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
|
|
@@ -24,7 +24,8 @@ ______________________________________________________________________
|
|
|
24
24
|
| `--gui-framework` | Select GUI backend: `tkinter` (default) or `textual` (terminal UI). |
|
|
25
25
|
| `--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
26
|
| `--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. |
|
|
27
|
-
| `--profile` | Record wall-clock time for each SQL and metacommand statement. After the script completes, print a summary table sorted by elapsed time (descending), showing time, percentage of total, source location, command type, and a command preview.
|
|
27
|
+
| `--profile` | Record wall-clock time for each SQL and metacommand statement. After the script completes, print a summary table sorted by elapsed time (descending), showing time, percentage of total, source location, command type, and a command preview. |
|
|
28
|
+
| `--profile-limit N` | Number of top statements to display in the `--profile` summary (default: 20). Remaining statements are counted and noted in the output footer. |
|
|
28
29
|
| `--ping` | Test database connectivity and exit. Connects using the supplied connection parameters, queries for the server version, and prints a one-line success message (exit 0) or the error (exit 1). No script file argument is required. |
|
|
29
30
|
| `--lint` | Parse the script and perform static analysis without connecting to a database. Reports unmatched IF/ENDIF, LOOP/END LOOP, and BEGIN BATCH/END BATCH blocks (errors), potentially undefined `!!$VAR!!` references (warnings), and missing INCLUDE file targets (warnings). Exits 0 if no errors, 1 if errors found. |
|
|
30
31
|
|
|
@@ -40,12 +41,12 @@ ______________________________________________________________________
|
|
|
40
41
|
|
|
41
42
|
### Metacommands
|
|
42
43
|
|
|
43
|
-
| Metacommand | Description
|
|
44
|
-
| ---------------------- |
|
|
45
|
-
| `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.
|
|
46
|
-
| `BREAKPOINT` | Pause script execution and drop into an interactive debug REPL.
|
|
47
|
-
| `CONFIG SHOW_PROGRESS` | Enable the Rich progress bar for IMPORT operations at runtime.
|
|
48
|
-
| `CONFIG LOG_SQL` | Enable SQL query audit logging — writes executed SQL to the log file.
|
|
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. |
|
|
49
50
|
|
|
50
51
|
### Conditional Tests
|
|
51
52
|
|
|
@@ -106,6 +107,46 @@ New options in `execsql.conf`:
|
|
|
106
107
|
| `py.typed` marker | PEP 561 marker enabling downstream static type checking. |
|
|
107
108
|
| Structured keyword registry | `--dump-keywords` introspects the dispatch table and outputs JSON used by the grammar generator and test suite. |
|
|
108
109
|
|
|
110
|
+
### Debugging { #debugging }
|
|
111
|
+
|
|
112
|
+
execsql2 adds a full interactive debugging system that has no equivalent in upstream execsql.
|
|
113
|
+
|
|
114
|
+
**`BREAKPOINT` metacommand** — insert `-- !x! BREAKPOINT` anywhere in a script to pause execution and drop into a debug REPL. The REPL provides a `execsql debug>` prompt where you can inspect state and interact with the database before resuming.
|
|
115
|
+
|
|
116
|
+
**`--debug` CLI flag** — start the script in step-through mode, pausing before every statement as if `BREAKPOINT` were inserted at the top with `.next` always active.
|
|
117
|
+
|
|
118
|
+
**REPL commands** (all dot-prefixed to avoid collisions with variable names and SQL):
|
|
119
|
+
|
|
120
|
+
| Command | Description |
|
|
121
|
+
| ---------------------- | ------------------------------------------------------------------------- |
|
|
122
|
+
| `.continue` / `.c` | Resume normal script execution |
|
|
123
|
+
| `.abort` / `.q` | Halt the script with exit status 1 |
|
|
124
|
+
| `.vars` / `.v` | List all user, system, local, and counter variables (grouped by type) |
|
|
125
|
+
| `.vars all` / `.v all` | Include environment variables (`&`) in the listing |
|
|
126
|
+
| `.next` / `.n` | Execute the next statement, then pause again (step mode) |
|
|
127
|
+
| `.where` / `.w` | Show the current script file, line number, and upcoming statement text |
|
|
128
|
+
| `.stack` | Show the command-list stack (script name, cursor position, nesting depth) |
|
|
129
|
+
| `.set VAR VAL` / `.s` | Set or update a substitution variable; prints confirmation on success |
|
|
130
|
+
| `.help` / `.h` | Show available commands |
|
|
131
|
+
|
|
132
|
+
**Non-prefixed input** is interpreted as either a variable lookup or ad-hoc SQL:
|
|
133
|
+
|
|
134
|
+
| Input | Behavior |
|
|
135
|
+
| ------------------------------ | -------------------------------------------------------------------------------- |
|
|
136
|
+
| `logfile` | Print the value of the `logfile` substitution variable |
|
|
137
|
+
| `$ARG_1` | Print the value of a system/built-in variable |
|
|
138
|
+
| `SELECT count(*) FROM orders;` | Execute SQL against the current database and pretty-print the results in a table |
|
|
139
|
+
|
|
140
|
+
**Key behaviors:**
|
|
141
|
+
|
|
142
|
+
- **Non-interactive safety** — `BREAKPOINT` is silently skipped when `sys.stdin` is not a TTY (CI pipelines, piped input, cron). Scripts never hang in automation.
|
|
143
|
+
- **Ad-hoc SQL** — any input ending with `;` is executed against the current database connection using the same cursor and transaction state as the script. Queries return a formatted table; DML statements (INSERT, UPDATE, DELETE) execute and commit/rollback according to the current autocommit and batch settings.
|
|
144
|
+
- **Variable inspection** — bare names look up user-defined variables (e.g., `logfile`); sigil-prefixed names look up system (`$`), environment (`&`), local (`~`), or counter (`@`) variables. If a `$`-prefixed name isn't found, the REPL strips the sigil and retries (since `SUB` stores keys without a prefix).
|
|
145
|
+
- **Step mode** — `.next` executes exactly one statement then re-enters the REPL. When stepping, the entry banner shows "Step" instead of "Breakpoint" to distinguish stepping from an explicit `BREAKPOINT`. Combined with `.where`, `.vars`, and SQL queries, this allows line-by-line script debugging with full state visibility.
|
|
146
|
+
- **Location display** — on entry to the REPL (via `BREAKPOINT` or step mode) the banner shows a horizontal rule with the label ("Breakpoint" or "Step"), the current filename and line number, and the upcoming statement. Use `.where` (or `.w`) at any time to re-display this information.
|
|
147
|
+
- **ANSI color output** — the REPL uses ANSI color on TTY outputs: bold yellow for section labels, cyan for filenames and variable names, dim for separators and `=` signs, red for error messages, bold for SQL column headers, and dim italic for `NULL` values. Color is suppressed when `NO_COLOR` or `EXECSQL_NO_COLOR` environment variables are set, or when the output stream is not a TTY.
|
|
148
|
+
- **Readline support** — on platforms where `readline` is available (macOS, Linux), the REPL supports arrow-key history navigation and line editing.
|
|
149
|
+
|
|
109
150
|
______________________________________________________________________
|
|
110
151
|
|
|
111
152
|
## Changed Behavior
|
|
@@ -38,6 +38,47 @@ DEBUG WRITE <script_name> [[APPEND] TO <filename>]
|
|
|
38
38
|
|
|
39
39
|
This is an alias for the [WRITE SCRIPT](../reference/metacommands.md#write_script) metacommand.
|
|
40
40
|
|
|
41
|
+
# Interactive Debug REPL (BREAKPOINT)
|
|
42
|
+
|
|
43
|
+
Insert `-- !x! BREAKPOINT` anywhere in a script to pause execution and drop into the interactive debug REPL:
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- !x! BREAKPOINT
|
|
47
|
+
SELECT * FROM orders WHERE status = 'pending';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The REPL prints the current file name, line number, and the upcoming statement when it opens:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
[Breakpoint] myscript.sql:42 — Script paused. Type '.help' for commands, '.c' to resume.
|
|
54
|
+
myscript.sql:42 (sql)
|
|
55
|
+
→ SELECT * FROM orders WHERE status = 'pending';
|
|
56
|
+
execsql debug>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Available commands:**
|
|
60
|
+
|
|
61
|
+
| Command | Shortcut | Description |
|
|
62
|
+
| -------------- | -------- | ------------------------------------------------------------------------- |
|
|
63
|
+
| `.continue` | `.c` | Resume normal script execution |
|
|
64
|
+
| `.abort` | `.q` | Halt the script with exit status 1 |
|
|
65
|
+
| `.vars` | `.v` | List user, system, local, and counter substitution variables |
|
|
66
|
+
| `.vars all` | `.v all` | Include environment variables (`&`) in the listing |
|
|
67
|
+
| `.next` | `.n` | Execute the next statement, then pause again (step mode) |
|
|
68
|
+
| `.where` | `.w` | Re-display the current script location and upcoming statement |
|
|
69
|
+
| `.stack` | | Show the command-list stack (script name, cursor position, nesting depth) |
|
|
70
|
+
| `.set VAR VAL` | `.s` | Set or update a substitution variable |
|
|
71
|
+
| `.help` | `.h` | Show available commands |
|
|
72
|
+
|
|
73
|
+
Anything not starting with `.` is treated as a variable lookup or SQL:
|
|
74
|
+
|
|
75
|
+
- A bare name (e.g. `logfile`) prints the value of that substitution variable.
|
|
76
|
+
- Any input ending with `;` is executed as SQL against the current database (expects columns returned, e.g. SELECT).
|
|
77
|
+
|
|
78
|
+
The `--debug` CLI flag starts execution in step mode, pausing before every statement.
|
|
79
|
+
|
|
80
|
+
In non-interactive environments (CI, piped input) `BREAKPOINT` is silently skipped so automated pipelines are never blocked.
|
|
81
|
+
|
|
41
82
|
The ON ERROR_HALT metacommands allow custom reporting (or cleanup) actions to be taken when errors occur.
|
|
42
83
|
|
|
43
84
|
Setting the configuration setting [write_warnings](../reference/configuration.md#write_warnings) to "Yes" can also assist with debugging by displaying conditions that may result from errors in the script.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.12.1"
|
|
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" }
|
|
@@ -158,7 +158,7 @@ skip-magic-trailing-comma = false
|
|
|
158
158
|
line-ending = "auto"
|
|
159
159
|
|
|
160
160
|
[tool.bumpversion]
|
|
161
|
-
current_version = "2.
|
|
161
|
+
current_version = "2.12.1"
|
|
162
162
|
commit = true
|
|
163
163
|
commit_args = "--no-verify"
|
|
164
164
|
tag = true
|
|
@@ -182,7 +182,7 @@ addopts = [
|
|
|
182
182
|
"--cov=execsql",
|
|
183
183
|
"--cov-branch",
|
|
184
184
|
"--cov-report=xml",
|
|
185
|
-
"--cov-fail-under=
|
|
185
|
+
"--cov-fail-under=90",
|
|
186
186
|
"--strict-markers",
|
|
187
187
|
"--strict-config",
|
|
188
188
|
"--color=yes",
|
|
@@ -278,6 +278,11 @@ def main(
|
|
|
278
278
|
"--profile",
|
|
279
279
|
help="Record per-statement execution times and print a timing summary after the script completes.",
|
|
280
280
|
),
|
|
281
|
+
profile_limit: int = typer.Option(
|
|
282
|
+
20,
|
|
283
|
+
"--profile-limit",
|
|
284
|
+
help="Number of top statements to show in the --profile timing summary (default: 20).",
|
|
285
|
+
),
|
|
281
286
|
debug: bool = typer.Option(
|
|
282
287
|
False,
|
|
283
288
|
"--debug",
|
|
@@ -449,6 +454,7 @@ def main(
|
|
|
449
454
|
output_dir=output_dir,
|
|
450
455
|
progress=progress,
|
|
451
456
|
profile=profile,
|
|
457
|
+
profile_limit=profile_limit,
|
|
452
458
|
ping=ping,
|
|
453
459
|
lint=lint,
|
|
454
460
|
debug=debug,
|
|
@@ -71,12 +71,14 @@ def _print_dry_run(cmdlist: object) -> None:
|
|
|
71
71
|
# ---------------------------------------------------------------------------
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def _print_profile(profile_data: list[tuple]) -> None:
|
|
74
|
+
def _print_profile(profile_data: list[tuple], limit: int = 20) -> None:
|
|
75
75
|
"""Print a per-statement timing summary to stdout.
|
|
76
76
|
|
|
77
77
|
Args:
|
|
78
78
|
profile_data: List of ``(source, line_no, command_type, elapsed_secs,
|
|
79
79
|
command_text_preview)`` tuples collected during execution.
|
|
80
|
+
limit: Maximum number of top statements to display (default: 20).
|
|
81
|
+
All statements are included in totals regardless of this value.
|
|
80
82
|
"""
|
|
81
83
|
if not profile_data:
|
|
82
84
|
_console.print("[dim]Profile: no statements recorded.[/dim]")
|
|
@@ -85,9 +87,9 @@ def _print_profile(profile_data: list[tuple]) -> None:
|
|
|
85
87
|
total_secs = sum(row[3] for row in profile_data)
|
|
86
88
|
n = len(profile_data)
|
|
87
89
|
|
|
88
|
-
# Sort descending by elapsed time; show top
|
|
90
|
+
# Sort descending by elapsed time; show top `limit` (or all if <= limit).
|
|
89
91
|
sorted_data = sorted(profile_data, key=lambda r: r[3], reverse=True)
|
|
90
|
-
display = sorted_data[:
|
|
92
|
+
display = sorted_data[:limit]
|
|
91
93
|
|
|
92
94
|
_console.print()
|
|
93
95
|
_console.print(f"[bold cyan]Profile:[/bold cyan] {n} statement{'s' if n != 1 else ''} in {total_secs:.3f}s")
|
|
@@ -115,10 +117,10 @@ def _print_profile(profile_data: list[tuple]) -> None:
|
|
|
115
117
|
f"{preview_short}",
|
|
116
118
|
)
|
|
117
119
|
|
|
118
|
-
if len(sorted_data) >
|
|
119
|
-
omitted = len(sorted_data) -
|
|
120
|
+
if len(sorted_data) > limit:
|
|
121
|
+
omitted = len(sorted_data) - limit
|
|
120
122
|
_console.print(
|
|
121
|
-
f"[dim] ... {omitted} more statement{'s' if omitted != 1 else ''} not shown (top
|
|
123
|
+
f"[dim] ... {omitted} more statement{'s' if omitted != 1 else ''} not shown (top {limit} by time)[/dim]",
|
|
122
124
|
)
|
|
123
125
|
|
|
124
126
|
_console.print()
|
|
@@ -213,6 +215,7 @@ def _run(
|
|
|
213
215
|
output_dir: str | None = None,
|
|
214
216
|
progress: bool = False,
|
|
215
217
|
profile: bool = False,
|
|
218
|
+
profile_limit: int = 20,
|
|
216
219
|
ping: bool = False,
|
|
217
220
|
lint: bool = False,
|
|
218
221
|
debug: bool = False,
|
|
@@ -552,7 +555,7 @@ def _run(
|
|
|
552
555
|
if debug:
|
|
553
556
|
_state.step_mode = True
|
|
554
557
|
|
|
555
|
-
_execute_script_direct(conf, profile=profile)
|
|
558
|
+
_execute_script_direct(conf, profile=profile, profile_limit=profile_limit)
|
|
556
559
|
|
|
557
560
|
|
|
558
561
|
# ---------------------------------------------------------------------------
|
|
@@ -611,7 +614,7 @@ def _execute_script_textual_console(conf: ConfigData) -> None:
|
|
|
611
614
|
_state.exec_log.log_exit_end()
|
|
612
615
|
|
|
613
616
|
|
|
614
|
-
def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
|
|
617
|
+
def _execute_script_direct(conf: ConfigData, *, profile: bool = False, profile_limit: int = 20) -> None:
|
|
615
618
|
"""Run runscripts() in the current (main) thread — used when Textual is not active.
|
|
616
619
|
|
|
617
620
|
Args:
|
|
@@ -619,6 +622,8 @@ def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
|
|
|
619
622
|
profile: When ``True``, print a per-statement timing summary after the
|
|
620
623
|
script completes. Timing data must already have been activated on
|
|
621
624
|
``_state.profile_data`` before this function is called.
|
|
625
|
+
profile_limit: Maximum number of top statements to display in the
|
|
626
|
+
profile summary (default: 20).
|
|
622
627
|
"""
|
|
623
628
|
import execsql.state as _state
|
|
624
629
|
import execsql.utils.gui as _gui
|
|
@@ -645,7 +650,7 @@ def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
|
|
|
645
650
|
gui_console_off()
|
|
646
651
|
_state.exec_log.log_status_info(f"{_state.cmds_run} commands run")
|
|
647
652
|
if profile and _state.profile_data is not None:
|
|
648
|
-
_print_profile(_state.profile_data)
|
|
653
|
+
_print_profile(_state.profile_data, limit=profile_limit)
|
|
649
654
|
sys.exit(exc.code)
|
|
650
655
|
except ConfigError:
|
|
651
656
|
raise
|
|
@@ -673,7 +678,7 @@ def _execute_script_direct(conf: ConfigData, *, profile: bool = False) -> None:
|
|
|
673
678
|
gui_console_off()
|
|
674
679
|
_state.exec_log.log_status_info(f"{_state.cmds_run} commands run")
|
|
675
680
|
if profile and _state.profile_data is not None:
|
|
676
|
-
_print_profile(_state.profile_data)
|
|
681
|
+
_print_profile(_state.profile_data, limit=profile_limit)
|
|
677
682
|
_state.exec_log.log_exit_end()
|
|
678
683
|
|
|
679
684
|
|
|
@@ -234,18 +234,22 @@ class Database(ABC):
|
|
|
234
234
|
pass # Non-critical: some drivers lack rowcount support.
|
|
235
235
|
|
|
236
236
|
def decode_row() -> Generator:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
try:
|
|
238
|
+
while True:
|
|
239
|
+
rows = curs.fetchmany()
|
|
240
|
+
if not rows:
|
|
241
|
+
break
|
|
242
|
+
else:
|
|
243
|
+
for row in rows:
|
|
244
|
+
if self.encoding:
|
|
245
|
+
yield [
|
|
246
|
+
c.decode(self.encoding, "backslashreplace") if isinstance(c, bytes) else c
|
|
247
|
+
for c in row
|
|
248
|
+
]
|
|
249
|
+
else:
|
|
250
|
+
yield row
|
|
251
|
+
finally:
|
|
252
|
+
curs.close()
|
|
249
253
|
|
|
250
254
|
return [d[0] for d in curs.description], decode_row()
|
|
251
255
|
|
|
@@ -253,6 +257,10 @@ class Database(ABC):
|
|
|
253
257
|
"""Execute *sql* and return ``(column_names, row_iterator)`` where each row is a ``dict``."""
|
|
254
258
|
# Return an iterable that yields dictionaries of row data
|
|
255
259
|
curs = self.cursor()
|
|
260
|
+
try:
|
|
261
|
+
curs.arraysize = _state.conf.export_row_buffer
|
|
262
|
+
except Exception:
|
|
263
|
+
pass # Non-critical: not all drivers support arraysize.
|
|
256
264
|
try:
|
|
257
265
|
curs.execute(sql)
|
|
258
266
|
except Exception:
|
|
@@ -264,18 +272,24 @@ class Database(ABC):
|
|
|
264
272
|
pass # Non-critical: some drivers lack rowcount support.
|
|
265
273
|
hdrs = [d[0] for d in curs.description]
|
|
266
274
|
|
|
267
|
-
def
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
def dict_rows() -> Generator:
|
|
276
|
+
try:
|
|
277
|
+
while True:
|
|
278
|
+
rows = curs.fetchmany()
|
|
279
|
+
if not rows:
|
|
280
|
+
break
|
|
281
|
+
for row in rows:
|
|
282
|
+
if self.encoding:
|
|
283
|
+
r = [
|
|
284
|
+
c.decode(self.encoding, "backslashreplace") if isinstance(c, bytes) else c for c in row
|
|
285
|
+
]
|
|
286
|
+
else:
|
|
287
|
+
r = row
|
|
288
|
+
yield dict(zip(hdrs, r))
|
|
289
|
+
finally:
|
|
290
|
+
curs.close()
|
|
277
291
|
|
|
278
|
-
return hdrs,
|
|
292
|
+
return hdrs, dict_rows()
|
|
279
293
|
|
|
280
294
|
def schema_exists(self, schema_name: str) -> bool:
|
|
281
295
|
"""Return ``True`` if *schema_name* exists in this database."""
|