datafusion_functions/math/
log.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Math function: `log()`.
19
20use 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            // log(x) defaults to log(10, x)
98            (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    // Support overloaded log(base, x) and log(x) which defaults to log(10, x)
121    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        // note in f64::log params order is different than in sql. e.g in sql log(base, x) == f64::log(x, base)
132        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    /// Simplify the `log` function by the relevant rules:
186    /// 1. Log(a, 1) ===> 0
187    /// 2. Log(a, Power(a, b)) ===> b
188    /// 3. Log(a, a) ===> 1
189    fn simplify(
190        &self,
191        mut args: Vec<Expr>,
192        info: &dyn SimplifyInfo,
193    ) -> Result<ExprSimplifyResult> {
194        // Args are either
195        // log(number)
196        // log(base, number)
197        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        // default to base 10
206        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(); // length checked above
224                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
248/// Returns true if the function is `PowerFunc`
249fn 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                ]))), // num
279                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]))), // num
294            ],
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))), // num
310            ],
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))), // num
339            ],
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))), // num
371                ColumnarValue::Scalar(ScalarValue::Float32(Some(32.0))), // num
372            ],
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))), // num
404                ColumnarValue::Scalar(ScalarValue::Float64(Some(64.0))), // num
405            ],
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                ]))), // num
436            ],
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                ]))), // num
470            ],
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                ]))), // base
507                ColumnarValue::Array(Arc::new(Float64Array::from(vec![
508                    8.0, 4.0, 81.0, 625.0,
509                ]))), // num
510            ],
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                ]))), // base
547                ColumnarValue::Array(Arc::new(Float32Array::from(vec![
548                    8.0, 4.0, 81.0, 625.0,
549                ]))), // num
550            ],
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    // Test log() simplification errors
577    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        // Expect 0 args to error
583        let _ = LogFunc::new().simplify(vec![], &context).unwrap_err();
584        // Expect 3 args to error
585        let _ = LogFunc::new()
586            .simplify(vec![lit(1), lit(2), lit(3)], &context)
587            .unwrap_err();
588    }
589
590    #[test]
591    // Test that non-simplifiable log() expressions are unchanged after simplification
592    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        // One argument with no simplifications
598        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        // Two arguments with no simplifications
605        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        // [Unordered, Ascending, Descending, Literal]
619        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        // Test log(num)
639        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        // Test log(base, num), where `nulls_first` is the same
645        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            // base: Unordered
656            SortProperties::Unordered,
657            SortProperties::Unordered,
658            SortProperties::Unordered,
659            SortProperties::Unordered,
660            // base: Ascending, num: Unordered
661            SortProperties::Unordered,
662            // base: Ascending, num: Ascending
663            SortProperties::Unordered,
664            // base: Ascending, num: Descending
665            SortProperties::Ordered(SortOptions {
666                descending: true,
667                nulls_first: true,
668            }),
669            // base: Ascending, num: Literal
670            SortProperties::Ordered(SortOptions {
671                descending: true,
672                nulls_first: true,
673            }),
674            // base: Descending, num: Unordered
675            SortProperties::Unordered,
676            // base: Descending, num: Ascending
677            SortProperties::Ordered(SortOptions {
678                descending: false,
679                nulls_first: true,
680            }),
681            // base: Descending, num: Descending
682            SortProperties::Unordered,
683            // base: Descending, num: Literal
684            SortProperties::Ordered(SortOptions {
685                descending: false,
686                nulls_first: true,
687            }),
688            // base: Literal, num: Unordered
689            SortProperties::Unordered,
690            // base: Literal, num: Ascending
691            SortProperties::Ordered(SortOptions {
692                descending: false,
693                nulls_first: true,
694            }),
695            // base: Literal, num: Descending
696            SortProperties::Ordered(SortOptions {
697                descending: true,
698                nulls_first: true,
699            }),
700            // base: Literal, num: Literal
701            SortProperties::Singleton,
702        ];
703        assert_eq!(results, expected);
704
705        // Test with different `nulls_first`
706        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}