Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/ast/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,8 @@ pub enum TypedFunction {
},
/// `CURRENT_DATE`
CurrentDate,
/// `CURRENT_TIME` — returns the current time of day (ANSI / PostgreSQL).
CurrentTime,
/// `CURRENT_TIMESTAMP` / `NOW()` / `GETDATE()`
CurrentTimestamp,
/// `STR_TO_TIME(expr, format)` / `TO_TIMESTAMP` / `PARSE_DATETIME`
Expand Down Expand Up @@ -885,7 +887,9 @@ impl TypedFunction {
end.walk(visitor);
}
TypedFunction::DateTrunc { expr, .. } => expr.walk(visitor),
TypedFunction::CurrentDate | TypedFunction::CurrentTimestamp => {}
TypedFunction::CurrentDate
| TypedFunction::CurrentTime
| TypedFunction::CurrentTimestamp => {}
TypedFunction::StrToTime { expr, format }
| TypedFunction::TimeToStr { expr, format } => {
expr.walk(visitor);
Expand Down Expand Up @@ -1147,6 +1151,7 @@ impl TypedFunction {
unit,
},
TypedFunction::CurrentDate => TypedFunction::CurrentDate,
TypedFunction::CurrentTime => TypedFunction::CurrentTime,
TypedFunction::CurrentTimestamp => TypedFunction::CurrentTimestamp,
TypedFunction::StrToTime { expr, format } => TypedFunction::StrToTime {
expr: Box::new(expr.transform(func)),
Expand Down
342 changes: 341 additions & 1 deletion src/dialects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,350 @@ fn is_hive_family(d: Dialect) -> bool {
}

/// Dialects in the T-SQL family.
fn is_tsql_family(d: Dialect) -> bool {
pub(crate) fn is_tsql_family(d: Dialect) -> bool {
matches!(d, Dialect::Tsql | Dialect::Fabric)
}

/// Returns `true` when `name` (case-insensitive) is a T-SQL reserved keyword
/// that must be quoted with square brackets when used as an identifier
/// (e.g. as a column alias, table alias, or column name).
///
/// Sourced from Microsoft's documented T-SQL reserved words (current and
/// future). Covers both the ANSI/ODBC reserved set and SQL Server's
/// dialect-specific reservations. Not exhaustive for every contextual
/// keyword — focuses on words that, when emitted unquoted as aliases, will
/// cause MSSQL syntax error 156.
#[must_use]
pub(crate) fn is_tsql_reserved(name: &str) -> bool {
// Reserved word set — keep in sorted order for binary_search.
// Source: docs.microsoft.com "Reserved Keywords (Transact-SQL)" and
// "ODBC Reserved Keywords", plus ANSI/ISO future reserved words.
const RESERVED: &[&str] = &[
"ABSOLUTE",
"ACTION",
"ADA",
"ADD",
"ALL",
"ALLOCATE",
"ALTER",
"AND",
"ANY",
"ARE",
"AS",
"ASC",
"ASSERTION",
"AT",
"AUTHORIZATION",
"AVG",
"BACKUP",
"BEGIN",
"BETWEEN",
"BIT",
"BIT_LENGTH",
"BOTH",
"BREAK",
"BROWSE",
"BULK",
"BY",
"CASCADE",
"CASCADED",
"CASE",
"CAST",
"CATALOG",
"CHAR",
"CHARACTER",
"CHARACTER_LENGTH",
"CHAR_LENGTH",
"CHECK",
"CHECKPOINT",
"CLOSE",
"CLUSTERED",
"COALESCE",
"COLLATE",
"COLLATION",
"COLUMN",
"COMMIT",
"COMPUTE",
"CONNECT",
"CONNECTION",
"CONSTRAINT",
"CONSTRAINTS",
"CONTAINS",
"CONTAINSTABLE",
"CONTINUE",
"CONVERT",
"CORRESPONDING",
"COUNT",
"CREATE",
"CROSS",
"CURRENT",
"CURRENT_DATE",
"CURRENT_TIME",
"CURRENT_TIMESTAMP",
"CURRENT_USER",
"CURSOR",
"DATABASE",
"DATE",
"DBCC",
"DEALLOCATE",
"DEC",
"DECIMAL",
"DECLARE",
"DEFAULT",
"DEFERRABLE",
"DEFERRED",
"DELETE",
"DENY",
"DESC",
"DESCRIBE",
"DESCRIPTOR",
"DIAGNOSTICS",
"DISCONNECT",
"DISK",
"DISTINCT",
"DISTRIBUTED",
"DOMAIN",
"DOUBLE",
"DROP",
"DUMP",
"ELSE",
"END",
"ERRLVL",
"ESCAPE",
"EXCEPT",
"EXCEPTION",
"EXEC",
"EXECUTE",
"EXISTS",
"EXIT",
"EXTERNAL",
"EXTRACT",
"FETCH",
"FILE",
"FILLFACTOR",
"FLOAT",
"FOR",
"FOREIGN",
"FORTRAN",
"FOUND",
"FREETEXT",
"FREETEXTTABLE",
"FROM",
"FULL",
"FUNCTION",
"GET",
"GLOBAL",
"GO",
"GOTO",
"GRANT",
"GROUP",
"HAVING",
"HOLDLOCK",
"HOUR",
"IDENTITY",
"IDENTITYCOL",
"IDENTITY_INSERT",
"IF",
"IMMEDIATE",
"IN",
"INCLUDE",
"INDEX",
"INDICATOR",
"INITIALLY",
"INNER",
"INPUT",
"INSENSITIVE",
"INSERT",
"INT",
"INTEGER",
"INTERSECT",
"INTERVAL",
"INTO",
"IS",
"ISOLATION",
"JOIN",
"KEY",
"KILL",
"LANGUAGE",
"LAST",
"LEADING",
"LEFT",
"LEVEL",
"LIKE",
"LINENO",
"LOAD",
"LOCAL",
"LOWER",
"MATCH",
"MAX",
"MERGE",
"MIN",
"MINUTE",
"MODULE",
"MONTH",
"NAMES",
"NATIONAL",
"NATURAL",
"NCHAR",
"NEXT",
"NO",
"NOCHECK",
"NONCLUSTERED",
"NONE",
"NOT",
"NULL",
"NULLIF",
"NUMERIC",
"OCTET_LENGTH",
"OF",
"OFF",
"OFFSETS",
"ON",
"ONLY",
"OPEN",
"OPENDATASOURCE",
"OPENQUERY",
"OPENROWSET",
"OPENXML",
"OPTION",
"OR",
"ORDER",
"OUTER",
"OUTPUT",
"OVER",
"OVERLAPS",
"PAD",
"PARTIAL",
"PASCAL",
"PERCENT",
"PIVOT",
"PLAN",
"POSITION",
"PRECISION",
"PREPARE",
"PRESERVE",
"PRIMARY",
"PRINT",
"PRIOR",
"PRIVILEGES",
"PROC",
"PROCEDURE",
"PUBLIC",
"RAISERROR",
"READ",
"READTEXT",
"REAL",
"RECONFIGURE",
"REFERENCES",
"RELATIVE",
"REPLICATION",
"RESTORE",
"RESTRICT",
"RETURN",
"REVERT",
"REVOKE",
"RIGHT",
"ROLLBACK",
"ROWCOUNT",
"ROWGUIDCOL",
"ROWS",
"RULE",
"SAVE",
"SCHEMA",
"SCROLL",
"SECOND",
"SECTION",
"SECURITYAUDIT",
"SELECT",
"SEMANTICKEYPHRASETABLE",
"SEMANTICSIMILARITYDETAILSTABLE",
"SEMANTICSIMILARITYTABLE",
"SESSION",
"SESSION_USER",
"SET",
"SETUSER",
"SHUTDOWN",
"SIZE",
"SMALLINT",
"SOME",
"SPACE",
"SQL",
"SQLCA",
"SQLCODE",
"SQLERROR",
"SQLSTATE",
"SQLWARNING",
"STATISTICS",
"SUBSTRING",
"SUM",
"SYSTEM_USER",
"TABLE",
"TABLESAMPLE",
"TEMPORARY",
"TEXTSIZE",
"THEN",
"TIME",
"TIMESTAMP",
"TIMEZONE_HOUR",
"TIMEZONE_MINUTE",
"TO",
"TOP",
"TRAILING",
"TRAN",
"TRANSACTION",
"TRANSLATE",
"TRANSLATION",
"TRIGGER",
"TRIM",
"TRUE",
"TRUNCATE",
"TRY_CONVERT",
"TSEQUAL",
"UNION",
"UNIQUE",
"UNKNOWN",
"UNPIVOT",
"UPDATE",
"UPDATETEXT",
"UPPER",
"USAGE",
"USE",
"USER",
"USING",
"VALUE",
"VALUES",
"VARCHAR",
"VARYING",
"VIEW",
"WAITFOR",
"WHEN",
"WHENEVER",
"WHERE",
"WHILE",
"WITH",
"WITHIN GROUP",
"WORK",
"WRITE",
"WRITETEXT",
"YEAR",
"ZONE",
];

// Cheap upper-case comparison without allocation for ASCII identifiers.
if name.is_empty() || name.len() > 32 {
return false;
}
let mut buf = [0u8; 32];
for (i, b) in name.as_bytes().iter().enumerate() {
buf[i] = b.to_ascii_uppercase();
}
let upper = match std::str::from_utf8(&buf[..name.len()]) {
Ok(s) => s,
Err(_) => return false,
};
RESERVED.binary_search(&upper).is_ok()
}

/// Dialects that natively support ILIKE.
pub(crate) fn supports_ilike_builtin(d: Dialect) -> bool {
matches!(
Expand Down
5 changes: 4 additions & 1 deletion src/dialects/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ fn typed_function_canonical_name(func: &TypedFunction) -> &'static str {
TypedFunction::DateTrunc { .. } => "DATE_TRUNC",
TypedFunction::DateSub { .. } => "DATE_SUB",
TypedFunction::CurrentDate => "CURRENT_DATE",
TypedFunction::CurrentTime => "CURRENT_TIME",
TypedFunction::CurrentTimestamp => "NOW",
TypedFunction::StrToTime { .. } => "STR_TO_TIME",
TypedFunction::TimeToStr { .. } => "TIME_TO_STR",
Expand Down Expand Up @@ -368,7 +369,9 @@ fn typed_function_canonical_name(func: &TypedFunction) -> &'static str {
/// Extract the argument expressions from a TypedFunction (in positional order).
fn typed_function_args(func: &TypedFunction) -> Vec<Expr> {
match func {
TypedFunction::CurrentDate | TypedFunction::CurrentTimestamp => vec![],
TypedFunction::CurrentDate
| TypedFunction::CurrentTime
| TypedFunction::CurrentTimestamp => vec![],
TypedFunction::RowNumber | TypedFunction::Rank | TypedFunction::DenseRank => vec![],
TypedFunction::Length { expr }
| TypedFunction::Upper { expr }
Expand Down
Loading