1818
1919package org .apache .paimon .utils ;
2020
21+ import org .apache .paimon .predicate .AlwaysFalse ;
2122import org .apache .paimon .predicate .Equal ;
2223import org .apache .paimon .predicate .In ;
2324import org .apache .paimon .predicate .LeafBinaryFunction ;
@@ -44,26 +45,33 @@ public class PartitionPredicateHelper {
4445 * Build a partition-typed predicate from a string-based leaf predicate on the "partition"
4546 * column.
4647 *
47- * @return the predicate on partition fields, or {@code null} if the partition spec is invalid
48- * (indicating no results should be returned)
48+ * @return {@code null} if the predicate type is unsupported for pushdown (caller should skip
49+ * pushdown), {@link PredicateBuilder#alwaysFalse()} if no partition can match (caller
50+ * should return empty), or a normal predicate to push down.
4951 */
5052 @ Nullable
5153 public static Predicate buildPartitionPredicate (
52- LeafPredicate partitionPredicate , List <String > partitionKeys , RowType partitionType ) {
54+ LeafPredicate partitionPredicate ,
55+ List <String > partitionKeys ,
56+ RowType partitionType ,
57+ String defaultPartitionName ) {
5358 if (partitionPredicate .function () instanceof Equal ) {
5459 LinkedHashMap <String , String > partSpec =
5560 parsePartitionSpec (
5661 partitionPredicate .literals ().get (0 ).toString (), partitionKeys );
5762 if (partSpec == null ) {
58- return null ;
63+ return PredicateBuilder . alwaysFalse () ;
5964 }
6065 PredicateBuilder partBuilder = new PredicateBuilder (partitionType );
6166 List <Predicate > predicates = new ArrayList <>();
6267 for (int i = 0 ; i < partitionKeys .size (); i ++) {
63- Object value =
64- TypeUtils .castFromString (
65- partSpec .get (partitionKeys .get (i )), partitionType .getTypeAt (i ));
66- predicates .add (partBuilder .equal (i , value ));
68+ String strValue = partSpec .get (partitionKeys .get (i ));
69+ if (defaultPartitionName .equals (strValue )) {
70+ predicates .add (partBuilder .isNull (i ));
71+ } else {
72+ Object value = TypeUtils .castFromString (strValue , partitionType .getTypeAt (i ));
73+ predicates .add (partBuilder .equal (i , value ));
74+ }
6775 }
6876 return PredicateBuilder .and (predicates );
6977 } else if (partitionPredicate .function () instanceof In ) {
@@ -77,34 +85,44 @@ public static Predicate buildPartitionPredicate(
7785 }
7886 List <Predicate > andPredicates = new ArrayList <>();
7987 for (int i = 0 ; i < partitionKeys .size (); i ++) {
80- Object value =
81- TypeUtils .castFromString (
82- partSpec .get (partitionKeys .get (i )), partitionType .getTypeAt (i ));
83- andPredicates .add (partBuilder .equal (i , value ));
88+ String strValue = partSpec .get (partitionKeys .get (i ));
89+ if (defaultPartitionName .equals (strValue )) {
90+ andPredicates .add (partBuilder .isNull (i ));
91+ } else {
92+ Object value =
93+ TypeUtils .castFromString (strValue , partitionType .getTypeAt (i ));
94+ andPredicates .add (partBuilder .equal (i , value ));
95+ }
8496 }
8597 orPredicates .add (PredicateBuilder .and (andPredicates ));
8698 }
87- return orPredicates .isEmpty () ? null : PredicateBuilder .or (orPredicates );
88- } else if (partitionPredicate .function () instanceof LeafBinaryFunction ) {
99+ return orPredicates .isEmpty ()
100+ ? PredicateBuilder .alwaysFalse ()
101+ : PredicateBuilder .or (orPredicates );
102+ }
103+ if (partitionPredicate .function () instanceof LeafBinaryFunction ) {
89104 LinkedHashMap <String , String > partSpec =
90105 parsePartitionSpec (
91106 partitionPredicate .literals ().get (0 ).toString (), partitionKeys );
92107 if (partSpec == null ) {
93- return null ;
108+ return PredicateBuilder . alwaysFalse () ;
94109 }
95110 PredicateBuilder partBuilder = new PredicateBuilder (partitionType );
96111 List <Predicate > predicates = new ArrayList <>();
97112 for (int i = 0 ; i < partitionKeys .size (); i ++) {
98- Object value =
99- TypeUtils .castFromString (
100- partSpec .get (partitionKeys .get (i )), partitionType .getTypeAt (i ));
101- predicates .add (
102- new LeafPredicate (
103- partitionPredicate .function (),
104- partitionType .getTypeAt (i ),
105- i ,
106- partitionKeys .get (i ),
107- Collections .singletonList (value )));
113+ String strValue = partSpec .get (partitionKeys .get (i ));
114+ if (defaultPartitionName .equals (strValue )) {
115+ predicates .add (partBuilder .isNull (i ));
116+ } else {
117+ Object value = TypeUtils .castFromString (strValue , partitionType .getTypeAt (i ));
118+ predicates .add (
119+ new LeafPredicate (
120+ partitionPredicate .function (),
121+ partitionType .getTypeAt (i ),
122+ i ,
123+ partitionKeys .get (i ),
124+ Collections .singletonList (value )));
125+ }
108126 }
109127 return PredicateBuilder .and (predicates );
110128 }
@@ -115,20 +133,30 @@ public static boolean applyPartitionFilter(
115133 SnapshotReader snapshotReader ,
116134 @ Nullable LeafPredicate partitionPredicate ,
117135 List <String > partitionKeys ,
118- RowType partitionType ) {
136+ RowType partitionType ,
137+ String defaultPartitionName ) {
119138 if (partitionPredicate == null ) {
120139 return true ;
121140 }
122141
123- Predicate predicate =
124- buildPartitionPredicate (partitionPredicate , partitionKeys , partitionType );
125- if (predicate == null ) {
142+ Predicate result =
143+ buildPartitionPredicate (
144+ partitionPredicate , partitionKeys , partitionType , defaultPartitionName );
145+ if (result == null ) {
146+ return true ;
147+ }
148+ if (isAlwaysFalse (result )) {
126149 return false ;
127150 }
128- snapshotReader .withPartitionFilter (predicate );
151+ snapshotReader .withPartitionFilter (result );
129152 return true ;
130153 }
131154
155+ private static boolean isAlwaysFalse (Predicate predicate ) {
156+ return predicate instanceof LeafPredicate
157+ && ((LeafPredicate ) predicate ).function ().equals (AlwaysFalse .INSTANCE );
158+ }
159+
132160 @ Nullable
133161 public static LinkedHashMap <String , String > parsePartitionSpec (
134162 String partitionStr , List <String > partitionKeys ) {
0 commit comments