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, PartialEq, Eq, Hash)]
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::config::ConfigOptions;
264 use datafusion_common::DFSchema;
265 use datafusion_expr::execution_props::ExecutionProps;
266 use datafusion_expr::simplify::SimplifyContext;
267
268 #[test]
269 #[should_panic]
270 fn test_log_invalid_base_type() {
271 let arg_fields = vec![
272 Field::new("a", DataType::Float64, false).into(),
273 Field::new("a", DataType::Int64, false).into(),
274 ];
275 let args = ScalarFunctionArgs {
276 args: vec![
277 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
278 10.0, 100.0, 1000.0, 10000.0,
279 ]))), ColumnarValue::Array(Arc::new(Int64Array::from(vec![5, 10, 15, 20]))),
281 ],
282 arg_fields,
283 number_rows: 4,
284 return_field: Field::new("f", DataType::Float64, true).into(),
285 config_options: Arc::new(ConfigOptions::default()),
286 };
287 let _ = LogFunc::new().invoke_with_args(args);
288 }
289
290 #[test]
291 fn test_log_invalid_value() {
292 let arg_field = Field::new("a", DataType::Int64, false).into();
293 let args = ScalarFunctionArgs {
294 args: vec![
295 ColumnarValue::Array(Arc::new(Int64Array::from(vec![10]))), ],
297 arg_fields: vec![arg_field],
298 number_rows: 1,
299 return_field: Field::new("f", DataType::Float64, true).into(),
300 config_options: Arc::new(ConfigOptions::default()),
301 };
302
303 let result = LogFunc::new().invoke_with_args(args);
304 result.expect_err("expected error");
305 }
306
307 #[test]
308 fn test_log_scalar_f32_unary() {
309 let arg_field = Field::new("a", DataType::Float32, false).into();
310 let args = ScalarFunctionArgs {
311 args: vec![
312 ColumnarValue::Scalar(ScalarValue::Float32(Some(10.0))), ],
314 arg_fields: vec![arg_field],
315 number_rows: 1,
316 return_field: Field::new("f", DataType::Float32, true).into(),
317 config_options: Arc::new(ConfigOptions::default()),
318 };
319 let result = LogFunc::new()
320 .invoke_with_args(args)
321 .expect("failed to initialize function log");
322
323 match result {
324 ColumnarValue::Array(arr) => {
325 let floats = as_float32_array(&arr)
326 .expect("failed to convert result to a Float32Array");
327
328 assert_eq!(floats.len(), 1);
329 assert!((floats.value(0) - 1.0).abs() < 1e-10);
330 }
331 ColumnarValue::Scalar(_) => {
332 panic!("Expected an array value")
333 }
334 }
335 }
336
337 #[test]
338 fn test_log_scalar_f64_unary() {
339 let arg_field = Field::new("a", DataType::Float64, false).into();
340 let args = ScalarFunctionArgs {
341 args: vec![
342 ColumnarValue::Scalar(ScalarValue::Float64(Some(10.0))), ],
344 arg_fields: vec![arg_field],
345 number_rows: 1,
346 return_field: Field::new("f", DataType::Float64, true).into(),
347 config_options: Arc::new(ConfigOptions::default()),
348 };
349 let result = LogFunc::new()
350 .invoke_with_args(args)
351 .expect("failed to initialize function log");
352
353 match result {
354 ColumnarValue::Array(arr) => {
355 let floats = as_float64_array(&arr)
356 .expect("failed to convert result to a Float64Array");
357
358 assert_eq!(floats.len(), 1);
359 assert!((floats.value(0) - 1.0).abs() < 1e-10);
360 }
361 ColumnarValue::Scalar(_) => {
362 panic!("Expected an array value")
363 }
364 }
365 }
366
367 #[test]
368 fn test_log_scalar_f32() {
369 let arg_fields = vec![
370 Field::new("a", DataType::Float32, false).into(),
371 Field::new("a", DataType::Float32, false).into(),
372 ];
373 let args = ScalarFunctionArgs {
374 args: vec![
375 ColumnarValue::Scalar(ScalarValue::Float32(Some(2.0))), ColumnarValue::Scalar(ScalarValue::Float32(Some(32.0))), ],
378 arg_fields,
379 number_rows: 1,
380 return_field: Field::new("f", DataType::Float32, true).into(),
381 config_options: Arc::new(ConfigOptions::default()),
382 };
383 let result = LogFunc::new()
384 .invoke_with_args(args)
385 .expect("failed to initialize function log");
386
387 match result {
388 ColumnarValue::Array(arr) => {
389 let floats = as_float32_array(&arr)
390 .expect("failed to convert result to a Float32Array");
391
392 assert_eq!(floats.len(), 1);
393 assert!((floats.value(0) - 5.0).abs() < 1e-10);
394 }
395 ColumnarValue::Scalar(_) => {
396 panic!("Expected an array value")
397 }
398 }
399 }
400
401 #[test]
402 fn test_log_scalar_f64() {
403 let arg_fields = vec![
404 Field::new("a", DataType::Float64, false).into(),
405 Field::new("a", DataType::Float64, false).into(),
406 ];
407 let args = ScalarFunctionArgs {
408 args: vec![
409 ColumnarValue::Scalar(ScalarValue::Float64(Some(2.0))), ColumnarValue::Scalar(ScalarValue::Float64(Some(64.0))), ],
412 arg_fields,
413 number_rows: 1,
414 return_field: Field::new("f", DataType::Float64, true).into(),
415 config_options: Arc::new(ConfigOptions::default()),
416 };
417 let result = LogFunc::new()
418 .invoke_with_args(args)
419 .expect("failed to initialize function log");
420
421 match result {
422 ColumnarValue::Array(arr) => {
423 let floats = as_float64_array(&arr)
424 .expect("failed to convert result to a Float64Array");
425
426 assert_eq!(floats.len(), 1);
427 assert!((floats.value(0) - 6.0).abs() < 1e-10);
428 }
429 ColumnarValue::Scalar(_) => {
430 panic!("Expected an array value")
431 }
432 }
433 }
434
435 #[test]
436 fn test_log_f64_unary() {
437 let arg_field = Field::new("a", DataType::Float64, false).into();
438 let args = ScalarFunctionArgs {
439 args: vec![
440 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
441 10.0, 100.0, 1000.0, 10000.0,
442 ]))), ],
444 arg_fields: vec![arg_field],
445 number_rows: 4,
446 return_field: Field::new("f", DataType::Float64, true).into(),
447 config_options: Arc::new(ConfigOptions::default()),
448 };
449 let result = LogFunc::new()
450 .invoke_with_args(args)
451 .expect("failed to initialize function log");
452
453 match result {
454 ColumnarValue::Array(arr) => {
455 let floats = as_float64_array(&arr)
456 .expect("failed to convert result to a Float64Array");
457
458 assert_eq!(floats.len(), 4);
459 assert!((floats.value(0) - 1.0).abs() < 1e-10);
460 assert!((floats.value(1) - 2.0).abs() < 1e-10);
461 assert!((floats.value(2) - 3.0).abs() < 1e-10);
462 assert!((floats.value(3) - 4.0).abs() < 1e-10);
463 }
464 ColumnarValue::Scalar(_) => {
465 panic!("Expected an array value")
466 }
467 }
468 }
469
470 #[test]
471 fn test_log_f32_unary() {
472 let arg_field = Field::new("a", DataType::Float32, false).into();
473 let args = ScalarFunctionArgs {
474 args: vec![
475 ColumnarValue::Array(Arc::new(Float32Array::from(vec![
476 10.0, 100.0, 1000.0, 10000.0,
477 ]))), ],
479 arg_fields: vec![arg_field],
480 number_rows: 4,
481 return_field: Field::new("f", DataType::Float32, true).into(),
482 config_options: Arc::new(ConfigOptions::default()),
483 };
484 let result = LogFunc::new()
485 .invoke_with_args(args)
486 .expect("failed to initialize function log");
487
488 match result {
489 ColumnarValue::Array(arr) => {
490 let floats = as_float32_array(&arr)
491 .expect("failed to convert result to a Float64Array");
492
493 assert_eq!(floats.len(), 4);
494 assert!((floats.value(0) - 1.0).abs() < 1e-10);
495 assert!((floats.value(1) - 2.0).abs() < 1e-10);
496 assert!((floats.value(2) - 3.0).abs() < 1e-10);
497 assert!((floats.value(3) - 4.0).abs() < 1e-10);
498 }
499 ColumnarValue::Scalar(_) => {
500 panic!("Expected an array value")
501 }
502 }
503 }
504
505 #[test]
506 fn test_log_f64() {
507 let arg_fields = vec![
508 Field::new("a", DataType::Float64, false).into(),
509 Field::new("a", DataType::Float64, false).into(),
510 ];
511 let args = ScalarFunctionArgs {
512 args: vec![
513 ColumnarValue::Array(Arc::new(Float64Array::from(vec![
514 2.0, 2.0, 3.0, 5.0,
515 ]))), ColumnarValue::Array(Arc::new(Float64Array::from(vec![
517 8.0, 4.0, 81.0, 625.0,
518 ]))), ],
520 arg_fields,
521 number_rows: 4,
522 return_field: Field::new("f", DataType::Float64, true).into(),
523 config_options: Arc::new(ConfigOptions::default()),
524 };
525 let result = LogFunc::new()
526 .invoke_with_args(args)
527 .expect("failed to initialize function log");
528
529 match result {
530 ColumnarValue::Array(arr) => {
531 let floats = as_float64_array(&arr)
532 .expect("failed to convert result to a Float64Array");
533
534 assert_eq!(floats.len(), 4);
535 assert!((floats.value(0) - 3.0).abs() < 1e-10);
536 assert!((floats.value(1) - 2.0).abs() < 1e-10);
537 assert!((floats.value(2) - 4.0).abs() < 1e-10);
538 assert!((floats.value(3) - 4.0).abs() < 1e-10);
539 }
540 ColumnarValue::Scalar(_) => {
541 panic!("Expected an array value")
542 }
543 }
544 }
545
546 #[test]
547 fn test_log_f32() {
548 let arg_fields = vec![
549 Field::new("a", DataType::Float32, false).into(),
550 Field::new("a", DataType::Float32, false).into(),
551 ];
552 let args = ScalarFunctionArgs {
553 args: vec![
554 ColumnarValue::Array(Arc::new(Float32Array::from(vec![
555 2.0, 2.0, 3.0, 5.0,
556 ]))), ColumnarValue::Array(Arc::new(Float32Array::from(vec![
558 8.0, 4.0, 81.0, 625.0,
559 ]))), ],
561 arg_fields,
562 number_rows: 4,
563 return_field: Field::new("f", DataType::Float32, true).into(),
564 config_options: Arc::new(ConfigOptions::default()),
565 };
566 let result = LogFunc::new()
567 .invoke_with_args(args)
568 .expect("failed to initialize function log");
569
570 match result {
571 ColumnarValue::Array(arr) => {
572 let floats = as_float32_array(&arr)
573 .expect("failed to convert result to a Float32Array");
574
575 assert_eq!(floats.len(), 4);
576 assert!((floats.value(0) - 3.0).abs() < f32::EPSILON);
577 assert!((floats.value(1) - 2.0).abs() < f32::EPSILON);
578 assert!((floats.value(2) - 4.0).abs() < f32::EPSILON);
579 assert!((floats.value(3) - 4.0).abs() < f32::EPSILON);
580 }
581 ColumnarValue::Scalar(_) => {
582 panic!("Expected an array value")
583 }
584 }
585 }
586 #[test]
587 fn test_log_simplify_errors() {
589 let props = ExecutionProps::new();
590 let schema =
591 Arc::new(DFSchema::new_with_metadata(vec![], HashMap::new()).unwrap());
592 let context = SimplifyContext::new(&props).with_schema(schema);
593 let _ = LogFunc::new().simplify(vec![], &context).unwrap_err();
595 let _ = LogFunc::new()
597 .simplify(vec![lit(1), lit(2), lit(3)], &context)
598 .unwrap_err();
599 }
600
601 #[test]
602 fn test_log_simplify_original() {
604 let props = ExecutionProps::new();
605 let schema =
606 Arc::new(DFSchema::new_with_metadata(vec![], HashMap::new()).unwrap());
607 let context = SimplifyContext::new(&props).with_schema(schema);
608 let result = LogFunc::new().simplify(vec![lit(2)], &context).unwrap();
610 let ExprSimplifyResult::Original(args) = result else {
611 panic!("Expected ExprSimplifyResult::Original")
612 };
613 assert_eq!(args.len(), 1);
614 assert_eq!(args[0], lit(2));
615 let result = LogFunc::new()
617 .simplify(vec![lit(2), lit(3)], &context)
618 .unwrap();
619 let ExprSimplifyResult::Original(args) = result else {
620 panic!("Expected ExprSimplifyResult::Original")
621 };
622 assert_eq!(args.len(), 2);
623 assert_eq!(args[0], lit(2));
624 assert_eq!(args[1], lit(3));
625 }
626
627 #[test]
628 fn test_log_output_ordering() {
629 let orders = vec![
631 ExprProperties::new_unknown(),
632 ExprProperties::new_unknown().with_order(SortProperties::Ordered(
633 SortOptions {
634 descending: false,
635 nulls_first: true,
636 },
637 )),
638 ExprProperties::new_unknown().with_order(SortProperties::Ordered(
639 SortOptions {
640 descending: true,
641 nulls_first: true,
642 },
643 )),
644 ExprProperties::new_unknown().with_order(SortProperties::Singleton),
645 ];
646
647 let log = LogFunc::new();
648
649 for order in orders.iter().cloned() {
651 let result = log.output_ordering(std::slice::from_ref(&order)).unwrap();
652 assert_eq!(result, order.sort_properties);
653 }
654
655 let mut results = Vec::with_capacity(orders.len() * orders.len());
657 for base_order in orders.iter() {
658 for num_order in orders.iter().cloned() {
659 let result = log
660 .output_ordering(&[base_order.clone(), num_order])
661 .unwrap();
662 results.push(result);
663 }
664 }
665 let expected = vec![
666 SortProperties::Unordered,
668 SortProperties::Unordered,
669 SortProperties::Unordered,
670 SortProperties::Unordered,
671 SortProperties::Unordered,
673 SortProperties::Unordered,
675 SortProperties::Ordered(SortOptions {
677 descending: true,
678 nulls_first: true,
679 }),
680 SortProperties::Ordered(SortOptions {
682 descending: true,
683 nulls_first: true,
684 }),
685 SortProperties::Unordered,
687 SortProperties::Ordered(SortOptions {
689 descending: false,
690 nulls_first: true,
691 }),
692 SortProperties::Unordered,
694 SortProperties::Ordered(SortOptions {
696 descending: false,
697 nulls_first: true,
698 }),
699 SortProperties::Unordered,
701 SortProperties::Ordered(SortOptions {
703 descending: false,
704 nulls_first: true,
705 }),
706 SortProperties::Ordered(SortOptions {
708 descending: true,
709 nulls_first: true,
710 }),
711 SortProperties::Singleton,
713 ];
714 assert_eq!(results, expected);
715
716 let base_order = ExprProperties::new_unknown().with_order(
718 SortProperties::Ordered(SortOptions {
719 descending: true,
720 nulls_first: true,
721 }),
722 );
723 let num_order = ExprProperties::new_unknown().with_order(
724 SortProperties::Ordered(SortOptions {
725 descending: false,
726 nulls_first: false,
727 }),
728 );
729 assert_eq!(
730 log.output_ordering(&[base_order, num_order]).unwrap(),
731 SortProperties::Unordered
732 );
733 }
734}