Coverage Report

Created: 2026-05-15 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/parquet/schema_desc.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
18
#pragma once
19
20
#include <gen_cpp/Planner_types.h>
21
#include <gen_cpp/parquet_types.h>
22
#include <stddef.h>
23
#include <stdint.h>
24
25
#include <string>
26
#include <unordered_map>
27
#include <unordered_set>
28
#include <vector>
29
30
#include "common/cast_set.h"
31
#include "common/status.h"
32
#include "core/data_type/data_type.h"
33
#include "core/data_type/data_type_nothing.h"
34
#include "util/slice.h"
35
36
namespace doris {
37
38
// Constant for unassigned column IDs
39
constexpr uint64_t UNASSIGNED_COLUMN_ID = UINT64_MAX;
40
41
struct FieldSchema {
42
    std::string name;
43
    std::string lower_case_name; // for hms column name case insensitive match
44
    // the referenced parquet schema element
45
    tparquet::SchemaElement parquet_schema;
46
47
    // Used to identify whether this field is a nested field.
48
    DataTypePtr data_type;
49
50
    // Only valid when this field is a leaf node
51
    tparquet::Type::type physical_type;
52
    // The index order in FieldDescriptor._physical_fields
53
    int physical_column_index = -1;
54
    int16_t definition_level = 0;
55
    int16_t repetition_level = 0;
56
    int16_t repeated_parent_def_level = 0;
57
    std::vector<FieldSchema> children;
58
59
    //For UInt8 -> Int16,UInt16 -> Int32,UInt32 -> Int64,UInt64 -> Int128.
60
    bool is_type_compatibility = false;
61
    bool is_in_variant = false;
62
63
    FieldSchema()
64
121k
            : data_type(std::make_shared<DataTypeNothing>()), column_id(UNASSIGNED_COLUMN_ID) {}
65
1.29M
    ~FieldSchema() = default;
66
1.19M
    FieldSchema(const FieldSchema& fieldSchema) = default;
67
    std::string debug_string() const;
68
69
    int32_t field_id = -1;
70
    uint64_t column_id = UNASSIGNED_COLUMN_ID;
71
    uint64_t max_column_id = 0; // Maximum column ID for this field and its children
72
73
    // Column ID assignment and lookup methods
74
    void assign_ids(uint64_t& next_id);
75
    const FieldSchema* find_column_by_id(uint64_t target_id) const;
76
    uint64_t get_column_id() const;
77
    void set_column_id(uint64_t id);
78
    uint64_t get_max_column_id() const;
79
};
80
81
class FieldDescriptor {
82
private:
83
    // Only the schema elements at the first level
84
    std::vector<FieldSchema> _fields;
85
    // The leaf node of schema elements
86
    std::vector<FieldSchema*> _physical_fields;
87
    // Name to _fields, not all schema elements
88
    std::unordered_map<std::string, FieldSchema*> _name_to_field;
89
    // Used in from_thrift, marking the next schema position that should be parsed
90
    size_t _next_schema_pos;
91
    // useful for parse_node_field to decide whether to convert byte_array to VARBINARY type
92
    bool _enable_mapping_varbinary = false;
93
    bool _enable_mapping_timestamp_tz = false;
94
95
private:
96
    void parse_physical_field(const tparquet::SchemaElement& physical_schema, bool is_nullable,
97
                              FieldSchema* physical_field);
98
99
    Status parse_list_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
100
                            FieldSchema* list_field);
101
102
    Status parse_map_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
103
                           FieldSchema* map_field);
104
105
    Status parse_variant_field(const std::vector<tparquet::SchemaElement>& t_schemas,
106
                               size_t curr_pos, FieldSchema* variant_field);
107
108
    Status parse_struct_field(const std::vector<tparquet::SchemaElement>& t_schemas,
109
                              size_t curr_pos, FieldSchema* struct_field);
110
111
    Status parse_group_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
112
                             FieldSchema* group_field);
113
114
    Status parse_node_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
115
                            FieldSchema* node_field);
116
117
    std::pair<DataTypePtr, bool> convert_to_doris_type(tparquet::LogicalType logicalType,
118
                                                       bool nullable);
119
    std::pair<DataTypePtr, bool> convert_to_doris_type(
120
            const tparquet::SchemaElement& physical_schema, bool nullable);
121
    std::pair<DataTypePtr, bool> get_doris_type(const tparquet::SchemaElement& physical_schema,
122
                                                bool nullable);
123
124
public:
125
11.4k
    FieldDescriptor() = default;
126
86.9k
    ~FieldDescriptor() = default;
127
128
    /**
129
     * Parse FieldDescriptor from parquet thrift FileMetaData.
130
     * @param t_schemas list of schema elements
131
     */
132
    Status parse_from_thrift(const std::vector<tparquet::SchemaElement>& t_schemas);
133
134
    int get_column_index(const std::string& column) const;
135
136
    /**
137
     * Get the column(the first level schema element, maybe nested field) by index.
138
     * @param index Column index in _fields
139
     */
140
740k
    const FieldSchema* get_column(size_t index) const { return &_fields[index]; }
141
142
    /**
143
     * Get the column(the first level schema element, maybe nested field) by name.
144
     * @param name Column name
145
     * @return FieldSchema or nullptr if not exists
146
     */
147
    FieldSchema* get_column(const std::string& name) const;
148
149
    void get_column_names(std::unordered_set<std::string>* names) const;
150
151
    std::string debug_string() const;
152
153
817k
    int32_t size() const { return cast_set<int32_t>(_fields.size()); }
154
155
33.4k
    const std::vector<FieldSchema>& get_fields_schema() const { return _fields; }
156
157
    /**
158
     * Assign stable column IDs to schema fields.
159
     *
160
     * This uses an ORC-compatible encoding so that the results of
161
     * create_column_ids() are consistent across formats. IDs start from 1
162
     * and are assigned in a pre-order traversal (parent before children).
163
     * After calling this, each FieldSchema will have column_id and
164
     * max_column_id populated.
165
     */
166
    void assign_ids();
167
168
    const FieldSchema* find_column_by_id(uint64_t column_id) const;
169
11.4k
    void set_enable_mapping_varbinary(bool enable) { _enable_mapping_varbinary = enable; }
170
11.4k
    void set_enable_mapping_timestamp_tz(bool enable) { _enable_mapping_timestamp_tz = enable; }
171
};
172
173
} // namespace doris