execsql2 2.18.0__tar.gz → 2.18.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.18.0 → execsql2-2.18.1}/.github/workflows/ci-cd.yml +1 -1
- {execsql2-2.18.0 → execsql2-2.18.1}/CHANGELOG.md +12 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/PKG-INFO +42 -40
- {execsql2-2.18.0 → execsql2-2.18.1}/README.md +41 -39
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/about/divergence.md +7 -6
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/dev/adding_metacommands.md +1 -3
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/dev/architecture.md +4 -4
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/vscode-execsql/README.md +4 -4
- {execsql2-2.18.0 → execsql2-2.18.1}/pyproject.toml +2 -2
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/cli/__init__.py +3 -5
- execsql2-2.18.0/src/execsql/cli/lint_ast.py → execsql2-2.18.1/src/execsql/cli/lint.py +113 -46
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/dispatch.py +5 -10
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/script_ext.py +8 -7
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/engine.py +1 -12
- execsql2-2.18.0/tests/cli/test_lint_ast.py → execsql2-2.18.1/tests/cli/test_lint.py +130 -17
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_assert.py +0 -6
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_conditions_extra.py +3 -3
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_prompt.py +1 -1
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_engine.py +1 -13
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_executor.py +4 -4
- {execsql2-2.18.0 → execsql2-2.18.1}/uv.lock +1 -1
- execsql2-2.18.0/src/execsql/cli/lint.py +0 -91
- execsql2-2.18.0/tests/cli/test_lint.py +0 -98
- {execsql2-2.18.0 → execsql2-2.18.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.github/dependabot.yml +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.gitignore +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.pre-commit-config.yaml +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.python-version +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/.readthedocs.yaml +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/CONTRIBUTING.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/LICENSE.txt +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/NOTICE +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/SECURITY.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/about/contributors.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/about/copyright.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/api/cli.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/api/db.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/api/exporters.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/api/importers.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/api/index.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/api/metacommands.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/getting-started/installation.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/debugging.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/documentation.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/encoding.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/examples.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/formatter.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/logging.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/usage.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/actions.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/actions2.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/checkboxes.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/connect.b64 +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/connect.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/create_conf.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/entry_form.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/execsql_console.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/fatals.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/logo_small.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/unmatched.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/index.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/reference/configuration.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/reference/metacommands.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/reference/security.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/plugin-template/README.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/justfile +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/__main__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/api.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/cli/help.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/cli/run.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/config.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/access.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/base.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/duckdb.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/factory.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/sqlite.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exceptions.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/format.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/gui/base.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/gui/console.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/base.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/json.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/models.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/parser.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/plugins.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/py.typed +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/ast.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/control.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/executor.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/parser.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/script/variables.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/state.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/types.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/README.md +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/config_settings.sqlite +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/execsql.conf +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/make_config_db.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/md_compare.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/md_glossary.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/md_upsert.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/pg_compare.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/pg_glossary.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/pg_upsert.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/script_template.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/ss_compare.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/ss_glossary.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/templates/ss_upsert.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/cli/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/cli/test_cli.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/cli/test_ping.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/cli/test_profile.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/conftest.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_base.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_db_adapters_mocked_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_dsn.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_factory.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_postgres.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_postgres_inprocess.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_base.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_db.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_json.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_backends.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/conftest.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_log_redaction.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_api.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_api_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_ast.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_ast_parser.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_config.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_config_data.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_config_extended.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_debug_repl.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_error_messages.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_exceptions.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_format.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_mail.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_models.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_package.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_parser.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_parser_params.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_plugins.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_registry.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_script.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_state.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/test_types.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/__init__.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_auth.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_errors.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_regex.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_strings.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_timer.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.18.0 → execsql2-2.18.1}/zensical.toml +0 -0
|
@@ -283,7 +283,7 @@ jobs:
|
|
|
283
283
|
name: ${{ github.event.repository.name }}
|
|
284
284
|
path: dist/
|
|
285
285
|
- name: Create release
|
|
286
|
-
uses: softprops/action-gh-release@
|
|
286
|
+
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
|
|
287
287
|
with:
|
|
288
288
|
draft: false
|
|
289
289
|
prerelease: false
|
|
@@ -13,6 +13,18 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.18.1] - 2026-05-28
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Internal: `execsql.cli.lint_ast` has been folded into `execsql.cli.lint`; the AST walker entry point is now `execsql.cli.lint.lint()`. Code that imported `execsql.cli.lint_ast.lint_ast` should import `execsql.cli.lint.lint` instead.
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
|
|
24
|
+
- Internal: the unused `run_when_false` and `run_in_batch` flags on `MetaCommand` (and the matching keyword arguments on `MetaCommandList.add()`) — neither has been consulted since v2.16.0. Code that still passes either kwarg to `mcl.add()` will raise `TypeError`.
|
|
25
|
+
|
|
26
|
+
______________________________________________________________________
|
|
27
|
+
|
|
16
28
|
## [2.18.0] - 2026-05-27
|
|
17
29
|
|
|
18
30
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.18.
|
|
3
|
+
Version: 2.18.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: Homepage, https://execsql2.readthedocs.io
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -228,44 +228,46 @@ execsql script.sql # read connection from config file
|
|
|
228
228
|
|
|
229
229
|
## Options
|
|
230
230
|
|
|
231
|
-
| Flag | Description
|
|
232
|
-
| ------------------------------------- |
|
|
233
|
-
| `-t {p,m,s,l,k,a,f,o,d}` | Database type
|
|
234
|
-
| `-u USER` | Database username
|
|
235
|
-
| `-p PORT` | Server port
|
|
236
|
-
| `-a VALUE` | Set substitution variable `$ARG_x`
|
|
237
|
-
| `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values
|
|
238
|
-
| `-c SCRIPT` | Execute inline SQL or metacommand string
|
|
239
|
-
| `-d` | Auto-create export directories
|
|
240
|
-
| `-e ENCODING` / `--database-encoding` | Character encoding used in the database
|
|
241
|
-
| `-f ENCODING` | Script file encoding (default: UTF-8)
|
|
242
|
-
| `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output
|
|
243
|
-
| `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT
|
|
244
|
-
| `-l` | Write run log to `~/execsql.log`
|
|
245
|
-
| `-m` | List metacommands and exit
|
|
246
|
-
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist
|
|
247
|
-
| `-o` / `--online-help` | Open the online documentation in the default browser
|
|
248
|
-
| `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file)
|
|
249
|
-
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full)
|
|
250
|
-
| `-w` | Skip password prompt when a username is supplied
|
|
251
|
-
| `-y` / `--encodings` | List available encoding names and exit
|
|
252
|
-
| `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32)
|
|
253
|
-
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`)
|
|
254
|
-
| `--output-dir DIR` | Default base directory for EXPORT output files
|
|
255
|
-
| `--dry-run` | Parse the script and report commands without executing
|
|
256
|
-
| `--lint` | Static analysis: check structure and warn on issues (no DB)
|
|
257
|
-
| `--parse-tree` | Print the script's AST structure and exit (no DB)
|
|
258
|
-
| `--list-plugins` | List discovered plugins and exit
|
|
259
|
-
| `--ping` | Test database connectivity and exit
|
|
260
|
-
| `--profile` | Show per-statement timing summary after execution
|
|
261
|
-
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20)
|
|
262
|
-
| `--progress` | Show a progress bar for long-running IMPORT operations
|
|
263
|
-
| `--config FILE` | Load an explicit config file (highest priority after CLI args)
|
|
264
|
-
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs)
|
|
265
|
-
| `--
|
|
266
|
-
| `--
|
|
267
|
-
| `--
|
|
268
|
-
| `--
|
|
231
|
+
| Flag | Description |
|
|
232
|
+
| ------------------------------------- | ------------------------------------------------------------------ |
|
|
233
|
+
| `-t {p,m,s,l,k,a,f,o,d}` | Database type |
|
|
234
|
+
| `-u USER` | Database username |
|
|
235
|
+
| `-p PORT` | Server port |
|
|
236
|
+
| `-a VALUE` | Set substitution variable `$ARG_x` |
|
|
237
|
+
| `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values |
|
|
238
|
+
| `-c SCRIPT` | Execute inline SQL or metacommand string |
|
|
239
|
+
| `-d` | Auto-create export directories |
|
|
240
|
+
| `-e ENCODING` / `--database-encoding` | Character encoding used in the database |
|
|
241
|
+
| `-f ENCODING` | Script file encoding (default: UTF-8) |
|
|
242
|
+
| `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output |
|
|
243
|
+
| `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT |
|
|
244
|
+
| `-l` | Write run log to `~/execsql.log` |
|
|
245
|
+
| `-m` | List metacommands and exit |
|
|
246
|
+
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
|
|
247
|
+
| `-o` / `--online-help` | Open the online documentation in the default browser |
|
|
248
|
+
| `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file) |
|
|
249
|
+
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
|
|
250
|
+
| `-w` | Skip password prompt when a username is supplied |
|
|
251
|
+
| `-y` / `--encodings` | List available encoding names and exit |
|
|
252
|
+
| `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32) |
|
|
253
|
+
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
|
|
254
|
+
| `--output-dir DIR` | Default base directory for EXPORT output files |
|
|
255
|
+
| `--dry-run` | Parse the script and report commands without executing |
|
|
256
|
+
| `--lint` | Static analysis: check structure and warn on issues (no DB) |
|
|
257
|
+
| `--parse-tree` | Print the script's AST structure and exit (no DB) |
|
|
258
|
+
| `--list-plugins` | List discovered plugins and exit |
|
|
259
|
+
| `--ping` | Test database connectivity and exit |
|
|
260
|
+
| `--profile` | Show per-statement timing summary after execution |
|
|
261
|
+
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20) |
|
|
262
|
+
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
263
|
+
| `--config FILE` | Load an explicit config file (highest priority after CLI args) |
|
|
264
|
+
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs) |
|
|
265
|
+
| `--no-rm-file` | Disable the `RM_FILE` metacommand (no script-driven file deletion) |
|
|
266
|
+
| `--no-serve` | Disable the `SERVE` metacommand (no script-driven file streaming) |
|
|
267
|
+
| `--init-config` | Print a default `execsql.conf` template to stdout and exit |
|
|
268
|
+
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
269
|
+
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
270
|
+
| `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
|
|
269
271
|
|
|
270
272
|
Run `execsql --help` for the full option list, or `execsql -m` to list all metacommands.
|
|
271
273
|
|
|
@@ -393,7 +395,7 @@ execsql-format --check scripts/
|
|
|
393
395
|
```yaml
|
|
394
396
|
repos:
|
|
395
397
|
- repo: https://github.com/geocoug/execsql
|
|
396
|
-
rev: v2.
|
|
398
|
+
rev: v2.18.0
|
|
397
399
|
hooks:
|
|
398
400
|
- id: execsql-format
|
|
399
401
|
args: [--in-place]
|
|
@@ -99,44 +99,46 @@ execsql script.sql # read connection from config file
|
|
|
99
99
|
|
|
100
100
|
## Options
|
|
101
101
|
|
|
102
|
-
| Flag | Description
|
|
103
|
-
| ------------------------------------- |
|
|
104
|
-
| `-t {p,m,s,l,k,a,f,o,d}` | Database type
|
|
105
|
-
| `-u USER` | Database username
|
|
106
|
-
| `-p PORT` | Server port
|
|
107
|
-
| `-a VALUE` | Set substitution variable `$ARG_x`
|
|
108
|
-
| `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values
|
|
109
|
-
| `-c SCRIPT` | Execute inline SQL or metacommand string
|
|
110
|
-
| `-d` | Auto-create export directories
|
|
111
|
-
| `-e ENCODING` / `--database-encoding` | Character encoding used in the database
|
|
112
|
-
| `-f ENCODING` | Script file encoding (default: UTF-8)
|
|
113
|
-
| `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output
|
|
114
|
-
| `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT
|
|
115
|
-
| `-l` | Write run log to `~/execsql.log`
|
|
116
|
-
| `-m` | List metacommands and exit
|
|
117
|
-
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist
|
|
118
|
-
| `-o` / `--online-help` | Open the online documentation in the default browser
|
|
119
|
-
| `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file)
|
|
120
|
-
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full)
|
|
121
|
-
| `-w` | Skip password prompt when a username is supplied
|
|
122
|
-
| `-y` / `--encodings` | List available encoding names and exit
|
|
123
|
-
| `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32)
|
|
124
|
-
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`)
|
|
125
|
-
| `--output-dir DIR` | Default base directory for EXPORT output files
|
|
126
|
-
| `--dry-run` | Parse the script and report commands without executing
|
|
127
|
-
| `--lint` | Static analysis: check structure and warn on issues (no DB)
|
|
128
|
-
| `--parse-tree` | Print the script's AST structure and exit (no DB)
|
|
129
|
-
| `--list-plugins` | List discovered plugins and exit
|
|
130
|
-
| `--ping` | Test database connectivity and exit
|
|
131
|
-
| `--profile` | Show per-statement timing summary after execution
|
|
132
|
-
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20)
|
|
133
|
-
| `--progress` | Show a progress bar for long-running IMPORT operations
|
|
134
|
-
| `--config FILE` | Load an explicit config file (highest priority after CLI args)
|
|
135
|
-
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs)
|
|
136
|
-
| `--
|
|
137
|
-
| `--
|
|
138
|
-
| `--
|
|
139
|
-
| `--
|
|
102
|
+
| Flag | Description |
|
|
103
|
+
| ------------------------------------- | ------------------------------------------------------------------ |
|
|
104
|
+
| `-t {p,m,s,l,k,a,f,o,d}` | Database type |
|
|
105
|
+
| `-u USER` | Database username |
|
|
106
|
+
| `-p PORT` | Server port |
|
|
107
|
+
| `-a VALUE` | Set substitution variable `$ARG_x` |
|
|
108
|
+
| `-b` / `--boolean-int` | Treat integers 0 and 1 as boolean values |
|
|
109
|
+
| `-c SCRIPT` | Execute inline SQL or metacommand string |
|
|
110
|
+
| `-d` | Auto-create export directories |
|
|
111
|
+
| `-e ENCODING` / `--database-encoding` | Character encoding used in the database |
|
|
112
|
+
| `-f ENCODING` | Script file encoding (default: UTF-8) |
|
|
113
|
+
| `-g ENCODING` / `--output-encoding` | Encoding for WRITE and EXPORT output |
|
|
114
|
+
| `-i ENCODING` / `--import-encoding` | Encoding for data files used with IMPORT |
|
|
115
|
+
| `-l` | Write run log to `~/execsql.log` |
|
|
116
|
+
| `-m` | List metacommands and exit |
|
|
117
|
+
| `-n` | Create a new SQLite or PostgreSQL database if it does not exist |
|
|
118
|
+
| `-o` / `--online-help` | Open the online documentation in the default browser |
|
|
119
|
+
| `-s N` / `--scan-lines` | Lines to scan for IMPORT format detection (0 = scan entire file) |
|
|
120
|
+
| `-v {0,1,2,3}` | GUI level (0=none, 1=password, 2=selection, 3=full) |
|
|
121
|
+
| `-w` | Skip password prompt when a username is supplied |
|
|
122
|
+
| `-y` / `--encodings` | List available encoding names and exit |
|
|
123
|
+
| `-z KB` / `--import-buffer` | Import buffer size in KB (default: 32) |
|
|
124
|
+
| `--dsn URL` | Connection string (e.g. `postgresql://user:pass@host/db`) |
|
|
125
|
+
| `--output-dir DIR` | Default base directory for EXPORT output files |
|
|
126
|
+
| `--dry-run` | Parse the script and report commands without executing |
|
|
127
|
+
| `--lint` | Static analysis: check structure and warn on issues (no DB) |
|
|
128
|
+
| `--parse-tree` | Print the script's AST structure and exit (no DB) |
|
|
129
|
+
| `--list-plugins` | List discovered plugins and exit |
|
|
130
|
+
| `--ping` | Test database connectivity and exit |
|
|
131
|
+
| `--profile` | Show per-statement timing summary after execution |
|
|
132
|
+
| `--profile-limit N` | Top N statements to display in `--profile` summary (default: 20) |
|
|
133
|
+
| `--progress` | Show a progress bar for long-running IMPORT operations |
|
|
134
|
+
| `--config FILE` | Load an explicit config file (highest priority after CLI args) |
|
|
135
|
+
| `--no-system-cmd` | Disable the `SYSTEM_CMD` metacommand (safer for CI / shared envs) |
|
|
136
|
+
| `--no-rm-file` | Disable the `RM_FILE` metacommand (no script-driven file deletion) |
|
|
137
|
+
| `--no-serve` | Disable the `SERVE` metacommand (no script-driven file streaming) |
|
|
138
|
+
| `--init-config` | Print a default `execsql.conf` template to stdout and exit |
|
|
139
|
+
| `--debug` | Start in step-through debug mode (REPL pauses before each stmt) |
|
|
140
|
+
| `--dump-keywords` | Print metacommand keywords as JSON and exit |
|
|
141
|
+
| `--gui-framework {tkinter,textual}` | GUI framework for interactive prompts |
|
|
140
142
|
|
|
141
143
|
Run `execsql --help` for the full option list, or `execsql -m` to list all metacommands.
|
|
142
144
|
|
|
@@ -264,7 +266,7 @@ execsql-format --check scripts/
|
|
|
264
266
|
```yaml
|
|
265
267
|
repos:
|
|
266
268
|
- repo: https://github.com/geocoug/execsql
|
|
267
|
-
rev: v2.
|
|
269
|
+
rev: v2.18.0
|
|
268
270
|
hooks:
|
|
269
271
|
- id: execsql-format
|
|
270
272
|
args: [--in-place]
|
|
@@ -296,9 +296,10 @@ ______________________________________________________________________
|
|
|
296
296
|
|
|
297
297
|
## Removed Features
|
|
298
298
|
|
|
299
|
-
| Feature
|
|
300
|
-
|
|
|
301
|
-
| Airspeed template processor
|
|
302
|
-
| Python 2 compatibility
|
|
303
|
-
| `FREE` keyword on `PROMPT DISPLAY`
|
|
304
|
-
| Legacy command-list execution engine
|
|
299
|
+
| Feature | Reason |
|
|
300
|
+
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
301
|
+
| Airspeed template processor | The `airspeed` library (Velocity clone) is unmaintained since ~2018. Use `FORMAT jinja` instead. The `airspeed` value for `template_processor` in `execsql.conf` is no longer accepted. |
|
|
302
|
+
| Python 2 compatibility | All Python 2 constructs (`stringtypes`, `u""` literals, `optparse`, etc.) have been removed. execsql2 requires Python 3.10+. |
|
|
303
|
+
| `FREE` keyword on `PROMPT DISPLAY` | The non-blocking display behavior was only implemented in the console backend; the Textual and Tkinter GUI backends ignored it. Removed rather than partially supported. |
|
|
304
|
+
| Legacy command-list execution engine | The `CommandList` data class, `IfLevels` / `IfItem` classes, and the `.run()` methods on `SqlStmt` / `MetacommandStmt` / `ScriptCmd` were the flat command-list engine inherited from the monolith. The AST executor has been the sole engine since v2.16.0; everything dependent on the flat-engine bookkeeping (`runscripts`, `check_iflevels`, `endloop`, and the `commandliststack` / `loopcommandstack` / `compiling_loop` / `loop_nest_level` / `if_stack` / `savedscripts` slots on `RuntimeContext`) has been removed. Variable scoping migrated to the unified `ast_exec_stack` via the new `ExecFrame` data class. Advanced consumers that read `_state.commandliststack` directly should use `_state.current_localvars()` / `current_paramvals()` / `outer_script_scopes()` instead. |
|
|
305
|
+
| `run_when_false` / `run_in_batch` flags on `MetaCommand` | The two boolean flags on `MetaCommand` (and the matching keyword arguments on `MetaCommandList.add()`) were the flat-engine's dispatch-time gates: `run_when_false` let `ELSE` / `ENDIF` handlers fire even when the IF stack was false, and `run_in_batch` permitted `END BATCH` / `ROLLBACK` to run inside a `BEGIN BATCH` block. Under the AST executor IF branching is structural, so handlers only execute on the chosen branch; the live `BEGIN BATCH` gate is `BatchLevels.in_batch()`. Neither flag was read by any live code path; both were removed in v2.18.1 along with the two tests that pinned their presence on every dispatch entry. Code that passed these kwargs to `mcl.add()` will raise `TypeError`. |
|
|
@@ -26,7 +26,7 @@ ______________________________________________________________________
|
|
|
26
26
|
1. It extracts the leading keyword and narrows candidates via the keyword index, falling back to the full list if no keyword matches.
|
|
27
27
|
1. It calls the handler, passing all named regex groups plus `"metacommandline"` as keyword arguments.
|
|
28
28
|
|
|
29
|
-
Conditional branching (`IF` / `ELSEIF` / `ELSE` / `ENDIF`) is **structural** under the AST executor — the parser turns those metacommands into `IfBlock` AST nodes and the executor only invokes handlers for the branch it actually entered. Handlers therefore do not need to consult any IF state
|
|
29
|
+
Conditional branching (`IF` / `ELSEIF` / `ELSE` / `ENDIF`) is **structural** under the AST executor — the parser turns those metacommands into `IfBlock` AST nodes and the executor only invokes handlers for the branch it actually entered. Handlers therefore do not need to consult any IF state; the `run_when_false` and `run_in_batch` flags that used to gate dispatch under the flat command-list engine were removed in v2.18.1.
|
|
30
30
|
|
|
31
31
|
### Handler naming conventions
|
|
32
32
|
|
|
@@ -114,8 +114,6 @@ mcl.add(
|
|
|
114
114
|
| `matching_regexes` | `str` or `tuple[str, ...]` | required | One regex string, or a tuple of strings all mapped to the same handler |
|
|
115
115
|
| `exec_func` | callable | required | The handler function |
|
|
116
116
|
| `description` | `str \| None` | `None` | Human-readable keyword name (shown in `DEBUG WRITE METACOMMANDLIST` and `--dump-keywords`) |
|
|
117
|
-
| `run_in_batch` | `bool` | `False` | Allow execution inside an open `BEGIN_BATCH`/`END_BATCH` block |
|
|
118
|
-
| `run_when_false` | `bool` | `False` | Execute even when the `IF`-stack condition is false (needed for `ELSE`, `ENDIF`, etc.) |
|
|
119
117
|
| `set_error_flag` | `bool` | `True` | Update `_state.status.metacommand_error` on success/failure |
|
|
120
118
|
| `category` | `str \| None` | `None` | Keyword category for `--dump-keywords` and VS Code grammar generation (e.g., `"action"`, `"control"`, `"config"`) |
|
|
121
119
|
|
|
@@ -85,7 +85,7 @@ flowchart LR
|
|
|
85
85
|
| `api.py` | Public `execsql.run()` Python entry point for notebooks, pipelines, and library use |
|
|
86
86
|
| `config.py` | `ConfigData` (INI merging), `StatObj` (runtime flags), `WriteHooks` (stdout/stderr redirection) |
|
|
87
87
|
| `state.py` | Thread-local runtime store — all shared mutable state lives here, isolated per-thread |
|
|
88
|
-
| `script/` | AST parser
|
|
88
|
+
| `script/` | AST node types, parser, `MetaCommandList`, `SubVarSet`, `BatchLevels`, `ScriptExecSpec`, `set_system_vars()` |
|
|
89
89
|
| `metacommands/` | `build_dispatch_table()`, all `x_*` handlers, `build_conditional_table()`, all `xf_*` predicates |
|
|
90
90
|
| `db/` | `Database` ABC, `DatabasePool`, 9 adapter modules (postgres, sqlite, duckdb, mysql, sqlserver, oracle, firebird, access, dsn) |
|
|
91
91
|
| `exporters/` | `ExportRecord`, `ExportMetadata`, `WriteSpec`, 20+ format writers (CSV, JSON, XML, HTML, etc.) |
|
|
@@ -110,7 +110,7 @@ execsql2 parses scripts into an AST and walks the tree to execute. There is no s
|
|
|
110
110
|
- **`script/ast.py`** — defines 9 `Node` subclasses (`SqlStatement`, `MetaCommandStatement`, `Comment`, `IfBlock`, `LoopBlock`, `BatchBlock`, `ScriptBlock`, `SqlBlock`, `IncludeDirective`) plus the `Script` root and supporting types (`SourceSpan` for source locations, `ConditionModifier` for ANDIF/ORIF, `ElseIfClause`, `ParamDef`).
|
|
111
111
|
- **`script/parser.py`** — `parse_script(path)` / `parse_string(text)` produce a `Script` tree. All block structures (IF/LOOP/BATCH/SCRIPT/SQL) are resolved at parse time into nested nodes; the parser also reports structural errors (unmatched blocks).
|
|
112
112
|
- **`script/executor.py`** — `execute(tree, ctx=)` walks the tree via `_execute_nodes()` / `_execute_node()`. SQL and metacommands delegate to the existing dispatch tables. INCLUDE'd files are parsed and recursed into natively; circular INCLUDE references are detected via `ctx.include_chain` and reported. Named SCRIPT blocks register in `ctx.ast_scripts` (instance-scoped) for later `EXECUTE SCRIPT` lookup. `ON ERROR_HALT` / `ON CANCEL_HALT EXECUTE SCRIPT` deferred scripts also run through the AST executor.
|
|
113
|
-
- **`cli/
|
|
113
|
+
- **`cli/lint.py`** — AST-based linter for variable and INCLUDE checks plus the Rich result printer; structural validation lives in the parser.
|
|
114
114
|
|
|
115
115
|
Use `--parse-tree` to print the AST without executing.
|
|
116
116
|
|
|
@@ -139,7 +139,7 @@ Metacommands are lines in SQL scripts prefixed with `-- !x!`. At import time, `m
|
|
|
139
139
|
|
|
140
140
|
### How dispatch works
|
|
141
141
|
|
|
142
|
-
`
|
|
142
|
+
For each `MetaCommandStatement` AST node, the executor calls `_exec_metacommand()` in `script/executor.py`, which delegates to `_state.metacommandlist.eval(cmd_str)`. The dispatcher extracts the leading keyword, narrows ~225 entries to a small candidate set via a keyword index, then tests each candidate's compiled regex against the full command. The matched handler is called with the regex's named groups as keyword arguments plus `metacommandline` (the original unmodified line), and its hit counter is incremented.
|
|
143
143
|
|
|
144
144
|
### Handler conventions
|
|
145
145
|
|
|
@@ -160,7 +160,7 @@ The `IF`/`ELSEIF`/`ELSE`/`ENDIF` metacommands control conditional execution stru
|
|
|
160
160
|
|
|
161
161
|
`_execute_if()` evaluates the IF condition (and any ANDIF/ORIF modifiers) via `CondParser`, then walks the chosen branch (the IF body, one of the ELSEIF clauses, or the ELSE body). For tracking and REPL introspection it pushes a non-scope `ExecFrame` (`kind="if"`/`"elseif"`/`"else"`) onto `ctx.ast_exec_stack` whose `scope_ref` points at the enclosing SCRIPT/main scope; the frame is popped when the branch finishes.
|
|
162
162
|
|
|
163
|
-
Because branching is structural, the
|
|
163
|
+
Because branching is structural, the executor never consults dispatch-time flags to decide whether an IF/ELSE/ELSEIF/ENDIF/ANDIF/ORIF handler should fire — the parser has already chosen the live branch. The corresponding dispatch entries (and the `BREAK` / `BEGIN BATCH` / `END BATCH` entries) remain registered as `ErrInfo` stubs so that a parser regression which let one of those metacommands fall through to the dispatch table would fail loudly rather than silently. (The pre-AST flat-command-list engine relied on a `_state.if_stack` + `IfLevels` state machine and matching `run_when_false` / `run_in_batch` flags on `MetaCommand`; both were removed once the AST became the sole execution engine.)
|
|
164
164
|
|
|
165
165
|
### CondParser
|
|
166
166
|
|
|
@@ -43,13 +43,13 @@ ______________________________________________________________________
|
|
|
43
43
|
Create a symlink from the VSCode extensions directory to this folder:
|
|
44
44
|
|
|
45
45
|
```sh
|
|
46
|
-
ln -s /path/to/execsql/vscode-execsql ~/.vscode/extensions/execsql-syntax
|
|
46
|
+
ln -s /path/to/execsql/extras/vscode-execsql ~/.vscode/extensions/execsql-syntax
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
For example, if you cloned the repo to `./execsql`:
|
|
50
50
|
|
|
51
51
|
```sh
|
|
52
|
-
ln -s
|
|
52
|
+
ln -s "$(pwd)/execsql/extras/vscode-execsql" ~/.vscode/extensions/execsql-syntax
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
### Windows
|
|
@@ -57,13 +57,13 @@ ln -s ./execsql/vscode-execsql ~/.vscode/extensions/execsql-syntax
|
|
|
57
57
|
Create a directory junction using Command Prompt **as Administrator**:
|
|
58
58
|
|
|
59
59
|
```cmd
|
|
60
|
-
mklink /J "%USERPROFILE%\.vscode\extensions\execsql-syntax" "C:\path\to\execsql\vscode-execsql"
|
|
60
|
+
mklink /J "%USERPROFILE%\.vscode\extensions\execsql-syntax" "C:\path\to\execsql\extras\vscode-execsql"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
Or using PowerShell **as Administrator**:
|
|
64
64
|
|
|
65
65
|
```powershell
|
|
66
|
-
New-Item -ItemType Junction -Path "$env:USERPROFILE\.vscode\extensions\execsql-syntax" -Target "C:\path\to\execsql\vscode-execsql"
|
|
66
|
+
New-Item -ItemType Junction -Path "$env:USERPROFILE\.vscode\extensions\execsql-syntax" -Target "C:\path\to\execsql\extras\vscode-execsql"
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### After installing
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.18.
|
|
7
|
+
version = "2.18.1"
|
|
8
8
|
description = "Runs a SQL script against a PostgreSQL, SQLite, MariaDB/MySQL, DuckDB, Firebird, MS-Access, MS-SQL-Server, or Oracle database, or an ODBC DSN. Provides metacommands to import and export data, copy data between databases, conditionally execute SQL and metacommands, and dynamically alter SQL and metacommands with substitution variables."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = { file = "LICENSE.txt" }
|
|
@@ -168,7 +168,7 @@ skip-magic-trailing-comma = false
|
|
|
168
168
|
line-ending = "auto"
|
|
169
169
|
|
|
170
170
|
[tool.bumpversion]
|
|
171
|
-
current_version = "2.18.
|
|
171
|
+
current_version = "2.18.1"
|
|
172
172
|
commit = true
|
|
173
173
|
tag = true
|
|
174
174
|
tag_name = "v{new_version}"
|
|
@@ -8,8 +8,7 @@ Submodules:
|
|
|
8
8
|
- :mod:`execsql.cli.help` — Rich-formatted help output & console objects
|
|
9
9
|
- :mod:`execsql.cli.dsn` — Connection-string (DSN URL) parser
|
|
10
10
|
- :mod:`execsql.cli.run` — Core execution logic (``_run``, ``_connect_initial_db``, ``_ping_db``, ``_print_dry_run``, ``_print_profile``)
|
|
11
|
-
- :mod:`execsql.cli.
|
|
12
|
-
- :mod:`execsql.cli.lint` — Shared lint result printing (``_print_lint_results``) used by the AST linter
|
|
11
|
+
- :mod:`execsql.cli.lint` — AST-based ``--lint`` static analyser and Rich result printer
|
|
13
12
|
"""
|
|
14
13
|
|
|
15
14
|
from __future__ import annotations
|
|
@@ -554,8 +553,7 @@ def main(
|
|
|
554
553
|
# Lint: AST-based static analysis (no DB connection needed)
|
|
555
554
|
# ------------------------------------------------------------------
|
|
556
555
|
if lint:
|
|
557
|
-
from execsql.cli.lint import _print_lint_results
|
|
558
|
-
from execsql.cli.lint_ast import lint_ast
|
|
556
|
+
from execsql.cli.lint import _print_lint_results, lint as _lint_script
|
|
559
557
|
from execsql.script.parser import parse_script, parse_string
|
|
560
558
|
|
|
561
559
|
label = script_name or "<inline>"
|
|
@@ -576,7 +574,7 @@ def main(
|
|
|
576
574
|
exit_code = _print_lint_results(issues, label)
|
|
577
575
|
raise typer.Exit(code=exit_code) from exc
|
|
578
576
|
|
|
579
|
-
issues =
|
|
577
|
+
issues = _lint_script(tree, script_path=script_name)
|
|
580
578
|
exit_code = _print_lint_results(issues, label)
|
|
581
579
|
raise typer.Exit(code=exit_code)
|
|
582
580
|
|
|
@@ -1,29 +1,37 @@
|
|
|
1
|
-
"""AST-based static analysis (lint) for execsql scripts.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1
|
+
"""AST-based static analysis (``--lint``) for execsql scripts.
|
|
2
|
+
|
|
3
|
+
Operates on the :class:`~execsql.script.ast.Script` tree produced by
|
|
4
|
+
:func:`execsql.script.parser.parse_script` / ``parse_string``. Runs as
|
|
5
|
+
an early CLI exit — no DB connection and no ``_state`` initialisation
|
|
6
|
+
required.
|
|
7
|
+
|
|
8
|
+
Checks performed:
|
|
9
|
+
|
|
10
|
+
1. **Parse errors** — the AST parser rejects unmatched IF / LOOP /
|
|
11
|
+
BATCH / SCRIPT blocks at parse time with precise source spans;
|
|
12
|
+
``cli/__init__.py`` reports any parse failure as a lint error before
|
|
13
|
+
:func:`lint` is even called.
|
|
14
|
+
2. **Empty scripts** — warns when no nodes were parsed.
|
|
15
|
+
3. **Potentially undefined variables** — flags ``!!$VAR!!`` references
|
|
16
|
+
with no preceding ``SUB``-family definition, ignoring built-in
|
|
17
|
+
``$VAR`` names discovered from the package source and the
|
|
18
|
+
non-``$`` sigils (``~``, ``#``, ``+``, ``@``, ``&``) that resolve at
|
|
19
|
+
runtime.
|
|
20
|
+
4. **Missing INCLUDE files** — warns when the resolved target does not
|
|
21
|
+
exist on disk (skipped when ``IF EXISTS`` is present).
|
|
22
|
+
5. **EXECUTE SCRIPT target resolution** — warns when a target name does
|
|
23
|
+
not correspond to a :class:`ScriptBlock` in the same file (skipped
|
|
24
|
+
when ``IF EXISTS`` is present).
|
|
25
|
+
|
|
26
|
+
Public surface:
|
|
27
|
+
|
|
28
|
+
- :func:`lint` — entry point; returns a list of
|
|
29
|
+
``(severity, source, line_no, message)`` tuples.
|
|
30
|
+
- :func:`_print_lint_results` — Rich console formatter for those
|
|
31
|
+
tuples; returns the ``--lint`` process exit code (``1`` when any
|
|
32
|
+
error-severity issue is present, ``0`` otherwise).
|
|
33
|
+
- :data:`_Issue`, :func:`_error`, :func:`_warning` — tuple type alias
|
|
34
|
+
and constructors used by the walker and the formatter.
|
|
27
35
|
"""
|
|
28
36
|
|
|
29
37
|
from __future__ import annotations
|
|
@@ -44,11 +52,27 @@ from execsql.script.ast import (
|
|
|
44
52
|
SqlStatement,
|
|
45
53
|
)
|
|
46
54
|
|
|
47
|
-
__all__ = ["
|
|
55
|
+
__all__ = ["_Issue", "_error", "_print_lint_results", "_warning", "lint"]
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
# ---------------------------------------------------------------------------
|
|
51
|
-
#
|
|
59
|
+
# Issue tuple type and constructors
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
_Issue = tuple[str, str, int, str] # (severity, source, line_no, message)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _error(source: str, line_no: int, message: str) -> _Issue:
|
|
67
|
+
return ("error", source, line_no, message)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _warning(source: str, line_no: int, message: str) -> _Issue:
|
|
71
|
+
return ("warning", source, line_no, message)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# Variable-related patterns
|
|
52
76
|
# ---------------------------------------------------------------------------
|
|
53
77
|
|
|
54
78
|
_RX_SUB = re.compile(r"^\s*SUB\s+(?P<name>[+~]?\w+)\s+", re.I)
|
|
@@ -72,22 +96,7 @@ _RX_VAR_REF = re.compile(r"!!([$@&~#+]?\w+)!!", re.I)
|
|
|
72
96
|
|
|
73
97
|
|
|
74
98
|
# ---------------------------------------------------------------------------
|
|
75
|
-
#
|
|
76
|
-
# ---------------------------------------------------------------------------
|
|
77
|
-
|
|
78
|
-
_Issue = tuple[str, str, int, str] # (severity, source, line_no, message)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def _error(source: str, line_no: int, message: str) -> _Issue:
|
|
82
|
-
return ("error", source, line_no, message)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _warning(source: str, line_no: int, message: str) -> _Issue:
|
|
86
|
-
return ("warning", source, line_no, message)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# ---------------------------------------------------------------------------
|
|
90
|
-
# Built-in variable discovery (reuse from flat linter)
|
|
99
|
+
# Built-in variable discovery
|
|
91
100
|
# ---------------------------------------------------------------------------
|
|
92
101
|
|
|
93
102
|
|
|
@@ -396,11 +405,11 @@ def _lint_nodes(
|
|
|
396
405
|
|
|
397
406
|
|
|
398
407
|
# ---------------------------------------------------------------------------
|
|
399
|
-
# Public
|
|
408
|
+
# Public entry point
|
|
400
409
|
# ---------------------------------------------------------------------------
|
|
401
410
|
|
|
402
411
|
|
|
403
|
-
def
|
|
412
|
+
def lint(
|
|
404
413
|
script: Script,
|
|
405
414
|
script_path: str | None = None,
|
|
406
415
|
) -> list[_Issue]:
|
|
@@ -437,3 +446,61 @@ def lint_ast(
|
|
|
437
446
|
)
|
|
438
447
|
|
|
439
448
|
return issues
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
# ---------------------------------------------------------------------------
|
|
452
|
+
# Result printing
|
|
453
|
+
# ---------------------------------------------------------------------------
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def _print_lint_results(issues: list[_Issue], script_label: str) -> int:
|
|
457
|
+
"""Print lint issues to the console using Rich formatting.
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
issues: List of ``(severity, source, line_no, message)`` tuples.
|
|
461
|
+
script_label: Human-readable label for the script (file path or
|
|
462
|
+
``<inline>``), shown in the summary line.
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
``1`` if any errors were found, ``0`` if only warnings or nothing.
|
|
466
|
+
"""
|
|
467
|
+
from execsql.cli.help import _console
|
|
468
|
+
|
|
469
|
+
n_errors = sum(1 for sev, *_ in issues if sev == "error")
|
|
470
|
+
n_warnings = sum(1 for sev, *_ in issues if sev == "warning")
|
|
471
|
+
|
|
472
|
+
_console.print(f"\n[bold cyan]Lint:[/bold cyan] {script_label}")
|
|
473
|
+
_console.print()
|
|
474
|
+
|
|
475
|
+
if not issues:
|
|
476
|
+
_console.print("[bold green]No issues found.[/bold green]")
|
|
477
|
+
_console.print()
|
|
478
|
+
return 0
|
|
479
|
+
|
|
480
|
+
# Sort: errors first, then warnings; within each group sort by line number.
|
|
481
|
+
_sev_order = {"error": 0, "warning": 1}
|
|
482
|
+
sorted_issues = sorted(issues, key=lambda i: (_sev_order.get(i[0], 9), i[2]))
|
|
483
|
+
|
|
484
|
+
# Compute the widest location string so columns align.
|
|
485
|
+
locs: list[str] = []
|
|
486
|
+
for _, source, line_no, _ in sorted_issues:
|
|
487
|
+
locs.append(f"{source}:{line_no}" if line_no else source)
|
|
488
|
+
loc_width = max(len(loc) for loc in locs) if locs else 0
|
|
489
|
+
|
|
490
|
+
for (severity, _source, _line_no, message), loc in zip(sorted_issues, locs):
|
|
491
|
+
pad = " " * (loc_width - len(loc))
|
|
492
|
+
if severity == "error":
|
|
493
|
+
_console.print(f" [bold red]ERROR [/bold red] [dim]{loc}[/dim]{pad} {message}")
|
|
494
|
+
else:
|
|
495
|
+
_console.print(f" [bold yellow]WARNING[/bold yellow] [dim]{loc}[/dim]{pad} {message}")
|
|
496
|
+
|
|
497
|
+
_console.print()
|
|
498
|
+
parts = []
|
|
499
|
+
if n_errors:
|
|
500
|
+
parts.append(f"[bold red]{n_errors} error{'s' if n_errors != 1 else ''}[/bold red]")
|
|
501
|
+
if n_warnings:
|
|
502
|
+
parts.append(f"[bold yellow]{n_warnings} warning{'s' if n_warnings != 1 else ''}[/bold yellow]")
|
|
503
|
+
_console.print(" " + ", ".join(parts))
|
|
504
|
+
_console.print()
|
|
505
|
+
|
|
506
|
+
return 1 if n_errors > 0 else 0
|