Skip to content
Query.Farm
Talk with Us

Protocol

On this page

The VGI application-level request, result, and metadata contracts.

source
public sealed class AggregateBindRequest

Description

The aggregate_bind RPC's packed request — mirrors BindRequest but scoped to what an aggregate actually needs (no FunctionType / copy_from / copy_to /etc., which only the shared bind RPC's scalar/table/table-in-out paths use). PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches the C++ extension's generated AggregateBindRequestSchema exactly: function_name, arguments, input_schema, settings, secrets, attach_opaque_data, schema_name.

Public members

public byte[] Arguments { get; set; }

Bind-time constant ("ConstParam") values only — embedded IPC struct positional_<i> , re-indexed sequentially over JUST the const positions (see Internal.TableArgCodec's doc comment and the C++ extension's BuildAggregateBindRequest : const values are collected in declaration order, not by their original argument index).

public byte[]? AttachOpaqueData { get; set; }
public byte[]? InputSchema { get; set; }

Schema-only IPC bytes describing the NON-const ("Param") input columns — the shape every aggregate_update call's input_batch carries (plus the synthetic __vgi_group_id column prepended by the C++ side).

public byte[]? Secrets { get; set; }
public byte[]? Settings { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class AggregateBindResult

Description

The aggregate_bind RPC's packed result (the dataclass embedded under the method's own auto-wrapped result field) — property order matches the C++ extension's generated AggregateBindResultSchema : output_schema, execution_id.

Public members

public byte[] ExecutionId { get; set; }

Scopes every subsequent aggregate_update / _combine / _finalize / _destructor call for this bound aggregate — minted fresh per bind, shared by every parallel worker connection DuckDB spawns for the one query.

public byte[] OutputSchema { get; set; }

Schema-only IPC bytes for the (possibly dynamically-resolved, for an ANY-typed return) single-field output schema.

source
public sealed class AggregateCombineRequest

Description

The aggregate_combine RPC's packed request — merges parallel-worker (or window segment-tree) partial state. MergeBatch's schema is always (source_group_id: int64, target_group_id: int64) : for each row, the accumulator state under source_group_id should be folded INTO the one under target_group_id . A source_group_id may repeat (one leaf state feeding several targets, e.g. a window segment tree) — it is NEVER implicitly deleted by combine; only aggregate_destructor frees state. PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches AggregateCombineRequestSchema exactly: function_name, execution_id, merge_batch, attach_opaque_data, schema_name.

Public members

public RecordBatch MergeBatch { get; set; }
public byte[] ExecutionId { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class AggregateCombineResult

Description

The aggregate_combine RPC's packed result — no fields (matches AggregateCombineResultSchema ). See AggregateUpdateResult's doc comment.

source
public sealed class AggregateDestructorRequest

Description

The aggregate_destructor RPC's packed request — best-effort cleanup fired once the C++ side has determined every DuckDB aggregate state it ever created for this bind has been torn down (see VgiAggregateDestroy 's destroy_counter / group_id_counter bookkeeping). GroupIdsBatch carries a single PLACEHOLDER row ( group_id=0 , not a real group) — this is a signal to free EVERYTHING this ExecutionId ever stored, not a per-group request; Internal.FunctionStorage.DeleteAll is the correct (and only correct) response. PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches AggregateDestructorRequestSchema exactly: function_name, execution_id, group_ids_batch, attach_opaque_data, schema_name.

Public members

public RecordBatch GroupIdsBatch { get; set; }
public byte[] ExecutionId { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class AggregateDestructorResult

Description

The aggregate_destructor RPC's packed result — no fields (matches AggregateDestructorResultSchema ). See AggregateUpdateResult's doc comment.

source
public enum AggregateDistinctDependent

Description

Wire values: NOT_DISTINCT_DEPENDENT, DISTINCT_DEPENDENT. NotDistinctDependent is the default (first, value 0) member — matches this non-nullable field's C++-side default.

source
public sealed class AggregateFinalizeRequest

Description

The aggregate_finalize RPC's packed request — produces one output row per requested group id. GroupIdsBatch's schema is always (group_id: int64) . A group id that never appeared in any aggregate_update / _combine call for this execution (e.g. an empty input table, or a group whose only rows were all-NULL under DEFAULT null handling) is legitimate — the resolved function must decide what "no accumulated state" means for its own result (NULL for SUM, 0 for COUNT, etc.). PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches AggregateFinalizeRequestSchema exactly: function_name, execution_id, group_ids_batch, output_schema, attach_opaque_data, schema_name.

Public members

public RecordBatch GroupIdsBatch { get; set; }
public byte[] ExecutionId { get; set; }
public byte[] OutputSchema { get; set; }

Schema-only IPC bytes for the single-field result column — the SAME resolved schema aggregate_bind returned (echoed back rather than re-derived, since a dynamic/ANY return type was only resolvable once, at bind time).

public byte[]? AttachOpaqueData { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class AggregateFinalizeResult

Description

The aggregate_finalize RPC's packed result — property order matches the C++ extension's generated AggregateFinalizeResultSchema : result_batch (its only field).

Public members

public RecordBatch ResultBatch { get; set; }

One column (matching AggregateFinalizeRequest.OutputSchema), one row per AggregateFinalizeRequest.GroupIdsBatch row, same order.

source
public enum AggregateOrderDependent

Description

Wire values: NOT_ORDER_DEPENDENT, ORDER_DEPENDENT. NotOrderDependent is the default (first, value 0) member — matches this non-nullable field's C++-side default.

source
public sealed class AggregateUpdateRequest

Description

The aggregate_update RPC's packed request — one call per DuckDB-side batch of rows being folded into per-group accumulator state. InputBatch's schema is always [__vgi_group_id: int64, …the aggregate's non-const Param columns, in declaration order] — the C++ extension assigns each DuckDB aggregate state a fresh group_id (monotonic, scoped to the whole bind) the first time it's touched by update/combine/finalize. PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches AggregateUpdateRequestSchema exactly: function_name, execution_id, input_batch, attach_opaque_data, schema_name.

Public members

public RecordBatch InputBatch { get; set; }
public byte[] ExecutionId { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class AggregateUpdateResult

Description

The aggregate_update RPC's packed result — no fields (matches the C++ extension's generated AggregateUpdateResultSchema , an empty schema); still wrapped in the standard {result: binary} outer envelope like every other unary RPC result.

source
public sealed class BindRequest

Description

The bind RPC's packed request — also embedded a SECOND level deep inside InitRequest.BindCall (see that property's doc comment and Internal.EmbeddedIpc). PROPERTY DECLARATION ORDER IS LOAD-BEARING: this type is decoded positionally against the incoming Arrow batch's columns (by index, not by looking up column names) — it must match the C++ extension's BuildBindRequest field order EXACTLY: function_name, arguments, function_type, input_schema, settings, secrets, attach_opaque_data, transaction_opaque_data, resolved_secrets_provided, at_unit, at_value, copy_from, copy_to, schema_name. See vgi_rpc_types.cpp 's own comment on this exact historical bug.

Public members

public CopyFromContext? CopyFrom { get; set; }
public CopyToContext? CopyTo { get; set; }
public FunctionType FunctionType { get; set; }
public bool ResolvedSecretsProvided { get; set; }
public byte[] Arguments { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public byte[]? InputSchema { get; set; }
public byte[]? Secrets { get; set; }
public byte[]? Settings { get; set; }
public byte[]? TransactionOpaqueData { get; set; }
public string FunctionName { get; set; }
public string? AtUnit { get; set; }
public string? AtValue { get; set; }
public string? SchemaName { get; set; }
source
public sealed class BindResponse

Description

The bind RPC's unary result. The C++ extension validates this type's embedded-IPC schema with STRICT arrow::Schema::Equals (field count, order, name, type, and nullability all must match exactly) against its own generated BindResultSchema() — so property declaration order matters here too, even though individual field reads on the C++ side are by name.

Public members

public List<string> LookupNames { get; set; }
public List<string> LookupScopes { get; set; }
public List<string> LookupSecretTypes { get; set; }
public byte[] OutputSchema { get; set; }

Serialized (schema-only, no row) Arrow IPC bytes describing the function's return value: one field, conventionally named "result".

public byte[]? OpaqueData { get; set; }
source
public sealed class CatalogAttachRequest

Description

The catalog_attach RPC's packed request. Property order matches the C++ extension's BuildCatalogAttachRequest field order: name, options, data_version_spec, implementation_version, client_capabilities.

Public members

public byte[]? ClientCapabilities { get; set; }
public byte[]? Options { get; set; }
public string Name { get; set; }
public string? DataVersionSpec { get; set; }
public string? ImplementationVersion { get; set; }
source
public sealed class CatalogAttachResult

Description

The catalog_attach RPC's unary result. The C++ extension validates this type's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated CatalogAttachResultSchema() — property declaration order matters and must match that 17-field schema exactly, even though individual reads on the C++ side are by name (most via .value_or(default) , tolerant of a missing/empty value but NOT of a missing/wrongly-typed column).

Public members

public Dictionary<string, string> Tags { get; set; }
public List<byte[]> AttachCatalogs { get; set; }

Each element a serialized companion-catalog descriptor — empty for M1.

public List<byte[]> GlobalFunctions { get; set; }

Each element a serialized FunctionInfo (protocol 1.3.0+ globally- published functions) — empty for M1.

public List<byte[]> SecretTypes { get; set; }

Each element a serialized SecretTypeSpec — empty for M1.

public List<byte[]> Settings { get; set; }

Each element a serialized Setting — empty for M1 (no settings surface yet).

public bool AttachOpaqueDataRequired { get; set; }
public bool CatalogVersionFrozen { get; set; }
public bool SupportsColumnStatistics { get; set; }
public bool SupportsTimeTravel { get; set; }
public bool SupportsTransactions { get; set; }
public byte[] AttachOpaqueData { get; set; }
public long CatalogVersion { get; set; }
public string DefaultSchema { get; set; }
public string GlobalFunctionPrefix { get; set; }
public string? Comment { get; set; }
public string? ResolvedDataVersion { get; set; }
public string? ResolvedImplementationVersion { get; set; }
source
public sealed class CatalogInfo

Description

One item of a catalog_catalogs ItemsResponse — the pre- ATTACH discovery surface vgi_catalogs('<worker location>') reads. The C++ extension validates this type's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated CatalogInfoSchema() — property declaration order matters and must match that schema exactly: name, implementation_version, data_version_spec, attach_option_specs, releases, source_url.

Public members

public List<CatalogRelease> Releases { get; set; }
public List<byte[]> AttachOptionSpecs { get; set; }

Each element a serialized attach-time option spec (same wire shape as a SettingSpec) — empty when this catalog declares none.

public string Name { get; set; }
public string? DataVersionSpec { get; set; }
public string? ImplementationVersion { get; set; }
public string? SourceUrl { get; set; }
source
public sealed class CatalogRelease

Description

Nested struct inside CatalogInfo.Releases. Property order matches the C++ side's struct field order: version, released_at, summary, notes_url.

Public members

public DateTimeOffset? ReleasedAt { get; set; }
public string Summary { get; set; }
public string Version { get; set; }
public string? NotesUrl { get; set; }
source
public sealed class CatalogVersionResponse

Public members

public long Version { get; set; }
source
public sealed class CopyFromContext

Description

A nested struct inside BindRequest (native Arrow struct — never embedded IPC on its own, since it isn't a top-level RPC parameter). Property declaration order matches the C++ extension's copy_from_type struct field order exactly: format, file_path, expected_schema.

Public members

public byte[] ExpectedSchema { get; set; }
public string FilePath { get; set; }
public string Format { get; set; }
source
public sealed class CopyFromFormatInfo

Description

One item of a catalog_copy_from_formats ItemsResponse — despite the RPC's historical name, this single method covers BOTH COPY … TO and COPY … FROM custom formats, disambiguated by Direction. The C++ extension validates this type's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated CopyFromFormatInfoSchema() — property declaration order matters and must match that schema exactly: comment, tags, format_name, handler, options, direction, description, ordered.

Public members

public Dictionary<string, string> Tags { get; set; }
public bool Ordered { get; set; }

COPY-TO-only: true forces a single-threaded, source-ordered sink (mirrors Buffering.ITableBufferingFunction.SinkOrderDependent) — always false for a COPY-FROM reader.

public byte[] Options { get; set; }

Serialized (schema-only) Arrow IPC bytes describing the format's options — same shape/metadata conventions as FunctionInfo.Arguments (every field a NAMED argument; vgi_doc metadata carries option_description ).

public string Description { get; set; }
public string Direction { get; set; }

"from" , "to" , or "both" .

public string FormatName { get; set; }

The bare (unqualified) name the FORMAT '…' COPY option names — the C++ extension prefixes this with the ATTACH alias for display ( vgi_copy_formats() 's own format_name column) and for the actual FORMAT '<alias>.<this>' SQL syntax; this worker never needs to know its own attach alias.

public string Handler { get; set; }

The bare schema-qualified-by-default-schema function name this format dispatches to — an ordinary Table.ITableFunction registration (COPY FROM) or Buffering.ITableBufferingFunction registration (COPY TO), found the SAME way any other bind/init call resolves a function name.

public string? Comment { get; set; }
source
public sealed class CopyToContext

Description

A nested struct inside BindRequest. Property declaration order matches the C++ extension's copy_to_type struct field order exactly: format, file_path.

Public members

public string FilePath { get; set; }
public string Format { get; set; }
source
public sealed class ForeignKeyInfo

Description

One element of TableInfo.ForeignKeyConstraints — each list entry is itself an independently embedded-IPC-encoded record of this shape (mirrors Internal.EmbeddedIpc's "list of independently-encoded items" convention), parsed by vgi_catalog_api.cpp 's ParseTableInfo off a single-row batch with these four field names: fk_columns, pk_columns, referenced_table, referenced_schema.

Public members

public List<string> FkColumns { get; set; }
public List<string> PkColumns { get; set; }
public string ReferencedSchema { get; set; }
public string ReferencedTable { get; set; }
source
public sealed class FunctionExample

Description

Nested struct inside FunctionInfo.Examples. Property order matches the C++ side's struct field order: sql, description, expected_output.

Public members

public string Description { get; set; }
public string Sql { get; set; }
public string? ExpectedOutput { get; set; }
source
public sealed class FunctionInfo

Description

One item of a catalog_schema_contents_functions ItemsResponse (also reusable for CatalogAttachResult.GlobalFunctions). The C++ extension validates each item's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated FunctionInfoSchema() — property declaration order is LOAD-BEARING and must match that 36-field schema exactly, field-for-field, even though individual value reads on the C++ side are by name.

Public members

public AggregateDistinctDependent DistinctDependent { get; set; }
public AggregateOrderDependent OrderDependent { get; set; }
public Dictionary<string, string> Tags { get; set; }
public FunctionNullHandling? NullHandling { get; set; }
public FunctionStability? Stability { get; set; }
public FunctionType FunctionType { get; set; }
public List<FunctionExample> Examples { get; set; }
public List<RequiredSecret> RequiredSecrets { get; set; }
public List<string> Categories { get; set; }
public List<string> RequiredSettings { get; set; }
public List<string> SupportedExpressionFilters { get; set; }
public VgiOrderPreservation? OrderPreservation { get; set; }
public VgiPartitionKind PartitionKind { get; set; }
public bool FiltersExactlyApplied { get; set; }
public bool HasFinalize { get; set; }
public bool InputFromArgs { get; set; }
public bool RequiresInputBatchIndex { get; set; }
public bool SinkOrderDependent { get; set; }
public bool SourceOrderDependent { get; set; }
public bool StreamingPartitioned { get; set; }
public bool SupportsBatchIndex { get; set; }
public bool SupportsPositions { get; set; }
public bool SupportsSplits { get; set; }
public bool SupportsWindow { get; set; }
public bool? FilterPushdown { get; set; }
public bool? LateMaterialization { get; set; }
public bool? ProjectionPushdown { get; set; }
public bool? SamplingPushdown { get; set; }
public byte[] Arguments { get; set; }

Serialized (schema-only) Arrow IPC bytes describing the function's positional arguments — field NAMES are cosmetic; only field TYPES/order/nullability matter to the C++ side's DuckDB signature registration.

public byte[] OutputSchema { get; set; }

Serialized (schema-only) Arrow IPC bytes describing the return value: exactly one field.

public int? MaxWorkers { get; set; }
public long? SplitTokenTtlSeconds { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string SchemaName { get; set; }
public string? Comment { get; set; }
source
public enum FunctionNullHandling

Description

Wire values: DEFAULT, SPECIAL (matches C++'s ParseFunctionNullHandling).

source
public enum FunctionStability

Description

Wire values: CONSISTENT, VOLATILE, CONSISTENT_WITHIN_QUERY (matches C++'s ParseFunctionStability).

source
public enum FunctionType

Description

The kind of function a BindRequest/FunctionInfo describes. Wire-encoded as dictionary(int16, utf8) by member name (default enum wire naming), producing exactly the strings the C++ extension's ParseVgiFunctionType recognizes: "SCALAR", "TABLE", "AGGREGATE", "TABLE_BUFFERING".

source
public sealed class GlobalInitResponse

Description

The first batch written on the stream init opens — a stream HEADER (its own complete IPC stream: schema + one row + EOS, written before the main output stream begins). See QueryFarm.VgiRpc.Streaming.IRpcStream.Header. C++ reads every field by name and tolerates any of them being absent, so property order doesn't matter here.

Public members

public byte[] ExecutionId { get; set; }
public byte[]? OpaqueData { get; set; }
public long MaxWorkers { get; set; }
source
public interface IVgiService

Description

The VGI RPC surface a worker serves. Scoped down for M1 to just the scalar-function execution path plus the catalog surface a plain ATTACH … (TYPE vgi, …) walks before it can resolve a scalar function call — matching the file's role in the M1 milestone plan. The full ~35-method surface (table/aggregate/table-in-out functions, DDL, transactions, time travel, etc.) is deferred to later milestones (M2+); a client calling a method not declared here gets a normal MethodNotImplementedException , not a crash. Two wire-shape conventions coexist (mirrors vgi-java's own VgiService doc comment): Packed — a single dataclass-equivalent parameter, auto-embedded as IPC-in- binary by SchemaDerivation / ValueCodec (BindAsync, InitAsync, CatalogAttachAsync). Flat — parameters map 1:1 to wire columns by snake_case name (everything else here). Every default-interface-method body below returns a safe, do-nothing/empty answer — a worker that registers no table/view/macro content is unaffected by them ever being invoked.

source
public sealed class InitRequest

Description

The init RPC's packed request — the method parameter itself, so ValueCodec auto- embeds THIS type as one outer binary IPC stream. Its own BindCall field is , independently, ANOTHER embedded IPC stream (a serialized BindRequest) — a "binary containing an embedded IPC stream nested inside an outer embedded IPC stream" shape that SchemaDerivation 's normal two-tier rule doesn't cover on its own, which is why BindCall is declared as plain byte[] here rather than typed as BindRequest — decode it with Internal.EmbeddedIpc.Decode{T}. PROPERTY DECLARATION ORDER IS LOAD-BEARING (see BindRequest's doc comment for why) — matches the C++ extension's BuildInitRequest / InitRequestSchema field order exactly, 19 fields. Most of these are irrelevant to a plain scalar-function exchange (they matter for table functions/pushdown/ordering/finalize) and are always null on that path. Phase is non-null for table-in-out/table-buffering (see VgiInitPhase) — a dictionary-encoded field decodes via the incoming array's OWN type regardless of declared CLR type, so any field that can carry a real (non-null) enum value needs an actual enum CLR type, not a bare string? (see VgiOrderByDirection's doc comment).

Public members

public List<byte[]>? JoinKeys { get; set; }
public List<byte[]>? SplitTokens { get; set; }
public List<long>? ProjectionIds { get; set; }
public VgiInitPhase? Phase { get; set; }
public VgiNullOrder? OrderByNullOrder { get; set; }
public VgiOrderByDirection? OrderByDirection { get; set; }
public byte[] BindCall { get; set; }
public byte[] OutputSchema { get; set; }
public byte[]? BindOpaqueData { get; set; }
public byte[]? ExecutionId { get; set; }
public byte[]? FinalizeStateId { get; set; }
public byte[]? InitOpaqueData { get; set; }
public byte[]? PushdownFilters { get; set; }
public byte[]? SubstreamId { get; set; }
public double? TablesamplePercentage { get; set; }
public long? OrderByLimit { get; set; }
public long? RowLimit { get; set; }
public long? TablesampleSeed { get; set; }
public string? OrderByColumnName { get; set; }
source
public sealed class ItemsResponse

Description

The common unary result shape for every catalog-discovery RPC ( catalog_catalogs , catalog_schemas , catalog_schema_contents_functions , etc.): a list of opaque binary blobs, each one an independently embedded-IPC-encoded item record (e.g. one SchemaInfo or FunctionInfo — see Internal.EmbeddedIpc). Wire field name "items", type list(binary) , not nullable.

Public members

public List<byte[]> Items { get; set; }
source
public sealed class MacroInfo

Description

One item of a catalog_schema_contents_macros / catalog_macro_get ItemsResponse. The C++ extension validates this type's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated MacroInfoSchema() — property declaration order matters and must match that schema exactly: comment, tags, name, schema_name, macro_type, parameters, parameter_default_values, definition, arguments_schema.

Public members

public Dictionary<string, string> Tags { get; set; }
public List<string> Parameters { get; set; }

Every macro parameter's name, in positional-binding order.

public MacroType MacroType { get; set; }
public byte[]? ArgumentsSchema { get; set; }

Reserved for future per-parameter documentation (additive, back-compat-guarded on the C++ side) — always null from this port today.

public byte[]? ParameterDefaultValues { get; set; }

A one-row embedded-IPC RecordBatch whose field NAMES are the (necessarily a subset of Parameters) defaulted parameters and whose single row holds each one's default value — null when no parameter has a default.

public string Definition { get; set; }

The macro body — a scalar expression (MacroType.Scalar) or a SELECT query (MacroType.Table), referencing Parameters by name.

public string Name { get; set; }
public string SchemaName { get; set; }
public string? Comment { get; set; }
source
public enum MacroType

Description

Whether a MacroInfo/Catalog.CatalogMacro is a scalar macro (usable in an expression position) or a table macro (usable as FROM schema.name(…) ). Wire- encoded as dictionary(int16, utf8) by member name (same convention as FunctionType) — the C++ extension's macro_type parser accepts this value case-insensitively, but "SCALAR"/"TABLE" (this enum's default wire naming) is the canonical form other language ports emit.

source
public enum OnConflict

Description

The ON CONFLICT behavior requested by a CREATE SCHEMA / CREATE VIEW DDL call. Wire-encoded as dictionary(int16, utf8) by member name (default enum wire naming), matching vgi_rpc_types.cpp 's on_conflict_values : "ERROR", "IGNORE", "REPLACE".

source
public sealed class RequiredSecret

Description

Nested struct inside FunctionInfo.RequiredSecrets. Property order matches the C++ side's struct field order: secret_type, scope, secret_name.

Public members

public string SecretType { get; set; }
public string? Scope { get; set; }
public string? SecretName { get; set; }
source
public sealed class ScanBranch

Description

One arm of a multi-branch table scan (or the sole, synthesized arm of an ordinary single-function table answering the diagnostic vgi_table_branches() function) — see ScanBranchesResult. Three mutually exclusive kinds, selected by which of FunctionName/SourceTable/FormatName is non-empty (the C++ parser rejects zero or more than one set): Function branch — FunctionName names a VGI OR a native DuckDB table function (e.g. read_parquet , iceberg_scan — resolved directly against DuckDB's own catalog, never tunneled through the worker pipe) to call with Arguments (the same flat arg_<N> /bare-name wire shape as ScanFunctionResult.Arguments). Catalog-table branch — SourceTable (+ optional SourceCatalog/SourceSchema) names a table in a companion catalog to scan directly. No in-scope fixture uses this kind yet. Format branch — FormatName ( csv / parquet /…) plus FormatLocations let the C++ client pick the matching reader function itself, without the worker needing to know the reader's exact spelling; FormatOptions (same flat wire shape as Arguments, but every field must be a NAMED option — no arg_<N> positional entries) become that reader's named arguments. BranchFilter is a raw SQL boolean expression (parsed, never bound, worker-side — binding happens in the C++ optimizer rewriter once a real column list is in hand) the optimizer uses to prune whole branches that can't match a query's WHERE clause. Property order matches the generated ScanBranchSchema() : function_name, arguments, branch_filter, writable, source_catalog, source_schema, source_table, format_name, format_locations, format_options.

Public members

public List<string>? FormatLocations { get; set; }
public bool Writable { get; set; }

Declares this branch the INSERT target for a multi-branch table. At most one branch across a whole table's ScanBranchesResult.Branches may set this — the C++ parser ( ParseScanBranchesResult ) rejects two or more at bind time.

public byte[] Arguments { get; set; }
public byte[]? FormatOptions { get; set; }
public string FunctionName { get; set; }
public string? BranchFilter { get; set; }
public string? FormatName { get; set; }
public string? SourceCatalog { get; set; }
public string? SourceSchema { get; set; }
public string? SourceTable { get; set; }
source
public sealed class ScanBranchesResult

Description

Result of the catalog_table_scan_branches_get RPC — describes a table's scan as a list of independent ScanBranches the C++ optimizer UNIONs together (or, for the common single-branch case, collapses back into the legacy single-function scan path — see VgiTableEntry::GetScanFunctionImpl ). Called for EVERY VGI table, not just multi-branch ones — the vgi_table_branches() diagnostic function and the capability-detection cache ( catalog/multi_branch_capability_cache.test ) both depend on a compliant worker answering it for a plain, wholly ordinary function-backed table too (as a single synthesized branch). Property order matches the generated ScanBranchesResultSchema() : branches, required_extensions.

Public members

public List<byte[]> Branches { get; set; }

Each element an Internal.EmbeddedIpc-encoded ScanBranch. MUST be non-empty — the C++ parser ( ParseScanBranchesResult ) throws a loud BinderException at bind time on an empty list (see catalog/multi_branch_empty_branches.test ), rather than silently returning zero rows.

public List<string> RequiredExtensions { get; set; }

DuckDB extensions required to scan any of Branches (e.g. ["iceberg"] for an iceberg_scan branch) — auto-loaded by the C++ side before the scan runs. Union across all branches; empty means none.

source
public sealed class ScanFunctionResult

Description

Tells the C++ extension which VGI table function to call to obtain a table's data (or to perform an INSERT/UPDATE/DELETE against it) — the same wire shape serves catalog_table_scan_function_get 's result, catalog_table_{insert,update,delete}function_get 's result, AND the four TableInfo.{scan,insert,update,delete}function inline fields (parsed with the identical ParseScanFunctionResult on the C++ side either way — see vgi_catalog_api.cpp ). Property order matches the generated ScanFunctionResultSchema() : function_name, arguments, required_extensions. Arguments is the SAME wire shape Internal.TableArgCodec decodes for a normal bind call, EXCEPT the positional-argument field prefix is arg<N> (not positional<N> ) and a named argument's field name carries NO prefix at all — see DecodeScanArguments in vgi_catalog_api.cpp . An empty/zero-length array (NOT a zero-field embedded IPC struct) means "no arguments" — DecodeScanArguments returns immediately when arguments_bytes.empty() , so a function-backed table/write-function with no extra arguments should just leave this as Array.Empty{T}<byte>() rather than constructing a degenerate embedded struct batch.

Public members

public List<string> RequiredExtensions { get; set; }
public byte[] Arguments { get; set; }
public string FunctionName { get; set; }
source
public sealed class ScanSplitWire

Description

One entry of TableFunctionPlanResult.Splits — a named, independently redeemable unit of scan work, serialized as its own self-contained 1-row embedded IPC stream (see Internal.EmbeddedIpc.Encode{T}/Internal.EmbeddedIpc.Decode{T}), the same "nested embedded IPC" shape as InitRequest.BindCall. Field ORDER and nullability are wire-significant — matches the C++ extension's generated ScanSplitSchema() exactly, 10 fields. The author-facing equivalent is Table.ScanSplit — a worker sets only Table.ScanSplit.Payload (and optional estimates); Internal.VgiServiceImpl stamps Token from it via Internal.SplitToken.Build and clears Payload before serializing (the payload rides sealed inside the token; shipping the plaintext beside it would make the seal decorative — see vgi-java's identical ScanSplit.withToken comment).

Public members

public List<long?>? LocationIds { get; set; }

Nullable-item list — the C++ schema declares list(int64) with a nullable item type (see the port-wide "nullable list-element" gotcha), so this is List<long?> rather than List<long> even though this worker never actually populates a null entry.

public bool RowsExact { get; set; }
public byte[] Payload { get; set; }
public byte[] Token { get; set; }
public byte[]? ColumnStatistics { get; set; }
public byte[]? EndPosition { get; set; }
public byte[]? PartitionBounds { get; set; }
public byte[]? StartPosition { get; set; }
public long? EstimatedBytes { get; set; }
public long? EstimatedRows { get; set; }
source
public sealed class SchemaInfo

Description

One item of a catalog_schemas / catalog_schema_get ItemsResponse. The C++ extension validates each item's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated SchemaInfoSchema() — property declaration order matters and must match that 5-field schema exactly: comment, tags, attach_opaque_data, name, estimated_object_count.

Public members

public Dictionary<string, long?>? EstimatedObjectCount { get; set; }

Declared with a nullable long? value type deliberately: Arrow's own map(utf8, int64) factory defaults the value field to NULLABLE, but SchemaDerivation 's map-value-field rule only infers "nullable" for a reference-typed value (a plain non-nullable long value type would derive a non-nullable value field, which fails the C++ side's strict schema-equality check against its generated SchemaInfoSchema() ).

public Dictionary<string, string> Tags { get; set; }
public byte[] AttachOpaqueData { get; set; }
public string Name { get; set; }
public string? Comment { get; set; }
source
public enum SchemaObjectType

Description

The catalog object kind requested by catalog_schema_contents_functions / _macros . Wire-encoded as dictionary(int16, utf8) by member name, producing exactly the strings the C++ extension sends: TABLE, VIEW, SCALAR_FUNCTION, TABLE_FUNCTION, AGGREGATE_FUNCTION, SCALAR_MACRO, TABLE_MACRO, INDEX.

source
public sealed class SecretTypeSpec

Description

One element of CatalogAttachResult.SecretTypes — a custom DuckDB secret TYPE ( CREATE SECRET (TYPE <name>, …) ) a worker declares at attach time. Mirrors vgi-python's SecretTypeSpec.ARROW_SCHEMA /vgi-java's SecretTypeSpec record on the C++ side ( vgi_catalog_metadata.hpp 's VgiSecretType / ParseVgiSecretType ): 3 columns, name / description plain strings, parameters_schema a schema-only IPC blob (see Internal.SchemaIpc.WriteSchemaOnly) describing the secret's key/value parameters — mark a sensitive field's metadata "redact":"true" so DuckDB masks it in duckdb_secrets() . Each SecretTypeSpec is itself embedded-IPC-encoded (Internal.EmbeddedIpc.Encode{T}) before being placed in CatalogAttachResult.SecretTypes's list(binary) .

Public members

public byte[] ParametersSchema { get; set; }

Schema-only IPC bytes describing the secret's key/value parameters — field metadata "redact":"true" on a field marks it for masking in duckdb_secrets() .

public string Description { get; set; }
public string Name { get; set; }
source
public sealed class SettingSpec

Description

One element of CatalogAttachResult.Settings — a global/session DuckDB setting ( SET <name> = … ) a worker declares at attach time, separate from the per-function Attributes.SettingAttribute/ RequiredSettings mechanism (which only reads an already-declared setting's CURRENT value; a setting must appear here at least once, from some worker, for DuckDB to know it exists at all — see duckdb_settings() ). Wire shape mirrors vgi-python's SettingSpec.ARROW_SCHEMA / VgiSetting on the C++ side ( vgi_catalog_metadata.hpp ): 4 columns, name / description plain strings, type a schema-only IPC blob for a single field named "value" (see Internal.SchemaIpc.WriteSchemaOnly), default_value a full one-row IPC batch for that same single "value" column (see Internal.RecordBatchIpc.Write) — null when the setting has no default. Each SettingSpec is itself embedded-IPC-encoded (Internal.EmbeddedIpc.Encode{T}) before being placed in CatalogAttachResult.Settings's list(binary) .

Public members

public byte[] Type { get; set; }

Schema-only IPC bytes for a single field named "value" carrying this setting's Arrow type.

public byte[]? DefaultValue { get; set; }

One-row IPC batch (single column "value" , typed per Type) holding this setting's default — null when there is none.

public string Description { get; set; }
public string Name { get; set; }
source
public sealed class TableBufferingCombineRequest

Description

The table_buffering_combine RPC's packed request — called once, on whatever worker the C++ extension's coordinator-election picks, after every Sink table_buffering_process call has completed. PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches TableBufferingCombineRequestSchema exactly: function_name, execution_id, state_ids, attach_opaque_data, transaction_id, schema_name.

Public members

public List<byte[]> StateIds { get; set; }

Every state_id returned by every process() call across every worker, in arbitrary order — duplicates are NOT deduplicated by the framework.

public byte[] ExecutionId { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public byte[]? TransactionId { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class TableBufferingCombineResult

Description

The table_buffering_combine RPC's packed result: finalize_state_ids — the keys the Source phase will iterate, one init(phase=TABLE_BUFFERING_FINALIZE) stream per id.

Public members

public List<byte[]> FinalizeStateIds { get; set; }
source
public sealed class TableBufferingDestructorRequest

Description

The table_buffering_destructor RPC's packed request — a best-effort call after the Source phase completes, giving the worker a chance to wipe any durable state it stashed for ExecutionId (see Internal.FunctionStorage ). PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches TableBufferingDestructorRequestSchema exactly: function_name, execution_id, attach_opaque_data, transaction_id, schema_name.

Public members

public byte[] ExecutionId { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public byte[]? TransactionId { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class TableBufferingDestructorResult

Description

The table_buffering_destructor RPC's packed result — no fields.

source
public sealed class TableBufferingProcessRequest

Description

The table_buffering_process RPC's packed request — the Sink phase's per-batch unary call. Unlike the streaming exchange path, this (and its two siblings below) is a completely standalone unary RPC: the C++ extension's InvokePooledUnaryRpc acquires SOME worker matching this worker's pool key (not necessarily the same process/connection that minted ExecutionId via init(phase=TABLE_BUFFERING) ) — so any state this call needs to hand off to table_buffering_combine /the FINALIZE producer must be durable, cross-PROCESS storage keyed by ExecutionId, never in-memory worker state (see Internal.FunctionStorage ). PROPERTY DECLARATION ORDER IS LOAD-BEARING — matches TableBufferingProcessRequestSchema exactly: function_name, execution_id, input_batch, attach_opaque_data, transaction_id, batch_index, schema_name.

Public members

public RecordBatch InputBatch { get; set; }

The one input batch to ingest — decodes automatically via ValueCodec 's RecordBatch -typed-property special case (an embedded-IPC binary field whose bytes are themselves a self-contained schema+batch IPC stream).

public byte[] ExecutionId { get; set; }
public byte[]? AttachOpaqueData { get; set; }
public byte[]? TransactionId { get; set; }
public long? BatchIndex { get; set; }
public string FunctionName { get; set; }
public string? SchemaName { get; set; }
source
public sealed class TableBufferingProcessResult

Description

The table_buffering_process RPC's packed result: one field, state_id — opaque bytes this worker chose to name where it stashed TableBufferingProcessRequest.InputBatch.

Public members

public byte[] StateId { get; set; }
source
public sealed class TableCreateRequest

Description

The catalog_table_create RPC's packed request (wire field name request , matching init / table_buffering_* 's packed-single-parameter convention). Property order matches the C++ extension's BuildTableCreateRequest field order exactly: attach_opaque_data, schema_name, name, columns, on_conflict, not_null_constraints, unique_constraints, check_constraints, primary_key_constraints, foreign_key_constraints, transaction_opaque_data.

Public members

public List<List<int?>> PrimaryKeyConstraints { get; set; }
public List<List<int?>> UniqueConstraints { get; set; }
public List<byte[]> ForeignKeyConstraints { get; set; }

Each element an Internal.EmbeddedIpc-encoded ForeignKeyInfo.

public List<int?> NotNullConstraints { get; set; }
public List<string> CheckConstraints { get; set; }
public OnConflict OnConflict { get; set; }
public byte[] AttachOpaqueData { get; set; }
public byte[] Columns { get; set; }

Serialized (schema-only) Arrow schema — see Internal.SchemaIpc.

public byte[]? TransactionOpaqueData { get; set; }
public string Name { get; set; }
public string SchemaName { get; set; }
source
public sealed class TableFunctionCardinalityRequest

Description

The table_function_cardinality RPC's packed request — a lazy, best-effort call the C++ extension makes at most once per bound call site and treats as non-critical (a failure/timeout just leaves the cardinality "unknown"; see VgiTableFunctionCardinality 's try/catch). Matches the generated TableFunctionCardinalityRequestSchema , 2 fields.

Public members

public byte[] BindCall { get; set; }
public byte[]? BindOpaqueData { get; set; }
source
public sealed class TableFunctionCardinalityResult

Description

The table_function_cardinality RPC's unary result. Matches the generated TableFunctionCardinalityResultSchema , 2 fields — both nullable ("unknown").

Public members

public long? Estimate { get; set; }
public long? Max { get; set; }
source
public sealed class TableFunctionDynamicToStringRequest

Description

The table_function_dynamic_to_string RPC's packed request — the bind call plus the scan's global_execution_id (the correlation key a Table.ITableFunction.DynamicToString implementation uses to retrieve whatever diagnostics it persisted while producing rows). Matches the generated TableFunctionDynamicToStringRequestSchema , 3 fields.

Public members

public byte[] BindCall { get; set; }
public byte[] GlobalExecutionId { get; set; }
public byte[]? BindOpaqueData { get; set; }
source
public sealed class TableFunctionDynamicToStringResult

Description

The table_function_dynamic_to_string RPC's unary result. Matches the generated TableFunctionDynamicToStringResultSchema , 2 fields — Keys[i]/Values[i] are paired positionally (same length). Empty means no extra diagnostics.

Public members

public List<string> Keys { get; set; }
public List<string> Values { get; set; }
source
public sealed class TableFunctionPlanRequest

Description

The table_function_plan RPC's packed request — the scan-planning phase that precedes per-split init (see Table.PlanRequest / Table.PlanResult for the author-facing view, and Internal.SplitToken for the envelope every split's token gets stamped with). plan() runs once with the STATIC pushdown filters known at that point and returns named splits; each split is then redeemed by init — possibly from a different process — which is what makes a retried/re-parallelized scan sound. PROPERTY DECLARATION ORDER IS LOAD-BEARING (see BindRequest's doc comment for why) — matches the C++ extension's generated TableFunctionPlanRequestSchema field order exactly, 19 fields. BindCall is a SECOND level of embedded IPC (a serialized BindRequest) — decode with Internal.EmbeddedIpc.Decode{T}, exactly like InitRequest.BindCall.

Public members

public List<byte[]>? JoinKeys { get; set; }
public List<long>? ProjectionIds { get; set; }
public VgiNullOrder? OrderByNullOrder { get; set; }
public VgiOrderByDirection? OrderByDirection { get; set; }
public bool FiltersComplete { get; set; }
public byte[] BindCall { get; set; }
public byte[]? BindOpaqueData { get; set; }
public byte[]? Cursor { get; set; }
public byte[]? EndPosition { get; set; }
public byte[]? PushdownFilters { get; set; }
public byte[]? RefinedFilters { get; set; }
public byte[]? StartPosition { get; set; }
public double? TablesamplePercentage { get; set; }
public long? MaxSplitsPerResponse { get; set; }
public long? MinSplits { get; set; }
public long? OrderByLimit { get; set; }
public long? RowLimit { get; set; }
public long? TablesampleSeed { get; set; }
public long? TargetSplitBytes { get; set; }
public string? OrderByColumnName { get; set; }
source
public sealed class TableFunctionPlanResult

Description

The table_function_plan RPC's unary result — the C++ extension validates this type's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated TableFunctionPlanResultSchema() (16 fields, order load-bearing — see BindResponse's doc comment for the same rule). An EMPTY Splits is legal and means "no work": a fully-pruned split-capable scan reaches it, and the client produces an empty result rather than an error. Built by Internal.VgiServiceImpl from an ITableFunction's author-facing Table.PlanResult — see that type's doc comment for the split-vs-"not split-capable" distinction.

Public members

public List<byte[]> Splits { get; set; }

One serialized ScanSplitWire per unit of work, in emission order — what the client actually reads out of each entry is just its stamped token .

public List<byte[]>? NextCursors { get; set; }

Continuation cursors for a paginated enumeration; normally 0 or 1 entries.

public List<byte[]>? Partitioning { get; set; }
public List<byte[]>? SortOrder { get; set; }
public List<string>? Locations { get; set; }
public byte[]? EndPosition { get; set; }
public byte[]? ExecutionId { get; set; }
public byte[]? InitOpaqueData { get; set; }
public byte[]? StartPosition { get; set; }
public long? CacheMaxAgeSeconds { get; set; }
public long? CatalogVersion { get; set; }

The catalog counter this plan is pinned to — every split's token anchor.

public long? EstimatedTotalBytes { get; set; }
public long? EstimatedTotalRows { get; set; }
public long? EstimatedTotalSplits { get; set; }
public long? MaxWorkers { get; set; }

Normative cap on splits in flight at once, or null for none.

public string Scope { get; set; }

Which consistency anchor every token in this plan binds: "catalog" or "transaction" . This worker always plans at catalog scope (see Internal.VgiServiceImpl's CatalogVersion constant's doc comment).

source
public sealed class TableFunctionStatisticsRequest

Description

The table_function_statistics RPC's packed request — same 2-field shape as TableFunctionCardinalityRequest (a full copy of the bind call, since a table function's per-column statistics are a pure function of its bind-time arguments). Matches the generated TableFunctionStatisticsRequestSchema .

Public members

public byte[] BindCall { get; set; }
public byte[]? BindOpaqueData { get; set; }
source
public sealed class TableInfo

Description

One item of a catalog_table_get / catalog_schema_contents_tables ItemsResponse. The C++ extension validates each item's embedded-IPC schema with STRICT arrow::Schema::Equals against its generated TableInfoSchema() — property declaration order matters and must match that 24-field schema exactly: comment, tags, name, schema_name, columns, not_null_constraints, unique_constraints, check_constraints, primary_key_constraints, foreign_key_constraints, supports_insert, supports_update, supports_delete, supports_returning, supports_column_statistics, scan_function, insert_function, update_function, delete_function, cardinality_estimate, cardinality_max, column_statistics, bind_result, required_filters.

Public members

public Dictionary<string, string> Tags { get; set; }
public List<List<int?>> PrimaryKeyConstraints { get; set; }
public List<List<int?>> UniqueConstraints { get; set; }
public List<List<string>> RequiredFilters { get; set; }
public List<byte[]> ForeignKeyConstraints { get; set; }

Each element an Internal.EmbeddedIpc-encoded ForeignKeyInfo.

public List<int?> NotNullConstraints { get; set; }
public List<string> CheckConstraints { get; set; }
public bool SupportsColumnStatistics { get; set; }
public bool SupportsDelete { get; set; }
public bool SupportsInsert { get; set; }
public bool SupportsReturning { get; set; }
public bool SupportsUpdate { get; set; }
public byte[] Columns { get; set; }

Serialized (schema-only, Internal.SchemaIpc) Arrow schema describing this table's columns — a column marked with Internal.VgiRowIdMetadata.Key field metadata is the row identity UPDATE/DELETE key.

public byte[]? BindResult { get; set; }
public byte[]? ColumnStatistics { get; set; }
public byte[]? DeleteFunction { get; set; }
public byte[]? InsertFunction { get; set; }
public byte[]? ScanFunction { get; set; }

Inline Internal.EmbeddedIpc-encoded ScanFunctionResult — when present, the C++ extension uses it directly and never fires the (unimplemented, in this worker) catalog_table_scan_function_get RPC.

public byte[]? UpdateFunction { get; set; }
public long? CardinalityEstimate { get; set; }
public long? CardinalityMax { get; set; }
public string Name { get; set; }
public string SchemaName { get; set; }
public string? Comment { get; set; }
source
public sealed class TransactionBeginResponse

Public members

public byte[]? TransactionOpaqueData { get; set; }
source
public enum VgiInitPhase

Description

Wire values: INPUT, FINALIZE, TABLE_BUFFERING, TABLE_BUFFERING_FINALIZE ( InitRequest.Phase ) — the C++ extension's phase_values array in BuildInitRequest ( vgi_rpc_types.cpp ). A dictionary-encoded ( dictionary(int16, utf8) ) field decodes via ValueCodec.ExtractEnum purely based on the incoming Arrow array's own type — declaring this field as a bare string? (as M1-M3 did, since phase was always null on the scalar/plain-table path) fails decode with "Enum 'System.String' has no member matching wire name …" the moment a real value ever rides the wire — see VgiOrderByDirection's doc comment for the same lesson learned earlier. Meaning: Input — table-in-out streaming exchange phase (ExchangeState-shaped): the client writes input batches, this replies with output batches. Finalize — table-in-out per-substream finalize (ProducerState-shaped, tick-driven), run on the SAME connection that just finished the Input phase. TableBuffering — table-buffering's Sink-phase init: mints/joins the query's execution_id on a fresh connection, which then immediately closes its (empty) input writer so the exchange completes; all real Sink traffic afterward is the standalone table_buffering_process / table_buffering_combine / table_buffering_destructor unary RPCs (each independently worker-pool-acquired — NOT guaranteed to land on this connection). TableBufferingFinalize — table-buffering's Source-phase init (ProducerState-shaped): carries InitRequest.FinalizeStateId , opened on whatever pooled connection the Source operator acquired for that one finalize_state_id .

source
public enum VgiNullOrder

Description

Wire values: NULLS_FIRST, NULLS_LAST ( InitRequest.OrderByNullOrder / TableFunctionPlanRequest.OrderByNullOrder ).

source
public enum VgiOrderByDirection

Description

Wire values: ASC, DESC ( InitRequest.OrderByDirection / TableFunctionPlanRequest.OrderByDirection ). A dictionary-encoded ( dictionary(int16, utf8) ) field decodes via ValueCodec.ExtractEnum purely based on the incoming Arrow array's own type (a Apache.Arrow.DictionaryArray) — declaring this field as a bare string? instead fails decode with "Enum 'System.String' has no member matching wire name …", so any non-always-null dictionary-encoded field needs its own enum CLR type, not just a string.

source
public enum VgiOrderPreservation

Description

Wire values: PRESERVES_ORDER, NO_ORDER_GUARANTEE, FIXED_ORDER (matches C++'s ParseVgiOrderPreservation).

source
public enum VgiPartitionKind

Description

Wire values: NOT_PARTITIONED, SINGLE_VALUE_PARTITIONS, OVERLAPPING_PARTITIONS, DISJOINT_PARTITIONS (matches C++'s ParseVgiPartitionKind). NotPartitioned is deliberately the first (default) member — FunctionInfo.PartitionKind is a non-nullable wire field, so an unset property must already resolve to the C++ side's own documented default.

source
public sealed class ViewInfo

Description

One item of a catalog_view_get / catalog_schema_contents_views ItemsResponse. Property declaration order matches the generated ViewInfoSchema() exactly: comment, tags, name, schema_name, definition, column_comments.

Public members

public Dictionary<string, string> ColumnComments { get; set; }
public Dictionary<string, string> Tags { get; set; }
public string Definition { get; set; }

The view's SQL SELECT statement.

public string Name { get; set; }
public string SchemaName { get; set; }
public string? Comment { get; set; }