Expand description
§datum-sql
datum-sql is Datum’s SQL front end: DataFusion parses, optimizes, and builds
physical expressions; Datum lowers the supported physical plan into ordinary
Datum stream stages and materializes it with Datum sinks.
The crate uses Arrow RecordBatch values as the SQL data unit. Append-only
queries stay as Source<RecordBatch>. Updating streams use
Source<ChangelogBatch>, where row-level ChangeOp metadata stays outside
Arrow row data so projections and filters cannot accidentally drop it.
§Install
cargo add datum-sqlEnable connector adapters only when you need them:
cargo add datum-sql --features mq,cdc§What Works
- Register in-memory Datum sources as SQL tables.
- Register append-only, committable, and changelog/updating sources.
- Parse and plan SQL with DataFusion 54 over Arrow 58.
- Lower scans, projection, filter, coalesce, limit, windowed aggregation, and supported inner equi-joins into Datum stages.
- Run bounded append-only queries and collect Arrow batches.
- Build continuous
SqlEvent<RecordBatch>streams carrying data, watermarks, and future barrier markers outside row data. - Assign event-time watermarks from an Arrow timestamp column with bounded out-of-orderness.
- Register append-only and changelog-aware SQL sinks.
- Execute
INSERT INTO <registered_sink> SELECT ...with at-least-once source-position commit ordering for committable sources. - Opt into Kafka JSON table/sink adapters with the
mqfeature and PostgreSQL CDC changelog adapters with thecdcfeature.
§Quick Start
use std::sync::Arc;
use arrow::array::{Int64Array, StringArray};
use arrow::datatypes::{DataType, Field, Schema};
use arrow::record_batch::RecordBatch;
use datum::Source;
use datum_sql::DatumSqlContext;
let schema = Arc::new(Schema::new(vec![
Field::new("city", DataType::Utf8, false),
Field::new("temp", DataType::Int64, false),
]));
let batch = RecordBatch::try_new(
Arc::clone(&schema),
vec![
Arc::new(StringArray::from(vec!["sf", "nyc", "sea"])),
Arc::new(Int64Array::from(vec![67, 74, 58])),
],
)?;
let context = DatumSqlContext::new();
context.register_source("weather", schema, Source::from_iter([batch]))?;
let output = context
.execute("SELECT city, temp FROM weather WHERE temp >= 70")
.await?;
assert_eq!(output[0].num_rows(), 1);Run the checked example from the repository:
cargo run -p datum-sql --example projection_filter§Windowed Streaming Query
Event-time queries register the timestamp column at the table boundary. The
continuous lowering returns a Datum source of SqlEvent<RecordBatch> values, so
watermarks stay outside user row data.
use std::sync::Arc;
use std::time::Duration;
use arrow::array::{Int64Array, TimestampNanosecondArray};
use arrow::datatypes::{DataType, Field, Schema, TimeUnit};
use arrow::record_batch::RecordBatch;
use datum::Source;
use datum_sql::{DatumSqlContext, EventTimeConfig};
let schema = Arc::new(Schema::new(vec![
Field::new("event_time", DataType::Timestamp(TimeUnit::Nanosecond, None), false),
Field::new("auction", DataType::Int64, false),
Field::new("price", DataType::Int64, false),
]));
let batch = RecordBatch::try_new(
Arc::clone(&schema),
vec![
Arc::new(TimestampNanosecondArray::from(vec![1_000_000_000_i64])),
Arc::new(Int64Array::from(vec![42])),
Arc::new(Int64Array::from(vec![900])),
],
)?;
let context = DatumSqlContext::new();
context.register_source_with_event_time(
"bids",
schema,
Source::from_iter([batch]),
EventTimeConfig::bounded_out_of_orderness("event_time", Duration::from_secs(1)),
)?;
let source = context
.streaming_source(
"SELECT date_bin(INTERVAL '10 seconds', event_time) AS window_start, \
auction, count(*) AS bid_count \
FROM bids \
GROUP BY date_bin(INTERVAL '10 seconds', event_time), auction",
)
.await?;§Continuous Queries And Sinks
For streaming use, call streaming_source to get a Datum source of
SqlEvent<RecordBatch> values, or call execute_streaming to materialize a
continuous query. SqlEvent::Data carries user rows; SqlEvent::Watermark and
SqlEvent::Barrier carry stream control metadata outside Arrow columns.
let context = DatumSqlContext::new();
context.register_source("input", schema, Source::from_iter([batch]))?;
context.register_append_sink("out", |_batch| Ok(()))?;
let handle = context
.execute_streaming("INSERT INTO out SELECT id FROM input")
.await?;
handle.wait()?;§Feature Flags
datum-sql has no default features.
mqenables Kafka JSON source and sink adapters built ondatum-mq.cdcenables PostgreSQL CDC changelog table adapters built ondatum-cdc.
§Known Limitations
- SQL coverage is intentionally narrow. Unsupported DataFusion physical nodes
return
DataFusionError::NotImplementedinstead of falling back to DataFusion execution. - Streaming joins support append-only inner equi-joins. Outer joins, semi/anti joins, non-equi joins, and changelog joins are not implemented yet.
- Windowed aggregation supports fixed tumbling and hopping windows over
event-time input, with a conservative aggregate set. Session windows,
grouping sets, aggregate
FILTER, distinct aggregates, and processing-time windows are deferred. - Changelog-aware sinks are explicit. Append-only sinks reject updating streams; there is no implicit adapter that drops retractions.
- Sink execution is at-least-once. Exactly-once requires checkpoint barriers, durable operator state, and transactional sink recovery work that is still design-only.
- Kafka support is JSON-row oriented today. Avro, Protobuf, schema registry integration, and long-lived producer batching are future work.
- CDC decoding covers the current
datum-cdcpgoutput text-value shape. Initial snapshots, DDL/schema history, and binary pgoutput decoding are not provided bydatum-sql. - The SQL benchmark record is frozen in
roadmap/benchmarks/sql.md. Current durable verdicts include a q3 all-metric win, q4/q7 wall wins, corrected q8 wall wins after WP-SQL-F1, and q5/ingest misses with named follow-up levers; cite that record for exact numbers and caveats.
§Design References
The design record and go/no-go notes live in
roadmap/M11-datum-sql.md. Benchmark plans and
results live under roadmap/benchmarks/.
Re-exports§
pub use connect::BatchEnvelope;pub use connect::EnvelopedBatch;pub use connect::EnvelopedRecordBatch;
Modules§
- connect
- Connector adapters for
datum-sql.
Structs§
- Changelog
Batch - A typed changelog envelope over a plain Arrow
RecordBatch. - Committable
Record Batch - Append-only SQL batch plus source metadata and a deferred commit handle.
- Continuous
Query Handle - Materialized handle for a streaming SQL query.
- Datum
SqlContext - SQL planning context that registers Datum sources and executes supported DataFusion physical plans on Datum.
- Event
Time Config - Event-time declaration for a registered SQL table.
- Source
Commit - A committable source position.
- SqlBarrier
- Future checkpoint/alignment marker carried outside SQL row data.
- SqlExecution
Metrics - Per-query counters populated by SQL lowering and native Datum stages.
- Streaming
Join Config - Streaming join lowering policy.
- Streaming
Join Metrics - Counters maintained by streaming join stages.
- Streaming
Join State Limits - Hard limits for unbounded streaming equi-joins.
- Streaming
Join Window - Event-time interval join settings.
- Watermark
- Event-time progress in nanoseconds since the Unix epoch.
- Windowed
Aggregation Metrics - Counters maintained by a windowed aggregation stage.
Enums§
- Change
Op - Row-level operation for SQL streams that can revise prior output.
- SqlEvent
- In-band SQL stream envelope for data and control records.
- SqlSource
Position - Type-erased source position carried by SQL sink pipelines.
- Streaming
Join Mode - Supported streaming join state classes.
- Watermark
Strategy - Strategy for producing watermarks from an event-time column.
Traits§
- SqlEvent
Payload - Payloads that expose the Arrow batch used to read event-time columns.
Functions§
- assign_
event_ time_ watermarks - Assigns event-time watermarks to a source.
- data_
events - Wraps every payload from a source as
SqlEvent::Data. - map_
sql_ event_ data - Applies a function to the data branch of a SQL event stream.