use serde_json::json;
use crate::engine::Engine;
use crate::session::SessionContext;
use crate::storage_adapter::Memory;
async fn open_session() -> SessionContext<Memory> {
let storage = Memory::new();
Engine::initialize(storage.clone())
.await
.expect("engine should initialize");
let engine = Engine::new(storage.clone())
.await
.expect("engine should open");
engine.open_session().await.expect("session should open")
}
async fn register(session: &SessionContext<Memory>, key: &str) {
let schema = json!({
"$schema": "https://lix.dev/schema-v1.json",
"key": key,
"columns": [
{ "name": "id", "type": "text", "nullable": false },
{ "name": "k", "type": "text", "nullable": false },
{ "name": "v", "type": "jsonb", "nullable": false },
],
"primary_key": ["id"],
});
session
.execute(
"INSERT INTO lix_registered_schema (value) VALUES (CAST($1 AS JSONB))",
&[crate::Value::Text(schema.to_string())],
)
.await
.expect("schema should register");
}
async fn seed(session: &SessionContext<Memory>, table: &str, count: usize) {
const CHUNK: usize = 250;
let mut index = 0;
while index < count {
let end = (index + CHUNK).min(count);
let values = (index..end)
.map(|i| format!("('r-{i}', 'k-{i}', CAST('{{\"n\":{i}}}' AS JSONB))"))
.collect::<Vec<_>>()
.join(",");
session
.execute(
&format!("INSERT INTO {table} (id, k, v) VALUES {values}"),
&[],
)
.await
.expect("rows should insert");
index = end;
}
}
#[tokio::test]
#[ignore = "measurement probe, not a gate"]
async fn json_column_equality_materialization() {
let n: usize = std::env::var("LIX_JPP_ROWS")
.ok()
.and_then(|raw| raw.parse().ok())
.unwrap_or(10_000);
println!("jpp | arm,n,answer_rows,provider_rows_examined,scan_rows,examined_per_answer_row");
{
let session = open_session().await;
register(&session, "jppstr").await;
seed(&session, "jppstr", n).await;
let (result, profile) = session
.execute_profiled("SELECT id FROM jppstr WHERE k = 'k-7'", &[])
.await
.expect("string-column scan should run");
println!(
"string,{n},{},{},{},{}",
result.rows().len(),
profile.provider_rows_examined,
profile.scan_rows,
profile.provider_rows_examined as f64 / result.rows().len().max(1) as f64
);
assert_eq!(
result.rows().len(),
1,
"the string arm must answer exactly one row"
);
}
{
let session = open_session().await;
register(&session, "jppjson").await;
seed(&session, "jppjson", n).await;
let text_spelling = session
.execute("SELECT id FROM jppjson WHERE v = '{\"n\":7}'", &[])
.await;
println!(
"jpp | text_spelling_err={:?}",
text_spelling.as_ref().err().map(|error| error.code.clone())
);
assert!(
text_spelling.is_err(),
"the text spelling must remain a type error"
);
let (result, profile) = session
.execute_profiled(
"SELECT id FROM jppjson WHERE v = CAST('{\"n\":7}' AS JSONB)",
&[],
)
.await
.expect("json-column scan should run");
println!(
"json,{n},{},{},{},{}",
result.rows().len(),
profile.provider_rows_examined,
profile.scan_rows,
profile.provider_rows_examined as f64 / result.rows().len().max(1) as f64
);
assert_eq!(
result.rows().len(),
1,
"the json arm must answer exactly one row"
);
}
{
let session = open_session().await;
register(&session, "jpppk").await;
seed(&session, "jpppk", n).await;
let (result, profile) = session
.execute_profiled("SELECT id FROM jpppk WHERE id = 'r-7'", &[])
.await
.expect("primary-key scan should run");
println!(
"primary_key,{n},{},{},{},{}",
result.rows().len(),
profile.provider_rows_examined,
profile.scan_rows,
profile.provider_rows_examined as f64 / result.rows().len().max(1) as f64
);
assert_eq!(result.rows().len(), 1, "the pk arm must answer one row");
}
}