zanmato1984 commented on code in PR #51691:
URL: https://github.com/apache/arrow/pull/51691#discussion_r4213490664


##########
cpp/src/arrow/compute/expression_test.cc:
##########
@@ -1401,6 +1457,15 @@ TEST(Expression, RemoveNamedRefs) {
   ExpectRemovesRefsTo(field_ref({"a", "b"}), field_ref({0, 0}), nested_schema);
 }
 
+TEST(Expression, NullableRemoveNamedRefs) {

Review Comment:
   Nit (non-blocking): Please also cover a required field inside a call, for 
example add(required_field, literal(1)). After RemoveNamedRefs, check that the 
transformed field argument is still bound, uses a positional reference, and 
retains nullable=false. This would protect nullability preservation through the 
recursive rewrite path, in addition to the direct-field case here. The call 
itself should remain conservatively nullable.



##########
cpp/src/arrow/compute/expression_test.cc:
##########
@@ -612,6 +612,62 @@ TEST(Expression, BindNestedFieldRef) {
                                                                field("b", 
int64())}))})));
 }
 
+TEST(Expression, NullableFieldRef) {
+  auto input_schema = schema({field("r", int32(), false), field("n", int32(), 
true)});
+  for (int i = 0; i < input_schema->num_fields(); ++i) {
+    for (const auto& ref : {FieldRef(input_schema->field(i)->name()), 
FieldRef(i)}) {
+      auto expr = field_ref(ref);
+      EXPECT_TRUE(expr.nullable());
+
+      ASSERT_OK_AND_ASSIGN(auto bound, expr.Bind(*input_schema));
+      EXPECT_EQ(bound.nullable(), input_schema->field(i)->nullable());
+      EXPECT_TRUE(expr.nullable());
+
+      ASSERT_OK_AND_ASSIGN(auto bound_to_type,
+                           expr.Bind(struct_(input_schema->fields())));
+      EXPECT_EQ(bound_to_type.nullable(), bound.nullable());
+    }
+  }
+}
+
+TEST(Expression, NullableConservativeFallback) {
+  auto input_schema = schema({field("r", int32(), false), field("n", int32(), 
true)});
+  EXPECT_TRUE(Expression{}.nullable());
+  for (const auto& expr : {literal(1), 
literal(std::make_shared<Int32Scalar>()),
+                           add(field_ref("r"), literal(1)), 
is_valid(field_ref("n"))}) {
+    EXPECT_TRUE(expr.nullable());

Review Comment:
   Nit (non-blocking): Identify the expression when one of these fallback cases 
fails.
   
   ```suggestion
       SCOPED_TRACE(expr.ToString());
       EXPECT_TRUE(expr.nullable());
   ```



##########
cpp/src/arrow/compute/expression_test.cc:
##########
@@ -612,6 +612,62 @@ TEST(Expression, BindNestedFieldRef) {
                                                                field("b", 
int64())}))})));
 }
 
+TEST(Expression, NullableFieldRef) {
+  auto input_schema = schema({field("r", int32(), false), field("n", int32(), 
true)});
+  for (int i = 0; i < input_schema->num_fields(); ++i) {
+    for (const auto& ref : {FieldRef(input_schema->field(i)->name()), 
FieldRef(i)}) {
+      auto expr = field_ref(ref);

Review Comment:
   Nit (non-blocking): Add the input field and reference to failure diagnostics 
for each case.
   
   ```suggestion
         SCOPED_TRACE(input_schema->field(i)->ToString());
         SCOPED_TRACE(ref.ToString());
         auto expr = field_ref(ref);
   ```



##########
cpp/src/arrow/compute/expression_test.cc:
##########
@@ -612,6 +612,62 @@ TEST(Expression, BindNestedFieldRef) {
                                                                field("b", 
int64())}))})));
 }
 
+TEST(Expression, NullableFieldRef) {
+  auto input_schema = schema({field("r", int32(), false), field("n", int32(), 
true)});
+  for (int i = 0; i < input_schema->num_fields(); ++i) {
+    for (const auto& ref : {FieldRef(input_schema->field(i)->name()), 
FieldRef(i)}) {
+      auto expr = field_ref(ref);
+      EXPECT_TRUE(expr.nullable());
+
+      ASSERT_OK_AND_ASSIGN(auto bound, expr.Bind(*input_schema));
+      EXPECT_EQ(bound.nullable(), input_schema->field(i)->nullable());
+      EXPECT_TRUE(expr.nullable());
+
+      ASSERT_OK_AND_ASSIGN(auto bound_to_type,
+                           expr.Bind(struct_(input_schema->fields())));
+      EXPECT_EQ(bound_to_type.nullable(), bound.nullable());
+    }
+  }
+}
+
+TEST(Expression, NullableConservativeFallback) {
+  auto input_schema = schema({field("r", int32(), false), field("n", int32(), 
true)});
+  EXPECT_TRUE(Expression{}.nullable());
+  for (const auto& expr : {literal(1), 
literal(std::make_shared<Int32Scalar>()),
+                           add(field_ref("r"), literal(1)), 
is_valid(field_ref("n"))}) {
+    EXPECT_TRUE(expr.nullable());
+    ASSERT_OK_AND_ASSIGN(auto bound, expr.Bind(*input_schema));
+    EXPECT_TRUE(bound.IsBound());
+    EXPECT_TRUE(bound.nullable());
+  }
+}
+
+TEST(Expression, NullableNestedFieldRef) {
+  for (bool parent_nullable : {false, true}) {
+    for (bool child_nullable : {false, true}) {
+      auto input_schema = schema(
+          {field("a", struct_({field("b", int32(), child_nullable)}), 
parent_nullable)});
+      for (const auto& ref : {FieldRef("a", "b"), FieldRef(FieldPath({0, 
0}))}) {
+        ASSERT_OK_AND_ASSIGN(auto bound, field_ref(ref).Bind(*input_schema));

Review Comment:
   Nit (non-blocking): Include the schema and reference so failures identify 
both parent/child nullability and the reference form.
   
   ```suggestion
           SCOPED_TRACE(input_schema->ToString());
           SCOPED_TRACE(ref.ToString());
           ASSERT_OK_AND_ASSIGN(auto bound, field_ref(ref).Bind(*input_schema));
   ```



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

Reply via email to