8181#include " exprs/vin_predicate.h"
8282#include " exprs/vruntimefilter_wrapper.h"
8383#include " format/orc/orc_file_reader.h"
84+ #include " format/table/iceberg_default_value.h"
8485#include " format/table/iceberg_reader.h"
8586#include " format/table/partition_column_filler.h"
8687#include " format/table/transactional_hive_common.h"
@@ -559,6 +560,7 @@ Status OrcReader::init_reader(
559560
560561 RETURN_IF_ERROR (_create_file_reader ());
561562 RETURN_IF_ERROR (_init_read_columns ());
563+ _nested_initial_default_values.clear ();
562564 return Status::OK ();
563565}
564566
@@ -2290,7 +2292,8 @@ Status OrcReader::_fill_doris_data_column(const std::string& col_name,
22902292 const auto * orc_struct = dynamic_cast <const orc::StructVectorBatch*>(cvb);
22912293 auto & doris_struct = static_cast <ColumnStruct&>(*data_column);
22922294 std::map<int , int > read_fields;
2293- std::set<int > missing_fields;
2295+ std::set<int > schema_missing_fields;
2296+ std::set<int > projected_out_fields;
22942297 const auto * doris_struct_type =
22952298 assert_cast<const DataTypeStruct*>(remove_nullable (data_type).get ());
22962299
@@ -2305,7 +2308,7 @@ Status OrcReader::_fill_doris_data_column(const std::string& col_name,
23052308 for (int i = 0 ; i < doris_struct.tuple_size (); ++i) {
23062309 const auto & table_column_name = doris_struct_type->get_name_by_position (i);
23072310 if (!root_node->children_column_exists (table_column_name)) {
2308- missing_fields .insert (i);
2311+ schema_missing_fields .insert (i);
23092312 continue ;
23102313 }
23112314 const auto & file_column_name = root_node->children_file_column_name (table_column_name);
@@ -2321,27 +2324,47 @@ Status OrcReader::_fill_doris_data_column(const std::string& col_name,
23212324 << " ], table_column: " << table_column_name
23222325 << " , file_column: " << file_column_name_lower;
23232326 } else {
2324- missing_fields .insert (i);
2325- VLOG_DEBUG << " [OrcReader] Missing field: doris_field[" << i
2327+ projected_out_fields .insert (i);
2328+ VLOG_DEBUG << " [OrcReader] Projected-out field: doris_field[" << i
23262329 << " ], table_column: " << table_column_name
23272330 << " , file_column: " << file_column_name_lower
2328- << " (not found in ORC file )" ;
2331+ << " (not found in projected ORC type )" ;
23292332 }
23302333 }
23312334
2332- for (int missing_field : missing_fields) {
2333- ColumnPtr& doris_field = doris_struct.get_column_ptr (missing_field);
2334- if (!doris_field->is_nullable ()) {
2335- return Status::InternalError (
2336- " Child field of '{}' is not nullable, but is missing in orc file" ,
2337- col_name);
2338- }
2335+ // The selected ORC type can omit physical struct children that were not requested. They
2336+ // still exist in the file schema, so they must not be treated as Iceberg schema-evolution
2337+ // misses. Append placeholders only to keep every ColumnStruct child at the same size; the
2338+ // projected-out values are never exposed to the query.
2339+ for (int projected_out_field : projected_out_fields) {
2340+ ColumnPtr& doris_field = doris_struct.get_column_ptr (projected_out_field);
23392341 auto mutable_field = IColumn::mutate (std::move (doris_field));
2340- reinterpret_cast <ColumnNullable*>(mutable_field.get ())
2341- ->insert_many_defaults (num_values);
2342+ mutable_field->insert_many_defaults (num_values);
23422343 doris_field = std::move (mutable_field);
23432344 }
23442345
2346+ for (int missing_field : schema_missing_fields) {
2347+ ColumnPtr& doris_field = doris_struct.get_column_ptr (missing_field);
2348+ const auto & doris_name = doris_struct_type->get_name_by_position (missing_field);
2349+ const auto & doris_type = doris_struct_type->get_element (missing_field);
2350+ const auto * iceberg_field = root_node->get_missing_column_field (doris_name);
2351+ if (iceberg_field != nullptr ) {
2352+ RETURN_IF_ERROR (iceberg::append_initial_default (
2353+ *iceberg_field, doris_type, num_values, &_nested_initial_default_values,
2354+ &doris_field));
2355+ } else {
2356+ if (!doris_field->is_nullable ()) {
2357+ return Status::InternalError (
2358+ " Child field of '{}' is not nullable, but is missing in orc file" ,
2359+ col_name);
2360+ }
2361+ auto mutable_field = IColumn::mutate (std::move (doris_field));
2362+ reinterpret_cast <ColumnNullable*>(mutable_field.get ())
2363+ ->insert_many_defaults (num_values);
2364+ doris_field = std::move (mutable_field);
2365+ }
2366+ }
2367+
23452368 for (auto read_field : read_fields) {
23462369 orc::ColumnVectorBatch* orc_field = orc_struct->fields [read_field.second ];
23472370 const orc::Type* orc_type = orc_column_type->getSubtype (read_field.second );
@@ -2353,7 +2376,7 @@ Status OrcReader::_fill_doris_data_column(const std::string& col_name,
23532376 field_name, doris_field, doris_type,
23542377 root_node->get_children_node (
23552378 doris_struct_type->get_name_by_position (read_field.first )),
2356- orc_type, orc_field, num_values));
2379+ orc_type, orc_field, num_values, orc_struct ));
23572380 }
23582381 return Status::OK ();
23592382 }
@@ -2368,7 +2391,8 @@ template <bool is_filter>
23682391Status OrcReader::_orc_column_to_doris_column (
23692392 const std::string& col_name, ColumnPtr& doris_column, const DataTypePtr& data_type,
23702393 std::shared_ptr<TableSchemaChangeHelper::Node> root_node, const orc::Type* orc_column_type,
2371- const orc::ColumnVectorBatch* cvb, size_t num_values) {
2394+ const orc::ColumnVectorBatch* cvb, size_t num_values,
2395+ const orc::ColumnVectorBatch* parent_cvb) {
23722396 DataTypePtr resolved_type;
23732397 ColumnPtr resolved_column;
23742398 MutableColumnPtr data_column;
@@ -2422,8 +2446,18 @@ Status OrcReader::_orc_column_to_doris_column(
24222446 fill_orc_null_map (nullable_column, cvb, num_values);
24232447 } else {
24242448 if (cvb->hasNulls ) {
2425- return Status::InternalError (" Not nullable column {} has null values in orc file" ,
2426- col_name);
2449+ if (parent_cvb == nullptr || !parent_cvb->hasNulls ) {
2450+ return Status::InternalError (
2451+ " Not nullable column {} has null values in orc file" , col_name);
2452+ }
2453+ DORIS_CHECK_GE (parent_cvb->capacity , num_values);
2454+ DORIS_CHECK_GE (cvb->capacity , num_values);
2455+ for (size_t i = 0 ; i < num_values; ++i) {
2456+ if (!cvb->notNull [i] && parent_cvb->notNull [i]) {
2457+ return Status::InternalError (
2458+ " Not nullable column {} has null values in orc file" , col_name);
2459+ }
2460+ }
24272461 }
24282462 data_column = std::move (mutable_resolved_column);
24292463 }
0 commit comments