Skip to content

v0.6.2

Choose a tag to compare

@markhammond markhammond released this 25 Oct 16:02
· 17 commits to main since this release

ChalkQL v0.6.2

A patch release with two fixes found by a new sample, and the sample itself: a live trade blotter that reads PostgreSQL live, lays a bounded tail of streamed rows over each page, and keeps its filters and alerts as standing views. A DateTime too far in the future for a nanosecond timestamp is now refused rather than wrapped into the past, and text compared with a number it cannot be read as is refused as the statement's mistake rather than reported as the planner's.

Targets .NET 10. A JDK 21 or newer is needed only to run the planner locally. Assemblies and namespaces retain the Chalk.* names.

NuGet packages:

Package Licence
ChalkQL NuGet Elastic License 2.0 engine, planner client, catalogue, execution, entitlements and standing views
ChalkQL.Sources NuGet Elastic License 2.0 POCO, Akade.IndexedSet, ADO.NET, DuckDB and source conformance implementations

POCO timestamps past 2262 are refused

A DateTime member of a POCO table maps to TIMESTAMP(9) by default: a 64-bit count of nanoseconds since 1970, which reaches 1677-09-21 to 2262-04-11. The conversion multiplied ticks by 100 without a check, so a later instant wrapped silently. 9999-12-31, a common "never", was stored as 1816-03-29, and every comparison with it was wrong: until > TIMESTAMP '2030-01-01' matched none of them.

  • The POCO source refuses such an instant where the table is built or refreshed, naming the table and member, and saying to declare it [ChalkColumn(Precision = 6)]. Microseconds reach any instant a DateTime holds.
  • ClrStorage.UnitsOf refuses it too, so nothing that converts a tick count to a nanosecond lane wraps. The engine's lane codec already refused it.

Text beside a number is the statement's mistake

qty = 'x', where qty is a DOUBLE, failed with "Planner internal error … AssertionError: cannot convert CHAR literal to class java.lang.Double", coded Internal. The assertion was the planner's own: its selectivity estimate asked Calcite for the text as the column's type.

  • Text a number cannot read is refused while the statement is validated, at the text, with its position: "the text 'x' cannot be read as a DOUBLE, which qty is: compare qty with a number, such as 1.5, or with text that reads as one", coded SqlValidation. The same holds for <>, <, <=, >, >=, IN, NOT IN and BETWEEN, either side, and for whole numbers: '1.5' cannot be a BIGINT. Text that reads as the number, qty = '1.5', is a number written in quotes and stands.
  • Text beside a date or a time is read by ChalkQL's cast when the statement runs, which reads more than Calcite's literals do. ts = '2026-01-08 12:00:00+02:00' failed with the same internal error before and now runs; as before, a cast to TIMESTAMP keeps the wall-clock time and leaves the offset. Text the cast cannot read fails there, naming it.
  • Wherever Calcite cannot read a text literal, the planner refuses it as a validation error rather than an internal one, and the estimate falls back to its default guess.

A virtual blotter sample

dotnet/samples/Chalk.Sample.Blotter is a trade blotter in Blazor Server and QuickGrid over millions of client orders and fills in PostgreSQL, which it starts and seeds itself or takes from a connection string.

  • Bolted on. It expects no watermark of the source: it reads orders_current live, keeps what the streams send until the database is seen to hold it, and lays that tail over each page, each order once at its newest version. One gap fill, after a haircut for what is in transit when it subscribes, covers what was sent before.
  • ChalkQL holds the live part. Each tab and filter in use is a standing view over the tail. Alerts are conditions in SQL, with a CodeMirror editor that completes and checks them through ChalkQL, kept as incremental views over the host's tables: as rows arrive, once the database is too far behind them, or on how many match in a sliding window, at least so many or none.
  • One small budget. The tail, the views and recent reads are held to one memory budget, 8 MB to start, stepped up to 16, 32 and 64 MB rather than refuse when no budget is chosen.
  • Traffic shaping. On a slow link the grid keeps pace with the browser.
  • Tested against an oracle. Pages read while the market runs keep their invariants, and pages held still match PostgreSQL's own answer row for row. Every rule a clock decides is tested at its boundary on a fake clock.

The sample's alert sounds are not part of ChalkQL and not under its licence; they may not be used commercially (wwwroot/sounds/NOTICE.md).

Changed

  • A POCO DateTime past 2262 at the default precision is refused where it used to be stored as a wrong instant. Declare such a member [ChalkColumn(Precision = 6)].
  • Text a number cannot read is a SqlValidation error, where it was Internal.
  • Text with an offset beside a timestamp runs, where it failed.
  • CI runs the sample's tests, in the integration job, against the image's PostgreSQL.

Compatibility

  • No breaking changes A 0.6.1 planner serves a 0.6.2 client, without the new refusals, and a 0.6.2 planner serves a 0.6.1 client.
  • A host that keeps a far-future "never" in a POCO DateTime stops at its table until the member is declared in microseconds. Its comparisons were wrong before.

Full Changelog: v0.6.1...v0.6.2

Licence and attribution

ChalkQL is source available under the Elastic License 2.0 (ELv2).

Apache Calcite and its JVM dependencies are bundled into the planner artefact distributed with ChalkQL. Their licences and attribution are recorded in THIRD-PARTY-NOTICES.txt.