@@ -62,6 +62,7 @@ public class ComputeSignatureHelper {
6262
6363 private static final String MAP_KEY = "key" ;
6464 private static final String MAP_VALUE = "value" ;
65+ private static final String ARRAY_ITEM = "array" ;
6566
6667 /** implementAbstractReturnType */
6768 public static FunctionSignature implementFollowToArgumentReturnType (
@@ -570,58 +571,54 @@ private static FunctionSignature defaultTimePrecisionPromotion(FunctionSignature
570571 private static FunctionSignature defaultDecimalV3PrecisionPromotion (
571572 FunctionSignature signature , List <Expression > arguments ) {
572573 // The wider type across all decimal slots, used for decimal slots that are not
573- // the key/value of a MAP (keeping the original behavior), for the placeholder
574- // return type, and for MAP leaves whose group has no concrete type information.
574+ // inside a MAP (keeping the original behavior), for the placeholder return type,
575+ // and for MAP-nested leaves whose group has no concrete type information.
575576 DecimalV3Type widerType = null ;
576577
577- // The KEY leaves and the VALUE leaves of a MAP are independent type variables:
578- // they must keep their own precision/scale instead of being merged into one wider
579- // type, otherwise widening one leaf (e.g. the scale of a big integral key) may
580- // overflow the other leaf. They are grouped by the resolved leaf type, so the
581- // corresponding leaves of different (or repeated) MAP arguments aggregate while
582- // the key leaves and the value leaves stay independent.
583- Map <DecimalV3Type , DecimalV3Type > mapKeyWider = Maps .newHashMap ();
584- Map <DecimalV3Type , DecimalV3Type > mapValueWider = Maps .newHashMap ();
585-
586- // Top-level scalar decimal leaves with a concrete resolved type. After
587- // Any/Follow resolution such a slot carries the same concrete type as the MAP
588- // key/value leaf it was resolved from (e.g. the lookup argument of element_at
589- // follows the MAP key type). It must be promoted together with that MAP leaf,
590- // otherwise the BE compares columns of different concrete decimal classes.
578+ // Decimal leaves inside a MAP are independent type variables: they must keep
579+ // their own precision/scale instead of being merged into one wider type,
580+ // otherwise widening one leaf (e.g. the scale of a big integral key) may overflow
581+ // the other leaf. They are grouped by the full structural path through nested
582+ // containers (e.g. "key", "value", "value/array", "value/key") and the resolved
583+ // leaf type, so the leaves of different (or repeated) MAP arguments on the same
584+ // path aggregate while leaves on different paths stay independent.
585+ Map <String , DecimalV3Type > groupWider = Maps .newHashMap ();
586+
587+ // The outermost MAP leaf group of each resolved type, used to link a top-level
588+ // scalar slot (e.g. element_at's lookup) with the MAP leaf it was resolved from:
589+ // after Any/Follow resolution both carry the same concrete type.
590+ Map <DecimalV3Type , String > mapLeafGroupByType = Maps .newHashMap ();
591+
592+ // Top-level scalar decimal leaves with a concrete resolved type, whose promoted
593+ // type must also be folded into the linked MAP leaf group.
591594 List <DecimalLeaf > scalarLeaves = Lists .newArrayList ();
592595
593596 DecimalV3Type [] widerHolder = new DecimalV3Type [1 ];
594597 for (int i = 0 ; i < arguments .size (); i ++) {
595598 DataType targetType = getSignatureArgumentType (signature , i );
596599 collectDecimalLeaf (targetType , arguments .get (i ).getDataType (), arguments .get (i ),
597- null , true , mapKeyWider , mapValueWider , scalarLeaves , widerHolder );
600+ "" , mapLeafGroupByType , groupWider , scalarLeaves , widerHolder );
598601 }
599602 widerType = widerHolder [0 ];
600603 if (widerType == null ) {
601604 return signature ;
602605 }
603606
604607 // Fold the promoted type of every top-level scalar slot into the MAP leaf group
605- // of the same resolved type (if any), so the MAP key/value and the scalar slot
606- // linked with it are promoted to one type.
608+ // of the same resolved type (if any), so the MAP leaf and the scalar slot linked
609+ // with it are promoted to one type.
607610 for (DecimalLeaf scalarLeaf : scalarLeaves ) {
608- DecimalV3Type linkedWider = mapKeyWider .get (scalarLeaf .resolvedType );
609- if (linkedWider == null ) {
610- linkedWider = mapValueWider .get (scalarLeaf .resolvedType );
611- if (linkedWider != null ) {
612- mapValueWider .put (scalarLeaf .resolvedType ,
613- mergeDecimalV3Type (linkedWider , scalarLeaf .promotedType ));
614- }
615- } else {
616- mapKeyWider .put (scalarLeaf .resolvedType ,
617- mergeDecimalV3Type (linkedWider , scalarLeaf .promotedType ));
611+ String linkedGroup = mapLeafGroupByType .get (scalarLeaf .resolvedType );
612+ if (linkedGroup != null ) {
613+ groupWider .merge (linkedGroup , scalarLeaf .promotedType ,
614+ ComputeSignatureHelper ::mergeDecimalV3Type );
618615 }
619616 }
620617
621618 List <DataType > newArgTypes = Lists .newArrayListWithCapacity (signature .argumentsTypes .size ());
622619 for (int i = 0 ; i < signature .argumentsTypes .size (); i ++) {
623- newArgTypes .add (replaceDecimalV3Leaf (signature .argumentsTypes .get (i ), null , true ,
624- mapKeyWider , mapValueWider , widerType ));
620+ newArgTypes .add (replaceDecimalV3Leaf (signature .argumentsTypes .get (i ), "" ,
621+ mapLeafGroupByType , groupWider , widerType ));
625622 }
626623 signature = signature .withArgumentTypes (signature .hasVarArgs , newArgTypes );
627624 if (signature .returnType instanceof DecimalV3Type
@@ -653,16 +650,16 @@ private static DecimalV3Type promotedDecimalV3Type(Expression arg, DataType argT
653650
654651 /**
655652 * Collect every decimal leaf of one argument and fold its promoted type into the
656- * corresponding group. {@code mapSide } is {@link #MAP_KEY} or {@link #MAP_VALUE}
657- * when the leaf is directly the key/value of a MAP (possibly nested in a MAP),
658- * otherwise {@code null}. {@code linkable} indicates the leaf is a top-level scalar
659- * slot (not nested in any MAP or ARRAY), which may be linked to a MAP key/value leaf
660- * of the same resolved type. {@code widerHolder} accumulates the wider type across
661- * all decimal leaves.
653+ * corresponding group. {@code path } is the full structural path through nested
654+ * containers (empty for a top-level slot, {@link #MAP_KEY}/{@link #MAP_VALUE} for
655+ * the key/value of a MAP, {@link #ARRAY_ITEM} for an ARRAY item), so an ARRAY nested
656+ * in a MAP value (e.g. "value/array") or the key/value of a nested MAP (e.g.
657+ * "value/key") keep the enclosing group instead of being merged with the outer
658+ * leaves. {@code widerHolder} accumulates the wider type across all decimal leaves.
662659 */
663660 private static void collectDecimalLeaf (DataType sigType , DataType argType , Expression arg ,
664- String mapSide , boolean linkable , Map <DecimalV3Type , DecimalV3Type > mapKeyWider ,
665- Map <DecimalV3Type , DecimalV3Type > mapValueWider , List <DecimalLeaf > scalarLeaves ,
661+ String path , Map <DecimalV3Type , String > mapLeafGroupByType ,
662+ Map <String , DecimalV3Type > groupWider , List <DecimalLeaf > scalarLeaves ,
666663 DecimalV3Type [] widerHolder ) {
667664 if (sigType instanceof DecimalV3Type ) {
668665 DecimalV3Type sigDecimal = (DecimalV3Type ) sigType ;
@@ -671,33 +668,38 @@ private static void collectDecimalLeaf(DataType sigType, DataType argType, Expre
671668 promoted = promotedDecimalV3Type (arg , argType );
672669 widerHolder [0 ] = mergeDecimalV3Type (widerHolder [0 ], promoted );
673670 }
674- if (mapSide == null ) {
675- if ( linkable && promoted != null && sigDecimal . getPrecision () > 0 ) {
676- // top-level scalar slot with a concrete resolved type may be linked
677- // with a MAP key/value leaf of the same type below
671+ if (path . isEmpty () ) {
672+ // top-level scalar slot: keep the original behavior of the single wider
673+ // type, but a concrete resolved type may be linked with a MAP leaf below
674+ if ( promoted != null && sigDecimal . getPrecision () > 0 ) {
678675 scalarLeaves .add (new DecimalLeaf (sigDecimal , promoted ));
679676 }
680- } else if (promoted != null ) {
681- if (mapSide == MAP_KEY ) {
682- mapKeyWider .merge (sigDecimal , promoted , ComputeSignatureHelper ::mergeDecimalV3Type );
683- } else {
684- mapValueWider .merge (sigDecimal , promoted , ComputeSignatureHelper ::mergeDecimalV3Type );
685- }
686- }
677+ } else if (isMapNested (path ) && promoted != null ) {
678+ String groupKey = path + ":" + sigDecimal ;
679+ groupWider .merge (groupKey , promoted , ComputeSignatureHelper ::mergeDecimalV3Type );
680+ // keep the outermost group (shortest path, key before value) for linking
681+ mapLeafGroupByType .putIfAbsent (sigDecimal , groupKey );
682+ }
683+ // other leaves (e.g. ARRAY items not nested in a MAP) keep the original
684+ // behavior of the single wider type
687685 return ;
688686 } else if (sigType instanceof MapType ) {
689687 MapType mapType = (MapType ) sigType ;
690688 if (argType instanceof MapType ) {
691689 MapType argMapType = (MapType ) argType ;
692690 collectDecimalLeaf (mapType .getKeyType (), argMapType .getKeyType (), arg ,
693- MAP_KEY , false , mapKeyWider , mapValueWider , scalarLeaves , widerHolder );
691+ appendPath (path , MAP_KEY ), mapLeafGroupByType , groupWider ,
692+ scalarLeaves , widerHolder );
694693 collectDecimalLeaf (mapType .getValueType (), argMapType .getValueType (), arg ,
695- MAP_VALUE , false , mapKeyWider , mapValueWider , scalarLeaves , widerHolder );
694+ appendPath (path , MAP_VALUE ), mapLeafGroupByType , groupWider ,
695+ scalarLeaves , widerHolder );
696696 } else if (argType instanceof NullType ) {
697697 collectDecimalLeaf (mapType .getKeyType (), argType , arg ,
698- MAP_KEY , false , mapKeyWider , mapValueWider , scalarLeaves , widerHolder );
698+ appendPath (path , MAP_KEY ), mapLeafGroupByType , groupWider ,
699+ scalarLeaves , widerHolder );
699700 collectDecimalLeaf (mapType .getValueType (), argType , arg ,
700- MAP_VALUE , false , mapKeyWider , mapValueWider , scalarLeaves , widerHolder );
701+ appendPath (path , MAP_VALUE ), mapLeafGroupByType , groupWider ,
702+ scalarLeaves , widerHolder );
701703 }
702704 return ;
703705 } else if (sigType instanceof ArrayType ) {
@@ -709,53 +711,69 @@ private static void collectDecimalLeaf(DataType sigType, DataType argType, Expre
709711 } else {
710712 return ;
711713 }
712- // ARRAY items are not MAP key/value leaves, keep the original behavior
714+ // carry the enclosing MAP path through the ARRAY so items nested in a MAP
715+ // value stay in the value group
713716 collectDecimalLeaf (((ArrayType ) sigType ).getItemType (), itemArgType , arg ,
714- null , false , mapKeyWider , mapValueWider , scalarLeaves , widerHolder );
717+ appendPath (path , ARRAY_ITEM ), mapLeafGroupByType , groupWider ,
718+ scalarLeaves , widerHolder );
715719 }
716720 // StructType and other types are not supported
717721 }
718722
719723 /**
720- * Replace every decimal leaf in {@code sigType}: MAP key/value leaves use the wider
721- * type of their own group, all other leaves (scalar, ARRAY item, etc.) keep the
722- * original behavior of using the single wider type across all decimal slots.
724+ * Replace every decimal leaf in {@code sigType}: leaves inside a MAP use the wider
725+ * type of their own structural group, all other leaves (scalar, ARRAY item, etc.)
726+ * keep the original behavior of using the single wider type across all decimal
727+ * slots.
723728 */
724- private static DataType replaceDecimalV3Leaf (DataType sigType , String mapSide , boolean topLevel ,
725- Map <DecimalV3Type , DecimalV3Type > mapKeyWider ,
726- Map < DecimalV3Type , DecimalV3Type > mapValueWider , DecimalV3Type widerType ) {
729+ private static DataType replaceDecimalV3Leaf (DataType sigType , String path ,
730+ Map <DecimalV3Type , String > mapLeafGroupByType , Map < String , DecimalV3Type > groupWider ,
731+ DecimalV3Type widerType ) {
727732 if (sigType instanceof DecimalV3Type ) {
728733 DecimalV3Type sigDecimal = (DecimalV3Type ) sigType ;
729- if (mapSide == null ) {
734+ if (path . isEmpty () ) {
730735 // a top-level scalar slot linked with a MAP leaf keeps the type of that
731736 // leaf (e.g. element_at's lookup must match the MAP key type)
732- if (topLevel && sigDecimal .getPrecision () > 0 ) {
733- DecimalV3Type linkedWider = mapKeyWider .get (sigDecimal );
734- if (linkedWider = = null ) {
735- linkedWider = mapValueWider .get (sigDecimal );
736- }
737- if ( linkedWider != null ) {
738- return linkedWider ;
737+ if (sigDecimal .getPrecision () > 0 ) {
738+ String linkedGroup = mapLeafGroupByType .get (sigDecimal );
739+ if (linkedGroup ! = null ) {
740+ DecimalV3Type linkedWider = groupWider .get (linkedGroup );
741+ if ( linkedWider != null ) {
742+ return linkedWider ;
743+ }
739744 }
740745 }
741746 return widerType ;
742747 }
743- DecimalV3Type groupWider = (mapSide == MAP_KEY ? mapKeyWider : mapValueWider ).get (sigDecimal );
744- return groupWider != null ? groupWider : widerType ;
748+ if (isMapNested (path )) {
749+ DecimalV3Type groupType = groupWider .get (path + ":" + sigDecimal );
750+ return groupType != null ? groupType : widerType ;
751+ }
752+ // other leaves (e.g. ARRAY items not nested in a MAP) keep the original
753+ // behavior of the single wider type
754+ return widerType ;
745755 } else if (sigType instanceof ArrayType ) {
746- return ArrayType .of (replaceDecimalV3Leaf (((ArrayType ) sigType ).getItemType (), null , false ,
747- mapKeyWider , mapValueWider , widerType ));
756+ return ArrayType .of (replaceDecimalV3Leaf (((ArrayType ) sigType ).getItemType (),
757+ appendPath ( path , ARRAY_ITEM ), mapLeafGroupByType , groupWider , widerType ));
748758 } else if (sigType instanceof MapType ) {
749759 MapType mapType = (MapType ) sigType ;
750760 return MapType .of (
751- replaceDecimalV3Leaf (mapType .getKeyType (), MAP_KEY , false ,
752- mapKeyWider , mapValueWider , widerType ),
753- replaceDecimalV3Leaf (mapType .getValueType (), MAP_VALUE , false ,
754- mapKeyWider , mapValueWider , widerType ));
761+ replaceDecimalV3Leaf (mapType .getKeyType (), appendPath ( path , MAP_KEY ) ,
762+ mapLeafGroupByType , groupWider , widerType ),
763+ replaceDecimalV3Leaf (mapType .getValueType (), appendPath ( path , MAP_VALUE ) ,
764+ mapLeafGroupByType , groupWider , widerType ));
755765 }
756766 return sigType ;
757767 }
758768
769+ private static String appendPath (String path , String segment ) {
770+ return path .isEmpty () ? segment : path + "/" + segment ;
771+ }
772+
773+ private static boolean isMapNested (String path ) {
774+ return path .contains (MAP_KEY ) || path .contains (MAP_VALUE );
775+ }
776+
759777 private static DecimalV3Type mergeDecimalV3Type (DecimalV3Type left , DecimalV3Type right ) {
760778 if (left == null ) {
761779 return right ;
0 commit comments