use selene_core::DbString;
const AGGREGATE_OPS: &[&str] = &[
"AVG",
"COLLECT_LIST",
"COUNT",
"MAX",
"MIN",
"PERCENTILE_CONT",
"PERCENTILE_DISC",
"STDDEV_POP",
"STDDEV_SAMP",
"SUM",
];
const KEYWORD_FUNCTION_CALLS: &[&str] = &[
"ABS",
"ACOS",
"ASIN",
"ATAN",
"BTRIM",
"BYTE_LENGTH",
"CARDINALITY",
"CEIL",
"CEILING",
"CHAR_LENGTH",
"CHARACTER_LENGTH",
"COS",
"COSH",
"COT",
"COALESCE",
"DATE",
"DATETIME",
"DEGREES",
"DURATION",
"ELEMENT_ID",
"ELEMENTS",
"EXP",
"FLOOR",
"LABELS",
"LEFT",
"LOCAL_DATETIME",
"LOCAL_TIME",
"LN",
"LOG",
"LOG10",
"LOWER",
"LTRIM",
"MOD",
"NULLIF",
"OCTET_LENGTH",
"PATH_LENGTH",
"POWER",
"RADIANS",
"RIGHT",
"RTRIM",
"SIN",
"SINH",
"SIZE",
"SQRT",
"TAN",
"TANH",
"TIME",
"TRIM",
"UPPER",
"ZONED_DATETIME",
"ZONED_TIME",
];
#[rustfmt::skip]
const KEYWORDS: &[&str] = &[
"ABSTRACT", "ABS", "ACOS", "ACYCLIC", "AGGREGATE", "AGGREGATES", "ALL",
"ALL_DIFFERENT", "ALTER", "AND", "ANY", "ARRAY", "AS", "ASC", "ASCENDING",
"ASIN", "AT", "ATAN", "AVG", "BIG", "BIGINT", "BINARY", "BINDING",
"BINDINGS", "BOOL", "BOOLEAN", "BOTH", "BTRIM", "BY", "BYTE_LENGTH",
"BYTEA", "BYTES", "CALL", "CARDINALITY", "CASE", "CAST", "CATALOG",
"CEIL", "CEILING", "CHAR", "CHAR_LENGTH", "CHARACTER_LENGTH",
"CHARACTERISTICS", "CLEAR", "CLONE", "CLOSE", "COALESCE", "COLLECT_LIST",
"COMMIT", "CONNECTING", "CONSTRAINT", "CONTAINS", "COPY", "COS", "COSH",
"COT", "COUNT", "CREATE", "CURRENT_DATE", "CURRENT_GRAPH",
"CURRENT_PROPERTY_GRAPH", "CURRENT_ROLE", "CURRENT_SCHEMA",
"CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "DATA", "DATE",
"DATETIME", "DAY", "DEC", "DECIMAL", "DEFAULT", "DEGREES", "DELETE",
"DESC", "DESCENDING", "DESTINATION", "DETACH", "DICTIONARY", "DIFFERENT",
"DIRECTED", "DIRECTORY", "DISTINCT", "DOUBLE", "DROP", "DRYRUN",
"DURATION", "DURATION_BETWEEN", "EDGE", "EDGES", "ELEMENT_ID",
"ELEMENTS", "ELSE", "ENCODING", "END", "ENDS", "EXACT", "EXCEPT",
"EXISTING", "EXISTS", "EXP", "EXTENDS", "FALSE", "FILL", "FILTER",
"FINISH", "FIRST", "FLOAT", "FLOAT16", "FLOAT32", "FLOAT64",
"FLOAT128", "FLOAT256", "FLOOR", "FOR", "FROM", "FUNCTION", "GQLSTATUS",
"GRANT", "GRAPH", "GROUP", "HAVING", "HOME_GRAPH", "HOME_PROPERTY_GRAPH",
"HOME_SCHEMA", "HOUR", "IF", "IMMUTABLE", "IMPLIES", "IN", "INDEX",
"INDEXED", "INFINITY", "INSERT", "INSTANT", "INT", "INT8", "INT16",
"INT32", "INT64", "INT128", "INT256", "INTEGER", "INTEGER8", "INTEGER16",
"INTEGER32", "INTEGER64", "INTEGER128", "INTEGER256", "INTERSECT",
"INTERVAL", "IS", "KEEP", "LABELED", "LABELS", "LAST", "LEADING",
"LEFT", "LET", "LIKE", "LIMIT", "LIST", "LN", "LOCAL",
"LOCAL_DATETIME", "LOCAL_TIME", "LOCAL_TIMESTAMP", "LOG", "LOG10",
"LOWER", "LTRIM", "MATCH", "MAX", "MERGE", "MIN", "MINUTE", "MOD",
"MONTH", "NEXT", "NFC", "NFD", "NFKC", "NFKD", "NO", "NODE",
"NODETACH", "NONE", "NORMALIZE", "NORMALIZED", "NOT", "NOTHING",
"NULL", "NULLIF", "NULLS", "NUMBER", "NUMERIC", "OCTET_LENGTH", "OF",
"OFFSET", "ON", "ONLY", "OPEN", "OPTIONAL", "OR", "ORDER", "ORDINALITY",
"OTHERWISE", "PARAMETER", "PARAMETERS", "PARTITION", "PATH", "PATH_LENGTH",
"PATHS", "PERCENTILE_CONT", "PERCENTILE_DISC", "POWER", "PRECISION",
"PROCEDURE", "PRODUCT", "PROJECT", "PROPERTY_EXISTS", "QUERY", "RADIANS",
"REAL", "RECORD", "RECORDS", "REDUCE", "REFERENCE", "REMOVE", "RENAME",
"REPEATABLE", "REPLACE", "RESET", "RETURN", "REVOKE", "RIGHT",
"ROLLBACK", "RTRIM", "SAME", "SCHEMA", "SEARCHABLE", "SECOND", "SELECT",
"SESSION", "SESSION_USER", "SET", "SHORTEST", "SHOW", "SIGNED", "SIMPLE",
"SIN", "SINH", "SINGLE", "SIZE", "SKIP", "SMALL", "SMALLINT", "SOURCE",
"SQRT", "START", "STARTS", "STDDEV_POP", "STDDEV_SAMP", "STRICT",
"STRING", "SUBSTRING", "SUM", "SYSTEM_USER", "TAN", "TANH", "TEMPORAL",
"THEN", "TIME", "TIMESTAMP", "TO", "TRAIL", "TRAILING", "TRANSACTION",
"TRIM", "TRUE", "TYPE", "TYPED", "TYPES", "UBIGINT", "UINT", "UINT8",
"UINT16", "UINT32", "UINT64", "UINT128", "UINT256", "UNION", "UNIQUE",
"UNIT", "UNKNOWN", "UNSIGNED", "UPPER", "USE", "USMALLINT",
"UUID", "VALUES", "VARBINARY", "VARCHAR", "VARIABLE", "WALK", "WARN",
"WHEN", "WHERE", "WHITESPACE", "WITH", "WITHOUT", "XOR", "YEAR", "YIELD",
"ZONED", "ZONED_DATETIME", "ZONED_TIME",
];
#[rustfmt::skip]
const CONTEXTUAL_IDENTIFIER_KEYWORDS: &[&str] = &[
"EXPLAIN", "INDEXES", "PROCEDURES", "VALUE",
];
pub(crate) fn fmt_ident(value: DbString) -> String {
let value = value.as_str();
let upper = value.to_ascii_uppercase();
if is_simple_ident(value) && !is_identifier_keyword(&upper) {
return value.to_owned();
}
format!("\"{}\"", value.replace('"', "\"\""))
}
pub(crate) fn fmt_expr_ident(value: DbString) -> String {
let value = value.as_str();
let upper = value.to_ascii_uppercase();
if is_simple_ident(value) && !KEYWORDS.contains(&upper.as_str()) {
return value.to_owned();
}
fmt_backtick_ident(value)
}
pub(super) fn fmt_call_segment(value: DbString) -> String {
let value = value.as_str();
if is_simple_ident(value) {
let upper = value.to_ascii_uppercase();
let is_aggregate = AGGREGATE_OPS.contains(&upper.as_str());
let is_keyword_function = KEYWORD_FUNCTION_CALLS.contains(&upper.as_str());
let is_keyword = KEYWORDS.contains(&upper.as_str());
if is_aggregate || is_keyword_function || !is_keyword {
return value.to_owned();
}
}
fmt_backtick_ident(value)
}
pub(super) fn escape_string(value: &str) -> String {
value
.replace('\\', "\\\\")
.replace('\n', "\\n")
.replace('\r', "\\r")
.replace('\t', "\\t")
.replace('\'', "''")
}
fn is_simple_ident(value: &str) -> bool {
let mut chars = value.chars();
let Some(first) = chars.next() else {
return false;
};
(first == '_' || first.is_alphabetic()) && chars.all(|ch| ch == '_' || ch.is_alphanumeric())
}
fn is_identifier_keyword(upper: &str) -> bool {
KEYWORDS.contains(&upper) || CONTEXTUAL_IDENTIFIER_KEYWORDS.contains(&upper)
}
fn fmt_backtick_ident(value: &str) -> String {
format!("`{}`", value.replace('`', "``"))
}