Coverage Report

Created: 2026-04-10 12:12

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
62
    FieldSchema()
63
121k
            : data_type(std::make_shared<DataTypeNothing>()), column_id(UNASSIGNED_COLUMN_ID) {}
64
735k
    ~FieldSchema() = default;
65
627k
    FieldSchema(const FieldSchema& fieldSchema) = default;
66
    std::string debug_string() const;
67
68
    int32_t field_id = -1;
69
    uint64_t column_id = UNASSIGNED_COLUMN_ID;
70
    uint64_t max_column_id = 0; // Maximum column ID for this field and its children
71
72
    // Column ID assignment and lookup methods
73
    void assign_ids(uint64_t& next_id);
74
    const FieldSchema* find_column_by_id(uint64_t target_id) const;
75
    uint64_t get_column_id() const;
76
    void set_column_id(uint64_t id);
77
    uint64_t get_max_column_id() const;
78
};
79
80
class FieldDescriptor {
81
private:
82
    // Only the schema elements at the first level
83
    std::vector<FieldSchema> _fields;
84
    // The leaf node of schema elements
85
    std::vector<FieldSchema*> _physical_fields;
86
    // Name to _fields, not all schema elements
87
    std::unordered_map<std::string, FieldSchema*> _name_to_field;
88
    // Used in from_thrift, marking the next schema position that should be parsed
89
    size_t _next_schema_pos;
90
    // useful for parse_node_field to decide whether to convert byte_array to VARBINARY type
91
    bool _enable_mapping_varbinary = false;
92
    bool _enable_mapping_timestamp_tz = false;
93
94
private:
95
    void parse_physical_field(const tparquet::SchemaElement& physical_schema, bool is_nullable,
96
                              FieldSchema* physical_field);
97
98
    Status parse_list_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
99
                            FieldSchema* list_field);
100
101
    Status parse_map_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
102
                           FieldSchema* map_field);
103
104
    Status parse_struct_field(const std::vector<tparquet::SchemaElement>& t_schemas,
105
                              size_t curr_pos, FieldSchema* struct_field);
106
107
    Status parse_group_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
108
                             FieldSchema* group_field);
109
110
    Status parse_node_field(const std::vector<tparquet::SchemaElement>& t_schemas, size_t curr_pos,
111
                            FieldSchema* node_field);
112
113
    std::pair<DataTypePtr, bool> convert_to_doris_type(tparquet::LogicalType logicalType,
114
                                                       bool nullable);
115
    std::pair<DataTypePtr, bool> convert_to_doris_type(
116
            const tparquet::SchemaElement& physical_schema, bool nullable);
117
    std::pair<DataTypePtr, bool> get_doris_type(const tparquet::SchemaElement& physical_schema,
118
                                                bool nullable);
119
120
public:
121
11.4k
    FieldDescriptor() = default;
122
49.8k
    ~FieldDescriptor() = default;
123
124
    /**
125
     * Parse FieldDescriptor from parquet thrift FileMetaData.
126
     * @param t_schemas list of schema elements
127
     */
128
    Status parse_from_thrift(const std::vector<tparquet::SchemaElement>& t_schemas);
129
130
    int get_column_index(const std::string& column) const;
131
132
    /**
133
     * Get the column(the first level schema element, maybe nested field) by index.
134
     * @param index Column index in _fields
135
     */
136
908k
    const FieldSchema* get_column(size_t index) const { return &_fields[index]; }
137
138
    /**
139
     * Get the column(the first level schema element, maybe nested field) by name.
140
     * @param name Column name
141
     * @return FieldSchema or nullptr if not exists
142
     */
143
    FieldSchema* get_column(const std::string& name) const;
144
145
    void get_column_names(std::unordered_set<std::string>* names) const;
146
147
    std::string debug_string() const;
148
149
871k
    int32_t size() const { return cast_set<int32_t>(_fields.size()); }
150
151
15.1k
    const std::vector<FieldSchema>& get_fields_schema() const { return _fields; }
152
153
    /**
154
     * Assign stable column IDs to schema fields.
155
     *
156
     * This uses an ORC-compatible encoding so that the results of
157
     * create_column_ids() are consistent across formats. IDs start from 1
158
     * and are assigned in a pre-order traversal (parent before children).
159
     * After calling this, each FieldSchema will have column_id and
160
     * max_column_id populated.
161
     */
162
    void assign_ids();
163
164
    const FieldSchema* find_column_by_id(uint64_t column_id) const;
165
11.4k
    void set_enable_mapping_varbinary(bool enable) { _enable_mapping_varbinary = enable; }
166
11.4k
    void set_enable_mapping_timestamp_tz(bool enable) { _enable_mapping_timestamp_tz = enable; }
167
};
168
169
} // namespace doris