vgi-python 0.29.4__tar.gz → 0.29.6__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.
- {vgi_python-0.29.4 → vgi_python-0.29.6}/PKG-INFO +1 -1
- {vgi_python-0.29.4 → vgi_python-0.29.6}/pyproject.toml +1 -1
- vgi_python-0.29.6/tests/table_in_out/test_blended_exchange.py +282 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/uv.lock +1 -1
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/client.py +130 -15
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/protocol.py +75 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.gitattributes +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.github/dependabot.yml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.github/workflows/release.yml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.gitignore +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.python-version +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/.vale.ini +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/CLAUDE.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/LICENSE +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/README.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/SECURITY.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/ci/README.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/ci/run-integration.sh +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/arguments.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/auth.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/catalogs.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/client.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/exceptions.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/filters.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/functions.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/http.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/index.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/metadata.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/observability.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/storage.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/transactor.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/api/worker.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/argument-serialization.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/logo.png +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/assets/social-card.png +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/authentication.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/catalog-interface.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/cli.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/column-statistics.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/concepts/index.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/contributing-docs.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/generator-api.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/global-functions.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/how-to/index.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/index.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/lifecycle.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/metadata.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/overrides/main.html +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/robots.txt +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/shared-storage.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/tutorial/index.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/tutorial/table.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/docs/vgi-logo.png +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/cache_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/calc_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/copy_format_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/filter_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/row_count_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/examples/sum_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/mkdocs.yml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/scripts/measure_startup.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/test-data/generate.sh +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_aggregate_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_batch_metadata_callback.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_cli.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_copy_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_scan_branches_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_split_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_stop_force.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_time_travel_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_splits.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/conftest.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/bad_version.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/generate.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/manifest.json +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/stale_anchor.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/truncated.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/valid_sealed.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table/test_wide_columns.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_auth.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_cache_control.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_call_state_attach.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_docstrings.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_exceptions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_fixture_catalog_parity.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_function_storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_java_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_rust_request_builders.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_rust_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_launch_transport.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_metadata.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_otel.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_serve.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_signing_key.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_split_token.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_union_argument.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_wire_record_schema_parity.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/test_worker_page.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_duckdb.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/splits.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/argument_spec.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/arguments.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/attach_header.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/auth.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/cache_control.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/catalog_interface.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/aggregate.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/client/errors.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/java_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/rust_request_builders.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/rust_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/exceptions.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/function_storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/http/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/http/landing.html +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/http/vgi-client.js +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/invocation.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/logging_config.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/meta_worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/metadata.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/otel.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/profiling.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/py.typed +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/scalar_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/schema_utils.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/secret_service.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/serve.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/split_token.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/table_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/transactor/client.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/transactor/server.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/vgi/worker.py +0 -0
- {vgi_python-0.29.4 → vgi_python-0.29.6}/wrangler.jsonc +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.29.
|
|
3
|
+
Version: 0.29.6
|
|
4
4
|
Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
|
|
5
5
|
Project-URL: Homepage, https://query.farm
|
|
6
6
|
Project-URL: Repository, https://github.com/Query-farm/vgi-python
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""RPC-level exchange coverage for blended (`RowTransformFunction`) table functions.
|
|
4
|
+
|
|
5
|
+
`test_blended_metadata.py` covers `resolve_metadata()` in isolation — no worker
|
|
6
|
+
subprocess. This file is the first exchange-level coverage for the blended
|
|
7
|
+
fixtures registered in `vgi/_test_fixtures/table_in_out.py`: it drives them
|
|
8
|
+
through `Client.table_in_out_function(...)`'s new `parent_row_callback`, which
|
|
9
|
+
decodes `vgi_rpc.parent_row#b64` provenance for a 1->N / 1->0 emit (see
|
|
10
|
+
`vgi.protocol._decode_parent_rows`).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
import pyarrow as pa
|
|
18
|
+
import pytest
|
|
19
|
+
|
|
20
|
+
from tests.conftest import make_schema
|
|
21
|
+
from vgi.arguments import Arguments
|
|
22
|
+
from vgi.client import Client
|
|
23
|
+
from vgi.client.client import ClientError
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TestBlendedExplodeProvenance:
|
|
27
|
+
"""`blended_explode`: 1->N fan-out with per-output-row parent_row provenance."""
|
|
28
|
+
|
|
29
|
+
def test_parent_row_pairing_is_correct(self, fixture_worker: str) -> None:
|
|
30
|
+
"""Each output row's parent index must point at the input row that produced it.
|
|
31
|
+
|
|
32
|
+
Asserting only the final row *set* would pass even for a naive
|
|
33
|
+
"assume identity" implementation whenever fan-out counts differ
|
|
34
|
+
across rows -- so this reconstructs the exact expected
|
|
35
|
+
(parent_row, emitted_value) pairs in emission order and compares.
|
|
36
|
+
"""
|
|
37
|
+
input_schema = make_schema([pa.field("n", pa.int64())])
|
|
38
|
+
counts = [0, 1, 3, 2]
|
|
39
|
+
batch = pa.RecordBatch.from_pydict({"n": counts}, schema=input_schema)
|
|
40
|
+
|
|
41
|
+
parent_rows_seen: list[int] = []
|
|
42
|
+
|
|
43
|
+
with Client(fixture_worker) as client:
|
|
44
|
+
results = list(
|
|
45
|
+
client.table_in_out_function(
|
|
46
|
+
function_name="blended_explode",
|
|
47
|
+
schema_name="main",
|
|
48
|
+
input=iter([batch]),
|
|
49
|
+
parent_row_callback=parent_rows_seen.extend,
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
assert len(results) == 1
|
|
54
|
+
out = results[0]
|
|
55
|
+
i_values = out.column("i").to_pylist()
|
|
56
|
+
|
|
57
|
+
assert len(i_values) == sum(counts)
|
|
58
|
+
assert len(parent_rows_seen) == len(i_values)
|
|
59
|
+
|
|
60
|
+
expected_pairs = [(row_idx, k) for row_idx, n in enumerate(counts) for k in range(n)]
|
|
61
|
+
actual_pairs = list(zip(parent_rows_seen, i_values, strict=True))
|
|
62
|
+
assert actual_pairs == expected_pairs
|
|
63
|
+
|
|
64
|
+
def test_all_rows_filtered_to_empty_result(self, fixture_worker: str) -> None:
|
|
65
|
+
"""n=0 for every input row -> a correctly-schemed, zero-row output."""
|
|
66
|
+
input_schema = make_schema([pa.field("n", pa.int64())])
|
|
67
|
+
batch = pa.RecordBatch.from_pydict({"n": [0, 0, 0]}, schema=input_schema)
|
|
68
|
+
|
|
69
|
+
parent_rows_seen: list[list[int]] = []
|
|
70
|
+
|
|
71
|
+
with Client(fixture_worker) as client:
|
|
72
|
+
results = list(
|
|
73
|
+
client.table_in_out_function(
|
|
74
|
+
function_name="blended_explode",
|
|
75
|
+
schema_name="main",
|
|
76
|
+
input=iter([batch]),
|
|
77
|
+
parent_row_callback=parent_rows_seen.append,
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
assert len(results) == 1
|
|
82
|
+
out = results[0]
|
|
83
|
+
assert out.num_rows == 0
|
|
84
|
+
assert out.schema.names == ["i"]
|
|
85
|
+
assert parent_rows_seen == [[]]
|
|
86
|
+
|
|
87
|
+
def test_identity_map_when_row_counts_match(self, fixture_worker: str) -> None:
|
|
88
|
+
"""n=1 for every row -> a 1:1 emit; the identity map is a valid decode.
|
|
89
|
+
|
|
90
|
+
`blended_explode` never encodes `vgi_rpc.parent_row` for a 1:1 emit
|
|
91
|
+
(the C++ reference assumes identity for that case too), so this
|
|
92
|
+
exercises `_decode_parent_rows`'s absent-metadata branch, not just
|
|
93
|
+
the explicit-array branch the other tests exercise.
|
|
94
|
+
"""
|
|
95
|
+
input_schema = make_schema([pa.field("n", pa.int64())])
|
|
96
|
+
batch = pa.RecordBatch.from_pydict({"n": [1, 1, 1]}, schema=input_schema)
|
|
97
|
+
|
|
98
|
+
parent_rows_seen: list[int] = []
|
|
99
|
+
|
|
100
|
+
with Client(fixture_worker) as client:
|
|
101
|
+
results = list(
|
|
102
|
+
client.table_in_out_function(
|
|
103
|
+
function_name="blended_explode",
|
|
104
|
+
schema_name="main",
|
|
105
|
+
input=iter([batch]),
|
|
106
|
+
parent_row_callback=parent_rows_seen.extend,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
assert len(results) == 1
|
|
111
|
+
assert results[0].column("i").to_pylist() == [0, 0, 0]
|
|
112
|
+
assert parent_rows_seen == [0, 1, 2]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TestHostileProvenance:
|
|
116
|
+
"""`hostile_provenance`: adversarial worker payloads must raise, never corrupt."""
|
|
117
|
+
|
|
118
|
+
@pytest.mark.parametrize("mode", ["range", "length", "base64"])
|
|
119
|
+
def test_malformed_provenance_raises_client_error(
|
|
120
|
+
self,
|
|
121
|
+
client_transport: Any,
|
|
122
|
+
mode: str,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Each hostile mode must raise ClientError on both subprocess and HTTP.
|
|
125
|
+
|
|
126
|
+
Symmetry matters here: an asymmetry between transports would mean one
|
|
127
|
+
parse path validates and the other doesn't -- a security bug, not
|
|
128
|
+
just an inconsistency.
|
|
129
|
+
"""
|
|
130
|
+
input_schema = make_schema([pa.field("x", pa.int64())])
|
|
131
|
+
batch = pa.RecordBatch.from_pydict({"x": [1, 2, 3]}, schema=input_schema)
|
|
132
|
+
|
|
133
|
+
with client_transport() as client, pytest.raises(ClientError):
|
|
134
|
+
list(
|
|
135
|
+
client.table_in_out_function(
|
|
136
|
+
function_name="hostile_provenance",
|
|
137
|
+
schema_name="main",
|
|
138
|
+
input=iter([batch]),
|
|
139
|
+
arguments=Arguments(named={"mode": pa.scalar(mode)}),
|
|
140
|
+
parent_row_callback=lambda _rows: None,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def test_no_callback_no_decode_no_raise(self, fixture_worker: str) -> None:
|
|
145
|
+
"""Without `parent_row_callback`, malformed provenance is never even decoded.
|
|
146
|
+
|
|
147
|
+
`decode_parent_rows` is derived from `parent_row_callback is not None`
|
|
148
|
+
-- this is the backward-compat guarantee that an ordinary (non-blended)
|
|
149
|
+
table-in-out caller, which never passes the callback, is completely
|
|
150
|
+
unaffected by this feature existing.
|
|
151
|
+
"""
|
|
152
|
+
input_schema = make_schema([pa.field("x", pa.int64())])
|
|
153
|
+
batch = pa.RecordBatch.from_pydict({"x": [1, 2, 3]}, schema=input_schema)
|
|
154
|
+
|
|
155
|
+
with Client(fixture_worker) as client:
|
|
156
|
+
results = list(
|
|
157
|
+
client.table_in_out_function(
|
|
158
|
+
function_name="hostile_provenance",
|
|
159
|
+
schema_name="main",
|
|
160
|
+
input=iter([batch]),
|
|
161
|
+
arguments=Arguments(named={"mode": pa.scalar("range")}),
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
assert len(results) == 1
|
|
166
|
+
assert results[0].column("hv").to_pylist() == [1, 2, 3]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class TestBlendedArityAndVarargs:
|
|
170
|
+
"""Broader coverage once the core provenance mechanism is proven."""
|
|
171
|
+
|
|
172
|
+
def test_geo_encode_two_arg_overload(self, fixture_worker: str) -> None:
|
|
173
|
+
"""`geo_encode(lat, lon)` -- the 2-arg overload, identity 1:1 map."""
|
|
174
|
+
input_schema = make_schema([pa.field("latitude", pa.float64()), pa.field("longitude", pa.float64())])
|
|
175
|
+
batch = pa.RecordBatch.from_pydict(
|
|
176
|
+
{"latitude": [52.0, 48.5], "longitude": [13.0, 2.3]},
|
|
177
|
+
schema=input_schema,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
parent_rows_seen: list[int] = []
|
|
181
|
+
|
|
182
|
+
with Client(fixture_worker) as client:
|
|
183
|
+
results = list(
|
|
184
|
+
client.table_in_out_function(
|
|
185
|
+
function_name="geo_encode",
|
|
186
|
+
schema_name="main",
|
|
187
|
+
input=iter([batch]),
|
|
188
|
+
arguments=Arguments(named={"precision": pa.scalar(1)}),
|
|
189
|
+
parent_row_callback=parent_rows_seen.extend,
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
assert len(results) == 1
|
|
194
|
+
assert results[0].column("geohash").to_pylist() == ["52.0:13.0", "48.5:2.3"]
|
|
195
|
+
assert parent_rows_seen == [0, 1]
|
|
196
|
+
|
|
197
|
+
def test_geo_encode_three_arg_overload(self, fixture_worker: str) -> None:
|
|
198
|
+
"""`geo_encode(lat, lon, alt)` -- same Meta.name, resolved by input arity."""
|
|
199
|
+
input_schema = make_schema(
|
|
200
|
+
[
|
|
201
|
+
pa.field("latitude", pa.float64()),
|
|
202
|
+
pa.field("longitude", pa.float64()),
|
|
203
|
+
pa.field("altitude", pa.float64()),
|
|
204
|
+
]
|
|
205
|
+
)
|
|
206
|
+
batch = pa.RecordBatch.from_pydict(
|
|
207
|
+
{"latitude": [52.0], "longitude": [13.0], "altitude": [100.0]},
|
|
208
|
+
schema=input_schema,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
with Client(fixture_worker) as client:
|
|
212
|
+
results = list(
|
|
213
|
+
client.table_in_out_function(
|
|
214
|
+
function_name="geo_encode",
|
|
215
|
+
schema_name="main",
|
|
216
|
+
input=iter([batch]),
|
|
217
|
+
arguments=Arguments(named={"precision": pa.scalar(0)}),
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
assert len(results) == 1
|
|
222
|
+
assert results[0].column("geohash").to_pylist() == ["52.0:13.0:100.0"]
|
|
223
|
+
|
|
224
|
+
def test_row_sum_varargs(self, fixture_worker: str) -> None:
|
|
225
|
+
"""`row_sum(a, b, c)` -- varargs positional input, positionally-named columns."""
|
|
226
|
+
input_schema = make_schema([pa.field("col0", pa.float64()), pa.field("col1", pa.float64())])
|
|
227
|
+
batch = pa.RecordBatch.from_pydict({"col0": [1.0, -2.0], "col1": [2.0, -3.0]}, schema=input_schema)
|
|
228
|
+
|
|
229
|
+
with Client(fixture_worker) as client:
|
|
230
|
+
results = list(
|
|
231
|
+
client.table_in_out_function(
|
|
232
|
+
function_name="row_sum",
|
|
233
|
+
schema_name="main",
|
|
234
|
+
input=iter([batch]),
|
|
235
|
+
arguments=Arguments(named={"absolute": pa.scalar(True)}),
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
assert len(results) == 1
|
|
240
|
+
assert results[0].column("row_sum").to_pylist() == [3.0, 5.0]
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class TestHasFinalizeSkip:
|
|
244
|
+
"""`has_finalize=False` skips the FINALIZE `init()` entirely for a no-finalize function.
|
|
245
|
+
|
|
246
|
+
Real interop bug found live against a non-Python worker SDK: the client
|
|
247
|
+
unconditionally sent a FINALIZE-phase init() for every table-in-out call,
|
|
248
|
+
which the Python fixture worker happens to no-op gracefully but a
|
|
249
|
+
TypeScript-SDK worker rejects outright ("a blended row-transform function
|
|
250
|
+
has no FINALIZE phase"). `has_finalize=False` avoids sending that init()
|
|
251
|
+
at all rather than depending on every SDK tolerating an unexpected one.
|
|
252
|
+
Output must be identical either way for a function that has no finalize
|
|
253
|
+
stage (there is nothing to skip yielding) -- this is the equivalence this
|
|
254
|
+
suite can prove locally; the cross-SDK rejection itself is TypeScript-only.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
def test_output_identical_with_and_without_has_finalize(self, client_transport: Any) -> None:
|
|
258
|
+
"""Same output whether the FINALIZE init() is sent or skipped, both transports."""
|
|
259
|
+
input_schema = make_schema([pa.field("n", pa.int64())])
|
|
260
|
+
batch = pa.RecordBatch.from_pydict({"n": [0, 1, 3, 2]}, schema=input_schema)
|
|
261
|
+
|
|
262
|
+
with client_transport() as client:
|
|
263
|
+
with_finalize = list(
|
|
264
|
+
client.table_in_out_function(
|
|
265
|
+
function_name="blended_explode",
|
|
266
|
+
schema_name="main",
|
|
267
|
+
input=iter([batch]),
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
with client_transport() as client:
|
|
271
|
+
without_finalize = list(
|
|
272
|
+
client.table_in_out_function(
|
|
273
|
+
function_name="blended_explode",
|
|
274
|
+
schema_name="main",
|
|
275
|
+
input=iter([batch]),
|
|
276
|
+
has_finalize=False,
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
assert len(with_finalize) == 1
|
|
281
|
+
assert len(without_finalize) == 1
|
|
282
|
+
assert with_finalize[0].column("i").to_pylist() == without_finalize[0].column("i").to_pylist()
|
|
@@ -118,6 +118,7 @@ from vgi.protocol import (
|
|
|
118
118
|
TableBufferingProcessRequest,
|
|
119
119
|
TableFunctionPlanRequest,
|
|
120
120
|
VgiProtocol,
|
|
121
|
+
_decode_parent_rows,
|
|
121
122
|
)
|
|
122
123
|
from vgi.table_function import TableInOutFunctionInitPhase
|
|
123
124
|
|
|
@@ -296,6 +297,33 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
296
297
|
return batches[0]
|
|
297
298
|
return combined[0]
|
|
298
299
|
|
|
300
|
+
@staticmethod
|
|
301
|
+
def _combine_parent_rows(parent_rows_batches: list[list[int]]) -> list[int]:
|
|
302
|
+
"""Concatenate per-output-batch parent-row lists in emission order.
|
|
303
|
+
|
|
304
|
+
Mirrors `_combine_batches` for blended row-transform provenance: when
|
|
305
|
+
a table-in-out call produces multiple output batches for one input
|
|
306
|
+
batch (a worker replying `HAVE_MORE_OUTPUT` more than once), each
|
|
307
|
+
sub-batch's decoded parent-row list already indexes into that same
|
|
308
|
+
shipped input batch (see `_decode_parent_rows`'s contract), so
|
|
309
|
+
concatenating the lists in the order the sub-batches were emitted is
|
|
310
|
+
sound — no index renumbering is needed, because `_combine_batches`
|
|
311
|
+
also concatenates the sub-batches themselves in that same order.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
parent_rows_batches: One decoded parent-row list per output
|
|
315
|
+
sub-batch, in emission order.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
The concatenated parent-row list, aligned row-for-row with what
|
|
319
|
+
`_combine_batches` produces from the corresponding output batches.
|
|
320
|
+
|
|
321
|
+
"""
|
|
322
|
+
combined: list[int] = []
|
|
323
|
+
for batch_rows in parent_rows_batches:
|
|
324
|
+
combined.extend(batch_rows)
|
|
325
|
+
return combined
|
|
326
|
+
|
|
299
327
|
@staticmethod
|
|
300
328
|
def _on_worker_log(msg: Message) -> None:
|
|
301
329
|
"""Forward log messages from vgi_rpc to the worker logger."""
|
|
@@ -1618,7 +1646,9 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1618
1646
|
worker: WorkerConnection,
|
|
1619
1647
|
input_batch: pa.RecordBatch,
|
|
1620
1648
|
batch_index: int,
|
|
1621
|
-
|
|
1649
|
+
*,
|
|
1650
|
+
decode_parent_rows: bool = False,
|
|
1651
|
+
) -> tuple[list[pa.RecordBatch], list[list[int]] | None]:
|
|
1622
1652
|
"""Send a batch to a worker and collect all output batches.
|
|
1623
1653
|
|
|
1624
1654
|
Sends the input batch via stream.exchange(), then checks the vgi.status
|
|
@@ -1629,19 +1659,32 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1629
1659
|
worker: The worker connection to use. Must have stream initialized.
|
|
1630
1660
|
input_batch: The input `RecordBatch` to send to the worker.
|
|
1631
1661
|
batch_index: Index of this batch in the input sequence (for logging).
|
|
1662
|
+
decode_parent_rows: When True, additionally decode each output
|
|
1663
|
+
batch's `vgi_rpc.parent_row#b64` metadata (see
|
|
1664
|
+
`vgi.protocol._decode_parent_rows`) — used by blended
|
|
1665
|
+
row-transform table functions, where a worker's output row
|
|
1666
|
+
count need not match its input row count. False for every
|
|
1667
|
+
other table-in-out / scalar caller, which has no provenance
|
|
1668
|
+
concept and must not have the row-count-mismatch check inside
|
|
1669
|
+
`_decode_parent_rows` start firing for it.
|
|
1632
1670
|
|
|
1633
1671
|
Returns:
|
|
1634
|
-
|
|
1672
|
+
A tuple of (output batches, per-batch decoded parent-row lists).
|
|
1673
|
+
The second element is `None` when `decode_parent_rows` is False —
|
|
1674
|
+
keeps the common path from building a list it never uses.
|
|
1635
1675
|
|
|
1636
1676
|
Raises:
|
|
1637
|
-
[`ClientError`][]: If worker.stream is None,
|
|
1638
|
-
an unexpected status,
|
|
1677
|
+
[`ClientError`][]: If worker.stream is None, if the worker returns
|
|
1678
|
+
an unexpected status, if the RPC call fails, or (when
|
|
1679
|
+
`decode_parent_rows` is True) if `vgi_rpc.parent_row#b64` is
|
|
1680
|
+
malformed or absent-with-mismatched-row-counts.
|
|
1639
1681
|
|
|
1640
1682
|
"""
|
|
1641
1683
|
if worker.stream is None:
|
|
1642
1684
|
raise ClientError(f"Worker {worker.worker_index} stream not initialized")
|
|
1643
1685
|
|
|
1644
1686
|
output_batches: list[pa.RecordBatch] = []
|
|
1687
|
+
parent_rows_batches: list[list[int]] | None = [] if decode_parent_rows else None
|
|
1645
1688
|
|
|
1646
1689
|
while True:
|
|
1647
1690
|
_logger.debug(
|
|
@@ -1665,6 +1708,18 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1665
1708
|
|
|
1666
1709
|
output_batches.append(output.batch)
|
|
1667
1710
|
|
|
1711
|
+
if parent_rows_batches is not None:
|
|
1712
|
+
try:
|
|
1713
|
+
parent_rows_batches.append(
|
|
1714
|
+
_decode_parent_rows(
|
|
1715
|
+
output.custom_metadata,
|
|
1716
|
+
output_rows=output.batch.num_rows,
|
|
1717
|
+
input_rows=input_batch.num_rows,
|
|
1718
|
+
)
|
|
1719
|
+
)
|
|
1720
|
+
except RuntimeError as e:
|
|
1721
|
+
raise ClientError(str(e)) from e
|
|
1722
|
+
|
|
1668
1723
|
# Check vgi.status for table-in-out status
|
|
1669
1724
|
status = None
|
|
1670
1725
|
if output.custom_metadata:
|
|
@@ -1678,22 +1733,25 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1678
1733
|
else:
|
|
1679
1734
|
raise ClientError(f"Unexpected status from worker {worker.worker_index}: {status!r}")
|
|
1680
1735
|
|
|
1681
|
-
return output_batches
|
|
1736
|
+
return output_batches, parent_rows_batches
|
|
1682
1737
|
|
|
1683
1738
|
def _worker_thread_loop(
|
|
1684
1739
|
self,
|
|
1685
1740
|
worker: WorkerConnection,
|
|
1686
1741
|
input_queue: Queue[tuple[int, pa.RecordBatch] | None],
|
|
1687
|
-
output_queue: Queue[tuple[int, list[pa.RecordBatch]] | BaseException],
|
|
1742
|
+
output_queue: Queue[tuple[int, list[pa.RecordBatch], list[list[int]] | None] | BaseException],
|
|
1743
|
+
*,
|
|
1744
|
+
decode_parent_rows: bool = False,
|
|
1688
1745
|
) -> None:
|
|
1689
1746
|
"""Thread function that processes batches for a single worker.
|
|
1690
1747
|
|
|
1691
1748
|
Runs in a dedicated thread, pulling (batch_index, batch) tuples from
|
|
1692
1749
|
the input queue, processing them via _process_batch_on_worker, and
|
|
1693
|
-
pushing (batch_index, output_batches) tuples to
|
|
1750
|
+
pushing (batch_index, output_batches, parent_rows_batches) tuples to
|
|
1751
|
+
the output queue.
|
|
1694
1752
|
|
|
1695
1753
|
When None is received from input_queue, signals thread completion by
|
|
1696
|
-
pushing (-1, []) to output_queue and exits.
|
|
1754
|
+
pushing (-1, [], None) to output_queue and exits.
|
|
1697
1755
|
|
|
1698
1756
|
If an exception occurs during processing, it is caught, logged, and
|
|
1699
1757
|
pushed to output_queue as the exception object itself.
|
|
@@ -1703,6 +1761,8 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1703
1761
|
input_queue: Thread-safe queue providing (batch_index, `RecordBatch`)
|
|
1704
1762
|
tuples for processing. A None value signals end of input.
|
|
1705
1763
|
output_queue: Thread-safe queue for results.
|
|
1764
|
+
decode_parent_rows: Forwarded to `_process_batch_on_worker` — see
|
|
1765
|
+
its docstring.
|
|
1706
1766
|
|
|
1707
1767
|
"""
|
|
1708
1768
|
try:
|
|
@@ -1710,12 +1770,14 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1710
1770
|
item = input_queue.get()
|
|
1711
1771
|
if item is None:
|
|
1712
1772
|
# End of input - signal thread completion
|
|
1713
|
-
output_queue.put((-1, []))
|
|
1773
|
+
output_queue.put((-1, [], None))
|
|
1714
1774
|
break
|
|
1715
1775
|
|
|
1716
1776
|
batch_index, input_batch = item
|
|
1717
|
-
outputs = self._process_batch_on_worker(
|
|
1718
|
-
|
|
1777
|
+
outputs, parent_rows_batches = self._process_batch_on_worker(
|
|
1778
|
+
worker, input_batch, batch_index, decode_parent_rows=decode_parent_rows
|
|
1779
|
+
)
|
|
1780
|
+
output_queue.put((batch_index, outputs, parent_rows_batches))
|
|
1719
1781
|
except Exception as e:
|
|
1720
1782
|
_logger.exception("worker_thread_error worker_index=%s", worker.worker_index)
|
|
1721
1783
|
output_queue.put(e)
|
|
@@ -1726,6 +1788,8 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1726
1788
|
all_workers: list[WorkerConnection],
|
|
1727
1789
|
first_batch: pa.RecordBatch,
|
|
1728
1790
|
remaining_input: Iterator[pa.RecordBatch],
|
|
1791
|
+
decode_parent_rows: bool = False,
|
|
1792
|
+
parent_row_callback: Callable[[list[int]], None] | None = None,
|
|
1729
1793
|
) -> Generator[pa.RecordBatch]:
|
|
1730
1794
|
"""Distribute input batches round-robin across workers and collect output.
|
|
1731
1795
|
|
|
@@ -1738,6 +1802,15 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1738
1802
|
first_batch: The first input batch, already consumed from the
|
|
1739
1803
|
iterator by the calling method.
|
|
1740
1804
|
remaining_input: Iterator for remaining input batches.
|
|
1805
|
+
decode_parent_rows: See `_process_batch_on_worker`. Forwarded to
|
|
1806
|
+
every worker thread; False (the default) preserves today's
|
|
1807
|
+
behavior exactly for every existing caller.
|
|
1808
|
+
parent_row_callback: Optional callback invoked once per yielded
|
|
1809
|
+
output batch, immediately before the yield, with that batch's
|
|
1810
|
+
combined parent-row list (see `_combine_parent_rows`) — the
|
|
1811
|
+
same "callback then yield" contract `table_function`'s
|
|
1812
|
+
`batch_metadata_callback` uses. Only meaningful when
|
|
1813
|
+
`decode_parent_rows` is True; ignored otherwise.
|
|
1741
1814
|
|
|
1742
1815
|
Yields:
|
|
1743
1816
|
Output `RecordBatch`es from processing. When multiple batches are
|
|
@@ -1754,7 +1827,7 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1754
1827
|
|
|
1755
1828
|
# Create queues for each worker
|
|
1756
1829
|
input_queues: list[Queue[tuple[int, pa.RecordBatch] | None]] = [Queue() for _ in range(num_workers)]
|
|
1757
|
-
output_queue: Queue[tuple[int, list[pa.RecordBatch]] | BaseException] = Queue()
|
|
1830
|
+
output_queue: Queue[tuple[int, list[pa.RecordBatch], list[list[int]] | None] | BaseException] = Queue()
|
|
1758
1831
|
|
|
1759
1832
|
# Start worker threads
|
|
1760
1833
|
threads: list[threading.Thread] = []
|
|
@@ -1762,6 +1835,7 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1762
1835
|
thread = threading.Thread(
|
|
1763
1836
|
target=self._worker_thread_loop,
|
|
1764
1837
|
args=(worker, input_queues[i], output_queue),
|
|
1838
|
+
kwargs={"decode_parent_rows": decode_parent_rows},
|
|
1765
1839
|
daemon=True,
|
|
1766
1840
|
)
|
|
1767
1841
|
thread.start()
|
|
@@ -1804,12 +1878,14 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1804
1878
|
raise ClientError.from_rpc_error(result) from result
|
|
1805
1879
|
raise ClientError(f"Worker thread failed: {result}") from result
|
|
1806
1880
|
|
|
1807
|
-
batch_idx, output_batches = result
|
|
1881
|
+
batch_idx, output_batches, parent_rows_batches = result
|
|
1808
1882
|
outputs_received += 1
|
|
1809
1883
|
|
|
1810
1884
|
# Combine output batches if needed
|
|
1811
1885
|
combined = self._combine_batches(output_batches)
|
|
1812
1886
|
if combined is not None:
|
|
1887
|
+
if decode_parent_rows and parent_row_callback is not None and parent_rows_batches is not None:
|
|
1888
|
+
parent_row_callback(self._combine_parent_rows(parent_rows_batches))
|
|
1813
1889
|
yield combined
|
|
1814
1890
|
|
|
1815
1891
|
_logger.debug(
|
|
@@ -1838,6 +1914,8 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1838
1914
|
pushdown_filters: bytes | None = None,
|
|
1839
1915
|
settings: dict[str, Any] | None = None,
|
|
1840
1916
|
transaction_opaque_data: bytes | None = None,
|
|
1917
|
+
parent_row_callback: Callable[[list[int]], None] | None = None,
|
|
1918
|
+
has_finalize: bool = True,
|
|
1841
1919
|
) -> Generator[pa.RecordBatch]:
|
|
1842
1920
|
"""Invoke a table-in-out function on the worker and stream results.
|
|
1843
1921
|
|
|
@@ -1865,6 +1943,34 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1865
1943
|
settings: Optional dictionary of settings/pragmas to
|
|
1866
1944
|
pass to the function.
|
|
1867
1945
|
transaction_opaque_data: Optional unique identifier for the DuckDB transaction.
|
|
1946
|
+
parent_row_callback: Optional callback invoked once per yielded
|
|
1947
|
+
output batch (before finalize), immediately before the yield,
|
|
1948
|
+
with that batch's decoded `vgi_rpc.parent_row` provenance —
|
|
1949
|
+
`parent_rows[i]` is the 0-based index into the input batch
|
|
1950
|
+
that produced output row `i`. Passing this switches on
|
|
1951
|
+
provenance decoding: a batch with no `vgi_rpc.parent_row`
|
|
1952
|
+
metadata is only accepted when its row count matches the
|
|
1953
|
+
input batch's (raising `ClientError` otherwise), since a
|
|
1954
|
+
worker changing row count without provenance is a worker bug
|
|
1955
|
+
for a function that opted into this contract. Intended for
|
|
1956
|
+
blended row-transform functions (`RowTransformFunction`,
|
|
1957
|
+
`FunctionInfo.input_from_args`); leave unset for ordinary
|
|
1958
|
+
table-in-out functions, which have no provenance concept and
|
|
1959
|
+
may legitimately change row count.
|
|
1960
|
+
has_finalize: Whether this function declares a FINALIZE stage
|
|
1961
|
+
(`FunctionInfo.has_finalize`). Defaults to `True` — every
|
|
1962
|
+
caller before this parameter existed got a FINALIZE-phase
|
|
1963
|
+
`init()` unconditionally, so this preserves that exactly.
|
|
1964
|
+
Pass `False` for a function known to have no finalize (every
|
|
1965
|
+
blended `RowTransformFunction` — `has_finalize` is always
|
|
1966
|
+
false for those, enforced at `resolve_metadata()`) to skip
|
|
1967
|
+
the FINALIZE `init()` entirely, not just send one expecting
|
|
1968
|
+
an empty reply. Confirmed load-bearing, not cosmetic: some
|
|
1969
|
+
worker SDKs (e.g. the TypeScript one) actively reject an
|
|
1970
|
+
unexpected FINALIZE `init()` for a function that never
|
|
1971
|
+
advertised `has_finalize`, rather than silently no-op'ing
|
|
1972
|
+
it — the C++ DuckDB extension avoids this the same way, by
|
|
1973
|
+
conditionally registering `in_out_function_final` at all.
|
|
1868
1974
|
|
|
1869
1975
|
Yields:
|
|
1870
1976
|
Output `RecordBatch`es from the function. In single-worker mode, output
|
|
@@ -1915,6 +2021,8 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1915
2021
|
all_workers=all_workers,
|
|
1916
2022
|
first_batch=first_batch,
|
|
1917
2023
|
remaining_input=input,
|
|
2024
|
+
decode_parent_rows=parent_row_callback is not None,
|
|
2025
|
+
parent_row_callback=parent_row_callback,
|
|
1918
2026
|
)
|
|
1919
2027
|
|
|
1920
2028
|
# Close all input streams
|
|
@@ -1926,7 +2034,14 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
1926
2034
|
# Close secondary workers
|
|
1927
2035
|
self._close_secondary_workers()
|
|
1928
2036
|
|
|
1929
|
-
# Finalize on primary worker
|
|
2037
|
+
# Finalize on primary worker — skipped entirely (not just
|
|
2038
|
+
# sent expecting an empty reply) when the function has no
|
|
2039
|
+
# finalize stage. See has_finalize's docstring: some worker
|
|
2040
|
+
# SDKs reject an unadvertised FINALIZE init() outright.
|
|
2041
|
+
if not has_finalize:
|
|
2042
|
+
_logger.debug("skipping_finalize_no_finalize_stage")
|
|
2043
|
+
return
|
|
2044
|
+
|
|
1930
2045
|
_logger.debug("finalizing_primary_worker")
|
|
1931
2046
|
yield from self._finalize_primary_worker(
|
|
1932
2047
|
bind_request,
|
|
@@ -2299,7 +2414,7 @@ class Client(CatalogClientMixin, AggregateClientMixin):
|
|
|
2299
2414
|
cursor=cursor,
|
|
2300
2415
|
)
|
|
2301
2416
|
try:
|
|
2302
|
-
response: PlanResponse = self._primary.proxy.table_function_plan(request=plan_request)
|
|
2417
|
+
response: PlanResponse = self._primary.proxy.table_function_plan(request=plan_request)
|
|
2303
2418
|
except RpcError as e:
|
|
2304
2419
|
raise ClientError.from_rpc_error(e) from e
|
|
2305
2420
|
|