@@ -54,6 +54,9 @@ void GpuExpressionExecutor::Initialize(const Expression& expr, GpuExpressionExec
5454
5555void GpuExpressionExecutor::SetInputColumns (const GPUIntermediateRelation& input_relation)
5656{
57+ input_count = 0 ;
58+ has_null_input_column = false ;
59+
5760 // Shallow copy the columns
5861 input_columns = input_relation.columns ;
5962
@@ -64,62 +67,140 @@ void GpuExpressionExecutor::SetInputColumns(const GPUIntermediateRelation& input
6467 }
6568 else
6669 {
67- // All columns should have the same count
68- // TODO: This is assuming that all columns have the same size, which is not always true if the pipeline source is the hash table from RIGHT join
69- const auto col = input_columns[0 ];
70- // The input column may be null, in which case the expression evaluation should be a no-op
71- input_count = col == nullptr ? 0
72- : col->row_ids == nullptr ? static_cast <cudf::size_type>(col->column_length )
73- : static_cast <cudf::size_type>(col->row_id_count );
70+ // All columns that are not null should have the same count
71+ for (const auto & col : input_columns)
72+ {
73+ const auto temp_count = col == nullptr ? 0
74+ : col->row_ids == nullptr
75+ ? static_cast <cudf::size_type>(col->column_length )
76+ : static_cast <cudf::size_type>(col->row_id_count );
77+ if (temp_count > 0 )
78+ {
79+ input_count = temp_count;
80+ }
81+ else
82+ {
83+ has_null_input_column = true ;
84+ }
85+ }
86+ }
87+ }
88+
89+ // Helper template function for HasNullLeaf()
90+ template <typename ExpressionT>
91+ bool GpuExpressionExecutor::HasNullLeafLoop (const ExpressionT& expr) const
92+ {
93+ for (const auto & child : expr.children )
94+ {
95+ if (HasNullLeaf (*child))
96+ {
97+ return true ;
98+ }
99+ }
100+ return false ;
101+ }
102+
103+ bool GpuExpressionExecutor::HasNullLeaf (const Expression& expr) const
104+ {
105+ // Check if the expression is a null reference
106+ switch (expr.GetExpressionClass ())
107+ {
108+ case ExpressionClass::BOUND_BETWEEN : {
109+ const auto & between_expr = expr.Cast <BoundBetweenExpression>();
110+ return HasNullLeaf (*between_expr.input ) || HasNullLeaf (*between_expr.lower ) ||
111+ HasNullLeaf (*between_expr.upper );
112+ }
113+ case ExpressionClass::BOUND_CASE : {
114+ const auto & case_expr = expr.Cast <BoundCaseExpression>();
115+ for (const auto & case_check : case_expr.case_checks )
116+ {
117+ if (HasNullLeaf (*case_check.when_expr ) || HasNullLeaf (*case_check.then_expr ))
118+ {
119+ return true ;
120+ }
121+ }
122+ return HasNullLeaf (*case_expr.else_expr );
123+ }
124+ case ExpressionClass::BOUND_CAST : {
125+ const auto & cast_expr = expr.Cast <BoundCastExpression>();
126+ return HasNullLeaf (*cast_expr.child );
127+ }
128+ case ExpressionClass::BOUND_COMPARISON : {
129+ const auto & comp_expr = expr.Cast <BoundComparisonExpression>();
130+ return HasNullLeaf (*comp_expr.left ) || HasNullLeaf (*comp_expr.right );
131+ }
132+ case ExpressionClass::BOUND_CONJUNCTION : {
133+ return HasNullLeafLoop (expr.Cast <BoundConjunctionExpression>());
134+ }
135+ case ExpressionClass::BOUND_CONSTANT : {
136+ // Base case
137+ return false ;
138+ }
139+ case ExpressionClass::BOUND_FUNCTION : {
140+ return HasNullLeafLoop (expr.Cast <BoundFunctionExpression>());
141+ }
142+ case ExpressionClass::BOUND_OPERATOR : {
143+ return HasNullLeafLoop (expr.Cast <BoundOperatorExpression>());
144+ }
145+ case ExpressionClass::BOUND_REF : {
146+ // Base case
147+ const auto & ref_expr = expr.Cast <BoundReferenceExpression>();
148+ const auto & col = input_columns[ref_expr.index ];
149+ return col == nullptr || col->data_wrapper .data == nullptr ;
150+ }
151+ default :
152+ throw InternalException (" HasNullLeaf called on an expression [" + expr.ToString () +
153+ " ] with unsupported expression class!" );
74154 }
155+ return false ;
75156}
76157
77158void GpuExpressionExecutor::Execute (const GPUIntermediateRelation& input_relation,
78159 GPUIntermediateRelation& output_relation)
79160{
80- D_ASSERT (expressions.size () == output_relation.columns );
161+ D_ASSERT (expressions.size () == output_relation.columns . size () );
81162 D_ASSERT (!expressions.empty ());
163+
82164 SetInputColumns (input_relation);
83165
84166 // Loop over expressions to execute
85167 for (idx_t i = 0 ; i < expressions.size (); ++i)
86168 {
169+ const auto & expr = *expressions[i];
170+
87171 // If the expression is a reference, just pass it through
88- if (expressions[i]-> expression_class == ExpressionClass::BOUND_REF )
172+ if (expr. expression_class == ExpressionClass::BOUND_REF )
89173 {
90- auto input_idx = expressions[i]-> Cast <BoundReferenceExpression>().index ;
174+ auto input_idx = expr. Cast <BoundReferenceExpression>().index ;
91175 output_relation.columns [i] = input_relation.columns [input_idx];
92176 continue ;
93177 }
94178
95- // Make placeholder column
179+ // Make placeholder output column
96180 output_relation.columns [i] =
97- make_shared_ptr<GPUColumn>(0 ,
98- convertLogicalTypeToColumnType (expressions[i]->return_type ),
99- nullptr );
181+ make_shared_ptr<GPUColumn>(0 , convertLogicalTypeToColumnType (expr.return_type ), nullptr );
100182
101- // If input count is zero, no-op
102- if (input_count == 0 )
183+ // Skip execution if the input count is zero or if there is a null leaf
184+ if (input_count == 0 || (has_null_input_column && HasNullLeaf (expr)) )
103185 {
104186 continue ;
105187 }
106188
107- // Execute the expression
189+ // Otherwise, execute the expression
108190 auto result = ExecuteExpression (i);
109191
110192 // Cast the `result` from libcudf to `return_type` if `result` has different types.
111193 // E.g., `extract(year from col)` from libcudf returns int16_t but duckdb requires int64_t
112194 auto cudf_return_type = GpuExpressionState::GetCudfType (expressions[i]->return_type );
113- if (result->type ().id () != cudf_return_type.id ()) {
114- result = cudf::cast (result->view (),
115- cudf_return_type,
116- cudf::get_default_stream (),
117- resource_ref);
195+ if (result->type ().id () != cudf_return_type.id ())
196+ {
197+ result =
198+ cudf::cast (result->view (), cudf_return_type, cudf::get_default_stream (), resource_ref);
118199 }
119200
120201 // Transfer to output relation (zero copy)
121202 output_relation.columns [i]->setFromCudfColumn (*result,
122- false ,
203+ false , // How to know?
123204 nullptr ,
124205 0 ,
125206 &GPUBufferManager::GetInstance ());
@@ -134,12 +215,12 @@ void GpuExpressionExecutor::Select(GPUIntermediateRelation& input_relation,
134215
135216 SetInputColumns (input_relation);
136217
137- // If input count is zero, no-op
138- if (input_count == 0 )
218+ // If the input count is zero or if there is a null leaf, just materialize
219+ if (input_count == 0 || (has_null_input_column && HasNullLeaf (*expressions[ 0 ])) )
139220 {
140221 HandleMaterializeRowIDs (input_relation,
141222 output_relation,
142- input_count ,
223+ 0 ,
143224 nullptr ,
144225 &GPUBufferManager::GetInstance (),
145226 true );
0 commit comments