kmb-query: SQL query layer for Kimberlite projections
This crate provides a minimal SQL query engine for compliance lookups against the projection store.
SQL Subset
Supported SQL features:
SELECTwith column list or*FROMsingle table orJOIN(INNER,LEFT,RIGHT,FULL OUTER,CROSS, withONorUSING(...)clauses)WHEREwith comparison predicates (=,<,>,<=,>=,IN)IN (SELECT ...),EXISTS,NOT EXISTS(uncorrelated subqueries)ORDER BY(ascending/descending)LIMITandOFFSET— literal or$NparameterGROUP BYwith aggregates (COUNT,SUM,AVG,MIN,MAX)- Aggregate
FILTER (WHERE ...)clauses, independent per aggregate HAVINGwith aggregate filteringUNION/UNION ALL/INTERSECT/INTERSECT ALL/EXCEPT/EXCEPT ALLDISTINCT- JSON operators
->,->>,@>in WHERE clauses CASE(searched and simple form)WITH(Common Table Expressions / CTEs), includingWITH RECURSIVEvia iterative fixed-point evaluation (depth cap 1000)- Subqueries in FROM and JOIN (
SELECT * FROM (SELECT ...) AS t) - Window functions (
OVER,PARTITION BY,ROW_NUMBER,RANK,DENSE_RANK,LAG,LEAD,FIRST_VALUE,LAST_VALUE) ALTER TABLE(ADD COLUMN, DROP COLUMN) — parser only; kernel execution pending- Parameterized queries (
$1,$2, ...) in WHERE, LIMIT, OFFSET, and DML values
Not yet supported:
- Correlated subqueries (uncorrelated only above)
- Scalar function projections (
UPPER,ROUND,EXTRACT, ...) in SELECT ILIKE,NOT LIKE,NOT ILIKECOALESCE,NULLIF,CASTin WHERE expressions
Usage
use kimberlite_query::{QueryEngine, Schema, SchemaBuilder, ColumnDef, DataType, Value};
use kimberlite_store::{BTreeStore, TableId};
// Define schema
let schema = SchemaBuilder::new()
.table(
"users",
TableId::new(1),
vec![
ColumnDef::new("id", DataType::BigInt).not_null(),
ColumnDef::new("name", DataType::Text).not_null(),
],
vec!["id".into()],
)
.build();
// Create engine
let engine = QueryEngine::new(schema);
// Execute query
let mut store = BTreeStore::open("data/projections")?;
let result = engine.query(&mut store, "SELECT * FROM users WHERE id = $1", &[Value::BigInt(42)])?;
Point-in-Time Queries
For compliance, you can query at a specific log position:
let result = engine.query_at(
&mut store,
"SELECT * FROM users WHERE id = 1",
&[],
Offset::new(1000), // Query state as of log position 1000
)?;