use nodedb_sql::parser::preprocess::lex::keyword_position_outside_literals;
use nodedb_types::starts_with_ascii_case_insensitive;
use crate::error::LiteError;
const UNFLAGGED_VERBS: [&str; 3] = ["INSERT", "UPSERT", "DELETE"];
pub(crate) fn refuse_returning(statement: &str, returning: bool) -> Result<(), LiteError> {
if returning {
return Err(returning_unsupported(statement));
}
Ok(())
}
pub(crate) fn refuse_unflagged_returning(sql: &str) -> Result<(), LiteError> {
let trimmed = sql.trim_start();
let Some(verb) = UNFLAGGED_VERBS
.iter()
.find(|verb| starts_with_ascii_case_insensitive(trimmed, verb))
else {
return Ok(());
};
if keyword_position_outside_literals(sql, "RETURNING").is_some() {
return Err(returning_unsupported(verb));
}
Ok(())
}
fn returning_unsupported(statement: &str) -> LiteError {
LiteError::Unsupported {
detail: format!("{statement} ... RETURNING is not supported in Lite"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_flag_is_refused() {
let err = refuse_returning("UPDATE", true).unwrap_err();
assert!(matches!(err, LiteError::Unsupported { .. }));
refuse_returning("UPDATE", false).unwrap();
}
#[test]
fn unflagged_verbs_with_returning_are_refused() {
for sql in [
"INSERT INTO t (id) VALUES ('a') RETURNING id",
"upsert into t (id) values ('a') returning *",
" DELETE FROM t WHERE id = 'a' RETURNING id",
] {
let err = refuse_unflagged_returning(sql).unwrap_err();
assert!(matches!(err, LiteError::Unsupported { .. }), "{sql}");
}
}
#[test]
fn returning_inside_literals_or_other_statements_passes() {
for sql in [
"INSERT INTO t (id, note) VALUES ('a', 'RETURNING')",
"INSERT INTO t (id, \"returning\") VALUES ('a', 1)",
"SELECT 'RETURNING'",
"UPDATE t SET n = 1 RETURNING n",
] {
refuse_unflagged_returning(sql).unwrap();
}
}
}