@query-farm/vgi 0.29.0 → 0.29.1
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/README.md +26 -1
- package/dist/arguments/argument-spec.d.ts +4 -2
- package/dist/catalog/composite.d.ts +13 -13
- package/dist/catalog/descriptors.d.ts +7 -3
- package/dist/catalog/interface.d.ts +67 -40
- package/dist/catalog/read-only.d.ts +32 -15
- package/dist/client/client.d.ts +36 -29
- package/dist/client/types.d.ts +2 -0
- package/dist/client-entry.d.ts +1 -1
- package/dist/client-entry.js +441 -279
- package/dist/client-entry.js.map +12 -11
- package/dist/filter-pushdown/capabilities.d.ts +3 -0
- package/dist/filter-pushdown/deserialize.d.ts +14 -15
- package/dist/filter-pushdown/evaluate.d.ts +14 -44
- package/dist/filter-pushdown/index.d.ts +2 -2
- package/dist/filter-pushdown/types.d.ts +120 -68
- package/dist/functions/aggregate.d.ts +4 -0
- package/dist/functions/registry.d.ts +5 -5
- package/dist/functions/scalar.d.ts +6 -1
- package/dist/functions/table-buffering.d.ts +9 -0
- package/dist/functions/table-in-out.d.ts +13 -0
- package/dist/functions/table.d.ts +9 -2
- package/dist/functions/types.d.ts +22 -2
- package/dist/generated/vgi-client.d.ts +56 -27
- package/dist/generated/vgi-protocol-schemas.d.ts +15 -0
- package/dist/http/fetch.d.ts +7 -1
- package/dist/index.core.d.ts +2 -1
- package/dist/index.js +2317 -1354
- package/dist/index.js.map +43 -40
- package/dist/metadata/types.d.ts +15 -2
- package/dist/protocol/handlers/shared.d.ts +3 -1
- package/dist/protocol/serializers/splits.d.ts +1 -1
- package/dist/protocol/types.d.ts +9 -3
- package/dist/schema-path.d.ts +7 -0
- package/dist/serve-entry.d.ts +22 -1
- package/dist/serve-entry.js +412 -238
- package/dist/serve-entry.js.map +27 -26
- package/dist/split-token.d.ts +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/worker-cf-entry.js +2030 -1201
- package/dist/worker-cf-entry.js.map +40 -36
- package/dist/worker.d.ts +1 -1
- package/package.json +3 -3
- package/src/arguments/argument-spec.ts +12 -4
- package/src/catalog/composite.ts +26 -26
- package/src/catalog/descriptors.ts +12 -3
- package/src/catalog/interface.ts +98 -48
- package/src/catalog/read-only.ts +124 -67
- package/src/client/client.ts +92 -72
- package/src/client/types.ts +2 -0
- package/src/client-entry.ts +2 -1
- package/src/filter-pushdown/capabilities.ts +13 -0
- package/src/filter-pushdown/collector.ts +22 -35
- package/src/filter-pushdown/deserialize.ts +657 -221
- package/src/filter-pushdown/evaluate.ts +410 -499
- package/src/filter-pushdown/index.ts +16 -2
- package/src/filter-pushdown/types.ts +88 -71
- package/src/functions/aggregate.ts +9 -1
- package/src/functions/registry.ts +24 -22
- package/src/functions/scalar.ts +26 -0
- package/src/functions/table-buffering.ts +46 -7
- package/src/functions/table-in-out.ts +187 -30
- package/src/functions/table.ts +58 -20
- package/src/functions/types.ts +24 -1
- package/src/generated/vgi-client.ts +61 -29
- package/src/generated/vgi-protocol-schemas.ts +225 -54
- package/src/http/fetch.ts +12 -0
- package/src/index.core.ts +2 -0
- package/src/metadata/resolve.ts +43 -1
- package/src/metadata/serialize.ts +43 -5
- package/src/metadata/types.ts +17 -1
- package/src/protocol/dispatch.ts +1 -1
- package/src/protocol/handlers/aggregate.ts +13 -7
- package/src/protocol/handlers/catalog/admin.ts +6 -6
- package/src/protocol/handlers/catalog/index_methods.ts +2 -2
- package/src/protocol/handlers/catalog/macro.ts +4 -4
- package/src/protocol/handlers/catalog/shared.ts +2 -0
- package/src/protocol/handlers/catalog/table.ts +16 -16
- package/src/protocol/handlers/catalog/view.ts +5 -5
- package/src/protocol/handlers/function.ts +2 -2
- package/src/protocol/handlers/shared.ts +12 -3
- package/src/protocol/handlers/table-buffering.ts +4 -4
- package/src/protocol/serializers/bind.ts +15 -6
- package/src/protocol/serializers/splits.ts +4 -2
- package/src/protocol/types.ts +9 -3
- package/src/schema-path.ts +25 -0
- package/src/serve-entry.ts +65 -3
- package/src/split-token.ts +2 -2
- package/src/types.ts +9 -0
- package/src/worker.ts +49 -4
package/src/catalog/read-only.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright 2025, 2026 Query Farm LLC - https://query.farm
|
|
2
2
|
// ReadOnlyCatalogInterface: derives catalog from registered functions + descriptors.
|
|
3
3
|
|
|
4
|
-
import { type VgiSchema, schema as schema_, type VgiField, field, type VgiDataType, int64, utf8, binary, bool, nullType, list, isInt } from "../arrow/index.js";
|
|
4
|
+
import { type VgiSchema, schema as schema_, type VgiField, field, type VgiDataType, int64, utf8, binary, bool, nullType, list, isInt, typeSignature } from "../arrow/index.js";
|
|
5
5
|
import {
|
|
6
6
|
CatalogInterface,
|
|
7
7
|
type AttachOpaqueData,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
encodeScanFunctionResult,
|
|
21
21
|
encodeFunctionInfo,
|
|
22
22
|
} from "./interface.js";
|
|
23
|
-
import type
|
|
23
|
+
import { schemaDescriptorPath, type CatalogDescriptor, type SchemaDescriptor, type TableDescriptor, type ViewDescriptor, type MacroDescriptor, type SettingDescriptor, type SecretTypeDescriptor, type ForeignKeyDef, type DefaultValue } from "./descriptors.js";
|
|
24
24
|
import { serializeColumnStatistics } from "../util/statistics.js";
|
|
25
25
|
import type { VgiFunction } from "../functions/types.js";
|
|
26
26
|
import type { FunctionRegistry } from "../functions/registry.js";
|
|
@@ -30,6 +30,8 @@ import { resolveMetadata } from "../metadata/resolve.js";
|
|
|
30
30
|
import { type AttachOptionSpec, validateRequiredAttachOptions } from "./attach-option.js";
|
|
31
31
|
import { FunctionStability, NullHandling, OrderPreservation, DEFAULT_MAX_WORKERS } from "../types.js";
|
|
32
32
|
import { Arguments } from "../arguments/arguments.js";
|
|
33
|
+
import { normalizeSchemaPath, schemaPathDisplay, schemaPathsEqual } from "../schema-path.js";
|
|
34
|
+
import { ForeignKeyInfoSchema } from "../generated/vgi-protocol-schemas.js";
|
|
33
35
|
|
|
34
36
|
export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
35
37
|
private _descriptor: CatalogDescriptor;
|
|
@@ -46,16 +48,39 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
46
48
|
// is declared in more than one schema. Table-backing functions are indexed
|
|
47
49
|
// under their table's schema for the same reason.
|
|
48
50
|
for (const sch of descriptor.schemas ?? []) {
|
|
49
|
-
for (const fn of sch.functions ?? []) registry.registerInSchema(fn, sch
|
|
51
|
+
for (const fn of sch.functions ?? []) registry.registerInSchema(fn, schemaDescriptorPath(sch), descriptor.name);
|
|
50
52
|
for (const tbl of sch.tables ?? []) {
|
|
51
53
|
for (const key of ["function", "insertFunction", "updateFunction", "deleteFunction"] as const) {
|
|
52
54
|
const fn = (tbl as any)[key];
|
|
53
|
-
if (fn) registry.registerInSchema(fn, sch
|
|
55
|
+
if (fn) registry.registerInSchema(fn, schemaDescriptorPath(sch), descriptor.name);
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Which schema declares `fn`, by object identity, or null if no schema does.
|
|
63
|
+
*
|
|
64
|
+
* A table's backing function is NOT necessarily declared in the table's own
|
|
65
|
+
* schema — `data.large_sequence` scans via `sequence`, which the example
|
|
66
|
+
* catalog declares in `main`. Populating `ScanFunctionResult.schemaPath` /
|
|
67
|
+
* `ScanBranch.schemaPath` (protocol 2.0.0) correctly needs this reverse
|
|
68
|
+
* lookup, not the `schemaPath` parameter of the call being served.
|
|
69
|
+
*
|
|
70
|
+
* Identity, not name: the whole point of the field is to disambiguate one
|
|
71
|
+
* name declared in several schemas, so a name match would answer the very
|
|
72
|
+
* question being asked. Null when the function is deliberately unadvertised
|
|
73
|
+
* (`cache_multicol`) — it has no schema to report and the client falls back
|
|
74
|
+
* to its own heuristic, which is exactly the pre-1.5.0 behaviour.
|
|
75
|
+
* Mirrors vgi-python's `ReadOnlyCatalogInterface._schema_for_function`.
|
|
76
|
+
*/
|
|
77
|
+
private _schemaForFunction(fn: VgiFunction): string[] | null {
|
|
78
|
+
for (const sch of this._descriptor.schemas ?? []) {
|
|
79
|
+
if ((sch.functions ?? []).includes(fn)) return schemaDescriptorPath(sch);
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
59
84
|
catalogs(): string[] {
|
|
60
85
|
return [this._descriptor.name];
|
|
61
86
|
}
|
|
@@ -168,7 +193,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
168
193
|
schemas(attachOpaqueData: AttachOpaqueData, transactionOpaqueData?: TransactionOpaqueData): SchemaInfo[] {
|
|
169
194
|
return this._descriptor.schemas.map((s) => ({
|
|
170
195
|
attach_opaque_data: attachOpaqueData,
|
|
171
|
-
|
|
196
|
+
path: schemaDescriptorPath(s),
|
|
172
197
|
comment: s.comment ?? null,
|
|
173
198
|
tags: s.tags ?? {},
|
|
174
199
|
estimated_object_count: this._estimatedObjectCount(s),
|
|
@@ -199,14 +224,14 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
199
224
|
|
|
200
225
|
override schemaGet(
|
|
201
226
|
attachOpaqueData: AttachOpaqueData,
|
|
202
|
-
|
|
227
|
+
path: string[],
|
|
203
228
|
transactionOpaqueData?: TransactionOpaqueData
|
|
204
229
|
): SchemaInfo | null {
|
|
205
|
-
const desc = this._descriptor.schemas.find((s) => s
|
|
230
|
+
const desc = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), path));
|
|
206
231
|
if (!desc) return null;
|
|
207
232
|
return {
|
|
208
233
|
attach_opaque_data: attachOpaqueData,
|
|
209
|
-
|
|
234
|
+
path: schemaDescriptorPath(desc),
|
|
210
235
|
comment: desc.comment ?? null,
|
|
211
236
|
tags: desc.tags ?? {},
|
|
212
237
|
estimated_object_count: this._estimatedObjectCount(desc),
|
|
@@ -215,10 +240,10 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
215
240
|
|
|
216
241
|
override async schemaContentsTables(
|
|
217
242
|
attachOpaqueData: AttachOpaqueData,
|
|
218
|
-
|
|
243
|
+
path: string[],
|
|
219
244
|
transactionOpaqueData?: TransactionOpaqueData
|
|
220
245
|
): Promise<TableInfo[]> {
|
|
221
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
246
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), path));
|
|
222
247
|
if (!schema || !schema.tables) return [];
|
|
223
248
|
|
|
224
249
|
return await Promise.all(schema.tables.map(async (t) => {
|
|
@@ -273,7 +298,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
273
298
|
const notNullIndices = resolveIndices(colSchema, t.notNull);
|
|
274
299
|
const uniqueIndices = resolveIndexGroups(colSchema, t.unique);
|
|
275
300
|
const pkIndices = resolveIndexGroups(colSchema, t.primaryKey);
|
|
276
|
-
const fkBytes = serializeForeignKeys(t.foreignKey,
|
|
301
|
+
const fkBytes = serializeForeignKeys(t.foreignKey, path);
|
|
277
302
|
|
|
278
303
|
// Validate required_filters: an AND (outer list) of OR-groups (inner
|
|
279
304
|
// lists). Each OR-group must be non-empty, and the leading dotted segment
|
|
@@ -311,7 +336,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
311
336
|
comment: t.comment ?? null,
|
|
312
337
|
tags: t.tags ?? {},
|
|
313
338
|
name: t.name,
|
|
314
|
-
|
|
339
|
+
schema_path: normalizeSchemaPath(path),
|
|
315
340
|
columns,
|
|
316
341
|
not_null_constraints: notNullIndices,
|
|
317
342
|
unique_constraints: uniqueIndices,
|
|
@@ -329,7 +354,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
329
354
|
// shadow the per-call dispatch and prevent workers from varying
|
|
330
355
|
// the scan function (e.g. version-resolved time-travel).
|
|
331
356
|
scan_function: t.function && !t.columns
|
|
332
|
-
? inlineScanFunction(t.function, t.arguments)
|
|
357
|
+
? inlineScanFunction(t.function, t.arguments, this._schemaForFunction(t.function))
|
|
333
358
|
: null,
|
|
334
359
|
insert_function: null,
|
|
335
360
|
update_function: null,
|
|
@@ -347,11 +372,11 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
347
372
|
|
|
348
373
|
override tableColumnStatisticsGet(
|
|
349
374
|
attachOpaqueData: AttachOpaqueData,
|
|
350
|
-
|
|
375
|
+
schemaPath: string[],
|
|
351
376
|
name: string,
|
|
352
377
|
transactionOpaqueData?: TransactionOpaqueData,
|
|
353
378
|
): { bytes: Uint8Array; cacheMaxAgeSeconds: number | null } | null {
|
|
354
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
379
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), schemaPath));
|
|
355
380
|
if (!schema || !schema.tables) return null;
|
|
356
381
|
const table = schema.tables.find((t) => t.name === name);
|
|
357
382
|
if (!table || !table.statistics) return null;
|
|
@@ -365,17 +390,17 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
365
390
|
|
|
366
391
|
override schemaContentsViews(
|
|
367
392
|
attachOpaqueData: AttachOpaqueData,
|
|
368
|
-
|
|
393
|
+
path: string[],
|
|
369
394
|
transactionOpaqueData?: TransactionOpaqueData
|
|
370
395
|
): ViewInfo[] {
|
|
371
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
396
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), path));
|
|
372
397
|
if (!schema || !schema.views) return [];
|
|
373
398
|
|
|
374
399
|
return schema.views.map((v) => ({
|
|
375
400
|
comment: v.comment ?? null,
|
|
376
401
|
tags: v.tags ?? {},
|
|
377
402
|
name: v.name,
|
|
378
|
-
|
|
403
|
+
schema_path: normalizeSchemaPath(path),
|
|
379
404
|
definition: v.definition,
|
|
380
405
|
column_comments: v.columnComments ?? {},
|
|
381
406
|
}));
|
|
@@ -383,11 +408,11 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
383
408
|
|
|
384
409
|
override schemaContentsFunctions(
|
|
385
410
|
attachOpaqueData: AttachOpaqueData,
|
|
386
|
-
|
|
411
|
+
path: string[],
|
|
387
412
|
type: string,
|
|
388
413
|
transactionOpaqueData?: TransactionOpaqueData
|
|
389
414
|
): FunctionInfo[] {
|
|
390
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
415
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), path));
|
|
391
416
|
if (!schema || !schema.functions) return [];
|
|
392
417
|
|
|
393
418
|
return schema.functions
|
|
@@ -401,21 +426,44 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
401
426
|
if (t === "AGGREGATE_FUNCTION") return (f.kind as string) === "aggregate";
|
|
402
427
|
return true;
|
|
403
428
|
})
|
|
404
|
-
.map((f) => this._functionToInfo(f,
|
|
429
|
+
.map((f) => this._functionToInfo(f, path));
|
|
405
430
|
}
|
|
406
431
|
|
|
407
432
|
/**
|
|
408
433
|
* Build the wire `FunctionInfo` for one declared function.
|
|
409
434
|
*
|
|
410
|
-
* `
|
|
435
|
+
* `schemaPath` is the schema the function actually lives in — it is the
|
|
411
436
|
* bind-dispatch key, so a function advertised through more than one route
|
|
412
437
|
* (schema contents *and* `global_functions`) must carry the same value in
|
|
413
438
|
* both. Shared by `schemaContentsFunctions` and `_globalFunctionInfos`.
|
|
414
439
|
*/
|
|
415
|
-
private _functionToInfo(f: VgiFunction,
|
|
440
|
+
private _functionToInfo(f: VgiFunction, path: string[]): FunctionInfo {
|
|
416
441
|
const meta = resolveMetadata(f);
|
|
417
442
|
const argSchema = argumentSpecsToSchema(f.argumentSpecs);
|
|
418
443
|
const argBytes = serializeSchema(argSchema);
|
|
444
|
+
const defaults = f.meta.parameterDefaultValues ?? null;
|
|
445
|
+
if (defaults) {
|
|
446
|
+
if (defaults.numRows !== 1) {
|
|
447
|
+
throw new Error(`parameterDefaultValues must contain exactly one row, got ${defaults.numRows}`);
|
|
448
|
+
}
|
|
449
|
+
let argumentIndex = 0;
|
|
450
|
+
for (const defaultField of defaults.schema.fields) {
|
|
451
|
+
while (argumentIndex < argSchema.fields.length && argSchema.fields[argumentIndex].name !== defaultField.name) {
|
|
452
|
+
argumentIndex++;
|
|
453
|
+
}
|
|
454
|
+
if (argumentIndex === argSchema.fields.length) {
|
|
455
|
+
throw new Error(`parameterDefaultValues field '${defaultField.name}' is not in argument signature order`);
|
|
456
|
+
}
|
|
457
|
+
const argumentField = argSchema.fields[argumentIndex];
|
|
458
|
+
if (typeSignature(argumentField.type) !== typeSignature(defaultField.type)) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`parameterDefaultValues field '${defaultField.name}' has type ${typeSignature(defaultField.type)}, ` +
|
|
461
|
+
`expected ${typeSignature(argumentField.type)}`,
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
argumentIndex++;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
419
467
|
|
|
420
468
|
// Use the function's default output schema if available,
|
|
421
469
|
// otherwise empty schema (table functions determine output at bind time)
|
|
@@ -443,12 +491,16 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
443
491
|
comment: null,
|
|
444
492
|
tags,
|
|
445
493
|
name: f.meta.name,
|
|
446
|
-
|
|
494
|
+
schema_path: normalizeSchemaPath(path),
|
|
447
495
|
function_type: meta.functionType.toUpperCase() as "SCALAR" | "TABLE" | "TABLE_BUFFERING" | "AGGREGATE",
|
|
448
496
|
arguments: argBytes,
|
|
449
497
|
output_schema: outputSchemaBytes,
|
|
498
|
+
parameter_default_values: defaults
|
|
499
|
+
? serializeBatch(defaults)
|
|
500
|
+
: null,
|
|
450
501
|
stability: meta.stability as any,
|
|
451
502
|
null_handling: meta.nullHandling as any,
|
|
503
|
+
argument_monotonicity: meta.argumentMonotonicity,
|
|
452
504
|
description: meta.description,
|
|
453
505
|
examples: meta.examples.map((e) => ({
|
|
454
506
|
sql: e.sql,
|
|
@@ -463,7 +515,16 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
463
515
|
// The C++ extension only honours this for rowid-bearing tables.
|
|
464
516
|
late_materialization:
|
|
465
517
|
(f.kind as string) === "table" && f.meta.lateMaterialization === true ? true : null,
|
|
466
|
-
|
|
518
|
+
filter_semantic_profiles: meta.filterSemanticProfiles,
|
|
519
|
+
additional_filter_functions: meta.additionalFilterFunctions.map((value) => ({
|
|
520
|
+
namespace: value.namespace, name: value.name, version: BigInt(value.version) as any,
|
|
521
|
+
})),
|
|
522
|
+
runtime_filter_algorithms: meta.runtimeFilterAlgorithms.map((value) => ({
|
|
523
|
+
namespace: value.namespace, name: value.name, version: BigInt(value.version) as any,
|
|
524
|
+
})),
|
|
525
|
+
filter_evaluation_contexts: meta.filterEvaluationContexts.map((value) => ({
|
|
526
|
+
profile: value.profile, provider_fingerprint: value.providerFingerprint,
|
|
527
|
+
})),
|
|
467
528
|
order_preservation: meta.preservesOrder as any,
|
|
468
529
|
max_workers: meta.maxWorkers ?? DEFAULT_MAX_WORKERS,
|
|
469
530
|
// Opt-in features the read-only catalog's functions do not use; the
|
|
@@ -533,7 +594,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
533
594
|
/**
|
|
534
595
|
* Build `FunctionInfo` records for `CatalogDescriptor.globalFunctions`.
|
|
535
596
|
*
|
|
536
|
-
* Each entry's `
|
|
597
|
+
* Each entry's `schema_path` is the schema it actually lives in — the
|
|
537
598
|
* globally visible name is derived client-side from
|
|
538
599
|
* `global_function_prefix`, so `name` stays unprefixed here. An entry not
|
|
539
600
|
* declared in any schema is a descriptor bug, so it throws rather than
|
|
@@ -544,32 +605,32 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
544
605
|
const globals = this._descriptor.globalFunctions ?? [];
|
|
545
606
|
if (globals.length === 0) return [];
|
|
546
607
|
|
|
547
|
-
const schemaOf = new Map<VgiFunction, string>();
|
|
608
|
+
const schemaOf = new Map<VgiFunction, string[]>();
|
|
548
609
|
for (const sch of this._descriptor.schemas) {
|
|
549
610
|
for (const fn of sch.functions ?? []) {
|
|
550
|
-
if (!schemaOf.has(fn)) schemaOf.set(fn, sch
|
|
611
|
+
if (!schemaOf.has(fn)) schemaOf.set(fn, schemaDescriptorPath(sch));
|
|
551
612
|
}
|
|
552
613
|
}
|
|
553
614
|
|
|
554
615
|
return globals.map((fn) => {
|
|
555
|
-
const
|
|
556
|
-
if (
|
|
616
|
+
const schemaPath = schemaOf.get(fn);
|
|
617
|
+
if (schemaPath === undefined) {
|
|
557
618
|
throw new Error(
|
|
558
619
|
`Catalog '${this._descriptor.name}': '${fn.meta.name}' is listed in ` +
|
|
559
620
|
`globalFunctions but is not declared in any schema.`,
|
|
560
621
|
);
|
|
561
622
|
}
|
|
562
|
-
return this._functionToInfo(fn,
|
|
623
|
+
return this._functionToInfo(fn, schemaPath);
|
|
563
624
|
});
|
|
564
625
|
}
|
|
565
626
|
|
|
566
627
|
override schemaContentsMacros(
|
|
567
628
|
attachOpaqueData: AttachOpaqueData,
|
|
568
|
-
|
|
629
|
+
path: string[],
|
|
569
630
|
type: string,
|
|
570
631
|
transactionOpaqueData?: TransactionOpaqueData
|
|
571
632
|
): MacroInfo[] {
|
|
572
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
633
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), path));
|
|
573
634
|
if (!schema || !schema.macros) return [];
|
|
574
635
|
|
|
575
636
|
return schema.macros
|
|
@@ -584,7 +645,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
584
645
|
comment: m.comment ?? null,
|
|
585
646
|
tags: m.tags ?? {},
|
|
586
647
|
name: m.name,
|
|
587
|
-
|
|
648
|
+
schema_path: normalizeSchemaPath(path),
|
|
588
649
|
macro_type: (m.macroType === "scalar" ? "SCALAR" : "TABLE") as "SCALAR" | "TABLE",
|
|
589
650
|
parameters: m.parameters,
|
|
590
651
|
parameter_default_values: m.parameterDefaultValues ?? new Uint8Array(0),
|
|
@@ -601,16 +662,16 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
601
662
|
|
|
602
663
|
override schemaContentsIndexes(
|
|
603
664
|
attachOpaqueData: AttachOpaqueData,
|
|
604
|
-
|
|
665
|
+
path: string[],
|
|
605
666
|
transactionOpaqueData?: TransactionOpaqueData
|
|
606
667
|
): IndexInfo[] {
|
|
607
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
668
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), path));
|
|
608
669
|
if (!schema || !schema.indexes) return [];
|
|
609
670
|
return schema.indexes.map((idx) => ({
|
|
610
671
|
comment: idx.comment ?? null,
|
|
611
672
|
tags: idx.tags ?? {},
|
|
612
673
|
name: idx.name,
|
|
613
|
-
|
|
674
|
+
schema_path: normalizeSchemaPath(path),
|
|
614
675
|
table_name: idx.tableName,
|
|
615
676
|
index_type: idx.indexType ?? "ART",
|
|
616
677
|
constraint_type: (idx.constraintType ?? "NONE") as IndexConstraintType,
|
|
@@ -621,29 +682,29 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
621
682
|
|
|
622
683
|
override indexGet(
|
|
623
684
|
attachOpaqueData: AttachOpaqueData,
|
|
624
|
-
|
|
685
|
+
schemaPath: string[],
|
|
625
686
|
name: string,
|
|
626
687
|
transactionOpaqueData?: TransactionOpaqueData
|
|
627
688
|
): IndexInfo | null {
|
|
628
|
-
const all = this.schemaContentsIndexes(attachOpaqueData,
|
|
689
|
+
const all = this.schemaContentsIndexes(attachOpaqueData, schemaPath, transactionOpaqueData);
|
|
629
690
|
return all.find((i) => i.name === name) ?? null;
|
|
630
691
|
}
|
|
631
692
|
|
|
632
693
|
override macroGet(
|
|
633
694
|
attachOpaqueData: AttachOpaqueData,
|
|
634
|
-
|
|
695
|
+
schemaPath: string[],
|
|
635
696
|
name: string,
|
|
636
697
|
transactionOpaqueData?: TransactionOpaqueData
|
|
637
698
|
): MacroInfo | null {
|
|
638
|
-
const macros = this.schemaContentsMacros(attachOpaqueData,
|
|
639
|
-
const tableMacros = this.schemaContentsMacros(attachOpaqueData,
|
|
699
|
+
const macros = this.schemaContentsMacros(attachOpaqueData, schemaPath, "SCALAR_MACRO", transactionOpaqueData);
|
|
700
|
+
const tableMacros = this.schemaContentsMacros(attachOpaqueData, schemaPath, "TABLE_MACRO", transactionOpaqueData);
|
|
640
701
|
const all = [...macros, ...tableMacros];
|
|
641
702
|
return all.find((m) => m.name.toLowerCase() === name.toLowerCase()) ?? null;
|
|
642
703
|
}
|
|
643
704
|
|
|
644
705
|
override tableScanFunctionGet(
|
|
645
706
|
attachOpaqueData: AttachOpaqueData,
|
|
646
|
-
|
|
707
|
+
schemaPath: string[],
|
|
647
708
|
name: string,
|
|
648
709
|
atUnit?: string,
|
|
649
710
|
atValue?: string,
|
|
@@ -652,18 +713,18 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
652
713
|
validateAtParams(atUnit, atValue);
|
|
653
714
|
|
|
654
715
|
// Find the table descriptor
|
|
655
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
716
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), schemaPath));
|
|
656
717
|
if (!schema || !schema.tables) {
|
|
657
|
-
throw new Error(`Table '${name}' not found in schema
|
|
718
|
+
throw new Error(`Table '${name}' not found in schema ${schemaPathDisplay(schemaPath)}`);
|
|
658
719
|
}
|
|
659
720
|
const table = schema.tables.find((t) => t.name === name);
|
|
660
721
|
if (!table) {
|
|
661
|
-
throw new Error(`Table '${name}' not found in schema
|
|
722
|
+
throw new Error(`Table '${name}' not found in schema ${schemaPathDisplay(schemaPath)}`);
|
|
662
723
|
}
|
|
663
724
|
|
|
664
725
|
// Reject AT clause on tables that don't support time travel
|
|
665
726
|
if (atUnit && !table.supportsTimeTravel) {
|
|
666
|
-
throw new Error(`Table
|
|
727
|
+
throw new Error(`Table ${schemaPathDisplay(schemaPath)}.${name} does not support time travel queries`);
|
|
667
728
|
}
|
|
668
729
|
|
|
669
730
|
if (table.function) {
|
|
@@ -675,6 +736,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
675
736
|
function_name: table.function.meta.name,
|
|
676
737
|
arguments: argBytes,
|
|
677
738
|
required_extensions: [],
|
|
739
|
+
schema_path: this._schemaForFunction(table.function),
|
|
678
740
|
};
|
|
679
741
|
}
|
|
680
742
|
|
|
@@ -683,7 +745,7 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
683
745
|
|
|
684
746
|
override async tableGet(
|
|
685
747
|
attachOpaqueData: AttachOpaqueData,
|
|
686
|
-
|
|
748
|
+
schemaPath: string[],
|
|
687
749
|
name: string,
|
|
688
750
|
atUnit?: string,
|
|
689
751
|
atValue?: string,
|
|
@@ -692,25 +754,25 @@ export class ReadOnlyCatalogInterface extends CatalogInterface {
|
|
|
692
754
|
validateAtParams(atUnit, atValue);
|
|
693
755
|
|
|
694
756
|
// Find the table descriptor to check supports_time_travel
|
|
695
|
-
const schema = this._descriptor.schemas.find((s) => s
|
|
757
|
+
const schema = this._descriptor.schemas.find((s) => schemaPathsEqual(schemaDescriptorPath(s), schemaPath));
|
|
696
758
|
if (schema?.tables) {
|
|
697
759
|
const tableDesc = schema.tables.find((t) => t.name === name);
|
|
698
760
|
if (tableDesc && atUnit && !tableDesc.supportsTimeTravel) {
|
|
699
|
-
throw new Error(`Table
|
|
761
|
+
throw new Error(`Table ${schemaPathDisplay(schemaPath)}.${name} does not support time travel queries`);
|
|
700
762
|
}
|
|
701
763
|
}
|
|
702
764
|
|
|
703
|
-
const tables = await this.schemaContentsTables(attachOpaqueData,
|
|
765
|
+
const tables = await this.schemaContentsTables(attachOpaqueData, schemaPath, transactionOpaqueData);
|
|
704
766
|
return tables.find((t) => t.name === name) ?? null;
|
|
705
767
|
}
|
|
706
768
|
|
|
707
769
|
override viewGet(
|
|
708
770
|
attachOpaqueData: AttachOpaqueData,
|
|
709
|
-
|
|
771
|
+
schemaPath: string[],
|
|
710
772
|
name: string,
|
|
711
773
|
transactionOpaqueData?: TransactionOpaqueData
|
|
712
774
|
): ViewInfo | null {
|
|
713
|
-
const views = this.schemaContentsViews(attachOpaqueData,
|
|
775
|
+
const views = this.schemaContentsViews(attachOpaqueData, schemaPath, transactionOpaqueData);
|
|
714
776
|
return views.find((v) => v.name === name) ?? null;
|
|
715
777
|
}
|
|
716
778
|
|
|
@@ -786,14 +848,7 @@ function resolveIndexGroups(schema: VgiSchema, groups?: string[][]): number[][]
|
|
|
786
848
|
return groups.map((group) => resolveIndices(schema, group));
|
|
787
849
|
}
|
|
788
850
|
|
|
789
|
-
|
|
790
|
-
field("fk_columns", list(field("item", utf8(), true)), false),
|
|
791
|
-
field("pk_columns", list(field("item", utf8(), true)), false),
|
|
792
|
-
field("referenced_table", utf8(), false),
|
|
793
|
-
field("referenced_schema", utf8(), false),
|
|
794
|
-
]);
|
|
795
|
-
|
|
796
|
-
function serializeForeignKeys(fks?: ForeignKeyDef[], schemaName?: string): Uint8Array[] {
|
|
851
|
+
function serializeForeignKeys(fks?: ForeignKeyDef[], schemaPath?: string[]): Uint8Array[] {
|
|
797
852
|
if (!fks || fks.length === 0) return [];
|
|
798
853
|
return fks.map((fk) =>
|
|
799
854
|
serializeBatch(
|
|
@@ -802,9 +857,9 @@ function serializeForeignKeys(fks?: ForeignKeyDef[], schemaName?: string): Uint8
|
|
|
802
857
|
fk_columns: [fk.columns],
|
|
803
858
|
pk_columns: [fk.referencedColumns],
|
|
804
859
|
referenced_table: [fk.referencedTable],
|
|
805
|
-
|
|
860
|
+
referenced_schema_path: [fk.referencedSchemaPath ?? schemaPath ?? ["main"]],
|
|
806
861
|
},
|
|
807
|
-
|
|
862
|
+
ForeignKeyInfoSchema
|
|
808
863
|
)
|
|
809
864
|
)
|
|
810
865
|
);
|
|
@@ -1001,14 +1056,16 @@ function inferScalarType(val: any): VgiDataType {
|
|
|
1001
1056
|
|
|
1002
1057
|
// Build the inlined `scan_function` payload for a function-backed table.
|
|
1003
1058
|
// Mirrors python's _inline_function_result: a serialized one-row batch of
|
|
1004
|
-
// ScanFunctionResultSchema (function_name, arguments, required_extensions
|
|
1005
|
-
// The C++ extension uses these bytes verbatim and skips the
|
|
1006
|
-
// catalog_table_scan_function_get RPC
|
|
1059
|
+
// ScanFunctionResultSchema (function_name, arguments, required_extensions,
|
|
1060
|
+
// schema_path). The C++ extension uses these bytes verbatim and skips the
|
|
1061
|
+
// catalog_table_scan_function_get RPC, so the schema this inlines has to agree
|
|
1062
|
+
// with what that RPC would have answered.
|
|
1007
1063
|
function inlineScanFunction(
|
|
1008
1064
|
func: VgiFunction,
|
|
1009
1065
|
args: Arguments | undefined,
|
|
1066
|
+
schemaPath: string[] | null,
|
|
1010
1067
|
): Uint8Array {
|
|
1011
1068
|
const argSchema = argumentSpecsToSchema(func.argumentSpecs);
|
|
1012
1069
|
const argBytes = serializeArgsBatch(args ?? new Arguments(), argSchema);
|
|
1013
|
-
return encodeScanFunctionResult(func.meta.name, argBytes);
|
|
1070
|
+
return encodeScanFunctionResult(func.meta.name, argBytes, [], schemaPath);
|
|
1014
1071
|
}
|