datafusion_functions/math/
factorial.rs1use arrow::array::{ArrayRef, AsArray, Int64Array};
19use std::sync::Arc;
20
21use arrow::datatypes::DataType::Int64;
22use arrow::datatypes::{DataType, Int64Type};
23
24use datafusion_common::{
25 Result, ScalarValue, exec_err, internal_err, utils::take_function_args,
26};
27use datafusion_expr::{
28 ColumnarValue, Documentation, ScalarFunctionArgs, ScalarUDFImpl, Signature,
29 Volatility,
30};
31use datafusion_macros::user_doc;
32
33#[user_doc(
34 doc_section(label = "Math Functions"),
35 description = "Factorial of a non-negative integer. Errors if the argument is negative or the result overflows.",
36 syntax_example = "factorial(numeric_expression)",
37 sql_example = r#"```sql
38> SELECT factorial(5);
39+---------------+
40| factorial(5) |
41+---------------+
42| 120 |
43+---------------+
44```"#,
45 standard_argument(name = "numeric_expression", prefix = "Numeric")
46)]
47#[derive(Debug, PartialEq, Eq, Hash)]
48pub struct FactorialFunc {
49 signature: Signature,
50}
51
52impl Default for FactorialFunc {
53 fn default() -> Self {
54 FactorialFunc::new()
55 }
56}
57
58impl FactorialFunc {
59 pub fn new() -> Self {
60 Self {
61 signature: Signature::uniform(1, vec![Int64], Volatility::Immutable),
62 }
63 }
64}
65
66impl ScalarUDFImpl for FactorialFunc {
67 fn name(&self) -> &str {
68 "factorial"
69 }
70
71 fn signature(&self) -> &Signature {
72 &self.signature
73 }
74
75 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
76 Ok(Int64)
77 }
78
79 fn is_strict(&self) -> bool {
80 true
81 }
82
83 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
84 let [arg] = take_function_args(self.name(), args.args)?;
85
86 match arg {
87 ColumnarValue::Scalar(scalar) => {
88 if scalar.is_null() {
89 return Ok(ColumnarValue::Scalar(ScalarValue::Int64(None)));
90 }
91
92 match scalar {
93 ScalarValue::Int64(Some(v)) => {
94 let result = compute_factorial(v)?;
95 Ok(ColumnarValue::Scalar(ScalarValue::Int64(Some(result))))
96 }
97 _ => {
98 internal_err!(
99 "Unexpected data type {:?} for function factorial",
100 scalar.data_type()
101 )
102 }
103 }
104 }
105 ColumnarValue::Array(array) => match array.data_type() {
106 Int64 => {
107 let result: Int64Array = array
108 .as_primitive::<Int64Type>()
109 .try_unary(compute_factorial)?;
110 Ok(ColumnarValue::Array(Arc::new(result) as ArrayRef))
111 }
112 other => {
113 internal_err!("Unexpected data type {other:?} for function factorial")
114 }
115 },
116 }
117 }
118
119 fn documentation(&self) -> Option<&Documentation> {
120 self.doc()
121 }
122}
123
124const FACTORIALS: [i64; 21] = [
125 1,
126 1,
127 2,
128 6,
129 24,
130 120,
131 720,
132 5040,
133 40320,
134 362880,
135 3628800,
136 39916800,
137 479001600,
138 6227020800,
139 87178291200,
140 1307674368000,
141 20922789888000,
142 355687428096000,
143 6402373705728000,
144 121645100408832000,
145 2432902008176640000,
146]; fn compute_factorial(n: i64) -> Result<i64> {
149 if n < 0 {
150 exec_err!("factorial of a negative number is undefined")
151 } else if n < FACTORIALS.len() as i64 {
152 Ok(FACTORIALS[n as usize])
153 } else {
154 exec_err!("Overflow happened on FACTORIAL({n})")
155 }
156}