execsql2 2.33.0__tar.gz → 2.34.0__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.33.0 → execsql2-2.34.0}/CHANGELOG.md +20 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/PKG-INFO +3 -3
- {execsql2-2.33.0 → execsql2-2.34.0}/README.md +2 -2
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/about/divergence.md +10 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/api/db.md +6 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/dev/adding_db_adapters.md +18 -17
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/dev/architecture.md +2 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/formatter.md +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/sql_syntax.md +20 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/index.md +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/reference/lint.md +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/reference/metacommands.md +22 -3
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/package-lock.json +2 -2
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/package.json +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/syntaxes/execsql.tmLanguage.json +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/pyproject.toml +2 -2
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/base.py +71 -8
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/duckdb.py +6 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/mysql.py +54 -2
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/oracle.py +36 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/postgres.py +17 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/sqlite.py +7 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/sqlserver.py +24 -0
- execsql2-2.34.0/src/execsql/db/target_types.py +277 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/json.py +51 -7
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/protocol.py +1 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/base.py +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/csv.py +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/__init__.py +17 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/io_export.py +3 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/io_fileops.py +2 -2
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/variables.py +4 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/types.py +2 -2
- execsql2-2.34.0/tests/data/existing_table_loads/changes.json +5618 -0
- execsql2-2.34.0/tests/data/existing_table_loads/duckdb.json +1112 -0
- execsql2-2.34.0/tests/data/existing_table_loads/mysql.json +1782 -0
- execsql2-2.34.0/tests/data/existing_table_loads/mysql_files.json +2104 -0
- execsql2-2.34.0/tests/data/existing_table_loads/postgres.json +1166 -0
- execsql2-2.34.0/tests/data/existing_table_loads/postgres_files.json +1228 -0
- execsql2-2.34.0/tests/data/existing_table_loads/sqlite.json +1090 -0
- execsql2-2.34.0/tests/data/existing_table_loads/sqlserver.json +1307 -0
- execsql2-2.34.0/tests/db/test_existing_table_loads.py +357 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_postgres_inprocess.py +13 -1
- execsql2-2.34.0/tests/db/test_target_types.py +520 -0
- execsql2-2.34.0/tests/exporters/test_jsonl.py +101 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/test_mysql.py +76 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/live_db.py +23 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_script.py +13 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/uv.lock +1 -1
- {execsql2-2.33.0 → execsql2-2.34.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.github/dependabot.yml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.github/workflows/ci-cd.yml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.gitignore +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.pre-commit-config.yaml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.pre-commit-hooks.yaml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.python-version +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/.readthedocs.yaml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/CONTRIBUTING.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/LICENSE.txt +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/NOTICE +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/SECURITY.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/about/contributors.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/about/copyright.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/api/cli.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/api/exporters.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/api/importers.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/api/index.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/api/metacommands.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/dev/adding_exporters.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/dev/adding_importers.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/dev/adding_metacommands.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/dev/releasing.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/getting-started/installation.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/getting-started/requirements.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/getting-started/syntax.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/debugging.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/documentation.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/editors.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/encoding.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/examples.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/logging.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/usage.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/guides/using_scripts.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/Compare_planets.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/actions.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/actions2.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/checkboxes.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/connect.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/data_error1_screenshot.jpg +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/execsql_logo_01.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/fatals.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/pause_terminal_sm.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/set_build_commands.jpg +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/unit_conversions_029.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/unmatched.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/images/vim_execsql_highlight.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/reference/configuration.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/reference/security.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/reference/substitution_vars.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/templates/compare.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/templates/glossary.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/templates/index.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/docs/templates/upsert.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/plugin-template/README.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/plugin-template/pyproject.toml +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/plugin-template/src/execsql_plugin_YOURNAME/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/plugin-template/tests/test_plugin.py.example +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/.vscodeignore +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/LICENSE.txt +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/README.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/extension.js +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/extras/vscode-execsql/icon.png +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/justfile +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/scripts/benchmarks/README.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/scripts/benchmarks/results/.gitignore +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/scripts/benchmarks/run_benchmarks.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/scripts/generate_vscode_grammar.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/scripts/release_notes.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/__main__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/api.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/application.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/config.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/format.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/init.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/lint.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/listing.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/lsp.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/commands/run.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/dispatch.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/dsn.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/help.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/lint.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/options.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/cli/run.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/config.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/config_validation.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/data/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/data/execsql.conf.template +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/data/script_header.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/access.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/dsn.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/factory.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/firebird.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/odbc.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/db/tiers.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/debug/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/debug/repl.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exceptions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/base.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/delimited.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/duckdb.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/feather.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/html.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/latex.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/markdown.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/ods.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/parquet.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/pretty.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/raw.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/sqlite.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/templates.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/values.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/xls.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/xlsx.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/xml.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/yaml.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/exporters/zip.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/format.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/gui/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/gui/base.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/gui/console.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/gui/desktop.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/gui/tui.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/feather.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/json.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/ods.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/importers/xls.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/interactive.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/_util.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/cache.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/code_actions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/completion.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/diagnostics.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/document.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/folding.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/formatting.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/hover.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/navigation.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/rename.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/lsp/server.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/manifest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/conditions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/connect.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/control.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/data.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/debug.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/dispatch.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/effects.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/io.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/io_import.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/io_write.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/prompt.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/reference.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/script_ext.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/system.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/metacommands/upsert.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/models.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/parser.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/plugins.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/py.typed +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/ast.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/control.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/engine.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/executor.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/script/parser.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/state.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/state.pyi +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/auth.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/color.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/crypto.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/datetime.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/errors.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/fileio.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/gui.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/mail.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/numeric.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/paths.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/regex.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/strings.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/src/execsql/utils/timer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/README.md +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/config_settings.sqlite +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/example_config_prompt.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/make_config_db.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/md_compare.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/md_glossary.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/md_upsert.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/pg_compare.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/pg_glossary.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/script_template.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/ss_compare.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/ss_glossary.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/templates/ss_upsert.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_cli.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_cli_e2e.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_cli_run.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_cli_surface.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_commands.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_config_validate.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_dispatch.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_error_location.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_init.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_lint.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_lint_fixtures.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_log_exit_record.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_manifest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_ping.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_profile.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_sigterm.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/cli/test_undeclared_imports.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/formatter_shapes/comment_anchoring.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/formatter_shapes/escapes_and_literals.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/formatter_shapes/substitution_and_metacommands.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/formatter_shapes/unparsable_sql.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/lint/conditions_and_blocks.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/lint/does_not_parse.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/lint/every_metacommand.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/lint/invalid.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/ods/libreoffice.fods +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/data/ods/libreoffice.ods +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_access_windows.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_base.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_bulk_inserts.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_copy_new.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_db_adapters_mocked.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_db_adapters_mocked_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_dsn.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_duckdb.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_factory.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_factory_postgres_encoding.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_import_atomic.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_import_replacement_quoted.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_import_short_rows.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_import_timezones.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_mysql_case_folding.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_mysql_inprocess.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_mysql_trailing_blank_lines.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_new_table_checks.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_odbc_fresh_sessions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_oracle_driver.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_postgres.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_quoted_substitution_roundtrip.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_replacement_missing_table.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_roundtrip_adapters.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_sqlite.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_sqlite_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_sqlserver_driver18.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_sqlserver_inprocess.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_tiers.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/db/test_transactions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_base.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_db.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_delimited.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_duckdb_exporter.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_exporters.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_feather.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_html_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_html_latex.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_json.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_json_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_latex_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_markdown.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_ods.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_ods_export.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_parquet.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_pretty_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_raw_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_columnar.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_dbfile.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_delimited.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_json.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_latex_xml.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_markup.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_ods.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_pretty_raw.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_template.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_values.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_roundtrip_xlsx.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_sqlite_exporter.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_templates.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_templates_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_values_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_values_roundtrip.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_xls_xlsx.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_xlsx.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_xml.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_yaml.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/exporters/test_zip.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_backends.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_backends_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_compare_stats.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_compute_row_diffs.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_desktop_dialogs.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_format_table.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_tui_pilot.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_tui_pilot_complex.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/gui/test_utils_gui_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_base_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_csv_blank_lines.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_csv_edge_cases.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_csv_importer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_feather_importer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_json_importer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_ods_importer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_ods_values.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_roundtrip_csv.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/importers/test_xls_importer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/test_dsn.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/test_duckdb.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/test_postgres.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/integration/test_sqlite.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_cache.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_code_actions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_completion.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_diagnostics.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_document.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_folding.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_formatting.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_hover.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_lsp_command.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_navigation.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_rename.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/lsp/test_server.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_assert.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_breakpoint.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_conditions_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_connect.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_io_export.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_io_import.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_connect.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_data.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_fileops_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_io.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_io_write_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_script_ext.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_system.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_metacommands_system_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_pg_upsert.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_prompt.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_row_count.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_serve_output.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/metacommands/test_show_scripts.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_lint_bad.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_smoke/sample.json +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_smoke/sample.jsonl +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/audit_smoke.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/config_runtime.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/control_flow.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/counters_and_locals.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/error_handling.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/import_options/has_comments.csv +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/import_options.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/includes/helper.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/includes.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/io_formats.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/io_roundtrip.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/multi_db.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/parquet_feather_roundtrip.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/parse_only/parse_tree.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/predicates.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/scripts.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/smoke.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/subroutine_loops.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/subvars_advanced.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/template_export/greeting.tmpl +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/template_export.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/timer.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/transactions.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/write_create_table.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/fixtures/xlsx_ods_roundtrip.sql +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/scripts/test_sql_scripts.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_env_detection.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_expansion_bomb.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_import_regex_hardening.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_log_redaction.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_log_secrets.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_network_paths.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_odbc_connection_strings.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_odbc_injection.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_path_containment.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_substitution_injection.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/security/test_zip_bomb.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/templates/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/templates/conftest.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/templates/test_compare.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/templates/test_glossary.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/templates/test_static.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/templates/test_upsert.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_api.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_api_errors.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_api_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_api_filewriter.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_api_isolation.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_api_subprocess.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_ast.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_ast_parser.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_bundled_templates.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_config.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_config_data.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_config_extended.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_date_inference.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_debug_repl.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_engine.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_error_messages.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_exceptions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_executor.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_executor_inprocess.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_format.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_interactive.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_mail.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_metacommand_errors.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_models.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_package.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_parser.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_parser_params.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_plugins.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_reference.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_registry.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_release_notes.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_state.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_tooling_versions.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/test_types.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/__init__.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_auth.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_auth_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_crypto.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_datetime.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_encodedfile_context.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_errors.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_errors_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_fileio.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_fileio_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_keyring_rules.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_mail_tls.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_numeric.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_regex.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_strings.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_timer.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/tests/utils/test_timer_extra.py +0 -0
- {execsql2-2.33.0 → execsql2-2.34.0}/zensical.toml +0 -0
|
@@ -13,6 +13,26 @@ ______________________________________________________________________
|
|
|
13
13
|
|
|
14
14
|
______________________________________________________________________
|
|
15
15
|
|
|
16
|
+
## [2.34.0] - 2026-10-18
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **`EXPORT … AS JSONL` and `EXPORT QUERY … AS JSONL`** write [JSON Lines](https://jsonlines.org/): one object per row, one row per line. `APPEND` adds rows to the file, which stays valid; `IMPORT … FROM JSON` reads it back.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **A value with a fraction loaded into an integer column** (`INTEGER`, `BIGINT`, `DECIMAL(10,0)`, …) of an existing table by `IMPORT` or `COPY` stops the load with an error that names the value and the column, and nothing is loaded. The database used to round it (`1.5` stored as `2`), or on SQL Server truncate it. A delimited file loaded into PostgreSQL or MySQL is not affected: the server reads it as written. Round the values in the source, or load them into a `DECIMAL` column with places for the fraction.
|
|
25
|
+
- **MySQL / MariaDB sessions run in strict mode**: a SQL statement, `IMPORT` or `COPY` that gives a column a value MySQL can't store as given stops with MySQL's error, and nothing is loaded. MySQL used to store a changed value with a warning (`3000000000` as `2147483647`, `2024-02-30` as `0000-00-00`, `abcdef` in a `VARCHAR(3)` as `abc`). A timestamp ending in `Z` is one such value: write it with `+00:00`. To keep the old behavior in a script, run `SET SESSION sql_mode = 'ANSI';` after connecting.
|
|
26
|
+
- **Scripts with many substitution variables run faster**: a statement or metacommand with no `!!`, `!'!` or `!"!` in it is no longer checked against every defined variable. With 500 variables each such line took about 0.1 ms longer; a 1,000-pass `LOOP` over `EXECUTE SCRIPT` with 200 variables now runs in half the time.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **`IMPORT` and `COPY` into a table that already exists** convert each value by the type of the column it goes into, not the type guessed from the source data. A text column gets the value as written: `yes`, `1`, `01/05/2024`, `1e3` and `" 7 "` were stored as `true`, `true`, `2024-01-05 00:00:00`, `1000` and `7`. On MySQL a missing value in such a column is NULL, not `''`. `TO NEW` and `TO REPLACEMENT` tables are created and filled as before.
|
|
31
|
+
- **`COPY` of a column of 0s and 1s into a PostgreSQL integer, numeric or float column** loads the numbers; it failed with "column … is of type integer but expression is of type boolean". The same goes for true/false values (`yes`/`no`, `t`/`f`) copied, or imported from a spreadsheet, JSON or Feather file, into such a column: they load as 1 and 0, as on the other databases.
|
|
32
|
+
- **A missing date or timestamp loaded into MySQL by `IMPORT` or `COPY`** is stored as NULL; it was stored as `0000-00-00` (or `0` in a number column). Delimited files were not affected.
|
|
33
|
+
|
|
34
|
+
______________________________________________________________________
|
|
35
|
+
|
|
16
36
|
## [2.33.0] - 2026-10-16
|
|
17
37
|
|
|
18
38
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: execsql2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.34.0
|
|
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/en/stable/
|
|
6
6
|
Project-URL: Repository, https://github.com/geocoug/execsql
|
|
@@ -318,7 +318,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/stable/getti
|
|
|
318
318
|
**Data movement**
|
|
319
319
|
|
|
320
320
|
- Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
|
|
321
|
-
- Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
321
|
+
- Export query results in 20+ formats including CSV, TSV, JSON, JSON Lines, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
322
322
|
- Copy data between databases, including across different DBMS types.
|
|
323
323
|
|
|
324
324
|
**Script logic**
|
|
@@ -441,7 +441,7 @@ execsql format --no-sql --in-place scripts/
|
|
|
441
441
|
```yaml
|
|
442
442
|
repos:
|
|
443
443
|
- repo: https://github.com/geocoug/execsql
|
|
444
|
-
rev: v2.
|
|
444
|
+
rev: v2.34.0
|
|
445
445
|
hooks:
|
|
446
446
|
- id: execsql-format
|
|
447
447
|
- id: execsql-lint
|
|
@@ -198,7 +198,7 @@ See the [full options reference](https://execsql2.readthedocs.io/en/stable/getti
|
|
|
198
198
|
**Data movement**
|
|
199
199
|
|
|
200
200
|
- Import data from CSV, TSV, JSON, Excel, OpenDocument, Feather, or Parquet files into a database table.
|
|
201
|
-
- Export query results in 20+ formats including CSV, TSV, JSON, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
201
|
+
- Export query results in 20+ formats including CSV, TSV, JSON, JSON Lines, YAML, XML, HTML, Markdown, LaTeX, XLSX, OpenDocument, Feather, Parquet, HDF5, DuckDB, SQLite, plain text, and Jinja2 templates.
|
|
202
202
|
- Copy data between databases, including across different DBMS types.
|
|
203
203
|
|
|
204
204
|
**Script logic**
|
|
@@ -321,7 +321,7 @@ execsql format --no-sql --in-place scripts/
|
|
|
321
321
|
```yaml
|
|
322
322
|
repos:
|
|
323
323
|
- repo: https://github.com/geocoug/execsql
|
|
324
|
-
rev: v2.
|
|
324
|
+
rev: v2.34.0
|
|
325
325
|
hooks:
|
|
326
326
|
- id: execsql-format
|
|
327
327
|
- id: execsql-lint
|
|
@@ -43,6 +43,7 @@ ______________________________________________________________________
|
|
|
43
43
|
| `PARQUET` | Export query or table results to Apache Parquet via `polars`. |
|
|
44
44
|
| `FEATHER` | Export to Apache Feather/IPC via `polars` (upstream used `pandas` + `pyarrow`). |
|
|
45
45
|
| `YAML` | Export query or table results as a YAML sequence of mappings via `PyYAML`. |
|
|
46
|
+
| `JSONL` | Export query or table results as JSON Lines: one object per row, one row per line; `APPEND` adds rows (#7). |
|
|
46
47
|
| `MARKDOWN` / `MD` | Export query or table results as a GitHub-Flavored Markdown (GFM) pipe table. Pure Python, no optional dependencies. |
|
|
47
48
|
| `XLSX` | Export query or table results to an Excel XLSX workbook via `openpyxl` (single or multi-sheet). |
|
|
48
49
|
|
|
@@ -228,6 +229,12 @@ Upstream opened MySQL and MariaDB connections as `latin1`, and execsql2 did the
|
|
|
228
229
|
|
|
229
230
|
The default is now `utf8mb4`. This does not change stored data or column charsets: the connection charset governs only how text moves between client and server, and MySQL transcodes to and from each column's own charset. A latin1 column read over a utf8mb4 connection returns the same string it always did. `-e latin1` restores the upstream behavior.
|
|
230
231
|
|
|
232
|
+
### MySQL Strict Mode
|
|
233
|
+
|
|
234
|
+
Upstream set `sql_mode='ANSI'` on every MySQL and MariaDB connection. That replaces the server's whole mode, and `ANSI` leaves out `STRICT_TRANS_TABLES`, so MySQL stored a changed value with a warning instead of raising an error: `3000000000` in an `INTEGER` column became `2147483647`, `2024-02-30` became `0000-00-00`, and `abcdef` in a `VARCHAR(3)` became `abc`. That applied to plain SQL statements, `IMPORT` and `COPY` alike (#101).
|
|
235
|
+
|
|
236
|
+
The mode is now `ANSI,STRICT_TRANS_TABLES`, so such a value is an error, as in the `mysql` client with the server's default settings. The mode stays fixed rather than added to the server's own, because execsql's quoting of literals relies on backslash escapes being on (no `NO_BACKSLASH_ESCAPES`). `LOAD DATA LOCAL`, which `IMPORT` uses for a delimited file, ignores strict mode, so `IMPORT` checks its warnings and rolls the load back if MySQL changed a value. `SET SESSION sql_mode = 'ANSI';` in a script restores the upstream behavior for that session, `LOAD DATA` included.
|
|
237
|
+
|
|
231
238
|
### Database Support Tiers
|
|
232
239
|
|
|
233
240
|
Upstream documented all nine DBMS adapters with equal confidence. execsql2 splits them by what is actually verified: **Supported** (PostgreSQL, MySQL/MariaDB, MS SQL Server, SQLite, DuckDB) run against a live server or a real database file on every CI run, while **best effort** (MS Access, Firebird, Oracle, ODBC DSN) are carried forward from the monolith and are exercised nowhere in CI.
|
|
@@ -436,6 +443,9 @@ These are behavioral changes driven by security or correctness issues in the ups
|
|
|
436
443
|
| XLSX export wrote `numeric` columns as text | `_cell_value()` passed `int` and `float` straight to openpyxl but let every other type fall through to `str()`. PostgreSQL, MySQL, and DuckDB all return `Decimal` for `numeric`/`decimal` columns, so measurement values arrived in Excel as text cells — invisible in a screenshot, useless in a formula, a sort, or a chart. openpyxl lists `Decimal` in its own `NUMERIC_TYPES` and stores it as a real numeric cell, so the conversion was never needed. Found by exporting from a live PostgreSQL container and checking `cell.data_type` rather than `cell.value`. |
|
|
437
444
|
| `EMAIL` SMTP login without a password | Upstream calls `smtplib.login()` with only a username when `[email] password` and `enc_password` are both unset, which raises `TypeError` from the stdlib — `login()` has no single-argument form. execsql2 reports a configuration error naming the missing password instead. |
|
|
438
445
|
| Type scan of large integers | Working out a column's type tested each integer against the binary type by building `bytearray(n)`, n zero bytes, so a column of large integers (epoch milliseconds: about a terabyte per value) hung or ran out of memory. The binary test now answers for an integer without building it; the inferred types are unchanged. |
|
|
446
|
+
| Values loaded into an existing table | `IMPORT` and `COPY` converted every value to the type guessed from the source data, even when the table was already there with its own column types, so a text column got a different string than the source held (`yes` and `1` stored as `true`, `01/05/2024` as `2024-01-05 00:00:00`, `1e3` as `1000`), and on PostgreSQL a column of 0s and 1s copied into an integer column failed (the guess was boolean). For a table that exists, the target column's type now decides: a text column gets the value as written, a number column gets true/false values as 1 and 0, and other columns are converted as before. Each adapter reads the column types from its catalog (`pg_attribute`, `SHOW COLUMNS`, `sys.columns`) or its driver's description of the table. `TO NEW` / `REPLACEMENT` are unchanged. |
|
|
447
|
+
| Missing dates loaded into MySQL | `IMPORT` and `COPY` sent a missing value in a column scanned as a date or timestamp as `''`, so MySQL stored `0000-00-00` (or `0` in a number column) instead of NULL. It is sent as NULL. |
|
|
448
|
+
| Fractions loaded into an integer column | A value with a fraction going into an integer column (including `DECIMAL(p,0)`, `NUMERIC(p)` and Oracle `NUMBER(p)`) of an existing table is an error naming the value and the column. Upstream sent the number and the database rounded it (`1.5` → `2`; SQL Server truncated it to `1`). |
|
|
439
449
|
|
|
440
450
|
______________________________________________________________________
|
|
441
451
|
|
|
@@ -8,6 +8,12 @@ If you are adding support for a new database, start with the [Adding Database Ad
|
|
|
8
8
|
|
|
9
9
|
::: execsql.db.base
|
|
10
10
|
|
|
11
|
+
## Target column types
|
|
12
|
+
|
|
13
|
+
`IMPORT` and `COPY` into a table that already exists convert each value by the type of the column it goes into (`Database.column_kinds()`), not by the type scanned from the source data.
|
|
14
|
+
|
|
15
|
+
::: execsql.db.target_types
|
|
16
|
+
|
|
11
17
|
## Database factory
|
|
12
18
|
|
|
13
19
|
Convenience constructors used internally to create typed `Database` instances. The CLI calls the appropriate factory function based on the `-t` flag value. Each factory validates its arguments (e.g., checking that a file exists) before constructing and returning the adapter.
|
|
@@ -133,23 +133,24 @@ class MyDBDatabase(Database):
|
|
|
133
133
|
|
|
134
134
|
These are the instance attributes and methods you must configure correctly:
|
|
135
135
|
|
|
136
|
-
| Attribute / Method | Type | Purpose
|
|
137
|
-
| ------------------ | ----------------- |
|
|
138
|
-
| `self.type` | `DbType` | DBMS type token (e.g., `dbt_sqlite`). Controls quoting and type-mapping.
|
|
139
|
-
| `self.paramstr` | `str` | SQL parameter placeholder: `"?"` (most drivers) or `"%s"` (psycopg, PyMySQL).
|
|
140
|
-
| `self.encoding` | `str` | Database character encoding. Detect from the database on connect if possible.
|
|
141
|
-
| `self.autocommit` | `bool` | `True` means the driver commits automatically; `False` requires explicit commits.
|
|
142
|
-
| `self.conn` | driver connection | Set in `open_db()`.
|
|
143
|
-
| `open_db()` | method | **Must override.** Establish the connection and assign `self.conn`.
|
|
144
|
-
| `table_exists()` | method | **Must override.** Query the DBMS catalog. Use parameterized queries.
|
|
145
|
-
| `column_exists()` | method | **Must override.** Check column presence.
|
|
146
|
-
| `table_columns()` | method | **Must override.** Return column names for a table.
|
|
147
|
-
| `view_exists()` | method | **Must override.** Check view presence.
|
|
148
|
-
| `schema_exists()` | method | **Must override.** Return `False` if schemas are not supported.
|
|
149
|
-
| `drop_table()` | method | **Must override.** Drop a table (used by IMPORT when creating fresh).
|
|
150
|
-
| `populate_table()` | method | **Must override.** Bulk-load rows from a generator (used by IMPORT).
|
|
151
|
-
| `row_inserter()` | method | Override for a faster bulk path that stores exactly what `executemany()` stores.
|
|
152
|
-
| `
|
|
136
|
+
| Attribute / Method | Type | Purpose |
|
|
137
|
+
| ------------------ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
138
|
+
| `self.type` | `DbType` | DBMS type token (e.g., `dbt_sqlite`). Controls quoting and type-mapping. |
|
|
139
|
+
| `self.paramstr` | `str` | SQL parameter placeholder: `"?"` (most drivers) or `"%s"` (psycopg, PyMySQL). |
|
|
140
|
+
| `self.encoding` | `str` | Database character encoding. Detect from the database on connect if possible. |
|
|
141
|
+
| `self.autocommit` | `bool` | `True` means the driver commits automatically; `False` requires explicit commits. |
|
|
142
|
+
| `self.conn` | driver connection | Set in `open_db()`. |
|
|
143
|
+
| `open_db()` | method | **Must override.** Establish the connection and assign `self.conn`. |
|
|
144
|
+
| `table_exists()` | method | **Must override.** Query the DBMS catalog. Use parameterized queries. |
|
|
145
|
+
| `column_exists()` | method | **Must override.** Check column presence. |
|
|
146
|
+
| `table_columns()` | method | **Must override.** Return column names for a table. |
|
|
147
|
+
| `view_exists()` | method | **Must override.** Check view presence. |
|
|
148
|
+
| `schema_exists()` | method | **Must override.** Return `False` if schemas are not supported. |
|
|
149
|
+
| `drop_table()` | method | **Must override.** Drop a table (used by IMPORT when creating fresh). |
|
|
150
|
+
| `populate_table()` | method | **Must override.** Bulk-load rows from a generator (used by IMPORT). |
|
|
151
|
+
| `row_inserter()` | method | Override for a faster bulk path that stores exactly what `executemany()` stores. |
|
|
152
|
+
| `column_kinds()` | method | Override if the driver's description of a table does not tell its column types apart (see `db/target_types.py`); IMPORT and COPY into an existing table convert each value by them. |
|
|
153
|
+
| `exec_cmd()` | method | Override if the DBMS can execute stored procedures or views as commands. |
|
|
153
154
|
|
|
154
155
|
Methods inherited from `Database` that you get for free include `execute()`, `cursor()`, `close()`, `commit()`, `rollback()`, `select_rowsource()`, `select_data()`, `schema_qualified_table_name()`, `quote_identifier()`, and `paramsubs()`.
|
|
155
156
|
|
|
@@ -264,6 +264,8 @@ ______________________________________________________________________
|
|
|
264
264
|
|
|
265
265
|
The base class provides shared implementations for `execute()`, `select_data()`, `select_rowsource()`, `select_rowdict()`, `commit()`, `rollback()`, and `populate_table()`.
|
|
266
266
|
|
|
267
|
+
`populate_table()` converts each value to the type scanned from the source (`models.DataTable`). For a table that was already there (`existing_table=True`, passed by IMPORT and COPY), `column_kinds()` reports the target columns' types and `db/target_types.py` picks each column's conversion instead: a text column gets the value as given, an integer column whole numbers or an error. `column_kinds()` reads the driver's description of `select * from <table> where 1=0` by default; PostgreSQL, MySQL and SQL Server read their catalogs, where the description cannot tell some types apart. `tests/db/test_existing_table_loads.py` compares what every value type stores in every column type with the record in `tests/data/existing_table_loads/`.
|
|
268
|
+
|
|
267
269
|
### DatabasePool
|
|
268
270
|
|
|
269
271
|
`DatabasePool` is a dict-like container mapping string aliases to open `Database` instances. It is the canonical `_state.dbs` object. Key operations:
|
|
@@ -272,7 +272,7 @@ select id,name,created_at from users where active = true order by name;
|
|
|
272
272
|
```yaml
|
|
273
273
|
repos:
|
|
274
274
|
- repo: https://github.com/geocoug/execsql
|
|
275
|
-
rev: v2.
|
|
275
|
+
rev: v2.34.0
|
|
276
276
|
hooks:
|
|
277
277
|
- id: execsql-format
|
|
278
278
|
- id: execsql-lint # optional: fail the commit on lint errors
|
|
@@ -4,6 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
When execsql connects to a SQL Server or MySQL/MariaDB database, it automatically configures the DBMS to expect ANSI-compatible SQL, to allow the use of more standards-compliant, and thus consistent, SQL. In particular, for MySQL/MariaDB, note that the double-quote character, rather than the backtick character, must be used to quote table, schema, and column names, and only the apostrophe can be used to quote character data.
|
|
6
6
|
|
|
7
|
+
### MySQL strict mode { #mysql_strict_mode }
|
|
8
|
+
|
|
9
|
+
On MySQL/MariaDB, execsql sets the session's `sql_mode` to `ANSI,STRICT_TRANS_TABLES`. A value that MySQL can't store as given is an error, for a SQL statement, [IMPORT](../reference/metacommands.md#import) or [COPY](../reference/metacommands.md#copy):
|
|
10
|
+
|
|
11
|
+
```sql
|
|
12
|
+
create table m (n integer, d date, s varchar(3));
|
|
13
|
+
insert into m values (3000000000, '2024-02-30', 'abcdef');
|
|
14
|
+
-- ERROR 1264 (22003): Out of range value for column 'n' at row 1
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Without strict mode, MySQL would have stored `2147483647`, `0000-00-00` and `abc` with only a warning. This is how the `mysql` client behaves with the server's default settings. An IMPORT of a delimited file that MySQL reads with `LOAD DATA` is held to the same rule: if MySQL warns that it changed a value, the IMPORT stops and nothing is loaded.
|
|
18
|
+
|
|
19
|
+
MySQL in strict mode does not read a trailing `Z` as a time zone: write `2024-01-05T10:00:00Z` as `2024-01-05 10:00:00+00:00`. A decimal with more places than its column has (`1.2345` into `DECIMAL(10,3)`) is still rounded, as MySQL does in strict mode.
|
|
20
|
+
|
|
21
|
+
A script that relies on MySQL's changed values can turn strict mode off for the rest of its session:
|
|
22
|
+
|
|
23
|
+
```sql
|
|
24
|
+
SET SESSION sql_mode = 'ANSI';
|
|
25
|
+
```
|
|
26
|
+
|
|
7
27
|
## Implicit Commits
|
|
8
28
|
|
|
9
29
|
By default, execsql immediately commits all SQL statements. The [AUTOCOMMIT](../reference/metacommands.md#autocommit) metacommand can be used to turn off automatic commits, and the [BATCH](../reference/metacommands.md#batch) metacommand can be used to delay commits until the end of a batch. [IMPORT](../reference/metacommands.md#import) and [COPY](../reference/metacommands.md#copy) are the only metacommands that change data, and they also automatically commit their changes when complete (unless [AUTOCOMMIT](../reference/metacommands.md#autocommit) has been turned off). If a new table is created with either of these metacommands (through the use of the NEW or REPLACEMENT keywords), the CREATE TABLE statement will not be committed separately from the data addition, except when using Firebird. Thus, if an error occurs during addition of the data, the new target table will not exist---except when using Firebird.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
You can use *execsql* to:
|
|
10
10
|
|
|
11
11
|
- Import data from text files and spreadsheets into a database.
|
|
12
|
-
- Export tables and views to ~30 data formats including CSV, OpenDocument spreadsheets, XLSX, HTML, JSON, YAML, Markdown, LaTeX, XML, Parquet, Feather, HDF5, SQLite, and DuckDB.
|
|
12
|
+
- Export tables and views to ~30 data formats including CSV, OpenDocument spreadsheets, XLSX, HTML, JSON, JSON Lines, YAML, Markdown, LaTeX, XML, Parquet, Feather, HDF5, SQLite, and DuckDB.
|
|
13
13
|
- Copy data between different databases, even databases using different DBMSs.
|
|
14
14
|
- Display tables or views on the terminal or in a GUI dialog window.
|
|
15
15
|
- Export data using template processors to produce non-tabular output with customized format and contents.
|
|
@@ -158,7 +158,7 @@ The `execsql-lint` [pre-commit](https://pre-commit.com/) hook runs `execsql lint
|
|
|
158
158
|
```yaml
|
|
159
159
|
repos:
|
|
160
160
|
- repo: https://github.com/geocoug/execsql
|
|
161
|
-
rev: v2.
|
|
161
|
+
rev: v2.34.0
|
|
162
162
|
hooks:
|
|
163
163
|
- id: execsql-lint
|
|
164
164
|
args: [--ignore, V002] # optional: any execsql lint option
|
|
@@ -889,6 +889,8 @@ Copies the data from a data table or view in one database to a data table in a s
|
|
|
889
889
|
|
|
890
890
|
The second (destination) table must have column names that are identical to the names of the columns in the first (source) table. The second table may have additional columns; if it does, they will not be affected and their names don't matter. The data types in the columns to be copied must be compatible, though not necessarily identical. The order of the columns in the two tables does not have to be identical.
|
|
891
891
|
|
|
892
|
+
When neither "NEW" nor "REPLACEMENT" is used, each value is converted for the destination column it goes into, as for [IMPORT into an existing table](#import_existing_table_types): a text column gets the value as the source database returned it (an integer `1` is stored as `1`, a date as `2024-01-05`), an integer column gets whole numbers and stops the COPY with an error on a value with a fraction instead of rounding it, and a number column gets a column of 0s and 1s, or of true/false values, as 1 and 0.
|
|
893
|
+
|
|
892
894
|
If the "NEW" keyword is used, the destination table will be automatically created with column names and data types that are compatible with the first (source) table. The data types used for the columns in the newly created table will be determined by a scan of all of the data in the first table, but may not exactly match those in the first table. A text column stays text, whatever its values look like: `00123`, `1`/`0` and `2024-01-05` are copied as written, not as numbers, booleans or dates. The source is read once; while its rows are scanned they are held in a temporary file, so a large copy does not need memory for the whole table. If the destination table already exists when the "NEW" keyword is used, the copy stops before creating anything, with the error `Table <name> already exists`; a name without a schema is looked for only in the schema the table would be created in (on MySQL, the current database)
|
|
893
895
|
|
|
894
896
|
If the "REPLACEMENT" keyword is used, the destination table will also be created to be compatible with the source table, but any existing destination table of the same name will be dropped first. This replacement is not atomic: if the drop succeeds but a later create or load step fails, the old destination table has already been removed. If the drop fails, *execsql* stops the COPY command before creating or loading the replacement table. *execsql* uses a "drop table" statement to drop an existing destination table, and this statement may not succeed if there are dependencies on that table (see the discussion of [implicit drop table
|
|
@@ -1165,6 +1167,15 @@ JSON
|
|
|
1165
1167
|
: [Javascript Object Notation](http://www.json.org/). The data table is represented as an array of JSON objects, where each object represents a row of the table. Each row is represented as a set of key:value pairs, with column names used as the keys. No description text will be included in the output even if it is provided.
|
|
1166
1168
|
|
|
1167
1169
|
|
|
1170
|
+
JSONL
|
|
1171
|
+
|
|
1172
|
+
: [JSON Lines](https://jsonlines.org/): one JSON object per row, one row per line, with no surrounding array. Values are written as for JSON (dates and times as text, NULL as `null`); a line break inside a value is written as `\n`, so each row stays on one line. If the "APPEND" keyword is used, the rows are added to the end of the file, which stays valid JSON Lines (appending JSON instead leaves two arrays in one file). A query that returns no rows writes an empty file. The file can be read back with [IMPORT … FROM JSON](#import). No description text will be included in the output even if it is provided:
|
|
1173
|
+
|
|
1174
|
+
```
|
|
1175
|
+
-- !x! EXPORT QUERY <<select * from events where day = current_date;>> APPEND TO events.jsonl AS JSONL
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
|
|
1168
1179
|
JSON_TS or JSON_TABLESCHEMA
|
|
1169
1180
|
|
|
1170
1181
|
: [JSON Table Schema](https://frictionlessdata.io/specs/table-schema/). The data themselves are not exported. Ordinarily this form would be used in conjunction with an additional export of the data to a CSV file. The column type descriptions in the output are derived by scanning the entire table to evaluate the data type of each column. If a "NOTYPE" keyword is included before the "DESCRIPTION" keyword, this scanning is not done, and no type information is included in the output file.:
|
|
@@ -1945,7 +1956,7 @@ The syntax for importing data from a [JSON](https://www.json.org/) file is:
|
|
|
1945
1956
|
IMPORT TO [NEW|REPLACEMENT] <table_name> FROM JSON <file_name>
|
|
1946
1957
|
```
|
|
1947
1958
|
|
|
1948
|
-
The JSON file must contain either a JSON array of objects (`[{…}, …]`) or [JSON Lines](https://jsonlines.org/) (JSONL, one object per line). Nested objects are flattened with dot-separated column names (e.g., an object `{"address": {"city": "Portland"}}` produces column `address.city`). Nested arrays within objects are stored as JSON strings. Records with different keys produce a superset of columns — missing keys become NULL. The parser auto-detects which form is present by peeking at the first non-whitespace character (`[` → array, `{` → JSON Lines); the file extension is not consulted.
|
|
1959
|
+
The JSON file must contain either a JSON array of objects (`[{…}, …]`) or [JSON Lines](https://jsonlines.org/) (JSONL, one object per line). Nested objects are flattened with dot-separated column names (e.g., an object `{"address": {"city": "Portland"}}` produces column `address.city`). Nested arrays within objects are stored as JSON strings. Records with different keys produce a superset of columns — missing keys become NULL. The parser auto-detects which form is present by peeking at the first non-whitespace character (`[` → array, `{` → JSON Lines); the file extension is not consulted. A file written by [EXPORT … AS JSONL](#export) is read as JSON Lines.
|
|
1949
1960
|
|
|
1950
1961
|
Column names in the input must be valid for the DBMS in use.
|
|
1951
1962
|
|
|
@@ -1968,7 +1979,15 @@ A data line with fewer values than the header has NULL for the values left off t
|
|
|
1968
1979
|
|
|
1969
1980
|
The rows are loaded in one transaction: if the load fails part-way (a constraint violation, a value of the wrong type, a row with too many columns), none of the file's rows are kept, on every DBMS. The exception is an ODBC DSN whose driver can only autocommit, which keeps the rows loaded before the failure; *execsql* warns once on such a connection.
|
|
1970
1981
|
|
|
1971
|
-
|
|
1982
|
+
<a id="import_existing_table_types"></a>
|
|
1983
|
+
If neither the NEW or REPLACEMENT keywords are used, the table must exist, must have column names identical to those in the input data, and columns must have data types that are compatible with (though not necessarily identical to) those in the input data. Each value is converted for the column it goes into:
|
|
1984
|
+
|
|
1985
|
+
- A text column (`CHAR`, `VARCHAR`, `TEXT`, …) gets the value as written in the file: `yes`, `00701`, `01/05/2024` and `1e3` are stored as those strings. A missing value is NULL.
|
|
1986
|
+
- An integer column (`INTEGER`, `BIGINT`, `DECIMAL(10,0)`, `NUMERIC(10)`, Oracle `NUMBER(10)`, …) gets whole numbers: `7`, `+7`, `7.0` and `7e0` load as 7. A value with a fraction, such as `1.5`, stops the IMPORT with an error that names the value and the column, and nothing is loaded; it is not rounded.
|
|
1987
|
+
- A number column gets a column of true/false values (`yes`/`no`, `t`/`f`, `1`/`0`; see [BOOLEAN_INT](#boolean_int) and [BOOLEAN_WORDS](#boolean_words)) as 1 and 0.
|
|
1988
|
+
- Any other column (a date, a timestamp, a Boolean, a decimal with places for a fraction) gets the value converted to the data type found by scanning that column of the input, as with NEW, and the database converts it to the column's type.
|
|
1989
|
+
|
|
1990
|
+
On PostgreSQL and MySQL a delimited text file goes to the server's own loader (`COPY`, `LOAD DATA`, or on PostgreSQL an `INSERT` of each value as text), and the server converts each value as written; the rules above apply to the other file formats, and to delimited files on the other DBMSs. On MySQL a value that the server can't store as given (out of range, an invalid date, text longer than the column) stops the IMPORT with an error, and nothing is loaded; see [MySQL strict mode](../guides/sql_syntax.md#mysql_strict_mode).
|
|
1972
1991
|
|
|
1973
1992
|
If a table is scanned to determine data types, any column that is completely empty (all null) will be created with the text data type. This provides the greatest flexibility for subsequent addition of data to the table. However, if that column ought to have a different data type, and a WHERE clause is applied to that column assuming a different data type, the DBMS may report an error because of incomparable data types.
|
|
1974
1993
|
|
|
@@ -2032,7 +2051,7 @@ When MS-Excel saves an OpenDocument spreadsheet, it may create an additional emp
|
|
|
2032
2051
|
Because the data addition to the target table is always committed except when [AUTOCOMMIT](#autocommit) is off, the IMPORT metacommand should be used with care within transactions that are managed with explicit SQL statements or with [BATCHes](#batch).
|
|
2033
2052
|
|
|
2034
2053
|
|
|
2035
|
-
In general, if an error occurs while importing data, none of the new data should be in the target table (the operation is not committed).
|
|
2054
|
+
In general, if an error occurs while importing data, none of the new data should be in the target table (the operation is not committed). On MySQL/MariaDB a value that the server would change to store it is an error too, unless the script has turned off [strict mode](../guides/sql_syntax.md#mysql_strict_mode). If the NEW or REPLACEMENT keywords are used, depending on the DBMS and where the error occurred, the target table may be created even if the data are not loaded.
|
|
2036
2055
|
|
|
2037
2056
|
The name, size, and date of the IMPORTed file are written to the [execsql.log](../guides/logging.md#logging) file.
|
|
2038
2057
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "execsql-syntax",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.34.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "execsql-syntax",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.34.0",
|
|
10
10
|
"license": "GPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"vscode-languageclient": "10.1.2"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "execsql-syntax",
|
|
3
3
|
"displayName": "execsql",
|
|
4
4
|
"description": "Syntax highlighting, lint findings, completion, hover, navigation, quick fixes and formatting for execsql scripts.",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.34.0",
|
|
6
6
|
"publisher": "geocoug",
|
|
7
7
|
"license": "GPL-3.0-or-later",
|
|
8
8
|
"icon": "icon.png",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
},
|
|
119
119
|
"export-formats": {
|
|
120
120
|
"comment": "Export/import format names",
|
|
121
|
-
"match": "(?i)\\b(json_tableschema|cgi-html|markdown|feather|json_ts|txt-and|unitsep|duckdb|sqlite|values|latex|plain|hdf5|html|json|tabq|tsvq|xlsx|yaml|b64|csv|ods|raw|tab|tsv|txt|xml|md|us)\\b",
|
|
121
|
+
"match": "(?i)\\b(json_tableschema|cgi-html|markdown|feather|json_ts|txt-and|unitsep|duckdb|sqlite|values|jsonl|latex|plain|hdf5|html|json|tabq|tsvq|xlsx|yaml|b64|csv|ods|raw|tab|tsv|txt|xml|md|us)\\b",
|
|
122
122
|
"name": "support.constant.execsql"
|
|
123
123
|
},
|
|
124
124
|
"secondary-operators": {
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "execsql2"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.34.0"
|
|
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" }
|
|
@@ -188,7 +188,7 @@ line-ending = "auto"
|
|
|
188
188
|
exclude = ["*.md"]
|
|
189
189
|
|
|
190
190
|
[tool.bumpversion]
|
|
191
|
-
current_version = "2.
|
|
191
|
+
current_version = "2.34.0"
|
|
192
192
|
commit = true
|
|
193
193
|
tag = true
|
|
194
194
|
tag_name = "v{new_version}"
|
|
@@ -24,7 +24,9 @@ from collections.abc import Callable, Generator, Iterator
|
|
|
24
24
|
|
|
25
25
|
from execsql.exceptions import ErrInfo
|
|
26
26
|
from execsql.utils.errors import exception_desc
|
|
27
|
+
from execsql.db.target_types import ColumnKinds, existing_table_converter, kind_for_python_type
|
|
27
28
|
from execsql.db.tiers import SupportTier, announce_tier
|
|
29
|
+
from execsql.types import DataType
|
|
28
30
|
import execsql.state as _state
|
|
29
31
|
|
|
30
32
|
__all__ = ["Database", "DatabasePool"]
|
|
@@ -604,6 +606,42 @@ class Database(ABC):
|
|
|
604
606
|
rows = curs.fetchall()
|
|
605
607
|
return [row[0] for row in rows]
|
|
606
608
|
|
|
609
|
+
def column_kinds(self, sq_name: str) -> ColumnKinds:
|
|
610
|
+
"""The execsql data type each column of table *sq_name* converts values to (see ``db.target_types``).
|
|
611
|
+
|
|
612
|
+
*sq_name* is schema-qualified and quoted, as for an ``INSERT``, and
|
|
613
|
+
is looked up the way the ``INSERT`` finds it. Keys are lower-cased
|
|
614
|
+
column names; a column with no fitting execsql type maps to ``None``.
|
|
615
|
+
This default reads the driver's description of an empty query on the
|
|
616
|
+
table; adapters whose catalog names types more exactly override it.
|
|
617
|
+
"""
|
|
618
|
+
description, _rows = self._column_type_query(sq_name, f"select * from {sq_name} where 1=0;")
|
|
619
|
+
return {d[0].lower(): self._described_kind(d) for d in description}
|
|
620
|
+
|
|
621
|
+
def _described_kind(self, description: Any) -> type[DataType] | None:
|
|
622
|
+
"""The execsql type for one column of a cursor description (a DB-API 7-item sequence)."""
|
|
623
|
+
return kind_for_python_type(description[1], description[5])
|
|
624
|
+
|
|
625
|
+
def _column_type_query(self, sq_name: str, sql: str, params: list | None = None) -> tuple[Any, list]:
|
|
626
|
+
"""Run *sql* for :meth:`column_kinds` of *sq_name*; return the cursor description and the rows."""
|
|
627
|
+
with self._cursor() as curs:
|
|
628
|
+
try:
|
|
629
|
+
if params is None:
|
|
630
|
+
curs.execute(sql)
|
|
631
|
+
else:
|
|
632
|
+
curs.execute(sql, params)
|
|
633
|
+
except ErrInfo:
|
|
634
|
+
raise
|
|
635
|
+
except Exception as e:
|
|
636
|
+
self.rollback()
|
|
637
|
+
raise ErrInfo(
|
|
638
|
+
type="db",
|
|
639
|
+
command_text=sql,
|
|
640
|
+
exception_msg=exception_desc(),
|
|
641
|
+
other_msg=f"Failed to get the column types of table {sq_name} of {self.name()}",
|
|
642
|
+
) from e
|
|
643
|
+
return curs.description, curs.fetchall()
|
|
644
|
+
|
|
607
645
|
def view_exists(self, view_name: str, schema_name: str | None = None) -> bool:
|
|
608
646
|
"""Return ``True`` if *view_name* (optionally in *schema_name*) exists."""
|
|
609
647
|
with self._cursor() as curs:
|
|
@@ -689,11 +727,17 @@ class Database(ABC):
|
|
|
689
727
|
rowsource: Any,
|
|
690
728
|
column_list: list[str],
|
|
691
729
|
tablespec_src: Callable,
|
|
730
|
+
*,
|
|
731
|
+
existing_table: bool = False,
|
|
692
732
|
) -> None:
|
|
693
733
|
"""Bulk-insert rows from *rowsource* into *table_name* using the columns in *column_list*.
|
|
694
734
|
|
|
695
735
|
*rowsource* must be a generator yielding lists of values in column order.
|
|
696
736
|
*tablespec_src* is a zero-argument callable that returns the table's type specification.
|
|
737
|
+
Each value is converted to the type scanned for its column; with
|
|
738
|
+
*existing_table* (the table was there before the IMPORT or COPY), the
|
|
739
|
+
type of the column it goes into decides instead (see
|
|
740
|
+
``db.target_types.existing_table_converter``).
|
|
697
741
|
"""
|
|
698
742
|
# The rowsource argument must be a generator yielding a list of values for the columns of the table.
|
|
699
743
|
# The column_list argument must an iterable containing column names. This may be a subset of
|
|
@@ -713,8 +757,13 @@ class Database(ABC):
|
|
|
713
757
|
sel_cols = [col for col in ts_colnames if col in column_list]
|
|
714
758
|
incl_col = [col in column_list for col in ts_colnames]
|
|
715
759
|
# Type conversion functions for the rowsource.
|
|
716
|
-
|
|
717
|
-
|
|
760
|
+
converters = [self._scanned_type_converter(col.column_type()[1]) for col in tablespec.cols]
|
|
761
|
+
if existing_table:
|
|
762
|
+
kinds = self.column_kinds(sq_name)
|
|
763
|
+
converters = [
|
|
764
|
+
existing_table_converter(kinds.get(col.name.lower()), col.column_type()[1], convert, col.name, sq_name)
|
|
765
|
+
for col, convert in zip(tablespec.cols, converters)
|
|
766
|
+
]
|
|
718
767
|
# Construct INSERT statement.
|
|
719
768
|
columns = [self.type.quoted(col) for col in sel_cols]
|
|
720
769
|
colspec = ",".join(columns)
|
|
@@ -812,10 +861,7 @@ class Database(ABC):
|
|
|
812
861
|
line[i] = None
|
|
813
862
|
# Trailing values left off the line are NULL.
|
|
814
863
|
line = list(line) + [None] * (len(ts_colnames) - len(line))
|
|
815
|
-
lt = [
|
|
816
|
-
type_objs[i].from_data(val) if val is not None else None for i, val in enumerate(line)
|
|
817
|
-
]
|
|
818
|
-
lt = [type_mod_fn[i](v) if type_mod_fn[i] else v for i, v in enumerate(lt)]
|
|
864
|
+
lt = [converters[i](val) for i, val in enumerate(line)]
|
|
819
865
|
row = []
|
|
820
866
|
for i, v in enumerate(lt):
|
|
821
867
|
if incl_col[i]:
|
|
@@ -869,14 +915,31 @@ class Database(ABC):
|
|
|
869
915
|
f"IMPORT into {sq_name} complete: {total_rows} rows imported.",
|
|
870
916
|
)
|
|
871
917
|
|
|
918
|
+
def _scanned_type_converter(self, data_type: type[DataType]) -> Callable[[Any], Any]:
|
|
919
|
+
"""Convert a value to *data_type*, then apply this DBMS's adjustment for that type, if any.
|
|
920
|
+
|
|
921
|
+
NULL is not converted, but it is adjusted: MySQL's adjustment for
|
|
922
|
+
dates turns it into ``""``.
|
|
923
|
+
"""
|
|
924
|
+
from_data = data_type().from_data
|
|
925
|
+
adjust = self.type.dialect[data_type][3]
|
|
926
|
+
if adjust is None:
|
|
927
|
+
return lambda value: None if value is None else from_data(value)
|
|
928
|
+
return lambda value: adjust(None if value is None else from_data(value))
|
|
929
|
+
|
|
872
930
|
def import_tabular_file(
|
|
873
931
|
self,
|
|
874
932
|
schema_name: str | None,
|
|
875
933
|
table_name: str,
|
|
876
934
|
csv_file_obj: Any,
|
|
877
935
|
skipheader: bool,
|
|
936
|
+
*,
|
|
937
|
+
existing_table: bool = False,
|
|
878
938
|
) -> None:
|
|
879
|
-
"""Import a CSV/tabular file into *table_name*; column names must be compatible.
|
|
939
|
+
"""Import a CSV/tabular file into *table_name*; column names must be compatible.
|
|
940
|
+
|
|
941
|
+
*existing_table*: the table was there before the IMPORT (see :meth:`populate_table`).
|
|
942
|
+
"""
|
|
880
943
|
# Import a text (CSV) file containing tabular data to a table. Columns must be compatible.
|
|
881
944
|
if not self.table_exists(table_name, schema_name):
|
|
882
945
|
raise ErrInfo(
|
|
@@ -912,7 +975,7 @@ class Database(ABC):
|
|
|
912
975
|
|
|
913
976
|
f = csv_file_obj.reader()
|
|
914
977
|
next(f)
|
|
915
|
-
self.populate_table(schema_name, table_name, f, import_cols, get_ts)
|
|
978
|
+
self.populate_table(schema_name, table_name, f, import_cols, get_ts, existing_table=existing_table)
|
|
916
979
|
|
|
917
980
|
def import_entire_file(
|
|
918
981
|
self,
|
|
@@ -18,6 +18,8 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
from execsql.db.base import Database, statement_start
|
|
21
|
+
from execsql.db.target_types import kind_for_type_name
|
|
22
|
+
from execsql.types import DataType
|
|
21
23
|
from execsql.exceptions import ErrInfo
|
|
22
24
|
from execsql.utils.errors import exception_desc, fatal_error, install_hint
|
|
23
25
|
import execsql.state as _state
|
|
@@ -320,6 +322,10 @@ class DuckDBDatabase(Database):
|
|
|
320
322
|
|
|
321
323
|
return insert
|
|
322
324
|
|
|
325
|
+
def _described_kind(self, description: Any) -> type[DataType] | None:
|
|
326
|
+
"""DuckDB describes a column by its type's name (``DECIMAL(18,3)``, ``TIMESTAMP WITH TIME ZONE``)."""
|
|
327
|
+
return kind_for_type_name(str(description[1]))
|
|
328
|
+
|
|
323
329
|
def view_exists(self, view_name: str, schema_name: str | None = None) -> bool:
|
|
324
330
|
"""Return True if the named view exists in the DuckDB database."""
|
|
325
331
|
# DuckDB information_schema has no 'views' table; views are listed in 'tables'
|