Apache.Calcite.Extensions 2.0.1-pre.250

This is a prerelease version of Apache.Calcite.Extensions.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Apache.Calcite.Extensions --version 2.0.1-pre.250
                    
NuGet\Install-Package Apache.Calcite.Extensions -Version 2.0.1-pre.250
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Apache.Calcite.Extensions" Version="2.0.1-pre.250" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Apache.Calcite.Extensions" Version="2.0.1-pre.250" />
                    
Directory.Packages.props
<PackageReference Include="Apache.Calcite.Extensions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Apache.Calcite.Extensions --version 2.0.1-pre.250
                    
#r "nuget: Apache.Calcite.Extensions, 2.0.1-pre.250"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Apache.Calcite.Extensions@2.0.1-pre.250
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Apache.Calcite.Extensions&version=2.0.1-pre.250&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Apache.Calcite.Extensions&version=2.0.1-pre.250&prerelease
                    
Install as a Cake Tool

Apache.Calcite.Extensions

NuGet

Apache.Calcite.Extensions is what .NET adds on top of Apache Calcite running under IKVM: a calling convention that runs a query plan as compiled .NET code, the prepare pipeline that takes a statement from SQL text to such a plan, and the interop types both need.

No Java compiler runs when a statement is prepared, and a .NET user-defined function can be called from SQL.

Most people get this package as a dependency of Apache.Calcite.Data or Apache.Calcite.Adapter.AdoNet and never call it directly — every statement on a CalciteConnection is already planned and run this way, with nothing to configure. Reference it yourself when you want to drive Calcite's planner without an ADO.NET connection, or want typed access to Calcite's connection properties.

Targets .NET 8, and is verified on .NET 8 and .NET 10.

Install

dotnet add package Apache.Calcite.Extensions

Why you might want it

Calcite normally executes a query by generating Java source and compiling it at runtime with Janino. Under IKVM that works, but a Java compiler runs every time you prepare a statement, and any function you call from SQL has to be reachable by a Java class name.

This package replaces that step. A query plan is compiled into a System.Linq.Expressions tree and turned into a delegate, so:

  • No Java compiler runs when you prepare a statement.
  • A .NET method can be a SQL function, and no class name is written out. Calcite's own engine reaches one only through the class-loader stamp IKVM.Maven.Sdk puts on calcite-core, which IKVM 8.14.0 and 8.15.0 could not read — under those a .NET user-defined function had no plan under EnumerableConvention at all, Janino refusing the cli.-prefixed name IKVM gives a CLR class. IKVM 8.16.0 fixes it; here it never mattered, because nothing writes a name.

ClrCursorConvention mirrors Calcite's EnumerableConvention node for node and uses the same row types, and converter rules exist in both directions. A plan may hold nodes of both conventions: anything this convention has no rule for is planned by Calcite as usual, and rows cross between the two untouched.

One plan, opened either way, read either way. A plan of this convention is compiled to a ClrCursorFactory, with two members, Open(DataContext) and OpenAsync(DataContext, CancellationToken). Both hand back the same kind of object: an IClrCursor, a forward-only cursor with Read() and ReadAsync(CancellationToken) over one position. A consumer chooses how to open, and then chooses again on every advance how to read, and a row read with one member and the next with the other are consecutive rows of one result.

That is the shape DbDataReader has. A sequence cannot have it: an IEnumerable or an IAsyncEnumerable states once, at GetEnumerator or GetAsyncEnumerator, whether it will be pulled or awaited, and takes its cancellation at the same moment. A cursor takes the token per advance and hands it down to the leaf. So the same prepared statement can be read synchronously by one caller and awaited by another, and an EXPLAIN cannot tell you which will happen.

Running a plan yourself

Put this convention's rules on the planner, run Programs.standard(ClrCursorRelMetadata.Provider), then build the root with ClrCursorRelImplementor. This example is executed by a test in the repository, so it cannot go stale silently:

using Apache.Calcite.Extensions.Adapter.Cursor;
using Apache.Calcite.Extensions.Rel.Metadata;
using org.apache.calcite;
using org.apache.calcite.tools;

var calcRules = new java.util.ArrayList();
foreach (var rule in ClrCursorRules.CalcRules())
    calcRules.add(rule);

// Programs.standard(), with this convention's rules put on the planner in front of it -- a
// Frameworks planner carries Calcite's alone -- and its calc rules run afterwards, which is
// Programs.calc once more over this convention's list. standard's own calc pass still runs
var config = Frameworks.newConfigBuilder()
    .defaultSchema(rootSchema)
    .programs(
        Programs.sequence(
            new AddRulesProgram(ClrCursorRules.Rules()),
            Programs.standard(ClrCursorRelMetadata.Provider),
            Programs.hep(calcRules, true, ClrCursorRelMetadata.Provider)))
    .build();

var planner = Frameworks.getPlanner(config);
var logical = planner.rel(planner.validate(planner.parse(sql))).project();

// one sequence, so one transform, exactly as Programs.standard is driven.
// the logical root's own traits, not an empty set: they carry the collation the ORDER BY produced,
// and SortRemoveRule takes the sort away as unwanted if the required traits do not ask for it
var traits = logical.getTraitSet().replace(ClrCursorConvention.Instance).simplify();
var physical = planner.transform(0, traits, logical);

var implementor = new ClrCursorRelImplementor(
    physical.getCluster().getRexBuilder(), new java.util.HashMap());
var factory = implementor.ImplementRoot((ClrCursorRel)physical, ClrCursorPrefer.Array);

// opening runs the plan's acquisition -- a sort drains, a leaf executes -- and reading reads rows
await using var cursor = await factory.OpenAsync(dataContext, cancellationToken);
while (await cursor.ReadAsync(cancellationToken))
    Console.WriteLine(cursor.Current);

// or synchronously, over the same factory
using var pulled = factory.Open(dataContext);
while (pulled.Read())
    Console.WriteLine(pulled.Current);

A one-column result is the value itself, not a row of one.

ImplementRoot walks the tree twice, through each node's Implement and its ImplementAsync, and puts both opens on one factory; each is compiled the first time it is called. A node's two bodies differ only in what is acquired at open — one drains a sort by blocking, the other by awaiting — and produce the same cursor class, whose two advances step the same fields. An operator that acquires a source later than at its own open, as linq4j's concat does inside moveNext, takes both opens of that source and calls the one matching the advance it is in.

Four things about this program are deliberate and worth knowing before you substitute your own:

  • The calc rules are a separate pass. VolcanoCost.isLt compares row counts and nothing else, so a project and a calc are never cheaper than one another and the planner keeps whichever it saw first. Rewriting unconditionally afterwards as a hep pass is what makes a project's refusal to implement itself safe. Programs.standard() does the same thing for the same reason.
  • The planner pass registers Calcite's rules, then this convention's. Programs.standard() installs none and plans with whatever is on the planner, which works because RelOptUtil.registerDefaultRules has already put Calcite's there. Nothing has heard of this convention, so Rules() registers — but it registers Calcite's set as well as ours, not instead of it. Dropping Calcite's takes with it the logical rewrites that belong to no convention, and AVG, every DISTINCT aggregate and every OVER window each need one of those before any planner sees them. It is also what lets a node this convention has no rule for be planned in EnumerableConvention and carried across a converter.
  • The decorrelation is Calcite's and is run. A scalar sub-query and an EXISTS become joins, and an UNNEST over a correlation variable cannot be decorrelated and keeps its correlate — which is how Calcite reaches its own EnumerableCorrelate under Programs.standard() as well.
  • The metadata provider is Calcite's, with this convention's nodes added. Calcite answers some of what a plan is costed from — a limit's row-count bounds, the collation a merge join or a hash join keeps, an interpreter's cumulative cost — from handlers keyed on the Enumerable* class, which this convention's nodes never reach. ClrCursorRelMetadata.Provider puts the same handlers, keyed on the ClrCursor* class, in front of DefaultRelMetadataProvider.INSTANCE. Pass it to standard as well as to the calc pass: each hep pass sets the thread's metadata provider as it runs, and the planner pass inside standard costs with whatever the sub-query pass before it set.

Key public types

Type Purpose
ClrCursorConvention The calling convention itself. ClrCursorConvention.Instance is the singleton trait.
ClrCursorRules The convention's rules: Rules() and CalcRules(). Add these to a planner you built yourself.
ClrCursorRelImplementor Builds both opens of a plan and hands back a ClrCursorFactory. Two parallel hierarchies over one instance: VisitChild composes opens that acquire synchronously and VisitChildAsync opens that await. It carries no mode. Pulled and Awaited cross between them, and Opener and OpenerAsync defer an input's open for an operator that acquires it later.
ClrCursorResult / ClrCursorAsyncResult What a node's two bodies answer, one type per kind, built by Result and ResultAsync.
IClrCursorFactory / ClrCursorFactory A compiled plan: Open(DataContext) and OpenAsync(DataContext, CancellationToken) each hand back an IClrCursor, and ElementType says what one row is. ClrCursorFactory is the one the implementor builds.
IClrCursor / IClrCursor<T> A forward-only cursor with Read() and ReadAsync(CancellationToken) over one position, and Current. ClrCursor and ClrCursor<T> are the abstract bases every cursor of this project derives from; a source that is a cursor already implements the interface directly.
ClrCursorPrefer How a caller wants rows represented — Array is what a prepared statement asks for. It is in the Adapter.Cursor namespace with the rest of the convention.
ClrCursorRelFactories RelBuilder factories producing nodes of this convention.
IClrScannableTable / IClrQueryableTable / IClrCursorTable The table SPI: a table hands back .NET sequences rather than linq4j ones, or a cursor. One interface per table kind, carrying both halves. Scan, GetExpression and Open are required; ScanAsync, GetAsyncExpression and OpenAsync default to reading them across. A table whose rows only ever arrive asynchronously overrides those and drains its own sequence for the required half. A cursor table is for a source that is a forward-only cursor already, a DbDataReader say: its cursor is the plan's leaf and the token of each ReadAsync reaches it.
ClrCursorRel The interface every node of this convention implements. Two bodies: Implement composes opens that acquire synchronously, required, and ImplementAsync opens that await, optional and defaulting to Implement. That default is safe exactly when a body does not visit a child, which is not the same as having no input: a body that asks for its input and takes the default composes a synchronously opened input into an awaiting operator, which Expression.Call refuses.
CalciteConnectionProperties Typed .NET properties over Calcite's java.util.Properties.
CalciteConnectionPropertiesSchemaMap The schema.* sub-properties, as a dictionary.

The nodes (ClrCursorCalc, ClrCursorHashJoin, ClrCursorWindow, and the rest) and their rules are public too, so you can subclass or re-register them.

The operator set is not public. ClrCursorDefaults and the ClrCursorBuiltInMethod table that names its members are internal to this package. A node you write outside it builds calls to its own methods with Expression.Call, and an awaiting one appends the token the awaiting root takes, which is the implementor's CancellationToken parameter.

The SQL-text prepare pipeline is public, and it is what Apache.Calcite.Data prepares through. IClrPrepare and its implementation ClrPrepareImpl, in Apache.Calcite.Extensions.Prepare, take a statement to an IClrPrepare.Signature, whose Open and OpenAsync are the factory's. The contexts it runs against are internal, so to run SQL text use Apache.Calcite.Data; to drive the planner directly, use the public types above.

CalciteConnectionProperties

Strongly-typed .NET properties over a Calcite java.util.Properties map. Instead of reading and writing raw string keys, you get compile-time-checked access to Calcite's connection options:

using Apache.Calcite.Extensions.Config;
using java.util;
using org.apache.calcite.avatica.util;

var props = new CalciteConnectionProperties();

// Typed setters — no magic strings needed.
props.Lex                  = Lex.MYSQL_ANSI;
props.CaseSensitive        = false;
props.DefaultNullCollation = NullCollation.LOW;
props.Fun                  = "oracle,spatial";
props.TimeZone             = "UTC";
props.ForceDecorrelate     = true;
props.MaterializationsEnabled = false;
Property Type Default Description
Model string — URI or inline JSON model.
Schema string — Default schema name.
CaseSensitive bool from Lex (true under ORACLE) Case-sensitive identifier matching.
Lex Lex ORACLE Lexical policy (ORACLE, MYSQL, MYSQL_ANSI, SQL_SERVER, JAVA, BIG_QUERY).
Quoting Quoting from Lex Identifier quote character.
QuotedCasing Casing? from Lex Storage of quoted identifiers.
UnquotedCasing Casing? from Lex Storage of unquoted identifiers.
Fun string standard Function libraries, e.g. oracle,spatial.
Conformance SqlConformanceEnum DEFAULT SQL conformance level.
DefaultNullCollation NullCollation HIGH NULL sort order when NULLS FIRST/LAST is omitted.
TimeZone string JVM default Session time zone.
Locale string Locale.ROOT Session locale.
ForceDecorrelate bool true Aggressive subquery de-correlation.
TopDownGeneralDecorrelationEnabled bool false De-correlate with TopDownGeneralDecorrelator rather than RelDecorrelator.
MaterializationsEnabled bool true Use materializations in the planner.
CreateMaterializations bool true Create materializations on the fly.
TypeCoercion bool true Implicit type coercion during validation.
ApproximateDecimal bool false Allow approximate DECIMAL aggregate results.
ApproximateDistinctCount bool false Allow approximate COUNT(DISTINCT ...).
ApproximateTopN bool false Allow approximate Top-N results.
AutoTemp bool false Store query results in a temporary table.
NullEqualToEmpty bool true Treat empty strings as null, for the Druid adapter.
Spark bool false Use Spark as the in-process execution engine.
TopdownOpt bool calcite.planner.topdown.opt Enable top-down optimization in the Volcano planner.
LenientOperatorLookup bool false Silently create unknown functions during parsing.
DruidFetch int 16384 Rows to fetch per Druid query.
SchemaFactory string — Schema factory class name (when not using a model).
SchemaType string — Schema type: MAP, JDBC, or CUSTOM.
ParserFactory string — Custom SQL parser factory.
MetaTableFactory / MetaColumnFactory string — Avatica metadata factories.
TypeSystem string — Type system class name.

Defaults are Calcite's own, read from CalciteConnectionProperty in the version this package references (1.43).

CalciteConnectionPropertiesSchemaMap

Exposes the schema.*-prefixed sub-properties of a CalciteConnectionProperties instance as a typed dictionary, so operand values can be passed to a custom schema factory:

var props = new CalciteConnectionProperties();
props.SchemaProperties["directory"] = "data/csv";
props.SchemaProperties["flavor"]    = "scannable";
Package Purpose
Apache.Calcite.Data The ADO.NET provider. Executes SQL text through this convention.
Apache.Calcite.Adapter.AdoNet Exposes any ADO.NET data source to Calcite as a federated schema.

Further reading

License

Apache License 2.0.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on Apache.Calcite.Extensions:

Package Downloads
Apache.Calcite.Adapter.AdoNet

Apache Calcite Adapter for ADO.NET database connections.

Apache.Calcite.Data

Apache Calcite for ADO.NET. A native, in-process ADO.NET provider that runs the Apache Calcite SQL parser, planner, and runtime via IKVM.

Apache.Calcite.Cosmos.Adapter

Apache Calcite adapter that plans queries against Azure Cosmos DB by generating Cosmos SQL.

Apache.Calcite.Data.Common

What the Apache Calcite ADO.NET provider and the ADO.NET adapter both need. The CLR type mapping: which .NET type a Calcite SQL type is seen as, and the conversions across that boundary in both directions, through a chain of resolvers a caller can extend.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1-pre.254 0 9/28/2026
2.0.1-pre.250 0 9/27/2026
2.0.1-pre.236 44 9/26/2026
2.0.1-pre.230 52 9/26/2026
2.0.1-pre.190 151 9/20/2026
2.0.1-pre.188 311 9/19/2026
2.0.0-pre.9 188 9/1/2026
2.0.0-pre.8 258 8/31/2026
2.0.0-pre.7 266 8/14/2026
2.0.0-pre.5 125 8/12/2026
2.0.0-pre.4 117 8/11/2026
2.0.0-pre.3 149 8/10/2026
2.0.0-pre.2 142 8/9/2026
2.0.0-pre.1 151 8/9/2026
1.0.3 152 6/4/2026
1.0.2 129 5/28/2026
1.0.1 128 5/26/2026
1.0.0 112 5/26/2026
0.0.4 151 1/29/2026