Coverage Report

Created: 2026-10-27 18:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/aggregate/aggregate_function_count.h
Line
Count
Source
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
// This file is copied from
18
// https://github.com/ClickHouse/ClickHouse/blob/master/src/AggregateFunctions/AggregateFunctionCount.h
19
// and modified by Doris
20
21
#pragma once
22
23
#include <stddef.h>
24
25
#include <algorithm>
26
#include <boost/iterator/iterator_facade.hpp>
27
#include <memory>
28
#include <vector>
29
30
#include "core/assert_cast.h"
31
#include "core/column/column.h"
32
#include "core/column/column_fixed_length_object.h"
33
#include "core/column/column_nullable.h"
34
#include "core/column/column_vector.h"
35
#include "core/data_type/data_type.h"
36
#include "core/data_type/data_type_fixed_length_object.h"
37
#include "core/data_type/data_type_number.h"
38
#include "core/types.h"
39
#include "exprs/aggregate/aggregate_function.h"
40
#include "util/simd/bits.h"
41
42
namespace doris {
43
class Arena;
44
class BufferReadable;
45
class BufferWritable;
46
47
struct AggregateFunctionCountData {
48
    UInt64 count = 0;
49
};
50
51
/// Simply count number of calls.
52
class AggregateFunctionCount final
53
        : public IAggregateFunctionDataHelper<AggregateFunctionCountData, AggregateFunctionCount>,
54
          VarargsExpression,
55
          NotNullableAggregateFunction {
56
public:
57
    AggregateFunctionCount(const DataTypes& argument_types_)
58
154
            : IAggregateFunctionDataHelper(argument_types_) {}
59
60
4
    bool is_simple_count() const override { return true; }
61
4
    String get_name() const override { return "count"; }
62
63
82
    DataTypePtr get_return_type() const override { return std::make_shared<DataTypeInt64>(); }
64
65
1
    bool is_trivial() const override { return true; }
66
67
0
    WindowSpillStrategy window_spill_strategy() const override {
68
0
        return WindowSpillStrategy::PARTITION_REDUCE;
69
0
    }
70
71
12
    void add(AggregateDataPtr __restrict place, const IColumn**, ssize_t, Arena&) const override {
72
12
        ++data(place).count;
73
12
    }
74
75
    void add_batch_single_place(size_t batch_size, AggregateDataPtr place, const IColumn**,
76
8
                                Arena&) const override {
77
8
        data(place).count += batch_size;
78
8
    }
79
80
1
    void reset(AggregateDataPtr place) const override {
81
1
        AggregateFunctionCount::data(place).count = 0;
82
1
    }
83
84
    void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs,
85
122
               Arena&) const override {
86
122
        data(place).count += data(rhs).count;
87
122
    }
88
89
0
    void serialize(ConstAggregateDataPtr __restrict place, BufferWritable& buf) const override {
90
0
        buf.write_var_uint(data(place).count);
91
0
    }
92
93
    void deserialize(AggregateDataPtr __restrict place, BufferReadable& buf,
94
0
                     Arena&) const override {
95
0
        buf.read_var_uint(data(place).count);
96
0
    }
97
98
221
    void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn& to) const override {
99
221
        assert_cast<ColumnInt64&, TypeCheckOnRelease::DISABLE>(to).get_data().push_back(
100
221
                data(place).count);
101
221
    }
102
103
    void serialize_to_column(const std::vector<AggregateDataPtr>& places, size_t offset,
104
3
                             MutableColumnPtr& dst, const size_t num_rows) const override {
105
3
        auto& col = assert_cast<ColumnFixedLengthObject&>(*dst);
106
3
        DCHECK(col.item_size() == sizeof(Data))
107
0
                << "size is not equal: " << col.item_size() << " " << sizeof(Data);
108
3
        col.resize(num_rows);
109
3
        auto* data = col.get_data().data();
110
6
        for (size_t i = 0; i != num_rows; ++i) {
111
3
            *reinterpret_cast<Data*>(&data[sizeof(Data) * i]) =
112
3
                    *reinterpret_cast<Data*>(places[i] + offset);
113
3
        }
114
3
    }
115
116
    void streaming_agg_serialize_to_column(const IColumn** columns, MutableColumnPtr& dst,
117
1
                                           const size_t num_rows, Arena&) const override {
118
1
        auto& dst_col = assert_cast<ColumnFixedLengthObject&>(*dst);
119
1
        DCHECK(dst_col.item_size() == sizeof(Data))
120
0
                << "size is not equal: " << dst_col.item_size() << " " << sizeof(Data);
121
1
        dst_col.resize(num_rows);
122
1
        auto* data = dst_col.get_data().data();
123
4
        for (size_t i = 0; i != num_rows; ++i) {
124
3
            auto& state = *reinterpret_cast<Data*>(&data[sizeof(Data) * i]);
125
3
            state.count = 1;
126
3
        }
127
1
    }
128
129
    void deserialize_from_column_row(AggregateDataPtr __restrict place, const IColumn& column,
130
12
                                     size_t row, Arena&) const override {
131
12
        DCHECK_LT(row, column.size());
132
12
        const auto& col = assert_cast<const ColumnFixedLengthObject&>(column);
133
12
        const auto* data = reinterpret_cast<const Data*>(col.get_data().data());
134
12
        AggregateFunctionCount::data(place) = data[row];
135
12
    }
136
137
    void deserialize_and_merge_from_column_range(AggregateDataPtr __restrict place,
138
                                                 const IColumn& column, size_t begin, size_t end,
139
132
                                                 Arena&) const override {
140
132
        DCHECK(end <= column.size() && begin <= end)
141
0
                << ", begin:" << begin << ", end:" << end << ", column.size():" << column.size();
142
132
        auto& col = assert_cast<const ColumnFixedLengthObject&>(column);
143
132
        auto* data = reinterpret_cast<const Data*>(col.get_data().data());
144
266
        for (size_t i = begin; i <= end; ++i) {
145
134
            doris::AggregateFunctionCount::data(place).count += data[i].count;
146
134
        }
147
132
    }
148
149
69
    bool needs_deserialize_and_merge_scratch() const override { return false; }
150
151
    void deserialize_and_merge_vec(const AggregateDataPtr* places, size_t offset,
152
                                   AggregateDataPtr rhs, const IColumn* column, Arena& arena,
153
13
                                   const size_t num_rows) const override {
154
13
        const auto& col = assert_cast<const ColumnFixedLengthObject&>(*column);
155
13
        const auto* data = col.get_data().data();
156
13
        this->merge_vec(places, offset, AggregateDataPtr(data), arena, num_rows);
157
13
    }
158
159
    void deserialize_and_merge_vec_selected(const AggregateDataPtr* places, size_t offset,
160
                                            AggregateDataPtr rhs, const IColumn* column,
161
21
                                            Arena& arena, const size_t num_rows) const override {
162
21
        const auto& col = assert_cast<const ColumnFixedLengthObject&>(*column);
163
21
        const auto* data = col.get_data().data();
164
21
        this->merge_vec_selected(places, offset, AggregateDataPtr(data), arena, num_rows);
165
21
    }
166
167
    void serialize_without_key_to_column(ConstAggregateDataPtr __restrict place,
168
13
                                         IColumn& to) const override {
169
13
        auto& col = assert_cast<ColumnFixedLengthObject&>(to);
170
13
        DCHECK(col.item_size() == sizeof(Data))
171
0
                << "size is not equal: " << col.item_size() << " " << sizeof(Data);
172
13
        size_t old_size = col.size();
173
13
        col.resize(old_size + 1);
174
13
        (reinterpret_cast<Data*>(col.get_data().data()) + old_size)->count =
175
13
                AggregateFunctionCount::data(place).count;
176
13
    }
177
178
66
    MutableColumnPtr create_serialize_column() const override {
179
66
        return ColumnFixedLengthObject::create(sizeof(Data));
180
66
    }
181
182
152
    DataTypePtr get_serialized_type() const override {
183
152
        return std::make_shared<DataTypeFixedLengthObject>();
184
152
    }
185
186
    void add_range_single_place(int64_t partition_start, int64_t partition_end, int64_t frame_start,
187
                                int64_t frame_end, AggregateDataPtr place, const IColumn** columns,
188
                                Arena& arena, UInt8* use_null_result,
189
0
                                UInt8* could_use_previous_result) const override {
190
0
        frame_start = std::max<int64_t>(frame_start, partition_start);
191
0
        frame_end = std::min<int64_t>(frame_end, partition_end);
192
0
        if (frame_start >= frame_end) {
193
0
            if (!*could_use_previous_result) {
194
0
                *use_null_result = true;
195
0
            }
196
0
        } else {
197
0
            AggregateFunctionCount::data(place).count += frame_end - frame_start;
198
0
            *use_null_result = false;
199
0
            *could_use_previous_result = true;
200
0
        }
201
0
    }
202
};
203
204
// Used for unary count(nullable_expr). SQL count(expr) counts non-NULL values.
205
class AggregateFunctionCountNotNullUnary final
206
        : public IAggregateFunctionDataHelper<AggregateFunctionCountData,
207
                                              AggregateFunctionCountNotNullUnary> {
208
public:
209
    AggregateFunctionCountNotNullUnary(const DataTypes& argument_types_)
210
10
            : IAggregateFunctionDataHelper(argument_types_) {}
211
212
2
    String get_name() const override { return "count"; }
213
214
97
    DataTypePtr get_return_type() const override { return std::make_shared<DataTypeInt64>(); }
215
216
1
    bool is_trivial() const override { return true; }
217
218
0
    WindowSpillStrategy window_spill_strategy() const override {
219
0
        return WindowSpillStrategy::PARTITION_REDUCE;
220
0
    }
221
222
    void add(AggregateDataPtr __restrict place, const IColumn** columns, ssize_t row_num,
223
22
             Arena&) const override {
224
22
        data(place).count +=
225
22
                !assert_cast<const ColumnNullable&, TypeCheckOnRelease::DISABLE>(*columns[0])
226
22
                         .is_null_at(row_num);
227
22
    }
228
229
    void add_batch_single_place(size_t batch_size, AggregateDataPtr place, const IColumn** columns,
230
15
                                Arena&) const override {
231
15
        const auto& nullable_column =
232
15
                assert_cast<const ColumnNullable&, TypeCheckOnRelease::DISABLE>(*columns[0]);
233
15
        const auto& null_map = nullable_column.get_null_map_data();
234
15
        DCHECK_LE(batch_size, null_map.size());
235
15
        if (!nullable_column.has_null(0, batch_size)) {
236
7
            data(place).count += batch_size;
237
7
            return;
238
7
        }
239
8
        data(place).count +=
240
8
                simd::count_zero_num(reinterpret_cast<const int8_t*>(null_map.data()), batch_size);
241
8
    }
242
243
16
    void reset(AggregateDataPtr place) const override { data(place).count = 0; }
244
245
    void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs,
246
139
               Arena&) const override {
247
139
        data(place).count += data(rhs).count;
248
139
    }
249
250
0
    void serialize(ConstAggregateDataPtr __restrict place, BufferWritable& buf) const override {
251
0
        buf.write_var_uint(data(place).count);
252
0
    }
253
254
    void deserialize(AggregateDataPtr __restrict place, BufferReadable& buf,
255
0
                     Arena&) const override {
256
0
        buf.read_var_uint(data(place).count);
257
0
    }
258
259
265
    void insert_result_into(ConstAggregateDataPtr __restrict place, IColumn& to) const override {
260
265
        if (is_column_nullable(to)) {
261
1
            auto& null_column = assert_cast<ColumnNullable&, TypeCheckOnRelease::DISABLE>(to);
262
1
            null_column.get_null_map_data().push_back(0);
263
1
            assert_cast<ColumnInt64&, TypeCheckOnRelease::DISABLE>(null_column.get_nested_column())
264
1
                    .get_data()
265
1
                    .push_back(data(place).count);
266
264
        } else {
267
264
            assert_cast<ColumnInt64&, TypeCheckOnRelease::DISABLE>(to).get_data().push_back(
268
264
                    data(place).count);
269
264
        }
270
265
    }
271
272
18
    void check_result_column_type(const IColumn& to) const override {
273
18
        if (const auto* null_column = check_and_get_column<ColumnNullable>(to)) {
274
1
            IAggregateFunction::check_result_column_type(null_column->get_nested_column());
275
1
            return;
276
1
        }
277
17
        IAggregateFunction::check_result_column_type(to);
278
17
    }
279
280
    void serialize_to_column(const std::vector<AggregateDataPtr>& places, size_t offset,
281
7
                             MutableColumnPtr& dst, const size_t num_rows) const override {
282
7
        auto& col = assert_cast<ColumnFixedLengthObject&>(*dst);
283
7
        DCHECK(col.item_size() == sizeof(Data))
284
0
                << "size is not equal: " << col.item_size() << " " << sizeof(Data);
285
7
        col.resize(num_rows);
286
7
        auto* data = col.get_data().data();
287
15
        for (size_t i = 0; i != num_rows; ++i) {
288
8
            *reinterpret_cast<Data*>(&data[sizeof(Data) * i]) =
289
8
                    *reinterpret_cast<Data*>(places[i] + offset);
290
8
        }
291
7
    }
292
293
    void streaming_agg_serialize_to_column(const IColumn** columns, MutableColumnPtr& dst,
294
4
                                           const size_t num_rows, Arena&) const override {
295
4
        auto& col = assert_cast<ColumnFixedLengthObject&>(*dst);
296
4
        DCHECK(col.item_size() == sizeof(Data))
297
0
                << "size is not equal: " << col.item_size() << " " << sizeof(Data);
298
4
        col.resize(num_rows);
299
4
        auto& data = col.get_data();
300
4
        const ColumnNullable& input_col = assert_cast<const ColumnNullable&>(*columns[0]);
301
25
        for (size_t i = 0; i < num_rows; i++) {
302
21
            auto& state = *reinterpret_cast<Data*>(&data[sizeof(Data) * i]);
303
21
            state.count = !input_col.is_null_at(i);
304
21
        }
305
4
    }
306
307
    void deserialize_from_column_row(AggregateDataPtr __restrict place, const IColumn& column,
308
23
                                     size_t row, Arena&) const override {
309
23
        DCHECK_LT(row, column.size());
310
23
        const auto& col = assert_cast<const ColumnFixedLengthObject&>(column);
311
23
        const auto* data = reinterpret_cast<const Data*>(col.get_data().data());
312
23
        AggregateFunctionCountNotNullUnary::data(place) = data[row];
313
23
    }
314
315
    void deserialize_and_merge_from_column_range(AggregateDataPtr __restrict place,
316
                                                 const IColumn& column, size_t begin, size_t end,
317
158
                                                 Arena&) const override {
318
158
        DCHECK(end <= column.size() && begin <= end)
319
0
                << ", begin:" << begin << ", end:" << end << ", column.size():" << column.size();
320
158
        auto& col = assert_cast<const ColumnFixedLengthObject&>(column);
321
158
        auto* data = reinterpret_cast<const Data*>(col.get_data().data());
322
323
        for (size_t i = begin; i <= end; ++i) {
323
165
            doris::AggregateFunctionCountNotNullUnary::data(place).count += data[i].count;
324
165
        }
325
158
    }
326
327
72
    bool needs_deserialize_and_merge_scratch() const override { return false; }
328
329
    void deserialize_and_merge_vec(const AggregateDataPtr* places, size_t offset,
330
                                   AggregateDataPtr rhs, const IColumn* column, Arena& arena,
331
17
                                   const size_t num_rows) const override {
332
17
        const auto& col = assert_cast<const ColumnFixedLengthObject&>(*column);
333
17
        const auto* data = col.get_data().data();
334
17
        this->merge_vec(places, offset, AggregateDataPtr(data), arena, num_rows);
335
17
    }
336
337
    void deserialize_and_merge_vec_selected(const AggregateDataPtr* places, size_t offset,
338
                                            AggregateDataPtr rhs, const IColumn* column,
339
22
                                            Arena& arena, const size_t num_rows) const override {
340
22
        const auto& col = assert_cast<const ColumnFixedLengthObject&>(*column);
341
22
        const auto* data = col.get_data().data();
342
22
        this->merge_vec_selected(places, offset, AggregateDataPtr(data), arena, num_rows);
343
22
    }
344
345
    void serialize_without_key_to_column(ConstAggregateDataPtr __restrict place,
346
34
                                         IColumn& to) const override {
347
34
        auto& col = assert_cast<ColumnFixedLengthObject&>(to);
348
34
        DCHECK(col.item_size() == sizeof(Data))
349
0
                << "size is not equal: " << col.item_size() << " " << sizeof(Data);
350
34
        size_t old_size = col.size();
351
34
        col.resize(old_size + 1);
352
34
        (reinterpret_cast<Data*>(col.get_data().data()) + old_size)->count =
353
34
                AggregateFunctionCountNotNullUnary::data(place).count;
354
34
    }
355
356
37
    MutableColumnPtr create_serialize_column() const override {
357
37
        return ColumnFixedLengthObject::create(sizeof(Data));
358
37
    }
359
360
7
    DataTypePtr get_serialized_type() const override {
361
7
        return std::make_shared<DataTypeFixedLengthObject>();
362
7
    }
363
364
    void add_range_single_place(int64_t partition_start, int64_t partition_end, int64_t frame_start,
365
                                int64_t frame_end, AggregateDataPtr place, const IColumn** columns,
366
                                Arena& arena, UInt8* use_null_result,
367
0
                                UInt8* could_use_previous_result) const override {
368
0
        frame_start = std::max<int64_t>(frame_start, partition_start);
369
0
        frame_end = std::min<int64_t>(frame_end, partition_end);
370
0
        if (frame_start >= frame_end) {
371
0
            if (!*could_use_previous_result) {
372
0
                *use_null_result = true;
373
0
            }
374
0
        } else {
375
0
            const auto& nullable_column =
376
0
                    assert_cast<const ColumnNullable&, TypeCheckOnRelease::DISABLE>(*columns[0]);
377
0
            size_t count = 0;
378
0
            if (nullable_column.has_null()) {
379
0
                for (int64_t i = frame_start; i < frame_end; ++i) {
380
0
                    if (!nullable_column.is_null_at(i)) {
381
0
                        ++count;
382
0
                    }
383
0
                }
384
0
            } else {
385
0
                count = frame_end - frame_start;
386
0
            }
387
0
            *use_null_result = false;
388
0
            *could_use_previous_result = true;
389
0
            AggregateFunctionCountNotNullUnary::data(place).count += count;
390
0
        }
391
0
    }
392
};
393
394
} // namespace doris