OIiveirra commented on code in PR #69032:
URL: https://github.com/apache/doris/pull/69032#discussion_r4303140255


##########
be/src/exec/common/hash_table/hash_key_type.h:
##########
@@ -125,7 +125,11 @@ inline HashKeyType get_hash_key_type(const 
std::vector<DataTypePtr>& data_types)
         // fixed-width or string-key specialization even when it is the only 
grouping/join key.
         return HashKeyType::serialized;
     }
-    // serialized cannot be used in the case of single column, because the 
join operator will have some processing of column nullable, resulting in 
incorrect serialized results.
+    // Spatial columns store StringViews, not STRING offsets/chars. Use their 
column serializer
+    // for both values and nullable markers, as for the existing single-column 
complex keys.
+    if (t->get_primitive_type() == TYPE_GEOMETRY || t->get_primitive_type() == 
TYPE_GEOGRAPHY) {

Review Comment:
   Fixed in b490cd866af. I used the analysis-side alternative to keep this PR 
bounded: reject spatial source/probe runtime-filter contexts both at initial 
admission and after probe rewrites at the scan, and reject constant-list 
spatial IN/NOT IN before coercion. Serialized spatial hash joins remain 
supported.
   
   FE tests pass 93/93, including scalar controls and rewritten-probe checks. 
The real Iceberg regression passes in both generation and non-generation modes: 
bare geom/geog joins with pruning disabled and runtime-filter flags 1/2/8 all 
return 5, and their plans contain no RF. Both IN operators reject explicitly 
for both kinds.



##########
be/src/format/transformer/viceberg_parquet_writer.cpp:
##########
@@ -18,15 +18,128 @@
 #include "format/transformer/viceberg_parquet_writer.h"
 
 #include <parquet/api/reader.h>
+#include <parquet/arrow/schema.h>
+#include <parquet/file_writer.h>
 #include <parquet/schema.h>
 
 #include <algorithm>
+#include <numeric>
+#include <string>
 
+#include "exprs/function/geo/functions_geo.h"
 #include "format/table/iceberg/iceberg_arrow_block_convertor.h"
 #include "format/table/parquet_utils.h"
+#include "runtime/exec_env.h"
 #include "runtime/runtime_state.h"
 
 namespace doris {
+namespace {
+
+::parquet::schema::NodePtr clone_parquet_node(const ::parquet::schema::Node& 
node) {
+    if (node.is_primitive()) {
+        const auto& primitive = static_cast<const 
::parquet::schema::PrimitiveNode&>(node);
+        return ::parquet::schema::PrimitiveNode::Make(
+                primitive.name(), primitive.repetition(), 
primitive.logical_type(),
+                primitive.physical_type(), primitive.type_length(), 
primitive.field_id());
+    }
+
+    const auto& group = static_cast<const ::parquet::schema::GroupNode&>(node);
+    ::parquet::schema::NodeVector fields;
+    fields.reserve(group.field_count());
+    for (int index = 0; index < group.field_count(); ++index) {
+        fields.push_back(clone_parquet_node(*group.field(index)));
+    }
+    return ::parquet::schema::GroupNode::Make(
+            group.name(), group.repetition(), fields,
+            group.logical_type()->is_none() ? nullptr : group.logical_type(), 
group.field_id());
+}
+
+arrow::Result<::parquet::LogicalType::EdgeInterpolationAlgorithm> 
geography_algorithm(
+        const std::string& algorithm) {
+    using Algorithm = ::parquet::LogicalType::EdgeInterpolationAlgorithm;
+    if (algorithm == "spherical") {
+        return Algorithm::SPHERICAL;
+    }
+    if (algorithm == "vincenty") {
+        return Algorithm::VINCENTY;
+    }
+    if (algorithm == "thomas") {
+        return Algorithm::THOMAS;
+    }
+    if (algorithm == "andoyer") {
+        return Algorithm::ANDOYER;
+    }
+    if (algorithm == "karney") {
+        return Algorithm::KARNEY;
+    }
+    return arrow::Status::Invalid("Unsupported Iceberg geography edge 
algorithm: ", algorithm);
+}
+
+bool contains_spatial_type(iceberg::Type* type) {
+    switch (type->type_id()) {
+    case iceberg::TypeID::GEOMETRY:
+    case iceberg::TypeID::GEOGRAPHY:
+        return true;
+    case iceberg::TypeID::STRUCT:
+        for (const auto& field : type->as_struct_type()->fields()) {
+            if (contains_spatial_type(field.field_type())) {
+                return true;
+            }
+        }
+        return false;
+    case iceberg::TypeID::LIST:
+        return 
contains_spatial_type(type->as_list_type()->element_field().field_type());
+    case iceberg::TypeID::MAP:
+        return 
contains_spatial_type(type->as_map_type()->key_field().field_type()) ||
+               
contains_spatial_type(type->as_map_type()->value_field().field_type());
+    default:
+        return false;
+    }
+}
+
+arrow::Result<std::shared_ptr<::parquet::schema::GroupNode>> 
make_iceberg_parquet_schema(
+        const std::shared_ptr<arrow::Schema>& arrow_schema, const 
iceberg::Schema& iceberg_schema,
+        const std::shared_ptr<::parquet::WriterProperties>& writer_properties,
+        const std::shared_ptr<::parquet::ArrowWriterProperties>& 
arrow_properties) {
+    std::shared_ptr<::parquet::SchemaDescriptor> parquet_schema;
+    ARROW_RETURN_NOT_OK(::parquet::arrow::ToParquetSchema(arrow_schema.get(), 
*writer_properties,
+                                                          *arrow_properties, 
&parquet_schema));
+
+    const auto* source_root = parquet_schema->group_node();
+    ::parquet::schema::NodeVector fields;
+    fields.reserve(source_root->field_count());
+    for (int index = 0; index < source_root->field_count(); ++index) {
+        const auto& source_field = source_root->field(index);
+        const auto& iceberg_field = iceberg_schema.columns()[index];
+        if (iceberg_field.field_type()->type_id() == 
iceberg::TypeID::GEOMETRY) {
+            const auto& geometry =
+                    static_cast<const 
iceberg::GeometryType&>(*iceberg_field.field_type());
+            fields.push_back(::parquet::schema::PrimitiveNode::Make(
+                    source_field->name(), source_field->repetition(),
+                    ::parquet::LogicalType::Geometry(geometry.crs()), 
::parquet::Type::BYTE_ARRAY,

Review Comment:
   Fixed in b490cd866af using the permitted rejection alternative. The current 
sink does not transport referenced table-property definitions, so the Parquet 
writer rejects projjson: CRS references before ParquetFileWriter::Open for both 
spatial kinds, including case-equivalent prefixes. Ordinary nonreferenced CRS 
writes and raw custom-CRS reads remain available; no protocol or 
metadata-transport expansion was added.
   
   The BE writer/schema tests pass 19/19. The real Iceberg regression supplies 
a valid table-property CRS definition and matching source/target identities; 
both writes fail explicitly, and both target row counts and committed-file 
counts remain zero. Full suite generation and independent non-generation 
verification pass.



-- 
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