Re: Strange behavior with polygon and NaN

Поиск
Список
Период
Сортировка
От Kyotaro Horiguchi
Тема Re: Strange behavior with polygon and NaN
Дата
Msg-id 20211215.162055.355076641764301199.horikyota.ntt@gmail.com
обсуждение исходный текст
Ответ на Strange behavior with polygon and NaN  (Jesse Zhang <sbjesse@gmail.com>)
Ответы Re: Strange behavior with polygon and NaN  (Kyotaro Horiguchi <horikyota.ntt@gmail.com>)
Список pgsql-hackers
At Mon, 15 Mar 2021 08:34:00 -0400, David Steele <david@pgmasters.net> wrote in 
> On 12/21/20 3:30 AM, Kyotaro Horiguchi wrote:
> > At Tue, 01 Dec 2020 10:03:42 -0500, Tom Lane <tgl@sss.pgh.pa.us> wrote
> > in
> >> I think it should be "needs review" now.
> > Conflicted with some commit(s) uncertain to me. Rebased.

When I recently re-found the working tree on this topic in my laptop,
it contained an unfinished work of tristate geometric comparisons. I
had forgotten how come I started it but I found the starting thread
[1].  I've done that work to the level it anyhow looks working.

The first patch fixes arithmetic functions to handle NaNs, which is
the first motive of this thread.  The second introduces tri-state
comparison as requested in [1].  The tri-state comparison returns
TS_NULL or SQL-NULL for comparisons involving NaNs.  GiST index is
adjusted so that it treats null as false. On the way doing this, the
bug #17334 [2] and another bug raised earlier [3] are naturally fixed.

The first patch changes/fixes the behavior of geometric arithmetics as
follows *as the result*.

==== 1-a
Distance between an valid object and an object containing NaNs was 0.

select line '{-0.4,-1,-6}' <-> line '{3,NaN,5}';  -- 0 => NaN

==== 1-b
The distance between a point and a vertical or horizontal line was NaN
for some valid cases.

select point '(1e+300,Infinity)' <-> line '{-1,0,3}';  -- NaN -> Infinity

==== 1-c
The closest point of two objects could not be solved if it contains
Infinity.

select point '(1e+300,Infinity)' ## line '{1,0,5}';   -- null -> (-5,Infinity)

(I'm not sure the fix works for all possible cases..)

==== 1-d
Containment involving NaNs was falsely true for some kind of objects.

select point '(NaN,NaN)' <@ path '((1,2),(3,4))';         -- true -> null
select point '(NaN,NaN)' <@ polygon '((2,0),(2,4),(0,0))';-- true -> null

=== 1-e
The intersection detection of two objects containing NaNs was true.

select line '{-1,0,3}' ?# line '{3,NaN,5}';  -- true -> null


The second patch as the result changes/fixes the behavior of
geometrical arithmetics as the follows.

==== 2-a
The containment detection between a valid shape and a point containing
NaN(s) was false.  This is not necessarirly wrong but it is changed
according to [1]

select point '(0.5, NaN)' <@ box '(0,0,1,1)';   -- false -> null
select point '(NaN, NaN)' <@ path '[(0,0),(1,0),(1,1),(0,1)]'; -- false -> null

==== 2-b
The equality of two lines containing NaNs can be true or false. This
is right assuming NaN == NaN. But it is changed according to the
policy that NaN makes an object invalid.

select line '{NaN, 1, NaN}' = line '{NaN, 1, NaN}'; -- true -> null
select line '{NaN, 1, NaN}' = line '{NaN, 2, NaN}'; -- false -> null

==== 2-c
The result of the following expression changed from Infinity to
NaN. The distance from the point to to the box is indeterminant.

select box '(-Infinity,500),(-Infinity,100)' <-> point '123,456';

The internal function is dist_bp().  The difference comes from the
behavior that lseg_closept_line() ignores the NaN returned from
line_closept_point() and uses the last one of the two ends of the lseg
as the result, which is (-inf, 500).  With this patch the same
function returns (NaN, NaN) which leads to null as the final result.
The previos behavior (for these particular values) may be correct at a
glance but it is wrong at the time lseg_closept_line ignores the fact
that it could not determine which end is closer.

==== 2-d

The comparison between objects involving NaNs results was false, but
it is now null.  So NaNs that were shown as a part of the following
query are now excluded.  (I'm not sure this is the desired result.)

SELECT p1.f1, p2.f1 FROM
(VALUES (point '0,0'), (point '1,1'), (point 'NaN,NaN')) p1(f1),
(VALUES (point '0,0'), (point '1,1'), (point 'NaN,NaN')) p2(f1)
WHERE p1.f1 <> p2.f1;

     f1     |    f1     
 -----------+-----------
  (0,0)     | (1,1)
- (0,0)     | (NaN,NaN)
  (1,1)     | (0,0)
- (1,1)     | (NaN,NaN)
- (NaN,NaN) | (0,0)
- (NaN,NaN) | (1,1)
(6 rows)

==== 2-e

circle_same(~=) returned true for '<(3,5),NaN>' and circle
'<(3,5),0>', which is definitely bogus.
null.

SELECT circle '<(3,5),NaN>' ~= circle '<(3,5),0>';   -- true -> null
SELECT circle '<(3,5),NaN>' ~= circle '<(3,5),NaN>'; -- true -> null


[1] https://www.postgresql.org/message-id/CAOBaU_ZvJGkAuKqfFxQxnsirpaVci_-S3F3M5M1Wzrq1kGyC%3Dg%40mail.gmail.com
[2] https://www.postgresql.org/message-id/17334-135f485c21739caa%40postgresql.org
[3] https://www.postgresql.org/message-id/CAMbWs4-C9K-8V=cAY7q0ciZmJKBMiUnp_xBGzxgKpEWPKd0bng@mail.gmail.com

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
From 7f7dae7919d0566038df5ffcfe1d4b1437586fe9 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyoga.ntt@gmail.com>
Date: Fri, 13 Nov 2020 13:31:20 +0900
Subject: [PATCH v9 1/2] Fix NaN handling of geometric arithmetics

Geometrical arithmetic functions have not consciously handled NaNs. As
the result they made a unexpected results for NaNs in certain
cases. Correct that behavior by handling NaNs consciously.
---
 doc/src/sgml/func.sgml                     |  17 ++
 src/backend/utils/adt/geo_ops.c            | 337 ++++++++++++++++++---
 src/include/utils/float.h                  |  22 ++
 src/test/regress/expected/create_index.out |   2 +-
 src/test/regress/expected/geometry.out     | 331 +++++++++-----------
 5 files changed, 485 insertions(+), 224 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5801299b27..9c8991449b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -11456,6 +11456,23 @@ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple
     </para>
    </note>
 
+   <caution>
+     <para>
+       NaN and Infinity make geometric functions and operators behave
+       inconsistently. Geometric operators or functions that return a boolean
+       return false for operands that contain NaNs. Number-returning functions
+       and operators return NaN in most cases but sometimes return a valid
+       value if no NaNs are met while actual calculation.  Object-returning ones
+       yield an object that contain NaNs depending to the operation.  Likewise
+       the objects containing Infinity can make geometric operators and
+       functions behave inconsistently. For example (point
+       '(Infinity,Infinity)' <-> line '{-1,0,5}') is Infinity but (point
+       '(Infinity,Infinity)' <-> line '{0,-1,5}') is NaN. It can never
+       be a value other than these, but you should consider it uncertain how
+       geometric operators behave for objects containing Infinity.
+     </para>
+   </caution>
+
    <table id="functions-geometry-func-table">
     <title>Geometric Functions</title>
     <tgroup cols="1">
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 92dc9483b9..851c171108 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -904,9 +904,9 @@ box_intersect(PG_FUNCTION_ARGS)
 
     result = (BOX *) palloc(sizeof(BOX));
 
-    result->high.x = float8_min(box1->high.x, box2->high.x);
+    result->high.x = float8_min_nan(box1->high.x, box2->high.x);
     result->low.x = float8_max(box1->low.x, box2->low.x);
-    result->high.y = float8_min(box1->high.y, box2->high.y);
+    result->high.y = float8_min_nan(box1->high.y, box2->high.y);
     result->low.y = float8_max(box1->low.y, box2->low.y);
 
     PG_RETURN_BOX_P(result);
@@ -1061,15 +1061,23 @@ line_construct(LINE *result, Point *pt, float8 m)
         result->A = -1.0;
         result->B = 0.0;
         result->C = pt->x;
+
+        /* Avoid creating a valid line from an invalid point */
+        if (likely(!isnan(pt->x) && !isnan(pt->y)))
+            return;
     }
-    else if (m == 0)
+    else if (m == 0.0)
     {
         /* horizontal - use "y = C" */
         result->A = 0.0;
         result->B = -1.0;
         result->C = pt->y;
+
+        /* Avoid creating a valid line from an invalid point */
+        if (likely(!isnan(pt->x) && !isnan(pt->y)))
+            return;
     }
-    else
+    else if (!isnan(m))
     {
         /* use "mx - y + yinter = 0" */
         result->A = m;
@@ -1078,7 +1086,12 @@ line_construct(LINE *result, Point *pt, float8 m)
         /* on some platforms, the preceding expression tends to produce -0 */
         if (result->C == 0.0)
             result->C = 0.0;
+
+        if (likely(!isnan(result->C)))
+            return;
     }
+
+    result->A = result->B = result->C = get_float8_nan();
 }
 
 /* line_construct_pp()
@@ -1091,6 +1104,7 @@ line_construct_pp(PG_FUNCTION_ARGS)
     Point       *pt2 = PG_GETARG_POINT_P(1);
     LINE       *result = (LINE *) palloc(sizeof(LINE));
 
+    /* NaNs are considered to be equal by point_eq_point */
     if (point_eq_point(pt1, pt2))
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -1111,8 +1125,12 @@ line_intersect(PG_FUNCTION_ARGS)
 {
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
+    Point        xp;
 
-    PG_RETURN_BOOL(line_interpt_line(NULL, l1, l2));
+    if (line_interpt_line(&xp, l1, l2) && !isnan(xp.x) && !isnan(xp.y))
+        PG_RETURN_BOOL(true);
+    else
+        PG_RETURN_BOOL(false);
 }
 
 Datum
@@ -1130,14 +1148,17 @@ line_perp(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
 
+    if (unlikely(isnan(l1->C) || isnan(l2->C)))
+        return false;
+
     if (FPzero(l1->A))
-        PG_RETURN_BOOL(FPzero(l2->B));
+        PG_RETURN_BOOL(FPzero(l2->B) && !isnan(l1->B) && !isnan(l2->A));
     if (FPzero(l2->A))
-        PG_RETURN_BOOL(FPzero(l1->B));
+        PG_RETURN_BOOL(FPzero(l1->B) && !isnan(l2->B) && !isnan(l1->A));
     if (FPzero(l1->B))
-        PG_RETURN_BOOL(FPzero(l2->A));
+        PG_RETURN_BOOL(FPzero(l2->A) && !isnan(l1->A) && !isnan(l2->B));
     if (FPzero(l2->B))
-        PG_RETURN_BOOL(FPzero(l1->A));
+        PG_RETURN_BOOL(FPzero(l1->A) && !isnan(l2->A) && !isnan(l1->B));
 
     PG_RETURN_BOOL(FPeq(float8_div(float8_mul(l1->A, l2->A),
                                    float8_mul(l1->B, l2->B)), -1.0));
@@ -1148,7 +1169,7 @@ line_vertical(PG_FUNCTION_ARGS)
 {
     LINE       *line = PG_GETARG_LINE_P(0);
 
-    PG_RETURN_BOOL(FPzero(line->B));
+    PG_RETURN_BOOL(FPzero(line->B) && !isnan(line->A) && !isnan(line->C));
 }
 
 Datum
@@ -1156,7 +1177,7 @@ line_horizontal(PG_FUNCTION_ARGS)
 {
     LINE       *line = PG_GETARG_LINE_P(0);
 
-    PG_RETURN_BOOL(FPzero(line->A));
+    PG_RETURN_BOOL(FPzero(line->A) && !isnan(line->B) && !isnan(line->C));
 }
 
 
@@ -1206,9 +1227,20 @@ static inline float8
 line_sl(LINE *line)
 {
     if (FPzero(line->A))
+    {
+        /* C is likely to be NaN than B */
+        if (unlikely(isnan(line->C) || isnan(line->B)))
+            return get_float8_nan();
         return 0.0;
+    }
     if (FPzero(line->B))
+    {
+        /* C is likely to be NaN than A */
+        if (unlikely(isnan(line->C) || isnan(line->A)))
+            return get_float8_nan();
         return get_float8_infinity();
+    }
+
     return float8_div(line->A, -line->B);
 }
 
@@ -1220,9 +1252,20 @@ static inline float8
 line_invsl(LINE *line)
 {
     if (FPzero(line->A))
+    {
+        /* C is likely to be NaN than B */
+        if (unlikely(isnan(line->C) || isnan(line->B)))
+            return get_float8_nan();
         return get_float8_infinity();
+    }
+
     if (FPzero(line->B))
+    {
+        /* C is likely to be NaN than A */
+        if (unlikely(isnan(line->C) || isnan(line->A)))
+            return get_float8_nan();
         return 0.0;
+    }
     return float8_div(line->B, line->A);
 }
 
@@ -1235,14 +1278,23 @@ line_distance(PG_FUNCTION_ARGS)
 {
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
+    Point        xp;
     float8        ratio;
 
-    if (line_interpt_line(NULL, l1, l2))    /* intersecting? */
+    if (line_interpt_line(&xp, l1, l2)) /* intersecting? */
+    {
+        /* return NaN if NaN is involved */
+        if (isnan(xp.x) || isnan(xp.y))
+            PG_RETURN_FLOAT8(get_float8_nan());
+
         PG_RETURN_FLOAT8(0.0);
+    }
 
-    if (!FPzero(l1->A) && !isnan(l1->A) && !FPzero(l2->A) && !isnan(l2->A))
+    if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l2->A) || isnan(l2->B)))
+        ratio = get_float8_nan();
+    else if (!FPzero(l1->A) && !FPzero(l2->A))
         ratio = float8_div(l1->A, l2->A);
-    else if (!FPzero(l1->B) && !isnan(l1->B) && !FPzero(l2->B) && !isnan(l2->B))
+    else if (!FPzero(l1->B) && !FPzero(l2->B))
         ratio = float8_div(l1->B, l2->B);
     else
         ratio = 1.0;
@@ -1264,8 +1316,13 @@ line_interpt(PG_FUNCTION_ARGS)
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (!line_interpt_line(result, l1, l2))
+    if (!line_interpt_line(result, l1, l2) ||
+        isnan(result->x) || isnan(result->y))
+    {
+        pfree(result);
         PG_RETURN_NULL();
+    }
+
     PG_RETURN_POINT_P(result);
 }
 
@@ -1291,6 +1348,7 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
 
     if (!FPzero(l1->B))
     {
+        /* l1 is not virtucal */
         if (FPeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
             return false;
 
@@ -1298,18 +1356,34 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
                                  float8_mul(l2->B, l1->C)),
                        float8_mi(float8_mul(l1->A, l2->B),
                                  float8_mul(l2->A, l1->B)));
-        y = float8_div(-float8_pl(float8_mul(l1->A, x), l1->C), l1->B);
-    }
-    else if (!FPzero(l2->B))
-    {
-        if (FPeq(l1->A, float8_mul(l2->A, float8_div(l1->B, l2->B))))
-            return false;
-
-        x = float8_div(float8_mi(float8_mul(l2->B, l1->C),
-                                 float8_mul(l1->B, l2->C)),
+        /*
+         * You might want to simplify this expression by using x, but that can
+         * introduce unnecessary indeterminants (inf-inf) into the calculation,
+         * which leads to a wrong result.
+         * (For example, (-1, -1, Inf) and (1, -1, 0))
+         */
+        y = float8_div(float8_mi(float8_mul(l1->A, l2->C),
+                                 float8_mul(l2->A, l1->C)),
                        float8_mi(float8_mul(l2->A, l1->B),
                                  float8_mul(l1->A, l2->B)));
-        y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
+    }
+    else if (!FPzero(l2->B))
+    {
+        /* l2 is not virtical */
+        /*
+         * We know that l1 is vertical here. The lines cannot be parallel and
+         * the x-coord of the cross point is -C/A of l1.
+         */
+        x = -float8_div(l1->C, l1->A);
+
+        /*
+         * When l2->A is zero, y is determined independently from x even if it
+         * is Inf.
+         */
+        if (FPzero(l2->A))
+            y = -float8_div(l2->C, l2->B);
+        else
+            y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
     }
     else
         return false;
@@ -1639,8 +1713,8 @@ path_inter(PG_FUNCTION_ARGS)
     {
         b1.high.x = float8_max(p1->p[i].x, b1.high.x);
         b1.high.y = float8_max(p1->p[i].y, b1.high.y);
-        b1.low.x = float8_min(p1->p[i].x, b1.low.x);
-        b1.low.y = float8_min(p1->p[i].y, b1.low.y);
+        b1.low.x = float8_min_nan(p1->p[i].x, b1.low.x);
+        b1.low.y = float8_min_nan(p1->p[i].y, b1.low.y);
     }
     b2.high.x = b2.low.x = p2->p[0].x;
     b2.high.y = b2.low.y = p2->p[0].y;
@@ -1648,8 +1722,8 @@ path_inter(PG_FUNCTION_ARGS)
     {
         b2.high.x = float8_max(p2->p[i].x, b2.high.x);
         b2.high.y = float8_max(p2->p[i].y, b2.high.y);
-        b2.low.x = float8_min(p2->p[i].x, b2.low.x);
-        b2.low.y = float8_min(p2->p[i].y, b2.low.y);
+        b2.low.x = float8_min_nan(p2->p[i].x, b2.low.x);
+        b2.low.y = float8_min_nan(p2->p[i].y, b2.low.y);
     }
     if (!box_ov(&b1, &b2))
         PG_RETURN_BOOL(false);
@@ -1739,6 +1813,11 @@ path_distance(PG_FUNCTION_ARGS)
             statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
 
             tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
+
+            /* return NULL immediately if NaN is involved */
+            if (isnan(tmp))
+                PG_RETURN_NULL();
+
             if (!have_min || float8_lt(tmp, min))
             {
                 min = tmp;
@@ -1992,9 +2071,19 @@ static inline float8
 point_sl(Point *pt1, Point *pt2)
 {
     if (FPeq(pt1->x, pt2->x))
+    {
+        if (unlikely(isnan(pt1->y) || isnan(pt2->y)))
+            return get_float8_nan();
         return get_float8_infinity();
+    }
+
     if (FPeq(pt1->y, pt2->y))
+    {
+        if (unlikely(isnan(pt1->x) || isnan(pt2->x)))
+            return get_float8_nan();
         return 0.0;
+    }
+
     return float8_div(float8_mi(pt1->y, pt2->y), float8_mi(pt1->x, pt2->x));
 }
 
@@ -2008,9 +2097,19 @@ static inline float8
 point_invsl(Point *pt1, Point *pt2)
 {
     if (FPeq(pt1->x, pt2->x))
+    {
+        if (unlikely(isnan(pt1->y) || isnan(pt2->y)))
+            return get_float8_nan();
         return 0.0;
+    }
+
     if (FPeq(pt1->y, pt2->y))
+    {
+        if (unlikely(isnan(pt1->x) || isnan(pt2->x)))
+            return get_float8_nan();
         return get_float8_infinity();
+    }
+
     return float8_div(float8_mi(pt1->x, pt2->x), float8_mi(pt2->y, pt1->y));
 }
 
@@ -2426,6 +2525,11 @@ dist_ppath_internal(Point *pt, PATH *path)
 
         statlseg_construct(&lseg, &path->p[iprev], &path->p[i]);
         tmp = lseg_closept_point(NULL, &lseg, pt);
+
+        /* return NaN if NaN is involved */
+        if (unlikely(isnan(tmp)))
+            return tmp;
+
         if (!have_min || float8_lt(tmp, result))
         {
             result = tmp;
@@ -2619,6 +2723,8 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
         d = lseg_closept_point(NULL, &seg, pt);
         if (float8_lt(d, result))
             result = d;
+        else if (unlikely(isnan(d)))
+            return get_float8_nan();
     }
 
     return result;
@@ -2648,7 +2754,8 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
      * intersection point, we are done.
      */
     line_construct(&tmp, &lseg->p[0], lseg_sl(lseg));
-    if (!line_interpt_line(&interpt, &tmp, line))
+    if (!line_interpt_line(&interpt, &tmp, line) ||
+        unlikely(isnan(interpt.x) || isnan(interpt.y)))
         return false;
 
     /*
@@ -2690,6 +2797,78 @@ line_closept_point(Point *result, LINE *line, Point *point)
 {
     Point        closept;
     LINE        tmp;
+    float8        val;
+
+    /*
+     * Treat specially the cases where A = 0 and B = 0, which we regard the
+     * line is perfectly horizontal and vertical correspondingly, not applying
+     * the normal formula involving the behavior of inf*0 =NaN defined by
+     * IEEE754.
+     */
+    if (FPzero(line->A))
+    {
+        float8 y;
+
+        /* the line is horizontal */
+        Assert(!FPzero(line->B));
+
+        y = -line->C / line->B;
+
+        /* Avoid -0 */
+        if (y == 0.0)
+            y = 0.0;
+
+        if (result != NULL)
+        {
+            result->x = point->x;
+            result->y = y;
+        }
+
+        if (unlikely(isnan(point->x)))
+            return get_float8_nan();
+
+        return fabs(point->y - y);
+    }
+    else if (FPzero(line->B))
+    {
+        float x;
+
+        /* the line is virtical */
+        Assert(!FPzero(line->A));
+
+        x = -line->C / line->A;
+
+        /* Avoid -0 */
+        if (x == 0)
+            x = 0;
+
+        if (result != NULL)
+        {
+            result->x = x;
+            result->y = point->y;
+        }
+
+        if (unlikely(isnan(point->y)))
+            return get_float8_nan();
+
+        return fabs(point->x - x);
+    }
+
+    /*
+     * If it is unclear whether the point is on the line or not, then the
+     * results are ill-defined.  This eliminates cases where any of the given
+     * coordinates are NaN, as well as cases where infinite coordinates give
+     * rise to Inf - Inf, 0 * Inf, etc.
+     */
+    val = float8_pl(float8_pl(float8_mul(line->A, point->x),
+                              float8_mul(line->B, point->y)),
+                    line->C);
+    if (unlikely(isnan(val)))
+    {
+        if (result != NULL)
+            result->x = result->y = get_float8_nan();
+        return get_float8_nan();
+    }
 
     /*
      * We drop a perpendicular to find the intersection point.  Ordinarily we
@@ -2700,7 +2879,7 @@ line_closept_point(Point *result, LINE *line, Point *point)
     if (!line_interpt_line(&closept, &tmp, line))
     {
         if (result != NULL)
-            *result = *point;
+            result->x = result->y = get_float8_nan();
 
         return get_float8_nan();
     }
@@ -2708,7 +2887,17 @@ line_closept_point(Point *result, LINE *line, Point *point)
     if (result != NULL)
         *result = closept;
 
-    return point_dt(&closept, point);
+    /*
+     * If the closest point contains Inf's, the closest point may lose
+     * arithmetic relationship with the point which lets point_dt wrongly give
+     * NaN. To avoid that kind of failure, we calculate the distance using the
+     * following formula instead of using the closest point.
+     *
+     * | A*x + B*y + C | / sqrt(A^2 + B^2)
+     *
+     * The dividend have been already calculated so just divide it.
+     */
+    return float8_div(fabs(val), HYPOT(line->A, line->B));
 }
 
 Datum
@@ -2720,7 +2909,9 @@ close_pl(PG_FUNCTION_ARGS)
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (isnan(line_closept_point(result, line, pt)))
+    (void) line_closept_point(result, line, pt);
+
+    if (unlikely(isnan(result->x) || isnan(result->y)))
         PG_RETURN_NULL();
 
     PG_RETURN_POINT_P(result);
@@ -2777,6 +2968,7 @@ lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
     Point        point;
     float8        dist,
                 d;
+    bool        anynan = false;
 
     /* First, we handle the case when the line segments are intersecting. */
     if (lseg_interpt_lseg(result, on_lseg, to_lseg))
@@ -2788,6 +2980,7 @@ lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
      */
     dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]);
     d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]);
+    anynan |= (isnan(dist) || isnan(d));
     if (float8_lt(d, dist))
     {
         dist = d;
@@ -2797,6 +2990,7 @@ lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
 
     /* The closest point can still be one of the endpoints, so we test them. */
     d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]);
+    anynan |= isnan(d);
     if (float8_lt(d, dist))
     {
         dist = d;
@@ -2804,6 +2998,7 @@ lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
             *result = on_lseg->p[0];
     }
     d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]);
+    anynan |= isnan(d);
     if (float8_lt(d, dist))
     {
         dist = d;
@@ -2811,6 +3006,12 @@ lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
             *result = on_lseg->p[1];
     }
 
+    if (unlikely(anynan))
+    {
+        if (result != NULL)
+            result->x = result->y = get_float8_nan();
+        return get_float8_nan();
+    }
     return dist;
 }
 
@@ -2847,6 +3048,7 @@ box_closept_point(Point *result, BOX *box, Point *pt)
     Point        point,
                 closept;
     LSEG        lseg;
+    bool        anynan = false;
 
     if (box_contain_point(box, pt))
     {
@@ -2861,9 +3063,10 @@ box_closept_point(Point *result, BOX *box, Point *pt)
     point.y = box->high.y;
     statlseg_construct(&lseg, &box->low, &point);
     dist = lseg_closept_point(result, &lseg, pt);
-
+    anynan |= isnan(dist);
     statlseg_construct(&lseg, &box->high, &point);
     d = lseg_closept_point(&closept, &lseg, pt);
+    anynan |= isnan(d);
     if (float8_lt(d, dist))
     {
         dist = d;
@@ -2875,6 +3078,7 @@ box_closept_point(Point *result, BOX *box, Point *pt)
     point.y = box->low.y;
     statlseg_construct(&lseg, &box->low, &point);
     d = lseg_closept_point(&closept, &lseg, pt);
+    anynan |= isnan(d);
     if (float8_lt(d, dist))
     {
         dist = d;
@@ -2884,6 +3088,7 @@ box_closept_point(Point *result, BOX *box, Point *pt)
 
     statlseg_construct(&lseg, &box->high, &point);
     d = lseg_closept_point(&closept, &lseg, pt);
+    anynan |= isnan(d);
     if (float8_lt(d, dist))
     {
         dist = d;
@@ -2891,6 +3096,13 @@ box_closept_point(Point *result, BOX *box, Point *pt)
             *result = closept;
     }
 
+    if (unlikely(anynan))
+    {
+        if (result != NULL)
+            result->x = result->y = get_float8_nan();
+        return get_float8_nan();
+    }
+
     return dist;
 }
 
@@ -2920,6 +3132,7 @@ close_pb(PG_FUNCTION_ARGS)
  * even because of simple roundoff issues, there may not be a single closest
  * point.  We are likely to set the result to the second endpoint in these
  * cases.
+ * Returns Nan and stores {NaN,NaN} to result if NaN is involved,
  */
 static float8
 lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
@@ -2942,6 +3155,14 @@ lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
     }
     else
     {
+        /* return NaN if any of the two is NaN */
+        if (unlikely(isnan(dist1) || isnan(dist2)))
+        {
+            if (result != NULL)
+                result->x = result->y = get_float8_nan();
+            return get_float8_nan();
+        }
+
         if (result != NULL)
             *result = lseg->p[1];
 
@@ -3051,9 +3272,32 @@ close_sb(PG_FUNCTION_ARGS)
 static bool
 line_contain_point(LINE *line, Point *point)
 {
-    return FPzero(float8_pl(float8_pl(float8_mul(line->A, point->x),
-                                      float8_mul(line->B, point->y)),
-                            line->C));
+    /*
+     * Treat specially way the cases where A = 0 and B = 0, which we regard the
+     * line is perfectly horizontal and vertical correspondingly to avoid the
+     * normal formula based on the IEEE754's definitions yields unwanted NaNs.
+     */
+    if (line->A == 0.0)
+    {
+        /* the line is horizontal */
+        Assert(line->B != 0.0);
+
+        /* inf == inf here */
+        return FPeq(point->y, -line->C / line->B);
+    }
+    else if (line->B == 0.0)
+    {
+        /* the line is vertical */
+        Assert(line->A != 0.0);
+
+        /* inf == inf here */
+        return FPeq(point->x, -line->C / line->A);
+    }
+
+    return FPzero(float8_pl(
+                      float8_pl(float8_mul(line->A, point->x),
+                                float8_mul(line->B, point->y)),
+                      line->C));
 }
 
 Datum
@@ -3352,6 +3596,12 @@ make_bound_box(POLYGON *poly)
     y2 = y1 = poly->p[0].y;
     for (i = 1; i < poly->npts; i++)
     {
+        /* if NaN found, make an invalid boundbox */
+        if (unlikely(isnan(poly->p[i].x) || isnan(poly->p[i].y)))
+        {
+            x1 = x2 = y1 = y2 = get_float8_nan();
+            break;
+        }
         if (float8_lt(poly->p[i].x, x1))
             x1 = poly->p[i].x;
         if (float8_gt(poly->p[i].x, x2))
@@ -3837,6 +4087,11 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
     t.p[1] = *b;
     s.p[0] = poly->p[(start == 0) ? (poly->npts - 1) : (start - 1)];
 
+    /* Fast path. Check against boundbox. Also checks NaNs. */
+    if (!box_contain_point(&poly->boundbox, a) ||
+        !box_contain_point(&poly->boundbox, b))
+        return false;
+
     for (i = start; i < poly->npts && res; i++)
     {
         Point        interpt;
@@ -5309,6 +5564,10 @@ point_inside(Point *p, int npts, Point *plist)
     x0 = float8_mi(plist[0].x, p->x);
     y0 = float8_mi(plist[0].y, p->y);
 
+    /* NaN makes the point cannot be inside the polygon */
+    if (unlikely(isnan(x0) || isnan(y0) || isnan(p->x) || isnan(p->y)))
+        return 0;
+
     prev_x = x0;
     prev_y = y0;
     /* loop over polygon points and aggregate total_cross */
@@ -5318,6 +5577,10 @@ point_inside(Point *p, int npts, Point *plist)
         x = float8_mi(plist[i].x, p->x);
         y = float8_mi(plist[i].y, p->y);
 
+        /* NaN makes the point cannot be inside the polygon */
+        if (unlikely(isnan(x) || isnan(y)))
+            return 0;
+
         /* compute previous to current point crossing */
         if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
             return 2;
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index fcf7bd581b..4ab3f9d8ef 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -353,4 +353,26 @@ float8_max(const float8 val1, const float8 val2)
     return float8_gt(val1, val2) ? val1 : val2;
 }
 
+/*
+ * These two functions return NaN if either input is NaN, else the smaller
+ * of the two inputs.  This does NOT follow our usual sort rule, but it is
+ * convenient in some places.  (Note that float4_max and float8_max act this
+ * way anyway, so no similar variant is needed for them.)
+ */
+static inline float4
+float4_min_nan(const float4 val1, const float4 val2)
+{
+    return (isnan(val1) ? val1 :
+            (isnan(val2) ? val2 :
+             (val1 < val2 ? val1 : val2)));
+}
+
+static inline float8
+float8_min_nan(const float8 val1, const float8 val2)
+{
+    return (isnan(val1) ? val1 :
+            (isnan(val2) ? val2 :
+             (val1 < val2 ? val1 : val2)));
+}
+
 #endif                            /* FLOAT_H */
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index daf75dd5c4..5e77e0937d 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -139,7 +139,7 @@ SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
 SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)';
  count 
 -------
-     5
+     4
 (1 row)
 
 SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out
index d91d829f0f..77ff1b3d7b 100644
--- a/src/test/regress/expected/geometry.out
+++ b/src/test/regress/expected/geometry.out
@@ -517,7 +517,7 @@ SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TB
  (-3,4)            | {0,3,0}                               |                  4 |                  4
  (-3,4)            | {1,-1,0}                              |      4.94974746831 |      4.94974746831
  (-3,4)            | {-0.4,-1,-6}                          |      8.17059487979 |      8.17059487979
- (-3,4)            | {-0.000184615384615,-1,15.3846153846} |      11.3851690368 |      11.3851690368
+ (-3,4)            | {-0.000184615384615,-1,15.3846153846} |      11.3851690367 |      11.3851690367
  (-3,4)            | {3,NaN,5}                             |                NaN |                NaN
  (-3,4)            | {NaN,NaN,NaN}                         |                NaN |                NaN
  (-3,4)            | {0,-1,3}                              |                  1 |                  1
@@ -537,7 +537,7 @@ SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TB
  (-5,-12)          | {0,3,0}                               |                 12 |                 12
  (-5,-12)          | {1,-1,0}                              |      4.94974746831 |      4.94974746831
  (-5,-12)          | {-0.4,-1,-6}                          |      7.42781352708 |      7.42781352708
- (-5,-12)          | {-0.000184615384615,-1,15.3846153846} |      27.3855379948 |      27.3855379948
+ (-5,-12)          | {-0.000184615384615,-1,15.3846153846} |      27.3855379949 |      27.3855379949
  (-5,-12)          | {3,NaN,5}                             |                NaN |                NaN
  (-5,-12)          | {NaN,NaN,NaN}                         |                NaN |                NaN
  (-5,-12)          | {0,-1,3}                              |                 15 |                 15
@@ -553,7 +553,7 @@ SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TB
  (1e-300,-1e-300)  | {0,-1,3}                              |                  3 |                  3
  (1e-300,-1e-300)  | {-1,0,3}                              |                  3 |                  3
  (1e+300,Infinity) | {0,-1,5}                              |           Infinity |           Infinity
- (1e+300,Infinity) | {1,0,5}                               |                NaN |                NaN
+ (1e+300,Infinity) | {1,0,5}                               |             1e+300 |             1e+300
  (1e+300,Infinity) | {0,3,0}                               |           Infinity |           Infinity
  (1e+300,Infinity) | {1,-1,0}                              |           Infinity |           Infinity
  (1e+300,Infinity) | {-0.4,-1,-6}                          |           Infinity |           Infinity
@@ -561,16 +561,16 @@ SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TB
  (1e+300,Infinity) | {3,NaN,5}                             |                NaN |                NaN
  (1e+300,Infinity) | {NaN,NaN,NaN}                         |                NaN |                NaN
  (1e+300,Infinity) | {0,-1,3}                              |           Infinity |           Infinity
- (1e+300,Infinity) | {-1,0,3}                              |                NaN |                NaN
- (Infinity,1e+300) | {0,-1,5}                              |                NaN |                NaN
+ (1e+300,Infinity) | {-1,0,3}                              |             1e+300 |             1e+300
+ (Infinity,1e+300) | {0,-1,5}                              |             1e+300 |             1e+300
  (Infinity,1e+300) | {1,0,5}                               |           Infinity |           Infinity
- (Infinity,1e+300) | {0,3,0}                               |                NaN |                NaN
- (Infinity,1e+300) | {1,-1,0}                              |                NaN |                NaN
- (Infinity,1e+300) | {-0.4,-1,-6}                          |                NaN |                NaN
- (Infinity,1e+300) | {-0.000184615384615,-1,15.3846153846} |                NaN |                NaN
+ (Infinity,1e+300) | {0,3,0}                               |             1e+300 |             1e+300
+ (Infinity,1e+300) | {1,-1,0}                              |           Infinity |           Infinity
+ (Infinity,1e+300) | {-0.4,-1,-6}                          |           Infinity |           Infinity
+ (Infinity,1e+300) | {-0.000184615384615,-1,15.3846153846} |           Infinity |           Infinity
  (Infinity,1e+300) | {3,NaN,5}                             |                NaN |                NaN
  (Infinity,1e+300) | {NaN,NaN,NaN}                         |                NaN |                NaN
- (Infinity,1e+300) | {0,-1,3}                              |                NaN |                NaN
+ (Infinity,1e+300) | {0,-1,3}                              |             1e+300 |             1e+300
  (Infinity,1e+300) | {-1,0,3}                              |           Infinity |           Infinity
  (NaN,NaN)         | {0,-1,5}                              |                NaN |                NaN
  (NaN,NaN)         | {1,0,5}                               |                NaN |                NaN
@@ -587,7 +587,7 @@ SELECT p.f1, l.s, p.f1 <-> l.s AS dist_pl, l.s <-> p.f1 AS dist_lp FROM POINT_TB
  (10,10)           | {0,3,0}                               |                 10 |                 10
  (10,10)           | {1,-1,0}                              |                  0 |                  0
  (10,10)           | {-0.4,-1,-6}                          |      18.5695338177 |      18.5695338177
- (10,10)           | {-0.000184615384615,-1,15.3846153846} |      5.38276913903 |      5.38276913903
+ (10,10)           | {-0.000184615384615,-1,15.3846153846} |      5.38276913904 |      5.38276913904
  (10,10)           | {3,NaN,5}                             |                NaN |                NaN
  (10,10)           | {NaN,NaN,NaN}                         |                NaN |                NaN
  (10,10)           | {0,-1,3}                              |                  7 |                  7
@@ -653,7 +653,7 @@ SELECT p.f1, l.s, p.f1 <-> l.s AS dist_ps, l.s <-> p.f1 AS dist_sp FROM POINT_TB
  (1e+300,Infinity) | [(11,22),(33,44)]             |           Infinity |           Infinity
  (1e+300,Infinity) | [(-10,2),(-10,3)]             |           Infinity |           Infinity
  (1e+300,Infinity) | [(0,-20),(30,-20)]            |           Infinity |           Infinity
- (1e+300,Infinity) | [(NaN,1),(NaN,90)]            |           Infinity |           Infinity
+ (1e+300,Infinity) | [(NaN,1),(NaN,90)]            |                NaN |                NaN
  (Infinity,1e+300) | [(1,2),(3,4)]                 |           Infinity |           Infinity
  (Infinity,1e+300) | [(0,0),(6,6)]                 |           Infinity |           Infinity
  (Infinity,1e+300) | [(10,-10),(-3,-4)]            |           Infinity |           Infinity
@@ -892,13 +892,13 @@ SELECT p.f1, p1.f1, p.f1 <-> p1.f1 AS dist_ppoly, p1.f1 <-> p.f1 AS dist_polyp F
  (Infinity,1e+300) | ((1,2),(7,8),(5,6),(3,-4)) |      Infinity |      Infinity
  (Infinity,1e+300) | ((0,0))                    |      Infinity |      Infinity
  (Infinity,1e+300) | ((0,1),(0,1))              |      Infinity |      Infinity
- (NaN,NaN)         | ((2,0),(2,4),(0,0))        |             0 |             0
- (NaN,NaN)         | ((3,1),(3,3),(1,0))        |             0 |             0
- (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  |             0 |             0
- (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  |             0 |             0
- (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) |             0 |             0
- (NaN,NaN)         | ((0,0))                    |             0 |             0
- (NaN,NaN)         | ((0,1),(0,1))              |             0 |             0
+ (NaN,NaN)         | ((2,0),(2,4),(0,0))        |           NaN |           NaN
+ (NaN,NaN)         | ((3,1),(3,3),(1,0))        |           NaN |           NaN
+ (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  |           NaN |           NaN
+ (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  |           NaN |           NaN
+ (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) |           NaN |           NaN
+ (NaN,NaN)         | ((0,0))                    |           NaN |           NaN
+ (NaN,NaN)         | ((0,1),(0,1))              |           NaN |           NaN
  (10,10)           | ((2,0),(2,4),(0,0))        |            10 |            10
  (10,10)           | ((3,1),(3,3),(1,0))        | 9.89949493661 | 9.89949493661
  (10,10)           | ((1,2),(3,4),(5,6),(7,8))  | 3.60555127546 | 3.60555127546
@@ -919,7 +919,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (0,0)             | (-5,-12)          | {2.4,-1,0}
  (0,0)             | (1e+300,Infinity) | {-1,0,0}
  (0,0)             | (Infinity,1e+300) | {0,-1,0}
- (0,0)             | (NaN,NaN)         | {NaN,-1,NaN}
+ (0,0)             | (NaN,NaN)         | {NaN,NaN,NaN}
  (0,0)             | (10,10)           | {1,-1,0}
  (-10,0)           | (0,0)             | {0,-1,0}
  (-10,0)           | (-3,4)            | {0.571428571429,-1,5.71428571429}
@@ -928,7 +928,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (-10,0)           | (1e-300,-1e-300)  | {0,-1,0}
  (-10,0)           | (1e+300,Infinity) | {-1,0,-10}
  (-10,0)           | (Infinity,1e+300) | {0,-1,0}
- (-10,0)           | (NaN,NaN)         | {NaN,-1,NaN}
+ (-10,0)           | (NaN,NaN)         | {NaN,NaN,NaN}
  (-10,0)           | (10,10)           | {0.5,-1,5}
  (-3,4)            | (0,0)             | {-1.33333333333,-1,0}
  (-3,4)            | (-10,0)           | {0.571428571429,-1,5.71428571429}
@@ -937,7 +937,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (-3,4)            | (1e-300,-1e-300)  | {-1.33333333333,-1,0}
  (-3,4)            | (1e+300,Infinity) | {-1,0,-3}
  (-3,4)            | (Infinity,1e+300) | {0,-1,4}
- (-3,4)            | (NaN,NaN)         | {NaN,-1,NaN}
+ (-3,4)            | (NaN,NaN)         | {NaN,NaN,NaN}
  (-3,4)            | (10,10)           | {0.461538461538,-1,5.38461538462}
  (5.1,34.5)        | (0,0)             | {6.76470588235,-1,0}
  (5.1,34.5)        | (-10,0)           | {2.28476821192,-1,22.8476821192}
@@ -946,7 +946,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (5.1,34.5)        | (1e-300,-1e-300)  | {6.76470588235,-1,0}
  (5.1,34.5)        | (1e+300,Infinity) | {-1,0,5.1}
  (5.1,34.5)        | (Infinity,1e+300) | {0,-1,34.5}
- (5.1,34.5)        | (NaN,NaN)         | {NaN,-1,NaN}
+ (5.1,34.5)        | (NaN,NaN)         | {NaN,NaN,NaN}
  (5.1,34.5)        | (10,10)           | {-5,-1,60}
  (-5,-12)          | (0,0)             | {2.4,-1,0}
  (-5,-12)          | (-10,0)           | {-2.4,-1,-24}
@@ -955,7 +955,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (-5,-12)          | (1e-300,-1e-300)  | {2.4,-1,0}
  (-5,-12)          | (1e+300,Infinity) | {-1,0,-5}
  (-5,-12)          | (Infinity,1e+300) | {0,-1,-12}
- (-5,-12)          | (NaN,NaN)         | {NaN,-1,NaN}
+ (-5,-12)          | (NaN,NaN)         | {NaN,NaN,NaN}
  (-5,-12)          | (10,10)           | {1.46666666667,-1,-4.66666666667}
  (1e-300,-1e-300)  | (-10,0)           | {0,-1,-1e-300}
  (1e-300,-1e-300)  | (-3,4)            | {-1.33333333333,-1,3.33333333333e-301}
@@ -963,7 +963,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (1e-300,-1e-300)  | (-5,-12)          | {2.4,-1,-3.4e-300}
  (1e-300,-1e-300)  | (1e+300,Infinity) | {-1,0,1e-300}
  (1e-300,-1e-300)  | (Infinity,1e+300) | {0,-1,-1e-300}
- (1e-300,-1e-300)  | (NaN,NaN)         | {NaN,-1,NaN}
+ (1e-300,-1e-300)  | (NaN,NaN)         | {NaN,NaN,NaN}
  (1e-300,-1e-300)  | (10,10)           | {1,-1,-2e-300}
  (1e+300,Infinity) | (0,0)             | {-1,0,1e+300}
  (1e+300,Infinity) | (-10,0)           | {-1,0,1e+300}
@@ -971,8 +971,8 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (1e+300,Infinity) | (5.1,34.5)        | {-1,0,1e+300}
  (1e+300,Infinity) | (-5,-12)          | {-1,0,1e+300}
  (1e+300,Infinity) | (1e-300,-1e-300)  | {-1,0,1e+300}
- (1e+300,Infinity) | (Infinity,1e+300) | {NaN,-1,NaN}
- (1e+300,Infinity) | (NaN,NaN)         | {NaN,-1,NaN}
+ (1e+300,Infinity) | (Infinity,1e+300) | {NaN,NaN,NaN}
+ (1e+300,Infinity) | (NaN,NaN)         | {NaN,NaN,NaN}
  (1e+300,Infinity) | (10,10)           | {-1,0,1e+300}
  (Infinity,1e+300) | (0,0)             | {0,-1,1e+300}
  (Infinity,1e+300) | (-10,0)           | {0,-1,1e+300}
@@ -980,18 +980,18 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (Infinity,1e+300) | (5.1,34.5)        | {0,-1,1e+300}
  (Infinity,1e+300) | (-5,-12)          | {0,-1,1e+300}
  (Infinity,1e+300) | (1e-300,-1e-300)  | {0,-1,1e+300}
- (Infinity,1e+300) | (1e+300,Infinity) | {NaN,-1,NaN}
- (Infinity,1e+300) | (NaN,NaN)         | {NaN,-1,NaN}
+ (Infinity,1e+300) | (1e+300,Infinity) | {NaN,NaN,NaN}
+ (Infinity,1e+300) | (NaN,NaN)         | {NaN,NaN,NaN}
  (Infinity,1e+300) | (10,10)           | {0,-1,1e+300}
- (NaN,NaN)         | (0,0)             | {NaN,-1,NaN}
- (NaN,NaN)         | (-10,0)           | {NaN,-1,NaN}
- (NaN,NaN)         | (-3,4)            | {NaN,-1,NaN}
- (NaN,NaN)         | (5.1,34.5)        | {NaN,-1,NaN}
- (NaN,NaN)         | (-5,-12)          | {NaN,-1,NaN}
- (NaN,NaN)         | (1e-300,-1e-300)  | {NaN,-1,NaN}
- (NaN,NaN)         | (1e+300,Infinity) | {NaN,-1,NaN}
- (NaN,NaN)         | (Infinity,1e+300) | {NaN,-1,NaN}
- (NaN,NaN)         | (10,10)           | {NaN,-1,NaN}
+ (NaN,NaN)         | (0,0)             | {NaN,NaN,NaN}
+ (NaN,NaN)         | (-10,0)           | {NaN,NaN,NaN}
+ (NaN,NaN)         | (-3,4)            | {NaN,NaN,NaN}
+ (NaN,NaN)         | (5.1,34.5)        | {NaN,NaN,NaN}
+ (NaN,NaN)         | (-5,-12)          | {NaN,NaN,NaN}
+ (NaN,NaN)         | (1e-300,-1e-300)  | {NaN,NaN,NaN}
+ (NaN,NaN)         | (1e+300,Infinity) | {NaN,NaN,NaN}
+ (NaN,NaN)         | (Infinity,1e+300) | {NaN,NaN,NaN}
+ (NaN,NaN)         | (10,10)           | {NaN,NaN,NaN}
  (10,10)           | (0,0)             | {1,-1,0}
  (10,10)           | (-10,0)           | {0.5,-1,5}
  (10,10)           | (-3,4)            | {0.461538461538,-1,5.38461538462}
@@ -1000,7 +1000,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (10,10)           | (1e-300,-1e-300)  | {1,-1,0}
  (10,10)           | (1e+300,Infinity) | {-1,0,10}
  (10,10)           | (Infinity,1e+300) | {0,-1,10}
- (10,10)           | (NaN,NaN)         | {NaN,-1,NaN}
+ (10,10)           | (NaN,NaN)         | {NaN,NaN,NaN}
 (88 rows)
 
 -- Closest point to line
@@ -1068,24 +1068,24 @@ SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LINE_TBL l;
  (1e-300,-1e-300)  | {0,-1,3}                              | (1e-300,3)
  (1e-300,-1e-300)  | {-1,0,3}                              | (3,-1e-300)
  (1e+300,Infinity) | {0,-1,5}                              | (1e+300,5)
- (1e+300,Infinity) | {1,0,5}                               | 
+ (1e+300,Infinity) | {1,0,5}                               | (-5,Infinity)
  (1e+300,Infinity) | {0,3,0}                               | (1e+300,0)
- (1e+300,Infinity) | {1,-1,0}                              | (Infinity,NaN)
- (1e+300,Infinity) | {-0.4,-1,-6}                          | (-Infinity,NaN)
- (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | (-Infinity,NaN)
+ (1e+300,Infinity) | {1,-1,0}                              | (Infinity,Infinity)
+ (1e+300,Infinity) | {-0.4,-1,-6}                          | (-Infinity,Infinity)
+ (1e+300,Infinity) | {-0.000184615384615,-1,15.3846153846} | (-Infinity,Infinity)
  (1e+300,Infinity) | {3,NaN,5}                             | 
  (1e+300,Infinity) | {NaN,NaN,NaN}                         | 
  (1e+300,Infinity) | {0,-1,3}                              | (1e+300,3)
- (1e+300,Infinity) | {-1,0,3}                              | 
- (Infinity,1e+300) | {0,-1,5}                              | 
+ (1e+300,Infinity) | {-1,0,3}                              | (3,Infinity)
+ (Infinity,1e+300) | {0,-1,5}                              | (Infinity,5)
  (Infinity,1e+300) | {1,0,5}                               | (-5,1e+300)
- (Infinity,1e+300) | {0,3,0}                               | 
- (Infinity,1e+300) | {1,-1,0}                              | 
- (Infinity,1e+300) | {-0.4,-1,-6}                          | 
- (Infinity,1e+300) | {-0.000184615384615,-1,15.3846153846} | 
+ (Infinity,1e+300) | {0,3,0}                               | (Infinity,0)
+ (Infinity,1e+300) | {1,-1,0}                              | (Infinity,Infinity)
+ (Infinity,1e+300) | {-0.4,-1,-6}                          | (Infinity,-Infinity)
+ (Infinity,1e+300) | {-0.000184615384615,-1,15.3846153846} | (Infinity,-Infinity)
  (Infinity,1e+300) | {3,NaN,5}                             | 
  (Infinity,1e+300) | {NaN,NaN,NaN}                         | 
- (Infinity,1e+300) | {0,-1,3}                              | 
+ (Infinity,1e+300) | {0,-1,3}                              | (Infinity,3)
  (Infinity,1e+300) | {-1,0,3}                              | (3,1e+300)
  (NaN,NaN)         | {0,-1,5}                              | 
  (NaN,NaN)         | {1,0,5}                               | 
@@ -1168,7 +1168,7 @@ SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LSEG_TBL l;
  (1e+300,Infinity) | [(11,22),(33,44)]             | (33,44)
  (1e+300,Infinity) | [(-10,2),(-10,3)]             | (-10,3)
  (1e+300,Infinity) | [(0,-20),(30,-20)]            | (30,-20)
- (1e+300,Infinity) | [(NaN,1),(NaN,90)]            | (NaN,90)
+ (1e+300,Infinity) | [(NaN,1),(NaN,90)]            | 
  (Infinity,1e+300) | [(1,2),(3,4)]                 | (3,4)
  (Infinity,1e+300) | [(0,0),(6,6)]                 | (6,6)
  (Infinity,1e+300) | [(10,-10),(-3,-4)]            | (-3,-4)
@@ -1278,12 +1278,7 @@ SELECT p.f1, p1.f1 FROM POINT_TBL p, PATH_TBL p1 WHERE p.f1 <@ p1.f1;
 ------------------+---------------------------
  (0,0)            | [(0,0),(3,0),(4,5),(1,6)]
  (1e-300,-1e-300) | [(0,0),(3,0),(4,5),(1,6)]
- (NaN,NaN)        | ((1,2),(3,4))
- (NaN,NaN)        | ((1,2),(3,4))
- (NaN,NaN)        | ((1,2),(3,4))
- (NaN,NaN)        | ((10,20))
- (NaN,NaN)        | ((11,12),(13,14))
-(7 rows)
+(2 rows)
 
 --
 -- Lines
@@ -1371,8 +1366,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {0,-1,5}                              | {1,-1,0}                              |        0
  {0,-1,5}                              | {-0.4,-1,-6}                          |        0
  {0,-1,5}                              | {-0.000184615384615,-1,15.3846153846} |        0
- {0,-1,5}                              | {3,NaN,5}                             |        0
- {0,-1,5}                              | {NaN,NaN,NaN}                         |        0
+ {0,-1,5}                              | {3,NaN,5}                             |      NaN
+ {0,-1,5}                              | {NaN,NaN,NaN}                         |      NaN
  {0,-1,5}                              | {0,-1,3}                              |        2
  {0,-1,5}                              | {-1,0,3}                              |        0
  {1,0,5}                               | {0,-1,5}                              |        0
@@ -1381,8 +1376,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {1,0,5}                               | {1,-1,0}                              |        0
  {1,0,5}                               | {-0.4,-1,-6}                          |        0
  {1,0,5}                               | {-0.000184615384615,-1,15.3846153846} |        0
- {1,0,5}                               | {3,NaN,5}                             |        0
- {1,0,5}                               | {NaN,NaN,NaN}                         |        0
+ {1,0,5}                               | {3,NaN,5}                             |      NaN
+ {1,0,5}                               | {NaN,NaN,NaN}                         |      NaN
  {1,0,5}                               | {0,-1,3}                              |        0
  {1,0,5}                               | {-1,0,3}                              |        8
  {0,3,0}                               | {0,-1,5}                              |        5
@@ -1391,8 +1386,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {0,3,0}                               | {1,-1,0}                              |        0
  {0,3,0}                               | {-0.4,-1,-6}                          |        0
  {0,3,0}                               | {-0.000184615384615,-1,15.3846153846} |        0
- {0,3,0}                               | {3,NaN,5}                             |        0
- {0,3,0}                               | {NaN,NaN,NaN}                         |        0
+ {0,3,0}                               | {3,NaN,5}                             |      NaN
+ {0,3,0}                               | {NaN,NaN,NaN}                         |      NaN
  {0,3,0}                               | {0,-1,3}                              |        3
  {0,3,0}                               | {-1,0,3}                              |        0
  {1,-1,0}                              | {0,-1,5}                              |        0
@@ -1401,8 +1396,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {1,-1,0}                              | {1,-1,0}                              |        0
  {1,-1,0}                              | {-0.4,-1,-6}                          |        0
  {1,-1,0}                              | {-0.000184615384615,-1,15.3846153846} |        0
- {1,-1,0}                              | {3,NaN,5}                             |        0
- {1,-1,0}                              | {NaN,NaN,NaN}                         |        0
+ {1,-1,0}                              | {3,NaN,5}                             |      NaN
+ {1,-1,0}                              | {NaN,NaN,NaN}                         |      NaN
  {1,-1,0}                              | {0,-1,3}                              |        0
  {1,-1,0}                              | {-1,0,3}                              |        0
  {-0.4,-1,-6}                          | {0,-1,5}                              |        0
@@ -1411,8 +1406,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {-0.4,-1,-6}                          | {1,-1,0}                              |        0
  {-0.4,-1,-6}                          | {-0.4,-1,-6}                          |        0
  {-0.4,-1,-6}                          | {-0.000184615384615,-1,15.3846153846} |        0
- {-0.4,-1,-6}                          | {3,NaN,5}                             |        0
- {-0.4,-1,-6}                          | {NaN,NaN,NaN}                         |        0
+ {-0.4,-1,-6}                          | {3,NaN,5}                             |      NaN
+ {-0.4,-1,-6}                          | {NaN,NaN,NaN}                         |      NaN
  {-0.4,-1,-6}                          | {0,-1,3}                              |        0
  {-0.4,-1,-6}                          | {-1,0,3}                              |        0
  {-0.000184615384615,-1,15.3846153846} | {0,-1,5}                              |        0
@@ -1421,38 +1416,38 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {-0.000184615384615,-1,15.3846153846} | {1,-1,0}                              |        0
  {-0.000184615384615,-1,15.3846153846} | {-0.4,-1,-6}                          |        0
  {-0.000184615384615,-1,15.3846153846} | {-0.000184615384615,-1,15.3846153846} |        0
- {-0.000184615384615,-1,15.3846153846} | {3,NaN,5}                             |        0
- {-0.000184615384615,-1,15.3846153846} | {NaN,NaN,NaN}                         |        0
+ {-0.000184615384615,-1,15.3846153846} | {3,NaN,5}                             |      NaN
+ {-0.000184615384615,-1,15.3846153846} | {NaN,NaN,NaN}                         |      NaN
  {-0.000184615384615,-1,15.3846153846} | {0,-1,3}                              |        0
  {-0.000184615384615,-1,15.3846153846} | {-1,0,3}                              |        0
- {3,NaN,5}                             | {0,-1,5}                              |        0
- {3,NaN,5}                             | {1,0,5}                               |        0
- {3,NaN,5}                             | {0,3,0}                               |        0
- {3,NaN,5}                             | {1,-1,0}                              |        0
- {3,NaN,5}                             | {-0.4,-1,-6}                          |        0
- {3,NaN,5}                             | {-0.000184615384615,-1,15.3846153846} |        0
- {3,NaN,5}                             | {3,NaN,5}                             |        0
- {3,NaN,5}                             | {NaN,NaN,NaN}                         |        0
- {3,NaN,5}                             | {0,-1,3}                              |        0
- {3,NaN,5}                             | {-1,0,3}                              |        0
- {NaN,NaN,NaN}                         | {0,-1,5}                              |        0
- {NaN,NaN,NaN}                         | {1,0,5}                               |        0
- {NaN,NaN,NaN}                         | {0,3,0}                               |        0
- {NaN,NaN,NaN}                         | {1,-1,0}                              |        0
- {NaN,NaN,NaN}                         | {-0.4,-1,-6}                          |        0
- {NaN,NaN,NaN}                         | {-0.000184615384615,-1,15.3846153846} |        0
- {NaN,NaN,NaN}                         | {3,NaN,5}                             |        0
- {NaN,NaN,NaN}                         | {NaN,NaN,NaN}                         |        0
- {NaN,NaN,NaN}                         | {0,-1,3}                              |        0
- {NaN,NaN,NaN}                         | {-1,0,3}                              |        0
+ {3,NaN,5}                             | {0,-1,5}                              |      NaN
+ {3,NaN,5}                             | {1,0,5}                               |      NaN
+ {3,NaN,5}                             | {0,3,0}                               |      NaN
+ {3,NaN,5}                             | {1,-1,0}                              |      NaN
+ {3,NaN,5}                             | {-0.4,-1,-6}                          |      NaN
+ {3,NaN,5}                             | {-0.000184615384615,-1,15.3846153846} |      NaN
+ {3,NaN,5}                             | {3,NaN,5}                             |      NaN
+ {3,NaN,5}                             | {NaN,NaN,NaN}                         |      NaN
+ {3,NaN,5}                             | {0,-1,3}                              |      NaN
+ {3,NaN,5}                             | {-1,0,3}                              |      NaN
+ {NaN,NaN,NaN}                         | {0,-1,5}                              |      NaN
+ {NaN,NaN,NaN}                         | {1,0,5}                               |      NaN
+ {NaN,NaN,NaN}                         | {0,3,0}                               |      NaN
+ {NaN,NaN,NaN}                         | {1,-1,0}                              |      NaN
+ {NaN,NaN,NaN}                         | {-0.4,-1,-6}                          |      NaN
+ {NaN,NaN,NaN}                         | {-0.000184615384615,-1,15.3846153846} |      NaN
+ {NaN,NaN,NaN}                         | {3,NaN,5}                             |      NaN
+ {NaN,NaN,NaN}                         | {NaN,NaN,NaN}                         |      NaN
+ {NaN,NaN,NaN}                         | {0,-1,3}                              |      NaN
+ {NaN,NaN,NaN}                         | {-1,0,3}                              |      NaN
  {0,-1,3}                              | {0,-1,5}                              |        2
  {0,-1,3}                              | {1,0,5}                               |        0
  {0,-1,3}                              | {0,3,0}                               |        3
  {0,-1,3}                              | {1,-1,0}                              |        0
  {0,-1,3}                              | {-0.4,-1,-6}                          |        0
  {0,-1,3}                              | {-0.000184615384615,-1,15.3846153846} |        0
- {0,-1,3}                              | {3,NaN,5}                             |        0
- {0,-1,3}                              | {NaN,NaN,NaN}                         |        0
+ {0,-1,3}                              | {3,NaN,5}                             |      NaN
+ {0,-1,3}                              | {NaN,NaN,NaN}                         |      NaN
  {0,-1,3}                              | {0,-1,3}                              |        0
  {0,-1,3}                              | {-1,0,3}                              |        0
  {-1,0,3}                              | {0,-1,5}                              |        0
@@ -1461,8 +1456,8 @@ SELECT l1.s, l2.s, l1.s <-> l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {-1,0,3}                              | {1,-1,0}                              |        0
  {-1,0,3}                              | {-0.4,-1,-6}                          |        0
  {-1,0,3}                              | {-0.000184615384615,-1,15.3846153846} |        0
- {-1,0,3}                              | {3,NaN,5}                             |        0
- {-1,0,3}                              | {NaN,NaN,NaN}                         |        0
+ {-1,0,3}                              | {3,NaN,5}                             |      NaN
+ {-1,0,3}                              | {NaN,NaN,NaN}                         |      NaN
  {-1,0,3}                              | {0,-1,3}                              |        0
  {-1,0,3}                              | {-1,0,3}                              |        0
 (100 rows)
@@ -1475,31 +1470,23 @@ SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?# l2.s;
  {0,-1,5}                              | {1,-1,0}
  {0,-1,5}                              | {-0.4,-1,-6}
  {0,-1,5}                              | {-0.000184615384615,-1,15.3846153846}
- {0,-1,5}                              | {3,NaN,5}
- {0,-1,5}                              | {NaN,NaN,NaN}
  {0,-1,5}                              | {-1,0,3}
  {1,0,5}                               | {0,-1,5}
  {1,0,5}                               | {0,3,0}
  {1,0,5}                               | {1,-1,0}
  {1,0,5}                               | {-0.4,-1,-6}
  {1,0,5}                               | {-0.000184615384615,-1,15.3846153846}
- {1,0,5}                               | {3,NaN,5}
- {1,0,5}                               | {NaN,NaN,NaN}
  {1,0,5}                               | {0,-1,3}
  {0,3,0}                               | {1,0,5}
  {0,3,0}                               | {1,-1,0}
  {0,3,0}                               | {-0.4,-1,-6}
  {0,3,0}                               | {-0.000184615384615,-1,15.3846153846}
- {0,3,0}                               | {3,NaN,5}
- {0,3,0}                               | {NaN,NaN,NaN}
  {0,3,0}                               | {-1,0,3}
  {1,-1,0}                              | {0,-1,5}
  {1,-1,0}                              | {1,0,5}
  {1,-1,0}                              | {0,3,0}
  {1,-1,0}                              | {-0.4,-1,-6}
  {1,-1,0}                              | {-0.000184615384615,-1,15.3846153846}
- {1,-1,0}                              | {3,NaN,5}
- {1,-1,0}                              | {NaN,NaN,NaN}
  {1,-1,0}                              | {0,-1,3}
  {1,-1,0}                              | {-1,0,3}
  {-0.4,-1,-6}                          | {0,-1,5}
@@ -1507,8 +1494,6 @@ SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?# l2.s;
  {-0.4,-1,-6}                          | {0,3,0}
  {-0.4,-1,-6}                          | {1,-1,0}
  {-0.4,-1,-6}                          | {-0.000184615384615,-1,15.3846153846}
- {-0.4,-1,-6}                          | {3,NaN,5}
- {-0.4,-1,-6}                          | {NaN,NaN,NaN}
  {-0.4,-1,-6}                          | {0,-1,3}
  {-0.4,-1,-6}                          | {-1,0,3}
  {-0.000184615384615,-1,15.3846153846} | {0,-1,5}
@@ -1516,46 +1501,20 @@ SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?# l2.s;
  {-0.000184615384615,-1,15.3846153846} | {0,3,0}
  {-0.000184615384615,-1,15.3846153846} | {1,-1,0}
  {-0.000184615384615,-1,15.3846153846} | {-0.4,-1,-6}
- {-0.000184615384615,-1,15.3846153846} | {3,NaN,5}
- {-0.000184615384615,-1,15.3846153846} | {NaN,NaN,NaN}
  {-0.000184615384615,-1,15.3846153846} | {0,-1,3}
  {-0.000184615384615,-1,15.3846153846} | {-1,0,3}
- {3,NaN,5}                             | {0,-1,5}
- {3,NaN,5}                             | {1,0,5}
- {3,NaN,5}                             | {0,3,0}
- {3,NaN,5}                             | {1,-1,0}
- {3,NaN,5}                             | {-0.4,-1,-6}
- {3,NaN,5}                             | {-0.000184615384615,-1,15.3846153846}
- {3,NaN,5}                             | {3,NaN,5}
- {3,NaN,5}                             | {NaN,NaN,NaN}
- {3,NaN,5}                             | {0,-1,3}
- {3,NaN,5}                             | {-1,0,3}
- {NaN,NaN,NaN}                         | {0,-1,5}
- {NaN,NaN,NaN}                         | {1,0,5}
- {NaN,NaN,NaN}                         | {0,3,0}
- {NaN,NaN,NaN}                         | {1,-1,0}
- {NaN,NaN,NaN}                         | {-0.4,-1,-6}
- {NaN,NaN,NaN}                         | {-0.000184615384615,-1,15.3846153846}
- {NaN,NaN,NaN}                         | {3,NaN,5}
- {NaN,NaN,NaN}                         | {NaN,NaN,NaN}
- {NaN,NaN,NaN}                         | {0,-1,3}
- {NaN,NaN,NaN}                         | {-1,0,3}
  {0,-1,3}                              | {1,0,5}
  {0,-1,3}                              | {1,-1,0}
  {0,-1,3}                              | {-0.4,-1,-6}
  {0,-1,3}                              | {-0.000184615384615,-1,15.3846153846}
- {0,-1,3}                              | {3,NaN,5}
- {0,-1,3}                              | {NaN,NaN,NaN}
  {0,-1,3}                              | {-1,0,3}
  {-1,0,3}                              | {0,-1,5}
  {-1,0,3}                              | {0,3,0}
  {-1,0,3}                              | {1,-1,0}
  {-1,0,3}                              | {-0.4,-1,-6}
  {-1,0,3}                              | {-0.000184615384615,-1,15.3846153846}
- {-1,0,3}                              | {3,NaN,5}
- {-1,0,3}                              | {NaN,NaN,NaN}
  {-1,0,3}                              | {0,-1,3}
-(84 rows)
+(48 rows)
 
 -- Intersect with box
 SELECT l.s, b.f1 FROM LINE_TBL l, BOX_TBL b WHERE l.s ?# b.f1;
@@ -1578,16 +1537,16 @@ SELECT l.s, b.f1 FROM LINE_TBL l, BOX_TBL b WHERE l.s ?# b.f1;
 
 -- Intersection point with line
 SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2;
-                   s                   |                   s                   |             ?column?              
----------------------------------------+---------------------------------------+-----------------------------------
+                   s                   |                   s                   |            ?column?             
+---------------------------------------+---------------------------------------+---------------------------------
  {0,-1,5}                              | {0,-1,5}                              | 
  {0,-1,5}                              | {1,0,5}                               | (-5,5)
  {0,-1,5}                              | {0,3,0}                               | 
  {0,-1,5}                              | {1,-1,0}                              | (5,5)
  {0,-1,5}                              | {-0.4,-1,-6}                          | (-27.5,5)
  {0,-1,5}                              | {-0.000184615384615,-1,15.3846153846} | (56250,5)
- {0,-1,5}                              | {3,NaN,5}                             | (NaN,NaN)
- {0,-1,5}                              | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {0,-1,5}                              | {3,NaN,5}                             | 
+ {0,-1,5}                              | {NaN,NaN,NaN}                         | 
  {0,-1,5}                              | {0,-1,3}                              | 
  {0,-1,5}                              | {-1,0,3}                              | (3,5)
  {1,0,5}                               | {0,-1,5}                              | (-5,5)
@@ -1596,8 +1555,8 @@ SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {1,0,5}                               | {1,-1,0}                              | (-5,-5)
  {1,0,5}                               | {-0.4,-1,-6}                          | (-5,-4)
  {1,0,5}                               | {-0.000184615384615,-1,15.3846153846} | (-5,15.3855384615)
- {1,0,5}                               | {3,NaN,5}                             | (NaN,NaN)
- {1,0,5}                               | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {1,0,5}                               | {3,NaN,5}                             | 
+ {1,0,5}                               | {NaN,NaN,NaN}                         | 
  {1,0,5}                               | {0,-1,3}                              | (-5,3)
  {1,0,5}                               | {-1,0,3}                              | 
  {0,3,0}                               | {0,-1,5}                              | 
@@ -1606,8 +1565,8 @@ SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {0,3,0}                               | {1,-1,0}                              | (0,0)
  {0,3,0}                               | {-0.4,-1,-6}                          | (-15,0)
  {0,3,0}                               | {-0.000184615384615,-1,15.3846153846} | (83333.3333333,0)
- {0,3,0}                               | {3,NaN,5}                             | (NaN,NaN)
- {0,3,0}                               | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {0,3,0}                               | {3,NaN,5}                             | 
+ {0,3,0}                               | {NaN,NaN,NaN}                         | 
  {0,3,0}                               | {0,-1,3}                              | 
  {0,3,0}                               | {-1,0,3}                              | (3,0)
  {1,-1,0}                              | {0,-1,5}                              | (5,5)
@@ -1616,8 +1575,8 @@ SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {1,-1,0}                              | {1,-1,0}                              | 
  {1,-1,0}                              | {-0.4,-1,-6}                          | (-4.28571428571,-4.28571428571)
  {1,-1,0}                              | {-0.000184615384615,-1,15.3846153846} | (15.3817756722,15.3817756722)
- {1,-1,0}                              | {3,NaN,5}                             | (NaN,NaN)
- {1,-1,0}                              | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {1,-1,0}                              | {3,NaN,5}                             | 
+ {1,-1,0}                              | {NaN,NaN,NaN}                         | 
  {1,-1,0}                              | {0,-1,3}                              | (3,3)
  {1,-1,0}                              | {-1,0,3}                              | (3,3)
  {-0.4,-1,-6}                          | {0,-1,5}                              | (-27.5,5)
@@ -1626,48 +1585,48 @@ SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {-0.4,-1,-6}                          | {1,-1,0}                              | (-4.28571428571,-4.28571428571)
  {-0.4,-1,-6}                          | {-0.4,-1,-6}                          | 
  {-0.4,-1,-6}                          | {-0.000184615384615,-1,15.3846153846} | (-53.4862244113,15.3944897645)
- {-0.4,-1,-6}                          | {3,NaN,5}                             | (NaN,NaN)
- {-0.4,-1,-6}                          | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {-0.4,-1,-6}                          | {3,NaN,5}                             | 
+ {-0.4,-1,-6}                          | {NaN,NaN,NaN}                         | 
  {-0.4,-1,-6}                          | {0,-1,3}                              | (-22.5,3)
  {-0.4,-1,-6}                          | {-1,0,3}                              | (3,-7.2)
  {-0.000184615384615,-1,15.3846153846} | {0,-1,5}                              | (56250,5)
  {-0.000184615384615,-1,15.3846153846} | {1,0,5}                               | (-5,15.3855384615)
- {-0.000184615384615,-1,15.3846153846} | {0,3,0}                               | (83333.3333333,-1.7763568394e-15)
+ {-0.000184615384615,-1,15.3846153846} | {0,3,0}                               | (83333.3333333,0)
  {-0.000184615384615,-1,15.3846153846} | {1,-1,0}                              | (15.3817756722,15.3817756722)
  {-0.000184615384615,-1,15.3846153846} | {-0.4,-1,-6}                          | (-53.4862244113,15.3944897645)
  {-0.000184615384615,-1,15.3846153846} | {-0.000184615384615,-1,15.3846153846} | 
- {-0.000184615384615,-1,15.3846153846} | {3,NaN,5}                             | (NaN,NaN)
- {-0.000184615384615,-1,15.3846153846} | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {-0.000184615384615,-1,15.3846153846} | {3,NaN,5}                             | 
+ {-0.000184615384615,-1,15.3846153846} | {NaN,NaN,NaN}                         | 
  {-0.000184615384615,-1,15.3846153846} | {0,-1,3}                              | (67083.3333333,3)
  {-0.000184615384615,-1,15.3846153846} | {-1,0,3}                              | (3,15.3840615385)
- {3,NaN,5}                             | {0,-1,5}                              | (NaN,NaN)
- {3,NaN,5}                             | {1,0,5}                               | (NaN,NaN)
- {3,NaN,5}                             | {0,3,0}                               | (NaN,NaN)
- {3,NaN,5}                             | {1,-1,0}                              | (NaN,NaN)
- {3,NaN,5}                             | {-0.4,-1,-6}                          | (NaN,NaN)
- {3,NaN,5}                             | {-0.000184615384615,-1,15.3846153846} | (NaN,NaN)
- {3,NaN,5}                             | {3,NaN,5}                             | (NaN,NaN)
- {3,NaN,5}                             | {NaN,NaN,NaN}                         | (NaN,NaN)
- {3,NaN,5}                             | {0,-1,3}                              | (NaN,NaN)
- {3,NaN,5}                             | {-1,0,3}                              | (NaN,NaN)
- {NaN,NaN,NaN}                         | {0,-1,5}                              | (NaN,NaN)
- {NaN,NaN,NaN}                         | {1,0,5}                               | (NaN,NaN)
- {NaN,NaN,NaN}                         | {0,3,0}                               | (NaN,NaN)
- {NaN,NaN,NaN}                         | {1,-1,0}                              | (NaN,NaN)
- {NaN,NaN,NaN}                         | {-0.4,-1,-6}                          | (NaN,NaN)
- {NaN,NaN,NaN}                         | {-0.000184615384615,-1,15.3846153846} | (NaN,NaN)
- {NaN,NaN,NaN}                         | {3,NaN,5}                             | (NaN,NaN)
- {NaN,NaN,NaN}                         | {NaN,NaN,NaN}                         | (NaN,NaN)
- {NaN,NaN,NaN}                         | {0,-1,3}                              | (NaN,NaN)
- {NaN,NaN,NaN}                         | {-1,0,3}                              | (NaN,NaN)
+ {3,NaN,5}                             | {0,-1,5}                              | 
+ {3,NaN,5}                             | {1,0,5}                               | 
+ {3,NaN,5}                             | {0,3,0}                               | 
+ {3,NaN,5}                             | {1,-1,0}                              | 
+ {3,NaN,5}                             | {-0.4,-1,-6}                          | 
+ {3,NaN,5}                             | {-0.000184615384615,-1,15.3846153846} | 
+ {3,NaN,5}                             | {3,NaN,5}                             | 
+ {3,NaN,5}                             | {NaN,NaN,NaN}                         | 
+ {3,NaN,5}                             | {0,-1,3}                              | 
+ {3,NaN,5}                             | {-1,0,3}                              | 
+ {NaN,NaN,NaN}                         | {0,-1,5}                              | 
+ {NaN,NaN,NaN}                         | {1,0,5}                               | 
+ {NaN,NaN,NaN}                         | {0,3,0}                               | 
+ {NaN,NaN,NaN}                         | {1,-1,0}                              | 
+ {NaN,NaN,NaN}                         | {-0.4,-1,-6}                          | 
+ {NaN,NaN,NaN}                         | {-0.000184615384615,-1,15.3846153846} | 
+ {NaN,NaN,NaN}                         | {3,NaN,5}                             | 
+ {NaN,NaN,NaN}                         | {NaN,NaN,NaN}                         | 
+ {NaN,NaN,NaN}                         | {0,-1,3}                              | 
+ {NaN,NaN,NaN}                         | {-1,0,3}                              | 
  {0,-1,3}                              | {0,-1,5}                              | 
  {0,-1,3}                              | {1,0,5}                               | (-5,3)
  {0,-1,3}                              | {0,3,0}                               | 
  {0,-1,3}                              | {1,-1,0}                              | (3,3)
  {0,-1,3}                              | {-0.4,-1,-6}                          | (-22.5,3)
  {0,-1,3}                              | {-0.000184615384615,-1,15.3846153846} | (67083.3333333,3)
- {0,-1,3}                              | {3,NaN,5}                             | (NaN,NaN)
- {0,-1,3}                              | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {0,-1,3}                              | {3,NaN,5}                             | 
+ {0,-1,3}                              | {NaN,NaN,NaN}                         | 
  {0,-1,3}                              | {0,-1,3}                              | 
  {0,-1,3}                              | {-1,0,3}                              | (3,3)
  {-1,0,3}                              | {0,-1,5}                              | (3,5)
@@ -1676,16 +1635,16 @@ SELECT l1.s, l2.s, l1.s # l2.s FROM LINE_TBL l1, LINE_TBL l2;
  {-1,0,3}                              | {1,-1,0}                              | (3,3)
  {-1,0,3}                              | {-0.4,-1,-6}                          | (3,-7.2)
  {-1,0,3}                              | {-0.000184615384615,-1,15.3846153846} | (3,15.3840615385)
- {-1,0,3}                              | {3,NaN,5}                             | (NaN,NaN)
- {-1,0,3}                              | {NaN,NaN,NaN}                         | (NaN,NaN)
+ {-1,0,3}                              | {3,NaN,5}                             | 
+ {-1,0,3}                              | {NaN,NaN,NaN}                         | 
  {-1,0,3}                              | {0,-1,3}                              | (3,3)
  {-1,0,3}                              | {-1,0,3}                              | 
 (100 rows)
 
 -- Closest point to line segment
 SELECT l.s, l1.s, l.s ## l1.s FROM LINE_TBL l, LSEG_TBL l1;
-                   s                   |               s               |             ?column?              
----------------------------------------+-------------------------------+-----------------------------------
+                   s                   |               s               |            ?column?            
+---------------------------------------+-------------------------------+--------------------------------
  {0,-1,5}                              | [(1,2),(3,4)]                 | (3,4)
  {0,-1,5}                              | [(0,0),(6,6)]                 | (5,5)
  {0,-1,5}                              | [(10,-10),(-3,-4)]            | (-3,-4)
@@ -1705,7 +1664,7 @@ SELECT l.s, l1.s, l.s ## l1.s FROM LINE_TBL l, LSEG_TBL l1;
  {0,3,0}                               | [(1,2),(3,4)]                 | (1,2)
  {0,3,0}                               | [(0,0),(6,6)]                 | (0,0)
  {0,3,0}                               | [(10,-10),(-3,-4)]            | (-3,-4)
- {0,3,0}                               | [(-1000000,200),(300000,-40)] | (83333.3333333,-1.7763568394e-15)
+ {0,3,0}                               | [(-1000000,200),(300000,-40)] | (83333.3333333,0)
  {0,3,0}                               | [(11,22),(33,44)]             | (11,22)
  {0,3,0}                               | [(-10,2),(-10,3)]             | (-10,2)
  {0,3,0}                               | [(0,-20),(30,-20)]            | 
@@ -3721,13 +3680,13 @@ SELECT p.f1, poly.f1, poly.f1 @> p.f1 AS contains
  (Infinity,1e+300) | ((1,2),(7,8),(5,6),(3,-4)) | f
  (Infinity,1e+300) | ((0,0))                    | f
  (Infinity,1e+300) | ((0,1),(0,1))              | f
- (NaN,NaN)         | ((2,0),(2,4),(0,0))        | t
- (NaN,NaN)         | ((3,1),(3,3),(1,0))        | t
- (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | t
- (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | t
- (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | t
- (NaN,NaN)         | ((0,0))                    | t
- (NaN,NaN)         | ((0,1),(0,1))              | t
+ (NaN,NaN)         | ((2,0),(2,4),(0,0))        | f
+ (NaN,NaN)         | ((3,1),(3,3),(1,0))        | f
+ (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | f
+ (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | f
+ (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | f
+ (NaN,NaN)         | ((0,0))                    | f
+ (NaN,NaN)         | ((0,1),(0,1))              | f
  (10,10)           | ((2,0),(2,4),(0,0))        | f
  (10,10)           | ((3,1),(3,3),(1,0))        | f
  (10,10)           | ((1,2),(3,4),(5,6),(7,8))  | f
@@ -3797,13 +3756,13 @@ SELECT p.f1, poly.f1, p.f1 <@ poly.f1 AS contained
  (Infinity,1e+300) | ((1,2),(7,8),(5,6),(3,-4)) | f
  (Infinity,1e+300) | ((0,0))                    | f
  (Infinity,1e+300) | ((0,1),(0,1))              | f
- (NaN,NaN)         | ((2,0),(2,4),(0,0))        | t
- (NaN,NaN)         | ((3,1),(3,3),(1,0))        | t
- (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | t
- (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | t
- (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | t
- (NaN,NaN)         | ((0,0))                    | t
- (NaN,NaN)         | ((0,1),(0,1))              | t
+ (NaN,NaN)         | ((2,0),(2,4),(0,0))        | f
+ (NaN,NaN)         | ((3,1),(3,3),(1,0))        | f
+ (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | f
+ (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | f
+ (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | f
+ (NaN,NaN)         | ((0,0))                    | f
+ (NaN,NaN)         | ((0,1),(0,1))              | f
  (10,10)           | ((2,0),(2,4),(0,0))        | f
  (10,10)           | ((3,1),(3,3),(1,0))        | f
  (10,10)           | ((1,2),(3,4),(5,6),(7,8))  | f
-- 
2.27.0

From 400b0bd6102414ad3421ff03818d927c92dcbdb7 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Date: Thu, 8 Jul 2021 10:24:25 +0900
Subject: [PATCH v9 2/2] Make geometric comparisons tri-state

Geometric comparisons are constructively tri-state so that comparisons
with an invalid objects yields "undefined".  This patch introduces
that behavior. Geometric comparison operators return null forr invalid
objects or when the intermediate calculation yeilds NaN.  GiST is
adjusted to handle such null return from box placement operators
properly.
---
 src/backend/access/gist/gistproc.c     | 215 ++++---
 src/backend/utils/adt/geo_ops.c        | 796 ++++++++++++++++---------
 src/backend/utils/fmgr/fmgr.c          |  18 +-
 src/include/c.h                        |   7 +
 src/include/fmgr.h                     |  19 +-
 src/include/utils/float.h              | 109 ++++
 src/include/utils/geo_decls.h          | 107 ++++
 src/test/regress/expected/geometry.out |  75 +--
 src/test/regress/expected/line.out     |   3 +-
 src/test/regress/expected/point.out    |   6 +-
 src/test/regress/sql/line.sql          |   1 +
 11 files changed, 926 insertions(+), 430 deletions(-)

diff --git a/src/backend/access/gist/gistproc.c b/src/backend/access/gist/gistproc.c
index d474612b77..31a49c47da 100644
--- a/src/backend/access/gist/gistproc.c
+++ b/src/backend/access/gist/gistproc.c
@@ -873,75 +873,90 @@ static bool
 gist_box_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy)
 {
     bool        retval;
+    bool        isnull = false;
 
     switch (strategy)
     {
         case RTLeftStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_left,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_left,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverLeftStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overleft,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overleft,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverlapStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overlap,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overlap,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverRightStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overright,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overright,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTRightStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_right,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_right,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTSameStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_same,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_same,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTContainsStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_contain,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_contain,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTContainedByStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_contained,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_contained,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverBelowStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overbelow,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overbelow,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTBelowStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_below,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_below,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTAboveStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_above,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_above,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverAboveStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overabove,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overabove,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         default:
             elog(ERROR, "unrecognized strategy number: %d", strategy);
             retval = false;        /* keep compiler quiet */
             break;
     }
-    return retval;
+
+    /* consider null as false */
+    return retval && !isnull;
 }
 
 /*****************************************
@@ -958,71 +973,85 @@ static bool
 rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy)
 {
     bool        retval;
+    bool        isnull = false;
 
     switch (strategy)
     {
         case RTLeftStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_overright,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_overright,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTOverLeftStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_right,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_right,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTOverlapStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overlap,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overlap,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverRightStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_left,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_left,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTRightStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_overleft,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_overleft,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTSameStrategyNumber:
         case RTContainsStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_contain,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_contain,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTContainedByStrategyNumber:
-            retval = DatumGetBool(DirectFunctionCall2(box_overlap,
-                                                      PointerGetDatum(key),
-                                                      PointerGetDatum(query)));
+            retval = DatumGetBool(DirectFunctionCall2Ext(box_overlap,
+                                                         PointerGetDatum(key),
+                                                         PointerGetDatum(query),
+                                                         &isnull));
             break;
         case RTOverBelowStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_above,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_above,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTBelowStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_overabove,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_overabove,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTAboveStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_overbelow,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_overbelow,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         case RTOverAboveStrategyNumber:
-            retval = !DatumGetBool(DirectFunctionCall2(box_below,
-                                                       PointerGetDatum(key),
-                                                       PointerGetDatum(query)));
+            retval = !DatumGetBool(DirectFunctionCall2Ext(box_below,
+                                                          PointerGetDatum(key),
+                                                          PointerGetDatum(query),
+                                                          &isnull));
             break;
         default:
             elog(ERROR, "unrecognized strategy number: %d", strategy);
             retval = false;        /* keep compiler quiet */
             break;
     }
-    return retval;
+
+    /* consider null as false */
+    return retval || isnull;
 }
 
 /**************************************************
@@ -1237,22 +1266,30 @@ computeDistance(bool isLeaf, BOX *box, Point *point)
     else if (point->x <= box->high.x && point->x >= box->low.x)
     {
         /* point is over or below box */
-        Assert(box->low.y <= box->high.y);
+        Assert(box->low.y <= box->high.y
+               || isnan(box->low.y) || isnan(box->high.y));
+
         if (point->y > box->high.y)
             result = float8_mi(point->y, box->high.y);
         else if (point->y < box->low.y)
             result = float8_mi(box->low.y, point->y);
+        else if (isnan(point->y) || isnan(box->high.y) || isnan(box->low.y))
+            result = get_float8_nan();
         else
             elog(ERROR, "inconsistent point values");
     }
     else if (point->y <= box->high.y && point->y >= box->low.y)
     {
         /* point is to left or right of box */
-        Assert(box->low.x <= box->high.x);
+        Assert(box->low.x <= box->high.x
+               || isnan(box->low.y) || isnan(box->high.y));
+
         if (point->x > box->high.x)
             result = float8_mi(point->x, box->high.x);
         else if (point->x < box->low.x)
             result = float8_mi(box->low.x, point->x);
+        else if (isnan(point->x) || isnan(box->high.x) || isnan(box->low.x))
+            result = get_float8_nan();
         else
             elog(ERROR, "inconsistent point values");
     }
@@ -1406,12 +1443,18 @@ gist_point_consistent(PG_FUNCTION_ARGS)
                      * of polygon's bounding box and point
                      */
                     BOX           *box = DatumGetBoxP(entry->key);
+                    bool        isnull = false;
 
-                    Assert(box->high.x == box->low.x
-                           && box->high.y == box->low.y);
-                    result = DatumGetBool(DirectFunctionCall2(poly_contain_pt,
-                                                              PolygonPGetDatum(query),
-                                                              PointPGetDatum(&box->high)));
+                    Assert((box->high.x == box->low.x
+                            && box->high.y == box->low.y) ||
+                           (isnan(box->high.x) && isnan(box->low.x)) ||
+                           (isnan(box->high.y) && isnan(box->low.y)));
+                           
+                    result = DatumGetBool(DirectFunctionCall2Ext(poly_contain_pt,
+                                                                 PolygonPGetDatum(query),
+                                                                 PointPGetDatum(&box->high),
+                                                                 &isnull));
+                    result &= !isnull;
                     *recheck = false;
                 }
             }
@@ -1433,12 +1476,18 @@ gist_point_consistent(PG_FUNCTION_ARGS)
                      * of polygon's bounding box and point
                      */
                     BOX           *box = DatumGetBoxP(entry->key);
+                    bool        isnull = false;
+
+                    Assert((box->high.x == box->low.x
+                            && box->high.y == box->low.y) ||
+                           (isnan(box->high.x) && isnan(box->low.x)) ||
+                           (isnan(box->high.y) && isnan(box->low.y)));
+                    result = DatumGetBool(DirectFunctionCall2Ext(circle_contain_pt,
+                                                                 CirclePGetDatum(query),
+                                                                 PointPGetDatum(&box->high),
+                                                                 &isnull));
+                    result &= !isnull;
 
-                    Assert(box->high.x == box->low.x
-                           && box->high.y == box->low.y);
-                    result = DatumGetBool(DirectFunctionCall2(circle_contain_pt,
-                                                              CirclePGetDatum(query),
-                                                              PointPGetDatum(&box->high)));
                     *recheck = false;
                 }
             }
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 851c171108..59362db727 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -44,14 +44,14 @@
  *
  * Common operators are:
  * * Intersection point:
- *   bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
+ *   tsbool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2);
  *        Return whether the two objects intersect. If *result is not NULL,
  *        it is set to the intersection point.
  *
  * * Containment:
- *   bool type1_contain_type2(Type1 *obj1, Type2 *obj2);
+ *   tsbool type1_contain_type2(Type1 *obj1, Type2 *obj2);
  *        Return whether obj1 contains obj2.
- *   bool type1_contain_type2(Type1 *contains_obj, Type1 *contained_obj);
+ *   tsbool type1_contain_type2(Type1 *contains_obj, Type1 *contained_obj);
  *        Return whether obj1 contains obj2 (used when types are the same)
  *
  * * Distance of closest point in or on obj1 to obj2:
@@ -61,7 +61,8 @@
  *
  * These functions may be used to implement multiple SQL-level operators.  For
  * example, determining whether two lines are parallel is done by checking
- * whether they don't intersect.
+ * whether they don't intersect. tsbool-returning functions return TS_NULL if
+ * its calculation involves NaN.
  */
 
 /*
@@ -79,7 +80,7 @@ static inline void point_add_point(Point *result, Point *pt1, Point *pt2);
 static inline void point_sub_point(Point *result, Point *pt1, Point *pt2);
 static inline void point_mul_point(Point *result, Point *pt1, Point *pt2);
 static inline void point_div_point(Point *result, Point *pt1, Point *pt2);
-static inline bool point_eq_point(Point *pt1, Point *pt2);
+static inline tsbool point_eq_point(Point *pt1, Point *pt2);
 static inline float8 point_dt(Point *pt1, Point *pt2);
 static inline float8 point_sl(Point *pt1, Point *pt2);
 static int    point_inside(Point *p, int npts, Point *plist);
@@ -88,18 +89,18 @@ static int    point_inside(Point *p, int npts, Point *plist);
 static inline void line_construct(LINE *result, Point *pt, float8 m);
 static inline float8 line_sl(LINE *line);
 static inline float8 line_invsl(LINE *line);
-static bool line_interpt_line(Point *result, LINE *l1, LINE *l2);
-static bool line_contain_point(LINE *line, Point *point);
+static tsbool line_interpt_line(Point *result, LINE *l1, LINE *l2);
+static tsbool line_contain_point(LINE *line, Point *point);
 static float8 line_closept_point(Point *result, LINE *line, Point *pt);
 
 /* Routines for line segments */
 static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2);
 static inline float8 lseg_sl(LSEG *lseg);
 static inline float8 lseg_invsl(LSEG *lseg);
-static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
-static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
+static tsbool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line);
+static tsbool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2);
 static int    lseg_crossing(float8 x, float8 y, float8 px, float8 py);
-static bool lseg_contain_point(LSEG *lseg, Point *point);
+static tsbool lseg_contain_point(LSEG *lseg, Point *point);
 static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt);
 static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line);
 static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
@@ -107,14 +108,14 @@ static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg);
 /* Routines for boxes */
 static inline void box_construct(BOX *result, Point *pt1, Point *pt2);
 static void box_cn(Point *center, BOX *box);
-static bool box_ov(BOX *box1, BOX *box2);
+static tsbool box_ov(BOX *box1, BOX *box2);
 static float8 box_ar(BOX *box);
 static float8 box_ht(BOX *box);
 static float8 box_wd(BOX *box);
-static bool box_contain_point(BOX *box, Point *point);
-static bool box_contain_box(BOX *contains_box, BOX *contained_box);
-static bool box_contain_lseg(BOX *box, LSEG *lseg);
-static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
+static tsbool box_contain_point(BOX *box, Point *point);
+static tsbool box_contain_box(BOX *contains_box, BOX *contained_box);
+static tsbool box_contain_lseg(BOX *box, LSEG *lseg);
+static tsbool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg);
 static float8 box_closept_point(Point *result, BOX *box, Point *point);
 static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg);
 
@@ -124,9 +125,9 @@ static float8 circle_ar(CIRCLE *circle);
 /* Routines for polygons */
 static void make_bound_box(POLYGON *poly);
 static void poly_to_circle(CIRCLE *result, POLYGON *poly);
-static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
-static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
-static bool plist_same(int npts, Point *p1, Point *p2);
+static tsbool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
+static tsbool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly);
+static tsbool plist_same(int npts, Point *p1, Point *p2);
 static float8 dist_ppoly_internal(Point *pt, POLYGON *poly);
 
 /* Routines for encoding and decoding */
@@ -540,9 +541,13 @@ box_same(PG_FUNCTION_ARGS)
 {
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
+    tsbool        res1 = point_eq_point(&box1->high, &box2->high);
+    tsbool        res2 = point_eq_point(&box1->low, &box2->low);
 
-    PG_RETURN_BOOL(point_eq_point(&box1->high, &box2->high) &&
-                   point_eq_point(&box1->low, &box2->low));
+    if (res1 == TS_NULL || res2 == TS_NULL)
+        PG_RETURN_NULL();
+
+    PG_RETURN_BOOL(res1 && res2);
 }
 
 /*        box_overlap        -        does box1 overlap box2?
@@ -553,16 +558,16 @@ box_overlap(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_ov(box1, box2));
+    PG_RETURN_TSBOOL(box_ov(box1, box2));
 }
 
-static bool
+static tsbool
 box_ov(BOX *box1, BOX *box2)
 {
-    return (FPle(box1->low.x, box2->high.x) &&
-            FPle(box2->low.x, box1->high.x) &&
-            FPle(box1->low.y, box2->high.y) &&
-            FPle(box2->low.y, box1->high.y));
+    return TS_AND4(FPTle(box1->low.x, box2->high.x),
+                   FPTle(box2->low.x, box1->high.x),
+                   FPTle(box1->low.y, box2->high.y),
+                   FPTle(box2->low.y, box1->high.y));
 }
 
 /*        box_left        -        is box1 strictly left of box2?
@@ -573,7 +578,7 @@ box_left(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPlt(box1->high.x, box2->low.x));
+    PG_RETURN_TSBOOL(FPTlt(box1->high.x, box2->low.x));
 }
 
 /*        box_overleft    -        is the right edge of box1 at or left of
@@ -588,7 +593,7 @@ box_overleft(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPle(box1->high.x, box2->high.x));
+    PG_RETURN_TSBOOL(FPTle(box1->high.x, box2->high.x));
 }
 
 /*        box_right        -        is box1 strictly right of box2?
@@ -599,7 +604,7 @@ box_right(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPgt(box1->low.x, box2->high.x));
+    PG_RETURN_TSBOOL(FPTgt(box1->low.x, box2->high.x));
 }
 
 /*        box_overright    -        is the left edge of box1 at or right of
@@ -614,7 +619,7 @@ box_overright(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPge(box1->low.x, box2->low.x));
+    PG_RETURN_TSBOOL(FPTge(box1->low.x, box2->low.x));
 }
 
 /*        box_below        -        is box1 strictly below box2?
@@ -625,7 +630,7 @@ box_below(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPlt(box1->high.y, box2->low.y));
+    PG_RETURN_TSBOOL(FPTlt(box1->high.y, box2->low.y));
 }
 
 /*        box_overbelow    -        is the upper edge of box1 at or below
@@ -637,7 +642,7 @@ box_overbelow(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPle(box1->high.y, box2->high.y));
+    PG_RETURN_TSBOOL(FPTle(box1->high.y, box2->high.y));
 }
 
 /*        box_above        -        is box1 strictly above box2?
@@ -648,7 +653,7 @@ box_above(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPgt(box1->low.y, box2->high.y));
+    PG_RETURN_TSBOOL(FPTgt(box1->low.y, box2->high.y));
 }
 
 /*        box_overabove    -        is the lower edge of box1 at or above
@@ -660,7 +665,7 @@ box_overabove(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPge(box1->low.y, box2->low.y));
+    PG_RETURN_TSBOOL(FPTge(box1->low.y, box2->low.y));
 }
 
 /*        box_contained    -        is box1 contained by box2?
@@ -671,7 +676,7 @@ box_contained(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_box(box2, box1));
+    PG_RETURN_TSBOOL(box_contain_box(box2, box1));
 }
 
 /*        box_contain        -        does box1 contain box2?
@@ -682,19 +687,19 @@ box_contain(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_box(box1, box2));
+    PG_RETURN_TSBOOL(box_contain_box(box1, box2));
 }
 
 /*
  * Check whether the second box is in the first box or on its border
  */
-static bool
+static tsbool
 box_contain_box(BOX *contains_box, BOX *contained_box)
 {
-    return FPge(contains_box->high.x, contained_box->high.x) &&
-        FPle(contains_box->low.x, contained_box->low.x) &&
-        FPge(contains_box->high.y, contained_box->high.y) &&
-        FPle(contains_box->low.y, contained_box->low.y);
+    return TS_AND4(FPTge(contains_box->high.x, contained_box->high.x),
+                   FPTle(contains_box->low.x, contained_box->low.x),
+                   FPTge(contains_box->high.y, contained_box->high.y),
+                   FPTle(contains_box->low.y, contained_box->low.y));
 }
 
 
@@ -712,7 +717,7 @@ box_below_eq(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPle(box1->high.y, box2->low.y));
+    PG_RETURN_TSBOOL(FPTle(box1->high.y, box2->low.y));
 }
 
 Datum
@@ -721,7 +726,7 @@ box_above_eq(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPge(box1->low.y, box2->high.y));
+    PG_RETURN_TSBOOL(FPTge(box1->low.y, box2->high.y));
 }
 
 
@@ -734,7 +739,7 @@ box_lt(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPlt(box_ar(box1), box_ar(box2)));
+    PG_RETURN_TSBOOL(FPTlt(box_ar(box1), box_ar(box2)));
 }

 Datum
@@ -743,7 +748,7 @@ box_gt(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPgt(box_ar(box1), box_ar(box2)));
+    PG_RETURN_TSBOOL(FPTgt(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -752,7 +757,7 @@ box_eq(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPeq(box_ar(box1), box_ar(box2)));
+    PG_RETURN_TSBOOL(FPTeq(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -761,7 +766,7 @@ box_le(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPle(box_ar(box1), box_ar(box2)));
+    PG_RETURN_TSBOOL(FPTle(box_ar(box1), box_ar(box2)));
 }
 
 Datum
@@ -770,7 +775,7 @@ box_ge(PG_FUNCTION_ARGS)
     BOX           *box1 = PG_GETARG_BOX_P(0);
     BOX           *box2 = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(FPge(box_ar(box1), box_ar(box2)));
+    PG_RETURN_TSBOOL(FPTge(box_ar(box1), box_ar(box2)));
 }
 
 
@@ -981,7 +986,7 @@ line_in(PG_FUNCTION_ARGS)
     else
     {
         path_decode(s, true, 2, &lseg.p[0], &isopen, NULL, "line", str);
-        if (point_eq_point(&lseg.p[0], &lseg.p[1]))
+        if (point_eq_point(&lseg.p[0], &lseg.p[1]) == TS_TRUE)
             ereport(ERROR,
                     (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                      errmsg("invalid line specification: must be two distinct points")));
@@ -1105,7 +1110,7 @@ line_construct_pp(PG_FUNCTION_ARGS)
     LINE       *result = (LINE *) palloc(sizeof(LINE));
 
     /* NaNs are considered to be equal by point_eq_point */
-    if (point_eq_point(pt1, pt2))
+    if (point_eq_point(pt1, pt2) == TS_TRUE)
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                  errmsg("invalid line specification: must be two distinct points")));
@@ -1126,11 +1131,16 @@ line_intersect(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
     Point        xp;
+    tsbool        r;
 
-    if (line_interpt_line(&xp, l1, l2) && !isnan(xp.x) && !isnan(xp.y))
-        PG_RETURN_BOOL(true);
+    if ((r = line_interpt_line(&xp, l1, l2)) == TS_TRUE)
+    {
+        if (!isnan(xp.x) && !isnan(xp.y))
+            PG_RETURN_BOOL(true);
+        PG_RETURN_NULL();
+    }
     else
-        PG_RETURN_BOOL(false);
+        PG_RETURN_TSBOOL(r);
 }
 
 Datum
@@ -1139,7 +1149,7 @@ line_parallel(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(!line_interpt_line(NULL, l1, l2));
+    PG_RETURN_TSBOOL(TS_NOT(line_interpt_line(NULL, l1, l2)));
 }
 
 Datum
@@ -1148,18 +1158,27 @@ line_perp(PG_FUNCTION_ARGS)
     LINE       *l1 = PG_GETARG_LINE_P(0);
     LINE       *l2 = PG_GETARG_LINE_P(1);
 
-    if (unlikely(isnan(l1->C) || isnan(l2->C)))
-        return false;
+    if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
+                 isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
+        PG_RETURN_NULL();
 
+    /*
+     * Since the coefficients are all non-NaN, we don't need to use the
+     * tristate versions of FPTxx functions here.
+     */
     if (FPzero(l1->A))
-        PG_RETURN_BOOL(FPzero(l2->B) && !isnan(l1->B) && !isnan(l2->A));
+        PG_RETURN_BOOL(FPzero(l2->B));
     if (FPzero(l2->A))
-        PG_RETURN_BOOL(FPzero(l1->B) && !isnan(l2->B) && !isnan(l1->A));
+        PG_RETURN_BOOL(FPzero(l1->B));
     if (FPzero(l1->B))
-        PG_RETURN_BOOL(FPzero(l2->A) && !isnan(l1->A) && !isnan(l2->B));
+        PG_RETURN_BOOL(FPzero(l2->A));
     if (FPzero(l2->B))
-        PG_RETURN_BOOL(FPzero(l1->A) && !isnan(l2->A) && !isnan(l1->B));
+        PG_RETURN_BOOL(FPzero(l1->A));
 
+    /*
+     * We don't reach here with numbers less than EPSILON. Thus the following
+     * arithmetic cannot yield NaN.
+     */
     PG_RETURN_BOOL(FPeq(float8_div(float8_mul(l1->A, l2->A),
                                    float8_mul(l1->B, l2->B)), -1.0));
 }
@@ -1169,7 +1188,11 @@ line_vertical(PG_FUNCTION_ARGS)
 {
     LINE       *line = PG_GETARG_LINE_P(0);
 
-    PG_RETURN_BOOL(FPzero(line->B) && !isnan(line->A) && !isnan(line->C));
+    /* is it correct to return NULL for C = NaN? */
+    if (unlikely(isnan(line->A) || isnan(line->C)))
+        PG_RETURN_NULL();
+        
+    PG_RETURN_TSBOOL(FPTzero(line->B));
 }
 
 Datum
@@ -1177,7 +1200,11 @@ line_horizontal(PG_FUNCTION_ARGS)
 {
     LINE       *line = PG_GETARG_LINE_P(0);
 
-    PG_RETURN_BOOL(FPzero(line->A) && !isnan(line->B) && !isnan(line->C));
+    /* is it correct to return NULL for C = NaN? */
+    if (unlikely(isnan(line->B) || isnan(line->C)))
+        PG_RETURN_NULL();
+        
+    PG_RETURN_TSBOOL(FPTzero(line->A));
 }
 
 
@@ -1191,14 +1218,10 @@ line_eq(PG_FUNCTION_ARGS)
     LINE       *l2 = PG_GETARG_LINE_P(1);
     float8        ratio;
 
-    /* If any NaNs are involved, insist on exact equality */
+    /* If any NaNs are involved, the two cannot be equal */
     if (unlikely(isnan(l1->A) || isnan(l1->B) || isnan(l1->C) ||
                  isnan(l2->A) || isnan(l2->B) || isnan(l2->C)))
-    {
-        PG_RETURN_BOOL(float8_eq(l1->A, l2->A) &&
-                       float8_eq(l1->B, l2->B) &&
-                       float8_eq(l1->C, l2->C));
-    }
+        PG_RETURN_NULL();
 
     /* Otherwise, lines whose parameters are proportional are the same */
     if (!FPzero(l2->A))
@@ -1281,7 +1304,7 @@ line_distance(PG_FUNCTION_ARGS)
     Point        xp;
     float8        ratio;
 
-    if (line_interpt_line(&xp, l1, l2)) /* intersecting? */
+    if (line_interpt_line(&xp, l1, l2) == TS_TRUE) /* intersecting? */
     {
         /* return NaN if NaN is involved */
         if (isnan(xp.x) || isnan(xp.y))
@@ -1316,7 +1339,7 @@ line_interpt(PG_FUNCTION_ARGS)
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (!line_interpt_line(result, l1, l2) ||
+    if (line_interpt_line(result, l1, l2) != TS_TRUE ||
         isnan(result->x) || isnan(result->y))
     {
         pfree(result);
@@ -1340,17 +1363,19 @@ line_interpt(PG_FUNCTION_ARGS)
  * point would have NaN coordinates.  We shouldn't return false in this case
  * because that would mean the lines are parallel.
  */
-static bool
+static tsbool
 line_interpt_line(Point *result, LINE *l1, LINE *l2)
 {
     float8        x,
                 y;
+    tsbool        t;
 
-    if (!FPzero(l1->B))
+    if ((t = FPTzero(l1->B)) == TS_FALSE)
     {
         /* l1 is not virtucal */
-        if (FPeq(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B))))
-            return false;
+        t = FPTne(l2->A, float8_mul(l1->A, float8_div(l2->B, l1->B)));
+        if (t != TS_TRUE)
+            return t;
 
         x = float8_div(float8_mi(float8_mul(l1->B, l2->C),
                                  float8_mul(l2->B, l1->C)),
@@ -1367,7 +1392,7 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
                        float8_mi(float8_mul(l2->A, l1->B),
                                  float8_mul(l1->A, l2->B)));
     }
-    else if (!FPzero(l2->B))
+    else if (t == TS_TRUE && (t = FPTzero(l2->B)) == TS_FALSE)
     {
         /* l2 is not virtical */
         /*
@@ -1380,13 +1405,22 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
          * When l2->A is zero, y is determined independently from x even if it
          * is Inf.
          */
-        if (FPzero(l2->A))
-            y = -float8_div(l2->C, l2->B);
-        else
-            y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
+        switch (FPTzero(l2->A))
+        {
+            case TS_TRUE:
+                y = -float8_div(l2->C, l2->B);
+                break;
+            case TS_FALSE:
+                y = float8_div(-float8_pl(float8_mul(l2->A, x), l2->C), l2->B);
+                break;
+            case TS_NULL:
+                return TS_NULL;
+        }
     }
+    else if (t != TS_NULL)
+        return TS_FALSE;
     else
-        return false;
+        return TS_NULL;
 
     /* On some platforms, the preceding expressions tend to produce -0. */
     if (x == 0.0)
@@ -1397,7 +1431,7 @@ line_interpt_line(Point *result, LINE *l1, LINE *l2)
     if (result != NULL)
         point_construct(result, x, y);
 
-    return true;
+    return TS_TRUE;
 }
 
 
@@ -1704,6 +1738,7 @@ path_inter(PG_FUNCTION_ARGS)
                 j;
     LSEG        seg1,
                 seg2;
+    tsbool        r;
 
     Assert(p1->npts > 0 && p2->npts > 0);
 
@@ -1716,6 +1751,7 @@ path_inter(PG_FUNCTION_ARGS)
         b1.low.x = float8_min_nan(p1->p[i].x, b1.low.x);
         b1.low.y = float8_min_nan(p1->p[i].y, b1.low.y);
     }
+
     b2.high.x = b2.low.x = p2->p[0].x;
     b2.high.y = b2.low.y = p2->p[0].y;
     for (i = 1; i < p2->npts; i++)
@@ -1725,8 +1761,8 @@ path_inter(PG_FUNCTION_ARGS)
         b2.low.x = float8_min_nan(p2->p[i].x, b2.low.x);
         b2.low.y = float8_min_nan(p2->p[i].y, b2.low.y);
     }
-    if (!box_ov(&b1, &b2))
-        PG_RETURN_BOOL(false);
+    if ((r = box_ov(&b1, &b2)) != TS_TRUE)
+        PG_RETURN_TSBOOL(r);
 
     /* pairwise check lseg intersections */
     for (i = 0; i < p1->npts; i++)
@@ -1745,6 +1781,7 @@ path_inter(PG_FUNCTION_ARGS)
         for (j = 0; j < p2->npts; j++)
         {
             int            jprev;
+            tsbool        t;
 
             if (j > 0)
                 jprev = j - 1;
@@ -1757,13 +1794,14 @@ path_inter(PG_FUNCTION_ARGS)
 
             statlseg_construct(&seg1, &p1->p[iprev], &p1->p[i]);
             statlseg_construct(&seg2, &p2->p[jprev], &p2->p[j]);
-            if (lseg_interpt_lseg(NULL, &seg1, &seg2))
-                PG_RETURN_BOOL(true);
+            t = lseg_interpt_lseg(NULL, &seg1, &seg2);
+            if (t != TS_FALSE)
+                PG_RETURN_TSBOOL(t);
         }
     }
 
     /* if we dropped through, no two segs intersected */
-    PG_RETURN_BOOL(false);
+    PG_RETURN_TSBOOL(TS_FALSE);
 }
 
 /* path_distance()
@@ -2004,8 +2042,12 @@ point_eq(PG_FUNCTION_ARGS)
 {
     Point       *pt1 = PG_GETARG_POINT_P(0);
     Point       *pt2 = PG_GETARG_POINT_P(1);
+    tsbool        res = point_eq_point(pt1, pt2);
 
-    PG_RETURN_BOOL(point_eq_point(pt1, pt2));
+    if (res == TS_NULL)
+        PG_RETURN_NULL();
+
+    PG_RETURN_BOOL(res);
 }
 
 Datum
@@ -2013,23 +2055,22 @@ point_ne(PG_FUNCTION_ARGS)
 {
     Point       *pt1 = PG_GETARG_POINT_P(0);
     Point       *pt2 = PG_GETARG_POINT_P(1);
+    tsbool        res = point_eq_point(pt1, pt2);
 
-    PG_RETURN_BOOL(!point_eq_point(pt1, pt2));
+    if (res == TS_NULL)
+        PG_RETURN_NULL();
+
+    PG_RETURN_BOOL(!res);
 }
 
 
 /*
  * Check whether the two points are the same
  */
-static inline bool
+static inline tsbool
 point_eq_point(Point *pt1, Point *pt2)
 {
-    /* If any NaNs are involved, insist on exact equality */
-    if (unlikely(isnan(pt1->x) || isnan(pt1->y) ||
-                 isnan(pt2->x) || isnan(pt2->y)))
-        return (float8_eq(pt1->x, pt2->x) && float8_eq(pt1->y, pt2->y));
-
-    return (FPeq(pt1->x, pt2->x) && FPeq(pt1->y, pt2->y));
+    return TS_AND2(FPTeq(pt1->x, pt2->x), FPTeq(pt1->y, pt2->y));
 }
 
 
@@ -2070,6 +2111,7 @@ point_slope(PG_FUNCTION_ARGS)
 static inline float8
 point_sl(Point *pt1, Point *pt2)
 {
+    /* NaN doesn't equal to NaN, so don't bother using a tri-state value */
     if (FPeq(pt1->x, pt2->x))
     {
         if (unlikely(isnan(pt1->y) || isnan(pt2->y)))
@@ -2096,6 +2138,7 @@ point_sl(Point *pt1, Point *pt2)
 static inline float8
 point_invsl(Point *pt1, Point *pt2)
 {
+    /* NaN doesn't equal to NaN, so don't bother using a tri-state value */
     if (FPeq(pt1->x, pt2->x))
     {
         if (unlikely(isnan(pt1->y) || isnan(pt2->y)))
@@ -2254,7 +2297,7 @@ lseg_intersect(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(lseg_interpt_lseg(NULL, l1, l2));
+    PG_RETURN_TSBOOL(lseg_interpt_lseg(NULL, l1, l2));
 }
 
 
@@ -2264,7 +2307,7 @@ lseg_parallel(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_sl(l2)));
+    PG_RETURN_TSBOOL(FPTeq(lseg_sl(l1), lseg_sl(l2)));
 }
 
 /*
@@ -2276,7 +2319,7 @@ lseg_perp(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPeq(lseg_sl(l1), lseg_invsl(l2)));
+    PG_RETURN_TSBOOL(FPTeq(lseg_sl(l1), lseg_invsl(l2)));
 }
 
 Datum
@@ -2284,7 +2327,7 @@ lseg_vertical(PG_FUNCTION_ARGS)
 {
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
 
-    PG_RETURN_BOOL(FPeq(lseg->p[0].x, lseg->p[1].x));
+    PG_RETURN_TSBOOL(FPTeq(lseg->p[0].x, lseg->p[1].x));
 }
 
 Datum
@@ -2292,7 +2335,7 @@ lseg_horizontal(PG_FUNCTION_ARGS)
 {
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
 
-    PG_RETURN_BOOL(FPeq(lseg->p[0].y, lseg->p[1].y));
+    PG_RETURN_TSBOOL(FPTeq(lseg->p[0].y, lseg->p[1].y));
 }
 
 
@@ -2302,8 +2345,8 @@ lseg_eq(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(point_eq_point(&l1->p[0], &l2->p[0]) &&
-                   point_eq_point(&l1->p[1], &l2->p[1]));
+    PG_RETURN_TSBOOL(TS_AND2(point_eq_point(&l1->p[0], &l2->p[0]),
+                             point_eq_point(&l1->p[1], &l2->p[1])));
 }
 
 Datum
@@ -2312,8 +2355,9 @@ lseg_ne(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(!point_eq_point(&l1->p[0], &l2->p[0]) ||
-                   !point_eq_point(&l1->p[1], &l2->p[1]));
+    PG_RETURN_TSBOOL(TS_OR2(
+                         TS_NOT(point_eq_point(&l1->p[0], &l2->p[0])),
+                         TS_NOT(point_eq_point(&l1->p[1], &l2->p[1]))));
 }
 
 Datum
@@ -2322,8 +2366,8 @@ lseg_lt(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPlt(point_dt(&l1->p[0], &l1->p[1]),
-                        point_dt(&l2->p[0], &l2->p[1])));
+    PG_RETURN_TSBOOL(FPTlt(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 Datum
@@ -2332,8 +2376,8 @@ lseg_le(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPle(point_dt(&l1->p[0], &l1->p[1]),
-                        point_dt(&l2->p[0], &l2->p[1])));
+    PG_RETURN_TSBOOL(FPTle(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 Datum
@@ -2342,8 +2386,8 @@ lseg_gt(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPgt(point_dt(&l1->p[0], &l1->p[1]),
-                        point_dt(&l2->p[0], &l2->p[1])));
+    PG_RETURN_TSBOOL(FPTgt(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 Datum
@@ -2352,8 +2396,8 @@ lseg_ge(PG_FUNCTION_ARGS)
     LSEG       *l1 = PG_GETARG_LSEG_P(0);
     LSEG       *l2 = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(FPge(point_dt(&l1->p[0], &l1->p[1]),
-                        point_dt(&l2->p[0], &l2->p[1])));
+    PG_RETURN_TSBOOL(FPTge(point_dt(&l1->p[0], &l1->p[1]),
+                           point_dt(&l2->p[0], &l2->p[1])));
 }
 
 
@@ -2398,27 +2442,28 @@ lseg_center(PG_FUNCTION_ARGS)
  * This function is almost perfectly symmetric, even though it doesn't look
  * like it.  See lseg_interpt_line() for the other half of it.
  */
-static bool
+static tsbool
 lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2)
 {
     Point        interpt;
     LINE        tmp;
+    tsbool        r;
 
     line_construct(&tmp, &l2->p[0], lseg_sl(l2));
-    if (!lseg_interpt_line(&interpt, l1, &tmp))
-        return false;
+    if ((r = lseg_interpt_line(&interpt, l1, &tmp)) != TS_TRUE)
+        return r;
 
     /*
      * If the line intersection point isn't within l2, there is no valid
      * segment intersection point at all.
      */
-    if (!lseg_contain_point(l2, &interpt))
-        return false;
+    if ((r = lseg_contain_point(l2, &interpt)) != TS_TRUE)
+        return r;
 
     if (result != NULL)
         *result = interpt;
 
-    return true;
+    return TS_TRUE;
 }
 
 Datum
@@ -2430,7 +2475,7 @@ lseg_interpt(PG_FUNCTION_ARGS)
 
     result = (Point *) palloc(sizeof(Point));
 
-    if (!lseg_interpt_lseg(result, l1, l2))
+    if (lseg_interpt_lseg(result, l1, l2) != TS_TRUE)
         PG_RETURN_NULL();
     PG_RETURN_POINT_P(result);
 }
@@ -2702,8 +2747,13 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
     float8        d;
     int            i;
     LSEG        seg;
+    int            inside;
 
-    if (point_inside(pt, poly->npts, poly->p) != 0)
+    inside = point_inside(pt, poly->npts, poly->p);
+    if (inside == -1)
+        return get_float8_nan();
+
+    if (inside != 0)
         return 0.0;
 
     /* initialize distance with segment between first and last points */
@@ -2742,11 +2792,12 @@ dist_ppoly_internal(Point *pt, POLYGON *poly)
  * Return whether the line segment intersect with the line. If *result is not
  * NULL, it is set to the intersection point.
  */
-static bool
+static tsbool
 lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
 {
     Point        interpt;
     LINE        tmp;
+    tsbool        r;
 
     /*
      * First, we promote the line segment to a line, because we know how to
@@ -2754,32 +2805,40 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line)
      * intersection point, we are done.
      */
     line_construct(&tmp, &lseg->p[0], lseg_sl(lseg));
-    if (!line_interpt_line(&interpt, &tmp, line) ||
-        unlikely(isnan(interpt.x) || isnan(interpt.y)))
-        return false;
+    if ((r = line_interpt_line(&interpt, &tmp, line)) != TS_TRUE)
+        return r;
+
+    if (unlikely(isnan(interpt.x) || isnan(interpt.y)))
+        return TS_FALSE;
 
     /*
      * Then, we check whether the intersection point is actually on the line
      * segment.
      */
-    if (!lseg_contain_point(lseg, &interpt))
-        return false;
+    if ((r = lseg_contain_point(lseg, &interpt)) != TS_TRUE)
+        return r;
+
     if (result != NULL)
     {
+        tsbool    r;
+
         /*
          * If there is an intersection, then check explicitly for matching
          * endpoints since there may be rounding effects with annoying LSB
          * residue.
          */
-        if (point_eq_point(&lseg->p[0], &interpt))
+        if ((r = point_eq_point(&lseg->p[0], &interpt)) == TS_TRUE)
             *result = lseg->p[0];
-        else if (point_eq_point(&lseg->p[1], &interpt))
+        else if (r == TS_FALSE &&
+                 (r = point_eq_point(&lseg->p[1], &interpt)) == TS_TRUE)
             *result = lseg->p[1];
-        else
+        else if (r == TS_FALSE)
             *result = interpt;
+        else
+            return r;
     }
 
-    return true;
+    return TS_TRUE;
 }
 
 /*---------------------------------------------------------------------
@@ -2971,8 +3030,15 @@ lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)
     bool        anynan = false;
 
     /* First, we handle the case when the line segments are intersecting. */
-    if (lseg_interpt_lseg(result, on_lseg, to_lseg))
-        return 0.0;
+    switch(lseg_interpt_lseg(result, on_lseg, to_lseg))
+    {
+        case TS_TRUE:
+            return 0.0;
+        case TS_NULL:
+            return get_float8_nan();
+        case TS_FALSE:
+            break;
+    }
 
     /*
      * Then, we find the closest points from the endpoints of the second line
@@ -3050,12 +3116,19 @@ box_closept_point(Point *result, BOX *box, Point *pt)
     LSEG        lseg;
     bool        anynan = false;
 
-    if (box_contain_point(box, pt))
+    switch(box_contain_point(box, pt))
     {
-        if (result != NULL)
-            *result = *pt;
+        case TS_TRUE:
+            if (result != NULL)
+                *result = *pt;
 
-        return 0.0;
+            return 0.0;
+
+        case TS_NULL:
+            return get_float8_nan();
+
+        case TS_FALSE:
+            break;
     }
 
     /* pairwise check lseg distances */
@@ -3139,9 +3212,17 @@ lseg_closept_line(Point *result, LSEG *lseg, LINE *line)
 {
     float8        dist1,
                 dist2;
+    tsbool        r;
 
-    if (lseg_interpt_line(result, lseg, line))
+    if ((r = lseg_interpt_line(result, lseg, line)) == TS_TRUE)
         return 0.0;
+    else if (r == TS_NULL)
+    {
+        if (result != NULL)
+            result->x = result->y = get_float8_nan();
+
+        return get_float8_nan();
+    }
 
     dist1 = line_closept_point(NULL, line, &lseg->p[0]);
     dist2 = line_closept_point(NULL, line, &lseg->p[1]);
@@ -3204,8 +3285,15 @@ box_closept_lseg(Point *result, BOX *box, LSEG *lseg)
                 closept;
     LSEG        bseg;
 
-    if (box_interpt_lseg(result, box, lseg))
-        return 0.0;
+    switch (box_interpt_lseg(result, box, lseg))
+    {
+        case TS_TRUE:
+            return 0.0;
+        case TS_NULL:
+            return get_float8_nan();
+        case TS_FALSE:
+            break;
+    }
 
     /* pairwise check lseg distances */
     point.x = box->low.x;
@@ -3269,7 +3357,7 @@ close_sb(PG_FUNCTION_ARGS)
 /*
  *        Does the point satisfy the equation?
  */
-static bool
+static tsbool
 line_contain_point(LINE *line, Point *point)
 {
     /*
@@ -3283,7 +3371,7 @@ line_contain_point(LINE *line, Point *point)
         Assert(line->B != 0.0);
 
         /* inf == inf here */
-        return FPeq(point->y, -line->C / line->B);
+        return FPTeq(point->y, -line->C / line->B);
     }
     else if (line->B == 0.0)
     {
@@ -3291,13 +3379,13 @@ line_contain_point(LINE *line, Point *point)
         Assert(line->A != 0.0);
 
         /* inf == inf here */
-        return FPeq(point->x, -line->C / line->A);
+        return FPTeq(point->x, -line->C / line->A);
     }
 
-    return FPzero(float8_pl(
-                      float8_pl(float8_mul(line->A, point->x),
-                                float8_mul(line->B, point->y)),
-                      line->C));
+    return FPTzero(float8_pl(
+                       float8_pl(float8_mul(line->A, point->x),
+                                 float8_mul(line->B, point->y)),
+                       line->C));
 }
 
 Datum
@@ -3306,7 +3394,7 @@ on_pl(PG_FUNCTION_ARGS)
     Point       *pt = PG_GETARG_POINT_P(0);
     LINE       *line = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(line_contain_point(line, pt));
+    PG_RETURN_TSBOOL(line_contain_point(line, pt));
 }
 
 
@@ -3314,12 +3402,12 @@ on_pl(PG_FUNCTION_ARGS)
  *        Determine colinearity by detecting a triangle inequality.
  * This algorithm seems to behave nicely even with lsb residues - tgl 1997-07-09
  */
-static bool
+static tsbool
 lseg_contain_point(LSEG *lseg, Point *pt)
 {
-    return FPeq(point_dt(pt, &lseg->p[0]) +
-                point_dt(pt, &lseg->p[1]),
-                point_dt(&lseg->p[0], &lseg->p[1]));
+    return FPTeq(point_dt(pt, &lseg->p[0]) +
+                 point_dt(pt, &lseg->p[1]),
+                 point_dt(&lseg->p[0], &lseg->p[1]));
 }
 
 Datum
@@ -3328,18 +3416,25 @@ on_ps(PG_FUNCTION_ARGS)
     Point       *pt = PG_GETARG_POINT_P(0);
     LSEG       *lseg = PG_GETARG_LSEG_P(1);
 
-    PG_RETURN_BOOL(lseg_contain_point(lseg, pt));
+    PG_RETURN_TSBOOL(lseg_contain_point(lseg, pt));
 }
 
 
 /*
  * Check whether the point is in the box or on its border
  */
-static bool
+static tsbool
 box_contain_point(BOX *box, Point *point)
 {
-    return box->high.x >= point->x && box->low.x <= point->x &&
-        box->high.y >= point->y && box->low.y <= point->y;
+    if (box->high.x >= point->x && box->low.x <= point->x &&
+        box->high.y >= point->y && box->low.y <= point->y)
+        return TS_TRUE;
+    else if (!isnan(box->high.x) && !isnan(box->high.y) &&
+             !isnan(box->low.x) && !isnan(box->low.y) &&
+             !isnan(point->x) && !isnan(point->y))
+        return TS_FALSE;
+
+    return TS_NULL;
 }
 
 Datum
@@ -3348,7 +3443,7 @@ on_pb(PG_FUNCTION_ARGS)
     Point       *pt = PG_GETARG_POINT_P(0);
     BOX           *box = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_point(box, pt));
+    PG_RETURN_TSBOOL(box_contain_point(box, pt));
 }
 
 Datum
@@ -3357,7 +3452,7 @@ box_contain_pt(PG_FUNCTION_ARGS)
     BOX           *box = PG_GETARG_BOX_P(0);
     Point       *pt = PG_GETARG_POINT_P(1);
 
-    PG_RETURN_BOOL(box_contain_point(box, pt));
+    PG_RETURN_TSBOOL(box_contain_point(box, pt));
 }
 
 /* on_ppath -
@@ -3380,24 +3475,38 @@ on_ppath(PG_FUNCTION_ARGS)
                 n;
     float8        a,
                 b;
+    int            inside;
 
     /*-- OPEN --*/
     if (!path->closed)
     {
+        tsbool r;
+
         n = path->npts - 1;
         a = point_dt(pt, &path->p[0]);
         for (i = 0; i < n; i++)
         {
             b = point_dt(pt, &path->p[i + 1]);
-            if (FPeq(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1])))
-                PG_RETURN_BOOL(true);
+            r = FPTeq(float8_pl(a, b), point_dt(&path->p[i], &path->p[i + 1]));
+            if (r != TS_FALSE)
+                PG_RETURN_TSBOOL(r);
             a = b;
         }
+        /* See the PG_RETURN_BOOL at the end of this function */
         PG_RETURN_BOOL(false);
     }
 
     /*-- CLOSED --*/
-    PG_RETURN_BOOL(point_inside(pt, path->npts, path->p) != 0);
+    inside = point_inside(pt, path->npts, path->p);
+
+    if (inside < 0)
+        PG_RETURN_NULL();
+
+    /*
+     * PG_RETURN_BOOL is compatible and faster than PG_RETURN_TSBOOL when the
+     * value is guaranteed to be in bool.
+     */
+    PG_RETURN_BOOL(inside != 0);
 }
 
 
@@ -3412,8 +3521,8 @@ on_sl(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     LINE       *line = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(line_contain_point(line, &lseg->p[0]) &&
-                   line_contain_point(line, &lseg->p[1]));
+    PG_RETURN_TSBOOL(TS_AND2(line_contain_point(line, &lseg->p[0]),
+                             line_contain_point(line, &lseg->p[1])));
 }
 
 
@@ -3422,11 +3531,11 @@ on_sl(PG_FUNCTION_ARGS)
  *
  * It is, if both of its points are in the box or on its border.
  */
-static bool
+static tsbool
 box_contain_lseg(BOX *box, LSEG *lseg)
 {
-    return box_contain_point(box, &lseg->p[0]) &&
-        box_contain_point(box, &lseg->p[1]);
+    return TS_AND2(box_contain_point(box, &lseg->p[0]),
+                   box_contain_point(box, &lseg->p[1]));
 }
 
 Datum
@@ -3435,7 +3544,7 @@ on_sb(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     BOX           *box = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_contain_lseg(box, lseg));
+    PG_RETURN_TSBOOL(box_contain_lseg(box, lseg));
 }
 
 /*---------------------------------------------------------------------
@@ -3449,7 +3558,7 @@ inter_sl(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     LINE       *line = PG_GETARG_LINE_P(1);
 
-    PG_RETURN_BOOL(lseg_interpt_line(NULL, lseg, line));
+    PG_RETURN_TSBOOL(lseg_interpt_line(NULL, lseg, line));
 }
 
 
@@ -3468,12 +3577,13 @@ inter_sl(PG_FUNCTION_ARGS)
  * Optimize for non-intersection by checking for box intersection first.
  * - thomas 1998-01-30
  */
-static bool
+static tsbool
 box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
 {
     BOX            lbox;
     LSEG        bseg;
     Point        point;
+    tsbool        r;
 
     lbox.low.x = float8_min(lseg->p[0].x, lseg->p[1].x);
     lbox.low.y = float8_min(lseg->p[0].y, lseg->p[1].y);
@@ -3481,8 +3591,8 @@ box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
     lbox.high.y = float8_max(lseg->p[0].y, lseg->p[1].y);
 
     /* nothing close to overlap? then not going to intersect */
-    if (!box_ov(&lbox, box))
-        return false;
+    if ((r = box_ov(&lbox, box)) != TS_TRUE)
+        return r;
 
     if (result != NULL)
     {
@@ -3491,30 +3601,30 @@ box_interpt_lseg(Point *result, BOX *box, LSEG *lseg)
     }
 
     /* an endpoint of segment is inside box? then clearly intersects */
-    if (box_contain_point(box, &lseg->p[0]) ||
-        box_contain_point(box, &lseg->p[1]))
-        return true;
+    if ((r = TS_OR2(box_contain_point(box, &lseg->p[0]),
+                    box_contain_point(box, &lseg->p[1]))) != TS_FALSE)
+        return r;
 
     /* pairwise check lseg intersections */
     point.x = box->low.x;
     point.y = box->high.y;
     statlseg_construct(&bseg, &box->low, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     statlseg_construct(&bseg, &box->high, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     point.x = box->high.x;
     point.y = box->low.y;
     statlseg_construct(&bseg, &box->low, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     statlseg_construct(&bseg, &box->high, &point);
-    if (lseg_interpt_lseg(NULL, &bseg, lseg))
-        return true;
+    if ((r = lseg_interpt_lseg(NULL, &bseg, lseg)) != TS_FALSE)
+        return r;
 
     /* if we dropped through, no two segs intersected */
     return false;
@@ -3526,7 +3636,7 @@ inter_sb(PG_FUNCTION_ARGS)
     LSEG       *lseg = PG_GETARG_LSEG_P(0);
     BOX           *box = PG_GETARG_BOX_P(1);
 
-    PG_RETURN_BOOL(box_interpt_lseg(NULL, box, lseg));
+    PG_RETURN_TSBOOL(box_interpt_lseg(NULL, box, lseg));
 }
 
 
@@ -3541,6 +3651,7 @@ inter_lb(PG_FUNCTION_ARGS)
     LSEG        bseg;
     Point        p1,
                 p2;
+    tsbool        r;
 
     /* pairwise check lseg intersections */
     p1.x = box->low.x;
@@ -3548,25 +3659,28 @@ inter_lb(PG_FUNCTION_ARGS)
     p2.x = box->low.x;
     p2.y = box->high.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
     p1.x = box->high.x;
     p1.y = box->high.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
     p2.x = box->high.x;
     p2.y = box->low.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
     p1.x = box->low.x;
     p1.y = box->low.y;
     statlseg_construct(&bseg, &p1, &p2);
-    if (lseg_interpt_line(NULL, &bseg, line))
-        PG_RETURN_BOOL(true);
+    if ((r = lseg_interpt_line(NULL, &bseg, line)) != TS_FALSE)
+        PG_RETURN_TSBOOL(r);
 
-    /* if we dropped through, no intersection */
+    /*
+     * if we dropped through, no intersection "false" doesn't need
+     * PG_RETURN_TSBOOL()
+     */
     PG_RETURN_BOOL(false);
 }
 
@@ -3748,6 +3862,9 @@ poly_left(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.x < polyb->boundbox.low.x;
 
     /*
@@ -3771,6 +3888,9 @@ poly_overleft(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.x <= polyb->boundbox.high.x;
 
     /*
@@ -3794,6 +3914,9 @@ poly_right(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.x > polyb->boundbox.high.x;
 
     /*
@@ -3817,6 +3940,9 @@ poly_overright(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.x >= polyb->boundbox.low.x;
 
     /*
@@ -3840,6 +3966,9 @@ poly_below(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.y < polyb->boundbox.low.y;
 
     /*
@@ -3863,6 +3992,9 @@ poly_overbelow(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.high.y <= polyb->boundbox.high.y;
 
     /*
@@ -3886,6 +4018,9 @@ poly_above(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.y > polyb->boundbox.high.y;
 
     /*
@@ -3909,6 +4044,9 @@ poly_overabove(PG_FUNCTION_ARGS)
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
     bool        result;
 
+    if (isnan(polya->boundbox.high.x) || isnan(polyb->boundbox.high.x))
+        PG_RETURN_NULL();
+
     result = polya->boundbox.low.y >= polyb->boundbox.low.y;
 
     /*
@@ -3927,16 +4065,20 @@ poly_overabove(PG_FUNCTION_ARGS)
  * Check all points for matches in both forward and reverse
  *    direction since polygons are non-directional and are
  *    closed shapes.
+ *
+ * XXX: returns TS_FALSE when the two polygons consists of
+ * different number of points even if any of the points were
+ * NaN.  It might be thewrong defintion.
  *-------------------------------------------------------*/
 Datum
 poly_same(PG_FUNCTION_ARGS)
 {
     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
-    bool        result;
+    tsbool        result;
 
     if (polya->npts != polyb->npts)
-        result = false;
+        result = TS_FALSE;
     else
         result = plist_same(polya->npts, polya->p, polyb->p);
 
@@ -3952,22 +4094,25 @@ poly_same(PG_FUNCTION_ARGS)
 /*-----------------------------------------------------------------
  * Determine if polygon A overlaps polygon B
  *-----------------------------------------------------------------*/
-static bool
+static tsbool
 poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
 {
-    bool        result;
+    tsbool        result;
 
     Assert(polya->npts > 0 && polyb->npts > 0);
 
     /* Quick check by bounding box */
     result = box_ov(&polya->boundbox, &polyb->boundbox);
 
+    if (result == TS_NULL)
+        return TS_NULL;
+
     /*
      * Brute-force algorithm - try to find intersected edges, if so then
      * polygons are overlapped else check is one polygon inside other or not
      * by testing single point of them.
      */
-    if (result)
+    if (result == TS_TRUE)
     {
         int            ia,
                     ib;
@@ -3976,9 +4121,9 @@ poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
 
         /* Init first of polya's edge with last point */
         sa.p[0] = polya->p[polya->npts - 1];
-        result = false;
+        result = TS_FALSE;
 
-        for (ia = 0; ia < polya->npts && !result; ia++)
+        for (ia = 0; ia < polya->npts && result != TS_TRUE; ia++)
         {
             /* Second point of polya's edge is a current one */
             sa.p[1] = polya->p[ia];
@@ -3999,8 +4144,12 @@ poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
             sa.p[0] = sa.p[1];
         }
 
-        if (!result)
+        if (result == TS_NULL)
+            return TS_NULL;
+
+        if (result == TS_FALSE)
         {
+            /* in the case of NaN is handled ealier */
             result = (point_inside(polya->p, polyb->npts, polyb->p) ||
                       point_inside(polyb->p, polya->npts, polya->p));
         }
@@ -4014,7 +4163,7 @@ poly_overlap(PG_FUNCTION_ARGS)
 {
     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
-    bool        result;
+    tsbool        result;
 
     result = poly_overlap_internal(polya, polyb);
 
@@ -4024,7 +4173,7 @@ poly_overlap(PG_FUNCTION_ARGS)
     PG_FREE_IF_COPY(polya, 0);
     PG_FREE_IF_COPY(polyb, 1);
 
-    PG_RETURN_BOOL(result);
+    PG_RETURN_TSBOOL(result);
 }
 
 /*
@@ -4038,35 +4187,62 @@ poly_overlap(PG_FUNCTION_ARGS)
  *      mean that segment is in polygon!
  */
 
-static bool
+static tsbool
 touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
 {
     /* point a is on s, b is not */
     LSEG        t;
+    tsbool        r;
 
     t.p[0] = *a;
     t.p[1] = *b;
 
-    if (point_eq_point(a, s->p))
+    /*
+     * assume no parameters have NaN, so the tsbool functions shouldn't return
+     * TS_NULL.
+     */
+    Assert(!isnan(a->x) && !isnan(a->y) && !isnan(b->x) && !isnan(b->y) &&
+           !isnan(poly->boundbox.high.x));
+    if ((r = point_eq_point(a, s->p)) == TS_TRUE)
     {
-        if (lseg_contain_point(&t, s->p + 1))
-            return lseg_inside_poly(b, s->p + 1, poly, start);
+        switch(lseg_contain_point(&t, s->p + 1))
+        {
+            case TS_TRUE:
+                return lseg_inside_poly(b, s->p + 1, poly, start);
+            case TS_NULL:
+                return TS_NULL;
+            case TS_FALSE:
+                break;
+        }
     }
-    else if (point_eq_point(a, s->p + 1))
+    else if (r != TS_NULL &&
+             (r = point_eq_point(a, s->p + 1)) == TS_TRUE)
     {
-        if (lseg_contain_point(&t, s->p))
-            return lseg_inside_poly(b, s->p, poly, start);
+        switch(lseg_contain_point(&t, s->p))
+        {
+            case TS_TRUE:
+                return lseg_inside_poly(b, s->p, poly, start);
+            case TS_NULL:
+                return TS_NULL;
+            case TS_FALSE:
+                break;
+        }
     }
-    else if (lseg_contain_point(&t, s->p))
+    else if (r != TS_NULL &&
+             (r = lseg_contain_point(&t, s->p)) == TS_TRUE)
     {
         return lseg_inside_poly(b, s->p, poly, start);
     }
-    else if (lseg_contain_point(&t, s->p + 1))
+    else if (r != TS_NULL &&
+             (r = lseg_contain_point(&t, s->p + 1)) == TS_TRUE)
     {
         return lseg_inside_poly(b, s->p + 1, poly, start);
     }
 
-    return true;                /* may be not true, but that will check later */
+    if (r == TS_NULL)
+        return TS_NULL;
+
+    return TS_TRUE;            /* may be not true, but that will check later */
 }
 
 /*
@@ -4074,61 +4250,75 @@ touched_lseg_inside_poly(Point *a, Point *b, LSEG *s, POLYGON *poly, int start)
  * start is used for optimization - function checks
  * polygon's edges starting from start
  */
-static bool
+static tsbool
 lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
 {
     LSEG        s,
                 t;
     int            i;
-    bool        res = true,
-                intersection = false;
+    tsbool        res = TS_TRUE;
+    bool        intersection = false;
+    tsbool        res1;
+    tsbool        res2;
+    tsbool        maybe_contained;
 
     t.p[0] = *a;
     t.p[1] = *b;
     s.p[0] = poly->p[(start == 0) ? (poly->npts - 1) : (start - 1)];
 
     /* Fast path. Check against boundbox. Also checks NaNs. */
-    if (!box_contain_point(&poly->boundbox, a) ||
-        !box_contain_point(&poly->boundbox, b))
-        return false;
+    res1 = box_contain_point(&poly->boundbox, a);
+    res2 = box_contain_point(&poly->boundbox, b);
+
+    maybe_contained = TS_AND2(res1, res2);
+    if (maybe_contained != TS_TRUE)
+        return maybe_contained;
 
     for (i = start; i < poly->npts && res; i++)
     {
         Point        interpt;
+        tsbool        r;
 
         CHECK_FOR_INTERRUPTS();
 
         s.p[1] = poly->p[i];
 
-        if (lseg_contain_point(&s, t.p))
+        if ((r = lseg_contain_point(&s, t.p)) == TS_TRUE)
         {
-            if (lseg_contain_point(&s, t.p + 1))
-                return true;    /* t is contained by s */
+            r = lseg_contain_point(&s, t.p + 1);
+            if (r != TS_FALSE)
+                return r;        /* t is contained by s, or NaN involved */
 
             /* Y-cross */
             res = touched_lseg_inside_poly(t.p, t.p + 1, &s, poly, i + 1);
         }
-        else if (lseg_contain_point(&s, t.p + 1))
+        else if (r != TS_NULL &&
+                 (r = lseg_contain_point(&s, t.p + 1)) == TS_TRUE)
         {
             /* Y-cross */
             res = touched_lseg_inside_poly(t.p + 1, t.p, &s, poly, i + 1);
         }
-        else if (lseg_interpt_lseg(&interpt, &t, &s))
+        else if (r != TS_NULL &&
+                 (r = lseg_interpt_lseg(&interpt, &t, &s)) == TS_TRUE)
         {
             /*
              * segments are X-crossing, go to check each subsegment
              */
 
             intersection = true;
+
+            /* the calls below won't return TS_NULL */
             res = lseg_inside_poly(t.p, &interpt, poly, i + 1);
             if (res)
                 res = lseg_inside_poly(t.p + 1, &interpt, poly, i + 1);
         }
+        else if (r == TS_NULL)
+            return TS_NULL;
 
         s.p[0] = s.p[1];
     }
 
-    if (res && !intersection)
+    if (res == TS_TRUE && !intersection)
     {
         Point        p;
 
@@ -4139,7 +4329,19 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
         p.x = float8_div(float8_pl(t.p[0].x, t.p[1].x), 2.0);
         p.y = float8_div(float8_pl(t.p[0].y, t.p[1].y), 2.0);
 
-        res = point_inside(&p, poly->npts, poly->p);
+        switch (point_inside(&p, poly->npts, poly->p))
+        {
+            case 0:
+                res = TS_FALSE;
+                break;
+            case 1:
+            case 2:
+                res = TS_TRUE;
+                break;
+            case -1:
+                res = TS_NULL;
+                break;
+        }
     }
 
     return res;
@@ -4148,11 +4350,12 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start)
 /*
  * Check whether the first polygon contains the second
  */
-static bool
+static tsbool
 poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
 {
     int            i;
     LSEG        s;
+    tsbool        r;
 
     Assert(contains_poly->npts > 0 && contained_poly->npts > 0);
 
@@ -4160,20 +4363,22 @@ poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly)
      * Quick check to see if contained's bounding box is contained in
      * contains' bb.
      */
-    if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox))
-        return false;
+    r = box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox);
+    if (r != TS_TRUE)
+        return r;
 
     s.p[0] = contained_poly->p[contained_poly->npts - 1];
 
     for (i = 0; i < contained_poly->npts; i++)
     {
         s.p[1] = contained_poly->p[i];
-        if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0))
-            return false;
+        r = lseg_inside_poly(s.p, s.p + 1, contains_poly, 0);
+        if (r != TS_TRUE)
+            return r;
         s.p[0] = s.p[1];
     }
 
-    return true;
+    return TS_TRUE;
 }
 
 Datum
@@ -4181,7 +4386,7 @@ poly_contain(PG_FUNCTION_ARGS)
 {
     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
-    bool        result;
+    tsbool        result;
 
     result = poly_contain_poly(polya, polyb);
 
@@ -4191,7 +4396,7 @@ poly_contain(PG_FUNCTION_ARGS)
     PG_FREE_IF_COPY(polya, 0);
     PG_FREE_IF_COPY(polyb, 1);
 
-    PG_RETURN_BOOL(result);
+    PG_RETURN_TSBOOL(result);
 }
 
 
@@ -4203,7 +4408,7 @@ poly_contained(PG_FUNCTION_ARGS)
 {
     POLYGON    *polya = PG_GETARG_POLYGON_P(0);
     POLYGON    *polyb = PG_GETARG_POLYGON_P(1);
-    bool        result;
+    tsbool        result;
 
     /* Just switch the arguments and pass it off to poly_contain */
     result = poly_contain_poly(polyb, polya);
@@ -4214,7 +4419,7 @@ poly_contained(PG_FUNCTION_ARGS)
     PG_FREE_IF_COPY(polya, 0);
     PG_FREE_IF_COPY(polyb, 1);
 
-    PG_RETURN_BOOL(result);
+    PG_RETURN_TSBOOL(result);
 }
 
 
@@ -4223,8 +4428,12 @@ poly_contain_pt(PG_FUNCTION_ARGS)
 {
     POLYGON    *poly = PG_GETARG_POLYGON_P(0);
     Point       *p = PG_GETARG_POINT_P(1);
+    int            r = point_inside(p, poly->npts, poly->p);
 
-    PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
+    if (r >= 0)
+        PG_RETURN_BOOL(r != 0);
+
+    PG_RETURN_NULL();
 }
 
 Datum
@@ -4232,8 +4441,12 @@ pt_contained_poly(PG_FUNCTION_ARGS)
 {
     Point       *p = PG_GETARG_POINT_P(0);
     POLYGON    *poly = PG_GETARG_POLYGON_P(1);
+    int            r = point_inside(p, poly->npts, poly->p);
 
-    PG_RETURN_BOOL(point_inside(p, poly->npts, poly->p) != 0);
+    if (r >= 0)
+        PG_RETURN_BOOL(r != 0);
+
+    PG_RETURN_NULL();
 }
 
 
@@ -4255,9 +4468,16 @@ poly_distance(PG_FUNCTION_ARGS)
      * path distance will not give the right answer if one poly is entirely
      * within the other.
      */
-    if (poly_overlap_internal(polya, polyb))
-        PG_RETURN_FLOAT8(0.0);
-
+    switch (poly_overlap_internal(polya, polyb))
+    {
+        case TS_TRUE:
+            PG_RETURN_FLOAT8(0.0);
+        case TS_NULL:
+            PG_RETURN_FLOAT8(get_float8_nan());
+        case TS_FALSE:
+            break;
+    }
+            
     /*
      * When they don't overlap, the distance calculation is identical to that
      * for closed paths (i.e., we needn't care about the fact that polygons
@@ -4962,9 +5182,9 @@ circle_same(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(((isnan(circle1->radius) && isnan(circle1->radius)) ||
-                    FPeq(circle1->radius, circle2->radius)) &&
-                   point_eq_point(&circle1->center, &circle2->center));
+    PG_RETURN_TSBOOL(
+        TS_AND2(FPTeq(circle1->radius, circle2->radius),
+                point_eq_point(&circle1->center, &circle2->center)));
 }
 
 /*        circle_overlap    -        does circle1 overlap circle2?
@@ -4975,8 +5195,8 @@ circle_overlap(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
-                        float8_pl(circle1->radius, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(point_dt(&circle1->center, &circle2->center),
+                           float8_pl(circle1->radius, circle2->radius)));
 }
 
 /*        circle_overleft -        is the right edge of circle1 at or left of
@@ -4988,8 +5208,8 @@ circle_overleft(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(float8_pl(circle1->center.x, circle1->radius),
-                        float8_pl(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(float8_pl(circle1->center.x, circle1->radius),
+                           float8_pl(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_left        -        is circle1 strictly left of circle2?
@@ -5000,8 +5220,8 @@ circle_left(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.x, circle1->radius),
-                        float8_mi(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTlt(float8_pl(circle1->center.x, circle1->radius),
+                           float8_mi(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_right    -        is circle1 strictly right of circle2?
@@ -5012,8 +5232,8 @@ circle_right(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.x, circle1->radius),
-                        float8_pl(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTgt(float8_mi(circle1->center.x, circle1->radius),
+                           float8_pl(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_overright    -    is the left edge of circle1 at or right of
@@ -5025,8 +5245,8 @@ circle_overright(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPge(float8_mi(circle1->center.x, circle1->radius),
-                        float8_mi(circle2->center.x, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTge(float8_mi(circle1->center.x, circle1->radius),
+                           float8_mi(circle2->center.x, circle2->radius)));
 }
 
 /*        circle_contained        -        is circle1 contained by circle2?
@@ -5049,8 +5269,8 @@ circle_contain(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(point_dt(&circle1->center, &circle2->center),
-                        float8_mi(circle1->radius, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(point_dt(&circle1->center, &circle2->center),
+                           float8_mi(circle1->radius, circle2->radius)));
 }
 
 
@@ -5062,8 +5282,8 @@ circle_below(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPlt(float8_pl(circle1->center.y, circle1->radius),
-                        float8_mi(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTlt(float8_pl(circle1->center.y, circle1->radius),
+                           float8_mi(circle2->center.y, circle2->radius)));
 }
 
 /*        circle_above    -        is circle1 strictly above circle2?
@@ -5074,8 +5294,8 @@ circle_above(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPgt(float8_mi(circle1->center.y, circle1->radius),
-                        float8_pl(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTgt(float8_mi(circle1->center.y, circle1->radius),
+                           float8_pl(circle2->center.y, circle2->radius)));
 }
 
 /*        circle_overbelow -        is the upper edge of circle1 at or below
@@ -5087,8 +5307,8 @@ circle_overbelow(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(float8_pl(circle1->center.y, circle1->radius),
-                        float8_pl(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTle(float8_pl(circle1->center.y, circle1->radius),
+                           float8_pl(circle2->center.y, circle2->radius)));
 }
 
 /*        circle_overabove    -    is the lower edge of circle1 at or above
@@ -5100,13 +5320,15 @@ circle_overabove(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPge(float8_mi(circle1->center.y, circle1->radius),
-                        float8_mi(circle2->center.y, circle2->radius)));
+    PG_RETURN_TSBOOL(FPTge(float8_mi(circle1->center.y, circle1->radius),
+                           float8_mi(circle2->center.y, circle2->radius)));
 }
 
 
 /*        circle_relop    -        is area(circle1) relop area(circle2), within
  *                                our accuracy constraint?
+ *
+ *  XXX; area comparison doen't consider the NaN-ness of the center location.
  */
 Datum
 circle_eq(PG_FUNCTION_ARGS)
@@ -5114,7 +5336,7 @@ circle_eq(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPeq(circle_ar(circle1), circle_ar(circle2)));
+    PG_RETURN_TSBOOL(FPTeq(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5123,7 +5345,7 @@ circle_ne(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPne(circle_ar(circle1), circle_ar(circle2)));
+    PG_RETURN_TSBOOL(FPTne(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5132,7 +5354,7 @@ circle_lt(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPlt(circle_ar(circle1), circle_ar(circle2)));
+    PG_RETURN_TSBOOL(FPTlt(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5141,7 +5363,7 @@ circle_gt(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPgt(circle_ar(circle1), circle_ar(circle2)));
+    PG_RETURN_TSBOOL(FPTgt(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5150,7 +5372,7 @@ circle_le(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPle(circle_ar(circle1), circle_ar(circle2)));
+    PG_RETURN_TSBOOL(FPTle(circle_ar(circle1), circle_ar(circle2)));
 }
 
 Datum
@@ -5159,7 +5381,7 @@ circle_ge(PG_FUNCTION_ARGS)
     CIRCLE       *circle1 = PG_GETARG_CIRCLE_P(0);
     CIRCLE       *circle2 = PG_GETARG_CIRCLE_P(1);
 
-    PG_RETURN_BOOL(FPge(circle_ar(circle1), circle_ar(circle2)));
+    PG_RETURN_TSBOOL(FPTge(circle_ar(circle1), circle_ar(circle2)));
 }
 
 
@@ -5295,6 +5517,11 @@ circle_contain_pt(PG_FUNCTION_ARGS)
     float8        d;
 
     d = point_dt(&circle->center, point);
+
+    if (isnan(d) || isnan(circle->radius))
+        PG_RETURN_NULL();
+
+    /*  XXX: why don't we use FP(T)le? */
     PG_RETURN_BOOL(d <= circle->radius);
 }
 
@@ -5307,6 +5534,10 @@ pt_contained_circle(PG_FUNCTION_ARGS)
     float8        d;
 
     d = point_dt(&circle->center, point);
+    if (isnan(d) || isnan(circle->radius))
+        PG_RETURN_NULL();
+
+    /*  XXX: why don't we use FP(T)le? */
     PG_RETURN_BOOL(d <= circle->radius);
 }
 
@@ -5533,8 +5764,9 @@ poly_circle(PG_FUNCTION_ARGS)
  ***********************************************************************/
 
 /*
- *    Test to see if the point is inside the polygon, returns 1/0, or 2 if
- *    the point is on the polygon.
+ *    Test to see if the point is inside the polygon, returns 1/0, or 2 if the
+ *    point is on the polygon. -1 means undetermined, in case any operand is an
+ *    invalid object. (-1, 0 and 1 are compatible with tsbool type)
  *    Code adapted but not copied from integer-based routines in WN: A
  *    Server for the HTTP
  *    version 1.15.1, file wn/image.c
@@ -5566,7 +5798,7 @@ point_inside(Point *p, int npts, Point *plist)
 
     /* NaN makes the point cannot be inside the polygon */
     if (unlikely(isnan(x0) || isnan(y0) || isnan(p->x) || isnan(p->y)))
-        return 0;
+        return -1;
 
     prev_x = x0;
     prev_y = y0;
@@ -5579,7 +5811,7 @@ point_inside(Point *p, int npts, Point *plist)
 
         /* NaN makes the point cannot be inside the polygon */
         if (unlikely(isnan(x) || isnan(y)))
-            return 0;
+            return -1;
 
         /* compute previous to current point crossing */
         if ((cross = lseg_crossing(x, y, prev_x, prev_y)) == POINT_ON_POLYGON)
@@ -5606,6 +5838,8 @@ point_inside(Point *p, int npts, Point *plist)
  * Returns +/-1 if one point is on the positive X-axis.
  * Returns 0 if both points are on the positive X-axis, or there is no crossing.
  * Returns POINT_ON_POLYGON if the segment contains (0,0).
+ * This function doesn't check if the parameters contain NaNs, it's the
+ * responsibility of the callers.
  * Wow, that is one confusing API, but it is used above, and when summed,
  * can tell is if a point is in a polygon.
  */
@@ -5670,17 +5904,18 @@ lseg_crossing(float8 x, float8 y, float8 prev_x, float8 prev_y)
 }
 
 
-static bool
+static tsbool
 plist_same(int npts, Point *p1, Point *p2)
 {
     int            i,
                 ii,
                 j;
+    tsbool        r;
 
     /* find match for first point */
     for (i = 0; i < npts; i++)
     {
-        if (point_eq_point(&p2[i], &p1[0]))
+        if ((r = point_eq_point(&p2[i], &p1[0])) == TS_TRUE)
         {
 
             /* match found? then look forward through remaining points */
@@ -5688,26 +5923,29 @@ plist_same(int npts, Point *p1, Point *p2)
             {
                 if (j >= npts)
                     j = 0;
-                if (!point_eq_point(&p2[j], &p1[ii]))
+                if ((r = point_eq_point(&p2[j], &p1[ii])) != TS_TRUE)
                     break;
             }
             if (ii == npts)
-                return true;
+                return TS_TRUE;
 
             /* match not found forwards? then look backwards */
             for (ii = 1, j = i - 1; ii < npts; ii++, j--)
             {
                 if (j < 0)
                     j = (npts - 1);
-                if (!point_eq_point(&p2[j], &p1[ii]))
+                if ((r = point_eq_point(&p2[j], &p1[ii])) != TS_TRUE)
                     break;
             }
             if (ii == npts)
-                return true;
+                return TS_TRUE;
         }
+
+        if (r == TS_NULL)
+            return TS_NULL;
     }
 
-    return false;
+    return TS_FALSE;
 }
 
 
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index 3dfe6e5825..a2e6006a49 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -807,8 +807,15 @@ DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
     return result;
 }
 
+/*
+ * We make a special use of binary operator functions that expects null to be
+ * returned.  Thus only the two-operand version among these functions have
+ * additional parameter to deal with that case. This function has a translation
+ * macro DirectFunctionCall2Coll for the ordinary use.
+ */
 Datum
-DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
+DirectFunctionCall2CollExt(PGFunction func, Oid collation,
+                           Datum arg1, Datum arg2, bool *isnull)
 {
     LOCAL_FCINFO(fcinfo, 2);
     Datum        result;
@@ -822,9 +829,14 @@ DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
 
     result = (*func) (fcinfo);
 
-    /* Check for null result, since caller is clearly not expecting one */
+    /* Interpret null as false, since caller is clearly not expecting one */
     if (fcinfo->isnull)
-        elog(ERROR, "function %p returned NULL", (void *) func);
+    {
+        if (isnull)
+            *isnull = true;
+        else
+            elog(ERROR, "function %p returned NULL", (void *) func);
+    }
 
     return result;
 }
diff --git a/src/include/c.h b/src/include/c.h
index 7e591171a8..4e0a1b0077 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -402,6 +402,13 @@ typedef unsigned char bool;
 #endif                            /* not PG_USE_STDBOOL */
 #endif                            /* not C++ */
 
+/* tri-state boolean, false/true are compatible with bool */
+typedef enum tsbool
+{
+    TS_NULL = -1,
+    TS_FALSE = false,
+    TS_TRUE = true
+} tsbool;
 
 /* ----------------------------------------------------------------
  *                Section 3:    standard system types
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index cec663bdff..e10a5bf5fc 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -503,13 +503,16 @@ extern int no_such_variable
 
 /* These are for invocation of a specifically named function with a
  * directly-computed parameter list.  Note that neither arguments nor result
- * are allowed to be NULL.  Also, the function cannot be one that needs to
+ * are allowed to be C NULL.  Also, the function cannot be one that needs to
  * look at FmgrInfo, since there won't be any.
+ *
+ * Only the 2-operand version can call functions that may return SQL-null to
+ * deal with tristate comparison operators.
  */
 extern Datum DirectFunctionCall1Coll(PGFunction func, Oid collation,
                                      Datum arg1);
-extern Datum DirectFunctionCall2Coll(PGFunction func, Oid collation,
-                                     Datum arg1, Datum arg2);
+extern Datum DirectFunctionCall2CollExt(PGFunction func, Oid collation,
+                                        Datum arg1, Datum arg2, bool *isnull);
 extern Datum DirectFunctionCall3Coll(PGFunction func, Oid collation,
                                      Datum arg1, Datum arg2,
                                      Datum arg3);
@@ -623,6 +626,14 @@ extern Datum OidFunctionCall9Coll(Oid functionId, Oid collation,
                                   Datum arg3, Datum arg4, Datum arg5,
                                   Datum arg6, Datum arg7, Datum arg8,
                                   Datum arg9);
+/*
+ * The two-operand version of these functions is able to deal with SQL-null
+ * returns. Addtional translation macros for compatibility to other versions.
+ */
+#define DirectFunctionCall2Ext(func, arg1, arg2, isnull)                \
+    DirectFunctionCall2CollExt(func, InvalidOid, arg1, arg2, isnull)
+#define DirectFunctionCall2Coll(func, coll, arg1, arg2)            \
+    DirectFunctionCall2CollExt(func, coll, arg1, arg2, NULL)
 
 /* These macros allow the collation argument to be omitted (with a default of
  * InvalidOid, ie, no collation).  They exist mostly for backwards
@@ -631,7 +642,7 @@ extern Datum OidFunctionCall9Coll(Oid functionId, Oid collation,
 #define DirectFunctionCall1(func, arg1) \
     DirectFunctionCall1Coll(func, InvalidOid, arg1)
 #define DirectFunctionCall2(func, arg1, arg2) \
-    DirectFunctionCall2Coll(func, InvalidOid, arg1, arg2)
+    DirectFunctionCall2CollExt(func, InvalidOid, arg1, arg2, NULL)
 #define DirectFunctionCall3(func, arg1, arg2, arg3) \
     DirectFunctionCall3Coll(func, InvalidOid, arg1, arg2, arg3)
 #define DirectFunctionCall4(func, arg1, arg2, arg3, arg4) \
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index 4ab3f9d8ef..a983d6d8d0 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -353,6 +353,115 @@ float8_max(const float8 val1, const float8 val2)
     return float8_gt(val1, val2) ? val1 : val2;
 }
 
+/* tri-state equivalents */
+static inline tsbool
+float4_teq(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 == val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_teq(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 == val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tne(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 != val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tne(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 != val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tlt(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 < val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tlt(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 < val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tle(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 <= val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tle(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 <= val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tgt(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 > val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tgt(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 > val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float4_tge(const float4 val1, const float4 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 >= val2;
+
+    return TS_NULL;
+}
+
+static inline tsbool
+float8_tge(const float8 val1, const float8 val2)
+{
+    if (!isnan(val1) && !isnan(val2))
+        return val1 >= val2;
+
+    return TS_NULL;
+}
+
 /*
  * These two functions return NaN if either input is NaN, else the smaller
  * of the two inputs.  This does NOT follow our usual sort rule, but it is
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 0b87437d83..0825b1f44b 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -19,6 +19,7 @@
 #define GEO_DECLS_H
 
 #include <math.h>
+#include <limits.h>
 
 #include "fmgr.h"
 
@@ -40,6 +41,18 @@
 
 #define EPSILON                    1.0E-06
 
+/* helper function for tri-state checking */
+static inline tsbool
+FP_TSCHECK(double A, double B, bool cond)
+{
+    if (cond)
+        return TS_TRUE;
+    if (!isnan(A) && !isnan(B))
+        return TS_FALSE;
+
+    return TS_NULL;
+}
+
 #ifdef EPSILON
 #define FPzero(A)                (fabs(A) <= EPSILON)
 
@@ -88,6 +101,100 @@ FPge(double A, double B)
 #define FPge(A,B)                ((A) >= (B))
 #endif
 
+
+/* define as inline functions to avoid duplicate evaluation */
+static inline tsbool
+FPTzero(double A)
+{
+    if (fabs(A) <= EPSILON)
+        return TS_TRUE;
+    if (isnan(A))
+        return TS_NULL;
+    return TS_FALSE;
+}
+
+static inline tsbool
+FPTeq(double A, double B)
+{
+    return FP_TSCHECK(A, B, FPeq(A, B));
+}
+
+static inline tsbool
+FPTne(double A, double B)
+{
+    return FP_TSCHECK(A, B, FPne(A, B));
+}
+
+static inline tsbool
+FPTlt(double A, double B)
+{
+    return FP_TSCHECK(A, B, FPlt(A, B));
+}
+
+static inline tsbool
+FPTle(double A, double B)
+{
+    return FP_TSCHECK(A, B, FPle(A, B));
+}
+
+static inline tsbool
+FPTgt(double A, double B)
+{
+    return FP_TSCHECK(A, B, FPgt(A, B));
+}
+
+static inline tsbool
+FPTge(double A, double B)
+{
+    return FP_TSCHECK(A, B, FPge(A, B));
+}
+
+/* https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord */
+#define TS_SIGN(v) (-(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)))
+
+static inline tsbool
+TS_NOT(tsbool a)
+{
+    return (!a) | TS_SIGN(a);
+}
+
+/*
+ * Returns the result of tristate-OR of two tsbools.
+ * The operations on true and false are the same to boolean.  returns TS_NULL
+ * if any of the operands is TS_NULL.
+ */
+static inline tsbool
+TS_OR2(tsbool p1, tsbool p2)
+{
+
+    return p1 | p2;        /* not a boolean operator, but a bitwise operator */
+}
+
+/*
+ * Returns the result of tristate-AND of two tsbools.
+ * The operations on true and false are the same to boolean.  returns TS_NULL
+ * if any of the operands is TS_NULL.
+ */
+static inline tsbool
+TS_AND2(tsbool p1, tsbool p2)
+{
+    return (p1 & p2) | TS_SIGN(p1 | p2);
+}
+
+static inline tsbool
+TS_AND4(tsbool p1, tsbool p2, tsbool p3, tsbool p4)
+{
+    return (p1 & p2 & p3 & p4) | TS_SIGN(p1 | p2 | p3 | p4);
+}
+
+#define PG_RETURN_TSBOOL(e)            \
+    do                                \
+    {                                \
+        if ((e) != TS_NULL)            \
+            PG_RETURN_BOOL(e);        \
+        PG_RETURN_NULL();            \
+    } while (0)
+
 #define HYPOT(A, B)                pg_hypot(A, B)
 
 /*---------------------------------------------------------------------
diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out
index 77ff1b3d7b..5e5969c820 100644
--- a/src/test/regress/expected/geometry.out
+++ b/src/test/regress/expected/geometry.out
@@ -919,7 +919,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (0,0)             | (-5,-12)          | {2.4,-1,0}
  (0,0)             | (1e+300,Infinity) | {-1,0,0}
  (0,0)             | (Infinity,1e+300) | {0,-1,0}
- (0,0)             | (NaN,NaN)         | {NaN,NaN,NaN}
  (0,0)             | (10,10)           | {1,-1,0}
  (-10,0)           | (0,0)             | {0,-1,0}
  (-10,0)           | (-3,4)            | {0.571428571429,-1,5.71428571429}
@@ -928,7 +927,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (-10,0)           | (1e-300,-1e-300)  | {0,-1,0}
  (-10,0)           | (1e+300,Infinity) | {-1,0,-10}
  (-10,0)           | (Infinity,1e+300) | {0,-1,0}
- (-10,0)           | (NaN,NaN)         | {NaN,NaN,NaN}
  (-10,0)           | (10,10)           | {0.5,-1,5}
  (-3,4)            | (0,0)             | {-1.33333333333,-1,0}
  (-3,4)            | (-10,0)           | {0.571428571429,-1,5.71428571429}
@@ -937,7 +935,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (-3,4)            | (1e-300,-1e-300)  | {-1.33333333333,-1,0}
  (-3,4)            | (1e+300,Infinity) | {-1,0,-3}
  (-3,4)            | (Infinity,1e+300) | {0,-1,4}
- (-3,4)            | (NaN,NaN)         | {NaN,NaN,NaN}
  (-3,4)            | (10,10)           | {0.461538461538,-1,5.38461538462}
  (5.1,34.5)        | (0,0)             | {6.76470588235,-1,0}
  (5.1,34.5)        | (-10,0)           | {2.28476821192,-1,22.8476821192}
@@ -946,7 +943,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (5.1,34.5)        | (1e-300,-1e-300)  | {6.76470588235,-1,0}
  (5.1,34.5)        | (1e+300,Infinity) | {-1,0,5.1}
  (5.1,34.5)        | (Infinity,1e+300) | {0,-1,34.5}
- (5.1,34.5)        | (NaN,NaN)         | {NaN,NaN,NaN}
  (5.1,34.5)        | (10,10)           | {-5,-1,60}
  (-5,-12)          | (0,0)             | {2.4,-1,0}
  (-5,-12)          | (-10,0)           | {-2.4,-1,-24}
@@ -955,7 +951,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (-5,-12)          | (1e-300,-1e-300)  | {2.4,-1,0}
  (-5,-12)          | (1e+300,Infinity) | {-1,0,-5}
  (-5,-12)          | (Infinity,1e+300) | {0,-1,-12}
- (-5,-12)          | (NaN,NaN)         | {NaN,NaN,NaN}
  (-5,-12)          | (10,10)           | {1.46666666667,-1,-4.66666666667}
  (1e-300,-1e-300)  | (-10,0)           | {0,-1,-1e-300}
  (1e-300,-1e-300)  | (-3,4)            | {-1.33333333333,-1,3.33333333333e-301}
@@ -963,7 +958,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (1e-300,-1e-300)  | (-5,-12)          | {2.4,-1,-3.4e-300}
  (1e-300,-1e-300)  | (1e+300,Infinity) | {-1,0,1e-300}
  (1e-300,-1e-300)  | (Infinity,1e+300) | {0,-1,-1e-300}
- (1e-300,-1e-300)  | (NaN,NaN)         | {NaN,NaN,NaN}
  (1e-300,-1e-300)  | (10,10)           | {1,-1,-2e-300}
  (1e+300,Infinity) | (0,0)             | {-1,0,1e+300}
  (1e+300,Infinity) | (-10,0)           | {-1,0,1e+300}
@@ -972,7 +966,6 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (1e+300,Infinity) | (-5,-12)          | {-1,0,1e+300}
  (1e+300,Infinity) | (1e-300,-1e-300)  | {-1,0,1e+300}
  (1e+300,Infinity) | (Infinity,1e+300) | {NaN,NaN,NaN}
- (1e+300,Infinity) | (NaN,NaN)         | {NaN,NaN,NaN}
  (1e+300,Infinity) | (10,10)           | {-1,0,1e+300}
  (Infinity,1e+300) | (0,0)             | {0,-1,1e+300}
  (Infinity,1e+300) | (-10,0)           | {0,-1,1e+300}
@@ -981,17 +974,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (Infinity,1e+300) | (-5,-12)          | {0,-1,1e+300}
  (Infinity,1e+300) | (1e-300,-1e-300)  | {0,-1,1e+300}
  (Infinity,1e+300) | (1e+300,Infinity) | {NaN,NaN,NaN}
- (Infinity,1e+300) | (NaN,NaN)         | {NaN,NaN,NaN}
  (Infinity,1e+300) | (10,10)           | {0,-1,1e+300}
- (NaN,NaN)         | (0,0)             | {NaN,NaN,NaN}
- (NaN,NaN)         | (-10,0)           | {NaN,NaN,NaN}
- (NaN,NaN)         | (-3,4)            | {NaN,NaN,NaN}
- (NaN,NaN)         | (5.1,34.5)        | {NaN,NaN,NaN}
- (NaN,NaN)         | (-5,-12)          | {NaN,NaN,NaN}
- (NaN,NaN)         | (1e-300,-1e-300)  | {NaN,NaN,NaN}
- (NaN,NaN)         | (1e+300,Infinity) | {NaN,NaN,NaN}
- (NaN,NaN)         | (Infinity,1e+300) | {NaN,NaN,NaN}
- (NaN,NaN)         | (10,10)           | {NaN,NaN,NaN}
  (10,10)           | (0,0)             | {1,-1,0}
  (10,10)           | (-10,0)           | {0.5,-1,5}
  (10,10)           | (-3,4)            | {0.461538461538,-1,5.38461538462}
@@ -1000,8 +983,7 @@ SELECT p1.f1, p2.f1, line(p1.f1, p2.f1)
  (10,10)           | (1e-300,-1e-300)  | {1,-1,0}
  (10,10)           | (1e+300,Infinity) | {-1,0,10}
  (10,10)           | (Infinity,1e+300) | {0,-1,10}
- (10,10)           | (NaN,NaN)         | {NaN,NaN,NaN}
-(88 rows)
+(70 rows)
 
 -- Closest point to line
 SELECT p.f1, l.s, p.f1 ## l.s FROM POINT_TBL p, LINE_TBL l;
@@ -1310,11 +1292,9 @@ SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s = l2.s;
  {1,-1,0}                              | {1,-1,0}
  {-0.4,-1,-6}                          | {-0.4,-1,-6}
  {-0.000184615384615,-1,15.3846153846} | {-0.000184615384615,-1,15.3846153846}
- {3,NaN,5}                             | {3,NaN,5}
- {NaN,NaN,NaN}                         | {NaN,NaN,NaN}
  {0,-1,3}                              | {0,-1,3}
  {-1,0,3}                              | {-1,0,3}
-(10 rows)
+(8 rows)
 
 -- Parallel to line
 SELECT l1.s, l2.s FROM LINE_TBL l1, LINE_TBL l2 WHERE l1.s ?|| l2.s;
@@ -1865,8 +1845,7 @@ SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s = l2.s;
  [(11,22),(33,44)]             | [(11,22),(33,44)]
  [(-10,2),(-10,3)]             | [(-10,2),(-10,3)]
  [(0,-20),(30,-20)]            | [(0,-20),(30,-20)]
- [(NaN,1),(NaN,90)]            | [(NaN,1),(NaN,90)]
-(8 rows)
+(7 rows)
 
 -- Has points greater than or equal to line segment
 SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s >= l2.s;
@@ -1939,57 +1918,43 @@ SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s != l2.s;
  [(1,2),(3,4)]                 | [(11,22),(33,44)]
  [(1,2),(3,4)]                 | [(-10,2),(-10,3)]
  [(1,2),(3,4)]                 | [(0,-20),(30,-20)]
- [(1,2),(3,4)]                 | [(NaN,1),(NaN,90)]
  [(0,0),(6,6)]                 | [(1,2),(3,4)]
  [(0,0),(6,6)]                 | [(10,-10),(-3,-4)]
  [(0,0),(6,6)]                 | [(-1000000,200),(300000,-40)]
  [(0,0),(6,6)]                 | [(11,22),(33,44)]
  [(0,0),(6,6)]                 | [(-10,2),(-10,3)]
  [(0,0),(6,6)]                 | [(0,-20),(30,-20)]
- [(0,0),(6,6)]                 | [(NaN,1),(NaN,90)]
  [(10,-10),(-3,-4)]            | [(1,2),(3,4)]
  [(10,-10),(-3,-4)]            | [(0,0),(6,6)]
  [(10,-10),(-3,-4)]            | [(-1000000,200),(300000,-40)]
  [(10,-10),(-3,-4)]            | [(11,22),(33,44)]
  [(10,-10),(-3,-4)]            | [(-10,2),(-10,3)]
  [(10,-10),(-3,-4)]            | [(0,-20),(30,-20)]
- [(10,-10),(-3,-4)]            | [(NaN,1),(NaN,90)]
  [(-1000000,200),(300000,-40)] | [(1,2),(3,4)]
  [(-1000000,200),(300000,-40)] | [(0,0),(6,6)]
  [(-1000000,200),(300000,-40)] | [(10,-10),(-3,-4)]
  [(-1000000,200),(300000,-40)] | [(11,22),(33,44)]
  [(-1000000,200),(300000,-40)] | [(-10,2),(-10,3)]
  [(-1000000,200),(300000,-40)] | [(0,-20),(30,-20)]
- [(-1000000,200),(300000,-40)] | [(NaN,1),(NaN,90)]
  [(11,22),(33,44)]             | [(1,2),(3,4)]
  [(11,22),(33,44)]             | [(0,0),(6,6)]
  [(11,22),(33,44)]             | [(10,-10),(-3,-4)]
  [(11,22),(33,44)]             | [(-1000000,200),(300000,-40)]
  [(11,22),(33,44)]             | [(-10,2),(-10,3)]
  [(11,22),(33,44)]             | [(0,-20),(30,-20)]
- [(11,22),(33,44)]             | [(NaN,1),(NaN,90)]
  [(-10,2),(-10,3)]             | [(1,2),(3,4)]
  [(-10,2),(-10,3)]             | [(0,0),(6,6)]
  [(-10,2),(-10,3)]             | [(10,-10),(-3,-4)]
  [(-10,2),(-10,3)]             | [(-1000000,200),(300000,-40)]
  [(-10,2),(-10,3)]             | [(11,22),(33,44)]
  [(-10,2),(-10,3)]             | [(0,-20),(30,-20)]
- [(-10,2),(-10,3)]             | [(NaN,1),(NaN,90)]
  [(0,-20),(30,-20)]            | [(1,2),(3,4)]
  [(0,-20),(30,-20)]            | [(0,0),(6,6)]
  [(0,-20),(30,-20)]            | [(10,-10),(-3,-4)]
  [(0,-20),(30,-20)]            | [(-1000000,200),(300000,-40)]
  [(0,-20),(30,-20)]            | [(11,22),(33,44)]
  [(0,-20),(30,-20)]            | [(-10,2),(-10,3)]
- [(0,-20),(30,-20)]            | [(NaN,1),(NaN,90)]
- [(NaN,1),(NaN,90)]            | [(1,2),(3,4)]
- [(NaN,1),(NaN,90)]            | [(0,0),(6,6)]
- [(NaN,1),(NaN,90)]            | [(10,-10),(-3,-4)]
- [(NaN,1),(NaN,90)]            | [(-1000000,200),(300000,-40)]
- [(NaN,1),(NaN,90)]            | [(11,22),(33,44)]
- [(NaN,1),(NaN,90)]            | [(-10,2),(-10,3)]
- [(NaN,1),(NaN,90)]            | [(0,-20),(30,-20)]
-(56 rows)
+(42 rows)
 
 -- Parallel with line segment
 SELECT l1.s, l2.s FROM LSEG_TBL l1, LSEG_TBL l2 WHERE l1.s ?|| l2.s;
@@ -3680,13 +3645,13 @@ SELECT p.f1, poly.f1, poly.f1 @> p.f1 AS contains
  (Infinity,1e+300) | ((1,2),(7,8),(5,6),(3,-4)) | f
  (Infinity,1e+300) | ((0,0))                    | f
  (Infinity,1e+300) | ((0,1),(0,1))              | f
- (NaN,NaN)         | ((2,0),(2,4),(0,0))        | f
- (NaN,NaN)         | ((3,1),(3,3),(1,0))        | f
- (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | f
- (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | f
- (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | f
- (NaN,NaN)         | ((0,0))                    | f
- (NaN,NaN)         | ((0,1),(0,1))              | f
+ (NaN,NaN)         | ((2,0),(2,4),(0,0))        | 
+ (NaN,NaN)         | ((3,1),(3,3),(1,0))        | 
+ (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | 
+ (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | 
+ (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | 
+ (NaN,NaN)         | ((0,0))                    | 
+ (NaN,NaN)         | ((0,1),(0,1))              | 
  (10,10)           | ((2,0),(2,4),(0,0))        | f
  (10,10)           | ((3,1),(3,3),(1,0))        | f
  (10,10)           | ((1,2),(3,4),(5,6),(7,8))  | f
@@ -3756,13 +3721,13 @@ SELECT p.f1, poly.f1, p.f1 <@ poly.f1 AS contained
  (Infinity,1e+300) | ((1,2),(7,8),(5,6),(3,-4)) | f
  (Infinity,1e+300) | ((0,0))                    | f
  (Infinity,1e+300) | ((0,1),(0,1))              | f
- (NaN,NaN)         | ((2,0),(2,4),(0,0))        | f
- (NaN,NaN)         | ((3,1),(3,3),(1,0))        | f
- (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | f
- (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | f
- (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | f
- (NaN,NaN)         | ((0,0))                    | f
- (NaN,NaN)         | ((0,1),(0,1))              | f
+ (NaN,NaN)         | ((2,0),(2,4),(0,0))        | 
+ (NaN,NaN)         | ((3,1),(3,3),(1,0))        | 
+ (NaN,NaN)         | ((1,2),(3,4),(5,6),(7,8))  | 
+ (NaN,NaN)         | ((7,8),(5,6),(3,4),(1,2))  | 
+ (NaN,NaN)         | ((1,2),(7,8),(5,6),(3,-4)) | 
+ (NaN,NaN)         | ((0,0))                    | 
+ (NaN,NaN)         | ((0,1),(0,1))              | 
  (10,10)           | ((2,0),(2,4),(0,0))        | f
  (10,10)           | ((3,1),(3,3),(1,0))        | f
  (10,10)           | ((1,2),(3,4),(5,6),(7,8))  | f
@@ -4339,9 +4304,7 @@ SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 ~= c2.f1;
  <(100,200),10> | <(100,200),10>
  <(100,1),115>  | <(100,1),115>
  <(3,5),0>      | <(3,5),0>
- <(3,5),NaN>    | <(3,5),0>
- <(3,5),NaN>    | <(3,5),NaN>
-(9 rows)
+(7 rows)
 
 -- Overlap with circle
 SELECT c1.f1, c2.f1 FROM CIRCLE_TBL c1, CIRCLE_TBL c2 WHERE c1.f1 && c2.f1;
diff --git a/src/test/regress/expected/line.out b/src/test/regress/expected/line.out
index fe106589c6..175fb3ab45 100644
--- a/src/test/regress/expected/line.out
+++ b/src/test/regress/expected/line.out
@@ -78,10 +78,11 @@ select * from LINE_TBL;
  {-1,0,3}
 (10 rows)
 
+-- Both of them return null
 select '{nan, 1, nan}'::line = '{nan, 1, nan}'::line as true,
        '{nan, 1, nan}'::line = '{nan, 2, nan}'::line as false;
  true | false 
 ------+-------
- t    | f
+      | 
 (1 row)
 
diff --git a/src/test/regress/expected/point.out b/src/test/regress/expected/point.out
index 1dc535d1b3..7afcf07241 100644
--- a/src/test/regress/expected/point.out
+++ b/src/test/regress/expected/point.out
@@ -118,8 +118,7 @@ SELECT p.* FROM POINT_TBL p
  (1e-300,-1e-300)
  (1e+300,Infinity)
  (Infinity,1e+300)
- (NaN,NaN)
-(7 rows)
+(6 rows)
 
 SELECT p.* FROM POINT_TBL p
    WHERE p.f1 <@ path '[(0,0),(-10,0),(-10,10)]';
@@ -140,8 +139,7 @@ SELECT p.* FROM POINT_TBL p
  (1e-300,-1e-300)
  (1e+300,Infinity)
  (Infinity,1e+300)
- (NaN,NaN)
-(7 rows)
+(6 rows)
 
 SELECT p.f1, p.f1 <-> point '(0,0)' AS dist
    FROM POINT_TBL p
diff --git a/src/test/regress/sql/line.sql b/src/test/regress/sql/line.sql
index f589ffecc8..56afd3cc63 100644
--- a/src/test/regress/sql/line.sql
+++ b/src/test/regress/sql/line.sql
@@ -38,5 +38,6 @@ INSERT INTO LINE_TBL VALUES (line(point '(1,0)', point '(1,0)'));
 
 select * from LINE_TBL;
 
+-- Both of them return null
 select '{nan, 1, nan}'::line = '{nan, 1, nan}'::line as true,
        '{nan, 1, nan}'::line = '{nan, 2, nan}'::line as false;
-- 
2.27.0


В списке pgsql-hackers по дате отправления:

Предыдущее
От: "kuroda.hayato@fujitsu.com"
Дата:
Сообщение: RE: [Proposal] Add foreign-server health checks infrastructure
Следующее
От: Masahiko Sawada
Дата:
Сообщение: Re: parallel vacuum comments