pub fn escape_ident(name: &str) -> String {
format!("\"{}\"", name.replace('"', "\"\""))
}
pub fn quote_literal(s: &str) -> String {
format!("'{}'", s.replace('\'', "''"))
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct QualifiedIdentifier {
pub schema: String,
pub name: String,
}
impl QualifiedIdentifier {
pub fn new(schema: impl Into<String>, name: impl Into<String>) -> Self {
Self {
schema: schema.into(),
name: name.into(),
}
}
pub fn unqualified(name: impl Into<String>) -> Self {
Self {
schema: String::new(),
name: name.into(),
}
}
}
pub fn from_qi(qi: &QualifiedIdentifier) -> String {
if qi.schema.is_empty() {
escape_ident(&qi.name)
} else {
format!("{}.{}", escape_ident(&qi.schema), escape_ident(&qi.name))
}
}
pub fn is_valid_identifier(s: &str) -> bool {
if s.is_empty() {
return false;
}
let mut chars = s.chars();
let first = chars.next().unwrap();
if !first.is_ascii_alphabetic() && first != '_' {
return false;
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
}
pub fn is_keyword(s: &str) -> bool {
const KEYWORDS: &[&str] = &[
"all", "and", "any", "array", "as", "asc", "between", "by",
"case", "cast", "check", "column", "constraint", "create",
"cross", "current", "default", "delete", "desc", "distinct",
"drop", "else", "end", "exists", "false", "for", "foreign",
"from", "full", "group", "having", "in", "index", "inner",
"insert", "into", "is", "join", "key", "left", "like", "limit",
"not", "null", "offset", "on", "or", "order", "outer", "primary",
"references", "right", "select", "set", "table", "then", "to",
"true", "union", "unique", "update", "using", "values", "when",
"where", "with",
];
KEYWORDS.contains(&s.to_lowercase().as_str())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_escape_ident() {
assert_eq!(escape_ident("users"), "\"users\"");
assert_eq!(escape_ident("user_table"), "\"user_table\"");
assert_eq!(escape_ident("user\"name"), "\"user\"\"name\"");
assert_eq!(escape_ident("My Table"), "\"My Table\"");
assert_eq!(escape_ident(""), "\"\"");
}
#[test]
fn test_quote_literal() {
assert_eq!(quote_literal("hello"), "'hello'");
assert_eq!(quote_literal("it's"), "'it''s'");
assert_eq!(quote_literal(""), "''");
}
#[test]
fn test_from_qi() {
let qi = QualifiedIdentifier::new("public", "users");
assert_eq!(from_qi(&qi), "\"public\".\"users\"");
let qi = QualifiedIdentifier::unqualified("users");
assert_eq!(from_qi(&qi), "\"users\"");
let qi = QualifiedIdentifier::new("my schema", "my\"table");
assert_eq!(from_qi(&qi), "\"my schema\".\"my\"\"table\"");
}
#[test]
fn test_is_valid_identifier() {
assert!(is_valid_identifier("users"));
assert!(is_valid_identifier("_private"));
assert!(is_valid_identifier("user123"));
assert!(is_valid_identifier("user$table"));
assert!(!is_valid_identifier(""));
assert!(!is_valid_identifier("123users"));
assert!(!is_valid_identifier("my-table"));
assert!(!is_valid_identifier("my table"));
}
#[test]
fn test_is_keyword() {
assert!(is_keyword("select"));
assert!(is_keyword("SELECT"));
assert!(is_keyword("from"));
assert!(is_keyword("WHERE"));
assert!(!is_keyword("users"));
assert!(!is_keyword("my_column"));
}
}