@query-farm/vgi 0.41.0 → 0.43.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/dist/generated/vgi-service.d.ts +1315 -0
- package/dist/index.js +1845 -780
- package/dist/index.js.map +16 -15
- package/dist/protocol/dispatch.d.ts +25 -3
- package/dist/protocol/handlers/aggregate.d.ts +3 -2
- package/dist/protocol/handlers/catalog/admin.d.ts +2 -2
- package/dist/protocol/handlers/catalog/index.d.ts +3 -2
- package/dist/protocol/handlers/catalog/index_methods.d.ts +2 -2
- package/dist/protocol/handlers/catalog/macro.d.ts +2 -2
- package/dist/protocol/handlers/catalog/shared.d.ts +7 -14
- package/dist/protocol/handlers/catalog/table.d.ts +2 -2
- package/dist/protocol/handlers/catalog/view.d.ts +2 -2
- package/dist/protocol/handlers/function.d.ts +3 -2
- package/dist/protocol/handlers/shared.d.ts +0 -1
- package/dist/protocol/handlers/table-buffering.d.ts +3 -2
- package/dist/protocol/handlers/unimplemented.d.ts +4 -0
- package/dist/serve-entry.js +1844 -780
- package/dist/serve-entry.js.map +16 -15
- package/dist/worker-cf-entry.js +1845 -780
- package/dist/worker-cf-entry.js.map +16 -15
- package/package.json +1 -1
- package/src/generated/vgi-service.ts +2678 -0
- package/src/protocol/dispatch.ts +51 -24
- package/src/protocol/handlers/aggregate.ts +205 -232
- package/src/protocol/handlers/catalog/admin.ts +228 -299
- package/src/protocol/handlers/catalog/index.ts +17 -16
- package/src/protocol/handlers/catalog/index_methods.ts +26 -36
- package/src/protocol/handlers/catalog/macro.ts +54 -77
- package/src/protocol/handlers/catalog/shared.ts +30 -47
- package/src/protocol/handlers/catalog/table.ts +205 -270
- package/src/protocol/handlers/catalog/view.ts +59 -85
- package/src/protocol/handlers/function.ts +127 -156
- package/src/protocol/handlers/shared.ts +0 -4
- package/src/protocol/handlers/table-buffering.ts +53 -71
- package/src/protocol/handlers/unimplemented.ts +30 -0
|
@@ -1,115 +1,89 @@
|
|
|
1
1
|
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
2
|
// Catalog view handlers: view_get/create/drop/rename/comment_set.
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
import { Protocol } from "@query-farm/vgi-rpc";
|
|
4
|
+
import type { VgiService } from "../../../generated/vgi-service.js";
|
|
6
5
|
import { encodeViewInfo } from "../../../generated/vgi-client.js";
|
|
7
6
|
import {
|
|
8
|
-
CatalogViewCommentSetParamsSchema,
|
|
9
|
-
CatalogViewCreateParamsSchema,
|
|
10
|
-
CatalogViewDropParamsSchema,
|
|
11
|
-
CatalogViewGetParamsSchema,
|
|
12
7
|
CatalogViewGetResultSchema,
|
|
13
|
-
CatalogViewRenameParamsSchema,
|
|
14
8
|
} from "../../../generated/vgi-protocol-schemas.js";
|
|
15
9
|
import { toUint8Array } from "../../../util/bytes.js";
|
|
16
10
|
import {
|
|
17
|
-
RESULT_BINARY_SCHEMA,
|
|
18
11
|
wrapResult,
|
|
19
12
|
} from "../shared.js";
|
|
20
13
|
import {
|
|
21
14
|
type GetCatalog,
|
|
22
|
-
|
|
23
|
-
catalogUnary,
|
|
15
|
+
catalogHandler,
|
|
24
16
|
} from "./shared.js";
|
|
25
17
|
|
|
26
|
-
export function
|
|
18
|
+
export function catalogViewHandlers(getCatalog: GetCatalog, signingKey?: Uint8Array): Partial<VgiService> {
|
|
27
19
|
// catalog_view_get
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return wrapResult({
|
|
40
|
-
items: info ? [encodeViewInfo(info)] : [],
|
|
41
|
-
}, CatalogViewGetResultSchema);
|
|
42
|
-
},
|
|
20
|
+
const catalogViewGet = catalogHandler(signingKey, async (params) => {
|
|
21
|
+
const cat = getCatalog();
|
|
22
|
+
const info = await cat.viewGet(
|
|
23
|
+
toUint8Array(params.attach_opaque_data),
|
|
24
|
+
params.schema_path,
|
|
25
|
+
params.name,
|
|
26
|
+
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
27
|
+
);
|
|
28
|
+
return wrapResult({
|
|
29
|
+
items: info ? [encodeViewInfo(info)] : [],
|
|
30
|
+
}, CatalogViewGetResultSchema);
|
|
43
31
|
});
|
|
44
32
|
|
|
45
33
|
// catalog_view_create
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
58
|
-
);
|
|
59
|
-
return {};
|
|
60
|
-
},
|
|
34
|
+
const catalogViewCreate = catalogHandler(signingKey, async (params) => {
|
|
35
|
+
const cat = getCatalog();
|
|
36
|
+
await cat.viewCreate(
|
|
37
|
+
toUint8Array(params.attach_opaque_data),
|
|
38
|
+
params.schema_path,
|
|
39
|
+
params.name,
|
|
40
|
+
params.definition,
|
|
41
|
+
params.on_conflict,
|
|
42
|
+
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
43
|
+
);
|
|
44
|
+
return {};
|
|
61
45
|
});
|
|
62
46
|
|
|
63
47
|
// catalog_view_drop
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
75
|
-
);
|
|
76
|
-
return {};
|
|
77
|
-
},
|
|
48
|
+
const catalogViewDrop = catalogHandler(signingKey, async (params) => {
|
|
49
|
+
const cat = getCatalog();
|
|
50
|
+
await cat.viewDrop(
|
|
51
|
+
toUint8Array(params.attach_opaque_data),
|
|
52
|
+
params.schema_path,
|
|
53
|
+
params.name,
|
|
54
|
+
params.ignore_not_found,
|
|
55
|
+
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
56
|
+
);
|
|
57
|
+
return {};
|
|
78
58
|
});
|
|
79
59
|
|
|
80
60
|
// catalog_view_rename
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
93
|
-
);
|
|
94
|
-
return {};
|
|
95
|
-
},
|
|
61
|
+
const catalogViewRename = catalogHandler(signingKey, async (params) => {
|
|
62
|
+
const cat = getCatalog();
|
|
63
|
+
await cat.viewRename(
|
|
64
|
+
toUint8Array(params.attach_opaque_data),
|
|
65
|
+
params.schema_path,
|
|
66
|
+
params.name,
|
|
67
|
+
params.new_name,
|
|
68
|
+
params.ignore_not_found,
|
|
69
|
+
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
70
|
+
);
|
|
71
|
+
return {};
|
|
96
72
|
});
|
|
97
73
|
|
|
98
74
|
// catalog_view_comment_set
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
111
|
-
);
|
|
112
|
-
return {};
|
|
113
|
-
},
|
|
75
|
+
const catalogViewCommentSet = catalogHandler(signingKey, async (params) => {
|
|
76
|
+
const cat = getCatalog();
|
|
77
|
+
await cat.viewCommentSet(
|
|
78
|
+
toUint8Array(params.attach_opaque_data),
|
|
79
|
+
params.schema_path,
|
|
80
|
+
params.name,
|
|
81
|
+
params.comment,
|
|
82
|
+
params.ignore_not_found,
|
|
83
|
+
params.transaction_opaque_data ? toUint8Array(params.transaction_opaque_data) : undefined
|
|
84
|
+
);
|
|
85
|
+
return {};
|
|
114
86
|
});
|
|
87
|
+
|
|
88
|
+
return { catalogViewGet, catalogViewCreate, catalogViewDrop, catalogViewRename, catalogViewCommentSet };
|
|
115
89
|
}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// implements VgiFunction.bind/globalInit/createStreamHandlers.
|
|
5
5
|
|
|
6
6
|
import { type VgiSchema, schema, type VgiField, field, type VgiDataType, binary, int64 } from "../../arrow/index.js";
|
|
7
|
-
import {
|
|
7
|
+
import type { AuthContext } from "@query-farm/vgi-rpc";
|
|
8
|
+
import { exchangeStream, type VgiService } from "../../generated/vgi-service.js";
|
|
8
9
|
import type { FunctionRegistry } from "../../functions/registry.js";
|
|
9
10
|
import type { StreamHandlers, HandlerState } from "../../functions/types.js";
|
|
10
11
|
import {
|
|
@@ -20,25 +21,11 @@ import { batchToScalarDict, deserializeBatch, adoptArrowJsShape } from "../../ut
|
|
|
20
21
|
import { toUint8Array } from "../../util/bytes.js";
|
|
21
22
|
import { serializeColumnStatistics } from "../../util/statistics.js";
|
|
22
23
|
import {
|
|
23
|
-
BindParamsSchema,
|
|
24
24
|
BindResultSchema,
|
|
25
|
-
InitParamsSchema,
|
|
26
|
-
TableFunctionCardinalityParamsSchema,
|
|
27
25
|
TableFunctionCardinalityResultSchema,
|
|
28
|
-
TableFunctionDynamicToStringParamsSchema,
|
|
29
26
|
TableFunctionDynamicToStringResultSchema,
|
|
30
|
-
TableFunctionPlanParamsSchema,
|
|
31
|
-
TableFunctionStatisticsParamsSchema,
|
|
32
27
|
} from "../../generated/vgi-protocol-schemas.js";
|
|
33
|
-
import {
|
|
34
|
-
REQUEST_PARAMS_SCHEMA,
|
|
35
|
-
RESULT_BINARY_SCHEMA,
|
|
36
|
-
RESULT_BINARY_NULLABLE_SCHEMA,
|
|
37
|
-
unwrapRequest,
|
|
38
|
-
wrapResult,
|
|
39
|
-
overloadContext,
|
|
40
|
-
} from "./shared.js";
|
|
41
|
-
import { GLOBAL_INIT_RESPONSE_SCHEMA } from "../serializers/init.js";
|
|
28
|
+
import { unwrapRequest, wrapResult, overloadContext } from "./shared.js";
|
|
42
29
|
import { parseCarriedInitRequest } from "../carried-init-request.js";
|
|
43
30
|
import { openAttach } from "./catalog/shared.js";
|
|
44
31
|
import { currentRequestAuth } from "../../request-auth.js";
|
|
@@ -197,7 +184,8 @@ async function resolveSplitPayloads(
|
|
|
197
184
|
}
|
|
198
185
|
}
|
|
199
186
|
|
|
200
|
-
|
|
187
|
+
/** The per-function lifecycle's `vgi.v2` handlers. */
|
|
188
|
+
export function functionHandlers(config: FunctionHandlerConfig): Partial<VgiService> {
|
|
201
189
|
const { registry, signingKey, catalogInterface } = config;
|
|
202
190
|
|
|
203
191
|
// The framework mints every attach as uuid(16) || catalog_bytes (sealed on
|
|
@@ -232,10 +220,6 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
232
220
|
return overloadContext({ ...bindCall, attach_opaque_data: attach }, catalogInterface);
|
|
233
221
|
}
|
|
234
222
|
|
|
235
|
-
// Shared with the serializer that actually emits the header, so the
|
|
236
|
-
// advertised shape and the emitted shape cannot drift apart.
|
|
237
|
-
const initHeaderSchema = GLOBAL_INIT_RESPONSE_SCHEMA;
|
|
238
|
-
|
|
239
223
|
const emptySchema = schema([]);
|
|
240
224
|
|
|
241
225
|
// Dummy non-empty schema for the init exchange registration.
|
|
@@ -249,25 +233,20 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
249
233
|
// --------------------------------------------------------------------------
|
|
250
234
|
// bind (unary)
|
|
251
235
|
// --------------------------------------------------------------------------
|
|
252
|
-
|
|
253
|
-
params
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const serialized = serializeBindResponse(response);
|
|
262
|
-
return wrapResult(serialized, BindResultSchema);
|
|
263
|
-
},
|
|
264
|
-
});
|
|
236
|
+
const bind: VgiService["bind"] = async (params, ctx) => {
|
|
237
|
+
const innerParams = unwrapRequest(params.request);
|
|
238
|
+
const request = deserializeBindRequest(innerParams);
|
|
239
|
+
request.attach_opaque_data = await stripAttach(request.attach_opaque_data, ctx);
|
|
240
|
+
const func = registry.get(request.function_name, overloadContext(request, catalogInterface));
|
|
241
|
+
const response = await func.bind(request);
|
|
242
|
+
const serialized = serializeBindResponse(response);
|
|
243
|
+
return wrapResult(serialized, BindResultSchema);
|
|
244
|
+
};
|
|
265
245
|
|
|
266
246
|
// --------------------------------------------------------------------------
|
|
267
247
|
// init (streaming) - dynamically produces either producer or exchange streams
|
|
268
248
|
// --------------------------------------------------------------------------
|
|
269
|
-
|
|
270
|
-
params: InitParamsSchema,
|
|
249
|
+
const init = exchangeStream({
|
|
271
250
|
inputSchema: dummyInputSchema,
|
|
272
251
|
outputSchema: emptySchema,
|
|
273
252
|
init: async (params, ctx?: any) => {
|
|
@@ -457,7 +436,6 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
457
436
|
// the number of predicate ids, not the number of turns).
|
|
458
437
|
state.filterHistory = handlerState?.filterHistory ?? null;
|
|
459
438
|
},
|
|
460
|
-
headerSchema: initHeaderSchema,
|
|
461
439
|
headerInit: (params: any, state: any, ctx: any) => {
|
|
462
440
|
// During init, _initResponse is still in memory.
|
|
463
441
|
// For exchange, this is never called (headers are only in init response).
|
|
@@ -471,22 +449,18 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
471
449
|
// --------------------------------------------------------------------------
|
|
472
450
|
// table_function_cardinality (unary)
|
|
473
451
|
// --------------------------------------------------------------------------
|
|
474
|
-
|
|
475
|
-
params
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
return wrapResult(cardResult, TableFunctionCardinalityResultSchema);
|
|
488
|
-
},
|
|
489
|
-
});
|
|
452
|
+
const tableFunctionCardinality: VgiService["tableFunctionCardinality"] = async (params) => {
|
|
453
|
+
const innerParams = unwrapRequest(params.request);
|
|
454
|
+
const request = deserializeCardinalityRequest(innerParams);
|
|
455
|
+
const func = registry.get(request.bind_call.function_name, await strippedContext(request.bind_call));
|
|
456
|
+
let cardResult: Record<string, any>;
|
|
457
|
+
if (func.cardinality) {
|
|
458
|
+
cardResult = serializeTableCardinality(await func.cardinality(request));
|
|
459
|
+
} else {
|
|
460
|
+
cardResult = { estimate: null, max: null };
|
|
461
|
+
}
|
|
462
|
+
return wrapResult(cardResult, TableFunctionCardinalityResultSchema);
|
|
463
|
+
};
|
|
490
464
|
|
|
491
465
|
// --------------------------------------------------------------------------
|
|
492
466
|
// table_function_plan (unary)
|
|
@@ -499,77 +473,73 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
499
473
|
// is one unit of work. That keeps every existing worker serving unchanged under
|
|
500
474
|
// protocol 1.4.0, so splits stay opt-in rather than something every worker must
|
|
501
475
|
// now implement.
|
|
502
|
-
|
|
503
|
-
params
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
request.bind_call.function_name,
|
|
510
|
-
await strippedContext(request.bind_call),
|
|
511
|
-
);
|
|
476
|
+
const tableFunctionPlan: VgiService["tableFunctionPlan"] = async (params, ctx?: any) => {
|
|
477
|
+
const innerParams = unwrapRequest(params.request);
|
|
478
|
+
const request = deserializePlanRequest(innerParams);
|
|
479
|
+
const func = registry.get(
|
|
480
|
+
request.bind_call.function_name,
|
|
481
|
+
await strippedContext(request.bind_call),
|
|
482
|
+
);
|
|
512
483
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
),
|
|
564
|
-
SCAN_SPLIT_SCHEMA,
|
|
484
|
+
const plan: PlanResult = func.plan
|
|
485
|
+
? await func.plan(request)
|
|
486
|
+
: { splits: [{ payload: new Uint8Array(0) }] };
|
|
487
|
+
|
|
488
|
+
// The framework stamps every token: an author cannot forget the
|
|
489
|
+
// consistency anchor, cannot mis-bind the fingerprint, and never writes
|
|
490
|
+
// crypto — and the envelope stays a private implementation detail whose
|
|
491
|
+
// layout can change without touching worker code in five languages.
|
|
492
|
+
const fp = fingerprintInputs(toUint8Array(innerParams.bind_call));
|
|
493
|
+
const fingerprint = await bindFingerprint(
|
|
494
|
+
fp.schemaPath,
|
|
495
|
+
fp.functionName,
|
|
496
|
+
fp.args,
|
|
497
|
+
fp.settings,
|
|
498
|
+
// projection_ids is not a bind-call field (it rides the init request),
|
|
499
|
+
// so it feeds in empty — matching the reference implementation, which
|
|
500
|
+
// reads it off the bind call and likewise finds nothing.
|
|
501
|
+
new Uint8Array(0),
|
|
502
|
+
);
|
|
503
|
+
// A worker that names its version is taken at its word — it knows which
|
|
504
|
+
// snapshot it planned against. One that leaves it unset gets the LIVE
|
|
505
|
+
// version, never 0: minting 0 while redemption compares against the live
|
|
506
|
+
// counter refuses every token, and is invisible on a catalog whose version
|
|
507
|
+
// happens to be 0, which is most fixtures.
|
|
508
|
+
const anchor =
|
|
509
|
+
plan.catalogVersion != null
|
|
510
|
+
? splitAnchor(plan.catalogVersion)
|
|
511
|
+
: ((await liveSplitAnchor(catalogInterface, request.bind_call as any, ctx?.auth, signingKey)) ??
|
|
512
|
+
splitAnchor(0));
|
|
513
|
+
|
|
514
|
+
const blobs: Uint8Array[] = [];
|
|
515
|
+
for (const split of plan.splits) {
|
|
516
|
+
const token = await buildSplitToken({
|
|
517
|
+
payload: split.payload,
|
|
518
|
+
fingerprint,
|
|
519
|
+
anchor,
|
|
520
|
+
signingKey,
|
|
521
|
+
// Bind the caller. Without it every token seals under the anonymous
|
|
522
|
+
// identity, so one tenant's splits are replayable by another — the
|
|
523
|
+
// exact replay the AAD exists to stop.
|
|
524
|
+
auth: ctx?.auth,
|
|
525
|
+
});
|
|
526
|
+
blobs.push(
|
|
527
|
+
serializeBatch(
|
|
528
|
+
batchFromColumns(
|
|
529
|
+
Object.fromEntries(
|
|
530
|
+
SCAN_SPLIT_SCHEMA.fields.map((f) => [
|
|
531
|
+
f.name,
|
|
532
|
+
[scanSplitRow(split, token)[f.name] ?? null],
|
|
533
|
+
]),
|
|
565
534
|
),
|
|
535
|
+
SCAN_SPLIT_SCHEMA,
|
|
566
536
|
),
|
|
567
|
-
)
|
|
568
|
-
|
|
537
|
+
),
|
|
538
|
+
);
|
|
539
|
+
}
|
|
569
540
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
});
|
|
541
|
+
return wrapResult(planResponseRow(plan, blobs), PLAN_RESPONSE_SCHEMA);
|
|
542
|
+
};
|
|
573
543
|
|
|
574
544
|
// --------------------------------------------------------------------------
|
|
575
545
|
// table_function_statistics (unary)
|
|
@@ -578,19 +548,15 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
578
548
|
// the function declared a statistics() hook and it produced a non-empty
|
|
579
549
|
// list, else null. DuckDB uses the bounds for plan-time filter elimination
|
|
580
550
|
// (folds impossible filters to EMPTY_RESULT).
|
|
581
|
-
|
|
582
|
-
params
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (!stats || stats.length === 0) return { result: null };
|
|
591
|
-
return { result: serializeColumnStatistics(stats) };
|
|
592
|
-
},
|
|
593
|
-
});
|
|
551
|
+
const tableFunctionStatistics: VgiService["tableFunctionStatistics"] = async (params) => {
|
|
552
|
+
const innerParams = unwrapRequest(params.request);
|
|
553
|
+
const request = deserializeCardinalityRequest(innerParams);
|
|
554
|
+
const func = registry.get(request.bind_call.function_name, await strippedContext(request.bind_call));
|
|
555
|
+
if (!func.statistics) return { result: null };
|
|
556
|
+
const stats = func.statistics(request);
|
|
557
|
+
if (!stats || stats.length === 0) return { result: null };
|
|
558
|
+
return { result: serializeColumnStatistics(stats) };
|
|
559
|
+
};
|
|
594
560
|
|
|
595
561
|
// --------------------------------------------------------------------------
|
|
596
562
|
// table_function_dynamic_to_string (unary)
|
|
@@ -601,27 +567,32 @@ export function registerFunctionMethods(protocol: Protocol, config: FunctionHand
|
|
|
601
567
|
// intrinsics (Function, Rows Read, Threads). When the function doesn't
|
|
602
568
|
// declare dynamicToString, return empty maps so the C++ side falls back
|
|
603
569
|
// to intrinsics only.
|
|
604
|
-
|
|
605
|
-
params
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
570
|
+
const tableFunctionDynamicToString: VgiService["tableFunctionDynamicToString"] = async (params) => {
|
|
571
|
+
const innerParams = unwrapRequest(params.request);
|
|
572
|
+
const bindCallBytes = toUint8Array(innerParams.bind_call);
|
|
573
|
+
const bindCallBatch = deserializeBatch(bindCallBytes);
|
|
574
|
+
// Single-row bind_call batch -> dict via the codec/canonical path.
|
|
575
|
+
const bindParams = batchToScalarDict(bindCallBatch);
|
|
576
|
+
const bindCall = deserializeBindRequest(bindParams);
|
|
577
|
+
const globalExecutionId = toUint8Array(innerParams.global_execution_id);
|
|
578
|
+
const bindOpaqueData = innerParams.bind_opaque_data
|
|
579
|
+
? toUint8Array(innerParams.bind_opaque_data)
|
|
580
|
+
: null;
|
|
581
|
+
const func = registry.get(bindCall.function_name, await strippedContext(bindCall));
|
|
582
|
+
const map = func.dynamicToString
|
|
583
|
+
? await func.dynamicToString({ bindCall, bindOpaqueData, globalExecutionId })
|
|
584
|
+
: {};
|
|
585
|
+
const keys = Object.keys(map);
|
|
586
|
+
const values = keys.map((k) => map[k] ?? "");
|
|
587
|
+
return wrapResult({ keys, values }, TableFunctionDynamicToStringResultSchema);
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
return {
|
|
591
|
+
bind,
|
|
592
|
+
init,
|
|
593
|
+
tableFunctionCardinality,
|
|
594
|
+
tableFunctionPlan,
|
|
595
|
+
tableFunctionStatistics,
|
|
596
|
+
tableFunctionDynamicToString,
|
|
597
|
+
};
|
|
627
598
|
}
|
|
@@ -19,10 +19,6 @@ export const REQUEST_PARAMS_SCHEMA = schema([
|
|
|
19
19
|
field("request", binary(), false),
|
|
20
20
|
]);
|
|
21
21
|
|
|
22
|
-
export const RESULT_BINARY_NULLABLE_SCHEMA = schema([
|
|
23
|
-
field("result", binary(), true),
|
|
24
|
-
]);
|
|
25
|
-
|
|
26
22
|
/**
|
|
27
23
|
* Unwrap a "request" Binary column: deserialize the inner Arrow IPC batch
|
|
28
24
|
* and return flat columns as a dict (row 0).
|