cloud-fan commented on code in PR #58077:
URL: https://github.com/apache/spark/pull/58077#discussion_r3815458758


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/subquery.scala:
##########
@@ -165,14 +176,76 @@ case class InSubqueryExec(
     }
   }
 
+  // Three-valued IN semantics for multi-column subqueries (SPARK-58481).
+  // result rows are InternalRow objects; InSet uses TreeSet ordering which 
treats null fields as

Review Comment:
   **Nit:**
   
   Please capitalize `Result` at the start of this sentence.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/subquery.scala:
##########
@@ -165,14 +176,76 @@ case class InSubqueryExec(
     }
   }
 
+  // Three-valued IN semantics for multi-column subqueries (SPARK-58481).
+  // result rows are InternalRow objects; InSet uses TreeSet ordering which 
treats null fields as
+  // non-equal and therefore cannot distinguish a definitively-false candidate 
(a non-null field
+  // differs) from an indeterminate one (all non-null fields match but some 
fields are null).
+  // We replicate In.eval's per-candidate logic: TRUE if any candidate matches 
exactly,
+  // UNKNOWN if no TRUE and at least one candidate is indeterminate, FALSE 
otherwise.
+  private def evalMultiColumn(inputRow: InternalRow): Any = {
+    val value = child.eval(inputRow)
+    if (value == null) return null
+    val inputStruct = value.asInstanceOf[InternalRow]
+    val numFields = plan.output.length
+    var hasUnknown = false
+    var i = 0
+    while (i < result.length) {
+      val candidate = result(i).asInstanceOf[InternalRow]
+      var fieldIdx = 0
+      var candidateIsUnknown = false
+      var candidateIsFalse = false
+      while (fieldIdx < numFields && !candidateIsFalse) {
+        val inputField = inputStruct.get(fieldIdx, 
plan.output(fieldIdx).dataType)
+        val candidateField = candidate.get(fieldIdx, 
plan.output(fieldIdx).dataType)
+        if (candidateField == null || inputField == null) {
+          candidateIsUnknown = true
+        } else if (!inputField.equals(candidateField)) {

Review Comment:
   **Blocking:**
   
   Use Catalyst's interpreted ordering for each field here. `Object.equals` is 
not SQL equality for supported values such as `BinaryType` byte arrays, so 
equal binary fields from separately materialized rows compare unequal and a 
multi-column `IN` can incorrectly return FALSE. Please use the field data 
type's Catalyst ordering and add a binary-column regression case.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/subquery.scala:
##########
@@ -165,14 +176,76 @@ case class InSubqueryExec(
     }
   }
 
+  // Three-valued IN semantics for multi-column subqueries (SPARK-58481).
+  // result rows are InternalRow objects; InSet uses TreeSet ordering which 
treats null fields as
+  // non-equal and therefore cannot distinguish a definitively-false candidate 
(a non-null field
+  // differs) from an indeterminate one (all non-null fields match but some 
fields are null).
+  // We replicate In.eval's per-candidate logic: TRUE if any candidate matches 
exactly,
+  // UNKNOWN if no TRUE and at least one candidate is indeterminate, FALSE 
otherwise.
+  private def evalMultiColumn(inputRow: InternalRow): Any = {
+    val value = child.eval(inputRow)
+    if (value == null) return null
+    val inputStruct = value.asInstanceOf[InternalRow]
+    val numFields = plan.output.length
+    var hasUnknown = false
+    var i = 0
+    while (i < result.length) {
+      val candidate = result(i).asInstanceOf[InternalRow]
+      var fieldIdx = 0
+      var candidateIsUnknown = false
+      var candidateIsFalse = false
+      while (fieldIdx < numFields && !candidateIsFalse) {
+        val inputField = inputStruct.get(fieldIdx, 
plan.output(fieldIdx).dataType)

Review Comment:
   **Non-blocking:**
   
   Capture `plan.output` before the candidate loop and the current field's data 
type once per field. Both values are invariant, but these lookups currently run 
twice for every field of every result row.



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