github-actions[bot] commented on code in PR #65548:
URL: https://github.com/apache/doris/pull/65548#discussion_r3584570137


##########
be/src/format_v2/column_mapper.cpp:
##########
@@ -843,6 +843,192 @@ static bool rewrite_struct_element_path_to_file_expr(
     return true;
 }
 
+static VExprSPtr cast_file_expr_to_table_type(const VExprSPtr& file_expr,
+                                              const DataTypePtr& table_type,
+                                              RewriteContext* rewrite_context) 
{
+    DORIS_CHECK(file_expr != nullptr);
+    DORIS_CHECK(table_type != nullptr);
+    DORIS_CHECK(rewrite_context != nullptr);
+    auto cast_expr = Cast::create_shared(table_type);
+    cast_expr->add_child(file_expr);
+    rewrite_context->add_created_expr(cast_expr);
+    return cast_expr;
+}
+
+// Prefer comparing in the physical file leaf type when a table predicate uses 
a promoted struct
+// child. For example, with table STRUCT<a: BIGINT>, old-file STRUCT<a: INT>, 
and `s.a = 10`, the
+// localized predicate should be `file_s.a::INT = 10::INT`, not
+// `CAST(file_s.a::INT AS BIGINT) = 10::BIGINT`. Converting one literal avoids 
a cast for every row.
+//
+// This rewrite is valid only when every possible file value survives 
file-to-table conversion and
+// the particular literal survives a table-to-file-to-table round trip. A 
value such as BIGINT
+// 2147483648 cannot be represented by an INT file leaf, so that case 
deliberately falls back to
+// `CAST(file_s.a AS BIGINT) = 2147483648`, which preserves the original 
table-level semantics.
+static bool rewrite_binary_struct_literal_predicate(
+        const VExprSPtr& expr, const std::vector<ColumnMapping>& 
filter_mappings,
+        const std::map<GlobalIndex, FileSlotRewriteInfo>& global_to_file_slot,
+        RewriteContext* rewrite_context) {
+    if (!is_binary_comparison_predicate(expr)) {
+        return false;
+    }
+    auto children = expr->children();
+    int struct_child_idx = -1;
+    int literal_child_idx = -1;
+    if (is_struct_element_expr(children[0])) {
+        struct_child_idx = 0;
+        literal_child_idx = 1;
+    } else if (is_struct_element_expr(children[1])) {
+        struct_child_idx = 1;
+        literal_child_idx = 0;
+    } else {
+        return false;
+    }
+
+    const auto table_leaf_type = children[struct_child_idx]->data_type();
+    DORIS_CHECK(table_leaf_type != nullptr);
+    auto table_literal = 
unwrap_literal_for_file_cast(children[literal_child_idx], table_leaf_type);
+    if (table_literal == nullptr ||
+        !rewrite_struct_element_path_to_file_expr(children[struct_child_idx], 
filter_mappings,
+                                                  global_to_file_slot, 
rewrite_context)) {
+        return false;
+    }
+
+    const auto file_leaf_type = children[struct_child_idx]->data_type();
+    DORIS_CHECK(file_leaf_type != nullptr);
+    const FileSlotRewriteInfo leaf_rewrite_info {
+            .block_position = 0,
+            .file_type = file_leaf_type,
+            .table_type = table_leaf_type,
+            .file_column_name = {},
+    };
+    auto file_literal =
+            rewrite_literal_to_file_type(table_literal, leaf_rewrite_info, 
rewrite_context);

Review Comment:
   This localizes the struct-child comparison whenever the numeric value 
round-trips through the file leaf type, but the proof ignores nullability. 
`is_lossless_file_to_table_numeric_cast()` removes nullable from both sides, so 
an old file leaf `Nullable(INT)` mapped to a required table leaf `INT` is 
treated as safe. With a filter like `WHERE s.a > 10`, the new file-local 
predicate can drop rows where `s.a` is NULL before TableReader materializes the 
struct and `_align_column_nullability()` rejects NULLs under the required table 
type. The same file without pushdown would fail that table contract, while the 
pushed predicate silently succeeds on the surviving rows. Please keep these 
struct literal predicates at table level unless the file leaf nullability is 
also compatible with the table leaf, or add the same nullability proof used by 
materialization before returning a file-domain literal.



##########
be/src/format_v2/parquet/parquet_reader.cpp:
##########
@@ -454,6 +508,12 @@ Status 
ParquetReader::open(std::shared_ptr<format::FileScanRequest> request) {
         DORIS_CHECK(local_id >= 0 && local_id < num_fields);
     }
 
+    // Reject requested unsupported logical leaves before row-group 
statistics, dictionaries,
+    // bloom filters or page indexes inspect their physical fallback type. For 
example, a predicate
+    // on TIME_MILLIS must fail here even when its INT32 statistics would 
prune every row group;
+    // otherwise the same unsupported SELECT could fail or silently succeed 
depending on data.
+    RETURN_IF_ERROR(validate_requested_columns_supported(_state->file_schema, 
*request_snapshot));

Review Comment:
   This validates every predicate column, but Iceberg can add a predicate 
column that the query did not actually request. When all equality-delete keys 
are missing from the data file, `_append_equality_delete_row_count_carrier()` 
appends the first `DATA_COLUMN` only so the delete predicate has a batch row 
count, and final materialization ignores that hidden dependency. If the first 
physical data column is an unsupported logical leaf such as `TIME_MILLIS`, a 
query that projects only supported columns now fails here because the carrier 
is treated like a real requested column. Please choose a supported carrier, or 
use a virtual row-position carrier that this validation already skips, before 
rejecting unsupported projected leaves.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to