1use std::any::Any;
21use std::sync::Arc;
22
23use super::power::PowerFunc;
24
25use arrow::array::{ArrayRef, AsArray};
26use arrow::datatypes::{DataType, Float32Type, Float64Type};
27use datafusion_common::{
28 exec_err, internal_err, plan_datafusion_err, plan_err, Result, ScalarValue,
29};
30use datafusion_expr::expr::ScalarFunction;
31use datafusion_expr::simplify::{ExprSimplifyResult, SimplifyInfo};
32use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
33use datafusion_expr::{
34 lit, ColumnarValue, Documentation, Expr, ScalarFunctionArgs, ScalarUDF,
35 TypeSignature::*,
36};
37use datafusion_expr::{ScalarUDFImpl, Signature, Volatility};
38use datafusion_macros::user_doc;
39
40#[user_doc(
41 doc_section(label = "Math Functions"),
42 description = "Returns the base-x logarithm of a number. Can either provide a specified base, or if omitted then takes the base-10 of a number.",
43 syntax_example = r#"log(base, numeric_expression)
44log(numeric_expression)"#,
45 standard_argument(name = "base", prefix = "Base numeric"),
46 standard_argument(name = "numeric_expression", prefix = "Numeric")
47)]
48#[derive(Debug)]
49pub struct LogFunc {
50 signature: Signature,
51}
52
53impl Default for LogFunc {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl LogFunc {
60 pub fn new() -> Self {
61 use DataType::*;
62 Self {
63 signature: Signature::one_of(
64 vec![
65 Exact(vec![Float32]),
66 Exact(vec![Float64]),
67 Exact(vec![Float32, Float32]),
68 Exact(vec![Float64, Float64]),
69 ],
70 Volatility::Immutable,
71 ),
72 }
73 }
74}
75
76impl ScalarUDFImpl for LogFunc {
77 fn as_any(&self) -> &dyn Any {
78 self
79 }
80 fn name(&self) -> &str {
81 "log"
82 }
83
84 fn signature(&self) -> &Signature {
85 &self.signature
86 }
87
88 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
89 match &arg_types[0] {
90 DataType::Float32 => Ok(DataType::Float32),
91 _ => Ok(DataType::Float64),
92 }
93 }
94
95 fn output_ordering(&self, input: &[ExprProperties]) -> Result<SortProperties> {
96 let (base_sort_properties, num_sort_properties) = if input.len() == 1 {
97 (SortProperties::Singleton, input[0].sort_properties)
99 } else {
100 (input[0].sort_properties, input[1].sort_properties)
101 };
102 match (num_sort_properties, base_sort_properties) {
103 (first @ SortProperties::Ordered(num), SortProperties::Ordered(base))
104 if num.descending != base.descending
105 && num.nulls_first == base.nulls_first =>
106 {
107 Ok(first)
108 }
109 (
110 first @ (SortProperties::Ordered(_) | SortProperties::Singleton),
111 SortProperties::Singleton,
112 ) => Ok(first),
113 (SortProperties::Singleton, second @ SortProperties::Ordered(_)) => {
114 Ok(-second)
115 }
116 _ => Ok(SortProperties::Unordered),
117 }
118 }
119
120 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
122 let args = ColumnarValue::values_to_arrays(&args.args)?;
123
124 let mut base = ColumnarValue::Scalar(ScalarValue::Float32(Some(10.0)));
125
126 let mut x = &args[0];
127 if args.len() == 2 {
128 x = &args[1];
129 base = ColumnarValue::Array(Arc::clone(&args[0]));
130 }
131 let arr: ArrayRef = match args[0].data_type() {
133 DataType::Float64 => match base {
134 ColumnarValue::Scalar(ScalarValue::Float32(Some(base))) => {
135 Arc::new(x.as_primitive::<Float64Type>().unary::<_, Float64Type>(
136 |value: f64| f64::log(value, base as f64),
137 ))
138 }
139 ColumnarValue::Array(base) => {
140 let x = x.as_primitive::<Float64Type>();
141 let base = base.as_primitive::<Float64Type>();
142 let result = arrow::compute::binary::<_, _, _, Float64Type>(
143 x,
144 base,
145 f64::log,
146 )?;
147 Arc::new(result) as _
148 }
149 _ => {
150 return exec_err!("log function requires a scalar or array for base")
151 }
152 },
153
154 DataType::Float32 => match base {
155 ColumnarValue::Scalar(ScalarValue::Float32(Some(base))) => Arc::new(
156 x.as_primitive::<Float32Type>()
157 .unary::<_, Float32Type>(|value: f32| f32::log(value, base)),
158 ),
159 ColumnarValue::Array(base) => {
160 let x = x.as_primitive::<Float32Type>();
161 let base = base.as_primitive::<Float32Type>();
162 let result = arrow::compute::binary::<_, _, _, Float32Type>(
163 x,
164 base,
165 f32::log,
166 )?;
167 Arc::new(result) as _
168 }
169 _ => {
170 return exec_err!("log function requires a scalar or array for base")
171 }
172 },
173 other => {
174 return exec_err!("Unsupported data type {other:?} for function log")
175 }
176 };
177
178 Ok(ColumnarValue::Array(arr))
179 }
180
181 fn documentation(&self) -> Option<&Documentation> {
182 self.doc()
183 }
184
185 fn simplify(
190 &self,
191 mut args: Vec<Expr>,
192 info: &dyn SimplifyInfo,
193 ) -> Result<ExprSimplifyResult> {
194 let num_args = args.len();
198 if num_args > 2 {
199 return plan_err!("Expected log to have 1 or 2 arguments, got {num_args}");
200 }
201 let number = args.pop().ok_or_else(|| {
202 plan_datafusion_err!("Expected log to have 1 or 2 arguments, got 0")
203 })?;
204 let number_datatype = info.get_data_type(&number)?;
205 let base = if let Some(base) = args.pop() {
207 base
208 } else {
209 lit(ScalarValue::new_ten(&number_datatype)?)
210 };
211
212 match number {
213 Expr::Literal(value, _)
214 if value == ScalarValue::new_one(&number_datatype)? =>
215 {
216 Ok(ExprSimplifyResult::Simplified(lit(ScalarValue::new_zero(
217 &info.get_data_type(&base)?,
218 )?)))
219 }
220 Expr::ScalarFunction(ScalarFunction { func, mut args })
221 if is_pow(&func) && args.len() == 2 && base == args[0] =>
222 {
223 let b = args.pop().unwrap(); Ok(ExprSimplifyResult::Simplified(b))
225 }
226 number => {
227 if number == base {
228 Ok(ExprSimplifyResult::Simplified(lit(ScalarValue::new_one(
229 &number_datatype,
230 )?)))
231 } else {
232 let args = match num_args {
233 1 => vec![number],
234 2 => vec![base, number],
235 _ => {
236 return internal_err!(
237 "Unexpected number of arguments in log::simplify"
238 )
239 }
240 };
241 Ok(ExprSimplifyResult::Original(args))
242 }
243 }
244 }
245 }
246}
247
248fn is_pow(func: &ScalarUDF) -> bool {
250 func.inner().as_any().downcast_ref::<PowerFunc>().is_some()
251}
252
253#[cfg(test)]
254mod tests {
255 use std::collections::HashMap;
256
257 use super::*;
258
259 use arrow::array::{Float32Array, Float64Array, Int64Array};
260 use arrow::compute::SortOptions;
261 use arrow::datatypes::Field;
262 use datafusion_common::cast::{as_float32_array, as_float64_array};
263 use datafusion_common::DFSchema;
264 use datafusion_expr::execution_props::ExecutionProps;
265 use datafusion_expr::simplify::SimplifyContext;
266
267 #[test]
268 #[should_panic]
269 fn test_log_invalid_base_type() {
270 let arg_fields = vec![
271 Field::new("a", DataType::Float64, false).into(),
272 Field::new("a", DataType::Int64, false).into(),
273 ];
274 let args = ScalarFunctionArgs {
275 args: vec![
276 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
277 10.0, 100.0, 1000.0, 10000.0,
278 ]))), ColumnarValue::Array(Arc::new(Int64Array::from(vec![5, 10, 15, 20]))),
280 ],
281 arg_fields,
282 number_rows: 4,
283 return_field: Field::new("f", DataType::Float64, true).into(),
284 };
285 let _ = LogFunc::new().invoke_with_args(args);
286 }
287
288 #[test]
289 fn test_log_invalid_value() {
290 let arg_field = Field::new("a", DataType::Int64, false).into();
291 let args = ScalarFunctionArgs {
292 args: vec![
293 ColumnarValue::Array(Arc::new(Int64Array::from(vec![10]))), ],
295 arg_fields: vec![arg_field],
296 number_rows: 1,
297 return_field: Field::new("f", DataType::Float64, true).into(),
298 };
299
300 let result = LogFunc::new().invoke_with_args(args);
301 result.expect_err("expected error");
302 }
303
304 #[test]
305 fn test_log_scalar_f32_unary() {
306 let arg_field = Field::new("a", DataType::Float32, false).into();
307 let args = ScalarFunctionArgs {
308 args: vec![
309 ColumnarValue::Scalar(ScalarValue::Float32(Some(10.0))), ],
311 arg_fields: vec![arg_field],
312 number_rows: 1,
313 return_field: Field::new("f", DataType::Float32, true).into(),
314 };
315 let result = LogFunc::new()
316 .invoke_with_args(args)
317 .expect("failed to initialize function log");
318
319 match result {
320 ColumnarValue::Array(arr) => {
321 let floats = as_float32_array(&arr)
322 .expect("failed to convert result to a Float32Array");
323
324 assert_eq!(floats.len(), 1);
325 assert!((floats.value(0) - 1.0).abs() < 1e-10);
326 }
327 ColumnarValue::Scalar(_) => {
328 panic!("Expected an array value")
329 }
330 }
331 }
332
333 #[test]
334 fn test_log_scalar_f64_unary() {
335 let arg_field = Field::new("a", DataType::Float64, false).into();
336 let args = ScalarFunctionArgs {
337 args: vec![
338 ColumnarValue::Scalar(ScalarValue::Float64(Some(10.0))), ],
340 arg_fields: vec![arg_field],
341 number_rows: 1,
342 return_field: Field::new("f", DataType::Float64, true).into(),
343 };
344 let result = LogFunc::new()
345 .invoke_with_args(args)
346 .expect("failed to initialize function log");
347
348 match result {
349 ColumnarValue::Array(arr) => {
350 let floats = as_float64_array(&arr)
351 .expect("failed to convert result to a Float64Array");
352
353 assert_eq!(floats.len(), 1);
354 assert!((floats.value(0) - 1.0).abs() < 1e-10);
355 }
356 ColumnarValue::Scalar(_) => {
357 panic!("Expected an array value")
358 }
359 }
360 }
361
362 #[test]
363 fn test_log_scalar_f32() {
364 let arg_fields = vec![
365 Field::new("a", DataType::Float32, false).into(),
366 Field::new("a", DataType::Float32, false).into(),
367 ];
368 let args = ScalarFunctionArgs {
369 args: vec![
370 ColumnarValue::Scalar(ScalarValue::Float32(Some(2.0))), ColumnarValue::Scalar(ScalarValue::Float32(Some(32.0))), ],
373 arg_fields,
374 number_rows: 1,
375 return_field: Field::new("f", DataType::Float32, true).into(),
376 };
377 let result = LogFunc::new()
378 .invoke_with_args(args)
379 .expect("failed to initialize function log");
380
381 match result {
382 ColumnarValue::Array(arr) => {
383 let floats = as_float32_array(&arr)
384 .expect("failed to convert result to a Float32Array");
385
386 assert_eq!(floats.len(), 1);
387 assert!((floats.value(0) - 5.0).abs() < 1e-10);
388 }
389 ColumnarValue::Scalar(_) => {
390 panic!("Expected an array value")
391 }
392 }
393 }
394
395 #[test]
396 fn test_log_scalar_f64() {
397 let arg_fields = vec![
398 Field::new("a", DataType::Float64, false).into(),
399 Field::new("a", DataType::Float64, false).into(),
400 ];
401 let args = ScalarFunctionArgs {
402 args: vec![
403 ColumnarValue::Scalar(ScalarValue::Float64(Some(2.0))), ColumnarValue::Scalar(ScalarValue::Float64(Some(64.0))), ],
406 arg_fields,
407 number_rows: 1,
408 return_field: Field::new("f", DataType::Float64, true).into(),
409 };
410 let result = LogFunc::new()
411 .invoke_with_args(args)
412 .expect("failed to initialize function log");
413
414 match result {
415 ColumnarValue::Array(arr) => {
416 let floats = as_float64_array(&arr)
417 .expect("failed to convert result to a Float64Array");
418
419 assert_eq!(floats.len(), 1);
420 assert!((floats.value(0) - 6.0).abs() < 1e-10);
421 }
422 ColumnarValue::Scalar(_) => {
423 panic!("Expected an array value")
424 }
425 }
426 }
427
428 #[test]
429 fn test_log_f64_unary() {
430 let arg_field = Field::new("a", DataType::Float64, false).into();
431 let args = ScalarFunctionArgs {
432 args: vec![
433 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
434 10.0, 100.0, 1000.0, 10000.0,
435 ]))), ],
437 arg_fields: vec![arg_field],
438 number_rows: 4,
439 return_field: Field::new("f", DataType::Float64, true).into(),
440 };
441 let result = LogFunc::new()
442 .invoke_with_args(args)
443 .expect("failed to initialize function log");
444
445 match result {
446 ColumnarValue::Array(arr) => {
447 let floats = as_float64_array(&arr)
448 .expect("failed to convert result to a Float64Array");
449
450 assert_eq!(floats.len(), 4);
451 assert!((floats.value(0) - 1.0).abs() < 1e-10);
452 assert!((floats.value(1) - 2.0).abs() < 1e-10);
453 assert!((floats.value(2) - 3.0).abs() < 1e-10);
454 assert!((floats.value(3) - 4.0).abs() < 1e-10);
455 }
456 ColumnarValue::Scalar(_) => {
457 panic!("Expected an array value")
458 }
459 }
460 }
461
462 #[test]
463 fn test_log_f32_unary() {
464 let arg_field = Field::new("a", DataType::Float32, false).into();
465 let args = ScalarFunctionArgs {
466 args: vec![
467 ColumnarValue::Array(Arc::new(Float32Array::from(vec![
468 10.0, 100.0, 1000.0, 10000.0,
469 ]))), ],
471 arg_fields: vec![arg_field],
472 number_rows: 4,
473 return_field: Field::new("f", DataType::Float32, true).into(),
474 };
475 let result = LogFunc::new()
476 .invoke_with_args(args)
477 .expect("failed to initialize function log");
478
479 match result {
480 ColumnarValue::Array(arr) => {
481 let floats = as_float32_array(&arr)
482 .expect("failed to convert result to a Float64Array");
483
484 assert_eq!(floats.len(), 4);
485 assert!((floats.value(0) - 1.0).abs() < 1e-10);
486 assert!((floats.value(1) - 2.0).abs() < 1e-10);
487 assert!((floats.value(2) - 3.0).abs() < 1e-10);
488 assert!((floats.value(3) - 4.0).abs() < 1e-10);
489 }
490 ColumnarValue::Scalar(_) => {
491 panic!("Expected an array value")
492 }
493 }
494 }
495
496 #[test]
497 fn test_log_f64() {
498 let arg_fields = vec![
499 Field::new("a", DataType::Float64, false).into(),
500 Field::new("a", DataType::Float64, false).into(),
501 ];
502 let args = ScalarFunctionArgs {
503 args: vec![
504 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
505 2.0, 2.0, 3.0, 5.0,
506 ]))), ColumnarValue::Array(Arc::new(Float64Array::from(vec![
508 8.0, 4.0, 81.0, 625.0,
509 ]))), ],
511 arg_fields,
512 number_rows: 4,
513 return_field: Field::new("f", DataType::Float64, true).into(),
514 };
515 let result = LogFunc::new()
516 .invoke_with_args(args)
517 .expect("failed to initialize function log");
518
519 match result {
520 ColumnarValue::Array(arr) => {
521 let floats = as_float64_array(&arr)
522 .expect("failed to convert result to a Float64Array");
523
524 assert_eq!(floats.len(), 4);
525 assert!((floats.value(0) - 3.0).abs() < 1e-10);
526 assert!((floats.value(1) - 2.0).abs() < 1e-10);
527 assert!((floats.value(2) - 4.0).abs() < 1e-10);
528 assert!((floats.value(3) - 4.0).abs() < 1e-10);
529 }
530 ColumnarValue::Scalar(_) => {
531 panic!("Expected an array value")
532 }
533 }
534 }
535
536 #[test]
537 fn test_log_f32() {
538 let arg_fields = vec![
539 Field::new("a", DataType::Float32, false).into(),
540 Field::new("a", DataType::Float32, false).into(),
541 ];
542 let args = ScalarFunctionArgs {
543 args: vec![
544 ColumnarValue::Array(Arc::new(Float32Array::from(vec![
545 2.0, 2.0, 3.0, 5.0,
546 ]))), ColumnarValue::Array(Arc::new(Float32Array::from(vec![
548 8.0, 4.0, 81.0, 625.0,
549 ]))), ],
551 arg_fields,
552 number_rows: 4,
553 return_field: Field::new("f", DataType::Float32, true).into(),
554 };
555 let result = LogFunc::new()
556 .invoke_with_args(args)
557 .expect("failed to initialize function log");
558
559 match result {
560 ColumnarValue::Array(arr) => {
561 let floats = as_float32_array(&arr)
562 .expect("failed to convert result to a Float32Array");
563
564 assert_eq!(floats.len(), 4);
565 assert!((floats.value(0) - 3.0).abs() < f32::EPSILON);
566 assert!((floats.value(1) - 2.0).abs() < f32::EPSILON);
567 assert!((floats.value(2) - 4.0).abs() < f32::EPSILON);
568 assert!((floats.value(3) - 4.0).abs() < f32::EPSILON);
569 }
570 ColumnarValue::Scalar(_) => {
571 panic!("Expected an array value")
572 }
573 }
574 }
575 #[test]
576 fn test_log_simplify_errors() {
578 let props = ExecutionProps::new();
579 let schema =
580 Arc::new(DFSchema::new_with_metadata(vec![], HashMap::new()).unwrap());
581 let context = SimplifyContext::new(&props).with_schema(schema);
582 let _ = LogFunc::new().simplify(vec![], &context).unwrap_err();
584 let _ = LogFunc::new()
586 .simplify(vec![lit(1), lit(2), lit(3)], &context)
587 .unwrap_err();
588 }
589
590 #[test]
591 fn test_log_simplify_original() {
593 let props = ExecutionProps::new();
594 let schema =
595 Arc::new(DFSchema::new_with_metadata(vec![], HashMap::new()).unwrap());
596 let context = SimplifyContext::new(&props).with_schema(schema);
597 let result = LogFunc::new().simplify(vec![lit(2)], &context).unwrap();
599 let ExprSimplifyResult::Original(args) = result else {
600 panic!("Expected ExprSimplifyResult::Original")
601 };
602 assert_eq!(args.len(), 1);
603 assert_eq!(args[0], lit(2));
604 let result = LogFunc::new()
606 .simplify(vec![lit(2), lit(3)], &context)
607 .unwrap();
608 let ExprSimplifyResult::Original(args) = result else {
609 panic!("Expected ExprSimplifyResult::Original")
610 };
611 assert_eq!(args.len(), 2);
612 assert_eq!(args[0], lit(2));
613 assert_eq!(args[1], lit(3));
614 }
615
616 #[test]
617 fn test_log_output_ordering() {
618 let orders = vec![
620 ExprProperties::new_unknown(),
621 ExprProperties::new_unknown().with_order(SortProperties::Ordered(
622 SortOptions {
623 descending: false,
624 nulls_first: true,
625 },
626 )),
627 ExprProperties::new_unknown().with_order(SortProperties::Ordered(
628 SortOptions {
629 descending: true,
630 nulls_first: true,
631 },
632 )),
633 ExprProperties::new_unknown().with_order(SortProperties::Singleton),
634 ];
635
636 let log = LogFunc::new();
637
638 for order in orders.iter().cloned() {
640 let result = log.output_ordering(&[order.clone()]).unwrap();
641 assert_eq!(result, order.sort_properties);
642 }
643
644 let mut results = Vec::with_capacity(orders.len() * orders.len());
646 for base_order in orders.iter() {
647 for num_order in orders.iter().cloned() {
648 let result = log
649 .output_ordering(&[base_order.clone(), num_order])
650 .unwrap();
651 results.push(result);
652 }
653 }
654 let expected = vec![
655 SortProperties::Unordered,
657 SortProperties::Unordered,
658 SortProperties::Unordered,
659 SortProperties::Unordered,
660 SortProperties::Unordered,
662 SortProperties::Unordered,
664 SortProperties::Ordered(SortOptions {
666 descending: true,
667 nulls_first: true,
668 }),
669 SortProperties::Ordered(SortOptions {
671 descending: true,
672 nulls_first: true,
673 }),
674 SortProperties::Unordered,
676 SortProperties::Ordered(SortOptions {
678 descending: false,
679 nulls_first: true,
680 }),
681 SortProperties::Unordered,
683 SortProperties::Ordered(SortOptions {
685 descending: false,
686 nulls_first: true,
687 }),
688 SortProperties::Unordered,
690 SortProperties::Ordered(SortOptions {
692 descending: false,
693 nulls_first: true,
694 }),
695 SortProperties::Ordered(SortOptions {
697 descending: true,
698 nulls_first: true,
699 }),
700 SortProperties::Singleton,
702 ];
703 assert_eq!(results, expected);
704
705 let base_order = ExprProperties::new_unknown().with_order(
707 SortProperties::Ordered(SortOptions {
708 descending: true,
709 nulls_first: true,
710 }),
711 );
712 let num_order = ExprProperties::new_unknown().with_order(
713 SortProperties::Ordered(SortOptions {
714 descending: false,
715 nulls_first: false,
716 }),
717 );
718 assert_eq!(
719 log.output_ordering(&[base_order, num_order]).unwrap(),
720 SortProperties::Unordered
721 );
722 }
723}