1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3pub enum FunctionCategory {
4 Aggregate,
5 WindowRanking,
6 WindowDistribution,
7 WindowNavigation,
8 NullHandling,
9 DateTime,
10 String,
11 Math,
12 Trigonometric,
13 Comparison,
14 Json,
15 Boolean,
16 Array,
17 TypeConversion,
18 Constant,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26pub enum SqlFunction {
27 Count,
29 Sum,
30 Avg,
31 Min,
32 Max,
33 Stddev,
34 Variance,
35 StddevPop,
36 StddevSamp,
37 VarPop,
38 VarSamp,
39 ArrayAgg,
40 StringAgg,
41 GroupConcat,
42 Listagg,
43 Median,
44 Mode,
45 PercentileCont,
46 PercentileDisc,
47 ApproxCountDistinct,
48 AnyValue,
49 First,
50 Last,
51 BoolAnd,
52 BoolOr,
53 BitAnd,
54 BitOr,
55 BitXor,
56 Corr,
57 CovarPop,
58 CovarSamp,
59 RegrSlope,
60 Every,
61
62 RowNumber,
64 Rank,
65 DenseRank,
66 Ntile,
67
68 CumeDist,
70 PercentRank,
71
72 Lag,
74 Lead,
75 FirstValue,
76 LastValue,
77 NthValue,
78
79 Coalesce,
81 Nullif,
82 Ifnull,
83
84 Now,
86 CurrentTimestamp,
87 CurrentDate,
88 Date,
89 DateTrunc,
90 Extract,
91 DatePart,
92 Year,
93 Month,
94 Day,
95 DayOfWeek,
96 Quarter,
97 MakeDate,
98 MakeTime,
99 MakeTimestamp,
100 MakeTimestamptz,
101 Age,
102
103 Concat,
105 Upper,
106 Lower,
107 Trim,
108 Ltrim,
109 Rtrim,
110 Substring,
111 Substr,
112 Length,
113 CharLength,
114 CharacterLength,
115 ToChar,
116 Replace,
117 Translate,
118 Reverse,
119 Repeat,
120 Lpad,
121 Rpad,
122 Initcap,
123 QuoteIdent,
124 QuoteLiteral,
125 Left,
126 Right,
127 Position,
128 Strpos,
129 SplitPart,
130
131 Abs,
133 Sign,
134 Round,
135 Trunc,
136 Truncate,
137 Ceil,
138 Ceiling,
139 Floor,
140 Power,
141 Pow,
142 Sqrt,
143 Exp,
144 Ln,
145 Log,
146 Log10,
147 Log2,
148 Mod,
149
150 Sin,
152 Cos,
153 Tan,
154 Asin,
155 Acos,
156 Atan,
157 Atan2,
158 Sinh,
159 Cosh,
160 Tanh,
161
162 Pi,
164 Random,
165
166 Greatest,
168 Least,
169
170 JsonObject,
173 JsonArray,
175 ToJson,
177 JsonExtract,
179 JsonExtractText,
181 JsonArrayLength,
183 JsonObjectKeys,
185 JsonContains,
187 }
190
191const ALL_FUNCTIONS: &[SqlFunction] = &[
193 SqlFunction::Count,
194 SqlFunction::Sum,
195 SqlFunction::Avg,
196 SqlFunction::Min,
197 SqlFunction::Max,
198 SqlFunction::Stddev,
199 SqlFunction::Variance,
200 SqlFunction::StddevPop,
201 SqlFunction::StddevSamp,
202 SqlFunction::VarPop,
203 SqlFunction::VarSamp,
204 SqlFunction::ArrayAgg,
205 SqlFunction::StringAgg,
206 SqlFunction::GroupConcat,
207 SqlFunction::Listagg,
208 SqlFunction::Median,
209 SqlFunction::Mode,
210 SqlFunction::PercentileCont,
211 SqlFunction::PercentileDisc,
212 SqlFunction::ApproxCountDistinct,
213 SqlFunction::AnyValue,
214 SqlFunction::First,
215 SqlFunction::Last,
216 SqlFunction::BoolAnd,
217 SqlFunction::BoolOr,
218 SqlFunction::BitAnd,
219 SqlFunction::BitOr,
220 SqlFunction::BitXor,
221 SqlFunction::Corr,
222 SqlFunction::CovarPop,
223 SqlFunction::CovarSamp,
224 SqlFunction::RegrSlope,
225 SqlFunction::Every,
226 SqlFunction::RowNumber,
227 SqlFunction::Rank,
228 SqlFunction::DenseRank,
229 SqlFunction::Ntile,
230 SqlFunction::CumeDist,
231 SqlFunction::PercentRank,
232 SqlFunction::Lag,
233 SqlFunction::Lead,
234 SqlFunction::FirstValue,
235 SqlFunction::LastValue,
236 SqlFunction::NthValue,
237 SqlFunction::Coalesce,
238 SqlFunction::Nullif,
239 SqlFunction::Ifnull,
240 SqlFunction::Now,
241 SqlFunction::CurrentTimestamp,
242 SqlFunction::CurrentDate,
243 SqlFunction::Date,
244 SqlFunction::DateTrunc,
245 SqlFunction::Extract,
246 SqlFunction::DatePart,
247 SqlFunction::Year,
248 SqlFunction::Month,
249 SqlFunction::Day,
250 SqlFunction::DayOfWeek,
251 SqlFunction::Quarter,
252 SqlFunction::MakeDate,
253 SqlFunction::MakeTime,
254 SqlFunction::MakeTimestamp,
255 SqlFunction::MakeTimestamptz,
256 SqlFunction::Age,
257 SqlFunction::Concat,
258 SqlFunction::Upper,
259 SqlFunction::Lower,
260 SqlFunction::Trim,
261 SqlFunction::Ltrim,
262 SqlFunction::Rtrim,
263 SqlFunction::Substring,
264 SqlFunction::Substr,
265 SqlFunction::Length,
266 SqlFunction::CharLength,
267 SqlFunction::CharacterLength,
268 SqlFunction::ToChar,
269 SqlFunction::Replace,
270 SqlFunction::Translate,
271 SqlFunction::Reverse,
272 SqlFunction::Repeat,
273 SqlFunction::Lpad,
274 SqlFunction::Rpad,
275 SqlFunction::Initcap,
276 SqlFunction::QuoteIdent,
277 SqlFunction::QuoteLiteral,
278 SqlFunction::Left,
279 SqlFunction::Right,
280 SqlFunction::Position,
281 SqlFunction::Strpos,
282 SqlFunction::SplitPart,
283 SqlFunction::Abs,
284 SqlFunction::Sign,
285 SqlFunction::Round,
286 SqlFunction::Trunc,
287 SqlFunction::Truncate,
288 SqlFunction::Ceil,
289 SqlFunction::Ceiling,
290 SqlFunction::Floor,
291 SqlFunction::Power,
292 SqlFunction::Pow,
293 SqlFunction::Sqrt,
294 SqlFunction::Exp,
295 SqlFunction::Ln,
296 SqlFunction::Log,
297 SqlFunction::Log10,
298 SqlFunction::Log2,
299 SqlFunction::Mod,
300 SqlFunction::Sin,
301 SqlFunction::Cos,
302 SqlFunction::Tan,
303 SqlFunction::Asin,
304 SqlFunction::Acos,
305 SqlFunction::Atan,
306 SqlFunction::Atan2,
307 SqlFunction::Sinh,
308 SqlFunction::Cosh,
309 SqlFunction::Tanh,
310 SqlFunction::Pi,
311 SqlFunction::Random,
312 SqlFunction::Greatest,
313 SqlFunction::Least,
314 SqlFunction::JsonObject,
315 SqlFunction::JsonArray,
316 SqlFunction::ToJson,
317 SqlFunction::JsonExtract,
318 SqlFunction::JsonExtractText,
319 SqlFunction::JsonArrayLength,
320 SqlFunction::JsonObjectKeys,
321 SqlFunction::JsonContains,
322];
323
324impl SqlFunction {
325 pub fn from_name(name: &str) -> Option<Self> {
330 let upper = name.to_uppercase();
331
332 if let Some(canonical) = Self::resolve_alias(&upper) {
334 return Some(canonical);
335 }
336
337 ALL_FUNCTIONS.iter().find(|f| f.name() == upper).copied()
339 }
340
341 fn resolve_alias(upper_name: &str) -> Option<Self> {
343 match upper_name {
344 "JSON_BUILD_OBJECT" => Some(Self::JsonObject),
346 "JSON_BUILD_ARRAY" => Some(Self::JsonArray),
348 "TO_JSONB" | "ROW_TO_JSON" => Some(Self::ToJson),
350 "JSON_EXTRACT_PATH" => Some(Self::JsonExtract),
352 "JSON_EXTRACT_STRING" | "JSON_EXTRACT_PATH_TEXT" | "GET_JSON_OBJECT" | "JSON_VALUE" => {
354 Some(Self::JsonExtractText)
355 }
356 "JSON_KEYS" => Some(Self::JsonObjectKeys),
358 "NVL" => Some(Self::Ifnull),
360 _ => None,
361 }
362 }
363
364 pub fn name(&self) -> &'static str {
366 match self {
367 Self::Count => "COUNT",
368 Self::Sum => "SUM",
369 Self::Avg => "AVG",
370 Self::Min => "MIN",
371 Self::Max => "MAX",
372 Self::Stddev => "STDDEV",
373 Self::Variance => "VARIANCE",
374 Self::StddevPop => "STDDEV_POP",
375 Self::StddevSamp => "STDDEV_SAMP",
376 Self::VarPop => "VAR_POP",
377 Self::VarSamp => "VAR_SAMP",
378 Self::ArrayAgg => "ARRAY_AGG",
379 Self::StringAgg => "STRING_AGG",
380 Self::GroupConcat => "GROUP_CONCAT",
381 Self::Listagg => "LISTAGG",
382 Self::Median => "MEDIAN",
383 Self::Mode => "MODE",
384 Self::PercentileCont => "PERCENTILE_CONT",
385 Self::PercentileDisc => "PERCENTILE_DISC",
386 Self::ApproxCountDistinct => "APPROX_COUNT_DISTINCT",
387 Self::AnyValue => "ANY_VALUE",
388 Self::First => "FIRST",
389 Self::Last => "LAST",
390 Self::BoolAnd => "BOOL_AND",
391 Self::BoolOr => "BOOL_OR",
392 Self::BitAnd => "BIT_AND",
393 Self::BitOr => "BIT_OR",
394 Self::BitXor => "BIT_XOR",
395 Self::Corr => "CORR",
396 Self::CovarPop => "COVAR_POP",
397 Self::CovarSamp => "COVAR_SAMP",
398 Self::RegrSlope => "REGR_SLOPE",
399 Self::Every => "EVERY",
400 Self::RowNumber => "ROW_NUMBER",
401 Self::Rank => "RANK",
402 Self::DenseRank => "DENSE_RANK",
403 Self::Ntile => "NTILE",
404 Self::CumeDist => "CUME_DIST",
405 Self::PercentRank => "PERCENT_RANK",
406 Self::Lag => "LAG",
407 Self::Lead => "LEAD",
408 Self::FirstValue => "FIRST_VALUE",
409 Self::LastValue => "LAST_VALUE",
410 Self::NthValue => "NTH_VALUE",
411 Self::Coalesce => "COALESCE",
412 Self::Nullif => "NULLIF",
413 Self::Ifnull => "IFNULL",
414 Self::Now => "NOW",
415 Self::CurrentTimestamp => "CURRENT_TIMESTAMP",
416 Self::CurrentDate => "CURRENT_DATE",
417 Self::Date => "DATE",
418 Self::DateTrunc => "DATE_TRUNC",
419 Self::Extract => "EXTRACT",
420 Self::DatePart => "DATE_PART",
421 Self::Year => "YEAR",
422 Self::Month => "MONTH",
423 Self::Day => "DAY",
424 Self::DayOfWeek => "DAYOFWEEK",
425 Self::Quarter => "QUARTER",
426 Self::MakeDate => "MAKE_DATE",
427 Self::MakeTime => "MAKE_TIME",
428 Self::MakeTimestamp => "MAKE_TIMESTAMP",
429 Self::MakeTimestamptz => "MAKE_TIMESTAMPTZ",
430 Self::Age => "AGE",
431 Self::Concat => "CONCAT",
432 Self::Upper => "UPPER",
433 Self::Lower => "LOWER",
434 Self::Trim => "TRIM",
435 Self::Ltrim => "LTRIM",
436 Self::Rtrim => "RTRIM",
437 Self::Substring => "SUBSTRING",
438 Self::Substr => "SUBSTR",
439 Self::Length => "LENGTH",
440 Self::CharLength => "CHAR_LENGTH",
441 Self::CharacterLength => "CHARACTER_LENGTH",
442 Self::ToChar => "TO_CHAR",
443 Self::Replace => "REPLACE",
444 Self::Translate => "TRANSLATE",
445 Self::Reverse => "REVERSE",
446 Self::Repeat => "REPEAT",
447 Self::Lpad => "LPAD",
448 Self::Rpad => "RPAD",
449 Self::Initcap => "INITCAP",
450 Self::QuoteIdent => "QUOTE_IDENT",
451 Self::QuoteLiteral => "QUOTE_LITERAL",
452 Self::Left => "LEFT",
453 Self::Right => "RIGHT",
454 Self::Position => "POSITION",
455 Self::Strpos => "STRPOS",
456 Self::SplitPart => "SPLIT_PART",
457 Self::Abs => "ABS",
458 Self::Sign => "SIGN",
459 Self::Round => "ROUND",
460 Self::Trunc => "TRUNC",
461 Self::Truncate => "TRUNCATE",
462 Self::Ceil => "CEIL",
463 Self::Ceiling => "CEILING",
464 Self::Floor => "FLOOR",
465 Self::Power => "POWER",
466 Self::Pow => "POW",
467 Self::Sqrt => "SQRT",
468 Self::Exp => "EXP",
469 Self::Ln => "LN",
470 Self::Log => "LOG",
471 Self::Log10 => "LOG10",
472 Self::Log2 => "LOG2",
473 Self::Mod => "MOD",
474 Self::Sin => "SIN",
475 Self::Cos => "COS",
476 Self::Tan => "TAN",
477 Self::Asin => "ASIN",
478 Self::Acos => "ACOS",
479 Self::Atan => "ATAN",
480 Self::Atan2 => "ATAN2",
481 Self::Sinh => "SINH",
482 Self::Cosh => "COSH",
483 Self::Tanh => "TANH",
484 Self::Pi => "PI",
485 Self::Random => "RANDOM",
486 Self::Greatest => "GREATEST",
487 Self::Least => "LEAST",
488 Self::JsonObject => "JSON_OBJECT",
489 Self::JsonArray => "JSON_ARRAY",
490 Self::ToJson => "TO_JSON",
491 Self::JsonExtract => "JSON_EXTRACT",
492 Self::JsonExtractText => "JSON_EXTRACT_TEXT",
493 Self::JsonArrayLength => "JSON_ARRAY_LENGTH",
494 Self::JsonObjectKeys => "JSON_OBJECT_KEYS",
495 Self::JsonContains => "JSON_CONTAINS",
496 }
497 }
498
499 pub fn category(&self) -> FunctionCategory {
501 match self {
502 Self::Count
503 | Self::Sum
504 | Self::Avg
505 | Self::Min
506 | Self::Max
507 | Self::Stddev
508 | Self::Variance
509 | Self::StddevPop
510 | Self::StddevSamp
511 | Self::VarPop
512 | Self::VarSamp
513 | Self::ArrayAgg
514 | Self::StringAgg
515 | Self::GroupConcat
516 | Self::Listagg
517 | Self::Median
518 | Self::Mode
519 | Self::PercentileCont
520 | Self::PercentileDisc
521 | Self::ApproxCountDistinct
522 | Self::AnyValue
523 | Self::First
524 | Self::Last
525 | Self::BoolAnd
526 | Self::BoolOr
527 | Self::BitAnd
528 | Self::BitOr
529 | Self::BitXor
530 | Self::Corr
531 | Self::CovarPop
532 | Self::CovarSamp
533 | Self::RegrSlope
534 | Self::Every => FunctionCategory::Aggregate,
535
536 Self::RowNumber | Self::Rank | Self::DenseRank | Self::Ntile => {
537 FunctionCategory::WindowRanking
538 }
539
540 Self::CumeDist | Self::PercentRank => FunctionCategory::WindowDistribution,
541
542 Self::Lag | Self::Lead | Self::FirstValue | Self::LastValue | Self::NthValue => {
543 FunctionCategory::WindowNavigation
544 }
545
546 Self::Coalesce | Self::Nullif | Self::Ifnull => FunctionCategory::NullHandling,
547
548 Self::Now
549 | Self::CurrentTimestamp
550 | Self::CurrentDate
551 | Self::Date
552 | Self::DateTrunc
553 | Self::Extract
554 | Self::DatePart
555 | Self::Year
556 | Self::Month
557 | Self::Day
558 | Self::DayOfWeek
559 | Self::Quarter
560 | Self::MakeDate
561 | Self::MakeTime
562 | Self::MakeTimestamp
563 | Self::MakeTimestamptz
564 | Self::Age => FunctionCategory::DateTime,
565
566 Self::Concat
567 | Self::Upper
568 | Self::Lower
569 | Self::Trim
570 | Self::Ltrim
571 | Self::Rtrim
572 | Self::Substring
573 | Self::Substr
574 | Self::Length
575 | Self::CharLength
576 | Self::CharacterLength
577 | Self::ToChar
578 | Self::Replace
579 | Self::Translate
580 | Self::Reverse
581 | Self::Repeat
582 | Self::Lpad
583 | Self::Rpad
584 | Self::Initcap
585 | Self::QuoteIdent
586 | Self::QuoteLiteral
587 | Self::Left
588 | Self::Right
589 | Self::Position
590 | Self::Strpos
591 | Self::SplitPart => FunctionCategory::String,
592
593 Self::Abs
594 | Self::Sign
595 | Self::Round
596 | Self::Trunc
597 | Self::Truncate
598 | Self::Ceil
599 | Self::Ceiling
600 | Self::Floor
601 | Self::Power
602 | Self::Pow
603 | Self::Sqrt
604 | Self::Exp
605 | Self::Ln
606 | Self::Log
607 | Self::Log10
608 | Self::Log2
609 | Self::Mod => FunctionCategory::Math,
610
611 Self::Sin
612 | Self::Cos
613 | Self::Tan
614 | Self::Asin
615 | Self::Acos
616 | Self::Atan
617 | Self::Atan2
618 | Self::Sinh
619 | Self::Cosh
620 | Self::Tanh => FunctionCategory::Trigonometric,
621
622 Self::Pi | Self::Random => FunctionCategory::Constant,
623
624 Self::Greatest | Self::Least => FunctionCategory::Comparison,
625
626 Self::JsonObject
627 | Self::JsonArray
628 | Self::ToJson
629 | Self::JsonExtract
630 | Self::JsonExtractText
631 | Self::JsonArrayLength
632 | Self::JsonObjectKeys
633 | Self::JsonContains => FunctionCategory::Json,
634 }
635 }
636
637 pub fn is_aggregate(&self) -> bool {
639 self.category() == FunctionCategory::Aggregate
640 }
641
642 pub fn is_window(&self) -> bool {
644 matches!(
645 self.category(),
646 FunctionCategory::WindowRanking
647 | FunctionCategory::WindowDistribution
648 | FunctionCategory::WindowNavigation
649 )
650 }
651
652 pub fn all() -> impl Iterator<Item = SqlFunction> {
654 ALL_FUNCTIONS.iter().copied()
655 }
656}
657
658impl std::fmt::Display for SqlFunction {
659 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
660 f.write_str(self.name())
661 }
662}
663
664#[cfg(test)]
665mod tests {
666 use super::*;
667
668 #[test]
669 fn round_trip_all_variants() {
670 for f in SqlFunction::all() {
671 assert_eq!(
672 SqlFunction::from_name(f.name()),
673 Some(f),
674 "round-trip failed for {:?} (name={})",
675 f,
676 f.name()
677 );
678 }
679 }
680
681 #[test]
682 fn from_name_case_insensitive() {
683 assert_eq!(SqlFunction::from_name("count"), Some(SqlFunction::Count));
684 assert_eq!(SqlFunction::from_name("Count"), Some(SqlFunction::Count));
685 assert_eq!(SqlFunction::from_name("COUNT"), Some(SqlFunction::Count));
686 }
687
688 #[test]
689 fn from_name_unknown_returns_none() {
690 assert_eq!(SqlFunction::from_name("not_a_function"), None);
691 }
692
693 #[test]
694 fn aggregate_classification() {
695 assert!(SqlFunction::Count.is_aggregate());
696 assert!(SqlFunction::Sum.is_aggregate());
697 assert!(SqlFunction::Stddev.is_aggregate());
698 assert!(!SqlFunction::RowNumber.is_aggregate());
699 assert!(!SqlFunction::Upper.is_aggregate());
700 }
701
702 #[test]
703 fn window_classification() {
704 assert!(SqlFunction::RowNumber.is_window());
705 assert!(SqlFunction::Lag.is_window());
706 assert!(SqlFunction::CumeDist.is_window());
707 assert!(!SqlFunction::Count.is_window());
708 }
709
710 #[test]
711 fn all_variants_have_category() {
712 for f in SqlFunction::all() {
715 let _ = f.category();
716 }
717 }
718
719 #[test]
720 fn json_dialect_aliases() {
721 assert_eq!(
723 SqlFunction::from_name("json_build_object"),
724 Some(SqlFunction::JsonObject)
725 );
726 assert_eq!(
727 SqlFunction::from_name("json_build_array"),
728 Some(SqlFunction::JsonArray)
729 );
730 assert_eq!(
731 SqlFunction::from_name("to_jsonb"),
732 Some(SqlFunction::ToJson)
733 );
734 assert_eq!(
735 SqlFunction::from_name("row_to_json"),
736 Some(SqlFunction::ToJson)
737 );
738 assert_eq!(
739 SqlFunction::from_name("json_extract_path_text"),
740 Some(SqlFunction::JsonExtractText)
741 );
742
743 assert_eq!(
745 SqlFunction::from_name("json_extract_string"),
746 Some(SqlFunction::JsonExtractText)
747 );
748 assert_eq!(
749 SqlFunction::from_name("json_keys"),
750 Some(SqlFunction::JsonObjectKeys)
751 );
752
753 assert_eq!(
755 SqlFunction::from_name("get_json_object"),
756 Some(SqlFunction::JsonExtractText)
757 );
758
759 assert_eq!(
761 SqlFunction::from_name("json_object"),
762 Some(SqlFunction::JsonObject)
763 );
764 assert_eq!(
765 SqlFunction::from_name("json_extract"),
766 Some(SqlFunction::JsonExtract)
767 );
768 assert_eq!(
769 SqlFunction::from_name("json_contains"),
770 Some(SqlFunction::JsonContains)
771 );
772 }
773}