1515// specific language governing permissions and limitations
1616// under the License.
1717
18+ use crate :: expressions:: CastExpr ;
19+ use arrow_schema:: SchemaRef ;
20+ use datafusion_common:: { JoinSide , JoinType } ;
21+ use indexmap:: IndexSet ;
22+ use itertools:: Itertools ;
1823use std:: collections:: { HashMap , HashSet } ;
1924use std:: hash:: { Hash , Hasher } ;
2025use std:: sync:: Arc ;
@@ -31,12 +36,8 @@ use crate::{
3136 PhysicalSortRequirement ,
3237} ;
3338
34- use arrow_schema:: { SchemaRef , SortOptions } ;
39+ use arrow_schema:: SortOptions ;
3540use datafusion_common:: tree_node:: { Transformed , TreeNode } ;
36- use datafusion_common:: { JoinSide , JoinType } ;
37-
38- use indexmap:: IndexSet ;
39- use itertools:: Itertools ;
4041
4142/// A `EquivalenceProperties` object stores useful information related to a schema.
4243/// Currently, it keeps track of:
@@ -426,6 +427,87 @@ impl EquivalenceProperties {
426427 ( !meet. is_empty ( ) ) . then_some ( meet)
427428 }
428429
430+ /// we substitute the ordering according to input expression type, this is a simplified version
431+ /// In this case, we just substitute when the expression satisfy the following confition
432+ /// I. just have one column and is a CAST expression
433+ /// II. just have one parameter and is a ScalarFUnctionexpression and it is monotonic
434+ /// TODO: we could precompute all the senario that is computable, for example: atan(x + 1000) should also be substituted if
435+ /// x is DESC or ASC
436+ pub fn substitute_ordering_component (
437+ matching_exprs : Arc < Vec < & Arc < dyn PhysicalExpr > > > ,
438+ sort_expr : & [ PhysicalSortExpr ] ,
439+ schema : SchemaRef ,
440+ ) -> Vec < PhysicalSortExpr > {
441+ sort_expr
442+ . iter ( )
443+ . filter ( |sort_expr| {
444+ matching_exprs. iter ( ) . any ( |matched| !matched. eq ( * sort_expr) )
445+ } )
446+ . map ( |sort_expr| {
447+ let referring_exprs: Vec < _ > = matching_exprs
448+ . iter ( )
449+ . filter ( |matched| expr_refers ( matched, & sort_expr. expr ) )
450+ . cloned ( )
451+ . collect ( ) ;
452+ // does not referring to any matching component, we just skip it
453+
454+ if referring_exprs. len ( ) == 1 {
455+ // we check whether this expression is substitutable or not
456+ let r_expr = referring_exprs[ 0 ] . clone ( ) ;
457+ if let Some ( cast_expr) = r_expr. as_any ( ) . downcast_ref :: < CastExpr > ( ) {
458+ // we need to know whether the Cast Expr matches or not
459+ let expr_type =
460+ sort_expr. expr . data_type ( schema. as_ref ( ) ) . unwrap ( ) ;
461+ if cast_expr. expr . eq ( & sort_expr. expr )
462+ && cast_expr. is_bigger_cast ( expr_type)
463+ {
464+ PhysicalSortExpr {
465+ expr : r_expr. clone ( ) ,
466+ options : sort_expr. options ,
467+ }
468+ } else {
469+ sort_expr. clone ( )
470+ }
471+ } else {
472+ sort_expr. clone ( )
473+ }
474+ } else {
475+ sort_expr. clone ( )
476+ }
477+ } )
478+ . collect ( )
479+ }
480+ /// In projection, supposed we have a input function 'A DESC B DESC' and the output shares the same expression
481+ /// with A and B, we could surely use the ordering of the original ordering, However, if the A has been changed,
482+ /// for example, A-> Cast(A, Int64) or any other form, it is invalid if we continue using the original ordering
483+ /// Since it would cause bug in dependency constructions, we should substitute the input order in order to get correct
484+ /// dependency map, happen in issue 8838: <https://github.com/apache/arrow-datafusion/issues/8838>
485+ pub fn substitute_oeq_class (
486+ & mut self ,
487+ exprs : & [ ( Arc < dyn PhysicalExpr > , String ) ] ,
488+ mapping : & ProjectionMapping ,
489+ schema : SchemaRef ,
490+ ) {
491+ let matching_exprs: Arc < Vec < _ > > = Arc :: new (
492+ exprs
493+ . iter ( )
494+ . filter ( |( expr, _) | mapping. iter ( ) . any ( |( source, _) | source. eq ( expr) ) )
495+ . map ( |( source, _) | source)
496+ . collect ( ) ,
497+ ) ;
498+ let orderings = std:: mem:: take ( & mut self . oeq_class . orderings ) ;
499+ let new_order = orderings
500+ . into_iter ( )
501+ . map ( move |order| {
502+ Self :: substitute_ordering_component (
503+ matching_exprs. clone ( ) ,
504+ & order,
505+ schema. clone ( ) ,
506+ )
507+ } )
508+ . collect ( ) ;
509+ self . oeq_class = OrderingEquivalenceClass :: new ( new_order) ;
510+ }
429511 /// Projects argument `expr` according to `projection_mapping`, taking
430512 /// equivalences into account.
431513 ///
@@ -564,7 +646,6 @@ impl EquivalenceProperties {
564646
565647 // Get dependency map for existing orderings:
566648 let dependency_map = self . construct_dependency_map ( & mapping) ;
567-
568649 let orderings = mapping. iter ( ) . flat_map ( |( source, target) | {
569650 referred_dependencies ( & dependency_map, source)
570651 . into_iter ( )
0 commit comments