execsql2 2.1.1__tar.gz → 2.2.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.1.1 → execsql2-2.2.1}/.claude/project_context.md +87 -52
- {execsql2-2.1.1 → execsql2-2.2.1}/.github/workflows/ci-cd.yml +1 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.gitignore +6 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/.pre-commit-config.yaml +2 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/.readthedocs.yaml +2 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/CHANGELOG.md +128 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/PKG-INFO +12 -7
- {execsql2-2.1.1 → execsql2-2.2.1}/README.md +11 -6
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/api/cli.md +1 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/api/db.md +20 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/api/exporters.md +4 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/configuration.md +7 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/dev/adding_metacommands.md +14 -10
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/index.md +11 -11
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/syntax.md +3 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/usage.md +41 -0
- execsql2-2.2.1/extras/vscode-execsql/README.md +153 -0
- execsql2-2.2.1/extras/vscode-execsql/package.json +24 -0
- execsql2-2.2.1/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +240 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/justfile +9 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/pyproject.toml +3 -3
- execsql2-2.2.1/scripts/generate_vscode_grammar.py +306 -0
- execsql2-2.2.1/src/execsql/cli/__init__.py +436 -0
- execsql2-2.2.1/src/execsql/cli/dsn.py +86 -0
- execsql2-2.2.1/src/execsql/cli/help.py +140 -0
- execsql2-2.1.1/src/execsql/cli.py → execsql2-2.2.1/src/execsql/cli/run.py +14 -589
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/config.py +13 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/access.py +16 -12
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/base.py +158 -90
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/dsn.py +6 -5
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/duckdb.py +2 -2
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/firebird.py +23 -19
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/mysql.py +8 -7
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/oracle.py +11 -11
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/postgres.py +28 -16
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/sqlite.py +12 -11
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/sqlserver.py +5 -3
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exceptions.py +7 -7
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/base.py +6 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/delimited.py +44 -35
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/duckdb.py +2 -2
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/feather.py +6 -6
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/html.py +83 -69
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/json.py +50 -42
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/latex.py +33 -27
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/ods.py +4 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/parquet.py +2 -2
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/pretty.py +11 -9
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/raw.py +17 -13
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/sqlite.py +2 -2
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/templates.py +23 -15
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/values.py +22 -20
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/xls.py +4 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/xml.py +28 -13
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/importers/base.py +4 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/importers/csv.py +6 -6
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/importers/feather.py +4 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/importers/ods.py +4 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/importers/xls.py +4 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/__init__.py +518 -67
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/conditions.py +101 -27
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/control.py +8 -4
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/data.py +6 -6
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/debug.py +6 -2
- execsql2-2.2.1/src/execsql/metacommands/io.py +70 -0
- execsql2-2.2.1/src/execsql/metacommands/io_export.py +442 -0
- execsql2-2.2.1/src/execsql/metacommands/io_fileops.py +287 -0
- execsql2-2.2.1/src/execsql/metacommands/io_import.py +398 -0
- execsql2-2.2.1/src/execsql/metacommands/io_write.py +248 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/prompt.py +22 -66
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/system.py +7 -2
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/script.py +49 -5
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/types.py +20 -20
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/fileio.py +15 -8
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_sqlite_exporter.py +28 -7
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_templates.py +1 -1
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/metacommands/test_metacommands.py +4 -1
- execsql2-2.2.1/tests/metacommands/test_metacommands_data.py +381 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/metacommands/test_metacommands_extended.py +9 -9
- execsql2-2.2.1/tests/metacommands/test_metacommands_fileops_extra.py +335 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/metacommands/test_metacommands_io.py +10 -7
- execsql2-2.2.1/tests/metacommands/test_metacommands_io_write_extra.py +453 -0
- execsql2-2.2.1/tests/metacommands/test_metacommands_system_extra.py +324 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_cli.py +136 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_format.py +2 -0
- execsql2-2.2.1/tests/test_integration.py +818 -0
- execsql2-2.1.1/tests/test_integration.py → execsql2-2.2.1/tests/test_integration_duckdb.py +136 -53
- execsql2-2.2.1/tests/test_registry.py +172 -0
- execsql2-2.2.1/tests/utils/__init__.py +0 -0
- execsql2-2.2.1/tests/utils/test_auth_extra.py +111 -0
- execsql2-2.2.1/tests/utils/test_errors_extra.py +193 -0
- execsql2-2.2.1/tests/utils/test_fileio_extra.py +332 -0
- execsql2-2.2.1/tests/utils/test_timer_extra.py +36 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/uv.lock +1 -1
- execsql2-2.1.1/src/execsql/metacommands/io.py +0 -1313
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/changelog-manager.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/code-oracle.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/code-reviewer.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/docs-author.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/migration-coder.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/monolith-navigator.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/agents/test-engineer.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/commands/code-oracle.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/commands/migrate.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/commands/review-changes.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/commands/test-module.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/commands/update-changelog.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.claude/commands/where-is.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/.python-version +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/CONTRIBUTING.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/LICENSE.txt +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/NOTICE +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/api/importers.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/api/index.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/api/metacommands.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/change_log.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/contributors.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/copyright.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/debugging.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/documentation.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/encoding.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/examples.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/formatter.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/actions.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/actions2.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/checkboxes.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/connect.b64 +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/connect.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/create_conf.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/entry_form.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/execsql_console.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/fatals.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/logo_small.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/unmatched.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/installation.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/logging.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/metacommands.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/requirements.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/sql_syntax.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/substitution_vars.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/docs/using_scripts.md +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/__main__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/constants.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/db/factory.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/format.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/gui/base.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/gui/console.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/models.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/parser.py +0 -0
- /execsql2-2.1.1/tests/__init__.py → /execsql2-2.2.1/src/execsql/py.typed +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/state.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/READ_ME.rst +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/config_settings.sqlite +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/execsql.conf +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/make_config_db.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/md_compare.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/md_glossary.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/md_upsert.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/pg_compare.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/pg_glossary.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/pg_upsert.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/script_template.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/ss_compare.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/ss_glossary.sql +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/templates/ss_upsert.sql +0 -0
- {execsql2-2.1.1/tests/db → execsql2-2.2.1/tests}/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/conftest.py +0 -0
- {execsql2-2.1.1/tests/exporters → execsql2-2.2.1/tests/db}/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/db/test_base.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/db/test_factory.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/db/test_postgres.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.1.1/tests/gui → execsql2-2.2.1/tests/exporters}/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_base.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_db.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_json.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.1.1/tests/importers → execsql2-2.2.1/tests/gui}/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/gui/test_backends.py +0 -0
- {execsql2-2.1.1/tests/metacommands → execsql2-2.2.1/tests/importers}/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.1.1/tests/utils → execsql2-2.2.1/tests/metacommands}/__init__.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_config.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_config_data.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_constants.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_exceptions.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_mail.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_models.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_package.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_parser.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_script.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_state.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/test_types.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_auth.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_errors.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_regex.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_strings.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/tests/utils/test_timer.py +0 -0
- {execsql2-2.1.1 → execsql2-2.2.1}/zensical.toml +0 -0
|
@@ -7,6 +7,8 @@ ______________________________________________________________________
|
|
|
7
7
|
> **Canonical location:** `.claude/project_context.md` in the repo.
|
|
8
8
|
> Global memory (`~/.claude/projects/.../memory/`) mirrors this — the repo file is the source of truth.
|
|
9
9
|
> Update this file whenever any architectural, tooling, or directional decision is made.
|
|
10
|
+
> Update the CHANGELOG.md whenever making a release or significant change.
|
|
11
|
+
> Update ROADMAP.md whenever making a release or significant change (if direction changes or if features have already been implemented)
|
|
10
12
|
|
|
11
13
|
______________________________________________________________________
|
|
12
14
|
|
|
@@ -78,9 +80,17 @@ src/execsql/ # active codebase — all new work goes here
|
|
|
78
80
|
auth.py / crypto.py / datetime.py / errors.py / fileio.py
|
|
79
81
|
gui.py / mail.py / numeric.py / regex.py / strings.py / timer.py
|
|
80
82
|
docs/ # 18 MkDocs Markdown pages (Material theme)
|
|
83
|
+
extras/
|
|
84
|
+
vscode-execsql/ # VS Code syntax highlighting extension (repo-only, not in wheel)
|
|
85
|
+
package.json
|
|
86
|
+
syntaxes/
|
|
87
|
+
execsql.tmLanguage.json # auto-generated via `just generate-vscode-grammar`
|
|
88
|
+
scripts/
|
|
89
|
+
generate_vscode_grammar.py # generates tmLanguage.json from --dump-keywords
|
|
81
90
|
templates/ # SQL templates + config files
|
|
82
91
|
tests/
|
|
83
92
|
test_placeholder.py
|
|
93
|
+
test_registry.py # keyword consistency tests (dispatch table ↔ grammar ↔ CLI)
|
|
84
94
|
.github/workflows/
|
|
85
95
|
ci-cd.yml # CI/CD pipeline (see below)
|
|
86
96
|
.pre-commit-config.yaml
|
|
@@ -97,18 +107,18 @@ ______________________________________________________________________
|
|
|
97
107
|
|
|
98
108
|
## Tooling Decisions
|
|
99
109
|
|
|
100
|
-
| Tool | Purpose | Decision
|
|
101
|
-
| ------------------- | ------------------------ |
|
|
102
|
-
| `uv` | Package/env management | Chosen over pip/poetry
|
|
103
|
-
| `hatchling` | Build backend | via pyproject.toml
|
|
104
|
-
| `ruff` | Lint + format | Permissive config during migration, tighten during refactor
|
|
105
|
-
| `tox-uv` | Multi-Python test matrix | py310–py313
|
|
106
|
-
| `bump-my-version` | Version bumping | tags + commits, hooks run `uv lock`
|
|
107
|
-
| `just` | Task runner | `lint`, `test`, `test-all`, `docs`, `docs-serve`, `bump-
|
|
108
|
-
| `pre-commit` | Git hooks | gitleaks, uv-lock, ruff, mdformat, markdownlint, typos, validate-pyproject
|
|
109
|
-
| `mkdocs` + Material | Docs site | converted from Sphinx/RST
|
|
110
|
-
| `mkdocstrings` | API docs from docstrings | installed, not yet wired in
|
|
111
|
-
| `pytest-cov` | Coverage | configured, `--cov-fail-under` commented out until tests are written
|
|
110
|
+
| Tool | Purpose | Decision |
|
|
111
|
+
| ------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| `uv` | Package/env management | Chosen over pip/poetry |
|
|
113
|
+
| `hatchling` | Build backend | via pyproject.toml |
|
|
114
|
+
| `ruff` | Lint + format | Permissive config during migration, tighten during refactor |
|
|
115
|
+
| `tox-uv` | Multi-Python test matrix | py310–py313 |
|
|
116
|
+
| `bump-my-version` | Version bumping | tags + commits, hooks run `uv lock` |
|
|
117
|
+
| `just` | Task runner | `lint`, `test`, `test-all`, `docs`, `docs-serve`, `bump-*`, `generate-vscode-grammar`, `install-vscode` |
|
|
118
|
+
| `pre-commit` | Git hooks | gitleaks, uv-lock, ruff, mdformat, markdownlint, typos, validate-pyproject |
|
|
119
|
+
| `mkdocs` + Material | Docs site | converted from Sphinx/RST |
|
|
120
|
+
| `mkdocstrings` | API docs from docstrings | installed, not yet wired in |
|
|
121
|
+
| `pytest-cov` | Coverage | configured, `--cov-fail-under` commented out until tests are written |
|
|
112
122
|
|
|
113
123
|
## Package Layout Decision
|
|
114
124
|
|
|
@@ -154,72 +164,97 @@ Triggered on: push to `main`, any tag `v*.*.*`, pull requests.
|
|
|
154
164
|
|
|
155
165
|
`target-version = "py313"`, `line-length = 120`. Currently permissive — many legacy rules ignored (tabs, bare except, ambiguous names, unused imports, etc.). Intent is to tighten rules progressively during refactor.
|
|
156
166
|
|
|
157
|
-
##
|
|
167
|
+
## Keyword Registry & VS Code Extension
|
|
158
168
|
|
|
159
|
-
|
|
169
|
+
The dispatch table is the single source of truth for all metacommand keywords. Every `mcl.add()` call in `metacommands/__init__.py` has `description=` (keyword name) and `category=` (one of: `control`, `block`, `action`, `config`, `config_option`, `prompt`). Conditional functions in `conditions.py` use `category="condition"`.
|
|
160
170
|
|
|
161
|
-
|
|
171
|
+
- `execsql --dump-keywords` introspects the dispatch table at runtime and outputs structured JSON
|
|
172
|
+
- `scripts/generate_vscode_grammar.py` consumes that JSON to produce `extras/vscode-execsql/syntaxes/execsql.tmLanguage.json`
|
|
173
|
+
- `tests/test_registry.py` validates keyword consistency across dispatch table, CLI output, and grammar
|
|
174
|
+
- Export format constants (`QUERY_EXPORT_FORMATS`, `TABLE_EXPORT_FORMATS`, etc.) are centralized in `metacommands/__init__.py`
|
|
175
|
+
- The VS Code extension lives at `extras/vscode-execsql/` — it is NOT included in the Python wheel (it's an editor extension, not a library). Install via `just install-vscode` (symlinks into `~/.vscode/extensions/`).
|
|
162
176
|
|
|
163
|
-
|
|
164
|
-
rationale so the right one can be picked up without re-investigating context.
|
|
177
|
+
**When adding a new metacommand:** add `description=` and `category=` to the `mcl.add()` call, then run `just generate-vscode-grammar`.
|
|
165
178
|
|
|
166
179
|
______________________________________________________________________
|
|
167
180
|
|
|
168
|
-
|
|
181
|
+
## Known Issues / Docs Debt
|
|
169
182
|
|
|
170
|
-
|
|
171
|
-
`exporters/templates.py`; `airspeed` removed from valid `template_processor` config values.
|
|
172
|
-
Noted in `CHANGELOG.md` under `[Unreleased]`.
|
|
173
|
-
- **`feather = ["pandas", "pyarrow"]`** added to `pyproject.toml` optional-deps and `all` group.
|
|
174
|
-
- **`hdf5 = ["tables"]`** added to `pyproject.toml` optional-deps and `all` group.
|
|
183
|
+
*(Docs cleanup completed — anchor IDs fixed, RST artifacts resolved.)*
|
|
175
184
|
|
|
176
|
-
|
|
185
|
+
## Roadmap
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
Milestones are v2.x minor releases. Python 3.10 support is maintained for
|
|
188
|
+
the foreseeable future. See also `ROADMAP.md` in the repo root.
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
exist in any docs page. The modular refactor means every public class and
|
|
182
|
-
function now has a docstring. Adding API reference pages would give users
|
|
183
|
-
discoverable, always-up-to-date docs from zero extra writing.
|
|
190
|
+
### Completed
|
|
184
191
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
| Item | Version | Date |
|
|
193
|
+
| ------------------------------------------------- | ------------- | ------- |
|
|
194
|
+
| Monolith → modular refactor (80+ files) | 2.0.0a1–2.1.0 | 2026-03 |
|
|
195
|
+
| 30+ security/correctness fixes (ANALYSIS.md) | 2.1.x | 2026-03 |
|
|
196
|
+
| mkdocstrings API docs wired up | 2.1.0 | 2026-03 |
|
|
197
|
+
| `execsql-format` surfaced in README + docs nav | 2.1.0 | 2026-03 |
|
|
198
|
+
| Coverage floor raised to 70% (2,055 tests) | 2.1.2 | 2026-03 |
|
|
199
|
+
| Orphaned optional features cleaned up | 2.1.0 | 2026-03 |
|
|
200
|
+
| Keyring auth, Parquet/Feather/HDF5 export, DuckDB | 2.1.0 | 2026-03 |
|
|
201
|
+
| Progress bars, SQL audit logging | unreleased | 2026-03 |
|
|
202
|
+
| Full monolith parity verified | unreleased | 2026-03 |
|
|
189
203
|
|
|
190
204
|
______________________________________________________________________
|
|
191
205
|
|
|
192
|
-
###
|
|
206
|
+
### v2.2 — Stability & Testing
|
|
207
|
+
|
|
208
|
+
Theme: Harden what exists before adding features.
|
|
193
209
|
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
210
|
+
- **SQLite integration tests** — full lifecycle (connect, DDL, CRUD, export/import, disconnect)
|
|
211
|
+
- **End-to-end CLI tests** — run scripts through the actual CLI entry point
|
|
212
|
+
- **Exception chaining** — add `from e` to ~80 `except Exception: raise ErrInfo(...)` patterns
|
|
213
|
+
- **Exception hierarchy** — make `ErrInfo`, `DataTypeError`, `DbTypeError`, `DatabaseNotImplementedError` inherit from `ExecSqlError`
|
|
214
|
+
- **`py.typed` marker** — add `src/execsql/py.typed` for downstream type checking
|
|
215
|
+
- **README accuracy** — remove "not yet stable" warning, update feature list
|
|
216
|
+
- **Raise coverage floor** — target 75%
|
|
197
217
|
|
|
198
218
|
______________________________________________________________________
|
|
199
219
|
|
|
200
|
-
###
|
|
220
|
+
### v2.3 — Code Quality & Documentation
|
|
201
221
|
|
|
202
|
-
|
|
203
|
-
natural targets to push further:
|
|
222
|
+
Theme: Make the codebase welcoming to contributors and transparent to users.
|
|
204
223
|
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
224
|
+
- **Docstring coverage** — target 50%+ on public API (focus on `db/`, `exporters/`, `script.py`, `config.py`)
|
|
225
|
+
- **CONTRIBUTING.md** — dev setup, architecture overview, PR workflow, testing expectations
|
|
226
|
+
- **Security documentation** — document trust model and security boundaries (SHELL, path traversal, SMTP, passwords)
|
|
227
|
+
- **Ruff tightening** — enable E722 (bare except), E721 (type comparison), remove legacy suppressions
|
|
228
|
+
- **`__all__` exports** — add to all public modules
|
|
229
|
+
- **Remove lazy import anti-pattern** — replace ~30 `global module; import module` with top-level or proper lazy imports
|
|
208
230
|
|
|
209
|
-
|
|
210
|
-
|
|
231
|
+
______________________________________________________________________
|
|
232
|
+
|
|
233
|
+
### v2.4 — Architecture & Performance
|
|
234
|
+
|
|
235
|
+
Theme: Internal modernization. May include backward-incompatible internal API changes (public CLI behavior preserved).
|
|
236
|
+
|
|
237
|
+
- **Split large modules** — `script.py` (1,157 lines) and `metacommands/__init__.py` (1,606 lines)
|
|
238
|
+
- **Database ABC with `@abstractmethod`** — replace runtime `DatabaseNotImplementedError` with true abstract methods
|
|
239
|
+
- **Cursor context managers** — explicit cursor lifecycle in `execute()`, `select_data()`, `select_rowsource()`
|
|
240
|
+
- **Metacommand dispatch optimization** — consider dict/trie lookup instead of O(N) regex scan
|
|
241
|
+
- **Variable substitution optimization** — reduce O(V×D) complexity
|
|
242
|
+
- **Exporter protocol** — define a `Protocol` or ABC for exporters with a common interface
|
|
211
243
|
|
|
212
244
|
______________________________________________________________________
|
|
213
245
|
|
|
214
|
-
### 5
|
|
246
|
+
### v2.5+ — Future
|
|
247
|
+
|
|
248
|
+
- **Plugin system** — allow external packages to register exporters, importers, or metacommands
|
|
249
|
+
|
|
250
|
+
______________________________________________________________________
|
|
215
251
|
|
|
216
|
-
|
|
217
|
-
remnants (`type(x) == y` → `isinstance`, bare `except:` → `except Exception:`,
|
|
218
|
-
etc.), modernize idioms. No user-facing change.
|
|
252
|
+
### Ongoing / No-milestone
|
|
219
253
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
254
|
+
- PostgreSQL integration tests (requires external server — CI docker service)
|
|
255
|
+
- MySQL integration tests (same)
|
|
256
|
+
- `savedscripts` memory pruning
|
|
257
|
+
- Textual TUI polish
|
|
223
258
|
|
|
224
259
|
______________________________________________________________________
|
|
225
260
|
|
|
@@ -18,7 +18,7 @@ repos:
|
|
|
18
18
|
- id: gitleaks
|
|
19
19
|
# uv lockfile management
|
|
20
20
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
21
|
-
rev: 0.
|
|
21
|
+
rev: 0.11.1
|
|
22
22
|
hooks:
|
|
23
23
|
- id: uv-lock
|
|
24
24
|
# Base hooks
|
|
@@ -47,6 +47,7 @@ repos:
|
|
|
47
47
|
- mdformat-mkdocs==5.1.1
|
|
48
48
|
exclude: |
|
|
49
49
|
(?x)^(
|
|
50
|
+
docs/api/.*|
|
|
50
51
|
docs/metacommands\.md|
|
|
51
52
|
docs/documentation\.md|
|
|
52
53
|
docs/examples\.md
|
|
@@ -13,6 +13,134 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.2.1] - 2026-03-26
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Skip `TimerHandler` alarm tests on Windows where `signal.setitimer` is unavailable.
|
|
21
|
+
- Fix `UnicodeDecodeError` in CLI subprocess tests on Windows by specifying UTF-8 encoding.
|
|
22
|
+
|
|
23
|
+
______________________________________________________________________
|
|
24
|
+
|
|
25
|
+
## [2.2.0] - 2026-03-26
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `py.typed` marker for PEP 561 downstream type checking.
|
|
30
|
+
- 252 new tests (2,062 → 2,314) covering metacommands (`data`, `system`, `io_fileops`, `io_write`), utils (`auth`, `errors`, `fileio`, `timer`), and SQLite integration tests for JSON/HTML/LaTeX/TSV exports, DDL operations, WRITE-to-file, CONFIG metacommand, error handling, and inline commands; end-to-end CLI tests for `-c`, `--dsn sqlite://`, `-a` substitution, `--dry-run`, `--dump-keywords`, and `--version`. Coverage floor raised from 70% to 75%.
|
|
31
|
+
- `ods` optional-dependency extra in `pyproject.toml` (`pip install execsql2[ods]`).
|
|
32
|
+
- Keyring credential storage documentation in usage notes.
|
|
33
|
+
- `--progress` CLI flag and `CONFIG SHOW_PROGRESS` metacommand to display a rich progress bar during long-running IMPORT operations. Also configurable via `show_progress` in `execsql.conf`. (FEAT-5)
|
|
34
|
+
- Opt-in SQL query audit logging via `log_sql` config option and `CONFIG LOG_SQL` metacommand. When enabled, all executed SQL statements are written to the log file with a `sql` record type, database name, line number, and query text. (FEAT-6)
|
|
35
|
+
- `--dump-keywords` CLI option: outputs all metacommand keywords, conditional functions, config options, export formats, database types, and variable patterns as structured JSON. Enables tooling (e.g., editor grammar generators) to consume keyword data directly from the dispatch table.
|
|
36
|
+
- VS Code syntax highlighting extension colocated at `extras/vscode-execsql/`. The grammar (`execsql.tmLanguage.json`) is auto-generated from the dispatch table via `just generate-vscode-grammar`.
|
|
37
|
+
- Keyword registry: `MetaCommand` and `MetaCommandList` now support `category` parameter. All `mcl.add()` calls are tagged with `description=` and `category=`, making the dispatch table the single source of truth for keyword metadata.
|
|
38
|
+
- Export format constants (`QUERY_EXPORT_FORMATS`, `TABLE_EXPORT_FORMATS`, `SERVE_FORMATS`, etc.) centralized in `metacommands/__init__.py` and used in dispatch table regex construction.
|
|
39
|
+
- `tests/test_registry.py`: keyword consistency tests validating `--dump-keywords` output, dispatch table categories, conditional table coverage, export format constants, and grammar synchronization.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- CLI reorganized from flat `_cli_*.py` files into `cli/` subpackage (`cli/__init__.py`, `cli/run.py`, `cli/dsn.py`, `cli/help.py`). All existing `from execsql.cli import ...` paths preserved.
|
|
44
|
+
- Exception hierarchy: `ErrInfo`, `DataTypeError`, `DbTypeError`, and `DatabaseNotImplementedError` now inherit from `ExecSqlError` base class instead of bare `Exception`.
|
|
45
|
+
- Exception chaining: added `from e` to 115 `raise` statements across 38 files that previously discarded the original exception context.
|
|
46
|
+
- README: replaced "not yet stable" warning with "maintained fork" note reflecting current project maturity.
|
|
47
|
+
- Split `cli.py` (1245 lines) into `_cli_help.py`, `_cli_dsn.py`, and `_cli_run.py` with `cli.py` as a re-export façade. All existing import paths preserved. (REFAC-3)
|
|
48
|
+
- Split `metacommands/io.py` (1304 lines) into `io_export.py`, `io_import.py`, `io_write.py`, and `io_fileops.py` with `io.py` as a re-export façade. All existing import paths preserved. (REFAC-4)
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- `of` module typo throughout `exporters/ods.py`: all imports and references used `of` (e.g., `import of as of`, `of.table.Table`) instead of `of` (e.g., `import of as of`, `of.table.Table`). ODS export/import was completely broken when `odfpy` was installed. Same typo fixed in `tests/exporters/test_ods.py` and `tests/importers/test_ods_importer.py`.
|
|
53
|
+
|
|
54
|
+
- Unclosed `ScriptFile` in `script.py`: `read_sqlfile()` iterated through the file but never closed it, leaking a file handle on every script load.
|
|
55
|
+
|
|
56
|
+
- Unclosed CSV file handle in `exporters/delimited.py`: `evaluate_line_format()` opened a file for delimiter diagnosis but never closed it. `reader()` generator also leaked the file handle if not fully consumed or if an exception occurred mid-iteration. Both now use `try/finally`.
|
|
57
|
+
|
|
58
|
+
- Unclosed `sqlite3.Connection` in test assertions: `with sqlite3.connect(...)` only commits/rolls back but does not close the connection. Fixed in `tests/exporters/test_sqlite_exporter.py` and `tests/metacommands/test_metacommands.py`.
|
|
59
|
+
|
|
60
|
+
- File handle leaks in exporters: wrapped write operations in `try/finally` blocks to guarantee file handles are closed on error in `exporters/raw.py`, `exporters/xml.py`, `exporters/json.py`, `exporters/templates.py`, `exporters/values.py`, `exporters/pretty.py`, `exporters/html.py`, `exporters/latex.py`, `metacommands/debug.py`, `metacommands/control.py`, and `metacommands/prompt.py`. Previously, an exception during export could leak open file handles.
|
|
61
|
+
|
|
62
|
+
- XML export injection: `exporters/xml.py` now escapes `<`, `>`, `&` in cell values using `xml.sax.saxutils.escape()` and sanitizes `--` in XML comments. Previously, data containing XML special characters produced malformed XML output.
|
|
63
|
+
|
|
64
|
+
- HTML export XSS: `exporters/html.py` now escapes column headers and cell values using `html.escape()`. Previously, data containing `<script>` or other HTML markup was written directly to the output file.
|
|
65
|
+
|
|
66
|
+
- JSON export injection: `exporters/json.py` now uses `json.dumps()` to properly escape the `description` field in JSON-TS exports. Previously, descriptions containing quotes or backslashes produced malformed JSON. Also added missing `import json` to `write_query_to_json_ts` which previously relied on a global set by `write_query_to_json`.
|
|
67
|
+
|
|
68
|
+
- `JinjaTemplateReport.__repr__` incorrectly returned `StrTemplateReport(...)` instead of `JinjaTemplateReport(...)`.
|
|
69
|
+
|
|
70
|
+
- `exporters/templates.py`: removed bare `except: raise` clause in `JinjaTemplateReport.write_report()` and added proper exception chaining (`from e`) to Jinja2 template errors.
|
|
71
|
+
|
|
72
|
+
- `exporters/delimited.py`: `write_delimited_file()` now closes the output file handle via `try/finally`. Previously, the file was opened but never closed, leaking the handle on both success and error paths.
|
|
73
|
+
|
|
74
|
+
- SQL injection in remaining database metadata queries: `role_exists()` in `db/mysql.py`, `db/sqlserver.py`, and `db/firebird.py`, `schema_exists()` in `db/sqlserver.py`, `table_exists()` and `view_exists()` in `db/access.py` and `db/firebird.py` now use parameterized queries instead of f-string interpolation. `column_exists()` and `table_columns()` in `db/access.py` and `db/firebird.py` now use `quote_identifier()` for SQL identifiers that cannot be parameterized.
|
|
75
|
+
|
|
76
|
+
- SQL injection in `db/postgres.py`: `create_db()` now uses `quote_identifier()` for database name and encoding in `CREATE DATABASE` DDL. COPY command delimiter and quote character are now escaped/validated to prevent injection.
|
|
77
|
+
|
|
78
|
+
- CPU busy-loop in `utils/fileio.py`: `FileWriter.run()` now uses blocking `queue.get(timeout=0.1)` instead of `get_nowait()` in a tight loop, eliminating unnecessary CPU consumption when the file writer subprocess is idle.
|
|
79
|
+
|
|
80
|
+
- Substitution variable cycle detection in `script.py`: `substitute_vars()` now enforces a maximum of 100 iterations to prevent infinite loops when variables reference each other cyclically (e.g., `$A` expands to `!!$B!!` and `$B` expands to `!!$A!!`).
|
|
81
|
+
|
|
82
|
+
- Jinja2 template injection: `exporters/templates.py` now uses `jinja2.sandbox.SandboxedEnvironment` instead of the default `jinja2.Template` constructor. Previously, a malicious template file could access Python internals and execute arbitrary code.
|
|
83
|
+
|
|
84
|
+
- SQL injection in `import_entire_file()` across 6 database backends (`db/base.py`, `db/dsn.py`, `db/sqlite.py`, `db/sqlserver.py`, `db/postgres.py`, `db/access.py`): the `column_name` parameter is now quoted with `quote_identifier()` instead of being interpolated directly into the INSERT statement.
|
|
85
|
+
|
|
86
|
+
- XML export malformed output: `exporters/xml.py` now sanitizes column headers and table names used as XML element names, replacing invalid XML name characters with underscores. Previously, column names containing `>`, `<`, or other XML metacharacters produced malformed XML.
|
|
87
|
+
|
|
88
|
+
- `$SHEETS_TABLES_VALUES` SQL injection in `metacommands/io_import.py`: sheet names from ODS/XLS imports are now escaped (single quotes doubled) before embedding in SQL value expressions. Previously, a sheet name containing a single quote produced malformed or injectable SQL.
|
|
89
|
+
|
|
90
|
+
- HTTP header injection in SERVE metacommand (`metacommands/io_fileops.py`): the `Content-Disposition` filename is now sanitized (newlines/carriage returns stripped, quotes escaped) to prevent HTTP response splitting.
|
|
91
|
+
|
|
92
|
+
- Empty `dt_cast` type-cast mapping in `Database` base class (`db/base.py`): the monolith populated this dict with 8 type converters (int, float, str, bool, datetime, date, Decimal, bytearray) but the refactored version was initialized to `{}`. Now uses a lazy property that auto-populates on first access, ensuring all backends get the correct type casters even though none call `super().__init__()`.
|
|
93
|
+
|
|
94
|
+
- `WriteSpec.write()` file descriptor leak (`exporters/base.py`): the one-liner `EncodedFile(...).open("a").write(msg)` opened a file, wrote, and discarded the handle without closing. Now properly closes the handle in a try/finally block.
|
|
95
|
+
|
|
96
|
+
- Removed duplicate `x_halt_msg` function from `metacommands/prompt.py`. The canonical version in `metacommands/control.py` (used by the dispatch table) was the correct one; the prompt.py copy was dead code.
|
|
97
|
+
|
|
98
|
+
- `read_sqlfile()` double-open file leak (`script.py`): `ScriptFile.__init__` already opens the file, but `read_sqlfile()` called `.open("r")` again, creating a second leaked handle. Now uses the ScriptFile directly.
|
|
99
|
+
|
|
100
|
+
- Missing WRITE metacommand delimiter patterns in dispatch table (`metacommands/__init__.py`): added 5 missing delimiter variants (tilde, hash, backtick, bracket, single-quote) for WRITE and ON ERROR_HALT/ON CANCEL_HALT WRITE metacommands that were present in the monolith but missing from the refactored dispatch table.
|
|
101
|
+
|
|
102
|
+
- Missing bare (non-CONFIG) settings aliases in dispatch table (`metacommands/__init__.py`): added 10 bare settings forms (e.g., `FEEDBACK ON` without the `CONFIG` prefix) that the monolith supported but the refactored dispatch table omitted.
|
|
103
|
+
|
|
104
|
+
- `CONNECT TO DSN` metacommand unreachable (`metacommands/__init__.py`): the `x_connect_dsn` handler was imported but never registered with `mcl.add()`. DSN-based connections via metacommand were completely broken.
|
|
105
|
+
|
|
106
|
+
- `PARQUET` missing from EXPORT format regex lists (`metacommands/__init__.py`): the export handler code supported Parquet output but the dispatch table regex didn't include `PARQUET` as a valid format, making `EXPORT ... AS PARQUET` unreachable. Added to both EXPORT QUERY and EXPORT table format lists.
|
|
107
|
+
|
|
108
|
+
- Firebird error message typo (`db/firebird.py`): error message said "required to connect to MySQL" instead of "required to connect to Firebird".
|
|
109
|
+
|
|
110
|
+
- Oracle default port in function signature (`db/oracle.py`): default port parameter was `5432` (PostgreSQL's port) instead of `1521` (Oracle's port). The body already corrected to 1521 but the signature was misleading.
|
|
111
|
+
|
|
112
|
+
- Missing documentation for `show_progress` and `log_sql` config options in `docs/configuration.md`.
|
|
113
|
+
|
|
114
|
+
- Backward compatibility for `TXT-AND` export format (`metacommands/__init__.py`, `metacommands/io_export.py`): the monolith used `TXT-AND`/`TEXT-AND` but the refactored code renamed it to `TXT-AND`/`TEXT-AND`. Added `TXT-AND` as a backward-compatible alias so existing scripts continue to work.
|
|
115
|
+
|
|
116
|
+
- `docs/api/db.md` missing individual database adapter documentation — added all 9 adapters (postgres, sqlite, duckdb, sqlserver, mysql, oracle, firebird, access, dsn).
|
|
117
|
+
|
|
118
|
+
- `docs/api/exporters.md` missing `latex` and `zip` module references — added both.
|
|
119
|
+
|
|
120
|
+
______________________________________________________________________
|
|
121
|
+
|
|
122
|
+
## [2.1.2] - 2026-03-25
|
|
123
|
+
|
|
124
|
+
### Added
|
|
125
|
+
|
|
126
|
+
- DuckDB integration tests: 15 end-to-end tests (`tests/test_integration_duckdb.py`) covering basic SQL, substitution variables, CSV export/import, conditional execution, WRITE metacommand, round-trip, and DuckDB-specific features (views, schemas, native types).
|
|
127
|
+
|
|
128
|
+
### Changed
|
|
129
|
+
|
|
130
|
+
- Added PyPI version, Python versions, license, and Read the Docs badges to `README.md`.
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
|
|
134
|
+
- Config parser now accepts `k` (DuckDB) as a valid `db_type` in `execsql.conf`. Previously only the CLI flag `-t k` worked; config file validation rejected it.
|
|
135
|
+
|
|
136
|
+
- Read the Docs build: added `mkdocstrings-python` and editable project install to `.readthedocs.yaml` so `mkdocstrings` can resolve API references.
|
|
137
|
+
|
|
138
|
+
- Fixed escaped underscore in `docs/api/cli.md` (`\_run` → `_run`) that caused `mkdocstrings` to fail resolving `execsql.cli._run`.
|
|
139
|
+
|
|
140
|
+
- Excluded `docs/api/` from `mdformat` pre-commit hook to prevent it from re-escaping underscores in `mkdocstrings` `:::` directives.
|
|
141
|
+
|
|
142
|
+
______________________________________________________________________
|
|
143
|
+
|
|
16
144
|
## [2.1.1] - 2026-03-25
|
|
17
145
|
|
|
18
146
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.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
|
|
@@ -98,14 +98,15 @@ Provides-Extra: postgres
|
|
|
98
98
|
Requires-Dist: psycopg2-binary; extra == 'postgres'
|
|
99
99
|
Description-Content-Type: text/markdown
|
|
100
100
|
|
|
101
|
-
> [!
|
|
102
|
-
> **This
|
|
103
|
-
> The
|
|
104
|
-
>
|
|
105
|
-
>
|
|
101
|
+
> [!NOTE]
|
|
102
|
+
> **This is a maintained fork of [execsql](https://execsql.readthedocs.io/).**
|
|
103
|
+
> The original monolith has been fully refactored into a modular package with 2,000+ tests.
|
|
104
|
+
> The CLI and configuration are backwards-compatible with upstream v1.130.1.
|
|
105
|
+
> Report issues at [github.com/geocoug/execsql/issues](https://github.com/geocoug/execsql/issues).
|
|
106
106
|
|
|
107
107
|
<div align="center">
|
|
108
|
-
|
|
108
|
+
|
|
109
|
+
<img src="https://execsql2.readthedocs.io/en/latest/images/execsql_logo_01.png" alt="execsql logo">
|
|
109
110
|
|
|
110
111
|
*Multi-DBMS SQL script processor.*
|
|
111
112
|
|
|
@@ -115,6 +116,10 @@ Description-Content-Type: text/markdown
|
|
|
115
116
|
|
|
116
117
|
[](https://github.com/geocoug/execsql/actions/workflows/ci-cd.yml)
|
|
117
118
|
[](https://codecov.io/gh/geocoug/execsql)
|
|
119
|
+
[](https://execsql2.readthedocs.io/)
|
|
120
|
+
[](https://pypi.org/project/execsql2/)
|
|
121
|
+
[](https://pypi.org/project/execsql2/)
|
|
122
|
+
[](https://pypi.org/project/execsql2/)
|
|
118
123
|
[](https://pepy.tech/project/execsql2)
|
|
119
124
|
|
|
120
125
|
</div>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
> [!
|
|
2
|
-
> **This
|
|
3
|
-
> The
|
|
4
|
-
>
|
|
5
|
-
>
|
|
1
|
+
> [!NOTE]
|
|
2
|
+
> **This is a maintained fork of [execsql](https://execsql.readthedocs.io/).**
|
|
3
|
+
> The original monolith has been fully refactored into a modular package with 2,000+ tests.
|
|
4
|
+
> The CLI and configuration are backwards-compatible with upstream v1.130.1.
|
|
5
|
+
> Report issues at [github.com/geocoug/execsql/issues](https://github.com/geocoug/execsql/issues).
|
|
6
6
|
|
|
7
7
|
<div align="center">
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
<img src="https://execsql2.readthedocs.io/en/latest/images/execsql_logo_01.png" alt="execsql logo">
|
|
9
10
|
|
|
10
11
|
*Multi-DBMS SQL script processor.*
|
|
11
12
|
|
|
@@ -15,6 +16,10 @@
|
|
|
15
16
|
|
|
16
17
|
[](https://github.com/geocoug/execsql/actions/workflows/ci-cd.yml)
|
|
17
18
|
[](https://codecov.io/gh/geocoug/execsql)
|
|
19
|
+
[](https://execsql2.readthedocs.io/)
|
|
20
|
+
[](https://pypi.org/project/execsql2/)
|
|
21
|
+
[](https://pypi.org/project/execsql2/)
|
|
22
|
+
[](https://pypi.org/project/execsql2/)
|
|
18
23
|
[](https://pepy.tech/project/execsql2)
|
|
19
24
|
|
|
20
25
|
</div>
|
|
@@ -13,3 +13,23 @@ If you are adding support for a new database, start with the [Adding Database Ad
|
|
|
13
13
|
Convenience constructors used internally to create typed `Database` instances. The CLI calls the appropriate factory function based on the `-t` flag value. Each factory validates its arguments (e.g., checking that a file exists) before constructing and returning the adapter.
|
|
14
14
|
|
|
15
15
|
::: execsql.db.factory
|
|
16
|
+
|
|
17
|
+
## Adapters
|
|
18
|
+
|
|
19
|
+
::: execsql.db.postgres
|
|
20
|
+
|
|
21
|
+
::: execsql.db.sqlite
|
|
22
|
+
|
|
23
|
+
::: execsql.db.duckdb
|
|
24
|
+
|
|
25
|
+
::: execsql.db.sqlserver
|
|
26
|
+
|
|
27
|
+
::: execsql.db.mysql
|
|
28
|
+
|
|
29
|
+
::: execsql.db.oracle
|
|
30
|
+
|
|
31
|
+
::: execsql.db.firebird
|
|
32
|
+
|
|
33
|
+
::: execsql.db.access
|
|
34
|
+
|
|
35
|
+
::: execsql.db.dsn
|
|
@@ -26,6 +26,8 @@ Each module below implements one or more output formats. Every writer follows th
|
|
|
26
26
|
|
|
27
27
|
::: execsql.exporters.templates
|
|
28
28
|
|
|
29
|
+
::: execsql.exporters.latex
|
|
30
|
+
|
|
29
31
|
::: execsql.exporters.ods
|
|
30
32
|
|
|
31
33
|
::: execsql.exporters.xls
|
|
@@ -37,3 +39,5 @@ Each module below implements one or more output formats. Every writer follows th
|
|
|
37
39
|
::: execsql.exporters.sqlite
|
|
38
40
|
|
|
39
41
|
::: execsql.exporters.duckdb
|
|
42
|
+
|
|
43
|
+
::: execsql.exporters.zip
|
|
@@ -152,6 +152,10 @@ The section and property names that may be used in a configuration file are list
|
|
|
152
152
|
|
|
153
153
|
: Replaces newline characters that are in text values on [IMPORT](metacommands.md#import). Every sequence of a newline and any surrounding whitespace is replaced with a single space character.
|
|
154
154
|
|
|
155
|
+
`show_progress`
|
|
156
|
+
|
|
157
|
+
: Whether or not to display a rich progress bar during long-running IMPORT operations. The property value should be either "Yes" or "No". The default is "No". This can also be enabled via the `--progress` CLI flag or the `CONFIG SHOW_PROGRESS` metacommand. Requires the `rich` Python package.
|
|
158
|
+
|
|
155
159
|
`scan_lines` { #scan_lines }
|
|
156
160
|
|
|
157
161
|
: The number of lines of a data file to scan to determine the quoting character and delimiter character used. This is equivalent to the `-s` command-line option. The default value is 100.
|
|
@@ -306,6 +310,9 @@ The section and property names that may be used in a configuration file are list
|
|
|
306
310
|
`linux_config_file`
|
|
307
311
|
: The full name or path to an additional configuration file to be read if *execsql* is running on Linux. If only a path is specified, the name of the configuration file should be `execsql.conf`. The configuration file specified will be read immediately following the configuration file in which it is named. No configuration file will be read more than once. If the name or path are invalid, this setting will be silently ignored. This setting may include [substitution variables](substitution_vars.md#substitution_vars); at the time that configuration files are read, however, only environment variables and system variables related to the script name and path are defined.
|
|
308
312
|
|
|
313
|
+
`log_sql` { #log_sql }
|
|
314
|
+
: When set to "Yes", all executed SQL statements are written to the log file with a `sql` record type, including the database name, line number, and query text. The property value should be either "Yes" or "No". The default is "No". This can also be enabled via the `CONFIG LOG_SQL` metacommand.
|
|
315
|
+
|
|
309
316
|
`max_log_size_mb` { #max_log_size_mb }
|
|
310
317
|
: Maximum size of the log file in megabytes before it is rotated. When set to a positive integer, the log file is rotated to `.1` before a new run appends to it if the file size exceeds the configured threshold. The default is `0` (disabled — no rotation).
|
|
311
318
|
|
|
@@ -89,19 +89,22 @@ Open `src/execsql/metacommands/__init__.py` and add a `mcl.add()` call inside `b
|
|
|
89
89
|
mcl.add(
|
|
90
90
|
r"^\s*MY_COMMAND\s+(?P<target>\w+)\s+(?P<value>.+)$",
|
|
91
91
|
x_my_command,
|
|
92
|
+
description="MY_COMMAND",
|
|
93
|
+
category="action",
|
|
92
94
|
)
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
`MetaCommandList.add()` accepts these parameters:
|
|
96
98
|
|
|
97
|
-
| Parameter | Type | Default | Purpose
|
|
98
|
-
| ------------------ | -------------------------- | -------- |
|
|
99
|
-
| `matching_regexes` | `str` or `tuple[str, ...]` | required | One regex string, or a tuple of strings all mapped to the same handler
|
|
100
|
-
| `exec_func` | callable | required | The handler function
|
|
101
|
-
| `description` | `str \| None` | `None` | Human-readable
|
|
102
|
-
| `run_in_batch` | `bool` | `False` | Allow execution inside an open `BEGIN_BATCH`/`END_BATCH` block
|
|
103
|
-
| `run_when_false` | `bool` | `False` | Execute even when the `IF`-stack condition is false (needed for `ELSE`, `ENDIF`, etc.)
|
|
104
|
-
| `set_error_flag` | `bool` | `True` | Update `_state.status.metacommand_error` on success/failure
|
|
99
|
+
| Parameter | Type | Default | Purpose |
|
|
100
|
+
| ------------------ | -------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
101
|
+
| `matching_regexes` | `str` or `tuple[str, ...]` | required | One regex string, or a tuple of strings all mapped to the same handler |
|
|
102
|
+
| `exec_func` | callable | required | The handler function |
|
|
103
|
+
| `description` | `str \| None` | `None` | Human-readable keyword name (shown in `DEBUG WRITE METACOMMANDLIST` and `--dump-keywords`) |
|
|
104
|
+
| `run_in_batch` | `bool` | `False` | Allow execution inside an open `BEGIN_BATCH`/`END_BATCH` block |
|
|
105
|
+
| `run_when_false` | `bool` | `False` | Execute even when the `IF`-stack condition is false (needed for `ELSE`, `ENDIF`, etc.) |
|
|
106
|
+
| `set_error_flag` | `bool` | `True` | Update `_state.status.metacommand_error` on success/failure |
|
|
107
|
+
| `category` | `str \| None` | `None` | Keyword category for `--dump-keywords` and VS Code grammar generation (e.g., `"action"`, `"control"`, `"config"`) |
|
|
105
108
|
|
|
106
109
|
All regexes are compiled with `re.I` (case-insensitive) automatically.
|
|
107
110
|
|
|
@@ -209,9 +212,10 @@ ______________________________________________________________________
|
|
|
209
212
|
|
|
210
213
|
- [ ] Handler function added to the appropriate `src/execsql/metacommands/*.py` module
|
|
211
214
|
- [ ] Handler imported in `src/execsql/metacommands/__init__.py`
|
|
212
|
-
- [ ] `mcl.add(...)` call added in `build_dispatch_table()`
|
|
215
|
+
- [ ] `mcl.add(...)` call added in `build_dispatch_table()` with `description=` and `category=`
|
|
216
|
+
- [ ] Run `just generate-vscode-grammar` to update the VS Code grammar
|
|
213
217
|
- [ ] Integration test added to `tests/test_metacommands.py` (or relevant file)
|
|
214
|
-
- [ ] `pytest` passes locally
|
|
218
|
+
- [ ] `pytest` passes locally (including `tests/test_registry.py` keyword consistency checks)
|
|
215
219
|
|
|
216
220
|
______________________________________________________________________
|
|
217
221
|
|
|
@@ -8,17 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
You can use *execsql* to:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
- Import data from text files and spreadsheets into a database.
|
|
12
|
+
- Export tables and views to text files, OpenDocument spreadsheets, HTML, JSON, LaTeΧ, XML, or 17 other data formats.
|
|
13
|
+
- Copy data between different databases, even databases using different DBMSs.
|
|
14
|
+
- Display tables or views on the terminal or in a GUI dialog window.
|
|
15
|
+
- Export data using template processors to produce non-tabular output with customized format and contents.
|
|
16
|
+
- Conditionally execute different SQL commands and metacommands based on the DBMS in use, the database in use, data values, user input, and other conditions.
|
|
17
|
+
- Execute blocks of SQL statements and metacommands repeatedly, using any of three different looping methods.
|
|
18
|
+
- Prompt the user to select files or directories, answer questions, or enter data values.
|
|
19
|
+
- Allow the user to visually compare two tables or views.
|
|
20
|
+
- Serve data tables to a web application when used as a CGI script.
|
|
21
|
+
- Write messages to the console or to a file during the processing of a SQL script. These messages can be used to display the progress of the script or create a custom log of the operations that have been carried out or results obtained. Status messages and data exported in text format can be combined in a single text file. Data tables can be exported in a text format that is compatible with Markdown pipe tables, so that script output can be converted into a variety of document formats (see [Example 8](examples.md#example8) and [Example 11](examples.md#example11)).
|
|
22
22
|
|
|
23
23
|
Different DBMSs and DBMS-specific client programs provide different and incompatible extensions to SQL, ordinarily to allow interactions with the file system and to allow conditional tests and looping. Some DBMSs do not have any native extensions of this sort. *execsql* provides these features, as well as features for user interaction, in an identical fashion for all supported DBMSs. This allows standardization of the SQL scripting language used for different types of database management systems.
|
|
24
24
|
|