@query-farm/vgi 0.6.0
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.
- package/LICENSE +134 -0
- package/README.md +494 -0
- package/dist/__dbg.test-d.d.ts +1 -0
- package/dist/arguments/argument-spec.d.ts +51 -0
- package/dist/arguments/arguments.d.ts +13 -0
- package/dist/arrow/codec/branded.d.ts +83 -0
- package/dist/arrow/codec/registry.d.ts +29 -0
- package/dist/arrow/codec/repr.d.ts +23 -0
- package/dist/arrow/codec/type-descriptors.d.ts +109 -0
- package/dist/arrow/impl-arrowjs/build.d.ts +19 -0
- package/dist/arrow/impl-arrowjs/canonical.d.ts +15 -0
- package/dist/arrow/impl-arrowjs/empty.d.ts +7 -0
- package/dist/arrow/impl-arrowjs/filter.d.ts +11 -0
- package/dist/arrow/impl-arrowjs/index.d.ts +11 -0
- package/dist/arrow/impl-arrowjs/ipc.d.ts +24 -0
- package/dist/arrow/impl-arrowjs/iterate.d.ts +39 -0
- package/dist/arrow/impl-arrowjs/project.d.ts +10 -0
- package/dist/arrow/impl-arrowjs/schema.d.ts +62 -0
- package/dist/arrow/impl-arrowjs/statistics.d.ts +13 -0
- package/dist/arrow/impl-flechette/build.d.ts +14 -0
- package/dist/arrow/impl-flechette/canonical.d.ts +18 -0
- package/dist/arrow/impl-flechette/empty.d.ts +9 -0
- package/dist/arrow/impl-flechette/filter.d.ts +10 -0
- package/dist/arrow/impl-flechette/index.d.ts +11 -0
- package/dist/arrow/impl-flechette/ipc.d.ts +33 -0
- package/dist/arrow/impl-flechette/iterate.d.ts +29 -0
- package/dist/arrow/impl-flechette/message-meta.d.ts +10 -0
- package/dist/arrow/impl-flechette/normalize-type.d.ts +11 -0
- package/dist/arrow/impl-flechette/project.d.ts +3 -0
- package/dist/arrow/impl-flechette/schema.d.ts +47 -0
- package/dist/arrow/impl-flechette/statistics.d.ts +13 -0
- package/dist/arrow/index.d.ts +11 -0
- package/dist/arrow/predicates.d.ts +47 -0
- package/dist/arrow/repr.test-d.d.ts +52 -0
- package/dist/arrow/schema-types.d.ts +71 -0
- package/dist/arrow/types.d.ts +57 -0
- package/dist/catalog/attach-option.d.ts +33 -0
- package/dist/catalog/attach-options.d.ts +30 -0
- package/dist/catalog/composite.d.ts +33 -0
- package/dist/catalog/descriptors.d.ts +154 -0
- package/dist/catalog/interface.d.ts +239 -0
- package/dist/catalog/read-only.d.ts +33 -0
- package/dist/client/client.d.ts +189 -0
- package/dist/client/errors.d.ts +26 -0
- package/dist/client/helpers.d.ts +12 -0
- package/dist/client/protocol.d.ts +16 -0
- package/dist/client/types.d.ts +125 -0
- package/dist/client-entry.d.ts +11 -0
- package/dist/client-entry.js +48 -0
- package/dist/client-entry.js.map +9 -0
- package/dist/codec/asd.d.ts +25 -0
- package/dist/crypto.d.ts +36 -0
- package/dist/errors.d.ts +24 -0
- package/dist/filter-pushdown/collector.d.ts +32 -0
- package/dist/filter-pushdown/deserialize.d.ts +16 -0
- package/dist/filter-pushdown/evaluate.d.ts +51 -0
- package/dist/filter-pushdown/index.d.ts +4 -0
- package/dist/filter-pushdown/types.d.ts +84 -0
- package/dist/functions/aggregate.d.ts +129 -0
- package/dist/functions/copy-from.d.ts +81 -0
- package/dist/functions/copy-to.d.ts +121 -0
- package/dist/functions/registry.d.ts +16 -0
- package/dist/functions/scalar.d.ts +98 -0
- package/dist/functions/storage-cf-do.d.ts +47 -0
- package/dist/functions/storage.d.ts +86 -0
- package/dist/functions/table-buffering.d.ts +99 -0
- package/dist/functions/table-in-out.d.ts +64 -0
- package/dist/functions/table.d.ts +118 -0
- package/dist/functions/types.d.ts +157 -0
- package/dist/generated/vgi-client.d.ts +466 -0
- package/dist/generated/vgi-protocol-schemas.d.ts +115 -0
- package/dist/index.core.d.ts +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10888 -0
- package/dist/index.js.map +85 -0
- package/dist/metadata/resolve.d.ts +3 -0
- package/dist/metadata/serialize.d.ts +12 -0
- package/dist/metadata/types.d.ts +47 -0
- package/dist/protocol/dispatch.d.ts +23 -0
- package/dist/protocol/handlers/aggregate.d.ts +3 -0
- package/dist/protocol/handlers/catalog/admin.d.ts +3 -0
- package/dist/protocol/handlers/catalog/index.d.ts +3 -0
- package/dist/protocol/handlers/catalog/index_methods.d.ts +3 -0
- package/dist/protocol/handlers/catalog/macro.d.ts +3 -0
- package/dist/protocol/handlers/catalog/shared.d.ts +70 -0
- package/dist/protocol/handlers/catalog/table.d.ts +3 -0
- package/dist/protocol/handlers/catalog/view.d.ts +3 -0
- package/dist/protocol/handlers/function.d.ts +8 -0
- package/dist/protocol/handlers/shared.d.ts +31 -0
- package/dist/protocol/handlers/table-buffering.d.ts +3 -0
- package/dist/protocol/serialize.d.ts +4 -0
- package/dist/protocol/serializers/arguments.d.ts +3 -0
- package/dist/protocol/serializers/bind.d.ts +9 -0
- package/dist/protocol/serializers/cardinality.d.ts +7 -0
- package/dist/protocol/serializers/init.d.ts +9 -0
- package/dist/protocol/serializers/shared.d.ts +12 -0
- package/dist/protocol/state-serializer.d.ts +23 -0
- package/dist/protocol/types.d.ts +129 -0
- package/dist/secrets/helpers.d.ts +15 -0
- package/dist/types.d.ts +52 -0
- package/dist/util/arrow/index.d.ts +1 -0
- package/dist/util/bytes.d.ts +2 -0
- package/dist/util/statistics.d.ts +7 -0
- package/dist/worker-cf-entry.d.ts +30 -0
- package/dist/worker-cf-entry.js +8767 -0
- package/dist/worker-cf-entry.js.map +74 -0
- package/dist/worker.d.ts +36 -0
- package/package.json +96 -0
- package/src/arguments/argument-spec.ts +200 -0
- package/src/arguments/arguments.ts +81 -0
- package/src/arrow/codec/branded.ts +206 -0
- package/src/arrow/codec/registry.ts +674 -0
- package/src/arrow/codec/repr.ts +181 -0
- package/src/arrow/codec/type-descriptors.ts +137 -0
- package/src/arrow/impl-arrowjs/build.ts +88 -0
- package/src/arrow/impl-arrowjs/canonical.ts +509 -0
- package/src/arrow/impl-arrowjs/empty.ts +72 -0
- package/src/arrow/impl-arrowjs/filter.ts +53 -0
- package/src/arrow/impl-arrowjs/index.ts +54 -0
- package/src/arrow/impl-arrowjs/ipc.ts +67 -0
- package/src/arrow/impl-arrowjs/iterate.ts +159 -0
- package/src/arrow/impl-arrowjs/project.ts +73 -0
- package/src/arrow/impl-arrowjs/schema.ts +256 -0
- package/src/arrow/impl-arrowjs/statistics.ts +185 -0
- package/src/arrow/impl-flechette/build.ts +65 -0
- package/src/arrow/impl-flechette/canonical.ts +289 -0
- package/src/arrow/impl-flechette/empty.ts +20 -0
- package/src/arrow/impl-flechette/filter.ts +42 -0
- package/src/arrow/impl-flechette/index.ts +54 -0
- package/src/arrow/impl-flechette/ipc.ts +110 -0
- package/src/arrow/impl-flechette/iterate.ts +121 -0
- package/src/arrow/impl-flechette/message-meta.ts +175 -0
- package/src/arrow/impl-flechette/normalize-type.ts +99 -0
- package/src/arrow/impl-flechette/project.ts +64 -0
- package/src/arrow/impl-flechette/schema.ts +202 -0
- package/src/arrow/impl-flechette/statistics.ts +198 -0
- package/src/arrow/index.ts +129 -0
- package/src/arrow/predicates.ts +66 -0
- package/src/arrow/repr.test-d.ts +189 -0
- package/src/arrow/schema-types.ts +213 -0
- package/src/arrow/types.ts +74 -0
- package/src/catalog/attach-option.ts +107 -0
- package/src/catalog/attach-options.ts +79 -0
- package/src/catalog/composite.ts +180 -0
- package/src/catalog/descriptors.ts +164 -0
- package/src/catalog/interface.ts +721 -0
- package/src/catalog/read-only.ts +909 -0
- package/src/client/client.ts +1348 -0
- package/src/client/errors.ts +69 -0
- package/src/client/helpers.ts +56 -0
- package/src/client/protocol.ts +33 -0
- package/src/client/types.ts +140 -0
- package/src/client-entry.ts +112 -0
- package/src/codec/asd.ts +147 -0
- package/src/crypto.ts +143 -0
- package/src/errors.ts +61 -0
- package/src/filter-pushdown/collector.ts +147 -0
- package/src/filter-pushdown/deserialize.ts +253 -0
- package/src/filter-pushdown/evaluate.ts +591 -0
- package/src/filter-pushdown/index.ts +28 -0
- package/src/filter-pushdown/types.ts +91 -0
- package/src/functions/aggregate.ts +384 -0
- package/src/functions/copy-from.ts +282 -0
- package/src/functions/copy-to.ts +319 -0
- package/src/functions/registry.ts +258 -0
- package/src/functions/scalar.ts +356 -0
- package/src/functions/storage-cf-do.ts +303 -0
- package/src/functions/storage.ts +521 -0
- package/src/functions/table-buffering.ts +562 -0
- package/src/functions/table-in-out.ts +373 -0
- package/src/functions/table.ts +521 -0
- package/src/functions/types.ts +192 -0
- package/src/generated/vgi-client.ts +560 -0
- package/src/generated/vgi-protocol-schemas.ts +864 -0
- package/src/index.core.ts +267 -0
- package/src/index.ts +11 -0
- package/src/metadata/resolve.ts +109 -0
- package/src/metadata/serialize.ts +242 -0
- package/src/metadata/types.ts +60 -0
- package/src/protocol/dispatch.ts +52 -0
- package/src/protocol/handlers/aggregate.ts +355 -0
- package/src/protocol/handlers/catalog/admin.ts +368 -0
- package/src/protocol/handlers/catalog/index.ts +25 -0
- package/src/protocol/handlers/catalog/index_methods.ts +65 -0
- package/src/protocol/handlers/catalog/macro.ts +114 -0
- package/src/protocol/handlers/catalog/shared.ts +291 -0
- package/src/protocol/handlers/catalog/table.ts +401 -0
- package/src/protocol/handlers/catalog/view.ts +119 -0
- package/src/protocol/handlers/function.ts +302 -0
- package/src/protocol/handlers/shared.ts +80 -0
- package/src/protocol/handlers/table-buffering.ts +207 -0
- package/src/protocol/serialize.ts +23 -0
- package/src/protocol/serializers/arguments.ts +160 -0
- package/src/protocol/serializers/bind.ts +212 -0
- package/src/protocol/serializers/cardinality.ts +45 -0
- package/src/protocol/serializers/init.ts +247 -0
- package/src/protocol/serializers/shared.ts +46 -0
- package/src/protocol/state-serializer.ts +181 -0
- package/src/protocol/types.ts +153 -0
- package/src/secrets/helpers.ts +70 -0
- package/src/types.ts +71 -0
- package/src/util/arrow/index.ts +23 -0
- package/src/util/bytes.ts +14 -0
- package/src/util/statistics.ts +40 -0
- package/src/worker-cf-entry.ts +172 -0
- package/src/worker.ts +212 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// Unary handlers for the table_buffering (Sink+Source) lifecycle:
|
|
3
|
+
// table_buffering_process — sink one input batch, return state_id
|
|
4
|
+
// table_buffering_combine — group/merge state_ids -> finalize_state_ids
|
|
5
|
+
// table_buffering_destructor — best-effort end-of-query storage cleanup
|
|
6
|
+
//
|
|
7
|
+
// These RPCs carry no init context, so each cold-loads the init metadata that
|
|
8
|
+
// the TABLE_BUFFERING init phase persisted to FunctionStorage (keyed by
|
|
9
|
+
// execution_id), then rebuilds the user-facing TableBufferingParams. Mirrors
|
|
10
|
+
// vgi-python's worker._load_table_buffering_params.
|
|
11
|
+
|
|
12
|
+
import { deserializeBatch } from "../../arrow/index.js";
|
|
13
|
+
import { Protocol } from "@query-farm/vgi-rpc";
|
|
14
|
+
import type { FunctionRegistry } from "../../functions/registry.js";
|
|
15
|
+
import { toUint8Array } from "../../util/bytes.js";
|
|
16
|
+
import { batchToScalarDict, batchToSecretDict } from "../../util/arrow/index.js";
|
|
17
|
+
import {
|
|
18
|
+
TableBufferingProcessResultSchema,
|
|
19
|
+
TableBufferingCombineResultSchema,
|
|
20
|
+
TableBufferingDestructorResultSchema,
|
|
21
|
+
} from "../../generated/vgi-protocol-schemas.js";
|
|
22
|
+
import {
|
|
23
|
+
REQUEST_PARAMS_SCHEMA,
|
|
24
|
+
RESULT_BINARY_SCHEMA,
|
|
25
|
+
unwrapRequest,
|
|
26
|
+
wrapResult,
|
|
27
|
+
} from "./shared.js";
|
|
28
|
+
import { openAttach } from "./catalog/shared.js";
|
|
29
|
+
import {
|
|
30
|
+
BoundStorage,
|
|
31
|
+
storage as defaultStorage,
|
|
32
|
+
FrameworkNS,
|
|
33
|
+
} from "../../functions/storage.js";
|
|
34
|
+
import {
|
|
35
|
+
decodeBufferingInit,
|
|
36
|
+
type TableBufferingVgiFunction,
|
|
37
|
+
type TableBufferingParams,
|
|
38
|
+
} from "../../functions/table-buffering.js";
|
|
39
|
+
import { Arguments } from "../../arguments/arguments.js";
|
|
40
|
+
|
|
41
|
+
export function registerTableBufferingMethods(
|
|
42
|
+
protocol: Protocol,
|
|
43
|
+
registry: FunctionRegistry,
|
|
44
|
+
signingKey?: Uint8Array,
|
|
45
|
+
): void {
|
|
46
|
+
function resolveBuffering(name: string): TableBufferingVgiFunction {
|
|
47
|
+
const func = registry.get(name, {
|
|
48
|
+
arguments: new Arguments(),
|
|
49
|
+
inputSchema: null,
|
|
50
|
+
isScalar: false,
|
|
51
|
+
}) as any;
|
|
52
|
+
if (!func || func.kind !== "table_buffering") {
|
|
53
|
+
throw new Error(`Function '${name}' is not a table_buffering function`);
|
|
54
|
+
}
|
|
55
|
+
return func as TableBufferingVgiFunction;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Cold-load init metadata + build TableBufferingParams from a unary request.
|
|
59
|
+
async function loadParams(
|
|
60
|
+
inner: Record<string, any>,
|
|
61
|
+
clientLog: (level: string, message: string) => void,
|
|
62
|
+
ctx: any,
|
|
63
|
+
): Promise<{ func: TableBufferingVgiFunction; params: TableBufferingParams<any> }> {
|
|
64
|
+
const functionName: string = inner.function_name;
|
|
65
|
+
const executionId = toUint8Array(inner.execution_id);
|
|
66
|
+
// Unseal attach_opaque_data so combine()/process() see the SAME plaintext,
|
|
67
|
+
// attach-scoped bytes that bind()/init() and plain table functions see.
|
|
68
|
+
// Without this, on HTTP transport the buffering RPCs would scope persistent
|
|
69
|
+
// BoundStorage by the *sealed* envelope while reads scope by the plaintext,
|
|
70
|
+
// so attach-scoped state written in combine() is invisible to later reads.
|
|
71
|
+
const rawAttach = inner.attach_opaque_data ? toUint8Array(inner.attach_opaque_data) : null;
|
|
72
|
+
const attach =
|
|
73
|
+
rawAttach && rawAttach.length > 0
|
|
74
|
+
? await openAttach(rawAttach, ctx?.auth, signingKey)
|
|
75
|
+
: rawAttach;
|
|
76
|
+
const transactionId = inner.transaction_id ? toUint8Array(inner.transaction_id) : null;
|
|
77
|
+
|
|
78
|
+
const func = resolveBuffering(functionName);
|
|
79
|
+
const bound = new BoundStorage(defaultStorage, executionId);
|
|
80
|
+
const payload = await bound.stateGet(
|
|
81
|
+
FrameworkNS.BUFFERING_INIT,
|
|
82
|
+
BoundStorage.packIntKey(-1),
|
|
83
|
+
);
|
|
84
|
+
if (payload == null) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`table_buffering: unknown execution_id ${hex(executionId)} ` +
|
|
87
|
+
`(init never ran or destructor already fired)`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
const { request } = decodeBufferingInit(payload);
|
|
91
|
+
const args = func.bufferingExtractArgs(request.bind_call);
|
|
92
|
+
const settings = batchToScalarDict(request.bind_call.settings);
|
|
93
|
+
const secrets = batchToSecretDict(request.bind_call.secrets);
|
|
94
|
+
|
|
95
|
+
const params: TableBufferingParams<any> = {
|
|
96
|
+
args,
|
|
97
|
+
initCall: request,
|
|
98
|
+
outputSchema: request.output_schema,
|
|
99
|
+
settings,
|
|
100
|
+
secrets,
|
|
101
|
+
storage: bound,
|
|
102
|
+
executionId,
|
|
103
|
+
attachId: attach ?? new Uint8Array(0),
|
|
104
|
+
transactionId,
|
|
105
|
+
function_name: functionName,
|
|
106
|
+
batchIndex: null,
|
|
107
|
+
clientLog,
|
|
108
|
+
};
|
|
109
|
+
return { func, params };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ------------------------------------------------------------------------
|
|
113
|
+
// table_buffering_process — sink one batch, return state_id (unary)
|
|
114
|
+
// ------------------------------------------------------------------------
|
|
115
|
+
protocol.unary("table_buffering_process", {
|
|
116
|
+
params: REQUEST_PARAMS_SCHEMA,
|
|
117
|
+
result: RESULT_BINARY_SCHEMA,
|
|
118
|
+
handler: async (rpcParams, ctx?: any) => {
|
|
119
|
+
const inner = unwrapRequest(rpcParams.request);
|
|
120
|
+
const clientLog = makeClientLog(ctx);
|
|
121
|
+
const { func, params } = await loadParams(inner, clientLog, ctx);
|
|
122
|
+
if (inner.batch_index != null) {
|
|
123
|
+
params.batchIndex = Number(inner.batch_index);
|
|
124
|
+
}
|
|
125
|
+
const batch = deserializeBatch(toUint8Array(inner.input_batch));
|
|
126
|
+
const stateId = await func.bufferingConfig.process(batch, params);
|
|
127
|
+
if (!(stateId instanceof Uint8Array)) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`${func.meta.name}.process() must return Uint8Array (the opaque state_id)`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return wrapResult({ state_id: stateId }, TableBufferingProcessResultSchema);
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ------------------------------------------------------------------------
|
|
137
|
+
// table_buffering_combine — group/merge state_ids (unary)
|
|
138
|
+
// ------------------------------------------------------------------------
|
|
139
|
+
protocol.unary("table_buffering_combine", {
|
|
140
|
+
params: REQUEST_PARAMS_SCHEMA,
|
|
141
|
+
result: RESULT_BINARY_SCHEMA,
|
|
142
|
+
handler: async (rpcParams, ctx?: any) => {
|
|
143
|
+
const inner = unwrapRequest(rpcParams.request);
|
|
144
|
+
const clientLog = makeClientLog(ctx);
|
|
145
|
+
const { func, params } = await loadParams(inner, clientLog, ctx);
|
|
146
|
+
const stateIds = decodeBytesList(inner.state_ids);
|
|
147
|
+
const finalizeStateIds = await func.bufferingConfig.combine(stateIds, params);
|
|
148
|
+
const out = finalizeStateIds.map((fid, i) => {
|
|
149
|
+
if (!(fid instanceof Uint8Array)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`${func.meta.name}.combine() returned non-Uint8Array finalize_state_id at index ${i}`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return fid;
|
|
155
|
+
});
|
|
156
|
+
return wrapResult(
|
|
157
|
+
{ finalize_state_ids: out },
|
|
158
|
+
TableBufferingCombineResultSchema,
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// ------------------------------------------------------------------------
|
|
164
|
+
// table_buffering_destructor — best-effort end-of-query cleanup (unary)
|
|
165
|
+
// ------------------------------------------------------------------------
|
|
166
|
+
protocol.unary("table_buffering_destructor", {
|
|
167
|
+
params: REQUEST_PARAMS_SCHEMA,
|
|
168
|
+
result: RESULT_BINARY_SCHEMA,
|
|
169
|
+
handler: async (rpcParams) => {
|
|
170
|
+
try {
|
|
171
|
+
const inner = unwrapRequest(rpcParams.request);
|
|
172
|
+
const executionId = toUint8Array(inner.execution_id);
|
|
173
|
+
const bound = new BoundStorage(defaultStorage, executionId);
|
|
174
|
+
await bound.executionClear();
|
|
175
|
+
} catch {
|
|
176
|
+
// Teardown path — swallow; the entry is wiped when the worker exits.
|
|
177
|
+
}
|
|
178
|
+
return wrapResult({}, TableBufferingDestructorResultSchema);
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function makeClientLog(ctx: any): (level: string, message: string) => void {
|
|
184
|
+
if (ctx && typeof ctx.clientLog === "function") {
|
|
185
|
+
return (level: string, message: string) => ctx.clientLog(level, message);
|
|
186
|
+
}
|
|
187
|
+
return () => {};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function decodeBytesList(raw: any): Uint8Array[] {
|
|
191
|
+
if (raw == null) return [];
|
|
192
|
+
const out: Uint8Array[] = [];
|
|
193
|
+
const iter: Iterable<any> = Array.isArray(raw)
|
|
194
|
+
? raw
|
|
195
|
+
: typeof raw[Symbol.iterator] === "function"
|
|
196
|
+
? raw
|
|
197
|
+
: [];
|
|
198
|
+
for (const entry of iter) {
|
|
199
|
+
if (entry == null) continue;
|
|
200
|
+
out.push(toUint8Array(entry));
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function hex(buf: Uint8Array): string {
|
|
206
|
+
return Array.from(buf, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
207
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// Arrow serialization/deserialization for VGI protocol types.
|
|
3
|
+
// Barrel re-export — implementations live in serializers/. The wire format
|
|
4
|
+
// must match Python's ArrowSerializableDataclass output (identical field
|
|
5
|
+
// names, types, and metadata).
|
|
6
|
+
|
|
7
|
+
export { serializeArguments, deserializeArguments } from "./serializers/arguments.js";
|
|
8
|
+
export {
|
|
9
|
+
serializeBindRequest,
|
|
10
|
+
deserializeBindRequest,
|
|
11
|
+
serializeBindResponse,
|
|
12
|
+
deserializeBindResponse,
|
|
13
|
+
} from "./serializers/bind.js";
|
|
14
|
+
export {
|
|
15
|
+
serializeInitRequest,
|
|
16
|
+
deserializeInitRequest,
|
|
17
|
+
serializeGlobalInitResponse,
|
|
18
|
+
deserializeGlobalInitResponse,
|
|
19
|
+
} from "./serializers/init.js";
|
|
20
|
+
export {
|
|
21
|
+
deserializeCardinalityRequest,
|
|
22
|
+
serializeTableCardinality,
|
|
23
|
+
} from "./serializers/cardinality.js";
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// Arguments wire format — single-row Arrow batch with one "args" Struct
|
|
3
|
+
// column whose children are named `positional_<i>` / `named_<name>`.
|
|
4
|
+
// Matches Python's Arguments.serialize_to_bytes().
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type VgiDataType,
|
|
8
|
+
type VgiField,
|
|
9
|
+
schema as makeSchema,
|
|
10
|
+
field,
|
|
11
|
+
struct as makeStruct,
|
|
12
|
+
utf8,
|
|
13
|
+
binary,
|
|
14
|
+
int64,
|
|
15
|
+
bool,
|
|
16
|
+
nullType,
|
|
17
|
+
isInt,
|
|
18
|
+
batchFromColumns,
|
|
19
|
+
serializeBatch,
|
|
20
|
+
deserializeBatch,
|
|
21
|
+
readCanonicalValue,
|
|
22
|
+
} from "../../arrow/index.js";
|
|
23
|
+
import { codecFor } from "../../arrow/codec/registry.js";
|
|
24
|
+
import { Arguments } from "../../arguments/arguments.js";
|
|
25
|
+
|
|
26
|
+
export function serializeArguments(args: Arguments): Uint8Array {
|
|
27
|
+
// Arguments are serialized as a single-row batch with one "args" Struct column.
|
|
28
|
+
// The struct has fields: "positional_0", "positional_1", ... and "named_<name>".
|
|
29
|
+
|
|
30
|
+
const structFields: VgiField[] = [];
|
|
31
|
+
const structValues: Record<string, any> = {};
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < args.positional.length; i++) {
|
|
34
|
+
const val = args.positional[i];
|
|
35
|
+
const fieldName = `positional_${i}`;
|
|
36
|
+
structFields.push(field(fieldName, inferScalarType(val), true));
|
|
37
|
+
structValues[fieldName] = val;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const [name, val] of args.named) {
|
|
41
|
+
const fieldName = `named_${name}`;
|
|
42
|
+
structFields.push(field(fieldName, inferScalarType(val), true));
|
|
43
|
+
structValues[fieldName] = val;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const structType = makeStruct(structFields);
|
|
47
|
+
const sch = makeSchema([field("args", structType, true)]);
|
|
48
|
+
|
|
49
|
+
// Coerce JS numbers to BigInt for Int64 children — both backends require
|
|
50
|
+
// BigInt at 64-bit width.
|
|
51
|
+
const structRow: Record<string, any> = {};
|
|
52
|
+
for (const f of structFields) {
|
|
53
|
+
let val = structValues[f.name];
|
|
54
|
+
if (isInt(f.type) && (f.type as any).bitWidth === 64 && typeof val === "number") {
|
|
55
|
+
val = BigInt(val);
|
|
56
|
+
}
|
|
57
|
+
structRow[f.name] = val;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const batch = batchFromColumns({ args: [structRow] }, sch);
|
|
61
|
+
return serializeBatch(batch);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function inferScalarType(val: any): VgiDataType {
|
|
65
|
+
if (val === null || val === undefined) return nullType();
|
|
66
|
+
if (typeof val === "string") return utf8();
|
|
67
|
+
if (typeof val === "boolean") return bool();
|
|
68
|
+
if (typeof val === "number") return int64();
|
|
69
|
+
if (typeof val === "bigint") return int64();
|
|
70
|
+
if (val instanceof Uint8Array || val instanceof ArrayBuffer) return binary();
|
|
71
|
+
return utf8(); // fallback (e.g. an Int32Array INTERVAL quad -> stringified)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function deserializeArguments(bytes: Uint8Array): Arguments {
|
|
75
|
+
if (!bytes || bytes.length === 0) return new Arguments();
|
|
76
|
+
|
|
77
|
+
// Ensure we have a clean copy (not a view into a larger buffer)
|
|
78
|
+
const cleanBytes = bytes.byteOffset !== 0 || bytes.byteLength !== bytes.buffer.byteLength
|
|
79
|
+
? new Uint8Array(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength))
|
|
80
|
+
: bytes;
|
|
81
|
+
|
|
82
|
+
const batch = deserializeBatch(cleanBytes);
|
|
83
|
+
const positional: any[] = [];
|
|
84
|
+
const named = new Map<string, any>();
|
|
85
|
+
|
|
86
|
+
if (batch.numRows === 0) {
|
|
87
|
+
return new Arguments(positional, named);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Arguments are serialized as a single "args" Struct column.
|
|
91
|
+
// The struct has fields named "positional_0", "positional_1", etc.
|
|
92
|
+
// for positional args, and "named_<name>" for named args.
|
|
93
|
+
const argsCol = batch.getChild("args");
|
|
94
|
+
if (argsCol) {
|
|
95
|
+
// Extract per-positional types from the struct's children
|
|
96
|
+
const argsStructType = argsCol.type as any;
|
|
97
|
+
const structSchema = argsStructType.children
|
|
98
|
+
? makeSchema(argsStructType.children)
|
|
99
|
+
: batch.schema;
|
|
100
|
+
|
|
101
|
+
// Read the args struct via the codec/canonical path (backend-agnostic,
|
|
102
|
+
// lossless, RICH) — surfaces as a plain { field: value } object.
|
|
103
|
+
const structType = argsCol.type as unknown as VgiDataType;
|
|
104
|
+
const structVal = codecFor(structType).canonicalToRich(
|
|
105
|
+
readCanonicalValue(structType, argsCol, 0),
|
|
106
|
+
) as any;
|
|
107
|
+
if (structVal && typeof structVal === "object") {
|
|
108
|
+
const dict: Record<string, any> = structVal;
|
|
109
|
+
for (const [key, value] of Object.entries(dict)) {
|
|
110
|
+
if (key.startsWith("positional_")) {
|
|
111
|
+
const idx = parseInt(key.slice("positional_".length), 10);
|
|
112
|
+
while (positional.length <= idx) positional.push(null);
|
|
113
|
+
positional[idx] = value;
|
|
114
|
+
} else if (key.startsWith("named_")) {
|
|
115
|
+
const name = key.slice("named_".length);
|
|
116
|
+
named.set(name, value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return new Arguments(positional, named, structSchema);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Fallback: flat columns (legacy format - each field is a column)
|
|
124
|
+
const sch = batch.schema;
|
|
125
|
+
if (!sch || sch.fields.length === 0) {
|
|
126
|
+
return new Arguments(positional, named);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const f of sch.fields) {
|
|
130
|
+
const col = batch.getChild(f.name);
|
|
131
|
+
const ftype = f.type as unknown as VgiDataType;
|
|
132
|
+
const val = col
|
|
133
|
+
? codecFor(ftype).canonicalToRich(readCanonicalValue(ftype, col, 0))
|
|
134
|
+
: null;
|
|
135
|
+
|
|
136
|
+
if (f.name.startsWith("positional_")) {
|
|
137
|
+
const idx = parseInt(f.name.slice("positional_".length), 10);
|
|
138
|
+
while (positional.length <= idx) positional.push(null);
|
|
139
|
+
positional[idx] = val;
|
|
140
|
+
} else if (f.name.startsWith("named_")) {
|
|
141
|
+
const name = f.name.slice("named_".length);
|
|
142
|
+
named.set(name, val);
|
|
143
|
+
} else {
|
|
144
|
+
// Try numeric field name (old format)
|
|
145
|
+
const metadata = f.metadata;
|
|
146
|
+
const isNamed = metadata?.get?.("vgi_arg") === "named";
|
|
147
|
+
if (isNamed) {
|
|
148
|
+
named.set(f.name, val);
|
|
149
|
+
} else {
|
|
150
|
+
const idx = parseInt(f.name, 10);
|
|
151
|
+
if (!isNaN(idx)) {
|
|
152
|
+
while (positional.length <= idx) positional.push(null);
|
|
153
|
+
positional[idx] = val;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return new Arguments(positional, named, sch);
|
|
160
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// BindRequest / BindResponse wire serialization. Schemas mirror Python's
|
|
3
|
+
// ArrowSerializableDataclass field declaration order so the format is
|
|
4
|
+
// positional-compatible with the Python reader.
|
|
5
|
+
|
|
6
|
+
import { type VgiSchema, schema, type VgiField, field, type VgiBatch, type VgiDataType, utf8, binary, bool, list, struct } from "../../arrow/index.js";
|
|
7
|
+
import { Arguments } from "../../arguments/arguments.js";
|
|
8
|
+
import { FunctionType } from "../../types.js";
|
|
9
|
+
import type { BindRequest, BindResponse, CopyFromContext, CopyToContext } from "../types.js";
|
|
10
|
+
import {
|
|
11
|
+
serializeSchema,
|
|
12
|
+
deserializeSchema,
|
|
13
|
+
serializeBatch,
|
|
14
|
+
deserializeBatch,
|
|
15
|
+
} from "../../util/arrow/index.js";
|
|
16
|
+
import { toUint8Array, buildSingleRowBatch } from "./shared.js";
|
|
17
|
+
import { serializeArguments, deserializeArguments } from "./arguments.js";
|
|
18
|
+
|
|
19
|
+
const BIND_REQUEST_SCHEMA = schema([
|
|
20
|
+
field("function_name", utf8(), false),
|
|
21
|
+
field("arguments", binary(), false),
|
|
22
|
+
field("function_type", utf8(), false),
|
|
23
|
+
field("input_schema", binary(), true),
|
|
24
|
+
field("settings", binary(), true),
|
|
25
|
+
field("secrets", binary(), true),
|
|
26
|
+
field("attach_opaque_data", binary(), true),
|
|
27
|
+
field("transaction_opaque_data", binary(), true),
|
|
28
|
+
field("resolved_secrets_provided", bool(), false),
|
|
29
|
+
// Time travel AT clause (additive, nullable, name-keyed -> wire-compatible).
|
|
30
|
+
// Both null when the scan has no AT clause. See BindRequest.at_unit.
|
|
31
|
+
field("at_unit", utf8(), true),
|
|
32
|
+
field("at_value", utf8(), true),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
// COPY ... FROM context — a nullable nested struct<format, file_path,
|
|
36
|
+
// expected_schema>. Byte-for-byte the shape the C++ extension builds in
|
|
37
|
+
// vgi_rpc_types.cpp (BuildBindRequest's copy_from branch) and the Python
|
|
38
|
+
// BindRequest.copy_from field. Only appended to the serialized batch when the
|
|
39
|
+
// bind actually opens a COPY scan, so ordinary scans keep the legacy wire shape.
|
|
40
|
+
const COPY_FROM_STRUCT_TYPE = struct([
|
|
41
|
+
field("format", utf8(), false),
|
|
42
|
+
field("file_path", utf8(), false),
|
|
43
|
+
field("expected_schema", binary(), false),
|
|
44
|
+
]);
|
|
45
|
+
const COPY_FROM_FIELD = field("copy_from", COPY_FROM_STRUCT_TYPE, true);
|
|
46
|
+
|
|
47
|
+
// COPY ... TO context — a nullable nested struct<format, file_path>.
|
|
48
|
+
// Byte-for-byte the shape the C++ extension builds in vgi_rpc_types.cpp
|
|
49
|
+
// (BuildBindRequest's copy_to branch) and the Python BindRequest.copy_to field.
|
|
50
|
+
// Only appended to the serialized batch when the bind actually opens a COPY TO
|
|
51
|
+
// sink, so ordinary scans keep the legacy wire shape.
|
|
52
|
+
const COPY_TO_STRUCT_TYPE = struct([
|
|
53
|
+
field("format", utf8(), false),
|
|
54
|
+
field("file_path", utf8(), false),
|
|
55
|
+
]);
|
|
56
|
+
const COPY_TO_FIELD = field("copy_to", COPY_TO_STRUCT_TYPE, true);
|
|
57
|
+
|
|
58
|
+
const BIND_RESPONSE_SCHEMA = schema([
|
|
59
|
+
field("output_schema", binary(), false),
|
|
60
|
+
field("opaque_data", binary(), true),
|
|
61
|
+
field("lookup_secret_types", list(field("item", utf8(), true)), false),
|
|
62
|
+
field("lookup_scopes", list(field("item", utf8(), true)), false),
|
|
63
|
+
field("lookup_names", list(field("item", utf8(), true)), false),
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
export function serializeBindRequest(req: BindRequest): VgiBatch {
|
|
67
|
+
const row: Record<string, any> = {
|
|
68
|
+
function_name: req.function_name,
|
|
69
|
+
arguments: serializeArguments(req.arguments),
|
|
70
|
+
function_type: req.function_type,
|
|
71
|
+
input_schema: req.input_schema ? serializeSchema(req.input_schema) : null,
|
|
72
|
+
settings: req.settings ? serializeBatch(req.settings) : null,
|
|
73
|
+
secrets: req.secrets ? serializeBatch(req.secrets) : null,
|
|
74
|
+
attach_opaque_data: req.attach_opaque_data ?? null,
|
|
75
|
+
transaction_opaque_data: req.transaction_opaque_data ?? null,
|
|
76
|
+
resolved_secrets_provided: req.resolved_secrets_provided ?? false,
|
|
77
|
+
at_unit: req.at_unit ?? null,
|
|
78
|
+
at_value: req.at_value ?? null,
|
|
79
|
+
};
|
|
80
|
+
// Append the copy_from / copy_to struct columns only for COPY scans/sinks, so
|
|
81
|
+
// non-COPY binds serialize to the exact legacy shape (fields matched by name
|
|
82
|
+
// on the reader). At most one is present in practice, but both are handled
|
|
83
|
+
// generically so the wire stays additive either way.
|
|
84
|
+
const extraFields: VgiField[] = [];
|
|
85
|
+
if (req.copy_from) {
|
|
86
|
+
extraFields.push(COPY_FROM_FIELD);
|
|
87
|
+
row.copy_from = {
|
|
88
|
+
format: req.copy_from.format,
|
|
89
|
+
file_path: req.copy_from.file_path,
|
|
90
|
+
expected_schema: serializeSchema(req.copy_from.expected_schema),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (req.copy_to) {
|
|
94
|
+
extraFields.push(COPY_TO_FIELD);
|
|
95
|
+
row.copy_to = {
|
|
96
|
+
format: req.copy_to.format,
|
|
97
|
+
file_path: req.copy_to.file_path,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (extraFields.length > 0) {
|
|
101
|
+
const schemaWithCopy = schema([...BIND_REQUEST_SCHEMA.fields, ...extraFields]);
|
|
102
|
+
return buildSingleRowBatch(schemaWithCopy, row);
|
|
103
|
+
}
|
|
104
|
+
return buildSingleRowBatch(BIND_REQUEST_SCHEMA, row);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function parseCopyFromContext(raw: any): CopyFromContext | null {
|
|
108
|
+
// The struct column decodes to a plain { format, file_path, expected_schema }
|
|
109
|
+
// object via the codec/canonical path (expected_schema is binary -> Uint8Array).
|
|
110
|
+
if (raw == null) return null;
|
|
111
|
+
const format = raw.format;
|
|
112
|
+
const filePath = raw.file_path;
|
|
113
|
+
const schemaBytes = raw.expected_schema;
|
|
114
|
+
if (format == null || filePath == null || schemaBytes == null) return null;
|
|
115
|
+
return {
|
|
116
|
+
format: String(format),
|
|
117
|
+
file_path: String(filePath),
|
|
118
|
+
expected_schema: deserializeSchema(toUint8Array(schemaBytes)),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseCopyToContext(raw: any): CopyToContext | null {
|
|
123
|
+
// The struct column decodes to a plain { format, file_path } object via the
|
|
124
|
+
// codec/canonical path.
|
|
125
|
+
if (raw == null) return null;
|
|
126
|
+
const format = raw.format;
|
|
127
|
+
const filePath = raw.file_path;
|
|
128
|
+
if (format == null || filePath == null) return null;
|
|
129
|
+
return { format: String(format), file_path: String(filePath) };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function deserializeBindRequest(
|
|
133
|
+
params: Record<string, any>
|
|
134
|
+
): BindRequest {
|
|
135
|
+
// params come from the RPC layer - already extracted from a single-row batch
|
|
136
|
+
// arguments may be null/undefined when DuckDB sends empty args
|
|
137
|
+
const args = params.arguments
|
|
138
|
+
? deserializeArguments(toUint8Array(params.arguments))
|
|
139
|
+
: new Arguments();
|
|
140
|
+
|
|
141
|
+
// function_type might be a string directly or enum name
|
|
142
|
+
let functionType: FunctionType;
|
|
143
|
+
const ftStr = String(params.function_type);
|
|
144
|
+
if (ftStr === "scalar" || ftStr === "SCALAR") {
|
|
145
|
+
functionType = FunctionType.SCALAR;
|
|
146
|
+
} else if (ftStr === "table" || ftStr === "TABLE") {
|
|
147
|
+
functionType = FunctionType.TABLE;
|
|
148
|
+
} else if (ftStr === "aggregate" || ftStr === "AGGREGATE") {
|
|
149
|
+
functionType = FunctionType.AGGREGATE;
|
|
150
|
+
} else {
|
|
151
|
+
functionType = ftStr as FunctionType;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
function_name: params.function_name,
|
|
156
|
+
arguments: args,
|
|
157
|
+
function_type: functionType,
|
|
158
|
+
input_schema: params.input_schema
|
|
159
|
+
? deserializeSchema(toUint8Array(params.input_schema))
|
|
160
|
+
: null,
|
|
161
|
+
settings: params.settings
|
|
162
|
+
? deserializeBatch(toUint8Array(params.settings))
|
|
163
|
+
: null,
|
|
164
|
+
secrets: params.secrets
|
|
165
|
+
? deserializeBatch(toUint8Array(params.secrets))
|
|
166
|
+
: null,
|
|
167
|
+
attach_opaque_data: params.attach_opaque_data ? toUint8Array(params.attach_opaque_data) : null,
|
|
168
|
+
transaction_opaque_data: params.transaction_opaque_data
|
|
169
|
+
? toUint8Array(params.transaction_opaque_data)
|
|
170
|
+
: null,
|
|
171
|
+
resolved_secrets_provided: params.resolved_secrets_provided ?? false,
|
|
172
|
+
// Empty string -> null (matches the C++ BuildBindRequest convention).
|
|
173
|
+
at_unit: params.at_unit ? String(params.at_unit) : null,
|
|
174
|
+
at_value: params.at_value ? String(params.at_value) : null,
|
|
175
|
+
// COPY ... FROM context — absent for ordinary scans (params.copy_from
|
|
176
|
+
// undefined -> null). The struct column decodes to a plain object.
|
|
177
|
+
copy_from: parseCopyFromContext(params.copy_from),
|
|
178
|
+
// COPY ... TO context — absent for ordinary scans / COPY FROM.
|
|
179
|
+
copy_to: parseCopyToContext(params.copy_to),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function serializeBindResponse(
|
|
184
|
+
resp: BindResponse
|
|
185
|
+
): Record<string, any> {
|
|
186
|
+
return {
|
|
187
|
+
output_schema: serializeSchema(resp.output_schema),
|
|
188
|
+
opaque_data: resp.opaque_data ?? null,
|
|
189
|
+
lookup_secret_types: resp.lookup_secret_types ?? [],
|
|
190
|
+
lookup_scopes: resp.lookup_scopes ?? [],
|
|
191
|
+
lookup_names: resp.lookup_names ?? [],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function deserializeBindResponse(
|
|
196
|
+
params: Record<string, any>
|
|
197
|
+
): BindResponse {
|
|
198
|
+
const toStrArray = (val: any): string[] => {
|
|
199
|
+
if (!val) return [];
|
|
200
|
+
const arr = Array.isArray(val) ? val : [...val];
|
|
201
|
+
return arr.filter((v: any) => v != null).map(String);
|
|
202
|
+
};
|
|
203
|
+
return {
|
|
204
|
+
output_schema: deserializeSchema(toUint8Array(params.output_schema)),
|
|
205
|
+
opaque_data: params.opaque_data ? toUint8Array(params.opaque_data) : null,
|
|
206
|
+
lookup_secret_types: toStrArray(params.lookup_secret_types),
|
|
207
|
+
lookup_scopes: toStrArray(params.lookup_scopes),
|
|
208
|
+
lookup_names: toStrArray(params.lookup_names),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export { BIND_REQUEST_SCHEMA, BIND_RESPONSE_SCHEMA };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
|
+
// TableFunctionCardinalityRequest / TableCardinality wire serialization.
|
|
3
|
+
|
|
4
|
+
import { type VgiSchema, schema, type VgiField, field, type VgiDataType, binary, int64 } from "../../arrow/index.js";
|
|
5
|
+
import type { TableFunctionCardinalityRequest, TableCardinality } from "../types.js";
|
|
6
|
+
import { deserializeBatch, batchToScalarDict } from "../../util/arrow/index.js";
|
|
7
|
+
import { toUint8Array } from "./shared.js";
|
|
8
|
+
import { deserializeBindRequest } from "./bind.js";
|
|
9
|
+
|
|
10
|
+
const TABLE_CARDINALITY_SCHEMA = schema([
|
|
11
|
+
field("estimate", int64(), true),
|
|
12
|
+
field("max", int64(), true),
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const TABLE_FUNCTION_CARDINALITY_REQUEST_SCHEMA = schema([
|
|
16
|
+
field("bind_call", binary(), false),
|
|
17
|
+
field("bind_opaque_data", binary(), true),
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
export function deserializeCardinalityRequest(
|
|
21
|
+
params: Record<string, any>
|
|
22
|
+
): TableFunctionCardinalityRequest {
|
|
23
|
+
const bindCallBytes = toUint8Array(params.bind_call);
|
|
24
|
+
const bindCallBatch = deserializeBatch(bindCallBytes);
|
|
25
|
+
const bindParams = batchToScalarDict(bindCallBatch);
|
|
26
|
+
const bindCall = deserializeBindRequest(bindParams);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
bind_call: bindCall,
|
|
30
|
+
bind_opaque_data: params.bind_opaque_data
|
|
31
|
+
? toUint8Array(params.bind_opaque_data)
|
|
32
|
+
: null,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function serializeTableCardinality(
|
|
37
|
+
card: TableCardinality
|
|
38
|
+
): Record<string, any> {
|
|
39
|
+
return {
|
|
40
|
+
estimate: card.estimate,
|
|
41
|
+
max: card.max,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { TABLE_CARDINALITY_SCHEMA, TABLE_FUNCTION_CARDINALITY_REQUEST_SCHEMA };
|