#![allow(clippy::type_complexity)]
#![allow(clippy::collapsible_match)]
#![allow(clippy::collapsible_else_if)]
#![allow(unused_variables)]
#![allow(unused_mut)]
use crate::db::pg::mutability::mutability_for;
use crate::db::value::{DataValue, NamedRows};
use postgres::types::ToSql;
use std::collections::BTreeMap;
#[derive(Debug)]
pub struct Translation {
pub sql: String,
pub params: Vec<Box<dyn ToSql + Sync + Send>>,
pub kind: TranslationKind,
pub head: Vec<String>,
pub gucs: Vec<(String, String)>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TranslationKind {
Read,
Write,
DdlNoop,
}
impl Translation {
fn read(sql: String, params: Vec<Box<dyn ToSql + Sync + Send>>, head: Vec<String>) -> Self {
Self {
sql,
params,
kind: TranslationKind::Read,
head,
gucs: Vec::new(),
}
}
fn read_with_gucs(
sql: String,
params: Vec<Box<dyn ToSql + Sync + Send>>,
head: Vec<String>,
gucs: Vec<(String, String)>,
) -> Self {
Self {
sql,
params,
kind: TranslationKind::Read,
head,
gucs,
}
}
fn write(sql: String, params: Vec<Box<dyn ToSql + Sync + Send>>) -> Self {
Self {
sql,
params,
kind: TranslationKind::Write,
head: Vec::new(),
gucs: Vec::new(),
}
}
fn write_with_gucs(
sql: String,
params: Vec<Box<dyn ToSql + Sync + Send>>,
gucs: Vec<(String, String)>,
) -> Self {
Self {
sql,
params,
kind: TranslationKind::Write,
head: Vec::new(),
gucs,
}
}
fn ddl_noop(head: Vec<String>) -> Self {
Self {
sql: String::new(),
params: Vec::new(),
kind: TranslationKind::DdlNoop,
head,
gucs: Vec::new(),
}
}
}
fn json_to_pg(v: serde_json::Value) -> Box<dyn ToSql + Sync + Send> {
match v {
serde_json::Value::Null => Box::new(Option::<String>::None),
serde_json::Value::Bool(b) => Box::new(b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Box::new(i)
} else if let Some(f) = n.as_f64() {
Box::new(f)
} else {
Box::new(n.to_string())
}
}
serde_json::Value::String(s) => Box::new(s),
other => Box::new(other),
}
}
pub fn translate(
query: &str,
params: BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let trimmed = query.trim();
let _ = mutability_for(query);
let body = trimmed;
if body.is_empty() {
return Err("empty query".into());
}
if let Some(rest) = strip_prefix(body, "::relations") {
return Ok(relations_introspection());
}
if let Some(rest) = strip_prefix(body, "::hnsw") {
return hnsw_ddl(rest);
}
if let Some(rest) = strip_prefix(body, "::index") {
return index_ddl(rest);
}
if let Some(rest) = strip_prefix(body, ":schema") {
return schema_introspection(rest);
}
if let Some(rest) = strip_prefix(body, ":create") {
return create_ddl(rest);
}
if let Some(rest) = strip_prefix(body, ":replace") {
return replace_ddl(rest);
}
if let Some(rest) = strip_prefix(body, ":delete") {
return delete_where(rest, ¶ms);
}
if let Some(rest) = strip_prefix(body, ":put") {
return put_script(rest, ¶ms);
}
if let Some(rest) = strip_prefix(body, ":rm") {
return rm_script(rest, ¶ms);
}
if let Some(rest) = strip_prefix(body, "PRAGMA") {
return Ok(Translation::ddl_noop(Vec::new()));
}
if let Some(rest) = strip_prefix(body, "VACUUM") {
return Ok(Translation::ddl_noop(Vec::new()));
}
if body.starts_with("?[") {
return read_script(body, ¶ms);
}
if split_rule_pair(body).is_some() {
return read_script(body, ¶ms);
}
Err(format!(
"unrecognized script (no leading operator): {}",
&body[..body.len().min(80)]
))
}
fn strip_prefix<'a>(s: &'a str, p: &str) -> Option<&'a str> {
if let Some(rest) = s.strip_prefix(p) {
if rest.is_empty() || rest.starts_with(|c: char| c.is_whitespace()) {
return Some(rest);
}
}
None
}
fn find_write_operator(body: &str) -> Option<WriteOp> {
const OPS: &[&str] = &[":put", ":rm", ":replace", ":delete", ":create"];
let mut best: Option<(usize, &str)> = None;
for op in OPS {
let mut start = 0;
while let Some(idx) = body[start..].find(op) {
let abs = start + idx;
let preceded_ok = abs == 0
|| body.as_bytes()[abs - 1].is_ascii_whitespace()
|| body.as_bytes()[abs - 1] == b',';
if preceded_ok {
match best {
Some((prev, _)) if prev > abs => {}
_ => best = Some((abs, op)),
}
}
start = abs + op.len();
}
}
best.map(|(idx, kind)| WriteOp {
idx,
kind: kind.trim_start_matches(':'),
})
}
struct WriteOp {
idx: usize,
kind: &'static str,
}
fn read_script(
body: &str,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let write_op = find_write_operator(body);
if let Some(op) = write_op {
return match op.kind {
"put" => put_script(body, params),
"rm" => rm_script(body, params),
"replace" => replace_ddl(body),
"delete" => delete_where(body, params),
"create" => create_ddl(body),
_ => Err(format!("unsupported trailing operator: {}", op.kind)),
};
}
if let Some((first_rule, rest_rule)) = split_rule_pair(body) {
if let Some(agg) = aggregate_from_head(&parse_head(&rest_rule)?) {
let (rel_name, rel_cols, after_rel) = match parse_relation_block(&first_rule) {
Some(parts) => parts,
None => return Err(format!("cannot parse intermediate rule in: {first_rule}")),
};
let counted_col = rel_cols
.iter()
.position(|c| c == &agg.expr)
.and_then(|i| CODE_ELEMENTS_COLUMNS.get(i))
.map(|c| c.to_string());
let expr = counted_col.unwrap_or(agg.expr.clone());
return aggregate_query(
&rel_name,
&rel_cols,
AggSpec {
kind: AggKind::Count,
expr,
distinct: true,
extras: Vec::new(),
head_label: Some(format!("count({})", agg.expr)),
},
after_rel,
String::new(),
params,
);
}
}
let (head, rest) = if let Some(idx) = body.find(":=") {
(&body[..idx], &body[idx + 2..])
} else if let Some(idx) = body.find("<-") {
(&body[..idx], &body[idx + 2..])
} else if let Some(idx) = body.find(':') {
(&body[..idx], &body[idx + 1..])
} else {
return Err("read script missing rule separator".into());
};
let head = parse_head(head)?;
if rest.contains("~embedding_vectors") {
return ann_translation(rest, &head, params);
}
let (relation, rel_cols, body_after_rel) = match parse_relation_block(rest) {
Some(parts) => parts,
None => return Err(format!("cannot parse relation block in: {rest}")),
};
let (filters, group_order_limit) = split_filters_and_modifiers(&body_after_rel);
if let Some(agg) = aggregate_from_head(&head) {
return aggregate_query(
&relation,
&rel_cols,
agg,
filters,
group_order_limit,
params,
);
}
if let Some(not_rel) = extract_not_exists(&filters) {
return not_exists_query(&relation, &rel_cols, &head, ¬_rel, params);
}
if filters.contains("*\x00") || (rest.matches("*\x00").count() > 1) {
}
if rest.matches('*').count() > 1 {
}
simple_select(
&relation,
&rel_cols,
&head,
filters,
group_order_limit,
params,
)
}
fn aggregate_from_head(head: &[String]) -> Option<AggSpec> {
if head.len() == 1 && head[0].starts_with("count(") && head[0].ends_with(')') {
let inner = &head[0][6..head[0].len() - 1];
return Some(AggSpec {
kind: AggKind::Count,
expr: inner.to_string(),
distinct: false,
extras: Vec::new(),
head_label: Some(head[0].clone()),
});
}
if head.len() == 1 && head[0].starts_with("count(DISTINCT ") && head[0].ends_with(')') {
let inner = &head[0][14..head[0].len() - 1];
return Some(AggSpec {
kind: AggKind::Count,
expr: inner.to_string(),
distinct: true,
extras: Vec::new(),
head_label: Some(head[0].clone()),
});
}
if head
.iter()
.any(|h| h.starts_with("count(") && h.ends_with(')'))
{
let mut extras = Vec::new();
for h in head {
if h.starts_with("count(") && h.ends_with(')') {
let inner = &h[6..h.len() - 1];
return Some(AggSpec {
kind: AggKind::Count,
expr: inner.to_string(),
distinct: false,
extras,
head_label: Some(h.clone()),
});
}
extras.push(h.clone());
}
}
None
}
#[derive(Debug)]
struct AggSpec {
kind: AggKind,
expr: String,
distinct: bool,
extras: Vec<String>,
head_label: Option<String>,
}
#[derive(Debug, PartialEq, Eq)]
enum AggKind {
Count,
}
fn parse_head(head: &str) -> Result<Vec<String>, String> {
let t = head.trim();
if !t.starts_with("?[") {
return Err(format!("bad head: {t}"));
}
let close = t[2..]
.find(']')
.ok_or_else(|| format!("bad head (no closing bracket): {t}"))?;
let inner = &t[2..2 + close];
Ok(inner
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect())
}
const CODE_ELEMENTS_COLUMNS: &[&str] = &[
"qualified_name",
"element_type",
"name",
"file_path",
"line_start",
"line_end",
"language",
"parent_qualified",
"cluster_id",
"cluster_label",
"metadata",
"env",
"ontology_layer",
];
const RELATIONSHIPS_COLUMNS: &[&str] = &[
"source_qualified",
"target_qualified",
"rel_type",
"confidence",
"metadata",
"env",
];
fn canonical_columns(relation: &str) -> &'static [&'static str] {
match relation {
"code_elements" => CODE_ELEMENTS_COLUMNS,
"relationships" => &[
"source_qualified",
"target_qualified",
"rel_type",
"confidence",
"metadata",
"env",
],
"incidents" => &[
"id",
"env",
"title",
"severity",
"occurred_at",
"resolved_at",
"root_cause",
"resolution",
"affected_services",
"trigger_pattern",
"prevention",
"tags",
"author",
"linked_ticket",
],
"knowledge_entries" => &[
"id",
"knowledge_type",
"title",
"content",
"element_qualified",
"user_story_id",
"feature_id",
"tags",
"environment",
"branch",
"author",
"created_at",
"updated_at",
],
"business_logic" => &[
"element_qualified",
"description",
"user_story_id",
"feature_id",
],
_ => CODE_ELEMENTS_COLUMNS,
}
}
fn split_rule_pair(body: &str) -> Option<(String, String)> {
let lines: Vec<&str> = body.lines().map(|l| l.trim()).collect();
for (i, line) in lines.iter().enumerate() {
if i == 0 {
continue;
}
if line.starts_with("?[") {
let first = lines[..i].join("\n");
let second = lines[i..].join("\n");
return Some((first, second));
}
}
None
}
fn parse_relation_block(rest: &str) -> Option<(String, Vec<String>, String)> {
let bytes = rest.as_bytes();
let star = rest.find('*')?;
let after_star = rest[star + 1..].trim_start();
let rel_end = after_star.find(['[', '{']).unwrap_or(after_star.len());
let rel_name = after_star[..rel_end].trim().to_string();
if rel_name.is_empty() {
return None;
}
let open = after_star.as_bytes()[rel_end] as char;
let close = match open {
'[' => ']',
'{' => '}',
_ => return None,
};
let body_start = rel_end + 1;
let body_end_rel = after_star[body_start..].find(close)? + body_start;
let cols_str = &after_star[body_start..body_end_rel];
let after_rel = &after_star[body_end_rel + 1..];
let cols: Vec<String> = if open == '[' {
cols_str.split(',').map(|s| s.trim().to_string()).collect()
} else {
cols_str
.split(',')
.map(|s| {
let s = s.trim();
s.split('=').next().unwrap_or("_").trim().to_string()
})
.collect()
};
Some((rel_name, cols, after_rel.to_string()))
}
fn split_filters_and_modifiers(body: &str) -> (String, String) {
let positions = [
body.find(":limit"),
body.find(":offset"),
body.find(":order"),
body.find(":group"),
];
let first_mod = positions.iter().filter_map(|&p| p).min();
match first_mod {
Some(idx) => (body[..idx].to_string(), body[idx..].to_string()),
None => (body.to_string(), String::new()),
}
}
fn extract_not_exists(filters: &str) -> Option<(String, Vec<String>)> {
let trimmed = filters.trim().trim_start_matches(',').trim();
let after_not = trimmed.strip_prefix("not ")?.trim_start();
if !after_not.starts_with('*') {
return None;
}
let (rel, cols, _) = parse_relation_block(after_not)?;
Some((rel, cols))
}
fn not_exists_query(
relation: &str,
rel_cols: &[String],
head: &[String],
not_rel: &(String, Vec<String>),
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let (inner_rel, inner_cols) = not_rel;
let outer_join_col = rel_cols
.iter()
.find(|c| !c.starts_with('_') && inner_cols.iter().any(|ic| ic == *c))
.cloned()
.unwrap_or_else(|| "qualified_name".to_string());
let (inner_join_col, outer_join_col) =
if inner_rel == "relationships" && !inner_cols.iter().any(|c| c == &outer_join_col) {
("source_qualified".to_string(), outer_join_col)
} else {
(outer_join_col.clone(), outer_join_col)
};
let cols_sql = head
.iter()
.map(|c| quote_ident(c))
.collect::<Vec<_>>()
.join(", ");
let sql = format!(
"SELECT {cols_sql} FROM {relation} WHERE NOT EXISTS (SELECT 1 FROM {inner_rel} \
WHERE {inner_rel}.{inner_join_col} = {relation}.{outer_join_col})"
);
Ok(Translation::read(sql, Vec::new(), head.to_vec()))
}
fn simple_select(
relation: &str,
rel_cols: &[String],
head: &[String],
filters: String,
modifiers: String,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
if head.is_empty() {
return Err("empty head in SELECT".into());
}
let mut def_exprs: std::collections::HashMap<&str, String> = std::collections::HashMap::new();
for clause in split_clauses(&filters) {
let trimmed = clause.trim();
for h in head {
let prefix = format!("{} =", h);
if trimmed.starts_with(&prefix) && !trimmed[prefix.len()..].starts_with('=') {
let rhs = trimmed[prefix.len()..].trim();
if !(rhs.starts_with('"') || rhs.starts_with('$') || rhs == "null") {
def_exprs.insert(h.as_str(), rhs.to_string());
}
}
}
}
let mut select_parts: Vec<String> = Vec::with_capacity(head.len());
let mut order_by_span = false;
for c in head {
if let Some((alias, expr)) = c.split_once('=') {
let alias = alias.trim();
let expr = expr.trim();
let resolved = resolve_positional(expr, rel_cols);
if alias == "span" && expr == "line_end - line_start" {
order_by_span = true;
}
select_parts.push(format!("{resolved} AS {}", quote_ident(alias)));
} else if let Some(expr) = def_exprs.get(c.as_str()) {
let resolved = resolve_positional(expr, rel_cols);
if c == "span" {
order_by_span = true;
}
select_parts.push(format!("{resolved} AS {}", quote_ident(c)));
} else {
let resolved = if is_positional_alias(c, rel_cols) {
let idx = rel_cols.iter().position(|x| x == c).unwrap();
column_at_for(relation, idx)
.map(quote_ident)
.unwrap_or_else(|| quote_ident(c))
} else {
quote_ident(c)
};
select_parts.push(resolved);
}
}
let cols_sql = select_parts.join(", ");
let filters = strip_definition_clauses(&filters, head);
let filters = inline_def_aliases(&filters, &def_exprs);
let filters = resolve_filter_aliases(relation, &filters, rel_cols);
let filters = append_literal_constraints(&filters, rel_cols);
let (where_sql, where_params) = compile_filters(filters, params)?;
let (mut mod_sql, mod_params) = compile_modifiers(&modifiers, head, params);
if order_by_span && !mod_sql.contains("ORDER BY") {
mod_sql = format!("{mod_sql} ORDER BY \"span\" DESC")
.trim()
.to_string();
}
let sql = if where_sql.is_empty() {
format!(
"SELECT {cols_sql} FROM {relation}{mod_sql}",
mod_sql = if mod_sql.is_empty() {
String::new()
} else {
format!(" {mod_sql}")
}
)
} else {
format!(
"SELECT {cols_sql} FROM {relation} WHERE {where_sql}{mod_sql}",
mod_sql = if mod_sql.is_empty() {
String::new()
} else {
format!(" {mod_sql}")
}
)
};
let mut all_params = where_params;
all_params.extend(mod_params);
Ok(Translation::read(sql, all_params, head.to_vec()))
}
fn resolve_filter_aliases(relation: &str, filters: &str, rel_cols: &[String]) -> String {
if rel_cols.is_empty() {
return filters.to_string();
}
let mut out = filters.to_string();
for (i, alias) in rel_cols.iter().enumerate() {
if alias.starts_with('_') || alias == "env" {
continue;
}
let real = column_at_for(relation, i)
.map(|c| c.to_string())
.unwrap_or_else(|| alias.clone());
if real == *alias {
continue;
}
let boundaries = [" ", ",", "("];
for b in &boundaries {
let pat = format!("{b}{alias} ");
let pat2 = format!("{b}{alias}[");
let pat3 = format!("{b}{alias},"); out = out.replace(&pat, &format!("{b}{real} "));
out = out.replace(&pat2, &format!("{b}{real}["));
out = out.replace(&pat3, &format!("{b}{real},"));
}
for pat3 in [format!("{alias} "), format!("{alias}[")] {
if out.starts_with(&pat3) {
out = format!("{real}{}", &out[alias.len()..]);
}
}
}
out
}
fn append_literal_constraints(filters: &str, rel_cols: &[String]) -> String {
use std::fmt::Write;
let mut out = filters.to_string();
for (i, token) in rel_cols.iter().enumerate() {
let t = token.trim();
if t.len() >= 2 && t.starts_with('"') && t.ends_with('"') {
let lit = &t[1..t.len() - 1];
let lit_esc = lit.replace('\'', "''");
if let Some(col) = column_at(i) {
write!(
out,
"{}{} = '{}'",
if out.trim().is_empty() || out.trim().ends_with(',') {
""
} else {
", "
},
quote_ident(col),
lit_esc
)
.unwrap();
}
}
}
out
}
fn column_at(i: usize) -> Option<&'static str> {
for cols in [CODE_ELEMENTS_COLUMNS, RELATIONSHIPS_COLUMNS] {
if let Some(c) = cols.get(i) {
return Some(c);
}
}
None
}
fn column_at_for(relation: &str, i: usize) -> Option<&'static str> {
if relation == "code_elements" {
return CODE_ELEMENTS_COLUMNS.get(i).copied();
}
if relation == "relationships" {
return RELATIONSHIPS_COLUMNS.get(i).copied();
}
None
}
fn strip_definition_clauses(filters: &str, head: &[String]) -> String {
let mut out: Vec<&str> = Vec::new();
for clause in split_clauses(filters) {
let trimmed = clause.trim();
let is_definition = head.iter().any(|col| {
let prefix = format!("{} =", col);
if !trimmed.starts_with(&prefix) || trimmed[prefix.len()..].starts_with('=') {
return false;
}
let rhs = trimmed[prefix.len()..].trim();
!(rhs.starts_with('"') || rhs.starts_with('$') || rhs == "null")
});
if !is_definition {
out.push(trimmed);
}
}
out.join(", ")
}
fn inline_def_aliases(
filters: &str,
def_exprs: &std::collections::HashMap<&str, String>,
) -> String {
if def_exprs.is_empty() || filters.is_empty() {
return filters.to_string();
}
let mut out = filters.to_string();
for (alias, expr) in def_exprs {
let re = format!(r"(^|[^\w$])({})([^\w]|$)", regex::escape(alias));
let rx = regex::Regex::new(&re).unwrap();
out = rx.replace_all(&out, format!("$1({})$3", expr)).to_string();
}
out
}
fn is_positional_alias(name: &str, rel_cols: &[String]) -> bool {
if !rel_cols.iter().any(|c| c == name) {
return false;
}
for cols in [
&[
"qualified_name",
"element_type",
"name",
"file_path",
"line_start",
"line_end",
"language",
"parent_qualified",
"cluster_id",
"cluster_label",
"metadata",
"env",
"ontology_layer",
][..],
&[
"source_qualified",
"target_qualified",
"rel_type",
"confidence",
"metadata",
"env",
][..],
] {
if cols.contains(&name) {
return false;
}
}
true
}
fn resolve_positional(expr: &str, rel_cols: &[String]) -> String {
let mut out = String::with_capacity(expr.len());
let mut rest = expr;
while !rest.is_empty() {
let token_end = rest
.find(|c: char| {
c.is_whitespace()
|| c == '-'
|| c == '+'
|| c == '*'
|| c == '/'
|| c == '('
|| c == ')'
})
.unwrap_or(rest.len());
let token = &rest[..token_end];
if !token.is_empty() {
if rel_cols.iter().any(|c| c == token) {
out.push_str("e_ident(token));
} else {
out.push_str(token);
}
}
if token_end >= rest.len() {
break;
}
out.push_str(&rest[token_end..token_end + 1]);
rest = &rest[token_end + 1..];
}
out
}
fn aggregate_query(
relation: &str,
rel_cols: &[String],
agg: AggSpec,
filters: String,
modifiers: String,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let filters = resolve_filter_aliases(relation, &filters, rel_cols);
let filters = append_literal_constraints(&filters, rel_cols);
let (where_sql, where_params) = compile_filters(filters, params)?;
let (group_sql, order_sql, _group_cols, mut mod_params) = compile_group_order(&modifiers);
let table_cols = canonical_columns(relation);
let resolve_alias = |expr: &str| -> String {
if let Some(idx) = rel_cols.iter().position(|c| c == expr) {
if let Some(col) = table_cols.get(idx) {
return quote_ident(col);
}
}
quote_ident(expr)
};
let count_expr = if agg.distinct {
format!("DISTINCT {}", resolve_alias(&agg.expr))
} else if agg.expr == "_" || agg.expr.is_empty() {
"*".to_string()
} else if agg.expr.len() == 1 && agg.expr.chars().next().unwrap().is_ascii_alphabetic() {
"*".to_string()
} else if is_column_token(&agg.expr) {
resolve_alias(&agg.expr)
} else {
agg.expr.clone()
};
let select_list = if agg.extras.is_empty() {
format!("count({count_expr})")
} else {
let extras = agg
.extras
.iter()
.map(|c| resolve_alias(c))
.collect::<Vec<_>>()
.join(", ");
format!("{extras}, count({count_expr})")
};
let group_sql = if group_sql.is_empty() && !agg.extras.is_empty() {
format!(
" GROUP BY {}",
agg.extras
.iter()
.map(|c| resolve_alias(c))
.collect::<Vec<_>>()
.join(", ")
)
} else {
group_sql
};
let sql = if where_sql.is_empty() {
format!(
"SELECT {select_list} FROM {relation}{group_sql}{order_sql}",
group_sql = if group_sql.is_empty() {
String::new()
} else {
format!(" {group_sql}")
},
order_sql = if order_sql.is_empty() {
String::new()
} else {
format!(" {order_sql}")
}
)
} else {
format!(
"SELECT {select_list} FROM {relation} WHERE {where_sql}{group_sql}{order_sql}",
group_sql = if group_sql.is_empty() {
String::new()
} else {
format!(" {group_sql}")
},
order_sql = if order_sql.is_empty() {
String::new()
} else {
format!(" {order_sql}")
}
)
};
let mut all_params = where_params;
all_params.append(&mut mod_params);
let mut head = Vec::with_capacity(1 + agg.extras.len());
head.extend(agg.extras.iter().cloned());
head.push(
agg.head_label
.clone()
.unwrap_or_else(|| format!("count({})", agg.expr)),
);
Ok(Translation::read(sql, all_params, head))
}
fn compile_group_order(
modifiers: &str,
) -> (
String,
String,
Vec<String>,
Vec<Box<dyn ToSql + Sync + Send>>,
) {
let mut group_cols: Vec<String> = Vec::new();
let mut order_clause = String::new();
let params: Vec<Box<dyn ToSql + Sync + Send>> = Vec::new();
let mut rest = modifiers;
while let Some(idx) = rest.find(':') {
let op = rest[idx + 1..]
.split(|c: char| c.is_whitespace())
.next()
.unwrap_or("");
let after = &rest[idx + 1 + op.len()..];
let (consumed, _value) = match op {
"group" => match extract_bracket_block(after) {
Some((body, consumed)) => {
group_cols = body.split(',').map(|s| s.trim().to_string()).collect();
(consumed, ())
}
None => (after.len(), ()),
},
"order" => {
let trimmed = after.trim_start();
let trimmed = trimmed
.split_once(':')
.map(|(head, _)| head)
.unwrap_or(trimmed)
.trim_end();
let (expr, desc) = if let Some(stripped) = trimmed.strip_prefix('-') {
(stripped.trim_start(), true)
} else {
(trimmed, false)
};
let (expr, trailing_dir) = {
let lower = expr.to_ascii_lowercase();
if lower.ends_with(" desc") {
(expr[..expr.len() - 5].trim_end(), true)
} else if lower.ends_with(" asc") {
(expr[..expr.len() - 4].trim_end(), false)
} else {
(expr, desc)
}
};
let expr_lc = expr.to_ascii_lowercase();
let order_expr = if expr_lc.starts_with("count(") && expr_lc.ends_with(')') {
let inner = &expr[6..expr.len() - 1];
if inner == "_" || inner.is_empty() {
"count(*)".to_string()
} else if inner.len() == 1
&& inner.chars().next().unwrap().is_ascii_alphabetic()
{
"*".to_string()
} else {
format!("count({})", quote_ident(inner))
}
} else if expr_lc.starts_with("count(distinct ") && expr_lc.ends_with(')') {
let inner = &expr[15..expr.len() - 1];
format!("count(DISTINCT {})", quote_ident(inner))
} else {
quote_ident(expr)
};
let dir = if trailing_dir { "DESC" } else { "ASC" };
order_clause = format!("ORDER BY {order_expr} {dir}");
let next_colon = after.find(':').unwrap_or(after.len());
let after_dir = if trailing_dir {
next_colon + 1
} else {
next_colon
};
(after_dir, ())
}
_ => (op.len(), ()),
};
rest = &rest[idx + 1 + consumed..];
}
let group_clause = if group_cols.is_empty() {
String::new()
} else {
format!(
"GROUP BY {}",
group_cols
.iter()
.map(|c| quote_ident(c))
.collect::<Vec<_>>()
.join(", ")
)
};
(group_clause, order_clause, group_cols, params)
}
fn extract_bracket_block(start: &str) -> Option<(&str, usize)> {
let s = start.trim_start();
let trimmed_len = start.len() - s.len();
if !s.starts_with('[') {
return None;
}
let close = s.find(']')?;
Some((&s[1..close], trimmed_len + close + 1))
}
fn compile_modifiers(
modifiers: &str,
_head: &[String],
params: &BTreeMap<String, serde_json::Value>,
) -> (String, Vec<Box<dyn ToSql + Sync + Send>>) {
let mut out = String::new();
let bound: Vec<Box<dyn ToSql + Sync + Send>> = Vec::new();
let placeholder_idx = params.len() + 1; let _ = placeholder_idx;
let mut rest = modifiers;
while let Some(idx) = rest.find(':') {
let op = rest[idx + 1..]
.split(|c: char| c.is_whitespace() || c == ',')
.next()
.unwrap_or("");
let after = rest[idx + 1 + op.len()..].trim_start();
match op {
"limit" => {
let (num, consumed) = parse_uint(after);
if !out.is_empty() && !out.ends_with(' ') {
out.push(' ');
}
out.push_str(&format!("LIMIT {num}"));
rest = &rest[idx + 1 + op.len() + consumed..];
continue;
}
"offset" => {
let (num, consumed) = parse_uint(after);
if !out.is_empty() && !out.ends_with(' ') {
out.push(' ');
}
out.push_str(&format!("OFFSET {num}"));
rest = &rest[idx + 1 + op.len() + consumed..];
continue;
}
"order" => {
let trimmed = after.trim_start();
let (expr, desc) = if let Some(stripped) = trimmed.strip_prefix('-') {
(stripped.trim_start(), true)
} else {
(trimmed, false)
};
let expr_lc = expr.to_ascii_lowercase();
let order_expr = if expr_lc.starts_with("count(") && expr_lc.ends_with(')') {
let inner = &expr[6..expr.len() - 1];
if inner == "_" || inner.is_empty() {
"count(*)".to_string()
} else {
format!("count({})", quote_ident(inner))
}
} else {
quote_ident(expr)
};
let dir = if desc { "DESC" } else { "ASC" };
if !out.is_empty() && !out.ends_with(' ') {
out.push(' ');
}
out.push_str(&format!("ORDER BY {order_expr} {dir}"));
let next = after.find(':').unwrap_or(after.len());
rest = &rest[idx + 1 + op.len() + next..];
continue;
}
"group" => {
let (body, consumed) = match extract_bracket_block(after) {
Some(b) => b,
None => ("", after.len()),
};
let cols: Vec<String> = body.split(',').map(|s| s.trim().to_string()).collect();
if !out.is_empty() && !out.ends_with(' ') {
out.push(' ');
}
out.push_str(&format!(
"GROUP BY {}",
cols.iter()
.map(|c| quote_ident(c))
.collect::<Vec<_>>()
.join(", ")
));
rest = &rest[idx + 1 + op.len() + consumed..];
continue;
}
_ => {
let next = rest[idx + 1..].find(':').unwrap_or(rest.len() - idx - 1);
rest = &rest[idx + 1 + next..];
continue;
}
}
}
(out, bound)
}
fn parse_uint(s: &str) -> (u64, usize) {
let mut end = 0;
for (i, c) in s.char_indices() {
if c.is_ascii_digit() {
end = i + 1;
} else {
break;
}
}
let n: u64 = s[..end].parse().unwrap_or(0);
(n, end)
}
fn compile_filters(
filters: String,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<(String, Vec<Box<dyn ToSql + Sync + Send>>), String> {
let trimmed = filters.trim().trim_start_matches(',').trim().to_string();
if trimmed.is_empty() {
return Ok((String::new(), Vec::new()));
}
let clauses = split_clauses(&trimmed);
let mut out_clauses: Vec<String> = Vec::with_capacity(clauses.len());
let mut out_params: Vec<Box<dyn ToSql + Sync + Send>> = Vec::new();
let mut next_idx = 1;
for clause in clauses {
let (rendered, used, _) = render_clause(clause, params, &mut next_idx)?;
out_clauses.push(rendered);
out_params.extend(used);
}
Ok((out_clauses.join(" AND "), out_params))
}
fn split_clauses(s: &str) -> Vec<&str> {
let mut out = Vec::new();
let mut depth = 0usize;
let mut last = 0usize;
let bytes = s.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
let c = bytes[i] as char;
match c {
'(' | '[' => depth += 1,
')' | ']' => depth = depth.saturating_sub(1),
',' | '\n' if depth == 0 => {
let rest = s[i + 1..].trim_start();
if !rest.starts_with("or ") && !rest.starts_with("or\n") && !rest.starts_with("or(")
{
let piece = s[last..i].trim();
if !piece.is_empty() {
out.push(piece);
}
last = i + 1;
}
}
'a' if depth == 0
&& i >= 1
&& bytes[i - 1] == b' '
&& i + 3 <= bytes.len()
&& &s[i..i + 3] == "and"
&& (i + 3 == bytes.len() || (bytes[i + 3] as char).is_ascii_whitespace()) =>
{
let piece = s[last..(i - 1)].trim();
if !piece.is_empty() {
out.push(piece);
}
last = i + 4; i += 3;
}
_ => {}
}
i += 1;
}
let tail = s[last..].trim();
if !tail.is_empty() {
out.push(tail);
}
out
}
fn split_top_level_or(s: &str) -> Vec<&str> {
let mut out = Vec::new();
let mut depth = 0usize;
let mut last = 0usize;
let bytes = s.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
let c = bytes[i] as char;
match c {
'(' | '[' => depth += 1,
')' | ']' => depth = depth.saturating_sub(1),
'o' if depth == 0
&& i >= 1
&& bytes[i - 1] == b' '
&& i + 2 <= bytes.len()
&& &s[i..i + 2] == "or"
&& (i + 2 == bytes.len() || (bytes[i + 2] as char).is_ascii_whitespace()) =>
{
let piece = s[last..(i - 1)].trim();
if !piece.is_empty() {
out.push(piece);
}
last = i + 3;
i += 1;
}
_ => {}
}
i += 1;
}
let tail = s[last..].trim();
if !tail.is_empty() {
out.push(tail);
}
out
}
fn find_top_level_or(s: &str) -> Option<usize> {
let parts = split_top_level_or(s);
if parts.len() > 1 {
Some(1)
} else {
None
}
}
fn render_clause<'a>(
clause: &'a str,
params: &BTreeMap<String, serde_json::Value>,
next_idx: &mut usize,
) -> Result<(String, Vec<Box<dyn ToSql + Sync + Send>>, &'a str), String> {
let trimmed = clause.trim();
if find_top_level_or(trimmed).is_some() {
let parts = split_top_level_or(trimmed);
let mut rendered = Vec::with_capacity(parts.len());
let mut used: Vec<Box<dyn ToSql + Sync + Send>> = Vec::new();
for p in parts {
let (r, u, _) = render_clause(p.trim(), params, next_idx)?;
rendered.push(r);
used.extend(u);
}
return Ok((format!("({})", rendered.join(" OR ")), used, clause));
}
if trimmed.starts_with('(') && trimmed.ends_with(')') {
let inner = &trimmed[1..trimmed.len() - 1];
let parts: Vec<&str> = inner.split(" or ").collect();
if parts.len() > 1 {
let mut rendered = Vec::with_capacity(parts.len());
let mut used: Vec<Box<dyn ToSql + Sync + Send>> = Vec::new();
for p in parts {
let (r, u, _) = render_clause(p.trim(), params, next_idx)?;
rendered.push(r);
used.extend(u);
}
return Ok((format!("({})", rendered.join(" OR ")), used, clause));
}
let parts: Vec<&str> = inner.split(" and ").collect();
if parts.len() > 1 {
let mut rendered = Vec::with_capacity(parts.len());
let mut used: Vec<Box<dyn ToSql + Sync + Send>> = Vec::new();
for p in parts {
let (r, u, _) = render_clause(p.trim(), params, next_idx)?;
rendered.push(r);
used.extend(u);
}
return Ok((format!("({})", rendered.join(" AND ")), used, clause));
}
}
if let Some(rest) = trimmed.strip_prefix("!regex_matches(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
let mut parts = body.splitn(2, ',');
let col = parts
.next()
.ok_or_else(|| format!("bad !regex_matches: {trimmed}"))?
.trim();
let pat = parts
.next()
.ok_or_else(|| format!("bad !regex_matches: {trimmed}"))?
.trim();
let col_sql = string_op_col(col);
let (placeholder, used) = match strip_lowercase_wrapper(pat) {
Some(inner) => {
let (r, u) = render_value_or_param(inner, params, next_idx)?;
(format!("lower({r})"), u)
}
None => render_value_or_param(pat, params, next_idx)?,
};
return Ok((format!("{col_sql} !~ {placeholder}"), used, clause));
}
if let Some(rest) = trimmed.strip_prefix("regex_matches(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
let mut parts = body.splitn(2, ',');
let col = parts
.next()
.ok_or_else(|| format!("bad regex_matches: {trimmed}"))?
.trim();
let pat = parts
.next()
.ok_or_else(|| format!("bad regex_matches: {trimmed}"))?
.trim();
let col_sql = string_op_col(col);
let (placeholder, used) = match strip_lowercase_wrapper(pat) {
Some(inner) => {
let (r, u) = render_value_or_param(inner, params, next_idx)?;
(format!("lower({r})"), u)
}
None => render_value_or_param(pat, params, next_idx)?,
};
return Ok((format!("{col_sql} ~ {placeholder}"), used, clause));
}
if let Some(rest) = trimmed.strip_prefix("str_includes(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
let mut parts = body.splitn(2, ',');
let hay = parts
.next()
.ok_or_else(|| format!("bad str_includes: {trimmed}"))?
.trim();
let needle = parts
.next()
.ok_or_else(|| format!("bad str_includes: {trimmed}"))?
.trim();
let hay_sql = string_op_col(hay);
let (needle_sql, used) = match strip_lowercase_wrapper(needle) {
Some(inner) => {
let (rendered, used) = render_value_or_param(inner, params, next_idx)?;
(format!("lower({rendered})"), used)
}
None => {
let (rendered, used) = render_value_or_param(needle, params, next_idx)?;
(rendered, used)
}
};
return Ok((
format!("{hay_sql} LIKE '%' || {needle_sql} || '%'"),
used,
clause,
));
}
if let Some(rest) = trimmed.strip_prefix("str_contains(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
let mut parts = body.splitn(2, ',');
let hay = parts
.next()
.ok_or_else(|| format!("bad str_contains: {trimmed}"))?
.trim();
let needle = parts
.next()
.ok_or_else(|| format!("bad str_contains: {trimmed}"))?
.trim();
let hay_sql = string_op_col(hay);
let (placeholder, used) = match strip_lowercase_wrapper(needle) {
Some(inner) => {
let (r, u) = render_value_or_param(inner, params, next_idx)?;
(format!("lower({r})"), u)
}
None => render_value_or_param(needle, params, next_idx)?,
};
return Ok((
format!("{hay_sql} LIKE '%' || {placeholder} || '%'"),
used,
clause,
));
}
if let Some(rest) = trimmed.strip_prefix("starts_with(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
let mut parts = body.splitn(2, ',');
let hay = parts
.next()
.ok_or_else(|| format!("bad starts_with: {trimmed}"))?
.trim();
let needle = parts
.next()
.ok_or_else(|| format!("bad starts_with: {trimmed}"))?
.trim();
let hay_sql = string_op_col(hay);
let (placeholder, used) = match strip_lowercase_wrapper(needle) {
Some(inner) => {
let (r, u) = render_value_or_param(inner, params, next_idx)?;
(format!("lower({r})"), u)
}
None => render_value_or_param(needle, params, next_idx)?,
};
return Ok((format!("{hay_sql} LIKE {placeholder} || '%'"), used, clause));
}
if let Some(in_idx) = find_word_operator(trimmed, "in") {
let lhs = trimmed[..in_idx].trim();
let rhs = trimmed[in_idx + 2..].trim();
let lhs_sql = scalar_expr(lhs);
if rhs.starts_with('[') && rhs.ends_with(']') {
let strs: Vec<String> = match serde_json::from_str::<Vec<serde_json::Value>>(rhs) {
Ok(flat) => flat
.into_iter()
.map(|v| match v {
serde_json::Value::String(s) => s,
other => other.to_string(),
})
.collect(),
Err(_) => {
let items = parse_nested_lists(rhs)?;
items
.into_iter()
.filter_map(|mut row| row.pop())
.map(|v| match v {
serde_json::Value::String(s) => s,
other => other.to_string(),
})
.collect()
}
};
let placeholder = format!("${}", *next_idx);
*next_idx += 1;
return Ok((
format!("{lhs_sql} = ANY({placeholder}::text[])"),
vec![Box::new(strs)],
clause,
));
}
if let Some(name) = rhs.strip_prefix('$') {
let v = params.get(name).cloned().unwrap_or(serde_json::Value::Null);
let strs: Vec<String> = match v {
serde_json::Value::Array(arr) => arr
.into_iter()
.map(|item| match item {
serde_json::Value::String(s) => s,
other => other.to_string(),
})
.collect(),
_ => Vec::new(),
};
let placeholder = format!("${}", *next_idx);
*next_idx += 1;
return Ok((
format!("{lhs_sql} = ANY({placeholder}::text[])"),
vec![Box::new(strs)],
clause,
));
}
return Err(format!("unsupported IN RHS: {rhs}"));
}
let op_pos = find_top_level_op(trimmed)?;
let op_raw = &trimmed[op_pos..op_pos + 2.min(trimmed.len() - op_pos)];
let (op, op_len) = if matches!(op_raw, "==" | "!=" | ">=" | "<=") {
if op_raw == "==" {
("=", 2) } else {
(op_raw, 2)
}
} else if let Some(c) = trimmed[op_pos..].chars().next() {
match c {
'=' | '<' | '>' => (&trimmed[op_pos..op_pos + 1], 1),
_ => return Err(format!("unknown operator in clause: {trimmed}")),
}
} else {
return Err(format!("unknown operator in clause: {trimmed}"));
};
let lhs = trimmed[..op_pos].trim();
let rhs = trimmed[op_pos + op_len..].trim();
let lhs_sql = scalar_expr(lhs);
if rhs == "null" {
let sql = match op {
"=" => format!("{lhs_sql} IS NULL"),
"!=" | "<>" => format!("{lhs_sql} IS NOT NULL"),
_ => return Err(format!("null with non-equality: {trimmed}")),
};
return Ok((sql, Vec::new(), clause));
}
if rhs.starts_with('"') && rhs.ends_with('"') && rhs.len() >= 2 {
let inner = &rhs[1..rhs.len() - 1];
let value = unescape_cozo_string(inner);
let placeholder = format!("${}", *next_idx);
*next_idx += 1;
let used = vec![json_to_pg(serde_json::Value::String(value))];
return Ok((format!("{lhs_sql} {op} {placeholder}"), used, clause));
}
if let Some(name) = rhs.strip_prefix('$') {
let v = params.get(name).cloned().unwrap_or(serde_json::Value::Null);
let placeholder = format!("${}", *next_idx);
*next_idx += 1;
let used = vec![json_to_pg(v)];
return Ok((format!("{lhs_sql} {op} {placeholder}"), used, clause));
}
let rhs_sql = scalar_expr(rhs);
Ok((format!("{lhs_sql} {op} {rhs_sql}"), Vec::new(), clause))
}
fn find_top_level_op(s: &str) -> Result<usize, String> {
let mut depth = 0usize;
let mut in_string = false;
let bytes = s.as_bytes();
let mut i = 0usize;
while i < bytes.len() {
let c = bytes[i] as char;
if in_string {
if c == '"' {
in_string = false;
}
i += 1;
continue;
}
match c {
'"' => in_string = true,
'(' => depth += 1,
')' => depth = depth.saturating_sub(1),
'=' | '!' | '<' | '>' if depth == 0 => return Ok(i),
_ => {}
}
i += 1;
}
Err(format!("no top-level operator: {s}"))
}
fn strip_lowercase_wrapper(s: &str) -> Option<&str> {
let t = s.trim();
t.strip_prefix("lowercase(")?.strip_suffix(')')
}
fn find_word_operator(s: &str, word: &str) -> Option<usize> {
let bytes = s.as_bytes();
let mut depth = 0usize;
let mut in_string = false;
let mut i = 0usize;
while i < bytes.len() {
let c = bytes[i] as char;
if in_string {
if c == '"' {
in_string = false;
}
i += 1;
continue;
}
match c {
'"' => {
in_string = true;
i += 1;
}
'(' => {
depth += 1;
i += 1;
}
')' => {
depth = depth.saturating_sub(1);
i += 1;
}
c if c.is_ascii_alphabetic() && depth == 0 => {
let start = i;
while i < bytes.len() && (bytes[i] as char).is_ascii_alphabetic() {
i += 1;
}
let token = &s[start..i];
if token == word {
let before_ok = start == 0
|| (bytes[start - 1] as char).is_ascii_whitespace()
|| bytes[start - 1] == b'(';
let after_ok = i == bytes.len()
|| (bytes[i] as char).is_ascii_whitespace()
|| bytes[i] == b'[';
if before_ok && after_ok {
return Some(start);
}
}
}
_ => {
i += 1;
}
}
}
None
}
fn scalar_expr(s: &str) -> String {
let trimmed = s.trim();
if trimmed.starts_with('(') && trimmed.ends_with(')') {
return trimmed.to_string();
}
if let Some(rest) = trimmed.strip_prefix("lowercase(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
return format!("lower({})", scalar_expr(body));
}
if let Some(rest) = trimmed.strip_prefix("upper(") {
let body = rest.strip_suffix(')').unwrap_or(rest);
return format!("upper({})", scalar_expr(body));
}
if trimmed.starts_with('"') && trimmed.ends_with('"') && trimmed.len() >= 2 {
return trimmed.to_string(); }
if is_column_token(trimmed) {
return quote_ident(trimmed);
}
trimmed.to_string()
}
const JSONB_COLUMNS: &[&str] = &[
"metadata",
"tags",
"deploy_envs",
"graph_read_users",
"graph_write_users",
"members",
"affected_services",
"elements_by_type_json",
"relationships_by_type_json",
"vectors_by_type_json",
];
fn string_op_col(s: &str) -> String {
let trimmed = s.trim();
if let Some(inner) = trimmed.strip_prefix("lowercase(") {
let inner = inner.strip_suffix(')').unwrap_or(inner);
let inner_sql = string_op_col(inner);
return format!("lower({inner_sql})");
}
if JSONB_COLUMNS.contains(&trimmed) {
return format!("{}::text", scalar_expr(trimmed));
}
scalar_expr(trimmed)
}
fn is_column_token(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
_ => return false,
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
fn unescape_cozo_string(s: &str) -> String {
let mut out = String::with_capacity(s.len());
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.next() {
Some('"') => out.push('"'),
Some('\\') => out.push('\\'),
Some('n') => out.push('\n'),
Some('t') => out.push('\t'),
Some(other) => {
out.push('\\');
out.push(other);
}
None => out.push('\\'),
}
} else {
out.push(c);
}
}
out
}
fn render_value_or_param(
token: &str,
params: &BTreeMap<String, serde_json::Value>,
next_idx: &mut usize,
) -> Result<(String, Vec<Box<dyn ToSql + Sync + Send>>), String> {
let t = token.trim();
if let Some(name) = t.strip_prefix('$') {
let v = params.get(name).cloned().unwrap_or(serde_json::Value::Null);
let placeholder = format!("${}", *next_idx);
*next_idx += 1;
return Ok((placeholder, vec![json_to_pg(v)]));
}
if t.starts_with('"') && t.ends_with('"') && t.len() >= 2 {
let inner = &t[1..t.len() - 1];
let value = unescape_cozo_string(inner);
let placeholder = format!("${}", *next_idx);
*next_idx += 1;
return Ok((
placeholder,
vec![json_to_pg(serde_json::Value::String(value))],
));
}
Ok((scalar_expr(t), Vec::new()))
}
fn ann_translation(
rest: &str,
head: &[String],
_params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let vec_literal = extract_ann_vec_literal(rest)?;
let k = extract_ann_int_field(rest, "k").unwrap_or(50);
let ef = extract_ann_int_field(rest, "ef");
let dist_col = head.first().cloned().unwrap_or_else(|| "dist".to_string());
let qn_col = head
.get(1)
.cloned()
.unwrap_or_else(|| "qualified_name".to_string());
let sql = format!(
"SELECT vec <-> $1::text::vector AS {dist_col}, {qn_col} \
FROM embedding_vectors \
ORDER BY vec <-> $1::text::vector \
LIMIT $2::int8",
dist_col = quote_ident(&dist_col),
qn_col = quote_ident(&qn_col),
);
let used: Vec<Box<dyn ToSql + Sync + Send>> = vec![Box::new(vec_literal), Box::new(k as i64)];
let gucs = ef
.map(|n| vec![("hnsw.ef_search".to_string(), n.to_string())])
.unwrap_or_default();
Ok(Translation::read_with_gucs(sql, used, head.to_vec(), gucs))
}
fn extract_ann_vec_literal(s: &str) -> Result<String, String> {
let lb = s
.find("vec([")
.ok_or_else(|| "ANN query missing vec([ literal)".to_string())?;
let after = &s[lb + 5..];
let rb = after
.find("])")
.ok_or_else(|| "ANN query missing closing ])".to_string())?;
Ok(format!("[{}]", &after[..rb]))
}
fn extract_ann_int_field(s: &str, field: &str) -> Option<usize> {
let needle = format!("{field}:");
let i = s.find(&needle)?;
let after = &s[i + needle.len()..];
let trimmed = after.trim_start();
let mut end = 0;
for (j, c) in trimmed.char_indices() {
if c.is_ascii_digit() {
end = j + 1;
} else {
break;
}
}
trimmed[..end].parse().ok()
}
fn put_script(
body: &str,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let (target, source) = match body.find(":put") {
Some(idx) => (body[idx + 4..].trim(), body[..idx].trim()),
None => (body.trim(), ""),
};
let target = target.split("<-").next().unwrap_or(target).trim();
let brace_open = target.find('{').unwrap_or(0);
let tail = &target[brace_open..];
let inner = tail.trim_start_matches('{').trim_end_matches('}').trim();
let (cols, pk) = parse_put_target(inner)?;
let is_keyed = pk.is_some();
let source = source.trim_start_matches(',').trim();
if source.starts_with("?[") {
let after_arrow = source
.split_once("<-")
.map(|(_, r)| r.trim())
.ok_or_else(|| "missing <- in :put".to_string())?;
return put_from_literal(after_arrow, &cols, pk.as_deref(), is_keyed, params);
}
if let Some(name) = source.strip_prefix('$') {
let v = params.get(name).cloned();
return put_from_batch(name, v, &cols, pk.as_deref(), is_keyed);
}
if let Some((target_part, arrow_part)) = body.split_once("<-") {
let target_part = target_part.trim();
let arrow_part = arrow_part.trim();
if target_part.starts_with(":put") || source.is_empty() {
if let Some(name) = arrow_part.strip_prefix('$') {
let v = params.get(name).cloned().unwrap_or(serde_json::Value::Null);
let rows: Vec<Vec<serde_json::Value>> = match v {
serde_json::Value::Array(outer) => outer
.into_iter()
.filter_map(|r| match r {
serde_json::Value::Array(row) => Some(row),
_ => None,
})
.collect(),
serde_json::Value::Object(obj) => vec![cols
.iter()
.map(|c| obj.get(c).cloned().unwrap_or(serde_json::Value::Null))
.collect()],
_ => Vec::new(),
};
if rows.is_empty() || rows.iter().any(|r| r.len() != cols.len()) {
return Ok(Translation::write(
"SELECT 1 WHERE false".to_string(),
Vec::new(),
));
}
let table = infer_table(&cols, pk.as_deref());
let pk = pk.or_else(|| {
if table == "index_hashes" {
Some("path".to_string())
} else {
None
}
});
let keyed = is_keyed || pk.is_some();
return build_insert(&table, &cols, pk.as_deref(), &rows, keyed);
}
}
}
Err(format!("unrecognized :put source: {source}"))
}
fn parse_put_target(inner: &str) -> Result<(Vec<String>, Option<String>), String> {
let mut cols = Vec::new();
let mut pk = None;
for part in inner.split(',') {
let p = part.trim();
if p.is_empty() {
continue;
}
if let Some((left, right)) = p.split_once("=>") {
let left = left.trim();
let right = right.trim();
cols.push(left.to_string());
pk = Some(left.to_string());
cols.push(right.to_string());
} else {
cols.push(p.to_string());
}
}
if cols.is_empty() {
return Err("empty :put target".into());
}
Ok((cols, pk))
}
#[allow(dead_code)]
fn is_keyed_table(_cols: &[String]) -> bool {
false
}
fn put_from_literal(
literal: &str,
cols: &[String],
pk: Option<&str>,
is_keyed: bool,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let literal = substitute_params(literal, params);
let rows = parse_nested_lists(&literal)?;
if rows.is_empty() {
return Ok(Translation::write(
"SELECT 1 WHERE false".to_string(),
Vec::new(),
));
}
let n_cols = cols.len();
for (i, r) in rows.iter().enumerate() {
if r.len() != n_cols {
return Err(format!(
":put row {i} has {} cols, expected {n_cols}",
r.len()
));
}
}
build_insert(&infer_table(cols, pk), cols, pk, &rows, is_keyed)
}
fn infer_table(cols: &[String], pk: Option<&str>) -> String {
if let Some(pk_col) = pk {
match pk_col {
"qualified_name" => {
if cols.contains(&"vector".to_string()) {
return "embedding_vectors".into();
}
if cols.contains(&"usearch_key".to_string()) {
return "embedding_state".into();
}
}
"path" => return "index_hashes".into(),
"key" => return "index_inventory".into(),
"id" if cols.len() == 2 => return "migrations".into(),
_ => {}
}
}
if cols == ["path", "hash"] || cols == ["hash"] || cols == ["path"] {
"index_hashes".into()
} else if cols.contains(&"element_type".to_string()) {
"code_elements".into()
} else if cols.contains(&"rel_type".to_string()) {
"relationships".into()
} else if cols.contains(&"knowledge_type".to_string()) {
"knowledge_entries".into()
} else if cols.contains(&"user_story_id".to_string()) {
"business_logic".into()
} else if cols.contains(&"service_name".to_string()) {
"service_metadata".into()
} else if cols.contains(&"workflow_id".to_string()) {
"feature_workflow_links".into()
} else if cols.contains(&"severity".to_string()) {
"incidents".into()
} else if cols.contains(&"savings_percent".to_string()) {
"context_metrics".into()
} else if cols.contains(&"cache_key".to_string()) {
"query_cache".into()
} else if cols.contains(&"team_id".to_string()) {
"team_invites".into()
} else if cols.contains(&"graph_read_users".to_string()) {
"teams".into()
} else if cols.contains(&"key_hash".to_string()) {
"api_keys".into()
} else {
"unknown_table".into()
}
}
fn infer_table_by_key(col: &str) -> Option<&'static str> {
match col {
"qualified_name" => Some("embedding_vectors"),
"key" => Some("index_inventory"),
"path" => Some("index_hashes"),
_ => None,
}
}
fn substitute_params(literal: &str, params: &BTreeMap<String, serde_json::Value>) -> String {
let mut out = String::with_capacity(literal.len());
let mut rest = literal;
while let Some(idx) = rest.find('$') {
out.push_str(&rest[..idx]);
let after = &rest[idx + 1..];
let name_len = after
.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_'))
.unwrap_or(after.len());
let name = &after[..name_len];
let v = params.get(name).cloned().unwrap_or(serde_json::Value::Null);
out.push_str(&v.to_string());
rest = &after[name_len..];
}
out.push_str(rest);
out
}
fn parse_nested_lists(s: &str) -> Result<Vec<Vec<serde_json::Value>>, String> {
let trimmed = s.trim();
if !trimmed.starts_with('[') {
return Err(format!("expected list literal: {s}"));
}
let json_src = convert_cozo_vec_literals(trimmed);
serde_json::from_str::<Vec<Vec<serde_json::Value>>>(&json_src)
.map_err(|e| format!("cannot parse list literal as JSON: {e} (input: {json_src})"))
}
fn convert_cozo_vec_literals(s: &str) -> String {
let mut out = String::with_capacity(s.len());
let mut rest = s;
while let Some(idx) = rest.find("vec([") {
out.push_str(&rest[..idx]);
let after = &rest[idx + 5..];
let close = after.find("])").unwrap_or(after.len());
out.push('[');
out.push_str(&after[..close]);
out.push(']');
rest = &after[close + 2..];
}
out.push_str(rest);
out
}
fn build_insert(
table: &str,
cols: &[String],
pk: Option<&str>,
rows: &[Vec<serde_json::Value>],
is_keyed: bool,
) -> Result<Translation, String> {
let pg_cols: Vec<String> = cols
.iter()
.map(|c| {
if table == "embedding_vectors" && c == "vector" {
"vec".to_string()
} else {
c.clone()
}
})
.collect();
let col_sql = pg_cols
.iter()
.map(|c| quote_ident(c))
.collect::<Vec<_>>()
.join(", ");
let mut all_params: Vec<Box<dyn ToSql + Sync + Send>> =
Vec::with_capacity(rows.len() * cols.len());
let mut values_sql = String::new();
for (i, row) in rows.iter().enumerate() {
if i > 0 {
values_sql.push_str(", ");
}
values_sql.push('(');
for (j, v) in row.iter().enumerate() {
if j > 0 {
values_sql.push_str(", ");
}
if pg_cols[j] == "vec" {
let placeholder = format!("${}::text::vector", all_params.len() + 1);
values_sql.push_str(&placeholder);
if let serde_json::Value::Array(arr) = v {
let literal = pgvector_from_json(arr);
all_params.push(Box::new(literal));
} else {
return Err(format!("vec column must be a JSON array, got: {v}"));
}
continue;
}
let is_api_keys_text_ts = table == "api_keys"
&& matches!(
pg_cols[j].as_str(),
"created_at" | "last_used_at" | "revoked_at"
);
let null_cast = if is_api_keys_text_ts {
"::text"
} else {
match pg_cols[j].as_str() {
"line_start"
| "line_end"
| "timestamp"
| "created_at"
| "updated_at"
| "expires_at"
| "occurred_at"
| "slo_p99_ms"
| "incident_count"
| "last_incident"
| "input_tokens"
| "output_tokens"
| "output_elements"
| "execution_time_ms"
| "baseline_tokens"
| "baseline_lines_scanned"
| "tokens_saved"
| "correct_elements"
| "total_expected"
| "query_depth"
| "resolved_at"
| "total_elements"
| "total_relationships"
| "total_vectors"
| "total_documents"
| "total_doc_sections"
| "estimated_vector_bytes"
| "estimated_hnsw_bytes"
| "usearch_key" => "::bigint",
"savings_percent" | "f1_score" | "confidence" => "::float8",
"success" | "is_deleted" | "accepted" => "::bool",
c if JSONB_COLUMNS.contains(&c) => "::jsonb",
_ => "::text",
}
};
let placeholder = format!("${}{null_cast}", all_params.len() + 1);
values_sql.push_str(&placeholder);
if JSONB_COLUMNS.contains(&pg_cols[j].as_str()) {
if matches!(v, serde_json::Value::Null) {
all_params.push(Box::new(Option::<serde_json::Value>::None));
} else {
let parsed = match v {
serde_json::Value::String(s) => {
serde_json::from_str::<serde_json::Value>(s)
.unwrap_or_else(|_| serde_json::Value::String(s.clone()))
}
other => other.clone(),
};
all_params.push(Box::new(parsed));
}
} else if matches!(v, serde_json::Value::Null) {
let typed_none: Box<dyn postgres::types::ToSql + Send + Sync> = match pg_cols[j]
.as_str()
{
c if JSONB_COLUMNS.contains(&c) => Box::new(Option::<serde_json::Value>::None),
"savings_percent" | "f1_score" | "confidence" => Box::new(Option::<f64>::None),
"success" | "is_deleted" | "accepted" => Box::new(Option::<bool>::None),
"line_start"
| "line_end"
| "timestamp"
| "created_at"
| "updated_at"
| "expires_at"
| "occurred_at"
| "resolved_at"
| "slo_p99_ms"
| "incident_count"
| "last_incident"
| "input_tokens"
| "output_tokens"
| "output_elements"
| "execution_time_ms"
| "baseline_tokens"
| "baseline_lines_scanned"
| "tokens_saved"
| "correct_elements"
| "total_expected"
| "query_depth"
| "total_elements"
| "total_relationships"
| "total_vectors"
| "total_documents"
| "total_doc_sections"
| "estimated_vector_bytes"
| "estimated_hnsw_bytes"
| "usearch_key" => Box::new(Option::<i64>::None),
_ => Box::new(Option::<String>::None),
};
all_params.push(typed_none);
} else {
all_params.push(json_to_pg(v.clone()));
}
}
values_sql.push(')');
}
let sql = match (is_keyed, pk) {
(true, Some(pk_str)) => format!(
"INSERT INTO {table} ({col_sql}) VALUES {values_sql} \
ON CONFLICT ({pk}) DO UPDATE SET {update_set}",
pk = quote_ident(pk_str),
update_set = update_set_clause(&pg_cols, pk_str),
),
_ => format!("INSERT INTO {table} ({col_sql}) VALUES {values_sql}"),
};
let gucs = embedding_gucs_for(table);
Ok(if gucs.is_empty() {
Translation::write(sql, all_params)
} else {
Translation::write_with_gucs(sql, all_params, gucs)
})
}
pub fn embedding_gucs_for(table: &str) -> Vec<(String, String)> {
if table != "embedding_vectors" {
return Vec::new();
}
let ef = std::env::var("LEANKG_HNSW_EF_CONST")
.ok()
.and_then(|v| v.parse::<u32>().ok())
.filter(|n| (1..=2000).contains(n));
match ef {
Some(n) => vec![("hnsw.ef_construction".to_string(), n.to_string())],
None => Vec::new(),
}
}
fn update_set_clause(cols: &[String], pk: &str) -> String {
cols.iter()
.filter(|c| c.as_str() != pk)
.map(|c| format!("{} = EXCLUDED.{}", quote_ident(c), quote_ident(c)))
.collect::<Vec<_>>()
.join(", ")
}
fn pgvector_from_json(arr: &[serde_json::Value]) -> String {
let mut out = String::from("[");
for (i, v) in arr.iter().enumerate() {
if i > 0 {
out.push(',');
}
if let serde_json::Value::Number(n) = v {
if let Some(f) = n.as_f64() {
out.push_str(&format!("{f}"));
} else {
out.push_str(&n.to_string());
}
} else {
out.push_str(&v.to_string());
}
}
out.push(']');
out
}
fn put_from_batch(
name: &str,
value: Option<serde_json::Value>,
cols: &[String],
pk: Option<&str>,
is_keyed: bool,
) -> Result<Translation, String> {
let rows = match value {
Some(serde_json::Value::Array(arr)) => {
let mut out = Vec::with_capacity(arr.len());
for r in arr {
if let serde_json::Value::Array(row) = r {
out.push(row);
} else {
return Err(format!(":put batch row must be an array, got: {r}"));
}
}
out
}
_ => Vec::new(),
};
if rows.is_empty() {
return Ok(Translation::write(
"SELECT 1 WHERE false".to_string(),
Vec::new(),
));
}
let table = infer_table(cols, pk);
let n_cols = cols.len();
for (i, r) in rows.iter().enumerate() {
if r.len() != n_cols {
return Err(format!(
":put batch row {i} has {} cols, expected {n_cols}",
r.len()
));
}
}
let _ = name;
build_insert(&table, cols, pk, &rows, is_keyed)
}
fn rm_script(
body: &str,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let idx = body.find(":rm").ok_or("no :rm in body".to_string())?;
let target = body[idx + 3..].trim();
let brace_open = target.find('{').unwrap_or(0);
let target_cols = &target[brace_open..];
let inner = target_cols
.trim_start_matches('{')
.trim_end_matches('}')
.trim();
let cols: Vec<String> = inner.split(',').map(|s| s.trim().to_string()).collect();
if cols.is_empty() {
return Err("empty :rm target".into());
}
let source = body[..idx].trim();
if source.contains(":=") {
let after_assign = source.split_once(":=").map(|(_, r)| r).unwrap_or("");
let (relation, _rel_cols, body_after) = match parse_relation_block(after_assign) {
Some(p) => p,
None => return Err(format!("bad :rm relation block: {after_assign}")),
};
let (filters, _) = split_filters_and_modifiers(&body_after);
let table = if relation.is_empty() {
infer_table(&cols, None)
} else {
relation
};
let n_stars = after_assign.matches('*').count();
if n_stars >= 2 {
return cross_relation_rm(after_assign, &table, filters);
}
let (where_sql, params) = compile_filters(filters, params)?;
let sql = if where_sql.is_empty() {
format!("DELETE FROM {table}")
} else {
format!("DELETE FROM {table} WHERE {where_sql}")
};
return Ok(Translation::write(sql, params));
}
if let Some((_, after_arrow)) = source.split_once("<-") {
let list_text = after_arrow.trim();
let arr = parse_nested_lists(list_text)?;
let key_col = cols.first().cloned().unwrap_or_default();
let table = match infer_table_by_key(&key_col) {
Some(t) => t.to_string(),
None => infer_table(&cols, None),
};
let pk = key_col;
let strs: Vec<String> = arr
.into_iter()
.filter_map(|row| {
row.into_iter().next().map(|v| match v {
serde_json::Value::String(s) => s,
other => other.to_string(),
})
})
.collect();
let sql = format!(
"DELETE FROM {table} WHERE {pk} = ANY($1::text[])",
table = table,
pk = quote_ident(&pk),
);
return Ok(Translation::write(sql, vec![Box::new(strs)]));
}
Err(format!("unsupported :rm shape: {source}"))
}
fn cross_relation_rm(
after_assign: &str,
table: &str,
_filters: String,
) -> Result<Translation, String> {
let sql = format!(
"DELETE FROM {table} WHERE source_qualified IN (SELECT qualified_name FROM code_elements WHERE file_path ~ $1)"
);
let pat = match extract_regex_literal(after_assign) {
Some(p) => p,
None => "^ontology://".to_string(),
};
Ok(Translation::write(sql, vec![Box::new(pat)]))
}
fn extract_regex_literal(s: &str) -> Option<String> {
let i = s.find("regex_matches(")?;
let after = &s[i + "regex_matches(".len()..];
let close = after.find(')')?;
let inner = &after[..close];
let parts: Vec<&str> = inner.splitn(2, ',').collect();
if parts.len() != 2 {
return None;
}
let pat = parts[1].trim();
if pat.starts_with('"') && pat.ends_with('"') {
Some(unescape_cozo_string(&pat[1..pat.len() - 1]))
} else {
None
}
}
fn delete_where(
body: &str,
params: &BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
let trimmed = body.trim();
let where_idx = trimmed
.find("where")
.ok_or_else(|| ":delete missing where".to_string())?;
let table = trimmed[..where_idx].trim();
let filters = trimmed[where_idx + 5..].trim().to_string();
let (where_sql, bound) = compile_filters(filters, params)?;
let sql = if where_sql.is_empty() {
format!("DELETE FROM {table}")
} else {
format!("DELETE FROM {table} WHERE {where_sql}")
};
Ok(Translation::write(sql, bound))
}
fn create_ddl(body: &str) -> Result<Translation, String> {
let _ = body;
Ok(Translation::ddl_noop(Vec::new()))
}
fn replace_ddl(_body: &str) -> Result<Translation, String> {
Ok(Translation::ddl_noop(Vec::new()))
}
fn index_ddl(rest: &str) -> Result<Translation, String> {
let trimmed = rest.trim();
if let Some(after) = trimmed.strip_prefix("create") {
let body = after.trim();
if let Some((target, cols_block)) = body.split_once('{') {
let target = target.trim();
let cols = cols_block.trim_end_matches('}').trim();
if let Some((table, idx)) = target.split_once(':') {
let table = table.trim();
let idx = idx.trim();
let pg_idx = format!("{table}_{idx}");
let col_list = cols
.split(',')
.map(|s| quote_ident(s.trim()))
.collect::<Vec<_>>()
.join(", ");
let sql = format!("CREATE INDEX IF NOT EXISTS {pg_idx} ON {table} ({col_list})");
return Ok(Translation::write(sql, Vec::new()));
}
}
}
if let Some(after) = trimmed.strip_prefix("drop") {
let body = after.trim();
if let Some((table, idx)) = body.split_once(':') {
let table = table.trim();
let idx = idx.trim();
let pg_idx = format!("{table}_{idx}");
return Ok(Translation::write(
format!("DROP INDEX IF EXISTS {pg_idx}"),
Vec::new(),
));
}
}
Ok(Translation::ddl_noop(Vec::new()))
}
fn hnsw_ddl(rest: &str) -> Result<Translation, String> {
let trimmed = rest.trim();
if let Some(after) = trimmed.strip_prefix("drop") {
let target = after.trim();
if target.starts_with("embedding_vectors") {
return Ok(Translation::write(
"DROP INDEX IF EXISTS embedding_vectors_vec_hnsw_idx".to_string(),
Vec::new(),
));
}
return Ok(Translation::ddl_noop(Vec::new()));
}
if let Some(_after) = trimmed.strip_prefix("create") {
if trimmed.contains("embedding_vectors") {
return Ok(Translation::write(
"CREATE INDEX IF NOT EXISTS embedding_vectors_vec_hnsw_idx \
ON embedding_vectors USING hnsw (vec vector_cosine_ops) \
WITH (m = 16, ef_construction = 200)"
.to_string(),
Vec::new(),
));
}
return Ok(Translation::ddl_noop(Vec::new()));
}
Ok(Translation::ddl_noop(Vec::new()))
}
fn relations_introspection() -> Translation {
Translation::read(
"SELECT name FROM ( \
SELECT table_name AS name FROM information_schema.tables \
WHERE table_schema = current_schema() AND table_type = 'BASE TABLE' \
UNION ALL \
SELECT CASE WHEN indexname LIKE '%\\_vec\\_hnsw\\_idx' \
THEN tablename || ':vec_idx' ELSE indexname END AS name \
FROM pg_indexes WHERE schemaname = current_schema() \
) t ORDER BY name"
.to_string(),
Vec::new(),
vec!["name".to_string()],
)
}
fn schema_introspection(_rest: &str) -> Result<Translation, String> {
let table = _rest
.trim()
.trim_start_matches('{')
.trim_end_matches('}')
.trim();
let sql = format!(
"SELECT column_name, data_type, is_nullable FROM information_schema.columns \
WHERE table_schema = current_schema() AND table_name = '{table}' \
ORDER BY ordinal_position"
);
Ok(Translation::read(
sql,
Vec::new(),
vec![
"column_name".to_string(),
"data_type".to_string(),
"is_nullable".to_string(),
],
))
}
pub fn map_row(
row: &postgres::Row,
head: &[String],
) -> Result<Vec<DataValue>, Box<dyn std::error::Error>> {
let mut out = Vec::with_capacity(head.len());
for (i, col) in head.iter().enumerate() {
if JSONB_COLUMNS.contains(&col.as_str()) {
let v: DataValue = match row.try_get::<_, Option<serde_json::Value>>(i) {
Ok(Some(j)) => DataValue::Str(serde_json::to_string(&j).unwrap_or_default()),
_ => DataValue::Null,
};
out.push(v);
continue;
}
let v: DataValue = if let Ok(s) = row.try_get::<_, Option<String>>(i) {
match s {
Some(s) => DataValue::Str(s),
None => DataValue::Null,
}
} else if let Ok(n) = row.try_get::<_, Option<i64>>(i) {
match n {
Some(n) => DataValue::from(n),
None => DataValue::Null,
}
} else if let Ok(n) = row.try_get::<_, Option<i32>>(i) {
match n {
Some(n) => DataValue::from(n as i64),
None => DataValue::Null,
}
} else if let Ok(f) = row.try_get::<_, Option<f64>>(i) {
match f {
Some(f) => DataValue::from(f),
None => DataValue::Null,
}
} else if let Ok(b) = row.try_get::<_, Option<bool>>(i) {
match b {
Some(b) => DataValue::Bool(b),
None => DataValue::Null,
}
} else {
DataValue::Null
};
out.push(v);
}
Ok(out)
}
pub fn named_rows_from_result(
result: Vec<postgres::Row>,
head: &[String],
) -> Result<NamedRows, Box<dyn std::error::Error>> {
let mut rows: Vec<Vec<DataValue>> = Vec::with_capacity(result.len());
for row in &result {
rows.push(map_row(row, head)?);
}
Ok(NamedRows::new(head.to_vec(), rows))
}
pub(crate) fn quote_ident(s: &str) -> String {
if s.is_empty() {
return "\"\"".into();
}
let safe = s
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.');
if !safe {
return format!("\"{}\"", s.replace('"', "\"\""));
}
format!("\"{s}\"")
}
pub fn translate_for(
query: &str,
params: BTreeMap<String, serde_json::Value>,
) -> Result<Translation, String> {
translate(query, params)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_query_errors() {
assert!(translate("", BTreeMap::new()).is_err());
}
#[test]
fn read_simple_select() {
let t = translate("?[a, b, c] := *table[a, b, c]", BTreeMap::new()).unwrap();
assert_eq!(t.kind, TranslationKind::Read);
assert_eq!(t.sql, "SELECT \"a\", \"b\", \"c\" FROM table");
assert_eq!(t.head, vec!["a", "b", "c"]);
}
#[test]
fn read_equality_param() {
let mut p = BTreeMap::new();
p.insert("qn".into(), serde_json::json!("foo"));
let t = translate("?[a] := *t[a], a = $qn", p).unwrap();
assert!(t.sql.contains("WHERE \"a\" = $1"));
assert_eq!(t.params.len(), 1);
}
#[test]
fn read_null_equality() {
let t = translate(
"?[id] := *api_keys[id, kh], revoked_at = null",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("IS NULL"), "got: {}", t.sql);
}
#[test]
fn read_null_inequality() {
let t = translate(
"?[cid] := *code_elements[qn, _, _, _, _, _, _, _, cid, _, _], cid != null",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("IS NOT NULL"), "got: {}", t.sql);
}
#[test]
fn read_or_pair() {
let mut p = BTreeMap::new();
p.insert("a".into(), serde_json::json!("x"));
p.insert("b".into(), serde_json::json!("y"));
let t = translate("?[t] := *r[s, t, _, _, _, _], (s = $a or s = $b)", p).unwrap();
assert!(t.sql.contains(" OR "), "got: {}", t.sql);
assert_eq!(t.params.len(), 2);
}
#[test]
fn read_top_level_or_chain() {
let t = translate(
"?[a] := *code_elements[qn, et, name, fp, _, _, _, _, _, _, _, _, _], \
str_includes(lowercase(name), \"x\") or \
str_includes(lowercase(qn), \"x\") or \
str_includes(lowercase(fp), \"x\") :limit 5",
BTreeMap::new(),
)
.unwrap();
assert_eq!(t.sql.matches(" OR ").count(), 2, "got: {}", t.sql);
assert!(t.sql.contains("LIMIT 5"), "got: {}", t.sql);
}
#[test]
fn head_alias_resolves_to_real_column() {
let t = translate(
"?[qualified_name, file_path, line_end, line_start, language, name, span] := \
*code_elements[qualified_name, et, name, file_path, line_start, line_end, language, _, _, _, _, env, ontology_layer], \
line_end >= 0, line_start >= 0, (line_end - line_start) >= 1, \
et in [\"function\", \"method\", \"struct\"], \
span = line_end - line_start :order -span",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("\"qualified_name\""), "got: {}", t.sql);
assert!(t.sql.contains("\"element_type\" = ANY"), "got: {}", t.sql);
assert!(t.sql.contains("AS \"span\""), "got: {}", t.sql);
assert!(t.sql.contains("ORDER BY \"span\" DESC"), "got: {}", t.sql);
}
#[test]
fn head_alias_tgt_resolves_target_qualified() {
let t = translate(
"?[tgt] := *relationships[_, tgt, rel, _, _, _], (rel = \"calls\" or rel = \"tested_by\")",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("\"target_qualified\""), "got: {}", t.sql);
assert!(t.sql.contains(" OR "), "got: {}", t.sql);
assert!(t.sql.contains("\"rel_type\" = $"), "got: {}", t.sql);
}
#[test]
fn qn_in_list_filter_resolves_qualified_name() {
let mut p = BTreeMap::new();
p.insert("qns".into(), serde_json::json!(["a", "b"]));
let t = translate(
"?[name] := *code_elements[qn, _, name, _, _, _, _, _, _, _, _, env, ontology_layer], qn in $qns",
p,
)
.unwrap();
assert!(t.sql.contains("\"qualified_name\" = ANY"), "got: {}", t.sql);
}
#[test]
fn read_limit_offset() {
let t = translate("?[a] := *t[a] :limit 10 :offset 5", BTreeMap::new()).unwrap();
assert!(t.sql.contains("LIMIT 10"), "got: {}", t.sql);
assert!(t.sql.contains("OFFSET 5"), "got: {}", t.sql);
}
#[test]
fn read_in_list_literal() {
let t = translate(
"?[s, t] := *r[s, t, _, _, _, _], rel_type in [\"calls\", \"imports\"]",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("ANY"), "got: {}", t.sql);
assert_eq!(t.params.len(), 1);
}
#[test]
fn read_regex_matches() {
let t = translate(
"?[qn] := *code_elements[qn, _, _, f, _, _, _, _, _, _, _], regex_matches(f, \"^ontology://\")",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("~"), "got: {}", t.sql);
}
#[test]
fn read_regex_matches_with_param() {
let mut p = BTreeMap::new();
p.insert("pat".into(), serde_json::json!("^foo"));
let t = translate("?[qn] := *t[qn, fp], regex_matches(fp, $pat)", p).unwrap();
assert!(t.sql.contains("~ $1"), "got: {}", t.sql);
}
#[test]
fn relationships_alias_regex_matches_resolves_tgt() {
let t = translate(
"?[src, tgt, rel_type, conf, meta] := *relationships[src, tgt, rel_type, conf, meta, _], regex_matches(tgt, \".*main.*\") :limit 5",
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql.contains("target_qualified"),
"tgt alias not resolved to target_qualified: got: {}",
t.sql
);
assert!(
!t.sql.contains("\"tgt\""),
"raw tgt column leaked: got: {}",
t.sql
);
}
#[test]
fn read_str_includes() {
let mut p = BTreeMap::new();
p.insert("pattern".into(), serde_json::json!("needle"));
let t = translate(
"?[qn] := *t[qn, _, _, _, _, _, _, _, _, _, _], str_includes(lowercase(qn), lowercase($pattern))",
p,
)
.unwrap();
assert!(t.sql.contains("LIKE '%' ||"), "got: {}", t.sql);
}
#[test]
fn read_starts_with() {
let t = translate(
"?[name] := *code_elements[_, _, _, _, _, _, _, _, _, _, _, _, _], starts_with(file_path, \"src/\")",
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql.contains("LIKE") && t.sql.contains("|| '%'"),
"got: {}",
t.sql
);
}
#[test]
fn read_range() {
let mut p = BTreeMap::new();
p.insert("lo".into(), serde_json::json!("a"));
p.insert("hi".into(), serde_json::json!("b"));
let t = translate(
"?[fp] := *code_elements[qn, et, name, fp, ls, le, lg, pq, _, _, _, _, _], fp >= $lo and fp < $hi",
p,
)
.unwrap();
assert!(t.sql.contains("AND"), "got: {}", t.sql);
}
#[test]
fn count_query_simple() {
let t = translate(
"?[count(n)] := *code_elements[n, _, _, _, _, _, _, _, _, _, _, _, _]",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.starts_with("SELECT count(*)"), "got: {}", t.sql);
}
#[test]
fn count_with_group_order() {
let t = translate(
"?[qualified_name, env, count(n)] := *code_elements[n, _, _, qualified_name, _, _, _, _, _, _, env, _] :group [qualified_name, env] :order count(n) desc",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("GROUP BY"), "got: {}", t.sql);
assert!(t.sql.contains("ORDER BY"), "got: {}", t.sql);
assert!(t.sql.contains("DESC"), "got: {}", t.sql);
}
#[test]
fn count_aggregate_neg_order() {
let t = translate(
"?[language, count(language)] := *code_elements[_, _, _, _, _, _, language, _, _, _, _, _, _] :order -count(language)",
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql.contains("ORDER BY count(\"language\") DESC"),
"got: {}",
t.sql
);
}
#[test]
fn not_exists_orphans() {
let t = translate(
"?[qn, usk, ch, st, em] := *embedding_state[qn, usk, ch, st, em], not *code_elements[qn, _, _, _, _, _, _, _, _, _, _, _, _]",
BTreeMap::new(),
)
.unwrap();
assert!(t.sql.contains("NOT EXISTS"), "got: {}", t.sql);
assert!(t.sql.contains("code_elements"), "got: {}", t.sql);
}
#[test]
fn ann_query_translation() {
let q = "?[dist, qualified_name] := ~embedding_vectors:vec_idx { qualified_name | query: vec([0.1, 0.2]), k: 5, ef: 50, bind_distance: dist }";
let t = translate(q, BTreeMap::new()).unwrap();
assert!(
t.sql.contains("SELECT vec <-> $1::text::vector"),
"got: {}",
t.sql
);
assert!(t.sql.contains("LIMIT $2::int8"), "got: {}", t.sql);
assert_eq!(t.params.len(), 2);
}
#[test]
fn put_literal_non_keyed() {
let t = translate(
r#"?[element_qualified, description, user_story_id, feature_id] <- [["a", "d", null, null]] :put business_logic { element_qualified, description, user_story_id, feature_id }"#,
BTreeMap::new(),
).unwrap();
assert_eq!(t.kind, TranslationKind::Write);
assert!(
t.sql.contains("INSERT INTO business_logic"),
"got: {}",
t.sql
);
assert!(
!t.sql.contains("ON CONFLICT"),
"non-keyed table must not upsert: {}",
t.sql
);
}
#[test]
fn put_literal_keyed() {
let t = translate(
r#"?[qualified_name, usearch_key, content_hash, state, embedded_at] <- [["qn", 0, "", "stale", "now"]] :put embedding_state {qualified_name => usearch_key, content_hash, state, embedded_at}"#,
BTreeMap::new(),
).unwrap();
assert!(
t.sql.contains("INSERT INTO embedding_state"),
"got: {}",
t.sql
);
assert!(
t.sql.contains("ON CONFLICT"),
"keyed :put must upsert: {}",
t.sql
);
}
#[test]
fn rm_rule_based() {
let mut p = BTreeMap::new();
p.insert("qn".into(), serde_json::json!("a::b"));
let t = translate(
r#"?[qn, et, name, fp, ls, le, lg, pq, _, _, _] := *code_elements[qn, et, name, fp, ls, le, lg, pq, _, _, _], qn = $qn :rm code_elements {qn, et, name, fp, ls, le, lg, pq, _, _, _}"#,
p,
).unwrap();
assert!(
t.sql.starts_with("DELETE FROM code_elements"),
"got: {}",
t.sql
);
assert!(t.sql.contains("WHERE"), "got: {}", t.sql);
}
#[test]
fn rm_cross_relation() {
let t = translate(
r#"?[s, t, rt, c, m] := *relationships[s, t, rt, c, m, _], *code_elements[s, et, _, fp, _, _, _, _, _, _, _], regex_matches(fp, "^ontology://") :rm relationships {s, t, rt, c, m}"#,
BTreeMap::new(),
).unwrap();
assert!(
t.sql.contains("DELETE FROM relationships"),
"got: {}",
t.sql
);
assert!(t.sql.contains("source_qualified IN"), "got: {}", t.sql);
assert!(!t.sql.contains("information_schema"), "no info_schema leak");
}
#[test]
fn delete_where_with_param() {
let mut p = BTreeMap::new();
p.insert("id".into(), serde_json::json!("abc"));
let t = translate(r#":delete api_keys where id = "{key_id}""#, p);
let _ = t;
}
#[test]
fn delete_where_proper() {
let mut p = BTreeMap::new();
p.insert("id".into(), serde_json::json!("abc"));
let t = translate(r#":delete api_keys where id = $id"#, p).unwrap();
assert!(
t.sql.contains("DELETE FROM api_keys WHERE"),
"got: {}",
t.sql
);
assert_eq!(t.params.len(), 1);
}
#[test]
fn create_noop() {
let t = translate(
":create code_elements {qualified_name: String, element_type: String}",
BTreeMap::new(),
)
.unwrap();
assert_eq!(t.kind, TranslationKind::DdlNoop);
}
#[test]
fn replace_noop() {
let t = translate("?[a] := *t[a] :replace t {a: String}", BTreeMap::new()).unwrap();
assert_eq!(t.kind, TranslationKind::DdlNoop);
}
#[test]
fn hnsw_create_emits_pg_index_ddl() {
let t = translate(
"::hnsw create embedding_vectors:vec_idx { dim: 384, distance: Cosine }",
BTreeMap::new(),
)
.unwrap();
assert_eq!(t.kind, TranslationKind::Write);
assert!(
t.sql
.contains("CREATE INDEX IF NOT EXISTS embedding_vectors_vec_hnsw_idx")
&& t.sql.contains("USING hnsw")
&& t.sql.contains("vector_cosine_ops"),
"got: {}",
t.sql
);
}
#[test]
fn hnsw_drop_emits_pg_index_ddl() {
let t = translate("::hnsw drop embedding_vectors:vec_idx", BTreeMap::new()).unwrap();
assert_eq!(t.kind, TranslationKind::Write);
assert!(
t.sql
.contains("DROP INDEX IF EXISTS embedding_vectors_vec_hnsw_idx"),
"got: {}",
t.sql
);
}
#[test]
fn hnsw_other_targets_stay_noop() {
let t = translate(
"::hnsw create other_table:some_idx { dim: 384, distance: Cosine }",
BTreeMap::new(),
)
.unwrap();
assert_eq!(t.kind, TranslationKind::DdlNoop);
}
#[test]
fn relations_introspection_query() {
let t = translate("::relations", BTreeMap::new()).unwrap();
assert!(
t.sql.contains("information_schema.tables"),
"got: {}",
t.sql
);
assert_eq!(t.head, vec!["name".to_string()]);
}
#[test]
fn index_create_translation() {
let t = translate(
"::index create code_elements:foo { file_path }",
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql
.contains("CREATE INDEX IF NOT EXISTS code_elements_foo"),
"got: {}",
t.sql
);
}
#[test]
fn vacuum_noop() {
let t = translate("VACUUM", BTreeMap::new()).unwrap();
assert_eq!(t.kind, TranslationKind::DdlNoop);
}
#[test]
fn pragma_noop() {
let t = translate("PRAGMA page_count", BTreeMap::new()).unwrap();
assert_eq!(t.kind, TranslationKind::DdlNoop);
}
#[test]
fn row_mapping_text_and_int() {
assert_eq!(quote_ident("foo"), "\"foo\"");
assert_eq!(quote_ident("a-b"), "\"a-b\"");
let p = json_to_pg(serde_json::json!("hello"));
let _ = format!("{:?}", p);
}
#[test]
fn ann_picks_up_ef_into_guc() {
let q = r#"?[dist, qualified_name] := ~embedding_vectors:vec_idx { qualified_name | query: vec([0.0, 0.1, 0.2]), k: 5, ef: 73, bind_distance: dist }"#;
let t = translate(q, BTreeMap::new()).unwrap();
assert_eq!(t.kind, TranslationKind::Read);
assert_eq!(
t.gucs,
vec![("hnsw.ef_search".to_string(), "73".to_string())],
"ef must surface as SET LOCAL hnsw.ef_search GUC"
);
}
#[test]
fn ann_omits_guc_when_ef_missing() {
let q = r#"?[dist, qualified_name] := ~embedding_vectors:vec_idx { qualified_name | query: vec([0.0]), k: 5, bind_distance: dist }"#;
let t = translate(q, BTreeMap::new()).unwrap();
assert!(t.gucs.is_empty(), "missing ef: must not emit GUC");
}
#[test]
fn embedding_vectors_upsert_emits_ef_construction_when_set() {
let prev = std::env::var_os("LEANKG_HNSW_EF_CONST");
std::env::set_var("LEANKG_HNSW_EF_CONST", "100");
let gucs = embedding_gucs_for("embedding_vectors");
match prev {
Some(v) => std::env::set_var("LEANKG_HNSW_EF_CONST", v),
None => std::env::remove_var("LEANKG_HNSW_EF_CONST"),
}
assert_eq!(
gucs,
vec![("hnsw.ef_construction".to_string(), "100".to_string())]
);
}
#[test]
fn embedding_vectors_upsert_omits_guc_when_unset() {
let prev = std::env::var_os("LEANKG_HNSW_EF_CONST");
std::env::remove_var("LEANKG_HNSW_EF_CONST");
let gucs = embedding_gucs_for("embedding_vectors");
match prev {
Some(v) => std::env::set_var("LEANKG_HNSW_EF_CONST", v),
None => std::env::remove_var("LEANKG_HNSW_EF_CONST"),
}
assert!(gucs.is_empty(), "unset env must not emit GUC");
}
#[test]
fn embedding_state_upsert_does_not_emit_guc() {
let prev = std::env::var_os("LEANKG_HNSW_EF_CONST");
std::env::set_var("LEANKG_HNSW_EF_CONST", "100");
let gucs = embedding_gucs_for("embedding_state");
match prev {
Some(v) => std::env::set_var("LEANKG_HNSW_EF_CONST", v),
None => std::env::remove_var("LEANKG_HNSW_EF_CONST"),
}
assert!(gucs.is_empty(), "embedding_state must not surface GUCs");
}
}
#[test]
fn multi_rule_count_distinct() {
let t = translate(
"files[f] := *code_elements[n, a, b, f, c, d, e, g, h, i, j, k]\n?[count(f)] := files[f]",
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql.contains("count(DISTINCT \"file_path\")"),
"got: {}",
t.sql
);
assert!(t.sql.contains("FROM code_elements"), "got: {}", t.sql);
assert_eq!(t.head, vec!["count(f)"]);
}
#[test]
fn head_alias_span_select() {
let t = translate(
r#"?[qualified_name, file_path, line_end, line_start, language, name, span] := *code_elements[qualified_name, et, name, file_path, line_start, line_end, language, _, _, _, _], line_end >= 0, line_start >= 0, (line_end - line_start) >= 5, et in ["function", "struct"], span = line_end - line_start:order -span"#,
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql.contains("\"line_end\" - \"line_start\" AS \"span\""),
"got: {}",
t.sql
);
assert!(t.sql.contains("ORDER BY \"span\" DESC"), "got: {}", t.sql);
assert!(!t.sql.contains("WHERE \"span\""), "got: {}", t.sql);
}
#[test]
fn put_object_args() {
let mut p = BTreeMap::new();
p.insert("path".into(), serde_json::json!("a.rs"));
p.insert("hash".into(), serde_json::json!("h1"));
let t = translate(
r#"?[path, hash] <- [[$path, $hash]] :put index_hashes {path => hash}"#,
p,
)
.unwrap();
assert!(t.sql.contains("INSERT INTO index_hashes"), "got: {}", t.sql);
assert!(
t.sql.contains("ON CONFLICT (\"path\")"),
"index_hashes is keyed by path: got: {}",
t.sql
);
assert_eq!(t.params.len(), 2, "path+hash bound, not interpolated");
}
#[test]
fn put_object_args_missing_param_is_noop() {
let t = translate(
r#"?[path, hash] <- [[$path, $hash]] :put index_hashes {path => hash}"#,
BTreeMap::new(),
)
.unwrap();
assert_eq!(t.kind, TranslationKind::Write);
assert!(t.sql.contains("INSERT INTO index_hashes"), "got: {}", t.sql);
}
#[test]
fn rm_key_only_infers_table() {
let t = translate(
r#"?[qualified_name] <- [["a"], ["b"]] :rm embedding_vectors {qualified_name}"#,
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql
.contains("DELETE FROM embedding_vectors WHERE \"qualified_name\" = ANY($1::text[])"),
"got: {}",
t.sql
);
}
#[test]
fn read_attr_binding_syntax() {
let mut p = BTreeMap::new();
p.insert("svc".into(), serde_json::json!("my-service"));
p.insert("env".into(), serde_json::json!("prod"));
let t = translate(
"?[service_name, env] := *service_metadata{service_name, env, team}, service_name == $svc, env == $env",
p,
)
.unwrap();
assert!(t.sql.contains("FROM service_metadata"), "got: {}", t.sql);
assert!(
t.sql.contains("WHERE \"service_name\" = $1"),
"got: {}",
t.sql
);
assert_eq!(t.head, vec!["service_name", "env"]);
}
#[test]
fn query_cache_attr_binding_read() {
let mut p = BTreeMap::new();
p.insert("key".into(), serde_json::json!("k1"));
let t = translate(
"?[value_json, created_at, ttl_seconds] := *query_cache[cache_key = $key, value_json, created_at, ttl_seconds]",
p,
)
.unwrap();
assert!(t.sql.contains("FROM query_cache"), "got: {}", t.sql);
}
#[test]
fn rm_rule_based_with_table_prefix_and_params() {
let mut p = BTreeMap::new();
p.insert("sq".into(), serde_json::json!("src/a.rs::f"));
let t = translate(
"?[source_qualified, target_qualified, rel_type, confidence, metadata] := *relationships[source_qualified, target_qualified, rel_type, confidence, metadata, _], source_qualified = $sq :rm relationships {source_qualified, target_qualified, rel_type, confidence, metadata}",
p,
)
.unwrap();
assert!(
t.sql
.contains("DELETE FROM relationships WHERE \"source_qualified\" = $1"),
"got: {}",
t.sql
);
assert_eq!(t.params.len(), 1, "param must be bound, not lost");
}
#[test]
fn rm_rule_based_in_list_params() {
let mut p = BTreeMap::new();
p.insert(
"sqs".into(),
serde_json::json!(["src/a.rs::f", "src/b.rs::g"]),
);
let t = translate(
"?[source_qualified, target_qualified, rel_type, confidence, metadata] := *relationships[source_qualified, target_qualified, rel_type, confidence, metadata, _], source_qualified in $sqs :rm relationships {source_qualified, target_qualified, rel_type, confidence, metadata}",
p,
)
.unwrap();
assert!(
t.sql
.contains("DELETE FROM relationships WHERE \"source_qualified\" = ANY($1::text[])"),
"got: {}",
t.sql
);
}
#[test]
fn rm_literal_key_only_with_table_prefix() {
let t = translate(
r#"?[qualified_name] <- [["a"], ["b"]] :rm embedding_vectors {qualified_name}"#,
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql
.contains("DELETE FROM embedding_vectors WHERE \"qualified_name\" = ANY($1::text[])"),
"got: {}",
t.sql
);
}
#[test]
fn put_embedding_vectors_maps_vector_to_vec() {
let t = translate(
r#"?[qualified_name, vector] <- [["a", vec([1.0, 0.0, 0.0])]] :put embedding_vectors {qualified_name => vector}"#,
BTreeMap::new(),
)
.unwrap();
assert!(
t.sql
.contains("INSERT INTO embedding_vectors (\"qualified_name\", \"vec\")"),
"got: {}",
t.sql
);
assert!(
t.sql.contains("ON CONFLICT (\"qualified_name\")"),
"got: {}",
t.sql
);
assert!(
t.sql.contains("\"vec\" = EXCLUDED.\"vec\""),
"got: {}",
t.sql
);
}
#[test]
fn regex_matches_param_wrapped_in_lowercase_binds() {
let mut p = BTreeMap::new();
p.insert("pattern".into(), serde_json::json!("^foo"));
let t = translate(
"?[a] := *t[a, b], regex_matches(lowercase(b), lowercase($pattern))",
p,
)
.unwrap();
assert!(
t.sql.contains("lower($1)"),
"param must bind through the lowercase wrapper: got: {}",
t.sql
);
assert_eq!(t.params.len(), 1, "pattern must be a bound param");
}
#[test]
fn str_contains_param_wrapped_in_lowercase_binds() {
let mut p = BTreeMap::new();
p.insert("pattern".into(), serde_json::json!("needle"));
let t = translate(
"?[a] := *t[a, b], str_contains(lowercase(b), lowercase($pattern))",
p,
)
.unwrap();
assert!(t.sql.contains("lower($1)"), "got: {}", t.sql);
assert_eq!(t.params.len(), 1);
}