use spg_engine::{Engine, QueryResult};
use spg_storage::Value;
fn rows(e: &mut Engine, sql: &str) -> Vec<Vec<Value<'static>>> {
let r = e
.execute(sql)
.unwrap_or_else(|err| panic!("{sql}: {err:?}"));
let QueryResult::Rows { rows, .. } = r else {
panic!("expected Rows for {sql}");
};
rows.into_iter().map(|r| r.values).collect()
}
fn one_i64(e: &mut Engine, sql: &str) -> i64 {
let mut rs = rows(e, sql);
let row = rs.pop().expect("one row");
match row.into_iter().next().expect("one col") {
Value::BigInt(n) => n,
Value::Int(n) => i64::from(n),
other => panic!("expected integer, got {other:?}"),
}
}
#[test]
fn gin_on_jsonb_accelerates_simple_containment() {
let mut e = Engine::new();
e.execute("CREATE TABLE issues (id BIGINT NOT NULL, labels JSONB)")
.unwrap();
e.execute("CREATE INDEX issues_labels_gin ON issues USING GIN (labels)")
.expect("real JSONB-GIN");
e.execute(r#"INSERT INTO issues (id, labels) VALUES (1, '{"team":"ios"}'::jsonb)"#)
.unwrap();
e.execute(r#"INSERT INTO issues (id, labels) VALUES (2, '{"team":"android"}'::jsonb)"#)
.unwrap();
e.execute(
r#"INSERT INTO issues (id, labels) VALUES (3, '{"team":"ios","sev":"high"}'::jsonb)"#,
)
.unwrap();
let result = rows(
&mut e,
r#"SELECT id FROM issues WHERE labels @> '{"team":"ios"}'::jsonb ORDER BY id"#,
);
assert_eq!(result.len(), 2);
assert_eq!(result[0][0], Value::BigInt(1));
assert_eq!(result[1][0], Value::BigInt(3));
}
#[test]
fn gin_on_jsonb_handles_multi_key_containment() {
let mut e = Engine::new();
e.execute("CREATE TABLE issues (id BIGINT NOT NULL, labels JSONB)")
.unwrap();
e.execute("CREATE INDEX issues_labels_gin ON issues USING GIN (labels)")
.unwrap();
e.execute(
r#"INSERT INTO issues (id, labels) VALUES (1, '{"team":"ios","sev":"high"}'::jsonb)"#,
)
.unwrap();
e.execute(r#"INSERT INTO issues (id, labels) VALUES (2, '{"team":"ios","sev":"low"}'::jsonb)"#)
.unwrap();
e.execute(
r#"INSERT INTO issues (id, labels) VALUES (3, '{"team":"android","sev":"high"}'::jsonb)"#,
)
.unwrap();
let result = rows(
&mut e,
r#"SELECT id FROM issues WHERE labels @> '{"team":"ios","sev":"high"}'::jsonb ORDER BY id"#,
);
assert_eq!(result.len(), 1);
assert_eq!(result[0][0], Value::BigInt(1));
}
#[test]
fn gin_on_jsonb_excludes_non_matching_rows() {
let mut e = Engine::new();
e.execute("CREATE TABLE issues (id BIGINT NOT NULL, labels JSONB)")
.unwrap();
e.execute("CREATE INDEX issues_labels_gin ON issues USING GIN (labels)")
.unwrap();
e.execute(r#"INSERT INTO issues (id, labels) VALUES (1, '{"team":"ios"}'::jsonb)"#)
.unwrap();
e.execute(r#"INSERT INTO issues (id, labels) VALUES (2, '{"sev":"high"}'::jsonb)"#)
.unwrap();
let result = rows(
&mut e,
r#"SELECT id FROM issues WHERE labels @> '{"team":"ios","sev":"high"}'::jsonb"#,
);
assert!(result.is_empty(), "expected no matches, got {result:?}");
}
#[test]
fn sentori_acceptance_probe_filters_to_matching_team() {
let mut e = Engine::new();
e.execute("CREATE TABLE issues (id BIGINT NOT NULL, labels JSONB)")
.unwrap();
e.execute("CREATE INDEX issues_labels_gin ON issues USING GIN (labels)")
.unwrap();
let teams = ["ios", "android", "web", "rn"];
for i in 0..2000i64 {
let team = teams[(i as usize) % teams.len()];
e.execute(&format!(
r#"INSERT INTO issues (id, labels) VALUES ({i}, '{{"team":"{team}"}}'::jsonb)"#
))
.unwrap();
}
assert_eq!(
one_i64(
&mut e,
r#"SELECT count(*) FROM issues WHERE labels @> '{"team":"ios"}'::jsonb"#
),
500
);
assert_eq!(
one_i64(
&mut e,
r#"SELECT count(*) FROM issues WHERE labels @> '{"team":"linux"}'::jsonb"#
),
0
);
}