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+ #pragma once
18+ #include < boost/mpl/aux_/na_fwd.hpp>
19+
20+ #include " vec/functions/function.h"
21+
22+ namespace doris ::vectorized {
23+ #include " common/compile_check_begin.h"
24+
25+ // Helper struct to store information about const+nullable columns
26+ struct ColumnWithConstAndNullMap {
27+ const IColumn* nested_col = nullptr ;
28+ const NullMap* null_map = nullptr ;
29+ bool is_const = false ;
30+
31+ bool is_null_at (size_t row) const { return (null_map && (*null_map)[is_const ? 0 : row]); }
32+ };
33+
34+ // For functions that need to handle const+nullable column combinations
35+ // means that functioin `use_default_implementation_for_nulls()` returns false
36+ template <typename Impl, PrimitiveType ResultPrimitiveType>
37+ class FunctionNeedsToHandleNull : public IFunction {
38+ public:
39+ using ResultColumnType = PrimitiveTypeTraits<ResultPrimitiveType>::ColumnType;
40+
41+ static constexpr auto name = Impl::name;
42+ String get_name () const override { return name; }
43+
44+ static std::shared_ptr<IFunction> create () {
45+ return std::make_shared<FunctionNeedsToHandleNull>();
46+ }
47+
48+ size_t get_number_of_arguments () const override { return Impl::get_number_of_arguments (); }
49+
50+ bool is_variadic () const override {
51+ if constexpr (requires { Impl::is_variadic (); }) {
52+ return Impl::is_variadic ();
53+ }
54+ return false ;
55+ }
56+
57+ bool use_default_implementation_for_nulls () const override { return false ; }
58+
59+ DataTypePtr get_return_type_impl (const DataTypes& arguments) const override {
60+ return Impl::get_return_type_impl (arguments);
61+ }
62+
63+ Status execute_impl (FunctionContext* context, Block& block, const ColumnNumbers& arguments,
64+ uint32_t result, size_t input_rows_count) const override {
65+ auto res_col = ResultColumnType::create ();
66+ auto null_map = ColumnUInt8::create ();
67+ auto & null_map_data = null_map->get_data ();
68+ res_col->reserve (input_rows_count);
69+ null_map_data.resize_fill (input_rows_count, 0 );
70+
71+ const size_t arg_size = arguments.size ();
72+
73+ std::vector<ColumnWithConstAndNullMap> columns_info;
74+ columns_info.resize (arg_size);
75+ bool has_nullable = false ;
76+ collect_columns_info (columns_info, block, arguments, has_nullable);
77+
78+ // Check if there is a const null
79+ for (size_t i = 0 ; i < arg_size; ++i) {
80+ if (columns_info[i].is_const && columns_info[i].null_map &&
81+ (*columns_info[i].null_map )[0 ] &&
82+ execute_const_null (res_col, null_map_data, input_rows_count, i)) {
83+ block.replace_by_position (
84+ result, ColumnNullable::create (std::move (res_col), std::move (null_map)));
85+ return Status::OK ();
86+ }
87+ }
88+
89+ Impl::execute (columns_info, res_col, null_map_data, input_rows_count);
90+
91+ if (is_return_nullable (has_nullable, columns_info)) {
92+ block.replace_by_position (
93+ result, ColumnNullable::create (std::move (res_col), std::move (null_map)));
94+ } else {
95+ block.replace_by_position (result, std::move (res_col));
96+ }
97+
98+ return Status::OK ();
99+ }
100+
101+ private:
102+ // Handle a NULL literal
103+ // Default behavior is fill result with all NULLs
104+ // return true when the res_col is ready to be written back to the block without further processing
105+ bool execute_const_null (typename ResultColumnType::MutablePtr& res_col,
106+ PaddedPODArray<UInt8>& res_null_map_data, size_t input_rows_count,
107+ size_t null_index) const {
108+ if constexpr (requires {
109+ Impl::execute_const_null (res_col, res_null_map_data, input_rows_count,
110+ null_index);
111+ }) {
112+ return Impl::execute_const_null (res_col, res_null_map_data, input_rows_count,
113+ null_index);
114+ }
115+
116+ res_col->insert_many_defaults (input_rows_count);
117+ res_null_map_data.assign (input_rows_count, (UInt8)1 );
118+
119+ return true ;
120+ }
121+
122+ // Collect the required information for each column into columns_info
123+ // Including whether it is a constant column, nested column and null map(if exists).
124+ void collect_columns_info (std::vector<ColumnWithConstAndNullMap>& columns_info,
125+ const Block& block, const ColumnNumbers& arguments,
126+ bool & has_nullable) const {
127+ for (size_t i = 0 ; i < arguments.size (); ++i) {
128+ ColumnPtr col_ptr;
129+ const auto & col_with_type = block.get_by_position (arguments[i]);
130+ std::tie (col_ptr, columns_info[i].is_const ) = unpack_if_const (col_with_type.column );
131+
132+ if (is_column_nullable (*col_ptr)) {
133+ has_nullable = true ;
134+ const auto * nullable = check_and_get_column<ColumnNullable>(col_ptr.get ());
135+ columns_info[i].nested_col = &nullable->get_nested_column ();
136+ columns_info[i].null_map = &nullable->get_null_map_data ();
137+ } else {
138+ columns_info[i].nested_col = col_ptr.get ();
139+ }
140+ }
141+ }
142+
143+ // Determine if the return type should be wrapped in nullable
144+ // Default behavior is return nullable if any argument is nullable
145+ bool is_return_nullable (bool has_nullable,
146+ const std::vector<ColumnWithConstAndNullMap>& cols_info) const {
147+ if constexpr (requires { Impl::is_return_nullable (has_nullable, cols_info); }) {
148+ return Impl::is_return_nullable (has_nullable, cols_info);
149+ }
150+ return has_nullable;
151+ }
152+ };
153+ #include " common/compile_check_end.h"
154+ } // namespace doris::vectorized
0 commit comments