vgi-python 0.8.0__py3-none-any.whl
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/__init__.py +152 -0
- vgi/_duckdb.py +62 -0
- vgi/_storage_profile.py +132 -0
- vgi/_test_fixtures/__init__.py +20 -0
- vgi/_test_fixtures/accumulate/__init__.py +19 -0
- vgi/_test_fixtures/accumulate/worker.py +762 -0
- vgi/_test_fixtures/aggregate/__init__.py +62 -0
- vgi/_test_fixtures/aggregate/_common.py +21 -0
- vgi/_test_fixtures/aggregate/basic.py +232 -0
- vgi/_test_fixtures/aggregate/dynamic.py +409 -0
- vgi/_test_fixtures/aggregate/generic.py +86 -0
- vgi/_test_fixtures/aggregate/listagg.py +71 -0
- vgi/_test_fixtures/aggregate/percentile.py +107 -0
- vgi/_test_fixtures/aggregate/streaming.py +192 -0
- vgi/_test_fixtures/aggregate/varargs.py +75 -0
- vgi/_test_fixtures/aggregate/window.py +380 -0
- vgi/_test_fixtures/attach_options.py +308 -0
- vgi/_test_fixtures/bad_protocol.py +62 -0
- vgi/_test_fixtures/cancellable.py +336 -0
- vgi/_test_fixtures/catalog.py +813 -0
- vgi/_test_fixtures/http_server.py +394 -0
- vgi/_test_fixtures/nest_tensor.py +614 -0
- vgi/_test_fixtures/orchard_catalog.py +47 -0
- vgi/_test_fixtures/projection_repro/__init__.py +6 -0
- vgi/_test_fixtures/projection_repro/worker.py +454 -0
- vgi/_test_fixtures/scalar/__init__.py +116 -0
- vgi/_test_fixtures/scalar/_common.py +69 -0
- vgi/_test_fixtures/scalar/arithmetic.py +321 -0
- vgi/_test_fixtures/scalar/binary.py +120 -0
- vgi/_test_fixtures/scalar/formatting.py +176 -0
- vgi/_test_fixtures/scalar/geo.py +300 -0
- vgi/_test_fixtures/scalar/null_handling.py +107 -0
- vgi/_test_fixtures/scalar/random_demo.py +171 -0
- vgi/_test_fixtures/scalar/settings_secrets.py +102 -0
- vgi/_test_fixtures/scalar/type_info.py +219 -0
- vgi/_test_fixtures/schema_reconcile/__init__.py +29 -0
- vgi/_test_fixtures/schema_reconcile/worker.py +653 -0
- vgi/_test_fixtures/simple_writable.py +793 -0
- vgi/_test_fixtures/table/__init__.py +221 -0
- vgi/_test_fixtures/table/_common.py +162 -0
- vgi/_test_fixtures/table/batch_index.py +283 -0
- vgi/_test_fixtures/table/batch_index_broken.py +200 -0
- vgi/_test_fixtures/table/catalog_scans.py +162 -0
- vgi/_test_fixtures/table/filters.py +1005 -0
- vgi/_test_fixtures/table/late_materialization.py +249 -0
- vgi/_test_fixtures/table/make_series.py +273 -0
- vgi/_test_fixtures/table/misc.py +499 -0
- vgi/_test_fixtures/table/order_modes.py +164 -0
- vgi/_test_fixtures/table/pairs.py +437 -0
- vgi/_test_fixtures/table/partition_columns.py +472 -0
- vgi/_test_fixtures/table/partition_columns_broken.py +304 -0
- vgi/_test_fixtures/table/profiling_example.py +195 -0
- vgi/_test_fixtures/table/required_filters.py +234 -0
- vgi/_test_fixtures/table/sequence.py +710 -0
- vgi/_test_fixtures/table/settings.py +426 -0
- vgi/_test_fixtures/table/transaction_storage.py +162 -0
- vgi/_test_fixtures/table/tt_pushdown.py +191 -0
- vgi/_test_fixtures/table/versioned.py +230 -0
- vgi/_test_fixtures/table_in_out.py +1392 -0
- vgi/_test_fixtures/versioned.py +155 -0
- vgi/_test_fixtures/versioned_tables.py +595 -0
- vgi/_test_fixtures/worker.py +1631 -0
- vgi/_test_fixtures/writable/__init__.py +8 -0
- vgi/_test_fixtures/writable/generic.py +236 -0
- vgi/_test_fixtures/writable/table.py +149 -0
- vgi/_test_fixtures/writable/worker.py +1148 -0
- vgi/aggregate_function.py +607 -0
- vgi/argument_spec.py +472 -0
- vgi/arguments.py +1747 -0
- vgi/auth.py +55 -0
- vgi/catalog/__init__.py +88 -0
- vgi/catalog/attach_option.py +206 -0
- vgi/catalog/catalog_interface.py +2767 -0
- vgi/catalog/descriptors.py +870 -0
- vgi/catalog/duckdb_statistics.py +377 -0
- vgi/catalog/secret_type.py +96 -0
- vgi/catalog/setting.py +253 -0
- vgi/catalog/storage.py +372 -0
- vgi/client/__init__.py +67 -0
- vgi/client/catalog_mixin.py +1251 -0
- vgi/client/cli.py +582 -0
- vgi/client/cli_catalog.py +182 -0
- vgi/client/cli_schema.py +270 -0
- vgi/client/cli_table.py +907 -0
- vgi/client/cli_transaction.py +97 -0
- vgi/client/cli_utils.py +441 -0
- vgi/client/cli_view.py +303 -0
- vgi/client/client.py +2183 -0
- vgi/exceptions.py +205 -0
- vgi/function.py +245 -0
- vgi/function_storage.py +1636 -0
- vgi/function_storage_azure_sql.py +922 -0
- vgi/function_storage_cf_do.py +740 -0
- vgi/http/__init__.py +25 -0
- vgi/http/demo_storage.py +212 -0
- vgi/http/worker_page.py +1252 -0
- vgi/invocation.py +154 -0
- vgi/logging_config.py +93 -0
- vgi/meta_worker.py +661 -0
- vgi/metadata.py +1403 -0
- vgi/otel.py +406 -0
- vgi/protocol.py +2418 -0
- vgi/protocol_version.txt +1 -0
- vgi/py.typed +0 -0
- vgi/scalar_function.py +1211 -0
- vgi/schema_utils.py +234 -0
- vgi/secret_protocol.py +124 -0
- vgi/secret_service.py +238 -0
- vgi/serve.py +769 -0
- vgi/table_buffering_function.py +443 -0
- vgi/table_filter_pushdown.py +1528 -0
- vgi/table_function.py +1130 -0
- vgi/table_in_out_function.py +383 -0
- vgi/transactor/__init__.py +24 -0
- vgi/transactor/_duckdb_compat.py +27 -0
- vgi/transactor/client.py +137 -0
- vgi/transactor/protocol.py +149 -0
- vgi/transactor/server.py +740 -0
- vgi/worker.py +4761 -0
- vgi_python-0.8.0.dist-info/METADATA +735 -0
- vgi_python-0.8.0.dist-info/RECORD +124 -0
- vgi_python-0.8.0.dist-info/WHEEL +4 -0
- vgi_python-0.8.0.dist-info/entry_points.txt +5 -0
- vgi_python-0.8.0.dist-info/licenses/LICENSE +134 -0
vgi/__init__.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""VGI (Vector Gateway Interface) - Apache Arrow-based protocol for DuckDB extensions.
|
|
4
|
+
|
|
5
|
+
VGI provides a framework for connecting DuckDB to external programs via
|
|
6
|
+
streaming Arrow IPC. User-defined functions run in worker subprocesses
|
|
7
|
+
and communicate with the database through stdin/stdout.
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from vgi_rpc.log import Level, Message
|
|
12
|
+
|
|
13
|
+
from vgi.aggregate_function import (
|
|
14
|
+
AggregateBindParams,
|
|
15
|
+
AggregateFunction,
|
|
16
|
+
)
|
|
17
|
+
from vgi.argument_spec import (
|
|
18
|
+
VGI_ARG_KEY,
|
|
19
|
+
VGI_ARG_NAMED,
|
|
20
|
+
VGI_CONST_KEY,
|
|
21
|
+
VGI_CONST_TRUE,
|
|
22
|
+
VGI_TYPE_ANY,
|
|
23
|
+
VGI_TYPE_KEY,
|
|
24
|
+
VGI_TYPE_TABLE,
|
|
25
|
+
VGI_VARARGS_KEY,
|
|
26
|
+
VGI_VARARGS_TRUE,
|
|
27
|
+
ArgumentSpec,
|
|
28
|
+
argument_specs_to_schema,
|
|
29
|
+
schema_to_argument_specs,
|
|
30
|
+
)
|
|
31
|
+
from vgi.arguments import (
|
|
32
|
+
AnyArrow,
|
|
33
|
+
AnyArrowValue,
|
|
34
|
+
Arg,
|
|
35
|
+
Arguments,
|
|
36
|
+
ArgumentValidationError,
|
|
37
|
+
Auth,
|
|
38
|
+
ConstParam,
|
|
39
|
+
Param,
|
|
40
|
+
Returns,
|
|
41
|
+
TableInput,
|
|
42
|
+
)
|
|
43
|
+
from vgi.auth import AuthContext, CallContext
|
|
44
|
+
from vgi.metadata import (
|
|
45
|
+
CatalogFunctionType,
|
|
46
|
+
FunctionExample,
|
|
47
|
+
FunctionStability,
|
|
48
|
+
OrderPreservation,
|
|
49
|
+
ParameterInfo,
|
|
50
|
+
ResolvedMetadata,
|
|
51
|
+
TableInputValidationError,
|
|
52
|
+
functions_to_arrow,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Re-export commonly used protocol types
|
|
56
|
+
from vgi.protocol import (
|
|
57
|
+
BindRequest,
|
|
58
|
+
InitRequest,
|
|
59
|
+
VgiOutputCollector,
|
|
60
|
+
)
|
|
61
|
+
from vgi.scalar_function import (
|
|
62
|
+
RowCountMismatchError,
|
|
63
|
+
ScalarFunction,
|
|
64
|
+
ScalarFunctionGenerator,
|
|
65
|
+
TypeMismatchError,
|
|
66
|
+
)
|
|
67
|
+
from vgi.schema_utils import schema, schema_like
|
|
68
|
+
from vgi.table_filter_pushdown import (
|
|
69
|
+
ColumnBounds,
|
|
70
|
+
ColumnRefNode,
|
|
71
|
+
ComparisonNode,
|
|
72
|
+
ConjunctionNode,
|
|
73
|
+
ConstantNode,
|
|
74
|
+
ExpressionFilter,
|
|
75
|
+
ExpressionNode,
|
|
76
|
+
FilterDeserializationError,
|
|
77
|
+
FilterError,
|
|
78
|
+
FilterVersionError,
|
|
79
|
+
FunctionNode,
|
|
80
|
+
PushdownFilters,
|
|
81
|
+
deserialize_filters,
|
|
82
|
+
)
|
|
83
|
+
from vgi.table_in_out_function import (
|
|
84
|
+
TableInOutFunction,
|
|
85
|
+
TableInOutGenerator,
|
|
86
|
+
)
|
|
87
|
+
from vgi.worker import Worker
|
|
88
|
+
|
|
89
|
+
__all__ = [
|
|
90
|
+
"AggregateBindParams",
|
|
91
|
+
"AggregateFunction",
|
|
92
|
+
"AnyArrow",
|
|
93
|
+
"AnyArrowValue",
|
|
94
|
+
"Arg",
|
|
95
|
+
"ArgumentSpec",
|
|
96
|
+
"ArgumentValidationError",
|
|
97
|
+
"Arguments",
|
|
98
|
+
"Auth",
|
|
99
|
+
"AuthContext",
|
|
100
|
+
"CallContext",
|
|
101
|
+
"argument_specs_to_schema",
|
|
102
|
+
"BindRequest",
|
|
103
|
+
"ColumnBounds",
|
|
104
|
+
"ColumnRefNode",
|
|
105
|
+
"ComparisonNode",
|
|
106
|
+
"ConjunctionNode",
|
|
107
|
+
"ConstantNode",
|
|
108
|
+
"ConstParam",
|
|
109
|
+
"deserialize_filters",
|
|
110
|
+
"ExpressionFilter",
|
|
111
|
+
"ExpressionNode",
|
|
112
|
+
"FilterDeserializationError",
|
|
113
|
+
"FilterError",
|
|
114
|
+
"FilterVersionError",
|
|
115
|
+
"Param",
|
|
116
|
+
"PushdownFilters",
|
|
117
|
+
"Returns",
|
|
118
|
+
# Metadata constants for parsing argument spec schemas
|
|
119
|
+
"VGI_ARG_KEY",
|
|
120
|
+
"VGI_ARG_NAMED",
|
|
121
|
+
"VGI_CONST_KEY",
|
|
122
|
+
"VGI_CONST_TRUE",
|
|
123
|
+
"VGI_TYPE_KEY",
|
|
124
|
+
"VGI_TYPE_TABLE",
|
|
125
|
+
"VGI_TYPE_ANY",
|
|
126
|
+
"VGI_VARARGS_KEY",
|
|
127
|
+
"VGI_VARARGS_TRUE",
|
|
128
|
+
"FunctionNode",
|
|
129
|
+
"FunctionExample",
|
|
130
|
+
"FunctionStability",
|
|
131
|
+
"CatalogFunctionType",
|
|
132
|
+
"InitRequest",
|
|
133
|
+
"Level",
|
|
134
|
+
"VgiOutputCollector",
|
|
135
|
+
"Message",
|
|
136
|
+
"OrderPreservation",
|
|
137
|
+
"ParameterInfo",
|
|
138
|
+
"ResolvedMetadata",
|
|
139
|
+
"RowCountMismatchError",
|
|
140
|
+
"ScalarFunction",
|
|
141
|
+
"ScalarFunctionGenerator",
|
|
142
|
+
"TableInOutFunction",
|
|
143
|
+
"TableInOutGenerator",
|
|
144
|
+
"TableInput",
|
|
145
|
+
"TableInputValidationError",
|
|
146
|
+
"TypeMismatchError",
|
|
147
|
+
"Worker",
|
|
148
|
+
"functions_to_arrow",
|
|
149
|
+
"schema",
|
|
150
|
+
"schema_like",
|
|
151
|
+
"schema_to_argument_specs",
|
|
152
|
+
]
|
vgi/_duckdb.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""DuckDB-compatible engine resolution: prefer ``haybarn``, fall back to ``duckdb``.
|
|
4
|
+
|
|
5
|
+
vgi does not hard-depend on either engine. Code that needs an in-process
|
|
6
|
+
engine (expression-filter evaluation, the transactor, the statistics
|
|
7
|
+
fixtures) resolves it through this module, which prefers Query Farm's
|
|
8
|
+
``haybarn`` distribution when installed and falls back to stock ``duckdb``.
|
|
9
|
+
|
|
10
|
+
Install one via the extras: ``pip install vgi[haybarn]`` (preferred) or
|
|
11
|
+
``pip install vgi[duckdb]``.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import importlib
|
|
17
|
+
from types import ModuleType
|
|
18
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
import duckdb
|
|
22
|
+
|
|
23
|
+
#: Engine modules in preference order. Both expose the duckdb-python API
|
|
24
|
+
#: (``connect()``, ``DuckDBPyConnection``, ...).
|
|
25
|
+
_ENGINE_MODULES = ("haybarn", "duckdb")
|
|
26
|
+
|
|
27
|
+
_engine: ModuleType | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def engine_module() -> ModuleType:
|
|
31
|
+
"""Return the resolved engine module (``haybarn`` preferred, ``duckdb`` fallback).
|
|
32
|
+
|
|
33
|
+
The result is cached for the life of the process.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ImportError: if neither engine is installed.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
global _engine
|
|
40
|
+
if _engine is None:
|
|
41
|
+
for name in _ENGINE_MODULES:
|
|
42
|
+
try:
|
|
43
|
+
_engine = importlib.import_module(name)
|
|
44
|
+
break
|
|
45
|
+
except ImportError:
|
|
46
|
+
continue
|
|
47
|
+
else:
|
|
48
|
+
raise ImportError(
|
|
49
|
+
"No DuckDB-compatible engine is installed. Install 'haybarn' (preferred) "
|
|
50
|
+
"or 'duckdb', e.g. `pip install vgi[haybarn]` or `pip install vgi[duckdb]`."
|
|
51
|
+
)
|
|
52
|
+
return _engine
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def connect(*args: Any, **kwargs: Any) -> duckdb.DuckDBPyConnection:
|
|
56
|
+
"""Open a connection via the resolved engine's ``connect()``.
|
|
57
|
+
|
|
58
|
+
Typed as ``duckdb.DuckDBPyConnection`` for the type-checker; at runtime
|
|
59
|
+
the object is whichever engine module won resolution (haybarn's
|
|
60
|
+
connection class implements the same API).
|
|
61
|
+
"""
|
|
62
|
+
return cast("duckdb.DuckDBPyConnection", engine_module().connect(*args, **kwargs))
|
vgi/_storage_profile.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Backend-agnostic per-shard storage round-trip profiler (opt-in).
|
|
4
|
+
|
|
5
|
+
Enable with ``VGI_STORAGE_PROFILE=1``. Recording happens at the
|
|
6
|
+
``BoundStorage`` facade so it works against *any* backend — in-process sqlite
|
|
7
|
+
included — which lets the suite be profiled locally without a Cloudflare
|
|
8
|
+
deployment. Backends that already self-profile at their transport layer (the
|
|
9
|
+
cloudflare-do ``_post``, which sees per-page round-trips) set
|
|
10
|
+
``_profiles_at_transport = True`` so ``BoundStorage`` defers to them and the
|
|
11
|
+
two layers never double-count.
|
|
12
|
+
|
|
13
|
+
Stats are keyed by ``(shard_key, op)``. Each test run does one ATTACH with a
|
|
14
|
+
fresh random-nonce sealed envelope, so one ``shard_key`` == one test run — the
|
|
15
|
+
per-shard summary is effectively a per-test storage profile. A daemon flusher
|
|
16
|
+
logs a JSON summary periodically (and atexit) under ``vgi.storage.profile``.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import atexit
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
import threading
|
|
26
|
+
import time
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
_PROFILE_ON = os.environ.get("VGI_STORAGE_PROFILE") == "1"
|
|
30
|
+
_profile_logger = logging.getLogger("vgi.storage.profile")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _StorageProfiler:
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
self._lock = threading.Lock()
|
|
36
|
+
# (shard, op) -> [count, total_s, max_s, total_bytes, max_bytes]
|
|
37
|
+
# ``max_bytes`` is the largest single-call HTTP body (max of request
|
|
38
|
+
# and response direction) — the number that matters for provider
|
|
39
|
+
# request/response size limits.
|
|
40
|
+
self._stats: dict[tuple[str, str], list[float]] = {}
|
|
41
|
+
self._started = False
|
|
42
|
+
|
|
43
|
+
def record(self, shard: str, op: str, seconds: float, io_bytes: int) -> None:
|
|
44
|
+
with self._lock:
|
|
45
|
+
e = self._stats.setdefault((shard, op), [0.0, 0.0, 0.0, 0.0, 0.0])
|
|
46
|
+
e[0] += 1
|
|
47
|
+
e[1] += seconds
|
|
48
|
+
e[2] = max(e[2], seconds)
|
|
49
|
+
e[3] += io_bytes
|
|
50
|
+
e[4] = max(e[4], io_bytes)
|
|
51
|
+
|
|
52
|
+
def dump(self) -> None:
|
|
53
|
+
with self._lock:
|
|
54
|
+
by_shard: dict[str, dict[str, list[float]]] = {}
|
|
55
|
+
for (shard, op), e in self._stats.items():
|
|
56
|
+
by_shard.setdefault(shard, {})[op] = list(e)
|
|
57
|
+
for shard, ops in by_shard.items():
|
|
58
|
+
round_trips = int(sum(e[0] for e in ops.values()))
|
|
59
|
+
storage_s = sum(e[1] for e in ops.values())
|
|
60
|
+
op_summary = {
|
|
61
|
+
op: {
|
|
62
|
+
"n": int(e[0]),
|
|
63
|
+
"total_ms": round(e[1] * 1000, 1),
|
|
64
|
+
"avg_ms": round(e[1] / e[0] * 1000, 1) if e[0] else 0,
|
|
65
|
+
"max_ms": round(e[2] * 1000, 1),
|
|
66
|
+
"io_kb": round(e[3] / 1024, 1),
|
|
67
|
+
# largest single HTTP body (req or resp) — headroom vs
|
|
68
|
+
# provider size caps.
|
|
69
|
+
"max_call_kb": round(e[4] / 1024, 1),
|
|
70
|
+
}
|
|
71
|
+
for op, e in sorted(ops.items(), key=lambda kv: -kv[1][1])
|
|
72
|
+
}
|
|
73
|
+
_profile_logger.warning(
|
|
74
|
+
json.dumps(
|
|
75
|
+
{
|
|
76
|
+
"msg": "storage_profile",
|
|
77
|
+
"shard": shard,
|
|
78
|
+
"round_trips": round_trips,
|
|
79
|
+
"storage_s": round(storage_s, 2),
|
|
80
|
+
"ops": op_summary,
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def start(self, interval: float = 5.0) -> None:
|
|
86
|
+
if self._started:
|
|
87
|
+
return
|
|
88
|
+
self._started = True
|
|
89
|
+
|
|
90
|
+
def _loop() -> None:
|
|
91
|
+
while True:
|
|
92
|
+
time.sleep(interval)
|
|
93
|
+
self.dump()
|
|
94
|
+
|
|
95
|
+
threading.Thread(target=_loop, daemon=True, name="vgi-storage-profile").start()
|
|
96
|
+
atexit.register(self.dump)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
_profiler = _StorageProfiler()
|
|
100
|
+
if _PROFILE_ON:
|
|
101
|
+
_profiler.start()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _byte_size(obj: object) -> int:
|
|
105
|
+
"""Best-effort serialized size of a storage payload, without consuming generators.
|
|
106
|
+
|
|
107
|
+
bytes -> len; list/tuple -> summed size of elements (recursing one level so
|
|
108
|
+
``[(k, v), ...]`` and ``[item, ...]`` are both counted); everything else
|
|
109
|
+
(ints, None, namespaces, generators/iterators) -> 0.
|
|
110
|
+
"""
|
|
111
|
+
if isinstance(obj, (bytes, bytearray)):
|
|
112
|
+
return len(obj)
|
|
113
|
+
if isinstance(obj, (list, tuple)):
|
|
114
|
+
total = 0
|
|
115
|
+
for item in obj:
|
|
116
|
+
if isinstance(item, (bytes, bytearray)):
|
|
117
|
+
total += len(item)
|
|
118
|
+
elif isinstance(item, tuple):
|
|
119
|
+
total += sum(len(x) for x in item if isinstance(x, (bytes, bytearray)))
|
|
120
|
+
return total
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def io_call_bytes(args: tuple[Any, ...], kwargs: dict[str, Any], result: object) -> int:
|
|
125
|
+
"""Largest single-direction HTTP body for one BoundStorage call.
|
|
126
|
+
|
|
127
|
+
Writes carry the payload in the args (items / value / appended item);
|
|
128
|
+
reads carry it in the result. Provider request/response size caps apply
|
|
129
|
+
per direction, so the relevant figure is the max of the two.
|
|
130
|
+
"""
|
|
131
|
+
request = sum(_byte_size(a) for a in args) + sum(_byte_size(v) for v in kwargs.values())
|
|
132
|
+
return max(request, _byte_size(result))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Example implementations for VGI.
|
|
4
|
+
|
|
5
|
+
This package contains example functions and servers for testing and reference.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from vgi._test_fixtures.table_in_out import (
|
|
9
|
+
BufferInputFunction,
|
|
10
|
+
EchoFunction,
|
|
11
|
+
RepeatInputsFunction,
|
|
12
|
+
SumAllColumnsFunction,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"EchoFunction",
|
|
17
|
+
"BufferInputFunction",
|
|
18
|
+
"RepeatInputsFunction",
|
|
19
|
+
"SumAllColumnsFunction",
|
|
20
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
|
|
3
|
+
"""Accumulate fixture worker: persistent named row collections over BoundStorage.
|
|
4
|
+
|
|
5
|
+
Exposes ``accumulate`` / ``accumulate_read`` / ``accumulate_clear`` table
|
|
6
|
+
functions that persist rows through the framework's ``FunctionStorage``,
|
|
7
|
+
scoped per ATTACH session. The fixture is the end-to-end exercise of the
|
|
8
|
+
``BoundStorage`` interfaces: persistent attach-scoped K/V segments (including
|
|
9
|
+
ranged scans/deletes for TTL eviction), atomic counters, and execution-scoped
|
|
10
|
+
staging logs.
|
|
11
|
+
|
|
12
|
+
Hosted inside the consolidated ``vgi-fixture-worker`` alongside the other
|
|
13
|
+
reproducer catalogs; driven by ``test/sql/integration/accumulate/*.test`` in
|
|
14
|
+
``~/Development/vgi`` and mirrored by ``tests/conformance/test_accumulate.py``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from vgi._test_fixtures.accumulate.worker import AccumulateWorker
|
|
18
|
+
|
|
19
|
+
__all__ = ["AccumulateWorker"]
|