vgi-python 0.35.0__tar.gz → 0.36.1__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.35.0 → vgi_python-0.36.1}/PKG-INFO +1 -1
- {vgi_python-0.35.0 → vgi_python-0.36.1}/pyproject.toml +7 -2
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_stop_force.py +9 -3
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conftest.py +63 -3
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_client.py +24 -7
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/test_client.py +23 -7
- vgi_python-0.36.1/tests/test_dynamic_filter_http_turns.py +199 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/uv.lock +1 -1
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/filters.py +4 -1
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/sequence.py +4 -1
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/splits.py +15 -8
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/aggregate_function.py +8 -1
- vgi_python-0.36.1/vgi/auth.py +132 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/catalog_interface.py +40 -20
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/aggregate.py +4 -1
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/client.py +153 -98
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/filter_v2.py +62 -2
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/protocol.py +144 -26
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/scalar_function.py +3 -2
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/table_filter_pushdown.py +36 -0
- vgi_python-0.35.0/vgi/auth.py +0 -76
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.dockerignore +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.gitattributes +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/dependabot.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/styles/config/vocabularies/VGI/accept.txt +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/workflows/ci.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/workflows/docs.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/workflows/integration.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/workflows/release.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.github/workflows/tailnet-integration.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.gitignore +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.python-version +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/.vale.ini +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/CLAUDE.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/DOCS_ACCEPTANCE_CRITERIA.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/DOCS_REVIEW_RUBRIC.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/DOCS_USABILITY_TEST.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/LICENSE +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/PROTOCOL.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/README.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/RELEASE-RUNBOOK.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/SECURITY.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/ci/README.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/ci/preprocess-require.awk +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/ci/run-integration.sh +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/README.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/and-too-few-children.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/delta-required-upsert.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/nested-runtime-filter.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/remove-with-expression.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/required-runtime-filter.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/snapshot-nonzero-revision.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/unknown-document-property.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/negative/unknown-node.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/positive/advisory-upsert-delta.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/positive/empty-snapshot.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/positive/nested-required-snapshot.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/positive/remove-delta.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/cases/positive/runtime-filter-snapshot.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/filter-v2.schema.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/generate_runtime.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/manifest.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/run_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-add/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-add/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-add/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-multiply/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-multiply/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-multiply/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-negate/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-negate/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-negate/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-subtract/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-subtract/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/arithmetic-subtract/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/cast-integer-to-double/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/cast-integer-to-double/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/cast-integer-to-double/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-distinct_from/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-distinct_from/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-distinct_from/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-distinct_from/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-eq/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-eq/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-eq/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-eq/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ge/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ge/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ge/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ge/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-gt/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-gt/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-gt/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-gt/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-le/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-le/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-le/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-le/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-lt/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-lt/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-lt/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-lt/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ne/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ne/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ne/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-ne/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-not_distinct_from/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-not_distinct_from/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-not_distinct_from/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/comparison-not_distinct_from/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-atomic-multi-update/delta-0.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-atomic-multi-update/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-atomic-multi-update/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-atomic-multi-update/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-empty-noop/delta-0.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-empty-noop/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-empty-noop/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-empty-noop/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-upsert-remove-stale/delta-0.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-upsert-remove-stale/delta-1.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-upsert-remove-stale/delta-2.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-upsert-remove-stale/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-upsert-remove-stale/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/delta-upsert-remove-stale/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/empty-snapshot/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/empty-snapshot/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/empty-snapshot/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/empty-snapshot/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-cast-type-not-null/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-cast-type-not-null/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-column-index/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-column-index/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-column-name/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-column-name/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-contextual-cast-without-session/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-contextual-cast-without-session/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-duplicate-predicate-id/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-duplicate-predicate-id/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-external-column-index/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-external-column-index/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-external-column-index/join-keys-0.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-external-column-name/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-external-column-name/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-external-column-name/join-keys-0.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-field-non-struct/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-field-non-struct/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-in-payload-not-list/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-in-payload-not-list/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-in-payload-null-list/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-in-payload-null-list/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-invalid-overload/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-invalid-overload/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-missing-literal/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-missing-literal/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-non-boolean-root/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-non-boolean-root/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-unknown-standard-function/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/error-unknown-standard-function/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/external-in-nonzero-column/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/external-in-nonzero-column/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/external-in-nonzero-column/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/external-in-nonzero-column/join-keys-0.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/external-in-nonzero-column/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-contains/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-contains/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-contains/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-ends-with/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-ends-with/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-ends-with/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-list-contains/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-list-contains/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-list-contains/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-starts-with/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-starts-with/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/function-starts-with/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-empty/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-empty/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-empty/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-empty/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-with-null/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-with-null/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-with-null/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/in-with-null/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-not-null/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-not-null/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-not-null/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-not-null/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-null/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-null/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-null/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/is-null/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-and/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-and/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-and/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-not/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-not/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-not/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-or/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-or/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/kleene-or/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/nan-comparison/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/nan-comparison/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/nan-comparison/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/negative-zero-membership/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/negative-zero-membership/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/negative-zero-membership/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/nested-null-propagation/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/nested-null-propagation/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/nested-null-propagation/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-empty/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-empty/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-empty/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-empty/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-with-null/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-with-null/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-with-null/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/not-in-with-null/worker-expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/session-integer-division/expected.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/session-integer-division/filter.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime/session-integer-division/input.arrow +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/conformance/filter-v2/runtime-manifest.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/dist-vgi/.gitignore +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/aggregate-functions.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/arguments.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/auth.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/catalogs.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/client.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/exceptions.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/filters.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/functions.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/http.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/index.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/metadata.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/observability.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/storage.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/transactor.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/api/worker.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/argument-serialization.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/apple-touch-icon.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/favicon-16x16.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/favicon-32x32.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/favicon.ico +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/kinds/aggregate.svg +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/kinds/buffering.svg +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/kinds/scalar.svg +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/kinds/table-in-out.svg +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/kinds/table.svg +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/logo.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/assets/social-card.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/authentication.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/catalog-interface.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/cli.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/column-statistics.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/concepts/index.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/contributing-docs.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/filter-pushdown.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/generator-api.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/global-functions.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/catalogs.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/function-patterns.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/http-auth.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/index.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/iroh.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/pushdown-and-statistics.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/state-storage.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/how-to/tailnet.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/index.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/lifecycle.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/metadata.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/overrides/main.html +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/protocol/index.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/protocol/vgi-catalog-query-pushdown-design.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/protocol/vgi-duckdb-filter-adapter.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/protocol/vgi-filter-encoding-v2-spec.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/protocol/vgi-protocol-proposed-changes.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/protocol/vgi-runtime-filter-artifacts.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/robots.txt +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/shared-storage.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/stylesheets/extra.css +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/tutorial/index.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/tutorial/scalar.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/tutorial/table.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/docs/vgi-logo.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/batch_index_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/cache_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/calc_scalar_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/calc_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/copy_format_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/filter_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/greeting_scalar_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/row_count_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/series_streaming_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/examples/sum_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/identity.png +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/mkdocs.yml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/packages/vgi-fixtures/LICENSE +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/packages/vgi-fixtures/README.md +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/packages/vgi-fixtures/docker/Dockerfile +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/packages/vgi-fixtures/docker/docker-entrypoint.sh +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/packages/vgi-fixtures/pyproject.toml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/scripts/measure_startup.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/scripts/regen_generated.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/scripts/run_all_tests.sh +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/test-data/generate.sh +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/_http_fixtures.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_attach_option.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_catalog_interface.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_client_catalog.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_column_statistics.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_declarative.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_example_worker_catalog.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_integration.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_required_filters.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_scan_branches.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_serialization.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_setting.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_time_travel.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/catalog/test_writable_table.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_aggregate_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_batch_metadata_callback.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_bind_and_join_keys.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_broken_pipe.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_cli.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_cli_catalog_functions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_copy_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_http_response_budget.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_iroh_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_scan_branches_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_split_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_table_column_statistics.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_time_travel_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/client/test_worker_debug.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/_stub.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/conftest.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_accumulate.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_aggregate.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_attach.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_bearer_auth.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_const_param_enforcement.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_container.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_copy_from.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_copy_to.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_directory_parity.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_function_inventory.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_github.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_http_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_http_external_location.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_http_upload_url.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_macro.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_overload.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_protocol_inventory.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_protocol_version.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_resumable_scan.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_scalar.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_scalar_attach_opaque_data.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_secret.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_settings.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_splits.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_table.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_table_in_out.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_view.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/conformance/test_writable.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/anchor_len_overrun.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/bad_fingerprint.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/bad_flags_unsealed_but_key_present.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/bad_version.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/generate.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/manifest.json +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/reserved_flag_bit.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/stale_anchor.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/truncated.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/valid_sealed.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/data/split_tokens/valid_unsealed.bin +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_bernoulli_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_binary_packet_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_conditional_message_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_hash_seed_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_multiply_by_setting_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_multiply_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_random_bytes_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/scalar/test_return_secret_value_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_constant_columns_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_double_sequence_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_exception_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_filter_echo_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_logging_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_nested_sequence_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_partitioned_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_projected_data_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_sequence_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_settings_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_struct_settings_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/generator/test_ten_thousand_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table/test_wide_columns.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_buffer_input_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_echo_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_exception_functions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_filter_by_setting_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_repeat_inputs_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_substream_partial_sum_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/generator/test_sum_all_columns_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/test_blended_exchange.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/table_in_out/test_blended_metadata.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/Dockerfile +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/compose.yaml +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/probe.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/run.sh +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/test_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/tailnet/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_access_log_audit.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_aggregate_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_argument_spec.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_auth.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_bind_exceptions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_bind_request_at_clause.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_bound_storage_conformance.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_cache_control.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_call_state_attach.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_catalog_auth_binding.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_client_oauth.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_codegen_request_inventory.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_copy_from_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_copy_to_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_docstrings.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_documentation_examples.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_example_function_arg_types.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_examples_workers.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_exception_handling.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_exceptions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_filter_pushdown.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_filter_pushdown_extension.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_filter_v2.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_filter_v2_builder.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_filter_v2_runtime_corpus.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_filter_v2_spec_schema.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_fixture_catalog_parity.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_function_shape_mismatch.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_function_storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_function_storage_azure_sql.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_function_storage_cf_do.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_function_storage_cf_do_integration.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_function_storage_conformance.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_cpp_constants.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_cpp_protocol_name.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_cpp_protocol_version.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_cpp_request_builders.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_cpp_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_cpp_secret.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_go_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_java_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_protocol_version.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_rust_request_builders.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_rust_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_schemas_cross_lang.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_ts_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_generated_ts_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_http_demo_storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_http_s3_offload_input.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_http_s3_offload_output.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_import_side_effects.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_launch_transport.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_metadata.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_mypy_consolidated.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_nest_tensor.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_otel.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_projection_enforcement.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_projection_repro.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_protocol_classes.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_resolved_secrets.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_schema_paths_v2.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_schema_scoped_functions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_schema_utils.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_serve.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_setting_secret_annotations.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_signing_key.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_split_token.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_subprocess_transport.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_table_buffering_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_table_function_dynamic_to_string.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_tcp_transport.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_type_bounds.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_union_argument.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_wire_record_schema_parity.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_worker_cli.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_worker_page.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/test_write_results.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/transactor/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/tests/transactor/test_transactor.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_duckdb.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_storage_profile.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/accumulate/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/accumulate/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/_common.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/basic.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/dynamic.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/generic.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/listagg.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/percentile.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/same_name.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/secret_typed.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/streaming.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/varargs.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/aggregate/window.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/attach_options.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/bad_enum.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/bad_protocol.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/cancellable.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/catalog.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/companion.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/copy_from.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/copy_to.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/global_functions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/http_server.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/narrow_bind/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/narrow_bind/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/nest_tensor.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/orchard_catalog.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/projection_repro/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/projection_repro/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/_common.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/arithmetic.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/bench_ladder.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/binary.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/formatting.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/geo.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/null_handling.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/random_demo.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/same_name.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/settings_secrets.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/scalar/type_info.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/schema_reconcile/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/schema_reconcile/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/simple_writable.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/_common.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/batch_index.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/batch_index_broken.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/cache.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/catalog_scans.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/late_materialization.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/make_series.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/misc.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/order_modes.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/pairs.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/partition_columns.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/partition_columns_broken.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/profiling_example.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/required_filters.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/same_name_cached.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/same_name_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/settings.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/transaction_storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/tt_pushdown.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/typed_probe.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/versioned.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table/wide.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table_in_out.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/table_in_out_same_name.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/twin_catalogs.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/versioned.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/versioned_tables.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/writable/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/writable/generic.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/writable/table.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/_test_fixtures/writable/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/argument_spec.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/arguments.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/attach_header.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/cache_control.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/_descriptor_spec.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/attach_option.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/descriptors.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/duckdb_statistics.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/secret_type.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/setting.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/catalog/storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/catalog_mixin.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli_catalog.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli_schema.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli_table.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli_transaction.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli_utils.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/cli_view.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/client/errors.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/_common.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_constants.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_protocol_name.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_protocol_version.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_request_builders.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_secret_protocol_version.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_secret_request_builders.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/cpp_secret_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/go_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/java_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/protocol_version.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/rust_request_builders.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/rust_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/ts_client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/codegen/ts_schemas.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/copy_from_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/copy_to_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/exceptions.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/filter_v2_builder.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/function_storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/function_storage_azure_sql.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/function_storage_cf_do.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/http/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/http/demo_storage.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/http/landing.html +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/http/landing_page.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/http/vgi-client.js +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/http/worker_page.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/invocation.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/logging_config.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/meta_worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/metadata.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/otel.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/profiling.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/protocol_version.txt +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/py.typed +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/schema_path.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/schema_utils.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/secret_protocol.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/secret_service.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/serve.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/split_token.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/table_buffering_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/table_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/table_in_out_function.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/transactor/__init__.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/transactor/_duckdb_compat.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/transactor/client.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/transactor/protocol.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/transactor/server.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/worker.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/vgi/write_results.py +0 -0
- {vgi_python-0.35.0 → vgi_python-0.36.1}/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.36.1
|
|
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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vgi-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.36.1"
|
|
4
4
|
description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
keywords = [
|
|
@@ -223,7 +223,12 @@ warn_unused_ignores = false
|
|
|
223
223
|
python-version = "3.13"
|
|
224
224
|
|
|
225
225
|
[tool.pytest.ini_options]
|
|
226
|
-
|
|
226
|
+
# worksteal: xdist's default "load" hands each worker a contiguous slice of the
|
|
227
|
+
# collection up front, so tests/catalog/test_integration.py (30 tests that each
|
|
228
|
+
# start a fresh catalog worker process, ~0.7 s apiece) all landed on one xdist
|
|
229
|
+
# worker, which then ran for 20 s after the rest had finished. worksteal lets
|
|
230
|
+
# idle workers take queued tests from busy ones. Ignored without -n.
|
|
231
|
+
addopts = "--ruff --dist worksteal"
|
|
227
232
|
testpaths = ["tests"]
|
|
228
233
|
|
|
229
234
|
[tool.coverage.run]
|
|
@@ -26,8 +26,11 @@ import pytest
|
|
|
26
26
|
from vgi.arguments import Arguments
|
|
27
27
|
from vgi.client.client import Client, _default_pool
|
|
28
28
|
|
|
29
|
-
#
|
|
30
|
-
|
|
29
|
+
# How long the worker sleeps before each row. The first row alone costs this
|
|
30
|
+
# much, so it is kept short; the forced stop is held to half of it (see
|
|
31
|
+
# test_force_returns_promptly_mid_stream), which a teardown that waited out the
|
|
32
|
+
# sleep would miss by a wide margin, and a kill (milliseconds) clears.
|
|
33
|
+
_SLEEP_MS = 2_000
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
def _started_client(**kwargs: Any) -> Client:
|
|
@@ -65,10 +68,13 @@ def test_force_returns_promptly_mid_stream(tmp_path: Any) -> None:
|
|
|
65
68
|
)
|
|
66
69
|
next(gen) # worker is now mid-stream, sleeping between batches
|
|
67
70
|
|
|
71
|
+
# The client's reader thread asks for the next row as soon as it has the
|
|
72
|
+
# first, so the worker is now inside that row's sleep. A stop that waited
|
|
73
|
+
# it out would take nearly the whole sleep; hold the kill to half of it.
|
|
68
74
|
started = time.monotonic()
|
|
69
75
|
client.stop(force=True)
|
|
70
76
|
elapsed = time.monotonic() - started
|
|
71
|
-
assert elapsed < _SLEEP_MS /
|
|
77
|
+
assert elapsed < _SLEEP_MS / 2000.0, f"forced stop waited {elapsed:.2f}s on a sleeping worker"
|
|
72
78
|
|
|
73
79
|
|
|
74
80
|
def test_graceful_stop_is_unchanged() -> None:
|
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
"""Shared fixtures for VGI tests."""
|
|
4
4
|
|
|
5
|
+
import contextlib
|
|
5
6
|
import logging
|
|
6
7
|
import os
|
|
7
8
|
import shlex
|
|
8
9
|
import shutil
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
12
|
+
import threading
|
|
11
13
|
import uuid
|
|
12
14
|
from typing import Any
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
import
|
|
16
|
+
# pyarrow imports numpy, and numpy's OpenBLAS starts one thread per core at
|
|
17
|
+
# import and busy-waits them: about 2.7 CPU-seconds per process on a 48-core
|
|
18
|
+
# machine. No test does linear algebra. This module is loaded before xdist
|
|
19
|
+
# starts its workers, so every process of the session inherits the setting:
|
|
20
|
+
# xdist workers, fixture and example workers, HTTP servers, CLI runs. On
|
|
21
|
+
# 48 cores it cut the suite's CPU time from 363 s to 208 s.
|
|
22
|
+
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
import pyarrow as pa # noqa: E402
|
|
25
|
+
import pytest # noqa: E402
|
|
26
|
+
|
|
27
|
+
from vgi import schema # noqa: E402
|
|
18
28
|
|
|
19
29
|
# =============================================================================
|
|
20
30
|
# Fixture-worker transport
|
|
@@ -66,6 +76,38 @@ CATALOG_WORKER = _fixture_command(sys.executable, "-m", "vgi._test_fixtures.cata
|
|
|
66
76
|
SUBPROCESS_FIXTURE_WORKER = "vgi-fixture-worker"
|
|
67
77
|
|
|
68
78
|
|
|
79
|
+
def pytest_configure(config: pytest.Config) -> None:
|
|
80
|
+
"""Start the shared fixture worker while the test processes collect.
|
|
81
|
+
|
|
82
|
+
Otherwise the first tests to reach it all wait for its start (about a
|
|
83
|
+
second of imports) and then for the first function listings, which build
|
|
84
|
+
and encode every function's metadata before it is cached; each costs
|
|
85
|
+
the one GIL-bound worker 0.2-0.3 s, and the early tests run them all at
|
|
86
|
+
once. Collection takes several seconds, so warming the worker in the
|
|
87
|
+
background here costs nothing. Only the controlling process does it.
|
|
88
|
+
"""
|
|
89
|
+
if hasattr(config, "workerinput") or TEST_TRANSPORT != "launch":
|
|
90
|
+
return
|
|
91
|
+
threading.Thread(target=_warm_fixture_worker, name="warm-fixture-worker", daemon=True).start()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _warm_fixture_worker() -> None:
|
|
95
|
+
"""Launch the fixture worker and fill its function-listing caches."""
|
|
96
|
+
from vgi.catalog import SchemaObjectType
|
|
97
|
+
from vgi.client import Client
|
|
98
|
+
|
|
99
|
+
# Best effort: a worker that cannot start fails the tests that use it, with
|
|
100
|
+
# the real error.
|
|
101
|
+
with contextlib.suppress(Exception), Client(FIXTURE_WORKER) as client:
|
|
102
|
+
attach = client.catalog_attach(name="example", options={}, data_version_spec=None, implementation_version=None)
|
|
103
|
+
for kind in (
|
|
104
|
+
SchemaObjectType.TABLE_FUNCTION,
|
|
105
|
+
SchemaObjectType.SCALAR_FUNCTION,
|
|
106
|
+
SchemaObjectType.AGGREGATE_FUNCTION,
|
|
107
|
+
):
|
|
108
|
+
list(client.schema_contents(attach_opaque_data=attach.attach_opaque_data, path=["main"], type=kind))
|
|
109
|
+
|
|
110
|
+
|
|
69
111
|
def pytest_unconfigure(config: pytest.Config) -> None:
|
|
70
112
|
"""Stop this session's launched workers rather than leaving them to idle out.
|
|
71
113
|
|
|
@@ -132,6 +174,24 @@ def empty_batch_from_schema(schema: pa.Schema) -> pa.RecordBatch:
|
|
|
132
174
|
return pa.RecordBatch.from_pydict({field.name: [] for field in schema}, schema=schema)
|
|
133
175
|
|
|
134
176
|
|
|
177
|
+
def record_opened_connections(client: Any) -> list[int]:
|
|
178
|
+
"""Record the index of each worker connection a started ``client`` opens from now on.
|
|
179
|
+
|
|
180
|
+
The primary connection (index 0) is opened when the client starts, so call
|
|
181
|
+
this afterwards: the list then holds only secondary connections, which a
|
|
182
|
+
call opens for fan-out. Only records; each connection is opened for real.
|
|
183
|
+
"""
|
|
184
|
+
opened: list[int] = []
|
|
185
|
+
spawn = client._spawn_worker
|
|
186
|
+
|
|
187
|
+
def recording_spawn(worker_index: int) -> Any:
|
|
188
|
+
opened.append(worker_index)
|
|
189
|
+
return spawn(worker_index)
|
|
190
|
+
|
|
191
|
+
client._spawn_worker = recording_spawn
|
|
192
|
+
return opened
|
|
193
|
+
|
|
194
|
+
|
|
135
195
|
# =============================================================================
|
|
136
196
|
# Fixtures
|
|
137
197
|
# =============================================================================
|
|
@@ -4,12 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import os
|
|
7
8
|
from typing import cast
|
|
8
9
|
|
|
9
10
|
import pyarrow as pa
|
|
10
11
|
import pytest
|
|
11
12
|
|
|
12
|
-
from tests.conftest import assert_total_rows
|
|
13
|
+
from tests.conftest import assert_total_rows, record_opened_connections
|
|
13
14
|
from vgi import schema
|
|
14
15
|
from vgi.arguments import Arguments
|
|
15
16
|
from vgi.client import Client
|
|
@@ -388,6 +389,7 @@ class TestScalarFunctionParallel:
|
|
|
388
389
|
batches = [pa.RecordBatch.from_pydict({"x": list(range(i * 100, (i + 1) * 100))}, schema=s) for i in range(10)]
|
|
389
390
|
|
|
390
391
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
392
|
+
opened = record_opened_connections(client)
|
|
391
393
|
outputs = list(
|
|
392
394
|
client.scalar_function(
|
|
393
395
|
function_name="double",
|
|
@@ -397,6 +399,11 @@ class TestScalarFunctionParallel:
|
|
|
397
399
|
)
|
|
398
400
|
)
|
|
399
401
|
|
|
402
|
+
# Ten batches dealt round-robin reach every connection the client
|
|
403
|
+
# allows: four, or one per core on a smaller machine (GitHub's macOS
|
|
404
|
+
# runners have three).
|
|
405
|
+
assert opened == list(range(1, min(4, os.cpu_count() or 1)))
|
|
406
|
+
|
|
400
407
|
# Should get all 1000 rows back
|
|
401
408
|
assert_total_rows(outputs, 1000)
|
|
402
409
|
|
|
@@ -675,9 +682,12 @@ class TestRandomIntFunction:
|
|
|
675
682
|
class TestScalarMultiWorkerEdgeCases:
|
|
676
683
|
"""Tests for edge cases with multiple workers for scalar functions.
|
|
677
684
|
|
|
678
|
-
These tests
|
|
685
|
+
These tests exposed timeout/hang bugs when:
|
|
679
686
|
- Processing parquet with one batch of zero rows
|
|
680
|
-
- Additional workers spawned but
|
|
687
|
+
- Additional workers were spawned but received no batches
|
|
688
|
+
|
|
689
|
+
The client now opens a secondary connection only when a batch is dealt to
|
|
690
|
+
it, so no connection sits idle. The tests check that too.
|
|
681
691
|
"""
|
|
682
692
|
|
|
683
693
|
def test_zero_row_batch_single_worker(self, fixture_worker: str) -> None:
|
|
@@ -704,8 +714,9 @@ class TestScalarMultiWorkerEdgeCases:
|
|
|
704
714
|
s = schema(x=pa.int64())
|
|
705
715
|
zero_row_batch = pa.RecordBatch.from_pydict({"x": []}, schema=s)
|
|
706
716
|
|
|
707
|
-
#
|
|
717
|
+
# Allow 4 workers even though there's only one batch with zero rows
|
|
708
718
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
719
|
+
opened = record_opened_connections(client)
|
|
709
720
|
outputs = list(
|
|
710
721
|
client.scalar_function(
|
|
711
722
|
function_name="double",
|
|
@@ -715,17 +726,19 @@ class TestScalarMultiWorkerEdgeCases:
|
|
|
715
726
|
)
|
|
716
727
|
)
|
|
717
728
|
|
|
718
|
-
# Should complete without hanging
|
|
729
|
+
# Should complete without hanging, on the primary alone
|
|
719
730
|
assert len(outputs) == 1
|
|
720
731
|
assert outputs[0].num_rows == 0
|
|
732
|
+
assert opened == []
|
|
721
733
|
|
|
722
734
|
def test_single_batch_multiple_workers(self, fixture_worker: str) -> None:
|
|
723
735
|
"""Single normal batch with max_workers=4 should complete without hanging."""
|
|
724
736
|
s = schema(x=pa.int64())
|
|
725
737
|
single_batch = pa.RecordBatch.from_pydict({"x": [1, 2, 3]}, schema=s)
|
|
726
738
|
|
|
727
|
-
#
|
|
739
|
+
# Allow 4 workers even though there's only 1 batch
|
|
728
740
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
741
|
+
opened = record_opened_connections(client)
|
|
729
742
|
outputs = list(
|
|
730
743
|
client.scalar_function(
|
|
731
744
|
function_name="double",
|
|
@@ -737,6 +750,7 @@ class TestScalarMultiWorkerEdgeCases:
|
|
|
737
750
|
|
|
738
751
|
# Should complete without hanging and return correct data
|
|
739
752
|
assert_total_rows(outputs, 3)
|
|
753
|
+
assert opened == []
|
|
740
754
|
|
|
741
755
|
def test_fewer_batches_than_workers(self, fixture_worker: str) -> None:
|
|
742
756
|
"""2 batches with max_workers=4 should complete without hanging."""
|
|
@@ -744,8 +758,9 @@ class TestScalarMultiWorkerEdgeCases:
|
|
|
744
758
|
batch1 = pa.RecordBatch.from_pydict({"x": [1, 2]}, schema=s)
|
|
745
759
|
batch2 = pa.RecordBatch.from_pydict({"x": [3, 4, 5]}, schema=s)
|
|
746
760
|
|
|
747
|
-
#
|
|
761
|
+
# Allow 4 workers even though there are only 2 batches
|
|
748
762
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
763
|
+
opened = record_opened_connections(client)
|
|
749
764
|
outputs = list(
|
|
750
765
|
client.scalar_function(
|
|
751
766
|
function_name="double",
|
|
@@ -757,3 +772,5 @@ class TestScalarMultiWorkerEdgeCases:
|
|
|
757
772
|
|
|
758
773
|
# Should complete without hanging and return correct data (5 rows total)
|
|
759
774
|
assert_total_rows(outputs, 5)
|
|
775
|
+
# The second batch opened the one secondary connection it needed.
|
|
776
|
+
assert opened == [1]
|
|
@@ -10,7 +10,13 @@ import time
|
|
|
10
10
|
import pyarrow as pa
|
|
11
11
|
import pytest
|
|
12
12
|
|
|
13
|
-
from tests.conftest import
|
|
13
|
+
from tests.conftest import (
|
|
14
|
+
SUBPROCESS_FIXTURE_WORKER,
|
|
15
|
+
assert_single_result,
|
|
16
|
+
assert_total_rows,
|
|
17
|
+
make_schema,
|
|
18
|
+
record_opened_connections,
|
|
19
|
+
)
|
|
14
20
|
from vgi.client import Client
|
|
15
21
|
from vgi.client.client import ClientError
|
|
16
22
|
|
|
@@ -146,9 +152,12 @@ class TestEdgeCases:
|
|
|
146
152
|
class TestMultiWorkerEdgeCases:
|
|
147
153
|
"""Tests for edge cases with multiple workers.
|
|
148
154
|
|
|
149
|
-
These tests
|
|
155
|
+
These tests exposed timeout/hang bugs when:
|
|
150
156
|
- Processing parquet with one batch of zero rows
|
|
151
|
-
- Additional workers spawned but
|
|
157
|
+
- Additional workers were spawned but received no batches
|
|
158
|
+
|
|
159
|
+
The client now opens a secondary connection only when a batch is dealt to
|
|
160
|
+
it, so no connection sits idle. The tests check that too.
|
|
152
161
|
"""
|
|
153
162
|
|
|
154
163
|
def test_zero_row_batch_single_worker(self, fixture_worker: str) -> None:
|
|
@@ -174,8 +183,9 @@ class TestMultiWorkerEdgeCases:
|
|
|
174
183
|
schema = make_schema([pa.field("id", pa.int64()), pa.field("value", pa.int64())])
|
|
175
184
|
zero_row_batch = pa.RecordBatch.from_pydict({"id": [], "value": []}, schema=schema)
|
|
176
185
|
|
|
177
|
-
#
|
|
186
|
+
# Allow 4 workers even though there's only one batch with zero rows
|
|
178
187
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
188
|
+
opened = record_opened_connections(client)
|
|
179
189
|
output_batches = list(
|
|
180
190
|
client.table_in_out_function(
|
|
181
191
|
function_name="echo",
|
|
@@ -184,17 +194,19 @@ class TestMultiWorkerEdgeCases:
|
|
|
184
194
|
)
|
|
185
195
|
)
|
|
186
196
|
|
|
187
|
-
# Should complete without hanging
|
|
197
|
+
# Should complete without hanging, on the primary alone
|
|
188
198
|
assert len(output_batches) >= 1
|
|
189
199
|
assert_total_rows(output_batches, 0)
|
|
200
|
+
assert opened == []
|
|
190
201
|
|
|
191
202
|
def test_single_batch_multiple_workers(self, fixture_worker: str) -> None:
|
|
192
203
|
"""Single normal batch with max_workers=4 should complete without hanging."""
|
|
193
204
|
schema = make_schema([pa.field("id", pa.int64()), pa.field("value", pa.int64())])
|
|
194
205
|
single_batch = pa.RecordBatch.from_pydict({"id": [1, 2, 3], "value": [10, 20, 30]}, schema=schema)
|
|
195
206
|
|
|
196
|
-
#
|
|
207
|
+
# Allow 4 workers even though there's only 1 batch
|
|
197
208
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
209
|
+
opened = record_opened_connections(client)
|
|
198
210
|
output_batches = list(
|
|
199
211
|
client.table_in_out_function(
|
|
200
212
|
function_name="echo",
|
|
@@ -205,6 +217,7 @@ class TestMultiWorkerEdgeCases:
|
|
|
205
217
|
|
|
206
218
|
# Should complete without hanging and return correct data
|
|
207
219
|
assert_total_rows(output_batches, 3)
|
|
220
|
+
assert opened == []
|
|
208
221
|
|
|
209
222
|
def test_fewer_batches_than_workers(self, fixture_worker: str) -> None:
|
|
210
223
|
"""2 batches with max_workers=4 should complete without hanging."""
|
|
@@ -212,8 +225,9 @@ class TestMultiWorkerEdgeCases:
|
|
|
212
225
|
batch1 = pa.RecordBatch.from_pydict({"id": [1, 2], "value": [10, 20]}, schema=schema)
|
|
213
226
|
batch2 = pa.RecordBatch.from_pydict({"id": [3, 4, 5], "value": [30, 40, 50]}, schema=schema)
|
|
214
227
|
|
|
215
|
-
#
|
|
228
|
+
# Allow 4 workers even though there are only 2 batches
|
|
216
229
|
with Client(fixture_worker, worker_limit=4) as client:
|
|
230
|
+
opened = record_opened_connections(client)
|
|
217
231
|
output_batches = list(
|
|
218
232
|
client.table_in_out_function(
|
|
219
233
|
function_name="echo",
|
|
@@ -224,6 +238,8 @@ class TestMultiWorkerEdgeCases:
|
|
|
224
238
|
|
|
225
239
|
# Should complete without hanging and return correct data (5 rows total)
|
|
226
240
|
assert_total_rows(output_batches, 5)
|
|
241
|
+
# The second batch opened the one secondary connection it needed.
|
|
242
|
+
assert opened == [1]
|
|
227
243
|
|
|
228
244
|
|
|
229
245
|
class TestWorkerStderrCapture:
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Copyright 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Dynamic-filter state across HTTP turns: bounded replay, exact reconstruction.
|
|
4
|
+
|
|
5
|
+
An HTTP producer rebuilds its filters from the call and cursor tokens on every
|
|
6
|
+
turn. The cursor used to carry every tick's delta and replay all of them, each
|
|
7
|
+
re-validated through the embedded DuckDB evaluator, so turn ``k`` did ``k``
|
|
8
|
+
parses: a 100-batch Top-N scan spent ~20 s there. These tests drive the real
|
|
9
|
+
``dynamic_filter_echo`` fixture through the real HTTP stack, playing the client
|
|
10
|
+
(the requests are hand-built; every response is the worker's), and pin both
|
|
11
|
+
halves of the fix: a turn's parse work stays constant as ticks accumulate, and
|
|
12
|
+
the state rebuilt from the compacted history is the state the worker had.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import base64
|
|
18
|
+
import contextlib
|
|
19
|
+
import json
|
|
20
|
+
from collections.abc import Iterator
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
import pyarrow as pa
|
|
24
|
+
import pytest
|
|
25
|
+
from vgi_rpc.rpc import RpcServer
|
|
26
|
+
|
|
27
|
+
from vgi import filter_v2
|
|
28
|
+
from vgi._test_fixtures.worker import ExampleWorker
|
|
29
|
+
from vgi.arguments import Arguments
|
|
30
|
+
from vgi.invocation import FunctionType
|
|
31
|
+
from vgi.protocol import BindRequest, InitRequest, VgiProtocol
|
|
32
|
+
|
|
33
|
+
_KEY = b"\x5a" * 32
|
|
34
|
+
_METADATA: dict[bytes | str, bytes | str] = {
|
|
35
|
+
b"vgi_filter_encoding": b"vgi.filters.v2",
|
|
36
|
+
b"vgi_filter_version": b"2",
|
|
37
|
+
b"vgi_evaluation_context": b"vgi.none.v1",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _document_batch(document: dict[str, object], *values: int) -> pa.RecordBatch:
|
|
42
|
+
fields: list[pa.Field[Any]] = [pa.field("filter_spec", pa.string(), nullable=False)]
|
|
43
|
+
arrays: list[pa.Array[Any]] = [pa.array([json.dumps(document, separators=(",", ":"))])]
|
|
44
|
+
for index, value in enumerate(values):
|
|
45
|
+
fields.append(pa.field(f"value_{index}", pa.int64()))
|
|
46
|
+
arrays.append(pa.array([value], type=pa.int64()))
|
|
47
|
+
return pa.RecordBatch.from_arrays(arrays, schema=pa.schema(fields, metadata=_METADATA))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _empty_snapshot() -> pa.RecordBatch:
|
|
51
|
+
return _document_batch(
|
|
52
|
+
{"encoding": "vgi.filters.v2", "semantics": "vgi.duckdb.standard.v1", "kind": "snapshot", "predicates": []}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _upsert(predicate_id: str, revision: int, op: str, value_ref: int) -> dict[str, object]:
|
|
57
|
+
return {
|
|
58
|
+
"operation": "upsert",
|
|
59
|
+
"id": predicate_id,
|
|
60
|
+
"revision": revision,
|
|
61
|
+
"mode": "advisory",
|
|
62
|
+
"source": "top_n",
|
|
63
|
+
"expression": {
|
|
64
|
+
"node": "comparison",
|
|
65
|
+
"op": op,
|
|
66
|
+
"left": {"node": "column_ref", "column_index": 0, "column_name": "n"},
|
|
67
|
+
"right": {"node": "literal", "value_ref": value_ref},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _remove(predicate_id: str, revision: int) -> dict[str, object]:
|
|
73
|
+
return {"operation": "remove", "id": predicate_id, "revision": revision}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _tick(updates: list[dict[str, object]], *values: int) -> pa.KeyValueMetadata:
|
|
77
|
+
"""Tick metadata carrying one delta, framed the way the C++ client frames it."""
|
|
78
|
+
batch = _document_batch(
|
|
79
|
+
{"encoding": "vgi.filters.v2", "semantics": "vgi.duckdb.standard.v1", "kind": "delta", "updates": updates},
|
|
80
|
+
*values,
|
|
81
|
+
)
|
|
82
|
+
sink = pa.BufferOutputStream()
|
|
83
|
+
with pa.ipc.new_stream(sink, batch.schema) as writer:
|
|
84
|
+
writer.write_batch(batch)
|
|
85
|
+
return pa.KeyValueMetadata({b"vgi_pushdown_filters": base64.b64encode(sink.getvalue().to_pybytes())})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@contextlib.contextmanager
|
|
89
|
+
def _echo_stream(count: int, batch_size: int) -> Iterator[Any]:
|
|
90
|
+
"""Open a ``dynamic_filter_echo`` scan over the in-process HTTP stack."""
|
|
91
|
+
from vgi_rpc.http import http_connect, make_sync_client
|
|
92
|
+
|
|
93
|
+
worker = ExampleWorker(quiet=True)
|
|
94
|
+
worker._signing_key = _KEY
|
|
95
|
+
client = make_sync_client(RpcServer(VgiProtocol, worker, enable_describe=False), token_key=_KEY)
|
|
96
|
+
bind = BindRequest(
|
|
97
|
+
function_name="dynamic_filter_echo",
|
|
98
|
+
arguments=Arguments(positional=(pa.scalar(count),), named={"batch_size": pa.scalar(batch_size)}),
|
|
99
|
+
function_type=FunctionType.TABLE,
|
|
100
|
+
input_schema=None,
|
|
101
|
+
)
|
|
102
|
+
with http_connect(VgiProtocol, client=client, compression_level=None) as proxy: # type: ignore[type-abstract]
|
|
103
|
+
resp = proxy.bind(request=bind)
|
|
104
|
+
stream = proxy.init(
|
|
105
|
+
request=InitRequest(
|
|
106
|
+
bind_call=bind,
|
|
107
|
+
output_schema=resp.output_schema,
|
|
108
|
+
bind_opaque_data=resp.opaque_data,
|
|
109
|
+
pushdown_filters=_empty_snapshot(),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
try:
|
|
113
|
+
yield stream
|
|
114
|
+
finally:
|
|
115
|
+
stream.close()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@pytest.fixture
|
|
119
|
+
def parse_count(monkeypatch: pytest.MonkeyPatch) -> list[int]:
|
|
120
|
+
"""Count delta documents the worker parses (a spy: it records, never answers)."""
|
|
121
|
+
calls = [0]
|
|
122
|
+
original = filter_v2._Parser.parse_delta
|
|
123
|
+
|
|
124
|
+
def counting(self: Any) -> Any:
|
|
125
|
+
calls[0] += 1
|
|
126
|
+
return original(self)
|
|
127
|
+
|
|
128
|
+
monkeypatch.setattr(filter_v2._Parser, "parse_delta", counting)
|
|
129
|
+
return calls
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_a_turn_parses_a_bounded_number_of_deltas(parse_count: list[int]) -> None:
|
|
133
|
+
"""A tightening Top-N bound sends a delta every tick; no turn replays them all.
|
|
134
|
+
|
|
135
|
+
Rows descend from 999 in batches of 10 and each tick narrows ``n < bound``,
|
|
136
|
+
so every tick's delta changes the live predicate -- the case the C++ client
|
|
137
|
+
cannot skip. Before compaction turn ``k`` parsed ``k`` deltas.
|
|
138
|
+
"""
|
|
139
|
+
ticks = 60
|
|
140
|
+
with _echo_stream(count=1000, batch_size=10) as stream:
|
|
141
|
+
first = stream.tick() # the init turn's batch; metadata rides continuations only
|
|
142
|
+
assert first.batch.num_rows == 10
|
|
143
|
+
per_turn: list[int] = []
|
|
144
|
+
for revision in range(1, ticks + 1):
|
|
145
|
+
bound = 1000 - 10 * revision # rows of this tick are [bound - 10, bound - 1]
|
|
146
|
+
before = parse_count[0]
|
|
147
|
+
batch = stream.tick(_tick([_upsert("top_n:0", revision, "lt", 0)], bound)).batch
|
|
148
|
+
per_turn.append(parse_count[0] - before)
|
|
149
|
+
assert batch.column("n").to_pylist() == list(range(bound - 1, bound - 11, -1))
|
|
150
|
+
assert batch.column("pushed_filters")[0].as_py().count("ConstantFilter(n < ") == 1
|
|
151
|
+
assert f"ConstantFilter(n < {bound})" in batch.column("pushed_filters")[0].as_py()
|
|
152
|
+
|
|
153
|
+
# One parse for the new delta, one to replay the single delta the cursor keeps.
|
|
154
|
+
assert max(per_turn) <= 2, per_turn
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_a_resent_revision_is_stale_and_adds_no_replay(parse_count: list[int]) -> None:
|
|
158
|
+
"""Resending a revision with another value is a stale no-op: nothing applies, nothing accumulates."""
|
|
159
|
+
with _echo_stream(count=1000, batch_size=10) as stream:
|
|
160
|
+
stream.tick()
|
|
161
|
+
stream.tick(_tick([_upsert("top_n:0", 1, "lt", 0)], 100000))
|
|
162
|
+
before = parse_count[0]
|
|
163
|
+
for _ in range(20):
|
|
164
|
+
batch = stream.tick(_tick([_upsert("top_n:0", 1, "lt", 0)], 5)).batch
|
|
165
|
+
assert batch.num_rows == 10 # n < 5 would have emptied it
|
|
166
|
+
assert "ConstantFilter(n < 100000)" in batch.column("pushed_filters")[0].as_py()
|
|
167
|
+
assert parse_count[0] - before <= 2 * 20
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def test_rebuilt_state_keeps_the_order_the_worker_had() -> None:
|
|
171
|
+
"""A removed-then-re-added predicate keeps its position across a turn boundary.
|
|
172
|
+
|
|
173
|
+
Deltas: {a:1, b:1} -> [a, b]; {remove a:2, b:1} -> [b]; {a:3, b:1} -> [b, a].
|
|
174
|
+
The compacted history is the deltas that first carried a:3 and b:1 -- the
|
|
175
|
+
third and the first -- and replaying those alone yields [a, b]. The turn
|
|
176
|
+
after the third delta has to show the order the third turn itself had.
|
|
177
|
+
"""
|
|
178
|
+
with _echo_stream(count=1000, batch_size=10) as stream:
|
|
179
|
+
stream.tick()
|
|
180
|
+
stream.tick(_tick([_upsert("top_n:0", 1, "lt", 0), _upsert("top_n:1", 1, "gt", 1)], 100000, 5))
|
|
181
|
+
stream.tick(_tick([_remove("top_n:0", 2), _upsert("top_n:1", 1, "gt", 0)], 5))
|
|
182
|
+
applied = stream.tick(_tick([_upsert("top_n:0", 3, "lt", 0), _upsert("top_n:1", 1, "gt", 1)], 99999, 5)).batch
|
|
183
|
+
rebuilt = stream.tick().batch # no delta: filters come purely from the tokens
|
|
184
|
+
assert applied.num_rows == rebuilt.num_rows == 10
|
|
185
|
+
live = applied.column("pushed_filters")[0].as_py()
|
|
186
|
+
assert live.index("ConstantFilter(n > 5)") < live.index("ConstantFilter(n < 99999)"), live
|
|
187
|
+
assert rebuilt.column("pushed_filters")[0].as_py() == live
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def test_a_tombstone_survives_compaction() -> None:
|
|
191
|
+
"""A removal stays in force after its upsert is compacted away: a stale upsert cannot resurrect it."""
|
|
192
|
+
with _echo_stream(count=1000, batch_size=10) as stream:
|
|
193
|
+
stream.tick()
|
|
194
|
+
stream.tick(_tick([_upsert("top_n:0", 1, "lt", 0)], 100000))
|
|
195
|
+
stream.tick(_tick([_remove("top_n:0", 2)]))
|
|
196
|
+
stream.tick() # a turn rebuilt from the compacted tokens
|
|
197
|
+
stale = stream.tick(_tick([_upsert("top_n:0", 1, "lt", 0)], 5)).batch
|
|
198
|
+
assert stale.column("pushed_filters")[0].as_py() == "(none)"
|
|
199
|
+
assert stale.num_rows == 10
|
|
@@ -1003,7 +1003,10 @@ class FilterEchoPartitionedFunction(TableFunctionGenerator[_FilterEchoPartitione
|
|
|
1003
1003
|
end_idx = min(start_idx + chunk, params.args.count)
|
|
1004
1004
|
work_items.append(struct.pack(">QQ", start_idx, end_idx))
|
|
1005
1005
|
params.storage.queue_push(work_items)
|
|
1006
|
-
|
|
1006
|
+
# No more readers than work items. Left at the default (unbounded) the
|
|
1007
|
+
# client opens one stream per DuckDB thread -- 48 on a 48-core host for at
|
|
1008
|
+
# most MAX_PARTITIONS items, every extra one an init plus an empty drain.
|
|
1009
|
+
return GlobalInitResponse(max_workers=max(1, len(work_items)))
|
|
1007
1010
|
|
|
1008
1011
|
@classmethod
|
|
1009
1012
|
def initial_state(cls, params: ProcessParams[_FilterEchoPartitionedArgs]) -> _FilterEchoPartitionedState:
|
|
@@ -516,7 +516,10 @@ class PartitionedSequenceFunction(
|
|
|
516
516
|
|
|
517
517
|
# Always enqueue (even if empty) to register the invocation
|
|
518
518
|
params.storage.queue_push(work_items)
|
|
519
|
-
|
|
519
|
+
# No more readers than work items. Left at the default (unbounded) the
|
|
520
|
+
# client opens one stream per DuckDB thread -- 48 on a 48-core host for at
|
|
521
|
+
# most MAX_PARTITIONS items, every extra one an init plus an empty drain.
|
|
522
|
+
return GlobalInitResponse(max_workers=max(1, len(work_items)))
|
|
520
523
|
|
|
521
524
|
@classmethod
|
|
522
525
|
def initial_state(cls, params: ProcessParams[PartitionedSequenceFunctionArguments]) -> PartitionedSequenceState:
|
|
@@ -18,8 +18,8 @@ ways it goes right:
|
|
|
18
18
|
shape that silently truncates a scan if a reader treats an empty split as EOS.
|
|
19
19
|
* ``split_skewed(n)`` — one split ~100x the others, so greedy claiming can be
|
|
20
20
|
told apart from static assignment.
|
|
21
|
-
* ``split_many(n, splits
|
|
22
|
-
forces sequential re-init on a reused connection.
|
|
21
|
+
* ``split_many(n, splits)`` — far more splits than reader threads (pass them), which
|
|
22
|
+
forces sequential re-init on a reused connection. ``splits <= 0`` means 1000.
|
|
23
23
|
* ``split_echo_filters(n)`` — reports the pushdown it saw *per split*, so a
|
|
24
24
|
pushdown regression is visible without inferring it from row counts.
|
|
25
25
|
"""
|
|
@@ -35,6 +35,7 @@ from vgi_rpc.rpc import OutputCollector
|
|
|
35
35
|
|
|
36
36
|
from vgi.arguments import Arg
|
|
37
37
|
from vgi.cache_control import CacheControl
|
|
38
|
+
from vgi.filter_v2 import replaying_accepted_state
|
|
38
39
|
from vgi.metadata import FunctionExample, PartitionKind
|
|
39
40
|
from vgi.protocol import PlanResponse, ScanSplit, VgiOutputCollector
|
|
40
41
|
from vgi.schema_utils import partition_field, schema
|
|
@@ -321,7 +322,9 @@ class SplitManyFunction(_SplitBase):
|
|
|
321
322
|
|
|
322
323
|
@classmethod
|
|
323
324
|
def _split_count(cls, params: BindParams[SplitSequenceArgs]) -> int:
|
|
324
|
-
|
|
325
|
+
# Honour the caller's count, as every other SDK's twin does; a floor here
|
|
326
|
+
# made a test that asks for 64 splits pay for 1000 HTTP round trips.
|
|
327
|
+
return params.args.splits if params.args.splits > 0 else 1000
|
|
325
328
|
|
|
326
329
|
|
|
327
330
|
@dataclass(frozen=True)
|
|
@@ -1055,11 +1058,15 @@ class SplitDynamicFilterFunction(TableFunctionGenerator[SplitSequenceArgs, Split
|
|
|
1055
1058
|
init = params.init_call
|
|
1056
1059
|
merged = None
|
|
1057
1060
|
if init is not None and init.pushdown_filters is not None:
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1061
|
+
# The framework validated these at init; re-binding every
|
|
1062
|
+
# predicate through the evaluator on every tick is what made
|
|
1063
|
+
# this fixture cost ~10 ms a batch.
|
|
1064
|
+
with replaying_accepted_state():
|
|
1065
|
+
merged = cls.pushdown_filters(
|
|
1066
|
+
init.pushdown_filters,
|
|
1067
|
+
join_keys=init.join_keys,
|
|
1068
|
+
output_schema=init.output_schema,
|
|
1069
|
+
)
|
|
1063
1070
|
if merged is None:
|
|
1064
1071
|
merged = params.current_pushdown_filters
|
|
1065
1072
|
filter_str = _render_filters_canonical(merged)
|
|
@@ -518,7 +518,14 @@ class AggregateFunction[TState: StreamStateCodec](vgi.function.Function):
|
|
|
518
518
|
"""Compute the aggregate value for one output row.
|
|
519
519
|
|
|
520
520
|
Args:
|
|
521
|
-
rid:
|
|
521
|
+
rid: Identifies the output row being filled; do not use it to
|
|
522
|
+
index the partition. Over the per-row ``aggregate_window``
|
|
523
|
+
RPC it is DuckDB's own ``rid``: the row's index within the
|
|
524
|
+
current output chunk (``0 <= rid < 2048``), not its row in
|
|
525
|
+
the partition. When the default :meth:`window_batch` calls
|
|
526
|
+
this, it passes the partition row index instead. Either way
|
|
527
|
+
``subframes`` are partition-relative: use them to find the
|
|
528
|
+
rows to aggregate.
|
|
522
529
|
subframes: Frame ranges ``[(begin, end), ...]`` — 1 for the default
|
|
523
530
|
frame, 3 when ``EXCLUDE`` produces multiple subframes.
|
|
524
531
|
partition: The cached partition data.
|