vgi-python 0.9.0__tar.gz → 0.10.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.
- {vgi_python-0.9.0 → vgi_python-0.10.0}/PKG-INFO +1 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/packages/vgi-fixtures/pyproject.toml +1 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/pyproject.toml +1 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_declarative.py +39 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_serialization.py +2 -1
- vgi_python-0.10.0/tests/conformance/test_const_param_enforcement.py +69 -0
- vgi_python-0.10.0/tests/conformance/test_container.py +17 -0
- vgi_python-0.10.0/tests/conformance/test_copy_from.py +16 -0
- vgi_python-0.10.0/tests/conformance/test_copy_to.py +21 -0
- vgi_python-0.10.0/tests/conformance/test_github.py +15 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_secret.py +3 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_aggregate_function.py +51 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_argument_spec.py +101 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/__init__.py +2 -0
- vgi_python-0.10.0/vgi/_test_fixtures/aggregate/secret_typed.py +115 -0
- vgi_python-0.10.0/vgi/_test_fixtures/companion.py +169 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/formatting.py +2 -2
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table_in_out.py +46 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/worker.py +48 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/argument_spec.py +146 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/arguments.py +71 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/__init__.py +2 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/catalog_interface.py +96 -2
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/descriptors.py +15 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/_common.py +2 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/scalar_function.py +33 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/worker.py +16 -1
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.gitattributes +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.gitignore +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.python-version +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/.vale.ini +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/CLAUDE.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/LICENSE +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/README.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/SECURITY.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/ci/README.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/auth.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/client.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/filters.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/functions.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/http.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/index.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/observability.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/storage.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/api/worker.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/authentication.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/cli.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/generator-api.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/index.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/metadata.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/robots.txt +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/mkdocs.yml +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/test-data/generate.sh +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_required_field_filter_paths.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/conftest.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_auth.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_otel.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_serve.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/uv.lock +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/filters.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/auth.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/client/client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/invocation.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/meta_worker.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/metadata.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/otel.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/protocol.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/py.typed +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/serve.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/table_filter_pushdown.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/table_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.9.0 → vgi_python-0.10.0}/wrangler.jsonc +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
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
|
|
@@ -38,6 +38,7 @@ dependencies = ["vgi-python[test-fixtures-writable]", "vgi-python[duckdb]"]
|
|
|
38
38
|
|
|
39
39
|
[project.scripts]
|
|
40
40
|
vgi-fixture-worker = "vgi._test_fixtures.worker:main"
|
|
41
|
+
vgi-fixture-companion-worker = "vgi._test_fixtures.companion:main"
|
|
41
42
|
vgi-fixture-http = "vgi._test_fixtures.http_server:main"
|
|
42
43
|
vgi-fixture-versioned-worker = "vgi._test_fixtures.versioned:main"
|
|
43
44
|
vgi-fixture-versioned-tables-worker = "vgi._test_fixtures.versioned_tables:main"
|
|
@@ -41,8 +41,10 @@ from vgi.catalog import (
|
|
|
41
41
|
View,
|
|
42
42
|
ViewInfo,
|
|
43
43
|
)
|
|
44
|
+
from vgi.invocation import BindResponse
|
|
44
45
|
from vgi.schema_utils import schema
|
|
45
46
|
from vgi.table_function import (
|
|
47
|
+
BindParams,
|
|
46
48
|
ProcessParams,
|
|
47
49
|
TableFunctionGenerator,
|
|
48
50
|
bind_fixed_schema,
|
|
@@ -81,6 +83,31 @@ class UsersFunction(TableFunctionGenerator[EmptyArgs]):
|
|
|
81
83
|
out.finish()
|
|
82
84
|
|
|
83
85
|
|
|
86
|
+
@init_single_worker
|
|
87
|
+
class SecretLookupFunction(TableFunctionGenerator[EmptyArgs]):
|
|
88
|
+
"""Backing function whose ``on_bind`` performs a two-phase secret lookup.
|
|
89
|
+
|
|
90
|
+
This exercises the bug where ``Table(function=...)`` schema derivation took
|
|
91
|
+
the empty secret-scope-request schema instead of retrying the bind.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
class Meta: # noqa: D106
|
|
95
|
+
name = "secret_scan"
|
|
96
|
+
description = "Backing function that looks up a secret at bind time"
|
|
97
|
+
|
|
98
|
+
FIXED_SCHEMA: ClassVar[pa.Schema] = schema({"id": pa.int64(), "name": pa.string()})
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def on_bind(cls, params: BindParams[EmptyArgs]) -> BindResponse: # noqa: D102
|
|
102
|
+
params.secrets.get("some_secret") # unresolved -> registers a two-phase lookup
|
|
103
|
+
return BindResponse(output_schema=cls.FIXED_SCHEMA)
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def process(cls, params: ProcessParams[EmptyArgs], state: None, out: OutputCollector) -> None: # noqa: D102
|
|
107
|
+
out.emit(pa.RecordBatch.from_pydict({"id": [1], "name": ["x"]}, schema=params.output_schema))
|
|
108
|
+
out.finish()
|
|
109
|
+
|
|
110
|
+
|
|
84
111
|
@init_single_worker
|
|
85
112
|
@bind_fixed_schema
|
|
86
113
|
class EventsFunction(TableFunctionGenerator[EmptyArgs]):
|
|
@@ -186,6 +213,18 @@ class TestTableWithFunction:
|
|
|
186
213
|
assert resolved.field(0).name == "id"
|
|
187
214
|
assert resolved.field(1).name == "name"
|
|
188
215
|
|
|
216
|
+
def test_function_backed_table_derives_schema_despite_secret_lookup(self) -> None:
|
|
217
|
+
"""A backing function that calls secrets.get() in on_bind still yields its schema.
|
|
218
|
+
|
|
219
|
+
Regression: on_bind's two-phase secret lookup makes the first bind return an
|
|
220
|
+
empty secret-scope-request schema; schema derivation must retry with resolved
|
|
221
|
+
secrets rather than adopt the empty schema (which produced 0-column tables).
|
|
222
|
+
"""
|
|
223
|
+
table = Table(name="things", function=SecretLookupFunction)
|
|
224
|
+
resolved = table.resolved_columns
|
|
225
|
+
assert [f.name for f in resolved] == ["id", "name"]
|
|
226
|
+
assert len(resolved) == 2
|
|
227
|
+
|
|
189
228
|
def test_table_function_backed_with_constraints(self) -> None:
|
|
190
229
|
"""Function-backed table can have constraints validated."""
|
|
191
230
|
table = Table(
|
|
@@ -336,7 +375,6 @@ class TestTableWithFunction:
|
|
|
336
375
|
from typing import Any as _Any
|
|
337
376
|
|
|
338
377
|
from vgi.invocation import BindResponse
|
|
339
|
-
from vgi.table_function import BindParams
|
|
340
378
|
|
|
341
379
|
@init_single_worker
|
|
342
380
|
class _ManualBindFn(TableFunctionGenerator[EmptyArgs]):
|
|
@@ -1110,7 +1110,7 @@ class TestArrowSchemaCorrectness:
|
|
|
1110
1110
|
def test_catalog_attach_result_schema(self) -> None:
|
|
1111
1111
|
"""Verify CatalogAttachResult Arrow schema."""
|
|
1112
1112
|
schema = CatalogAttachResult.ARROW_SCHEMA
|
|
1113
|
-
assert len(schema) ==
|
|
1113
|
+
assert len(schema) == 15
|
|
1114
1114
|
assert schema.field("attach_opaque_data").type == pa.binary()
|
|
1115
1115
|
assert schema.field("supports_transactions").type == pa.bool_()
|
|
1116
1116
|
assert schema.field("supports_time_travel").type == pa.bool_()
|
|
@@ -1120,6 +1120,7 @@ class TestArrowSchemaCorrectness:
|
|
|
1120
1120
|
assert schema.field("default_schema").type == pa.string()
|
|
1121
1121
|
assert schema.field("settings").type == pa.list_(pa.binary())
|
|
1122
1122
|
assert schema.field("secret_types").type == pa.list_(pa.binary())
|
|
1123
|
+
assert schema.field("attach_catalogs").type == pa.list_(pa.binary())
|
|
1123
1124
|
assert schema.field("resolved_data_version").type == pa.string()
|
|
1124
1125
|
assert schema.field("resolved_implementation_version").type == pa.string()
|
|
1125
1126
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance test: ConstParam value constraints are enforced at bind.
|
|
4
|
+
|
|
5
|
+
A ``ConstParam`` may declare ``choices``/``ge``/``le``/``gt``/``lt``/``pattern``.
|
|
6
|
+
Those are surfaced for agent discovery (via ``vgi_function_arguments()``) AND
|
|
7
|
+
must be enforced: a const value that violates a declared constraint fails at
|
|
8
|
+
bind with ``ArgumentValidationError``, rather than silently reaching
|
|
9
|
+
``compute()``. This mirrors the legacy ``Arg`` descriptor path.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
import pyarrow as pa
|
|
17
|
+
import pytest
|
|
18
|
+
|
|
19
|
+
from vgi.arguments import Arguments, ArgumentValidationError, ConstParam, Param, Returns
|
|
20
|
+
from vgi.protocol import BindRequest, FunctionType
|
|
21
|
+
from vgi.scalar_function import BindResult, ScalarFunction
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _Measure(ScalarFunction):
|
|
25
|
+
class Meta:
|
|
26
|
+
name = "measure"
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def on_bind(cls, params: object) -> BindResult:
|
|
30
|
+
return BindResult(pa.string())
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def compute(
|
|
34
|
+
cls,
|
|
35
|
+
unit: Annotated[str, ConstParam("Output unit", choices=["mm", "cm", "m"])],
|
|
36
|
+
precision: Annotated[int, ConstParam("Decimals", ge=0, le=10)],
|
|
37
|
+
value: Annotated[pa.DoubleArray, Param(doc="Value")],
|
|
38
|
+
) -> Annotated[pa.StringArray, Returns()]:
|
|
39
|
+
return pa.array([str(value)], type=pa.string())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _bind(unit: str, precision: int) -> None:
|
|
43
|
+
_Measure.bind(
|
|
44
|
+
BindRequest(
|
|
45
|
+
function_name="measure",
|
|
46
|
+
arguments=Arguments(positional=(pa.scalar(unit), pa.scalar(precision))),
|
|
47
|
+
function_type=FunctionType.SCALAR,
|
|
48
|
+
input_schema=pa.schema([("value", pa.float64())]),
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_valid_const_values_bind() -> None:
|
|
54
|
+
"""A const value inside every declared constraint binds cleanly."""
|
|
55
|
+
_bind("cm", 2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_choice_violation_raises_at_bind() -> None:
|
|
59
|
+
"""A const value outside the choice set fails at bind."""
|
|
60
|
+
with pytest.raises(ArgumentValidationError) as exc:
|
|
61
|
+
_bind("xx", 2)
|
|
62
|
+
assert "unit" in str(exc.value)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_range_violation_raises_at_bind() -> None:
|
|
66
|
+
"""A const value outside the numeric range fails at bind."""
|
|
67
|
+
with pytest.raises(ArgumentValidationError) as exc:
|
|
68
|
+
_bind("cm", 99)
|
|
69
|
+
assert "precision" in str(exc.value)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/container/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"container",
|
|
11
|
+
[
|
|
12
|
+
"errors.test",
|
|
13
|
+
"shared_http.test",
|
|
14
|
+
"shared_tcp.test",
|
|
15
|
+
"sklearn_smoke.test",
|
|
16
|
+
],
|
|
17
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/copy_from/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"copy_from",
|
|
11
|
+
[
|
|
12
|
+
"basic.test",
|
|
13
|
+
"options.test",
|
|
14
|
+
"secrets.test",
|
|
15
|
+
],
|
|
16
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/copy_to/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"copy_to",
|
|
11
|
+
[
|
|
12
|
+
"basic.test",
|
|
13
|
+
"failure.test",
|
|
14
|
+
"options.test",
|
|
15
|
+
"ordered.test",
|
|
16
|
+
"parallel.test",
|
|
17
|
+
"secrets.test",
|
|
18
|
+
"tmp_file.test",
|
|
19
|
+
"types.test",
|
|
20
|
+
],
|
|
21
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Conformance stub for ``vgi/test/sql/integration/github/``."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from tests.conformance._stub import skip_area
|
|
8
|
+
|
|
9
|
+
skip_area(
|
|
10
|
+
"github",
|
|
11
|
+
[
|
|
12
|
+
"download.test",
|
|
13
|
+
"errors.test",
|
|
14
|
+
],
|
|
15
|
+
)
|
|
@@ -9,10 +9,13 @@ from tests.conformance._stub import skip_area
|
|
|
9
9
|
skip_area(
|
|
10
10
|
"secret",
|
|
11
11
|
[
|
|
12
|
+
"secret_aggregate.test",
|
|
13
|
+
"secret_function_backed_table.test",
|
|
12
14
|
"secret_no_secret.test",
|
|
13
15
|
"secret_registration.test",
|
|
14
16
|
"secret_scalar.test",
|
|
15
17
|
"secret_scoped.test",
|
|
16
18
|
"secret_table_function.test",
|
|
19
|
+
"secret_table_in_out.test",
|
|
17
20
|
],
|
|
18
21
|
)
|
|
@@ -13,7 +13,14 @@ import pytest
|
|
|
13
13
|
from vgi_rpc import ArrowSerializableDataclass, ArrowType
|
|
14
14
|
|
|
15
15
|
from vgi.aggregate_function import AggregateFunction
|
|
16
|
-
from vgi.arguments import
|
|
16
|
+
from vgi.arguments import (
|
|
17
|
+
Arguments,
|
|
18
|
+
ArgumentValidationError,
|
|
19
|
+
ConstParam,
|
|
20
|
+
Param,
|
|
21
|
+
Returns,
|
|
22
|
+
validate_const_arg_constraints,
|
|
23
|
+
)
|
|
17
24
|
from vgi.table_function import ProcessParams
|
|
18
25
|
|
|
19
26
|
# ---------------------------------------------------------------------------
|
|
@@ -199,6 +206,49 @@ class TestInitSubclassParameterExtraction:
|
|
|
199
206
|
assert arg.arrow_type == pa.float64()
|
|
200
207
|
assert arg.const is True
|
|
201
208
|
|
|
209
|
+
def test_const_param_constraints_enforced(self) -> None:
|
|
210
|
+
"""A ConstParam's declared constraints are enforced at bind (same as scalar)."""
|
|
211
|
+
|
|
212
|
+
class BoundedAgg(AggregateFunction[SimpleState]):
|
|
213
|
+
class Meta:
|
|
214
|
+
name = "test_bounded_agg"
|
|
215
|
+
|
|
216
|
+
@classmethod
|
|
217
|
+
def initial_state(cls, params: ProcessParams[Any]) -> SimpleState:
|
|
218
|
+
return SimpleState()
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def update(
|
|
222
|
+
cls,
|
|
223
|
+
states: dict[int, SimpleState],
|
|
224
|
+
group_ids: pa.Int64Array,
|
|
225
|
+
value: Annotated[pa.DoubleArray, Param(doc="Values")],
|
|
226
|
+
mode: Annotated[str, ConstParam("Mode", choices=["min", "max"])] = "min",
|
|
227
|
+
) -> None:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
@classmethod
|
|
231
|
+
def combine(cls, source: SimpleState, target: SimpleState, params: ProcessParams[Any]) -> SimpleState:
|
|
232
|
+
return SimpleState()
|
|
233
|
+
|
|
234
|
+
@classmethod
|
|
235
|
+
def finalize(
|
|
236
|
+
cls,
|
|
237
|
+
group_ids: pa.Int64Array,
|
|
238
|
+
states: dict[int, SimpleState],
|
|
239
|
+
params: ProcessParams[Any],
|
|
240
|
+
) -> Annotated[pa.RecordBatch, Returns(pa.float64())]:
|
|
241
|
+
return pa.record_batch({"result": pa.array([], type=pa.float64())})
|
|
242
|
+
|
|
243
|
+
# The ConstParam carries its choices (threaded via _const_param_to_arg)...
|
|
244
|
+
const_params = BoundedAgg._const_params
|
|
245
|
+
assert const_params["mode"].choices == ["min", "max"]
|
|
246
|
+
# ...and the shared bind-time validator (called by aggregate_bind) enforces it.
|
|
247
|
+
validate_const_arg_constraints(const_params, Arguments(positional=(pa.scalar("max"),)))
|
|
248
|
+
with pytest.raises(ArgumentValidationError) as exc:
|
|
249
|
+
validate_const_arg_constraints(const_params, Arguments(positional=(pa.scalar("median"),)))
|
|
250
|
+
assert "mode" in str(exc.value)
|
|
251
|
+
|
|
202
252
|
def test_const_param_phases(self) -> None:
|
|
203
253
|
"""ConstParam phase values are stored correctly."""
|
|
204
254
|
|
|
@@ -12,13 +12,18 @@ import pytest
|
|
|
12
12
|
from vgi.argument_spec import (
|
|
13
13
|
VGI_ARG_KEY,
|
|
14
14
|
VGI_ARG_NAMED,
|
|
15
|
+
VGI_CHOICES_KEY,
|
|
16
|
+
VGI_DEFAULT_KEY,
|
|
15
17
|
VGI_DOC_KEY,
|
|
18
|
+
VGI_PATTERN_KEY,
|
|
19
|
+
VGI_RANGE_KEY,
|
|
16
20
|
VGI_TYPE_ANY,
|
|
17
21
|
VGI_TYPE_KEY,
|
|
18
22
|
VGI_TYPE_TABLE,
|
|
19
23
|
VGI_VARARGS_KEY,
|
|
20
24
|
VGI_VARARGS_TRUE,
|
|
21
25
|
ArgumentSpec,
|
|
26
|
+
_format_range,
|
|
22
27
|
argument_specs_to_schema,
|
|
23
28
|
extract_argument_specs,
|
|
24
29
|
schema_to_argument_specs,
|
|
@@ -722,3 +727,99 @@ class TestArgumentDoc:
|
|
|
722
727
|
specs = {s.name: s for s in extract_argument_specs(FunctionWithDocs)}
|
|
723
728
|
assert specs["unit"].doc == "Unit string, e.g. 'mi'"
|
|
724
729
|
assert specs["scale"].doc == ""
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
class TestArgumentConstraints:
|
|
733
|
+
"""Per-argument constraint metadata (choices / range / pattern / default)."""
|
|
734
|
+
|
|
735
|
+
def test_format_range_notation(self) -> None:
|
|
736
|
+
"""Interval notation covers inclusive, exclusive, open, and empty bounds."""
|
|
737
|
+
assert _format_range(0, 100, None, None) == "[0, 100]"
|
|
738
|
+
assert _format_range(None, None, 0, None) == "(0, +inf)"
|
|
739
|
+
assert _format_range(1, None, None, 10) == "[1, 10)"
|
|
740
|
+
assert _format_range(None, 5, None, None) == "(-inf, 5]"
|
|
741
|
+
assert _format_range(None, None, None, None) is None
|
|
742
|
+
|
|
743
|
+
def test_writer_emits_constraint_keys(self) -> None:
|
|
744
|
+
"""A constrained spec writes value-encoded constraint metadata keys."""
|
|
745
|
+
spec = ArgumentSpec(
|
|
746
|
+
name="unit",
|
|
747
|
+
position="unit",
|
|
748
|
+
arrow_type=pa.utf8(),
|
|
749
|
+
default_json='"mm"',
|
|
750
|
+
choices_json='["mm", "cm", "m"]',
|
|
751
|
+
range_notation="[0, 100]",
|
|
752
|
+
pattern="^[A-Z]{2}$",
|
|
753
|
+
)
|
|
754
|
+
meta = argument_specs_to_schema([spec])[0].metadata
|
|
755
|
+
assert meta[VGI_DEFAULT_KEY] == b'"mm"'
|
|
756
|
+
assert meta[VGI_CHOICES_KEY] == b'["mm", "cm", "m"]'
|
|
757
|
+
assert meta[VGI_RANGE_KEY] == b"[0, 100]"
|
|
758
|
+
assert meta[VGI_PATTERN_KEY] == b"^[A-Z]{2}$"
|
|
759
|
+
|
|
760
|
+
def test_unconstrained_arg_omits_keys(self) -> None:
|
|
761
|
+
"""An arg without constraints yields none of the constraint keys."""
|
|
762
|
+
meta = argument_specs_to_schema([ArgumentSpec(name="v", position=0, arrow_type=pa.int64())])[0].metadata
|
|
763
|
+
for key in (VGI_DEFAULT_KEY, VGI_CHOICES_KEY, VGI_RANGE_KEY, VGI_PATTERN_KEY):
|
|
764
|
+
assert meta is None or key not in meta
|
|
765
|
+
|
|
766
|
+
def test_constraints_round_trip(self) -> None:
|
|
767
|
+
"""Constraint metadata survives the schema round-trip."""
|
|
768
|
+
spec = ArgumentSpec(
|
|
769
|
+
name="mode",
|
|
770
|
+
position="mode",
|
|
771
|
+
arrow_type=pa.utf8(),
|
|
772
|
+
default_json='"add"',
|
|
773
|
+
choices_json='["add", "mul"]',
|
|
774
|
+
range_notation="[0, 10)",
|
|
775
|
+
pattern="^x+$",
|
|
776
|
+
)
|
|
777
|
+
out = schema_to_argument_specs(argument_specs_to_schema([spec]))[0]
|
|
778
|
+
assert out.default_json == '"add"'
|
|
779
|
+
assert out.choices_json == '["add", "mul"]'
|
|
780
|
+
assert out.range_notation == "[0, 10)"
|
|
781
|
+
assert out.pattern == "^x+$"
|
|
782
|
+
|
|
783
|
+
def test_extract_constraints_from_arg(self) -> None:
|
|
784
|
+
"""extract_argument_specs reads choices/default/bounds/pattern off Arg."""
|
|
785
|
+
|
|
786
|
+
class FunctionWithConstraints(TableInOutFunction): # type: ignore[type-arg]
|
|
787
|
+
mode: str = Arg[str]( # type: ignore[assignment]
|
|
788
|
+
"mode", doc="Blend mode", choices=["add", "mul"], default="add"
|
|
789
|
+
)
|
|
790
|
+
level: int = Arg[int](0, ge=1, le=10) # type: ignore[assignment]
|
|
791
|
+
code: str = Arg[str](1, pattern="^[A-Z]{2}$") # type: ignore[assignment]
|
|
792
|
+
|
|
793
|
+
specs = {s.name: s for s in extract_argument_specs(FunctionWithConstraints)}
|
|
794
|
+
assert specs["mode"].choices_json == '["add", "mul"]'
|
|
795
|
+
assert specs["mode"].default_json == '"add"'
|
|
796
|
+
assert specs["level"].range_notation == "[1, 10]"
|
|
797
|
+
assert specs["code"].pattern == "^[A-Z]{2}$"
|
|
798
|
+
|
|
799
|
+
def test_const_param_constraints_surface(self) -> None:
|
|
800
|
+
"""The modern Param/ConstParam API surfaces choices / range / pattern."""
|
|
801
|
+
from vgi.arguments import ConstParam, Param, Returns
|
|
802
|
+
from vgi.scalar_function import ScalarFunction
|
|
803
|
+
|
|
804
|
+
class ConstrainedScalar(ScalarFunction):
|
|
805
|
+
class Meta:
|
|
806
|
+
name = "constrained_scalar"
|
|
807
|
+
description = "constraint demo"
|
|
808
|
+
|
|
809
|
+
@classmethod
|
|
810
|
+
def compute(
|
|
811
|
+
cls,
|
|
812
|
+
unit: Annotated[str, ConstParam("Output unit", choices=["mm", "cm", "m"])],
|
|
813
|
+
precision: Annotated[int, ConstParam("Decimals", ge=0, le=10)],
|
|
814
|
+
code: Annotated[str, ConstParam("Label code", pattern="^[A-Z]{2}$")],
|
|
815
|
+
value: Annotated[pa.DoubleArray, Param(doc="Value")],
|
|
816
|
+
) -> Annotated[pa.StringArray, Returns()]:
|
|
817
|
+
return value # never executed — the test only reads arg specs
|
|
818
|
+
|
|
819
|
+
specs = {s.name: s for s in extract_argument_specs(ConstrainedScalar)}
|
|
820
|
+
assert specs["unit"].choices_json == '["mm", "cm", "m"]'
|
|
821
|
+
assert specs["precision"].range_notation == "[0, 10]"
|
|
822
|
+
assert specs["code"].pattern == "^[A-Z]{2}$"
|
|
823
|
+
# The columnar Param carries no constraints.
|
|
824
|
+
assert specs["value"].choices_json is None
|
|
825
|
+
assert specs["value"].range_notation is None
|
|
@@ -32,6 +32,7 @@ from vgi._test_fixtures.aggregate.dynamic import (
|
|
|
32
32
|
from vgi._test_fixtures.aggregate.generic import GenericSumFunction
|
|
33
33
|
from vgi._test_fixtures.aggregate.listagg import ListAggFunction
|
|
34
34
|
from vgi._test_fixtures.aggregate.percentile import PercentileFunction
|
|
35
|
+
from vgi._test_fixtures.aggregate.secret_typed import SecretTypedSumFunction
|
|
35
36
|
from vgi._test_fixtures.aggregate.streaming import StreamingSumFunction
|
|
36
37
|
from vgi._test_fixtures.aggregate.varargs import SumAllFunction
|
|
37
38
|
from vgi._test_fixtures.aggregate.window import (
|
|
@@ -50,6 +51,7 @@ __all__ = [
|
|
|
50
51
|
"ListAggFunction",
|
|
51
52
|
"ListAggState",
|
|
52
53
|
"PercentileFunction",
|
|
54
|
+
"SecretTypedSumFunction",
|
|
53
55
|
"StreamingSumFunction",
|
|
54
56
|
"SumAllFunction",
|
|
55
57
|
"SumFunction",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Secret-aware aggregate fixture (secret_typed_sum).
|
|
4
|
+
|
|
5
|
+
Demonstrates the *supported* aggregate x secret intersection: an aggregate
|
|
6
|
+
reads a statically-resolved secret in ``on_bind`` (declared via the ``Secret()``
|
|
7
|
+
annotation, so the C++ extension resolves it up front and delivers it on
|
|
8
|
+
``AggregateBindRequest.secrets``) and uses a secret *value* to shape its output
|
|
9
|
+
type. The aggregated value itself is computed normally in ``update`` /
|
|
10
|
+
``finalize``.
|
|
11
|
+
|
|
12
|
+
Boundary (intentional, see ``vgi.worker.aggregate_bind``): secret *values* are
|
|
13
|
+
only available at bind time. ``aggregate_update`` / ``aggregate_finalize``
|
|
14
|
+
receive an empty ``ResolvedSecrets`` (persisting secrets to shared aggregate
|
|
15
|
+
storage would leak them), and a two-phase ``params.secrets.get()`` inside an
|
|
16
|
+
aggregate's ``on_bind`` raises ``NotImplementedError``. This fixture therefore
|
|
17
|
+
resolves the secret statically and threads the bind-time decision to
|
|
18
|
+
``finalize`` via the output schema (which round-trips bind -> finalize).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Annotated, Any
|
|
24
|
+
|
|
25
|
+
import pyarrow as pa
|
|
26
|
+
|
|
27
|
+
from vgi._test_fixtures.aggregate._common import SumState
|
|
28
|
+
from vgi.aggregate_function import AggregateBindParams, AggregateFunction
|
|
29
|
+
from vgi.arguments import Param, Returns, Secret
|
|
30
|
+
from vgi.invocation import BindResponse
|
|
31
|
+
from vgi.metadata import FunctionExample
|
|
32
|
+
from vgi.schema_utils import schema
|
|
33
|
+
from vgi.table_function import ProcessParams
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SecretTypedSumFunction(AggregateFunction[SumState]):
|
|
37
|
+
"""Sum an integer column, choosing the result type from a secret.
|
|
38
|
+
|
|
39
|
+
``on_bind`` reads the ``vgi_example`` secret (resolved statically by the C++
|
|
40
|
+
extension). When the secret's ``use_ssl`` field is true the aggregate returns
|
|
41
|
+
the sum as ``DOUBLE``; otherwise as ``BIGINT``. This proves the secret's
|
|
42
|
+
*value* was delivered to the aggregate's bind — an aggregate that never
|
|
43
|
+
declared a secret could not vary its output type this way.
|
|
44
|
+
|
|
45
|
+
SQL: ``SELECT secret_typed_sum(n) FROM t GROUP BY g``
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
class Meta:
|
|
49
|
+
name = "secret_typed_sum"
|
|
50
|
+
description = "Sum an integer column; the result type is chosen from a secret"
|
|
51
|
+
categories = ["aggregate", "secret"]
|
|
52
|
+
examples = [
|
|
53
|
+
FunctionExample(
|
|
54
|
+
sql="SELECT secret_typed_sum(n) FROM (SELECT 1 AS n UNION ALL SELECT 2)",
|
|
55
|
+
description="Sum a column with the result type selected by the vgi_example secret",
|
|
56
|
+
),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def on_bind(
|
|
61
|
+
cls,
|
|
62
|
+
params: AggregateBindParams,
|
|
63
|
+
*,
|
|
64
|
+
vgi_example: Annotated[dict[str, pa.Scalar[Any]] | None, Secret("vgi_example")] = None,
|
|
65
|
+
**kwargs: Any,
|
|
66
|
+
) -> BindResponse:
|
|
67
|
+
"""Choose the result type from the statically-resolved secret.
|
|
68
|
+
|
|
69
|
+
The ``Secret()`` annotation advertises the requirement so the extension
|
|
70
|
+
pre-resolves the secret and delivers it on the bind request (keyed by the
|
|
71
|
+
DuckDB secret *name*). ``to_dict().of_type()`` matches it by the secret's
|
|
72
|
+
connector-serialized ``type`` field — no ``get()`` (which would register a
|
|
73
|
+
pending lookup and trigger the unsupported two-phase retry).
|
|
74
|
+
"""
|
|
75
|
+
secret = next(iter(params.secrets.to_dict().of_type("vgi_example")), {})
|
|
76
|
+
use_ssl = secret.get("use_ssl")
|
|
77
|
+
as_double = bool(use_ssl.as_py()) if use_ssl is not None else False
|
|
78
|
+
result_type = pa.float64() if as_double else pa.int64()
|
|
79
|
+
return BindResponse(output_schema=schema(result=result_type))
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def initial_state(cls, params: ProcessParams[None]) -> SumState:
|
|
83
|
+
"""Identity element for the sum."""
|
|
84
|
+
return SumState(total=0)
|
|
85
|
+
|
|
86
|
+
@classmethod
|
|
87
|
+
def update(
|
|
88
|
+
cls,
|
|
89
|
+
states: dict[int, SumState],
|
|
90
|
+
group_ids: pa.Int64Array,
|
|
91
|
+
value: Annotated[pa.Int64Array, Param(doc="Integer column to sum")],
|
|
92
|
+
) -> None:
|
|
93
|
+
"""Accumulate integer values into per-group totals."""
|
|
94
|
+
for i in range(len(group_ids)):
|
|
95
|
+
gid: int = group_ids[i].as_py()
|
|
96
|
+
v = value[i].as_py()
|
|
97
|
+
if v is not None:
|
|
98
|
+
states[gid] = SumState(total=states[gid].total + v)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def combine(cls, source: SumState, target: SumState, params: ProcessParams[None]) -> SumState:
|
|
102
|
+
"""Merge two partial sums."""
|
|
103
|
+
return SumState(total=target.total + source.total)
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def finalize(
|
|
107
|
+
cls,
|
|
108
|
+
group_ids: pa.Int64Array,
|
|
109
|
+
states: dict[int, SumState],
|
|
110
|
+
params: ProcessParams[None],
|
|
111
|
+
) -> Annotated[pa.RecordBatch, Returns()]:
|
|
112
|
+
"""Emit each group's sum, cast to the bind-time (secret-chosen) type."""
|
|
113
|
+
result_field = params.output_schema.field(0)
|
|
114
|
+
results = [s.total if (s := states[gid.as_py()]) is not None else None for gid in group_ids]
|
|
115
|
+
return pa.record_batch({result_field.name: pa.array(results, type=result_field.type)})
|