Pure.RelationalSchema.Storage.PureQL.Projection 0.1.0-preview.4.1.0

This is a prerelease version of Pure.RelationalSchema.Storage.PureQL.Projection.
dotnet add package Pure.RelationalSchema.Storage.PureQL.Projection --version 0.1.0-preview.4.1.0
                    
NuGet\Install-Package Pure.RelationalSchema.Storage.PureQL.Projection -Version 0.1.0-preview.4.1.0
                    
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="Pure.RelationalSchema.Storage.PureQL.Projection" Version="0.1.0-preview.4.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Pure.RelationalSchema.Storage.PureQL.Projection" Version="0.1.0-preview.4.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Pure.RelationalSchema.Storage.PureQL.Projection" />
                    
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 Pure.RelationalSchema.Storage.PureQL.Projection --version 0.1.0-preview.4.1.0
                    
#r "nuget: Pure.RelationalSchema.Storage.PureQL.Projection, 0.1.0-preview.4.1.0"
                    
#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 Pure.RelationalSchema.Storage.PureQL.Projection@0.1.0-preview.4.1.0
                    
#: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=Pure.RelationalSchema.Storage.PureQL.Projection&version=0.1.0-preview.4.1.0&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Pure.RelationalSchema.Storage.PureQL.Projection&version=0.1.0-preview.4.1.0&prerelease
                    
Install as a Cake Tool

Pure.RelationalSchema.Storage.PureQL.Projection

Compiles PureQL queries over relational schema datasets into one LINQ expression tree: a PureQLQuery from PureQL.CSharp.Model becomes an IStoredTableDataSet whose IQueryable<IRow> tree a LINQ provider can translate to SQL, and which runs in memory when enumerated.

.NET build & test Build and Deploy NuGet License: MIT

Overview

Pure.RelationalSchema.Storage.PureQL.Projection is an interpreter for the PureQL specification 0.1.0-preview.2.0.1 over the Pure relational storage model. Given a collection of IStoredSchemaDataSet objects and a PureQLQuery, it produces an IStoredTableDataSet whose rows are the query's result under the specification's semantics.

The query is compiled when the projection is built to a single IQueryable<IRow> expression tree made of standard query operators over one root per stored table (see Expression tree). Enumerating the projection runs that tree in memory with LINQ to Objects; a provider that translates the tree (to PostgreSQL, across several databases) reads projection.Expression instead.

Public API

PureQLProjection

The only public type. Implements IStoredTableDataSet, IAsyncEnumerable<IRow> and IQueryable<IRow>.

Member Description
PureQLProjection(IEnumerable<IStoredSchemaDataSet> datasets, PureQLQuery query) Validates the query against the datasets' schemas and builds the projection.
PureQLProjection(IEnumerable<IStoredSchemaDataSet> datasets, Query query) The same for a query without subqueries (the model's subquery type).
ITable TableSchema An unnamed table with one column per select item, named by its alias and typed by its declared type.
IEnumerator<IRow> GetEnumerator() Runs the query and enumerates the result rows. Every enumeration runs the query again.
IAsyncEnumerator<IRow> GetAsyncEnumerator(...) Async enumeration over the same rows.
Expression Expression The query's expression tree, for a LINQ provider to translate (see below).
Type ElementType / IQueryProvider Provider IQueryable<IRow> plumbing: LINQ composed over the projection runs in memory.

What is supported

Every clause and operator of the specification: from and joins (inner, left, right, full, aliases and self-joins), where, groupBy over any row expression with keys referenced by alias, having, select, aggregate (total queries), orderBy over any expression, distinctBy, pagination, and subqueries read through from, join and in. All operators in the row, projection, group and total contexts are supported, including aggregates with predicate and over, and comparisons, min and max over every type.

A plain query returns one row per row left by where, a total query exactly one row (even over no rows) and a grouped query one row per group. distinctBy runs after orderBy and keeps, of the rows equal on the listed columns, the first in order.

The semantics are the specification's: null equals null and sorts first, ordering comparisons against null are false, lifted operators are null on a null operand, integer is a checked 64-bit integer, decimal is exact (System.Decimal), strings compare by code point, a datetime is an instant written in UTC, and seconds and time / datetime averages round half away from zero to the microsecond. Overflow, division by zero and dates outside the calendar fail the query with the corresponding .NET exception.

The checks the specification leaves to the interpreter run when the projection is built, and a failing query raises ArgumentException: entities, subqueries, sources and fields exist; each field declares its column's type and, after outer joins, its nullability; group key references name a key and repeat its type; source names, group key aliases, column aliases and subquery names are unique; distinctBy lists only column aliases; a subquery reads only subqueries declared before it.

Expression tree

projection.Expression is built only from constructs a SQL provider translates: Queryable operators (Enumerable ones only over a group or a list), members and methods of the BCL's value types, strings and Math, value tuples, constants and captured parameters. Nothing in it is compiled, invoked or evaluated on the client except the result rows, built by the last Select. Its shape is fixed:

scan(from) [.Where] (.SelectMany(join))* [.Where(rest of where)]
  plain:   [.OrderBy/ThenBy] .Select(columns)
  grouped: .GroupBy(keys) [.Where(having)] [.OrderBy/ThenBy] .Select(columns)
  total:   one-row constant .Select(aggregates over the rows)
[.DistinctBy(columns)] [.Skip] [.Take]
  • Sources. Every stored table is its own root, Constant(IStoredTableDataSet) typed IQueryable<IRow>, so a provider can tell tables (and their databases) apart and split the tree there. A table is always scanned as root.Select(row => (field, ...)), reading only the fields the query reads; a field read is IsNullOrEmpty(row.Cells[column].Value.TextValue) ? null : Parse(row.Cells[column].Value.TextValue, ...), the IColumn a constant. A subquery is inlined where it is read.
  • Rows are value tuples: a source row holds its fields, a joined row one source row per source, a group key one value per key, a query result one value per column. Every value is nullable: long? (integer), decimal?, string, bool?, DateOnly?, TimeOnly?, DateTime? (UTC), Guid?.
  • Joins are a correlated SelectMany (DefaultIfEmpty for an outer side); full is the left join Concat the unmatched joined rows (!Any(...)). An unmatched side is the default tuple: all nulls.
  • Aggregates are LINQ's over the group or, over all rows, over the query's rows, which are one shared subtree. A total query reads a one-row constant source, as SQL's SELECT without FROM.
  • Semantics are C#'s, which match the specification: null == null, an ordering comparison with null is false, lifted operators. Strings compare with string.CompareOrdinal and order with StringComparer.Ordinal (SQL's "C" collation, code point order); ascending order puts null first. Parameters are captured variables, the Value of a constant holding the parameter, as C# closures are; reading one fails while parameters cannot be bound.

Built in, before any provider sees it:

  • constants fold (an operation that fails, like 1 / 0, is kept and fails the query), true/false fold out of conditions, lifting is dropped where a value cannot be null and an operation over a null literal is null;
  • where is split into its and-ed conditions, each moved to the scan of the source it reads, or into the join that first sees every source it reads, unless an outer join may leave one of them without a row; a condition on a subquery source moves into the subquery (onto its having when it reads an aggregate);
  • adjacent Wheres merge, Where(true) and identity Selects are dropped, consecutive Selects are one, and pagination comes last;
  • a repeated order key or one that is the same for every row is dropped, a distinctBy over every group key is dropped, and a subquery read only by in (without pagination) is neither ordered nor distinctBy-filtered.

Run in memory, each stored table is read once per enumeration, a query nested in a lambda that reads nothing of it (a joined source, an in list, an aggregate over all rows) is computed once, and strings order by code point.

Storage mapping

Column type PureQL type
long, int, uint, ulong, ushort integer
double, float decimal
string, bool, date, time, datetime, uuid string, boolean, date, time, datetime, uuid

Empty cell text is NULL. A stored datetime without an offset is read as UTC. Result columns are typed long for integer and double for decimal; result cells hold invariant text (True/False, yyyy-MM-dd, HH:mm:ss[.fffffff], yyyy-MM-ddTHH:mm:ss[.fffffff] in UTC, lower case uuids).

Not supported

  • Parameters. There is no API to bind parameter values, so reading a parameter (in an expression, a list or pagination) raises NotSupportedException when the query runs; the tree is built and holds the parameter as a captured variable.
  • Stored times and datetimes are read to the .NET tick (100 ns); the specification's values have microsecond precision, so finer stored digits are kept as read.

Dependencies

Target Frameworks

  • .NET 8
  • .NET 9
  • .NET 10

Installation

dotnet add package Pure.RelationalSchema.Storage.PureQL.Projection

Usage

// datasets: IEnumerable<IStoredSchemaDataSet> populated by your storage layer
// query:    a PureQLQuery, built directly or deserialized with
//           PureQL.CSharp.Model.Serialization

IStoredTableDataSet result = new PureQLProjection(datasets, query);

foreach (IRow row in result)
{
    // ...
}

await foreach (IRow row in result)
{
    // ...
}

A query selecting one field:

PureQLQuery query = new PureQLQuery(
    new MainPlainQuery(
        new From(new FromEntity("mySchema.myTable")),
        [
            new SelectItemProjection(
                new SelectItemProjectionNonNullable(
                    new SelectItemProjectionString(
                        "name",
                        new StringProjection(new FieldString("mySchema.myTable", "name"))
                    )
                )
            ),
        ]
    )
);

PureQL.CSharp.Model.Samples holds a query for every feature of the specification; this package's tests run all of them.

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 is compatible.  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 is compatible.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-preview.4.1.0 33 10/15/2026
0.1.0-preview.4.0.0 45 10/14/2026
0.1.0-preview.3.0.0 66 10/10/2026
0.1.0-preview.2.2.2 519 7/24/2026
0.1.0-preview.2.2.1 106 7/22/2026
0.1.0-preview.2.2.0 87 7/22/2026
0.1.0-preview.2.1.3 203 7/14/2026
0.1.0-preview.2.1.2 127 7/12/2026
0.1.0-preview.2.1.1 104 7/11/2026
0.1.0-preview.2.1.0 207 6/23/2026
0.1.0-preview.2.0.1 128 6/15/2026
0.1.0-preview.2.0.0 219 5/28/2026
0.1.0-preview.1.0.1 627 3/21/2026
0.1.0-preview.1.0.0 268 3/9/2026
0.1.0-preview.0.1.0 143 2/25/2026