#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatementShape {
Rows,
NoRows,
Unknown,
}
pub fn statement_shape(sql: &str) -> StatementShape {
let keyword = first_keyword(sql).to_ascii_uppercase();
match keyword.as_str() {
"SELECT" | "SHOW" | "VALUES" | "TABLE" | "WITH" | "EXPLAIN" | "DESCRIBE" | "DESC"
| "FETCH" => StatementShape::Rows,
"INSERT" | "UPDATE" | "DELETE" | "MERGE" | "CREATE" | "DROP" | "ALTER" | "TRUNCATE"
| "SET" | "BEGIN" | "START" | "COMMIT" | "ROLLBACK" | "SAVEPOINT" | "RELEASE" | "USE"
| "GRANT" | "REVOKE" | "LOCK" | "UNLOCK" | "DISCARD" | "RESET" | "DEALLOCATE"
| "PREPARE" | "COPY" | "VACUUM" | "ANALYZE" | "COMMENT" | "LISTEN" | "NOTIFY"
| "UNLISTEN" => StatementShape::NoRows,
_ => StatementShape::Unknown,
}
}
fn first_keyword(sql: &str) -> &str {
let mut rest = sql;
loop {
let trimmed = rest.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
if let Some(after) = trimmed.strip_prefix("--") {
rest = after.split_once('\n').map(|(_, tail)| tail).unwrap_or("");
} else if let Some(after) = trimmed.strip_prefix("/*") {
rest = after.split_once("*/").map(|(_, tail)| tail).unwrap_or("");
} else {
rest = trimmed;
break;
}
}
let end = rest
.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_'))
.unwrap_or(rest.len());
&rest[..end]
}