You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2022/11/16 03:30:06 UTC

[incubator-sedona] branch master updated: [SEDONA-192] Null handling in predicates (#712)

This is an automated email from the ASF dual-hosted git repository.

jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-sedona.git


The following commit(s) were added to refs/heads/master by this push:
     new 1a0c8e23 [SEDONA-192] Null handling in predicates (#712)
1a0c8e23 is described below

commit 1a0c8e237344c8e2ef95b1eb3104881021d37105
Author: Tanel Kiis <ta...@users.noreply.github.com>
AuthorDate: Wed Nov 16 05:30:00 2022 +0200

    [SEDONA-192] Null handling in predicates (#712)
---
 .../sql/sedona_sql/expressions/Predicates.scala    | 28 +++++++++++++++-------
 .../org/apache/sedona/sql/predicateTestScala.scala | 18 ++++++++++++++
 2 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala
index 454110fe..9d9d1261 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala
@@ -20,22 +20,24 @@ package org.apache.spark.sql.sedona_sql.expressions
 
 import org.apache.sedona.sql.utils.GeometrySerializer
 import org.apache.spark.sql.catalyst.InternalRow
-import org.apache.spark.sql.catalyst.expressions.Expression
+import org.apache.spark.sql.catalyst.expressions.{ExpectsInputTypes, Expression, NullIntolerant}
 import org.apache.spark.sql.catalyst.expressions.codegen.CodegenFallback
 import org.apache.spark.sql.catalyst.util.ArrayData
 import org.apache.spark.sql.types.{BooleanType, DataType}
 import org.locationtech.jts.geom.Geometry
-import org.apache.spark.sql.catalyst.expressions.ExpectsInputTypes
 import org.apache.spark.sql.types.AbstractDataType
 import org.apache.spark.sql.sedona_sql.UDT.GeometryUDT
 
-abstract class ST_Predicate extends Expression with FoldableExpression with ExpectsInputTypes {
+abstract class ST_Predicate extends Expression
+  with FoldableExpression
+  with ExpectsInputTypes
+  with NullIntolerant {
 
   def inputExpressions: Seq[Expression]
 
   override def toString: String = s" **${this.getClass.getName}**  "
 
-  override def nullable: Boolean = false
+  override def nullable: Boolean = children.exists(_.nullable)
 
   override def inputTypes: Seq[AbstractDataType] = Seq(GeometryUDT, GeometryUDT)
 
@@ -43,12 +45,20 @@ abstract class ST_Predicate extends Expression with FoldableExpression with Expe
 
   override def children: Seq[Expression] = inputExpressions
 
-  override def eval(inputRow: InternalRow): Any = {
+  override final def eval(inputRow: InternalRow): Any = {
     val leftArray = inputExpressions(0).eval(inputRow).asInstanceOf[ArrayData]
-    val rightArray = inputExpressions(1).eval(inputRow).asInstanceOf[ArrayData]
-    val leftGeometry = GeometrySerializer.deserialize(leftArray)
-    val rightGeometry = GeometrySerializer.deserialize(rightArray)
-    evalGeom(leftGeometry, rightGeometry)
+    if (leftArray == null) {
+      null
+    } else {
+      val rightArray = inputExpressions(1).eval(inputRow).asInstanceOf[ArrayData]
+      if (rightArray == null) {
+        null
+      } else {
+        val leftGeometry = GeometrySerializer.deserialize(leftArray)
+        val rightGeometry = GeometrySerializer.deserialize(rightArray)
+        evalGeom(leftGeometry, rightGeometry)
+      }
+    }
   }
 
   def evalGeom(leftGeometry: Geometry, rightGeometry: Geometry): Boolean
diff --git a/sql/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala b/sql/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala
index c870e058..da0c088f 100644
--- a/sql/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala
+++ b/sql/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala
@@ -19,6 +19,9 @@
 
 package org.apache.sedona.sql
 
+import org.apache.spark.sql.catalyst.expressions.{EmptyRow, Literal}
+import org.apache.spark.sql.sedona_sql.expressions.{ST_Contains, ST_CoveredBy, ST_Covers, ST_Crosses, ST_Disjoint, ST_Equals, ST_Intersects, ST_OrderingEquals, ST_Overlaps, ST_Point, ST_Touches, ST_Within}
+
 class predicateTestScala extends TestBaseScala {
 
   describe("Sedona-SQL Predicate Test") {
@@ -245,5 +248,20 @@ class predicateTestScala extends TestBaseScala {
       assert(!within.get(0).asInstanceOf[Boolean])
       assert(coveredBy.get(0).asInstanceOf[Boolean])
     }
+
+    Seq(
+      ST_Contains, ST_Intersects, ST_Within, ST_Covers, ST_CoveredBy, ST_Crosses,
+      ST_Overlaps, ST_Touches, ST_Equals, ST_Disjoint, ST_OrderingEquals
+    ).foreach { predicate =>
+      it(s"Passed null handling in $predicate") {
+        val point = ST_Point(Literal.create(0.0) :: Literal.create(0.0) :: Literal.create(0.0):: Nil)
+        val missing = Literal.create(null)
+
+        assert(predicate(point :: point :: Nil).eval(EmptyRow) != null)
+        assert(predicate(point :: missing :: Nil).eval(EmptyRow) == null)
+        assert(predicate(missing :: point :: Nil).eval(EmptyRow) == null)
+        assert(predicate(missing :: missing :: Nil).eval(EmptyRow) == null)
+      }
+    }
   }
 }