execsql2 2.15.1__tar.gz → 2.15.2__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.15.1 → execsql2-2.15.2}/CHANGELOG.md +18 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/PKG-INFO +1 -1
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/about/divergence.md +5 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/pyproject.toml +2 -2
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/config.py +1 -1
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/duckdb.py +6 -7
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/sqlite.py +46 -46
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/parser.py +3 -2
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/types.py +28 -15
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_parser.py +2 -2
- {execsql2-2.15.1 → execsql2-2.15.2}/uv.lock +1 -1
- {execsql2-2.15.1 → execsql2-2.15.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.gitignore +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.pre-commit-config.yaml +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.python-version +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/.readthedocs.yaml +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/CLAUDE.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/CONTRIBUTING.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/LICENSE.txt +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/NOTICE +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/README.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/SECURITY.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/about/contributors.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/about/copyright.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/api/cli.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/api/db.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/api/exporters.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/api/importers.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/api/index.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/api/metacommands.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/dev/adding_db_adapters.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/dev/architecture.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/getting-started/installation.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/debugging.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/documentation.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/encoding.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/examples.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/formatter.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/logging.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/sql_syntax.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/usage.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/actions.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/actions2.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/checkboxes.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/connect.b64 +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/connect.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/create_conf.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/entry_form.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/execsql_console.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/fatals.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/logo_small.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/pause_terminal.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/pause_terminal_sm.b64 +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/prompt_compare.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/unit_conversions.b64 +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/unmatched.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/index.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/reference/configuration.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/reference/metacommands.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/reference/security.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/extras/vscode-execsql/package.json +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/justfile +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/__main__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/cli/help.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/cli/run.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/access.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/base.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/factory.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/mysql.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/oracle.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/postgres.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/db/sqlserver.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exceptions.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/json.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/protocol.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/format.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/gui/base.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/gui/console.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/base.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/csv.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/json.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/io_export.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/io_fileops.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/models.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/py.typed +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/script/control.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/script/engine.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/script/variables.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/state.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/README.md +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/config_settings.sqlite +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/execsql.conf +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/make_config_db.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/md_compare.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/md_glossary.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/md_upsert.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/pg_compare.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/pg_glossary.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/pg_upsert.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/script_template.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/ss_compare.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/ss_glossary.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/templates/ss_upsert.sql +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/test_cli.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/test_lint.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/test_ping.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/cli/test_profile.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/conftest.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/test_base.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/test_factory.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/test_postgres.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_base.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_db.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_json.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/gui/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/gui/test_backends.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/conftest.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/test_mysql.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_config.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_config_data.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_config_extended.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_engine.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_error_messages.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_exceptions.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_format.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_mail.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_models.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_package.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_registry.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_script.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_state.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/test_types.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/__init__.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_auth.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_errors.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_regex.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_strings.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_timer.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.15.1 → execsql2-2.15.2}/zensical.toml +0 -0
|
@@ -13,6 +13,24 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.15.2] - 2026-04-14
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `DT_Integer`, `DT_Float`, and `DT_Decimal` data type matchers now use pre-compiled regex class attributes instead of recompiling on every call — reduces overhead during large imports.
|
|
21
|
+
- `DT_Boolean` match tuples are now cached and only rebuilt when the `boolean_words`/`boolean_int` config changes, instead of on every `_is_match()`/`_from_data()` call.
|
|
22
|
+
- SQLite and DuckDB adapter methods (`table_exists`, `table_columns`, `view_exists`, `schema_exists`) now use the `_cursor()` context manager to prevent cursor leaks on exceptions.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `DT_Text.data_type_name` corrected from `"character"` to `"text"` — error messages now correctly identify the text data type instead of showing "character".
|
|
27
|
+
- `DT_Varchar._from_data()` now converts non-string data to string and enforces the 255-character length limit. Previously, non-string values passed through without conversion or length check.
|
|
28
|
+
- `WriteHooks.write_err()` no longer crashes on empty string input.
|
|
29
|
+
- `CondAstNode.eval()` now raises `CondParserError` for unknown node types instead of silently returning `None`.
|
|
30
|
+
- `NumericAstNode.eval()` now raises `NumericParserError` on division by zero instead of an unhandled `ZeroDivisionError`.
|
|
31
|
+
|
|
32
|
+
______________________________________________________________________
|
|
33
|
+
|
|
16
34
|
## [2.15.1] - 2026-04-14
|
|
17
35
|
|
|
18
36
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.15.
|
|
3
|
+
Version: 2.15.2
|
|
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
|
|
@@ -248,6 +248,11 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
248
248
|
| Empty-column check precedence | `DataTable` and `Database.populate_table()` had an operator precedence bug in the extra-column emptiness check — a redundant `and conf.del_empty_cols` caused incorrect short-circuit evaluation. Inherited from upstream. |
|
|
249
249
|
| SQLite import string processing | `SQLiteDatabase.populate_table()` applied `trim_strings`, `replace_newlines`, and `empty_strings` after copying row data, so processing never reached the INSERT. Fixed to process before extraction. Inherited from upstream. |
|
|
250
250
|
| `$CURRENT_DATABASE`/`$CURRENT_DBMS` stale after USE | These variables were only set at startup and on CONNECT, not refreshed when `USE` switched the active database. Now set in `set_static_system_vars()` so they update on any connection change. |
|
|
251
|
+
| `DT_Text.data_type_name` wrong | Was `"character"` (same as `DT_Character`), making error messages indistinguishable. Corrected to `"text"`. Inherited from upstream. |
|
|
252
|
+
| `DT_Varchar` non-string data unchecked | `_from_data()` only enforced the 255-char limit for `str` inputs; non-string values passed through without conversion or length check. Inherited from upstream. |
|
|
253
|
+
| `WriteHooks.write_err()` crash on empty string | `strval[-1]` raised `IndexError` on empty input. Fixed to use `str.endswith()`. Inherited from upstream. |
|
|
254
|
+
| `NumericParser` division by zero | `NumericAstNode.eval()` raised unhandled `ZeroDivisionError`. Now raises `NumericParserError` with a clear message. Inherited from upstream. |
|
|
255
|
+
| `CondAstNode.eval()` could return `None` | Missing fallthrough for unknown node types silently returned `None`. Now raises `CondParserError`. Inherited from upstream. |
|
|
251
256
|
|
|
252
257
|
______________________________________________________________________
|
|
253
258
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.15.
|
|
7
|
+
version = "2.15.2"
|
|
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" }
|
|
@@ -162,7 +162,7 @@ skip-magic-trailing-comma = false
|
|
|
162
162
|
line-ending = "auto"
|
|
163
163
|
|
|
164
164
|
[tool.bumpversion]
|
|
165
|
-
current_version = "2.15.
|
|
165
|
+
current_version = "2.15.2"
|
|
166
166
|
commit = true
|
|
167
167
|
commit_args = "--no-verify"
|
|
168
168
|
tag = true
|
|
@@ -569,7 +569,7 @@ class WriteHooks:
|
|
|
569
569
|
|
|
570
570
|
def write_err(self, strval: str) -> None:
|
|
571
571
|
"""Write an error string to the error-output hook, or to sys.stderr if unset."""
|
|
572
|
-
if strval
|
|
572
|
+
if not strval.endswith("\n"):
|
|
573
573
|
strval += "\n"
|
|
574
574
|
if self.err_func:
|
|
575
575
|
self.err_func(strval)
|
|
@@ -81,11 +81,10 @@ class DuckDBDatabase(Database):
|
|
|
81
81
|
def schema_exists(self, schema_name: str) -> bool:
|
|
82
82
|
"""Return True if the named schema exists in the current DuckDB catalog."""
|
|
83
83
|
# In DuckDB, the 'schemata' view is not limited to the current database.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
curs.close()
|
|
84
|
+
with self._cursor() as curs:
|
|
85
|
+
curs.execute(
|
|
86
|
+
"SELECT schema_name FROM information_schema.schemata WHERE schema_name = ? and catalog_name = ?;",
|
|
87
|
+
(schema_name, self.catalog_name),
|
|
88
|
+
)
|
|
89
|
+
rows = curs.fetchall()
|
|
91
90
|
return len(rows) > 0
|
|
@@ -82,21 +82,21 @@ class SQLiteDatabase(Database):
|
|
|
82
82
|
|
|
83
83
|
def table_exists(self, table_name: str, schema_name: str | None = None) -> bool:
|
|
84
84
|
"""Return True if the named table exists in the SQLite database."""
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
85
|
+
with self._cursor() as curs:
|
|
86
|
+
sql = "select name from sqlite_master where type='table' and name=?;"
|
|
87
|
+
try:
|
|
88
|
+
curs.execute(sql, (table_name,))
|
|
89
|
+
except ErrInfo:
|
|
90
|
+
raise
|
|
91
|
+
except Exception as e:
|
|
92
|
+
self.rollback()
|
|
93
|
+
raise ErrInfo(
|
|
94
|
+
type="db",
|
|
95
|
+
command_text=sql,
|
|
96
|
+
exception_msg=exception_desc(),
|
|
97
|
+
other_msg=f'Failed test for existence of SQLite table "{table_name}";',
|
|
98
|
+
) from e
|
|
99
|
+
rows = curs.fetchall()
|
|
100
100
|
return len(rows) > 0
|
|
101
101
|
|
|
102
102
|
def column_exists(
|
|
@@ -111,40 +111,40 @@ class SQLiteDatabase(Database):
|
|
|
111
111
|
|
|
112
112
|
def table_columns(self, table_name: str, schema_name: str | None = None) -> list[str]:
|
|
113
113
|
"""Return a list of column names for the given SQLite table."""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
114
|
+
with self._cursor() as curs:
|
|
115
|
+
quoted_tbl = self.quote_identifier(table_name)
|
|
116
|
+
sql = f"select * from {quoted_tbl} where 1=0;"
|
|
117
|
+
try:
|
|
118
|
+
curs.execute(sql)
|
|
119
|
+
except ErrInfo:
|
|
120
|
+
raise
|
|
121
|
+
except Exception as e:
|
|
122
|
+
self.rollback()
|
|
123
|
+
raise ErrInfo(
|
|
124
|
+
type="db",
|
|
125
|
+
command_text=sql,
|
|
126
|
+
exception_msg=exception_desc(),
|
|
127
|
+
other_msg=f"Failed to get column names for table {table_name} of {self.name()}",
|
|
128
|
+
) from e
|
|
129
|
+
return [d[0] for d in curs.description]
|
|
130
130
|
|
|
131
131
|
def view_exists(self, view_name: str) -> bool:
|
|
132
132
|
"""Return True if the named view exists in the SQLite database."""
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
133
|
+
with self._cursor() as curs:
|
|
134
|
+
sql = "select name from sqlite_master where type='view' and name=?;"
|
|
135
|
+
try:
|
|
136
|
+
curs.execute(sql, (view_name,))
|
|
137
|
+
except ErrInfo:
|
|
138
|
+
raise
|
|
139
|
+
except Exception as e:
|
|
140
|
+
self.rollback()
|
|
141
|
+
raise ErrInfo(
|
|
142
|
+
type="db",
|
|
143
|
+
command_text=sql,
|
|
144
|
+
exception_msg=exception_desc(),
|
|
145
|
+
other_msg=f'Failed test for existence of SQLite view "{view_name}";',
|
|
146
|
+
) from e
|
|
147
|
+
rows = curs.fetchall()
|
|
148
148
|
return len(rows) > 0
|
|
149
149
|
|
|
150
150
|
def schema_exists(self, schema_name: str) -> bool:
|
|
@@ -151,8 +151,6 @@ class CondAstNode(CondTokens):
|
|
|
151
151
|
|
|
152
152
|
def eval(self) -> bool:
|
|
153
153
|
"""Evaluate this subtree and return a boolean result."""
|
|
154
|
-
# Evaluates the subtrees and/or conditional value for this node,
|
|
155
|
-
# returning True or False.
|
|
156
154
|
if self.type == self.CONDITIONAL:
|
|
157
155
|
exec_fn = self.left[0].exec_fn
|
|
158
156
|
cmdargs = self.left[1]
|
|
@@ -168,6 +166,7 @@ class CondAstNode(CondTokens):
|
|
|
168
166
|
if lcond:
|
|
169
167
|
return True
|
|
170
168
|
return self.right.eval()
|
|
169
|
+
raise CondParserError(f"Unknown conditional node type: {self.type}")
|
|
171
170
|
|
|
172
171
|
|
|
173
172
|
# AST for numeric expressions
|
|
@@ -200,6 +199,8 @@ class NumericAstNode(NumTokens):
|
|
|
200
199
|
if self.type == self.MUL:
|
|
201
200
|
return lnum * rnum
|
|
202
201
|
elif self.type == self.DIV:
|
|
202
|
+
if rnum == 0:
|
|
203
|
+
raise NumericParserError("Division by zero.")
|
|
203
204
|
return lnum / rnum
|
|
204
205
|
elif self.type == self.ADD:
|
|
205
206
|
return lnum + rnum
|
|
@@ -296,14 +296,24 @@ class DT_Boolean(DataType):
|
|
|
296
296
|
data_type_name = "boolean"
|
|
297
297
|
data_type = bool
|
|
298
298
|
|
|
299
|
+
def __init__(self) -> None:
|
|
300
|
+
"""Initialise with empty match caches; populated on first use."""
|
|
301
|
+
self.true: tuple = ()
|
|
302
|
+
self.false: tuple = ()
|
|
303
|
+
self.bool_repr: tuple = ()
|
|
304
|
+
self._cache_key: tuple | None = None
|
|
305
|
+
|
|
299
306
|
def __repr__(self) -> str:
|
|
300
307
|
return "DT_Boolean()"
|
|
301
308
|
|
|
302
|
-
def
|
|
303
|
-
"""
|
|
309
|
+
def _ensure_bool_matches(self) -> None:
|
|
310
|
+
"""Rebuild true/false match tuples only when the config has changed."""
|
|
304
311
|
import execsql.state as _state
|
|
305
312
|
|
|
306
313
|
conf = _state.conf
|
|
314
|
+
key = (conf.boolean_words, conf.boolean_int)
|
|
315
|
+
if key == self._cache_key:
|
|
316
|
+
return
|
|
307
317
|
self.true = ("yes", "true")
|
|
308
318
|
self.false = ("no", "false")
|
|
309
319
|
if not conf.boolean_words:
|
|
@@ -313,6 +323,7 @@ class DT_Boolean(DataType):
|
|
|
313
323
|
self.true += ("1",)
|
|
314
324
|
self.false += ("0",)
|
|
315
325
|
self.bool_repr = self.true + self.false
|
|
326
|
+
self._cache_key = key
|
|
316
327
|
|
|
317
328
|
def _is_match(self, data: object) -> bool:
|
|
318
329
|
import execsql.state as _state
|
|
@@ -320,7 +331,7 @@ class DT_Boolean(DataType):
|
|
|
320
331
|
conf = _state.conf
|
|
321
332
|
if data is None:
|
|
322
333
|
return False
|
|
323
|
-
self.
|
|
334
|
+
self._ensure_bool_matches()
|
|
324
335
|
return bool(
|
|
325
336
|
isinstance(data, bool)
|
|
326
337
|
or conf.boolean_int
|
|
@@ -336,7 +347,7 @@ class DT_Boolean(DataType):
|
|
|
336
347
|
conf = _state.conf
|
|
337
348
|
if data is None:
|
|
338
349
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % "NULL")
|
|
339
|
-
self.
|
|
350
|
+
self._ensure_bool_matches()
|
|
340
351
|
if isinstance(data, bool):
|
|
341
352
|
return data
|
|
342
353
|
elif conf.boolean_int and type(data) is int and data in (0, 1):
|
|
@@ -352,6 +363,7 @@ class DT_Integer(DataType):
|
|
|
352
363
|
|
|
353
364
|
data_type_name = "integer"
|
|
354
365
|
data_type = int
|
|
366
|
+
_INT_RX = re.compile(r"^\s*[+-]?\d+\s*$")
|
|
355
367
|
|
|
356
368
|
def __repr__(self) -> str:
|
|
357
369
|
return "DT_Integer()"
|
|
@@ -367,7 +379,7 @@ class DT_Integer(DataType):
|
|
|
367
379
|
elif isinstance(data, str):
|
|
368
380
|
if leading_zero_num(data):
|
|
369
381
|
return False
|
|
370
|
-
if not
|
|
382
|
+
if not self._INT_RX.match(data):
|
|
371
383
|
return False
|
|
372
384
|
try:
|
|
373
385
|
i = int(data)
|
|
@@ -387,7 +399,7 @@ class DT_Integer(DataType):
|
|
|
387
399
|
return int(data)
|
|
388
400
|
else:
|
|
389
401
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
390
|
-
if isinstance(data, str) and not
|
|
402
|
+
if isinstance(data, str) and not self._INT_RX.match(data):
|
|
391
403
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
392
404
|
try:
|
|
393
405
|
i = int(data)
|
|
@@ -439,6 +451,7 @@ class DT_Float(DataType):
|
|
|
439
451
|
|
|
440
452
|
data_type_name = "float"
|
|
441
453
|
data_type = float
|
|
454
|
+
_FLOAT_RX = re.compile(r"^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$")
|
|
442
455
|
|
|
443
456
|
def __repr__(self) -> str:
|
|
444
457
|
return "DT_Float()"
|
|
@@ -450,7 +463,7 @@ class DT_Float(DataType):
|
|
|
450
463
|
return True
|
|
451
464
|
if leading_zero_num(data):
|
|
452
465
|
return False
|
|
453
|
-
if isinstance(data, str) and not
|
|
466
|
+
if isinstance(data, str) and not self._FLOAT_RX.match(data):
|
|
454
467
|
return False
|
|
455
468
|
try:
|
|
456
469
|
float(data)
|
|
@@ -465,7 +478,7 @@ class DT_Float(DataType):
|
|
|
465
478
|
return data
|
|
466
479
|
if leading_zero_num(data):
|
|
467
480
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
468
|
-
if isinstance(data, str) and not
|
|
481
|
+
if isinstance(data, str) and not self._FLOAT_RX.match(data):
|
|
469
482
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
470
483
|
try:
|
|
471
484
|
i = float(data)
|
|
@@ -480,6 +493,7 @@ class DT_Decimal(DataType):
|
|
|
480
493
|
data_type_name = "decimal"
|
|
481
494
|
data_type = Decimal
|
|
482
495
|
precspec = True
|
|
496
|
+
_DECIMAL_RX = re.compile(r"^[+-]?(\d+(\.\d*)?|\.\d+)$")
|
|
483
497
|
|
|
484
498
|
def __repr__(self) -> str:
|
|
485
499
|
return "DT_Decimal()"
|
|
@@ -504,7 +518,7 @@ class DT_Decimal(DataType):
|
|
|
504
518
|
self.set_scale_prec(data)
|
|
505
519
|
return data
|
|
506
520
|
elif isinstance(data, str):
|
|
507
|
-
if not
|
|
521
|
+
if not self._DECIMAL_RX.match(data):
|
|
508
522
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
509
523
|
try:
|
|
510
524
|
dec = Decimal(data)
|
|
@@ -565,21 +579,20 @@ class DT_Varchar(DataType):
|
|
|
565
579
|
# This varchar data type is the same as the character data type.
|
|
566
580
|
if data is None:
|
|
567
581
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % "NULL")
|
|
568
|
-
|
|
569
|
-
if isinstance(data, str):
|
|
582
|
+
if not isinstance(data, str):
|
|
570
583
|
try:
|
|
571
|
-
data =
|
|
584
|
+
data = str(data)
|
|
572
585
|
except ValueError as e:
|
|
573
586
|
raise DataTypeError(self.data_type_name, self._CONV_ERR % data) from e
|
|
574
|
-
|
|
575
|
-
|
|
587
|
+
if len(data) > 255:
|
|
588
|
+
raise DataTypeError(self.data_type_name, self._CONV_ERR % data)
|
|
576
589
|
return data
|
|
577
590
|
|
|
578
591
|
|
|
579
592
|
class DT_Text(DataType):
|
|
580
593
|
"""Unbounded text string data type."""
|
|
581
594
|
|
|
582
|
-
data_type_name = "
|
|
595
|
+
data_type_name = "text"
|
|
583
596
|
|
|
584
597
|
def __repr__(self) -> str:
|
|
585
598
|
return "DT_Text()"
|
|
@@ -299,7 +299,7 @@ class TestNumericParserEdgeCases:
|
|
|
299
299
|
return NumericParser(expr).parse().eval()
|
|
300
300
|
|
|
301
301
|
def test_division_by_zero(self):
|
|
302
|
-
with pytest.raises(
|
|
302
|
+
with pytest.raises(NumericParserError, match="Division by zero"):
|
|
303
303
|
self._eval("1 / 0")
|
|
304
304
|
|
|
305
305
|
def test_unmatched_open_paren(self):
|
|
@@ -338,7 +338,7 @@ class TestNumericParserEdgeCases:
|
|
|
338
338
|
assert self._eval(expr) == 50
|
|
339
339
|
|
|
340
340
|
def test_float_division_by_zero(self):
|
|
341
|
-
with pytest.raises(
|
|
341
|
+
with pytest.raises(NumericParserError, match="Division by zero"):
|
|
342
342
|
self._eval("1.0 / 0.0")
|
|
343
343
|
|
|
344
344
|
def test_unmatched_close_paren(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|