use super::literal::value_to_sql_literal;
use crate::error::SqlError;
use crate::parser::object_literal::{
parse_object_literal_array_complete, parse_object_literal_complete,
};
pub(super) fn try_rewrite_object_literal(sql: &str) -> Result<Option<String>, SqlError> {
let after_into = sql["INSERT INTO ".len()..].trim_start();
let Some(coll_end) = after_into.find(|c: char| c.is_whitespace()) else {
return Ok(None);
};
let coll_name = &after_into[..coll_end];
let rest = after_into[coll_end..].trim_start();
let obj_str = rest.trim_end_matches(';').trim_end();
if obj_str.starts_with('[') {
return rewrite_array_form(coll_name, obj_str);
}
if !obj_str.starts_with('{') {
return Ok(None);
}
let Some(parsed) = parse_object_literal_complete(obj_str) else {
return Ok(None);
};
let fields = parsed?;
if fields.is_empty() {
return Ok(None);
}
Ok(Some(fields_to_values_sql(coll_name, &[fields])))
}
fn rewrite_array_form(coll_name: &str, obj_str: &str) -> Result<Option<String>, SqlError> {
let Some(parsed) = parse_object_literal_array_complete(obj_str) else {
return Ok(None);
};
let objects = parsed?;
if objects.is_empty() {
return Ok(None);
}
Ok(Some(fields_to_values_sql(coll_name, &objects)))
}
fn fields_to_values_sql(
coll_name: &str,
rows: &[std::collections::HashMap<String, nodedb_types::Value>],
) -> String {
let mut all_keys: Vec<String> = rows
.iter()
.flat_map(|r| r.keys().cloned())
.collect::<std::collections::BTreeSet<_>>()
.into_iter()
.collect();
all_keys.sort();
let col_list = all_keys.join(", ");
let row_strs: Vec<String> = rows
.iter()
.map(|row| {
let vals: Vec<String> = all_keys
.iter()
.map(|k| match row.get(k) {
Some(v) => value_to_sql_literal(v),
None => "NULL".to_string(),
})
.collect();
format!("({})", vals.join(", "))
})
.collect();
format!(
"INSERT INTO {} ({}) VALUES {}",
coll_name,
col_list,
row_strs.join(", ")
)
}