vgi-python 0.36.1__tar.gz → 0.37.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.36.1 → vgi_python-0.37.0}/PKG-INFO +1 -1
- vgi_python-0.37.0/conformance/filter-v2/cases/positive/boolean-column-predicate.json +32 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/manifest.json +5 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/pyproject.toml +1 -1
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_filter_v2.py +166 -1
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_serve.py +157 -1
- {vgi_python-0.36.1 → vgi_python-0.37.0}/uv.lock +1 -1
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/__init__.py +2 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/filters.py +123 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/worker.py +2 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/auth.py +12 -3
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/filter_v2.py +18 -5
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/serve.py +15 -7
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/table_filter_pushdown.py +79 -1
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/worker.py +75 -1
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.dockerignore +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.gitattributes +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/dependabot.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/workflows/release.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.github/workflows/tailnet-integration.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.gitignore +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.python-version +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/.vale.ini +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/CLAUDE.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/LICENSE +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/PROTOCOL.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/README.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/SECURITY.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/ci/README.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/ci/run-integration.sh +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/README.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/and-too-few-children.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/delta-required-upsert.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/nested-runtime-filter.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/remove-with-expression.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/required-runtime-filter.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/snapshot-nonzero-revision.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/unknown-document-property.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/negative/unknown-node.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/positive/advisory-upsert-delta.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/positive/empty-snapshot.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/positive/nested-required-snapshot.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/positive/remove-delta.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/cases/positive/runtime-filter-snapshot.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/filter-v2.schema.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/generate_runtime.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/run_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-add/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-add/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-add/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-multiply/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-multiply/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-multiply/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-negate/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-negate/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-negate/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-subtract/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-subtract/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/arithmetic-subtract/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/cast-integer-to-double/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/cast-integer-to-double/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/cast-integer-to-double/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-distinct_from/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-distinct_from/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-distinct_from/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-distinct_from/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-eq/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-eq/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-eq/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-eq/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ge/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ge/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ge/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ge/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-gt/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-gt/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-gt/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-gt/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-le/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-le/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-le/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-le/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-lt/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-lt/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-lt/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-lt/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ne/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ne/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ne/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-ne/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-not_distinct_from/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-not_distinct_from/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-not_distinct_from/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/comparison-not_distinct_from/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-atomic-multi-update/delta-0.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-atomic-multi-update/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-atomic-multi-update/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-atomic-multi-update/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-empty-noop/delta-0.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-empty-noop/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-empty-noop/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-empty-noop/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-upsert-remove-stale/delta-0.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-upsert-remove-stale/delta-1.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-upsert-remove-stale/delta-2.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-upsert-remove-stale/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-upsert-remove-stale/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/delta-upsert-remove-stale/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/empty-snapshot/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/empty-snapshot/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/empty-snapshot/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/empty-snapshot/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-cast-type-not-null/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-cast-type-not-null/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-column-index/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-column-index/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-column-name/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-column-name/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-contextual-cast-without-session/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-contextual-cast-without-session/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-duplicate-predicate-id/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-duplicate-predicate-id/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-external-column-index/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-external-column-index/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-external-column-index/join-keys-0.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-external-column-name/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-external-column-name/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-external-column-name/join-keys-0.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-field-non-struct/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-field-non-struct/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-in-payload-not-list/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-in-payload-not-list/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-in-payload-null-list/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-in-payload-null-list/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-invalid-overload/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-invalid-overload/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-missing-literal/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-missing-literal/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-non-boolean-root/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-non-boolean-root/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-unknown-standard-function/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/error-unknown-standard-function/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/external-in-nonzero-column/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/external-in-nonzero-column/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/external-in-nonzero-column/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/external-in-nonzero-column/join-keys-0.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/external-in-nonzero-column/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-contains/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-contains/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-contains/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-ends-with/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-ends-with/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-ends-with/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-list-contains/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-list-contains/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-list-contains/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-starts-with/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-starts-with/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/function-starts-with/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-empty/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-empty/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-empty/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-empty/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-with-null/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-with-null/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-with-null/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/in-with-null/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-not-null/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-not-null/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-not-null/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-not-null/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-null/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-null/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-null/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/is-null/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-and/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-and/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-and/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-not/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-not/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-not/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-or/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-or/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/kleene-or/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/nan-comparison/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/nan-comparison/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/nan-comparison/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/negative-zero-membership/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/negative-zero-membership/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/negative-zero-membership/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/nested-null-propagation/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/nested-null-propagation/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/nested-null-propagation/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-empty/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-empty/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-empty/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-empty/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-with-null/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-with-null/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-with-null/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/not-in-with-null/worker-expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/session-integer-division/expected.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/session-integer-division/filter.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime/session-integer-division/input.arrow +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/conformance/filter-v2/runtime-manifest.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/arguments.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/auth.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/catalogs.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/client.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/exceptions.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/filters.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/functions.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/http.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/index.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/metadata.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/observability.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/storage.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/transactor.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/api/worker.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/argument-serialization.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/logo.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/assets/social-card.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/authentication.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/catalog-interface.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/cli.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/column-statistics.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/concepts/index.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/contributing-docs.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/generator-api.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/global-functions.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/index.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/iroh.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/how-to/tailnet.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/index.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/lifecycle.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/metadata.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/overrides/main.html +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/protocol/index.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/protocol/vgi-catalog-query-pushdown-design.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/protocol/vgi-duckdb-filter-adapter.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/protocol/vgi-filter-encoding-v2-spec.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/protocol/vgi-protocol-proposed-changes.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/protocol/vgi-runtime-filter-artifacts.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/robots.txt +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/shared-storage.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/tutorial/index.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/tutorial/table.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/docs/vgi-logo.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/cache_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/calc_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/copy_format_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/filter_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/row_count_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/examples/sum_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/identity.png +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/mkdocs.yml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/scripts/measure_startup.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/scripts/regen_generated.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/test-data/generate.sh +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_aggregate_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_batch_metadata_callback.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_bind_and_join_keys.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_cli.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_copy_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_http_response_budget.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_iroh_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_scan_branches_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_split_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_stop_force.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_time_travel_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_splits.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/conftest.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/bad_version.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/generate.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/manifest.json +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/stale_anchor.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/truncated.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/valid_sealed.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table/test_wide_columns.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/test_blended_exchange.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/table_in_out/test_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/Dockerfile +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/compose.yaml +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/probe.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/run.sh +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/test_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/tailnet/worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_auth.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_cache_control.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_call_state_attach.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_client_oauth.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_codegen_request_inventory.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_docstrings.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_dynamic_filter_http_turns.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_exceptions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_filter_v2_builder.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_filter_v2_runtime_corpus.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_filter_v2_spec_schema.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_fixture_catalog_parity.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_function_shape_mismatch.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_function_storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_cpp_protocol_name.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_java_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_rust_request_builders.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_rust_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_import_side_effects.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_launch_transport.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_metadata.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_otel.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_schema_paths_v2.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_signing_key.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_split_token.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_subprocess_transport.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_union_argument.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_wire_record_schema_parity.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_worker_page.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/test_write_results.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_duckdb.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/same_name_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/sequence.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/splits.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/aggregate_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/argument_spec.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/arguments.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/attach_header.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/cache_control.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/catalog_interface.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/aggregate.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/client/errors.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_protocol_name.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/java_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/rust_request_builders.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/rust_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/exceptions.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/filter_v2_builder.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/function_storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/http/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/http/landing.html +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/http/vgi-client.js +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/invocation.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/logging_config.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/meta_worker.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/metadata.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/otel.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/profiling.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/protocol.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/py.typed +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/scalar_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/schema_path.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/schema_utils.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/secret_service.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/split_token.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/table_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/transactor/client.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/transactor/server.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/vgi/write_results.py +0 -0
- {vgi_python-0.36.1 → vgi_python-0.37.0}/wrangler.jsonc +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.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
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"encoding": "vgi.filters.v2",
|
|
3
|
+
"semantics": "vgi.duckdb.standard.v1",
|
|
4
|
+
"kind": "snapshot",
|
|
5
|
+
"predicates": [
|
|
6
|
+
{
|
|
7
|
+
"id": "query:bare-boolean-column",
|
|
8
|
+
"revision": 0,
|
|
9
|
+
"mode": "required",
|
|
10
|
+
"source": "query",
|
|
11
|
+
"expression": {
|
|
12
|
+
"node": "column_ref",
|
|
13
|
+
"column_index": 0,
|
|
14
|
+
"column_name": "flag"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "query:negated-boolean-column",
|
|
19
|
+
"revision": 0,
|
|
20
|
+
"mode": "required",
|
|
21
|
+
"source": "query",
|
|
22
|
+
"expression": {
|
|
23
|
+
"node": "not",
|
|
24
|
+
"expression": {
|
|
25
|
+
"node": "column_ref",
|
|
26
|
+
"column_index": 1,
|
|
27
|
+
"column_name": "other_flag"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
"document": "cases/positive/runtime-filter-snapshot.json",
|
|
31
31
|
"expected": "valid"
|
|
32
32
|
},
|
|
33
|
+
{
|
|
34
|
+
"id": "structural.boolean-column-predicate",
|
|
35
|
+
"document": "cases/positive/boolean-column-predicate.json",
|
|
36
|
+
"expected": "valid"
|
|
37
|
+
},
|
|
33
38
|
{
|
|
34
39
|
"id": "structural.and-too-few-children",
|
|
35
40
|
"document": "cases/negative/and-too-few-children.json",
|
|
@@ -6,7 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
8
|
from collections.abc import Mapping
|
|
9
|
-
from typing import cast
|
|
9
|
+
from typing import Any, cast
|
|
10
10
|
|
|
11
11
|
import pyarrow as pa
|
|
12
12
|
import pytest
|
|
@@ -531,3 +531,168 @@ def test_unknown_arrow_extension_metadata_is_rejected() -> None:
|
|
|
531
531
|
_batch(_document([_predicate(_comparison())]), (extension_field, b"opaque")),
|
|
532
532
|
output_schema=pa.schema([("n", pa.int64())]),
|
|
533
533
|
)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
# ---------------------------------------------------------------------------
|
|
537
|
+
# A boolean column is a predicate on its own
|
|
538
|
+
#
|
|
539
|
+
# `WHERE flag` / `WHERE NOT flag` is idiomatic SQL, and DuckDB pushes it down as
|
|
540
|
+
# a bare `column_ref` rather than rewriting it to `flag = true`. The schema
|
|
541
|
+
# admits that -- `coreExpression` lists `columnRef` first -- but the decoder's
|
|
542
|
+
# boolean gate used to enumerate node *kinds* instead of asking for the resolved
|
|
543
|
+
# *type*, so it refused every one of them: "predicate root must resolve to
|
|
544
|
+
# BOOLEAN", on a node whose type it had already resolved to BOOLEAN and stored.
|
|
545
|
+
# ---------------------------------------------------------------------------
|
|
546
|
+
|
|
547
|
+
_BOOL_FIELDS: list[pa.Field[Any]] = [pa.field("flag", pa.bool_()), pa.field("n", pa.int64())]
|
|
548
|
+
_BOOL_SCHEMA = pa.schema(_BOOL_FIELDS)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _flag(index: int = 0) -> dict[str, object]:
|
|
552
|
+
return {"node": "column_ref", "column_index": index, "column_name": "flag"}
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def test_bare_boolean_column_is_a_valid_predicate_root() -> None:
|
|
556
|
+
"""`WHERE flag` -- the column itself is the predicate."""
|
|
557
|
+
filters = deserialize_filters(
|
|
558
|
+
_batch(_document([_predicate(_flag())])),
|
|
559
|
+
output_schema=_BOOL_SCHEMA,
|
|
560
|
+
)
|
|
561
|
+
batch = pa.RecordBatch.from_pydict(
|
|
562
|
+
{"flag": [True, False, None, True], "n": [1, 2, 3, 4]},
|
|
563
|
+
schema=_BOOL_SCHEMA,
|
|
564
|
+
)
|
|
565
|
+
# evaluate() is the raw three-valued predicate: NULL stays NULL.
|
|
566
|
+
assert filters.evaluate(batch).to_pylist() == [True, False, None, True]
|
|
567
|
+
# apply() is what selects rows, and there a NULL predicate is not satisfied
|
|
568
|
+
# -- the same rows `flag = true` would keep, which is what makes the SQL
|
|
569
|
+
# rewrite below a projection rather than a change of meaning.
|
|
570
|
+
assert filters.apply(batch).to_pydict()["n"] == [1, 4]
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def test_negated_boolean_column_is_a_valid_predicate_root() -> None:
|
|
574
|
+
"""`WHERE NOT flag` -- `not` over a bare column, which the gate also refused."""
|
|
575
|
+
filters = deserialize_filters(
|
|
576
|
+
_batch(_document([_predicate({"node": "not", "expression": _flag()})])),
|
|
577
|
+
output_schema=_BOOL_SCHEMA,
|
|
578
|
+
)
|
|
579
|
+
batch = pa.RecordBatch.from_pydict(
|
|
580
|
+
{"flag": [True, False, None, True], "n": [1, 2, 3, 4]},
|
|
581
|
+
schema=_BOOL_SCHEMA,
|
|
582
|
+
)
|
|
583
|
+
assert filters.evaluate(batch).to_pylist() == [False, True, None, False]
|
|
584
|
+
# NOT NULL is NULL, so row 3 is not kept -- exactly `flag = false`.
|
|
585
|
+
assert filters.apply(batch).to_pydict()["n"] == [2]
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def test_boolean_column_predicates_render_as_sql() -> None:
|
|
589
|
+
"""They must reach SQL, not just the evaluator.
|
|
590
|
+
|
|
591
|
+
Left as a generic v2 expression a bare column renders no SQL at all, so a
|
|
592
|
+
SQL-backed worker loses pushdown on the commonest boolean predicate there
|
|
593
|
+
is -- and DuckDB does not re-apply what it pushed down, so that is a wrong
|
|
594
|
+
answer, not a slow one.
|
|
595
|
+
"""
|
|
596
|
+
positive = deserialize_filters(_batch(_document([_predicate(_flag())])), output_schema=_BOOL_SCHEMA)
|
|
597
|
+
where, params = positive.to_sql()
|
|
598
|
+
assert where == '"flag" = ?'
|
|
599
|
+
assert params == [True]
|
|
600
|
+
|
|
601
|
+
negative = deserialize_filters(
|
|
602
|
+
_batch(_document([_predicate({"node": "not", "expression": _flag()})])),
|
|
603
|
+
output_schema=_BOOL_SCHEMA,
|
|
604
|
+
)
|
|
605
|
+
where, params = negative.to_sql()
|
|
606
|
+
assert where == '"flag" = ?'
|
|
607
|
+
assert params == [False]
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def test_boolean_column_inside_a_conjunction_still_pushes_down() -> None:
|
|
611
|
+
"""The shape that actually showed up: `WHERE other = x AND NOT flag`.
|
|
612
|
+
|
|
613
|
+
One unrenderable child used to cost the whole conjunction its pushdown.
|
|
614
|
+
"""
|
|
615
|
+
document = _document(
|
|
616
|
+
[
|
|
617
|
+
_predicate(
|
|
618
|
+
{
|
|
619
|
+
"node": "and",
|
|
620
|
+
"children": [
|
|
621
|
+
{
|
|
622
|
+
"node": "comparison",
|
|
623
|
+
"op": "gt",
|
|
624
|
+
"left": _column(name="n", index=1),
|
|
625
|
+
"right": {"node": "literal", "value_ref": 0},
|
|
626
|
+
},
|
|
627
|
+
{"node": "not", "expression": _flag()},
|
|
628
|
+
],
|
|
629
|
+
}
|
|
630
|
+
)
|
|
631
|
+
]
|
|
632
|
+
)
|
|
633
|
+
filters = deserialize_filters(
|
|
634
|
+
_batch(document, (pa.field("value_0", pa.int64()), 2)),
|
|
635
|
+
output_schema=_BOOL_SCHEMA,
|
|
636
|
+
)
|
|
637
|
+
where, params = filters.to_sql()
|
|
638
|
+
assert where == '("n" > ? AND "flag" = ?)'
|
|
639
|
+
assert params == [2, False]
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def test_an_unresolved_column_is_not_assumed_boolean() -> None:
|
|
643
|
+
"""Without a bind schema the column's type is unknown -- refuse, don't guess."""
|
|
644
|
+
with pytest.raises(FilterDeserializationError, match="predicate root"):
|
|
645
|
+
deserialize_filters(_batch(_document([_predicate(_flag())])))
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def test_a_non_boolean_column_is_still_refused() -> None:
|
|
649
|
+
"""`WHERE n` where n is BIGINT is not a predicate."""
|
|
650
|
+
with pytest.raises(FilterDeserializationError, match="predicate root"):
|
|
651
|
+
deserialize_filters(
|
|
652
|
+
_batch(_document([_predicate(_column(name="n", index=1))])),
|
|
653
|
+
output_schema=_BOOL_SCHEMA,
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def test_an_unrenderable_filter_raises_rather_than_vanishing() -> None:
|
|
658
|
+
"""`to_sql()` must never quietly drop a predicate it cannot render.
|
|
659
|
+
|
|
660
|
+
DuckDB does not re-apply a filter it pushed into a table function, so the
|
|
661
|
+
worker's WHERE clause is the only thing standing between the caller and
|
|
662
|
+
rows the predicate excludes. A skipped filter is therefore a wrong answer,
|
|
663
|
+
not a slow one -- which is why this raises instead of degrading.
|
|
664
|
+
"""
|
|
665
|
+
from vgi.table_filter_pushdown import FilterSQLUnsupportedError
|
|
666
|
+
|
|
667
|
+
# `n + 1 > 2` -- arithmetic has no legacy leaf shape and no SQL rendering.
|
|
668
|
+
document = _document(
|
|
669
|
+
[
|
|
670
|
+
_predicate(
|
|
671
|
+
{
|
|
672
|
+
"node": "comparison",
|
|
673
|
+
"op": "gt",
|
|
674
|
+
"left": {
|
|
675
|
+
"node": "arithmetic",
|
|
676
|
+
"op": "add",
|
|
677
|
+
"left": _column(name="n", index=1),
|
|
678
|
+
"right": {"node": "literal", "value_ref": 0},
|
|
679
|
+
},
|
|
680
|
+
"right": {"node": "literal", "value_ref": 1},
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
]
|
|
684
|
+
)
|
|
685
|
+
filters = deserialize_filters(
|
|
686
|
+
_batch(
|
|
687
|
+
document,
|
|
688
|
+
(pa.field("value_0", pa.int64()), 1),
|
|
689
|
+
(pa.field("value_1", pa.int64()), 2),
|
|
690
|
+
),
|
|
691
|
+
output_schema=_BOOL_SCHEMA,
|
|
692
|
+
)
|
|
693
|
+
# It evaluates correctly -- there is simply no SQL for it.
|
|
694
|
+
batch = pa.RecordBatch.from_pydict({"flag": [True, True, True], "n": [0, 2, 5]}, schema=_BOOL_SCHEMA)
|
|
695
|
+
assert filters.apply(batch).to_pydict()["n"] == [2, 5]
|
|
696
|
+
|
|
697
|
+
with pytest.raises(FilterSQLUnsupportedError, match="no SQL rendering"):
|
|
698
|
+
filters.to_sql()
|
|
@@ -24,7 +24,7 @@ from vgi_rpc import (
|
|
|
24
24
|
from vgi_rpc.rpc import AuthContext, PeerResolutionContext, peer_identity_primary
|
|
25
25
|
|
|
26
26
|
from vgi.arguments import Arguments, Auth, Param, Returns
|
|
27
|
-
from vgi.auth import TokenIdentity
|
|
27
|
+
from vgi.auth import IssuedGrant, TokenIdentity
|
|
28
28
|
from vgi.client import Client
|
|
29
29
|
from vgi.scalar_function import ScalarFunction
|
|
30
30
|
from vgi.serve import (
|
|
@@ -486,6 +486,32 @@ class _IntrospectingWorker(Worker):
|
|
|
486
486
|
return None
|
|
487
487
|
|
|
488
488
|
|
|
489
|
+
class _MintingWorker(Worker):
|
|
490
|
+
"""Worker that implements only the optional grant-minting hook."""
|
|
491
|
+
|
|
492
|
+
functions = [_DoubleFunc]
|
|
493
|
+
|
|
494
|
+
@classmethod
|
|
495
|
+
def mint_grant(cls, principal: str, purpose: str, scopes: list[str], ttl_seconds: int) -> IssuedGrant:
|
|
496
|
+
# A sealed self-contained credential: the worker owns the format and
|
|
497
|
+
# the framework never parses it. Capped TTL, to pin that a worker may
|
|
498
|
+
# issue SHORTER than asked.
|
|
499
|
+
ttl = min(ttl_seconds, 3600)
|
|
500
|
+
return IssuedGrant(
|
|
501
|
+
token=f"sealed:{principal}:{':'.join(scopes)}:{purpose}",
|
|
502
|
+
expires_at=1_000_000.0 + ttl,
|
|
503
|
+
grant_id="grant-1",
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
class _BothWorker(_IntrospectingWorker):
|
|
508
|
+
"""Worker that both resolves credentials and mints grants."""
|
|
509
|
+
|
|
510
|
+
@classmethod
|
|
511
|
+
def mint_grant(cls, principal: str, purpose: str, scopes: list[str], ttl_seconds: int) -> IssuedGrant:
|
|
512
|
+
return IssuedGrant(token=f"sealed:{principal}", expires_at=1_000_000.0, grant_id="g")
|
|
513
|
+
|
|
514
|
+
|
|
489
515
|
class TestIntrospectResolverDetection:
|
|
490
516
|
"""``resolve_token`` presence is detected by override, not by a flag."""
|
|
491
517
|
|
|
@@ -516,6 +542,31 @@ def _introspector_ctx(principal: str) -> object:
|
|
|
516
542
|
)
|
|
517
543
|
|
|
518
544
|
|
|
545
|
+
def _minting_ctx(principal: str, *, auth_age_s: float = 0.0) -> object:
|
|
546
|
+
"""Build a ``CallContext`` for a caller who authenticated *auth_age_s* ago.
|
|
547
|
+
|
|
548
|
+
Minting reads ``auth_time`` off the credential's claims, and a credential
|
|
549
|
+
without one cannot mint at all. That single rule is what stops a grant
|
|
550
|
+
minting another grant — a grant is not IdP-issued, so it carries no
|
|
551
|
+
``auth_time`` — and what makes a static bearer token, which proves a
|
|
552
|
+
machine holds a secret rather than that a human just authenticated,
|
|
553
|
+
fail closed here.
|
|
554
|
+
"""
|
|
555
|
+
import time
|
|
556
|
+
|
|
557
|
+
from vgi_rpc.rpc import AuthContext, CallContext
|
|
558
|
+
|
|
559
|
+
return CallContext(
|
|
560
|
+
auth=AuthContext(
|
|
561
|
+
domain="test",
|
|
562
|
+
authenticated=True,
|
|
563
|
+
principal=principal,
|
|
564
|
+
claims={"auth_time": time.time() - auth_age_s},
|
|
565
|
+
),
|
|
566
|
+
emit_client_log=lambda *a, **k: None,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
|
|
519
570
|
def _hosted_protocols(worker_cls: type[Worker]) -> frozenset[str]:
|
|
520
571
|
"""Return the protocol names the server built for *worker_cls* hosts.
|
|
521
572
|
|
|
@@ -655,6 +706,111 @@ class TestTokenIntrospection:
|
|
|
655
706
|
create_app(_IntrospectingWorker, prefix="/vgi", describe=False)
|
|
656
707
|
|
|
657
708
|
|
|
709
|
+
class TestGrantMinting:
|
|
710
|
+
"""``issue_grant`` — the other half of ``vgi_rpc.Identity.v1``.
|
|
711
|
+
|
|
712
|
+
Guarded very differently from ``introspect_token``, and deliberately so.
|
|
713
|
+
Introspection answers a question about *somebody else's* credential, so it
|
|
714
|
+
needs an allowlist. Minting produces a credential for the *caller* — there
|
|
715
|
+
is no subject on the wire, so cross-subject minting is closed by
|
|
716
|
+
construction and no allowlist applies. What this repo owns is the wiring:
|
|
717
|
+
a ``mint_grant`` override becomes an ``IdentityImpl`` that offers the
|
|
718
|
+
method, and a worker that never wrote one does not host it.
|
|
719
|
+
"""
|
|
720
|
+
|
|
721
|
+
@staticmethod
|
|
722
|
+
def _identity(worker_cls: type[Worker], **env: str) -> object:
|
|
723
|
+
from vgi.serve import _build_identity
|
|
724
|
+
|
|
725
|
+
with pytest.MonkeyPatch.context() as mp:
|
|
726
|
+
mp.delenv("VGI_INTROSPECT_PRINCIPALS", raising=False)
|
|
727
|
+
for key, value in env.items():
|
|
728
|
+
mp.setenv(key, value)
|
|
729
|
+
return _build_identity(worker_cls, None)
|
|
730
|
+
|
|
731
|
+
def test_base_worker_has_no_minter(self) -> None:
|
|
732
|
+
"""A worker that never wrote the minting gets no issuer."""
|
|
733
|
+
assert _SingleWorker._grant_minter() is None
|
|
734
|
+
|
|
735
|
+
def test_override_is_detected(self) -> None:
|
|
736
|
+
"""Implementing the hook is what enables it."""
|
|
737
|
+
minter = _MintingWorker._grant_minter()
|
|
738
|
+
assert minter is not None
|
|
739
|
+
|
|
740
|
+
def test_minting_alone_hosts_the_protocol(self) -> None:
|
|
741
|
+
"""A worker may mint without resolving — the two are independent."""
|
|
742
|
+
assert "vgi_rpc.Identity.v1" in _hosted_protocols(_MintingWorker)
|
|
743
|
+
|
|
744
|
+
def test_minting_alone_offers_only_issue_grant(self) -> None:
|
|
745
|
+
"""The protocol describes what the worker does, not what it could do."""
|
|
746
|
+
identity = self._identity(_MintingWorker)
|
|
747
|
+
assert identity is not None
|
|
748
|
+
assert identity.offered_methods() == frozenset({"issue_grant"}) # type: ignore[attr-defined]
|
|
749
|
+
|
|
750
|
+
def test_minting_alone_needs_no_introspect_allowlist(self) -> None:
|
|
751
|
+
"""No allowlist is required to mint: a grant is not an oracle.
|
|
752
|
+
|
|
753
|
+
Requiring one would be a startup error for a worker that never exposes
|
|
754
|
+
anybody else's identity — the exact opposite of the guard's purpose.
|
|
755
|
+
"""
|
|
756
|
+
with pytest.MonkeyPatch.context() as mp:
|
|
757
|
+
mp.delenv("VGI_INTROSPECT_PRINCIPALS", raising=False)
|
|
758
|
+
create_app(_MintingWorker, prefix="/vgi", describe=False)
|
|
759
|
+
|
|
760
|
+
def test_both_hooks_offer_both_methods(self) -> None:
|
|
761
|
+
"""Resolving and minting compose."""
|
|
762
|
+
identity = self._identity(_BothWorker, VGI_INTROSPECT_PRINCIPALS="proxy-a")
|
|
763
|
+
assert identity is not None
|
|
764
|
+
assert identity.offered_methods() == frozenset({"introspect_token", "issue_grant"}) # type: ignore[attr-defined]
|
|
765
|
+
|
|
766
|
+
def test_the_caller_is_the_subject(self) -> None:
|
|
767
|
+
"""The framework supplies the principal; the wire method has no subject field.
|
|
768
|
+
|
|
769
|
+
This is the property that makes an allowlist unnecessary, so pin it
|
|
770
|
+
here rather than trusting the signature to stay that way.
|
|
771
|
+
"""
|
|
772
|
+
identity = self._identity(_MintingWorker)
|
|
773
|
+
assert identity is not None
|
|
774
|
+
grant = identity.issue_grant( # type: ignore[attr-defined]
|
|
775
|
+
"nightly-etl", ["read"], 600, _minting_ctx("alice")
|
|
776
|
+
)
|
|
777
|
+
assert grant.token.startswith("sealed:alice:")
|
|
778
|
+
assert grant.grant_id == "grant-1"
|
|
779
|
+
|
|
780
|
+
def test_a_worker_may_issue_a_shorter_ttl_than_asked(self) -> None:
|
|
781
|
+
"""A worker may narrow the request; it must never widen it."""
|
|
782
|
+
identity = self._identity(_MintingWorker)
|
|
783
|
+
assert identity is not None
|
|
784
|
+
grant = identity.issue_grant( # type: ignore[attr-defined]
|
|
785
|
+
"long", [], 86_400, _minting_ctx("alice")
|
|
786
|
+
)
|
|
787
|
+
assert grant.expires_at == 1_000_000.0 + 3600
|
|
788
|
+
|
|
789
|
+
def test_stale_authentication_cannot_mint(self) -> None:
|
|
790
|
+
"""Minting while the user is present is enforced, not just documented."""
|
|
791
|
+
from vgi_rpc.rpc._token_identity import StaleAuthError
|
|
792
|
+
|
|
793
|
+
identity = self._identity(_MintingWorker)
|
|
794
|
+
assert identity is not None
|
|
795
|
+
with pytest.raises(StaleAuthError):
|
|
796
|
+
identity.issue_grant("x", [], 60, _minting_ctx("alice", auth_age_s=86_400)) # type: ignore[attr-defined]
|
|
797
|
+
|
|
798
|
+
def test_a_credential_with_no_auth_time_cannot_mint(self) -> None:
|
|
799
|
+
"""A grant cannot mint another grant, and a static bearer cannot mint at all.
|
|
800
|
+
|
|
801
|
+
Neither carries ``auth_time`` — only an IdP-issued credential does — so
|
|
802
|
+
the lineage cannot escape the identity provider. This is the rule that
|
|
803
|
+
makes delegation accountable to a human login rather than to whoever
|
|
804
|
+
currently holds a machine secret.
|
|
805
|
+
"""
|
|
806
|
+
from vgi_rpc.rpc._token_identity import StaleAuthError
|
|
807
|
+
|
|
808
|
+
identity = self._identity(_MintingWorker)
|
|
809
|
+
assert identity is not None
|
|
810
|
+
with pytest.raises(StaleAuthError):
|
|
811
|
+
identity.issue_grant("x", [], 60, _introspector_ctx("etl-service")) # type: ignore[attr-defined]
|
|
812
|
+
|
|
813
|
+
|
|
658
814
|
class TestAuthUnavailableReExport:
|
|
659
815
|
"""``AuthUnavailableError`` is reachable without naming a vgi_rpc module."""
|
|
660
816
|
|
|
@@ -65,6 +65,7 @@ from vgi._test_fixtures.table.catalog_scans import (
|
|
|
65
65
|
ProjectsScanFunction,
|
|
66
66
|
)
|
|
67
67
|
from vgi._test_fixtures.table.filters import (
|
|
68
|
+
BoolFilterEchoFunction,
|
|
68
69
|
DictFilterEchoFunction,
|
|
69
70
|
DynamicFilterEchoFunction,
|
|
70
71
|
ExpressionFilterTestFunction,
|
|
@@ -216,6 +217,7 @@ __all__ = [
|
|
|
216
217
|
"ConstantColumnsFunction",
|
|
217
218
|
"CountryPartitionedSalesFunction",
|
|
218
219
|
"DepartmentsScanFunction",
|
|
220
|
+
"BoolFilterEchoFunction",
|
|
219
221
|
"DictFilterEchoFunction",
|
|
220
222
|
"DisjointRangePartitionedFunction",
|
|
221
223
|
"DoubleSequenceFunction",
|
|
@@ -191,6 +191,129 @@ class FilterEchoFunction(TableFunctionGenerator[FilterEchoFunctionArgs, FilterEc
|
|
|
191
191
|
state.remaining -= size
|
|
192
192
|
|
|
193
193
|
|
|
194
|
+
# ============================================================================
|
|
195
|
+
# BoolFilterEchoFunction — a table function with a BOOLEAN column, so the
|
|
196
|
+
# bare-column predicate shapes (`WHERE flag`, `WHERE NOT flag`) can be driven
|
|
197
|
+
# end to end.
|
|
198
|
+
#
|
|
199
|
+
# filter_echo cannot cover these: its schema has no boolean column, so the
|
|
200
|
+
# predicate cannot even be written. The table-in-out `echo` path cannot either
|
|
201
|
+
# — DuckDB does not hand it a bare boolean column as a pushed predicate, so a
|
|
202
|
+
# test written there passes whether or not the worker can decode one. It takes
|
|
203
|
+
# a TABLE FUNCTION with a boolean column to see the shape at all, which is why
|
|
204
|
+
# a decoder that refused every one of them ("predicate root must resolve to
|
|
205
|
+
# BOOLEAN", vgi-python < 0.36.2) went unnoticed by a filter-pushdown suite of
|
|
206
|
+
# 348 files including one dedicated to booleans.
|
|
207
|
+
#
|
|
208
|
+
# `pushed_filters` echoes the SQL rendering, so this covers both halves: the
|
|
209
|
+
# decode (the rows are auto-applied and must be right) and `to_sql()` (a shape
|
|
210
|
+
# that parses but renders nothing would show up here as "(none)").
|
|
211
|
+
# ============================================================================
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@dataclass(slots=True, frozen=True)
|
|
215
|
+
class _BoolFilterEchoArgs:
|
|
216
|
+
"""Arguments for BoolFilterEchoFunction."""
|
|
217
|
+
|
|
218
|
+
count: Annotated[int, Arg(0, doc="Number of rows to generate", ge=0)]
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass(kw_only=True)
|
|
222
|
+
class _BoolFilterEchoState(ArrowSerializableDataclass):
|
|
223
|
+
"""Remaining rows plus the echoed SQL rendering of the pushed filters."""
|
|
224
|
+
|
|
225
|
+
remaining: int
|
|
226
|
+
current_index: int = 0
|
|
227
|
+
filter_str: str = "(none)"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@init_single_worker
|
|
231
|
+
@bind_fixed_schema
|
|
232
|
+
@_cardinality_from_count
|
|
233
|
+
class BoolFilterEchoFunction(TableFunctionGenerator[_BoolFilterEchoArgs, _BoolFilterEchoState]):
|
|
234
|
+
"""Rows with a nullable BOOLEAN column, echoing the pushed-down filters.
|
|
235
|
+
|
|
236
|
+
SCHEMA
|
|
237
|
+
------
|
|
238
|
+
Output: {"n": int64, "flag": bool, "pushed_filters": string}
|
|
239
|
+
|
|
240
|
+
``flag`` cycles TRUE, FALSE, NULL so every predicate is exercised against
|
|
241
|
+
all three truth values -- the NULL row is what distinguishes ``WHERE flag``
|
|
242
|
+
from ``WHERE flag IS NOT FALSE``, and ``WHERE NOT flag`` from
|
|
243
|
+
``WHERE flag IS NOT TRUE``.
|
|
244
|
+
|
|
245
|
+
Attributes:
|
|
246
|
+
FIXED_SCHEMA: The fixed Arrow output schema this function always produces.
|
|
247
|
+
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
class Meta:
|
|
251
|
+
"""Metadata for BoolFilterEchoFunction."""
|
|
252
|
+
|
|
253
|
+
name = "bool_filter_echo"
|
|
254
|
+
description = "Rows with a nullable BOOLEAN column, echoing pushed-down filters"
|
|
255
|
+
categories = ["generator", "diagnostic"]
|
|
256
|
+
filter_pushdown = True
|
|
257
|
+
auto_apply_filters = True
|
|
258
|
+
projection_pushdown = True
|
|
259
|
+
examples = [
|
|
260
|
+
FunctionExample(
|
|
261
|
+
sql="SELECT * FROM bool_filter_echo(6) WHERE flag",
|
|
262
|
+
description="A boolean column is a predicate on its own",
|
|
263
|
+
),
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
FIXED_SCHEMA: ClassVar[pa.Schema] = schema({"n": pa.int64(), "flag": pa.bool_(), "pushed_filters": pa.utf8()})
|
|
267
|
+
|
|
268
|
+
@classmethod
|
|
269
|
+
def initial_state(cls, params: ProcessParams[_BoolFilterEchoArgs]) -> _BoolFilterEchoState:
|
|
270
|
+
"""Create initial state, caching the SQL rendering of the pushed filters."""
|
|
271
|
+
assert params.init_call is not None
|
|
272
|
+
pf = params.init_call.pushdown_filters
|
|
273
|
+
filters = (
|
|
274
|
+
cls.pushdown_filters(
|
|
275
|
+
pf,
|
|
276
|
+
join_keys=params.init_call.join_keys,
|
|
277
|
+
output_schema=params.init_call.output_schema,
|
|
278
|
+
)
|
|
279
|
+
if pf is not None
|
|
280
|
+
else None
|
|
281
|
+
)
|
|
282
|
+
return _BoolFilterEchoState(
|
|
283
|
+
remaining=params.args.count,
|
|
284
|
+
filter_str=_format_pushed_filters(filters),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
@classmethod
|
|
288
|
+
def process(
|
|
289
|
+
cls,
|
|
290
|
+
params: ProcessParams[_BoolFilterEchoArgs],
|
|
291
|
+
state: _BoolFilterEchoState,
|
|
292
|
+
out: OutputCollector,
|
|
293
|
+
) -> None:
|
|
294
|
+
"""Emit rows whose ``flag`` cycles TRUE, FALSE, NULL."""
|
|
295
|
+
if state.remaining <= 0:
|
|
296
|
+
out.finish()
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
n_values = list(range(state.current_index, state.current_index + state.remaining))
|
|
300
|
+
flags: list[bool | None] = [(True, False, None)[i % 3] for i in n_values]
|
|
301
|
+
|
|
302
|
+
out.emit(
|
|
303
|
+
pa.RecordBatch.from_pydict(
|
|
304
|
+
{
|
|
305
|
+
"n": n_values,
|
|
306
|
+
"flag": flags,
|
|
307
|
+
"pushed_filters": [state.filter_str] * len(n_values),
|
|
308
|
+
},
|
|
309
|
+
schema=params.output_schema,
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
state.current_index += state.remaining
|
|
314
|
+
state.remaining = 0
|
|
315
|
+
|
|
316
|
+
|
|
194
317
|
# ============================================================================
|
|
195
318
|
# ValuePruneFunction — exercises PushdownFilters.get_column_values('n'), the
|
|
196
319
|
# partition-pruning idiom (resolve the discrete value set up front, fetch only
|
|
@@ -140,6 +140,7 @@ from vgi._test_fixtures.table import (
|
|
|
140
140
|
RFF_SIMPLE_COLUMNS,
|
|
141
141
|
RFF_STRUCT_COLUMNS,
|
|
142
142
|
BatchIndexOverflowFunction,
|
|
143
|
+
BoolFilterEchoFunction,
|
|
143
144
|
BrokenMissingPartitionValuesFunction,
|
|
144
145
|
BrokenPartitionColumnAbsentFromBatchFunction,
|
|
145
146
|
BrokenPartitionMinNeqMaxFunction,
|
|
@@ -486,6 +487,7 @@ _EXAMPLE_CATALOG = Catalog(
|
|
|
486
487
|
# TableFunctionGenerator - generate output without input
|
|
487
488
|
ConstantColumnsFunction,
|
|
488
489
|
SlowCancellableFunction,
|
|
490
|
+
BoolFilterEchoFunction,
|
|
489
491
|
FilterEchoFunction,
|
|
490
492
|
FilterEchoPartitionedFunction,
|
|
491
493
|
FilterEchoTableScanFunction,
|
|
@@ -9,8 +9,8 @@ HTTP auth factories (require ``vgi[http]``):
|
|
|
9
9
|
bearer_authenticate, bearer_authenticate_static, chain_authenticate,
|
|
10
10
|
OAuthResourceMetadata, AuthUnavailableError
|
|
11
11
|
|
|
12
|
-
Token introspection (always available):
|
|
13
|
-
TokenIdentity, TokenResolver
|
|
12
|
+
Token introspection and delegation (always available):
|
|
13
|
+
TokenIdentity, TokenResolver, IssuedGrant, GrantMinter
|
|
14
14
|
|
|
15
15
|
JWT auth (requires ``vgi[oauth]``):
|
|
16
16
|
jwt_authenticate
|
|
@@ -30,16 +30,25 @@ from vgi_rpc.rpc import AuthContext, CallContext
|
|
|
30
30
|
# on the ``http`` extra. Still not re-exported by ``vgi_rpc.rpc`` itself, so
|
|
31
31
|
# the private module remains the only import path; re-exported here so a worker
|
|
32
32
|
# that implements ``resolve_token`` never has to name one.
|
|
33
|
-
from vgi_rpc.rpc._token_identity import TokenIdentity
|
|
33
|
+
from vgi_rpc.rpc._token_identity import IssuedGrant, TokenIdentity
|
|
34
34
|
|
|
35
35
|
#: What :meth:`vgi.worker.Worker.resolve_token` is. Upstream dropped its own
|
|
36
36
|
#: alias when identity moved to the RPC layer; it is spelled out here so the
|
|
37
37
|
#: name ``vgi.auth.TokenResolver`` keeps working.
|
|
38
38
|
TokenResolver = Callable[[str], "TokenIdentity | None"]
|
|
39
39
|
|
|
40
|
+
#: What :meth:`vgi.worker.Worker.mint_grant` is: ``(principal, purpose, scopes,
|
|
41
|
+
#: ttl_seconds) -> IssuedGrant``. The subject is a *parameter of the call the
|
|
42
|
+
#: framework makes*, never of the wire method — ``issue_grant`` has no subject
|
|
43
|
+
#: field, so a worker is handed the caller's own principal and cannot be asked
|
|
44
|
+
#: to mint for anybody else.
|
|
45
|
+
GrantMinter = Callable[[str, str, list[str], int], "IssuedGrant"]
|
|
46
|
+
|
|
40
47
|
__all__ = [
|
|
41
48
|
"AuthContext",
|
|
42
49
|
"CallContext",
|
|
50
|
+
"GrantMinter",
|
|
51
|
+
"IssuedGrant",
|
|
43
52
|
"TokenIdentity",
|
|
44
53
|
"TokenResolver",
|
|
45
54
|
]
|
|
@@ -740,11 +740,24 @@ class _Parser:
|
|
|
740
740
|
|
|
741
741
|
@staticmethod
|
|
742
742
|
def _is_boolean(expression: FilterExpression) -> bool:
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
743
|
+
"""Whether *expression* resolves to BOOLEAN.
|
|
744
|
+
|
|
745
|
+
Ask the type, never the node kind. The two agree for every node whose
|
|
746
|
+
type is structurally fixed -- a comparison is BOOLEAN whatever its
|
|
747
|
+
operands -- and that is exactly why enumerating kinds looked sufficient.
|
|
748
|
+
It is not: ``column_ref``, ``field_ref``, ``cast`` and ``arithmetic``
|
|
749
|
+
are BOOLEAN or not depending on a type resolved from elsewhere (the
|
|
750
|
+
bind output schema, or a parent expression), and the schema admits all
|
|
751
|
+
four as a predicate root -- ``coreExpression`` lists ``columnRef``
|
|
752
|
+
first. Enumerating kinds rejected the lot, so the idiomatic
|
|
753
|
+
``WHERE flag`` / ``WHERE NOT flag`` -- which DuckDB pushes down as a
|
|
754
|
+
bare column reference rather than rewriting to ``flag = true`` -- was
|
|
755
|
+
refused as "predicate root must resolve to BOOLEAN" even though the
|
|
756
|
+
parser had already resolved the column to BOOLEAN and stored it on the
|
|
757
|
+
node.
|
|
758
|
+
"""
|
|
759
|
+
data_type = expression_type(expression)
|
|
760
|
+
return data_type is not None and pa.types.is_boolean(data_type)
|
|
748
761
|
|
|
749
762
|
def _payload(self, prefix: str, ref: object) -> tuple[pa.Field[Any], pa.Scalar[Any]]:
|
|
750
763
|
index = _uint(ref, f"{prefix}_ref")
|