execsql2 2.16.15__tar.gz → 2.16.16__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.16.15 → execsql2-2.16.16}/.github/workflows/ci-cd.yml +23 -12
- {execsql2-2.16.15 → execsql2-2.16.16}/.pre-commit-config.yaml +1 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/CHANGELOG.md +19 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/PKG-INFO +1 -1
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/about/divergence.md +2 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/reference/configuration.md +9 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/reference/security.md +30 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/pyproject.toml +2 -2
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/api.py +4 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/cli/__init__.py +156 -134
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/cli/help.py +10 -1
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/cli/run.py +4 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/config.py +2 -0
- execsql2-2.16.16/src/execsql/data/execsql.conf.template +327 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/sqlite.py +47 -43
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/system.py +10 -9
- execsql2-2.16.16/templates/execsql.conf +327 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/cli/test_cli.py +24 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/conftest.py +2 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_extended.py +5 -3
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_system.py +15 -4
- execsql2-2.16.16/tests/utils/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/uv.lock +1 -1
- execsql2-2.16.15/.github/PULL_REQUEST_TEMPLATE.md +0 -19
- execsql2-2.16.15/templates/execsql.conf +0 -287
- {execsql2-2.16.15 → execsql2-2.16.16}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/.gitignore +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/.python-version +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/.readthedocs.yaml +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/CONTRIBUTING.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/LICENSE.txt +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/NOTICE +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/README.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/SECURITY.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/about/contributors.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/about/copyright.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/api/cli.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/api/db.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/api/exporters.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/api/importers.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/api/index.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/api/metacommands.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/dev/architecture.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/getting-started/installation.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/debugging.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/documentation.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/encoding.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/examples.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/formatter.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/logging.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/usage.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/actions.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/actions2.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/checkboxes.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/connect.b64 +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/connect.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/create_conf.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/entry_form.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/execsql_console.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/fatals.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/logo_small.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/unmatched.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/index.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/reference/metacommands.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/plugin-template/README.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/justfile +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/__main__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/cli/lint_ast.py +0 -0
- {execsql2-2.16.15/tests → execsql2-2.16.16/src/execsql/data}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/access.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/base.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/factory.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exceptions.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/format.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/gui/base.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/gui/console.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/base.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/json.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/models.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/parser.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/plugins.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/py.typed +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/ast.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/control.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/engine.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/executor.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/parser.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/script/variables.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/state.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/types.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/README.md +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/config_settings.sqlite +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/make_config_db.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/md_compare.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/md_glossary.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/md_upsert.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/pg_compare.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/pg_glossary.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/pg_upsert.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/script_template.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/ss_compare.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/ss_glossary.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/templates/ss_upsert.sql +0 -0
- {execsql2-2.16.15/tests/cli → execsql2-2.16.16/tests}/__init__.py +0 -0
- {execsql2-2.16.15/tests/db → execsql2-2.16.16/tests/cli}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/cli/test_lint.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/cli/test_ping.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/cli/test_profile.py +0 -0
- {execsql2-2.16.15/tests/exporters → execsql2-2.16.16/tests/db}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_base.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_dsn.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_factory.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_postgres.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.16.15/tests/gui → execsql2-2.16.16/tests/exporters}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_base.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_db.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_json.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.16.15/tests/importers → execsql2-2.16.16/tests/gui}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/gui/test_backends.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.16.15/tests/integration → execsql2-2.16.16/tests/importers}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.16.15/tests/metacommands → execsql2-2.16.16/tests/integration}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/integration/conftest.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.16.15/tests/scripts → execsql2-2.16.16/tests/metacommands}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.16.15/tests/utils → execsql2-2.16.16/tests/scripts}/__init__.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_api.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_ast.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_ast_parser.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_config.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_config_data.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_config_extended.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_debug_repl.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_engine.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_error_messages.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_exceptions.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_executor.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_format.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_mail.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_models.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_package.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_parser.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_parser_params.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_plugins.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_registry.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_script.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_state.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/test_types.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_auth.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_errors.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_regex.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_strings.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_timer.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.16.15 → execsql2-2.16.16}/zensical.toml +0 -0
|
@@ -14,6 +14,20 @@ concurrency:
|
|
|
14
14
|
cancel-in-progress: true
|
|
15
15
|
|
|
16
16
|
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: lint
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
- uses: actions/setup-python@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.13"
|
|
27
|
+
- run: python -m pip install ruff
|
|
28
|
+
- run: ruff check src/ tests/
|
|
29
|
+
- run: ruff format --check src/ tests/
|
|
30
|
+
|
|
17
31
|
tests:
|
|
18
32
|
strategy:
|
|
19
33
|
fail-fast: false
|
|
@@ -43,7 +57,8 @@ jobs:
|
|
|
43
57
|
uses: actions/cache@v5
|
|
44
58
|
with:
|
|
45
59
|
path: ~/.cache/pip
|
|
46
|
-
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{
|
|
60
|
+
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{
|
|
61
|
+
hashFiles('**/pyproject.toml') }}
|
|
47
62
|
restore-keys: |
|
|
48
63
|
${{ runner.os }}-pip-${{ matrix.python-version }}-
|
|
49
64
|
- name: Install dependencies
|
|
@@ -86,10 +101,8 @@ jobs:
|
|
|
86
101
|
ports:
|
|
87
102
|
- 5432:5432
|
|
88
103
|
options: >-
|
|
89
|
-
--health-cmd pg_isready
|
|
90
|
-
--health-
|
|
91
|
-
--health-timeout 5s
|
|
92
|
-
--health-retries 5
|
|
104
|
+
--health-cmd pg_isready --health-interval 10s
|
|
105
|
+
--health-timeout 5s --health-retries 5
|
|
93
106
|
mysql:
|
|
94
107
|
image: mysql:8
|
|
95
108
|
env:
|
|
@@ -100,10 +113,8 @@ jobs:
|
|
|
100
113
|
ports:
|
|
101
114
|
- 3306:3306
|
|
102
115
|
options: >-
|
|
103
|
-
--health-cmd "mysqladmin ping"
|
|
104
|
-
--health-
|
|
105
|
-
--health-timeout 5s
|
|
106
|
-
--health-retries 5
|
|
116
|
+
--health-cmd "mysqladmin ping" --health-interval 10s
|
|
117
|
+
--health-timeout 5s --health-retries 5
|
|
107
118
|
steps:
|
|
108
119
|
- name: Check out repository code
|
|
109
120
|
uses: actions/checkout@v6
|
|
@@ -122,7 +133,7 @@ jobs:
|
|
|
122
133
|
build:
|
|
123
134
|
name: Build distribution 📦
|
|
124
135
|
runs-on: ubuntu-latest
|
|
125
|
-
needs: [tests, integration-tests]
|
|
136
|
+
needs: [ lint, tests, integration-tests ]
|
|
126
137
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
127
138
|
permissions:
|
|
128
139
|
contents: read
|
|
@@ -147,7 +158,7 @@ jobs:
|
|
|
147
158
|
|
|
148
159
|
publish:
|
|
149
160
|
name: PyPI Publish 🚀
|
|
150
|
-
needs: [build]
|
|
161
|
+
needs: [ build ]
|
|
151
162
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
152
163
|
runs-on: ubuntu-latest
|
|
153
164
|
environment:
|
|
@@ -170,7 +181,7 @@ jobs:
|
|
|
170
181
|
|
|
171
182
|
generate-release:
|
|
172
183
|
name: Generate GitHub Release
|
|
173
|
-
needs: [build]
|
|
184
|
+
needs: [ build ]
|
|
174
185
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
175
186
|
runs-on: ubuntu-latest
|
|
176
187
|
permissions:
|
|
@@ -13,6 +13,25 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.16.16] - 2026-05-02
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `--init-config` CLI flag to print a default `execsql.conf` template (with all options commented out and documented) to stdout. Use `execsql --init-config > execsql.conf` to bootstrap a configuration file.
|
|
21
|
+
- `--no-system-cmd` CLI flag to disable SYSTEM_CMD/SHELL metacommand execution. Scripts that use SHELL will fail with a clear error when this flag is active. Also configurable via `allow_system_cmd = No` in `execsql.conf` `[config]` section. The library API exposes the same control via `allow_system_cmd=False`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- CLI `--help` output now logically groups related options: connection, encoding, import/export, execution, GUI, configuration, and information.
|
|
26
|
+
- SYSTEM_CMD now uses `subprocess.run()` instead of the deprecated `subprocess.call()`.
|
|
27
|
+
- `execsql.conf` template updated: added missing options (`use_keyring`, `gui_framework`, `allow_system_cmd`, `log_sql`, `max_log_size_mb`, `show_progress`, `import_progress_interval`, `macos_config_file`), fixed incorrect defaults (`password_prompt`, `new_db`, `scan_lines`), added DuckDB (`k`) to database types, modernized all comments.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- SYSTEM_CMD no longer wraps arguments containing `&` in spurious double quotes. The previous behavior (a Windows `cmd.exe` workaround inherited from the upstream monolith) injected literal `"` characters into subprocess arguments, which could cause commands to fail or behave unexpectedly on non-`cmd.exe` targets.
|
|
32
|
+
|
|
33
|
+
______________________________________________________________________
|
|
34
|
+
|
|
16
35
|
## [2.16.15] - 2026-05-02
|
|
17
36
|
|
|
18
37
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.16.
|
|
3
|
+
Version: 2.16.16
|
|
4
4
|
Summary: Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables.
|
|
5
5
|
Project-URL: Homepage, https://execsql2.readthedocs.io
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -31,6 +31,8 @@ ______________________________________________________________________
|
|
|
31
31
|
| `--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); missing INCLUDE file targets (warnings); and unknown `EXECUTE SCRIPT` targets (warnings). Variable analysis uses two passes so definition order does not matter. The linter descends into named script blocks reached via `EXECUTE SCRIPT` / `EXEC SCRIPT` / `RUN SCRIPT`, reads `SUB_INI` INI files at lint time, recognizes `SUB_EMPTY` / `SUB_ADD` / `SUB_APPEND` / `SUBDATA` as definitions, suppresses false warnings for `$COUNTER_N`, and auto-discovers built-in system variables from the installed source. Exits 0 if no errors, 1 if errors found. |
|
|
32
32
|
| `--parse-tree` | Parse the script into an Abstract Syntax Tree and print a visual tree showing block nesting (IF/LOOP/BATCH/SCRIPT), source line ranges, compound conditions (ANDIF/ORIF), and all metacommands. Does not connect to a database or execute anything. Useful for understanding script structure and verifying the parser handles a script correctly. |
|
|
33
33
|
| `--list-plugins` | List all discovered plugins (metacommands, exporters, importers) from Python entry points and exit. Plugins extend execsql via `execsql.metacommands`, `execsql.exporters`, and `execsql.importers` entry point groups. |
|
|
34
|
+
| `--no-system-cmd` | Disable the `SYSTEM_CMD` (SHELL) metacommand. Scripts that use SHELL will fail with a clear error. Also configurable via `allow_system_cmd = No` in `execsql.conf` `[config]` section, or `allow_system_cmd=False` in the library API. |
|
|
35
|
+
| `--init-config` | Print a default `execsql.conf` template to stdout with all options commented out and documented. Redirect to a file to bootstrap a configuration: `execsql --init-config > execsql.conf`. |
|
|
34
36
|
|
|
35
37
|
### Export Formats
|
|
36
38
|
|
|
@@ -15,6 +15,12 @@ Configuration data is read from these files in the order listed above. Informati
|
|
|
15
15
|
|
|
16
16
|
An explicit configuration file can also be specified with the `--config FILE` command-line option. This file is loaded **after** all four implicit search paths, so its values take precedence over system, user, script-directory, and working-directory config files. CLI arguments still override everything. The `--config` file may chain additional configs via its `[config]` section, just like any other config file.
|
|
17
17
|
|
|
18
|
+
To generate a starter configuration file with all options commented out and documented, use:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
execsql --init-config > execsql.conf
|
|
22
|
+
```
|
|
23
|
+
|
|
18
24
|
In addition, *execsql* will read additional configuration files if they are specified in any of the standard configuration files ([see below](#config_config)).
|
|
19
25
|
|
|
20
26
|
Configuration files use the [INI](https://en.wikipedia.org/wiki/INI_file) file format. Section names are case sensitive and must be all in lowercase. Property names are not case sensitive. Property values are read as-is and may or may not be case sensitive, depending on their use. Comments can be included in configuration files; each comment line must start with the "#" character.
|
|
@@ -329,6 +335,9 @@ The section and property names that may be used in a configuration file are list
|
|
|
329
335
|
`max_log_size_mb` { #max_log_size_mb }
|
|
330
336
|
: 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).
|
|
331
337
|
|
|
338
|
+
`allow_system_cmd` { #allow_system_cmd }
|
|
339
|
+
: When set to "No", the `SYSTEM_CMD` (SHELL) metacommand is disabled. Any script that attempts to execute an OS command will fail with an error. The default is "Yes". This can also be set via the `--no-system-cmd` CLI flag or `allow_system_cmd=False` in the library API. See [Security — Disabling SYSTEM_CMD](security.md#disable_system_cmd) for details.
|
|
340
|
+
|
|
332
341
|
`log_datavars` { #conf_log_datavars }
|
|
333
342
|
: A value of 'Yes' or 'No' to control whether data variables that are created by the [SELECT_SUB](metacommands.md#select_sub), [PROMPT SELECT_SUB](metacommands.md#prompt_selsub) and [PROMPT ACTION](metacommands.md#prompt_action) metacommands are written to *execsql*'s [log file](../guides/logging.md#logging). By default, this is set to 'Yes', so that all data variable assignments are logged. The performance of scripts that make extensive use of these metacommands (e.g., [Example 27](../guides/examples.md#example27)) can be improved by setting this to 'No'.
|
|
334
343
|
|
|
@@ -21,6 +21,36 @@ Variable substitution is applied to the command string before execution, so any
|
|
|
21
21
|
|
|
22
22
|
If `outdir` is derived from user input, validate or sanitize it before use in a `SYSTEM_CMD` command.
|
|
23
23
|
|
|
24
|
+
!!! note "Windows `.bat`/`.cmd` files"
|
|
25
|
+
|
|
26
|
+
On Windows, when the target of a `SYSTEM_CMD` is a `.bat` or `.cmd` file, Windows may invoke `cmd.exe` to interpret it. In this case, the `&` character in arguments can be interpreted as a command separator by `cmd.exe`, even though the subprocess is not launched with `shell=True`. Avoid passing untrusted data containing `&` to batch files on Windows.
|
|
27
|
+
|
|
28
|
+
### Disabling SYSTEM_CMD { #disable_system_cmd }
|
|
29
|
+
|
|
30
|
+
To prevent scripts from executing OS commands entirely, use the `--no-system-cmd` CLI flag:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
execsql --no-system-cmd script.sql mydb
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Any script that uses `SYSTEM_CMD` or `SHELL` will fail with an error. This is useful for CI pipelines, shared execution environments, or running semi-trusted scripts where shell access is not appropriate.
|
|
37
|
+
|
|
38
|
+
The same restriction can be set permanently in `execsql.conf`:
|
|
39
|
+
|
|
40
|
+
```ini
|
|
41
|
+
[config]
|
|
42
|
+
allow_system_cmd = No
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The library API provides the same control:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import execsql
|
|
49
|
+
result = execsql.run("script.sql", dsn="sqlite:///my.db", allow_system_cmd=False)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The `--no-system-cmd` CLI flag always takes precedence — if the flag is passed, `SYSTEM_CMD` is disabled regardless of the config file setting.
|
|
53
|
+
|
|
24
54
|
## Credential Handling { #credentials }
|
|
25
55
|
|
|
26
56
|
### Interactive password prompts
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.16.
|
|
7
|
+
version = "2.16.16"
|
|
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" }
|
|
@@ -165,7 +165,7 @@ skip-magic-trailing-comma = false
|
|
|
165
165
|
line-ending = "auto"
|
|
166
166
|
|
|
167
167
|
[tool.bumpversion]
|
|
168
|
-
current_version = "2.16.
|
|
168
|
+
current_version = "2.16.16"
|
|
169
169
|
commit = true
|
|
170
170
|
tag = true
|
|
171
171
|
tag_name = "v{new_version}"
|
|
@@ -316,6 +316,7 @@ def run(
|
|
|
316
316
|
encoding: str = "utf-8",
|
|
317
317
|
halt_on_error: bool = True,
|
|
318
318
|
new_db: bool = False,
|
|
319
|
+
allow_system_cmd: bool = True,
|
|
319
320
|
) -> ScriptResult:
|
|
320
321
|
"""Execute a SQL script and return the result.
|
|
321
322
|
|
|
@@ -337,6 +338,8 @@ def run(
|
|
|
337
338
|
error. If ``False``, capture errors and continue.
|
|
338
339
|
new_db: If ``True``, create the database if it does not exist
|
|
339
340
|
(SQLite, PostgreSQL, DuckDB).
|
|
341
|
+
allow_system_cmd: If ``False``, the SYSTEM_CMD (SHELL) metacommand
|
|
342
|
+
is disabled and will raise an error if encountered.
|
|
340
343
|
|
|
341
344
|
Returns:
|
|
342
345
|
A :class:`ScriptResult` with execution outcome, timing, errors,
|
|
@@ -434,6 +437,7 @@ def run(
|
|
|
434
437
|
ctx.subvars = subvars
|
|
435
438
|
ctx.status = StatObj()
|
|
436
439
|
ctx.status.halt_on_err = halt_on_error
|
|
440
|
+
conf.allow_system_cmd = allow_system_cmd
|
|
437
441
|
ctx.conf = conf
|
|
438
442
|
|
|
439
443
|
# Capture output to a buffer (suppress stdout/stderr)
|
|
@@ -20,7 +20,7 @@ import typer
|
|
|
20
20
|
|
|
21
21
|
from execsql import __version__
|
|
22
22
|
from execsql.cli.dsn import _parse_connection_string, _SCHEME_TO_DBTYPE # noqa: F401 — re-export
|
|
23
|
-
from execsql.cli.help import _console, _err_console, _print_encodings, _print_metacommands # noqa: F401 — re-export
|
|
23
|
+
from execsql.cli.help import _console, _err_console, _init_config, _print_encodings, _print_metacommands # noqa: F401 — re-export
|
|
24
24
|
from execsql.cli.run import _connect_initial_db, _run # noqa: F401 — re-export
|
|
25
25
|
from execsql.exceptions import ConfigError, ErrInfo
|
|
26
26
|
|
|
@@ -29,6 +29,7 @@ __all__ = [
|
|
|
29
29
|
"_connect_initial_db",
|
|
30
30
|
"_console",
|
|
31
31
|
"_err_console",
|
|
32
|
+
"_init_config",
|
|
32
33
|
"_legacy_main",
|
|
33
34
|
"_parse_connection_string",
|
|
34
35
|
"_print_encodings",
|
|
@@ -72,28 +73,55 @@ def main(
|
|
|
72
73
|
"name (client-server DBs) or a database file path (file-based DBs)."
|
|
73
74
|
),
|
|
74
75
|
),
|
|
75
|
-
#
|
|
76
|
-
|
|
76
|
+
# -- Connection --------------------------------------------------------
|
|
77
|
+
db_type: str | None = typer.Option(
|
|
77
78
|
None,
|
|
78
|
-
"-
|
|
79
|
-
"--
|
|
80
|
-
metavar="
|
|
81
|
-
help=
|
|
79
|
+
"-t",
|
|
80
|
+
"--type",
|
|
81
|
+
metavar="{a,d,p,s,l,m,k,o,f}",
|
|
82
|
+
help=(
|
|
83
|
+
"Database type: [bold]a[/bold]=MS-Access, [bold]p[/bold]=PostgreSQL, "
|
|
84
|
+
"[bold]s[/bold]=SQL Server, [bold]l[/bold]=SQLite, [bold]m[/bold]=MySQL/MariaDB, "
|
|
85
|
+
"[bold]k[/bold]=DuckDB, [bold]o[/bold]=Oracle, [bold]f[/bold]=Firebird, "
|
|
86
|
+
"[bold]d[/bold]=DSN."
|
|
87
|
+
),
|
|
82
88
|
),
|
|
83
|
-
|
|
89
|
+
dsn: str | None = typer.Option(
|
|
84
90
|
None,
|
|
85
|
-
"
|
|
86
|
-
"--
|
|
87
|
-
metavar="
|
|
88
|
-
help=
|
|
91
|
+
"--dsn",
|
|
92
|
+
"--connection-string",
|
|
93
|
+
metavar="URL",
|
|
94
|
+
help=(
|
|
95
|
+
"Database connection URL, e.g. [cyan]postgresql://user:pass@host:5432/db[/cyan]. "
|
|
96
|
+
"Supported schemes: postgresql, mysql, mssql, oracle, firebird, sqlite, duckdb. "
|
|
97
|
+
"Overrides [cyan]-t[/cyan]/[cyan]-u[/cyan]/[cyan]-p[/cyan] and positional server/db args."
|
|
98
|
+
),
|
|
89
99
|
),
|
|
90
|
-
|
|
100
|
+
user: str | None = typer.Option(
|
|
91
101
|
None,
|
|
92
|
-
"-
|
|
93
|
-
"--
|
|
94
|
-
|
|
95
|
-
help="Auto-create directories for EXPORT metacommand. [dim]n=no (default), y=yes[/dim]",
|
|
102
|
+
"-u",
|
|
103
|
+
"--user",
|
|
104
|
+
help="Database user name.",
|
|
96
105
|
),
|
|
106
|
+
port: int | None = typer.Option(
|
|
107
|
+
None,
|
|
108
|
+
"-p",
|
|
109
|
+
"--port",
|
|
110
|
+
help="Database server port.",
|
|
111
|
+
),
|
|
112
|
+
no_passwd: bool = typer.Option(
|
|
113
|
+
False,
|
|
114
|
+
"-w",
|
|
115
|
+
"--no-passwd",
|
|
116
|
+
help="Skip password prompt when user is specified.",
|
|
117
|
+
),
|
|
118
|
+
new_db: bool = typer.Option(
|
|
119
|
+
False,
|
|
120
|
+
"-n",
|
|
121
|
+
"--new-db",
|
|
122
|
+
help="Create a new SQLite or Postgres database if it does not exist.",
|
|
123
|
+
),
|
|
124
|
+
# -- Encoding ----------------------------------------------------------
|
|
97
125
|
database_encoding: str | None = typer.Option(
|
|
98
126
|
None,
|
|
99
127
|
"-e",
|
|
@@ -118,35 +146,13 @@ def main(
|
|
|
118
146
|
"--import-encoding",
|
|
119
147
|
help="Encoding for data files used with IMPORT.",
|
|
120
148
|
),
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"-l",
|
|
124
|
-
"--user-logfile",
|
|
125
|
-
help="Write a log file to [cyan]~/execsql.log[/cyan].",
|
|
126
|
-
),
|
|
127
|
-
metacommands: bool = typer.Option(
|
|
128
|
-
False,
|
|
129
|
-
"-m",
|
|
130
|
-
"--metacommands",
|
|
131
|
-
help="List metacommands and exit.",
|
|
132
|
-
),
|
|
133
|
-
new_db: bool = typer.Option(
|
|
134
|
-
False,
|
|
135
|
-
"-n",
|
|
136
|
-
"--new-db",
|
|
137
|
-
help="Create a new SQLite or Postgres database if it does not exist.",
|
|
138
|
-
),
|
|
139
|
-
online_help: bool = typer.Option(
|
|
140
|
-
False,
|
|
141
|
-
"-o",
|
|
142
|
-
"--online-help",
|
|
143
|
-
help="Open the online documentation in the default browser.",
|
|
144
|
-
),
|
|
145
|
-
port: int | None = typer.Option(
|
|
149
|
+
# -- Import/Export -----------------------------------------------------
|
|
150
|
+
import_buffer: int | None = typer.Option(
|
|
146
151
|
None,
|
|
147
|
-
"-
|
|
148
|
-
"--
|
|
149
|
-
|
|
152
|
+
"-z",
|
|
153
|
+
"--import-buffer",
|
|
154
|
+
metavar="KB",
|
|
155
|
+
help="Import buffer size in KB. [dim]Default: 32[/dim]",
|
|
150
156
|
),
|
|
151
157
|
scanlines: int | None = typer.Option(
|
|
152
158
|
None,
|
|
@@ -155,59 +161,36 @@ def main(
|
|
|
155
161
|
metavar="N",
|
|
156
162
|
help="Lines to scan for IMPORT format detection. [dim]0 = scan entire file.[/dim]",
|
|
157
163
|
),
|
|
158
|
-
|
|
164
|
+
boolean_int: str | None = typer.Option(
|
|
159
165
|
None,
|
|
160
|
-
"-
|
|
161
|
-
"--
|
|
162
|
-
metavar="{
|
|
163
|
-
help=
|
|
164
|
-
"Database type: [bold]a[/bold]=MS-Access, [bold]p[/bold]=PostgreSQL, "
|
|
165
|
-
"[bold]s[/bold]=SQL Server, [bold]l[/bold]=SQLite, [bold]m[/bold]=MySQL/MariaDB, "
|
|
166
|
-
"[bold]k[/bold]=DuckDB, [bold]o[/bold]=Oracle, [bold]f[/bold]=Firebird, "
|
|
167
|
-
"[bold]d[/bold]=DSN."
|
|
168
|
-
),
|
|
166
|
+
"-b",
|
|
167
|
+
"--boolean-int",
|
|
168
|
+
metavar="{0,1,t,f,y,n}",
|
|
169
|
+
help="Treat integers 0 and 1 as boolean values.",
|
|
169
170
|
),
|
|
170
|
-
|
|
171
|
+
make_dirs: str | None = typer.Option(
|
|
171
172
|
None,
|
|
172
|
-
"-
|
|
173
|
-
"--
|
|
174
|
-
|
|
173
|
+
"-d",
|
|
174
|
+
"--directories",
|
|
175
|
+
metavar="{0,1,t,f,y,n}",
|
|
176
|
+
help="Auto-create directories for EXPORT metacommand. [dim]n=no (default), y=yes[/dim]",
|
|
175
177
|
),
|
|
176
|
-
|
|
178
|
+
output_dir: str | None = typer.Option(
|
|
177
179
|
None,
|
|
178
|
-
"-
|
|
179
|
-
"
|
|
180
|
-
metavar="{0,1,2,3}",
|
|
180
|
+
"--output-dir",
|
|
181
|
+
metavar="DIR",
|
|
181
182
|
help=(
|
|
182
|
-
"
|
|
183
|
-
"
|
|
183
|
+
"Default base directory for EXPORT output files. "
|
|
184
|
+
"Relative paths in EXPORT metacommands are joined to this directory. "
|
|
185
|
+
"Absolute paths and [cyan]stdout[/cyan] are unaffected."
|
|
184
186
|
),
|
|
185
187
|
),
|
|
186
|
-
|
|
187
|
-
None,
|
|
188
|
-
"--gui-framework",
|
|
189
|
-
metavar="{tkinter,textual}",
|
|
190
|
-
help="GUI framework to use with [cyan]--visible-prompts[/cyan]. [dim]Default: tkinter[/dim]",
|
|
191
|
-
),
|
|
192
|
-
no_passwd: bool = typer.Option(
|
|
193
|
-
False,
|
|
194
|
-
"-w",
|
|
195
|
-
"--no-passwd",
|
|
196
|
-
help="Skip password prompt when user is specified.",
|
|
197
|
-
),
|
|
198
|
-
encodings: bool = typer.Option(
|
|
188
|
+
progress: bool = typer.Option(
|
|
199
189
|
False,
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
help="List available encoding names and exit.",
|
|
203
|
-
),
|
|
204
|
-
import_buffer: int | None = typer.Option(
|
|
205
|
-
None,
|
|
206
|
-
"-z",
|
|
207
|
-
"--import-buffer",
|
|
208
|
-
metavar="KB",
|
|
209
|
-
help="Import buffer size in KB. [dim]Default: 32[/dim]",
|
|
190
|
+
"--progress",
|
|
191
|
+
help="Show a progress bar for long-running IMPORT operations.",
|
|
210
192
|
),
|
|
193
|
+
# -- Execution ---------------------------------------------------------
|
|
211
194
|
command: str | None = typer.Option(
|
|
212
195
|
None,
|
|
213
196
|
"-c",
|
|
@@ -221,7 +204,7 @@ def main(
|
|
|
221
204
|
dry_run: bool = typer.Option(
|
|
222
205
|
False,
|
|
223
206
|
"--dry-run",
|
|
224
|
-
help=
|
|
207
|
+
help="Parse the script and print the command list without connecting to a database or executing anything.",
|
|
225
208
|
),
|
|
226
209
|
lint: bool = typer.Option(
|
|
227
210
|
False,
|
|
@@ -232,51 +215,23 @@ def main(
|
|
|
232
215
|
"and missing INCLUDE files (warnings). Exits 0 if no errors, 1 if errors found."
|
|
233
216
|
),
|
|
234
217
|
),
|
|
235
|
-
|
|
218
|
+
parse_tree: bool = typer.Option(
|
|
236
219
|
False,
|
|
237
|
-
"--
|
|
238
|
-
help=(
|
|
239
|
-
"Test database connectivity and exit. "
|
|
240
|
-
"Prints connection details and the server version on success (exit 0), "
|
|
241
|
-
"or the error message on failure (exit 1). "
|
|
242
|
-
"No script file is required."
|
|
243
|
-
),
|
|
244
|
-
),
|
|
245
|
-
dsn: str | None = typer.Option(
|
|
246
|
-
None,
|
|
247
|
-
"--dsn",
|
|
248
|
-
"--connection-string",
|
|
249
|
-
metavar="URL",
|
|
250
|
-
help=(
|
|
251
|
-
"Database connection URL, e.g. [cyan]postgresql://user:pass@host:5432/db[/cyan]. "
|
|
252
|
-
"Supported schemes: postgresql, mysql, mssql, oracle, firebird, sqlite, duckdb. "
|
|
253
|
-
"Overrides [cyan]-t[/cyan]/[cyan]-u[/cyan]/[cyan]-p[/cyan] and positional server/db args."
|
|
254
|
-
),
|
|
255
|
-
),
|
|
256
|
-
output_dir: str | None = typer.Option(
|
|
257
|
-
None,
|
|
258
|
-
"--output-dir",
|
|
259
|
-
metavar="DIR",
|
|
220
|
+
"--parse-tree",
|
|
260
221
|
help=(
|
|
261
|
-
"
|
|
262
|
-
"
|
|
263
|
-
"Absolute paths and [cyan]stdout[/cyan] are unaffected."
|
|
222
|
+
"Parse the script into an abstract syntax tree and print the tree structure. "
|
|
223
|
+
"Does not connect to a database or execute anything."
|
|
264
224
|
),
|
|
265
225
|
),
|
|
266
|
-
|
|
267
|
-
False,
|
|
268
|
-
"--progress",
|
|
269
|
-
help="Show a progress bar for long-running IMPORT operations.",
|
|
270
|
-
),
|
|
271
|
-
dump_keywords: bool = typer.Option(
|
|
226
|
+
debug: bool = typer.Option(
|
|
272
227
|
False,
|
|
273
|
-
"--
|
|
274
|
-
help="
|
|
228
|
+
"--debug",
|
|
229
|
+
help="Start in step-through debug mode. The debug REPL pauses before each statement.",
|
|
275
230
|
),
|
|
276
|
-
|
|
231
|
+
no_system_cmd: bool = typer.Option(
|
|
277
232
|
False,
|
|
278
|
-
"--
|
|
279
|
-
help="
|
|
233
|
+
"--no-system-cmd",
|
|
234
|
+
help="Disable the SYSTEM_CMD (SHELL) metacommand. Scripts that use SHELL will fail with an error.",
|
|
280
235
|
),
|
|
281
236
|
profile: bool = typer.Option(
|
|
282
237
|
False,
|
|
@@ -288,6 +243,24 @@ def main(
|
|
|
288
243
|
"--profile-limit",
|
|
289
244
|
help="Number of top statements to show in the --profile timing summary (default: 20).",
|
|
290
245
|
),
|
|
246
|
+
# -- GUI ---------------------------------------------------------------
|
|
247
|
+
use_gui: str | None = typer.Option(
|
|
248
|
+
None,
|
|
249
|
+
"-v",
|
|
250
|
+
"--visible-prompts",
|
|
251
|
+
metavar="{0,1,2,3}",
|
|
252
|
+
help=(
|
|
253
|
+
"GUI level: [bold]0[/bold]=none (default), [bold]1[/bold]=GUI for password/pause, "
|
|
254
|
+
"[bold]2[/bold]=GUI for password/pause + DB selection, [bold]3[/bold]=full GUI console."
|
|
255
|
+
),
|
|
256
|
+
),
|
|
257
|
+
gui_framework: str | None = typer.Option(
|
|
258
|
+
None,
|
|
259
|
+
"--gui-framework",
|
|
260
|
+
metavar="{tkinter,textual}",
|
|
261
|
+
help="GUI framework to use with [cyan]--visible-prompts[/cyan]. [dim]Default: tkinter[/dim]",
|
|
262
|
+
),
|
|
263
|
+
# -- Configuration -----------------------------------------------------
|
|
291
264
|
config_file: str | None = typer.Option(
|
|
292
265
|
None,
|
|
293
266
|
"--config",
|
|
@@ -298,18 +271,62 @@ def main(
|
|
|
298
271
|
"The file may chain additional configs via its [cyan][config][/cyan] section."
|
|
299
272
|
),
|
|
300
273
|
),
|
|
301
|
-
|
|
274
|
+
init_config: bool = typer.Option(
|
|
302
275
|
False,
|
|
303
|
-
"--
|
|
276
|
+
"--init-config",
|
|
277
|
+
help="Print a default [cyan]execsql.conf[/cyan] template to stdout and exit.",
|
|
278
|
+
),
|
|
279
|
+
sub_vars: list[str] | None = typer.Option(
|
|
280
|
+
None,
|
|
281
|
+
"-a",
|
|
282
|
+
"--assign-arg",
|
|
283
|
+
metavar="VALUE",
|
|
284
|
+
help="Define the replacement string for a substitution variable [cyan]\\$ARG_x[/cyan].",
|
|
285
|
+
),
|
|
286
|
+
user_logfile: bool = typer.Option(
|
|
287
|
+
False,
|
|
288
|
+
"-l",
|
|
289
|
+
"--user-logfile",
|
|
290
|
+
help="Write a log file to [cyan]~/execsql.log[/cyan].",
|
|
291
|
+
),
|
|
292
|
+
# -- Information -------------------------------------------------------
|
|
293
|
+
metacommands: bool = typer.Option(
|
|
294
|
+
False,
|
|
295
|
+
"-m",
|
|
296
|
+
"--metacommands",
|
|
297
|
+
help="List metacommands and exit.",
|
|
298
|
+
),
|
|
299
|
+
encodings: bool = typer.Option(
|
|
300
|
+
False,
|
|
301
|
+
"-y",
|
|
302
|
+
"--encodings",
|
|
303
|
+
help="List available encoding names and exit.",
|
|
304
|
+
),
|
|
305
|
+
dump_keywords: bool = typer.Option(
|
|
306
|
+
False,
|
|
307
|
+
"--dump-keywords",
|
|
308
|
+
help="Dump all metacommand keywords as JSON and exit.",
|
|
309
|
+
),
|
|
310
|
+
list_plugins: bool = typer.Option(
|
|
311
|
+
False,
|
|
312
|
+
"--list-plugins",
|
|
313
|
+
help="List all discovered plugins (metacommands, exporters, importers) and exit.",
|
|
314
|
+
),
|
|
315
|
+
ping: bool = typer.Option(
|
|
316
|
+
False,
|
|
317
|
+
"--ping",
|
|
304
318
|
help=(
|
|
305
|
-
"
|
|
306
|
-
"
|
|
319
|
+
"Test database connectivity and exit. "
|
|
320
|
+
"Prints connection details and the server version on success (exit 0), "
|
|
321
|
+
"or the error message on failure (exit 1). "
|
|
322
|
+
"No script file is required."
|
|
307
323
|
),
|
|
308
324
|
),
|
|
309
|
-
|
|
325
|
+
online_help: bool = typer.Option(
|
|
310
326
|
False,
|
|
311
|
-
"
|
|
312
|
-
|
|
327
|
+
"-o",
|
|
328
|
+
"--online-help",
|
|
329
|
+
help="Open the online documentation in the default browser.",
|
|
313
330
|
),
|
|
314
331
|
version: bool | None = typer.Option(
|
|
315
332
|
None,
|
|
@@ -340,6 +357,10 @@ def main(
|
|
|
340
357
|
_print_encodings()
|
|
341
358
|
raise typer.Exit()
|
|
342
359
|
|
|
360
|
+
if init_config:
|
|
361
|
+
_init_config()
|
|
362
|
+
raise typer.Exit()
|
|
363
|
+
|
|
343
364
|
if dump_keywords:
|
|
344
365
|
import json as _json
|
|
345
366
|
|
|
@@ -580,6 +601,7 @@ def main(
|
|
|
580
601
|
ping=ping,
|
|
581
602
|
lint=lint,
|
|
582
603
|
debug=debug,
|
|
604
|
+
no_system_cmd=no_system_cmd,
|
|
583
605
|
config_file=config_file,
|
|
584
606
|
)
|
|
585
607
|
|
|
@@ -11,7 +11,7 @@ from encodings.aliases import aliases as codec_dict
|
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
from rich.table import Table
|
|
13
13
|
|
|
14
|
-
__all__ = ["_console", "_err_console", "_print_encodings", "_print_metacommands"]
|
|
14
|
+
__all__ = ["_console", "_err_console", "_init_config", "_print_encodings", "_print_metacommands"]
|
|
15
15
|
|
|
16
16
|
_console = Console()
|
|
17
17
|
_err_console = Console(stderr=True)
|
|
@@ -77,6 +77,15 @@ _SKIP_FROM_DISPATCH = {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
def _init_config() -> None:
|
|
81
|
+
"""Print the default execsql.conf template to stdout."""
|
|
82
|
+
import importlib.resources
|
|
83
|
+
import sys
|
|
84
|
+
|
|
85
|
+
template = importlib.resources.files("execsql.data").joinpath("execsql.conf.template").read_text(encoding="utf-8")
|
|
86
|
+
sys.stdout.write(template)
|
|
87
|
+
|
|
88
|
+
|
|
80
89
|
def _print_metacommands() -> None:
|
|
81
90
|
"""Print the metacommands table using Rich.
|
|
82
91
|
|