v8-0007-FIX-NaN-special-cases.patch

application/octet-stream

Filename: v8-0007-FIX-NaN-special-cases.patch
Type: application/octet-stream
Part: 6
Message: Re: SLOPE - Planner optimizations on monotonic expressions.

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v8-0007
Subject: FIX: NaN special cases
File+
src/backend/optimizer/path/pathkeys.c 74 19
src/backend/utils/adt/misc.c 221 178
src/include/catalog/pg_proc.dat 4 2
src/include/nodes/pathnodes.h 4 1
src/include/nodes/plannodes.h 6 0
src/test/regress/expected/slope.out 114 2
src/test/regress/sql/slope.sql 114 2
From 732b52575fc6a86ae4017272dd04c99ee7f1993a Mon Sep 17 00:00:00 2001
From: Alexandre Felipe <o.alexandre.felipe@gmail.com>
Date: Sun, 17 May 2026 17:12:31 +0100
Subject: [PATCH v8 7/7] FIX: NaN special cases

---
 src/backend/optimizer/path/pathkeys.c |  93 ++++--
 src/backend/utils/adt/misc.c          | 399 ++++++++++++++------------
 src/include/catalog/pg_proc.dat       |   6 +-
 src/include/nodes/pathnodes.h         |   5 +-
 src/include/nodes/plannodes.h         |   6 +
 src/test/regress/expected/slope.out   | 116 +++++++-
 src/test/regress/sql/slope.sql        | 116 +++++++-
 7 files changed, 537 insertions(+), 204 deletions(-)

diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index d5f6439f7f8..cddea99bf78 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -19,6 +19,7 @@
 
 #include "access/stratnum.h"
 #include "catalog/pg_opfamily.h"
+#include "catalog/pg_type.h"
 #include "fmgr.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/supportnodes.h"
@@ -41,7 +42,8 @@ static bool matches_boolean_partition_clause(RestrictInfo *rinfo,
 											 int partkeycol);
 static Var *find_var_for_subquery_tle(RelOptInfo *rel, TargetEntry *tle);
 static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
-static MonotonicFunction get_expr_slope_wrt(Expr *expr, Expr *target);
+static MonotonicFunction get_expr_slope_wrt(Expr *expr, Expr *target,
+											bool target_cannan);
 static PathKey *slope_emit_pathkey(PlannerInfo *root, PathKey *pk,
 								   Expr *indexkey, bool reverse_sort,
 								   bool nulls_first);
@@ -861,6 +863,44 @@ get_variation_source(Expr *expr, Expr **inner_out, Index *relid_out)
 	}
 }
 
+/*
+ * var_type_can_nan
+ *		True if a variation-source column can hold NaN values.
+ */
+static bool
+var_type_can_nan(Var *var)
+{
+	switch (var->vartype)
+	{
+		case FLOAT4OID:
+		case FLOAT8OID:
+		case NUMERICOID:
+			return true;
+		default:
+			return false;
+	}
+}
+
+/*
+ * finalize_slope
+ *		Apply NaN-aware restrictions to a composed monotonicity slope.
+ *
+ * Decreasing functions do not preserve sort order when the variation
+ * source can produce NaNs.  Flat functions are treated similarly, since
+ * NaNs and infinities in the source can still change the result.  An
+ * overall decreasing (or flat) result is only rejected once composition
+ * reaches the source.  For example, (1 - (1 - x)) is increasing in x even
+ * though (1 - x) is decreasing.
+ */
+static MonotonicFunction
+finalize_slope(MonotonicFunction slope, bool target_cannan)
+{
+	if (target_cannan &&
+		(slope == MONOTONICFUNC_DECREASING || slope == MONOTONICFUNC_BOTH))
+		return MONOTONICFUNC_NONE;
+	return slope;
+}
+
 /*
  * get_expr_slope_wrt
  *	  Determine the monotonicity slope of an expression with respect to
@@ -869,10 +909,11 @@ get_variation_source(Expr *expr, Expr **inner_out, Index *relid_out)
  * Returns the slope of 'expr' with respect to 'target'
  *   MONOTONICFUNC_INCREASING: monotonically increasing
  *   MONOTONICFUNC_DECREASING: monotonically decreasing
+ *   MONOTONICFUNC_BOTH:       independent of target
  *   MONOTONICFUNC_NONE:       cannot determine monotonicity
  */
 static MonotonicFunction
-get_expr_slope_wrt(Expr *expr, Expr *target)
+get_expr_slope_wrt(Expr *expr, Expr *target, bool target_cannan)
 {
 	MonotonicFunction slope = MONOTONICFUNC_INCREASING;
 
@@ -889,7 +930,7 @@ get_expr_slope_wrt(Expr *expr, Expr *target)
 
 		/* Check if we've reached the target */
 		if (equal(expr, target))
-			return slope;
+			return finalize_slope(slope, target_cannan);
 
 		/* Skip RelabelType (no-op coercion) */
 		if (IsA(expr, RelabelType))
@@ -944,7 +985,7 @@ get_expr_slope_wrt(Expr *expr, Expr *target)
 		if (req.slopes == NULL || req.nslopes <= 0)
 			return MONOTONICFUNC_NONE;
 
-		/* Find the single non-constant argument */
+		/* Find the single non-constant argument that can affect the result */
 		i = 0;
 		foreach(lc, args)
 		{
@@ -952,27 +993,36 @@ get_expr_slope_wrt(Expr *expr, Expr *target)
 
 			if (!IsA(arg, Const))
 			{
+				MonotonicFunction arg_slope;
+
+				if (unlikely(i >= req.nslopes))
+					return MONOTONICFUNC_NONE;
+
+				arg_slope = req.slopes[i];
+				if (arg_slope == MONOTONICFUNC_BOTH)
+				{
+					i++;
+					continue;
+				}
+				if (arg_slope == MONOTONICFUNC_DECREASING)
+					func_arg_slope = MONOTONICFUNC_DECREASING;
+				else if (arg_slope != MONOTONICFUNC_INCREASING)
+					return MONOTONICFUNC_NONE;
+
 				if (next_expr != NULL)
 				{
 					/* Multivariate - check if this is the target */
-					return equal(expr, target) ? slope : MONOTONICFUNC_NONE;
+					if (equal(expr, target))
+						return finalize_slope(slope, target_cannan);
+					return MONOTONICFUNC_NONE;
 				}
 				next_expr = arg;
-				if (likely(i < req.nslopes))
-				{
-					if (req.slopes[i] == MONOTONICFUNC_DECREASING)
-						func_arg_slope = MONOTONICFUNC_DECREASING;
-					else if (req.slopes[i] != MONOTONICFUNC_INCREASING)
-						return MONOTONICFUNC_NONE;
-				}
-				else
-					return MONOTONICFUNC_NONE;
 			}
 			i++;
 		}
 
 		if (next_expr == NULL)
-			return MONOTONICFUNC_NONE;	/* all constant */
+			return finalize_slope(MONOTONICFUNC_BOTH, target_cannan);
 
 		/* Compose slopes */
 		if (func_arg_slope == MONOTONICFUNC_DECREASING)
@@ -1007,7 +1057,8 @@ precompute_slope_pathkeys(PlannerInfo *root)
 
 		pk->pk_var = NULL;
 		pk->pk_varrelid = 0;
-		pk->pk_slope = MONOTONICFUNC_BOTH;	/* not yet computed */
+		pk->pk_slope = MONOTONICFUNC_UNKNOWN;
+		pk->pk_var_cannan = false;
 
 		if (pk->pk_eclass->ec_has_volatile ||
 			pk->pk_eclass->ec_members == NIL)
@@ -1024,6 +1075,8 @@ precompute_slope_pathkeys(PlannerInfo *root)
 		if (pk->pk_var == NULL || pk->pk_varrelid == 0 ||
 			pk->pk_var == em->em_expr)
 			pk->pk_var = NULL;
+		else if (IsA(pk->pk_var, Var))
+			pk->pk_var_cannan = var_type_can_nan((Var *) pk->pk_var);
 	}
 }
 
@@ -1188,19 +1241,21 @@ build_index_pathkeys(PlannerInfo *root,
 					{
 						PathKey    *spk;
 
-						if (qpk->pk_slope == MONOTONICFUNC_BOTH)
+						if (qpk->pk_slope == MONOTONICFUNC_UNKNOWN)
 						{
 							EquivalenceMember *em;
 
 							em = linitial(qpk->pk_eclass->ec_members);
 							qpk->pk_slope = get_expr_slope_wrt(em->em_expr,
-															   qpk->pk_var);
+															   qpk->pk_var,
+															   qpk->pk_var_cannan);
 						}
 						spk = slope_emit_pathkey(root, qpk, indexkey,
 												 reverse_sort, nulls_first);
 						if (spk && !pathkey_is_redundant(spk, retval))
 							retval = lappend(retval, spk);
-						slope_matched = true;
+						if (spk)
+							slope_matched = true;
 						continue;
 					}
 
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 19b7b48e80a..5d907a46941 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -33,6 +33,7 @@
 #include "miscadmin.h"
 #include "nodes/miscnodes.h"
 #include "nodes/supportnodes.h"
+#include "nodes/nodeFuncs.h"
 #include "parser/parse_type.h"
 #include "parser/scansup.h"
 #include "pgstat.h"
@@ -1121,84 +1122,46 @@ monotonic_slope_support(Node *rawreq, int nslopes,
 	return PointerGetDatum(NULL);
 }
 
-/*
- * arg0_asc_slope_support
- *		Prosupport: f(x, ...) is monotonically increasing in x.
- */
-Datum
-arg0_asc_slope_support(PG_FUNCTION_ARGS)
-{
-	static const MonotonicFunction pattern[1] = {MONOTONICFUNC_INCREASING};
-
-	return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
-								   lengthof(pattern), pattern);
-}
-
-/*
- * arg0_desc_slope_support
- *		Prosupport: f(x, ...) is monotonically decreasing in x.
- */
-Datum
-arg0_desc_slope_support(PG_FUNCTION_ARGS)
-{
-	static const MonotonicFunction pattern[1] = {MONOTONICFUNC_DECREASING};
-
-	return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
-								   lengthof(pattern), pattern);
-}
 
-/*
- * arg1_asc_slope_support
- *		Prosupport: f(a, x, ...) is monotonically increasing in x.
- */
-Datum
-arg1_asc_slope_support(PG_FUNCTION_ARGS)
-{
-	static const MonotonicFunction pattern[2] = {MONOTONICFUNC_NONE,
-												 MONOTONICFUNC_INCREASING};
-
-	return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
-								   lengthof(pattern), pattern);
-}
-
-/*
- * diff_slope_support
- *		Prosupport: f(x, y) = x - y is increasing in x, decreasing in y.
- */
-Datum
-diff_slope_support(PG_FUNCTION_ARGS)
-{
-	static const MonotonicFunction pattern[2] = {MONOTONICFUNC_INCREASING,
-												 MONOTONICFUNC_DECREASING};
-
-	return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
-								   lengthof(pattern), pattern);
-}
-
-/*
- * addition_slope_support
- *		Prosupport: f(x, y) = x + y is increasing in both x and y.
- */
-Datum
-addition_slope_support(PG_FUNCTION_ARGS)
-{
-	static const MonotonicFunction pattern[2] = {MONOTONICFUNC_INCREASING,
-												 MONOTONICFUNC_INCREASING};
-
-	return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
-								   lengthof(pattern), pattern);
+static bool
+get_2slope_args(Node *rawreq, Node **arg0, Node **arg1){
+	if(!IsA(rawreq, SupportRequestMonotonic))
+		return false;
+	SupportRequestMonotonic *req = (SupportRequestMonotonic *) rawreq;
+	List	   *args;
+	
+	if (IsA(req->expr, FuncExpr))
+		args = ((FuncExpr *) req->expr)->args;
+	else if (IsA(req->expr, OpExpr))
+		args = ((OpExpr *) req->expr)->args;
+	else
+	    return false;
+	if(list_length(args) < 2)
+		return false;
+	*arg0 = (Node *) linitial(args);
+	*arg1 = (Node *) lsecond(args);
+	return true;
 }
 
+enum NUMERIC_SIGN {
+	NUMERIC_SIGN_NINF=-2,
+	NUMERIC_SIGN_NEG=-1,
+	NUMERIC_SIGN_ZERO=0,
+	NUMERIC_SIGN_POS=1,
+	NUMERIC_SIGN_PINF=3,
+	NUMERIC_SIGN_NAN=4,
+	NUMERIC_SIGN_NULL=5,
+};
 /*
  * get_const_sign
  *		Helper to determine the sign of a numeric constant.
  *		Returns 1 for positive, -1 for negative, 0 for zero or unknown.
  */
-static int
+static inline enum NUMERIC_SIGN
 get_const_sign(Const *constval)
 {
 	if (constval->constisnull)
-		return 0;
+		return NUMERIC_SIGN_NULL;
 
 	switch (constval->consttype)
 	{
@@ -1223,39 +1186,170 @@ get_const_sign(Const *constval)
 		case FLOAT4OID:
 			{
 				float4		val = DatumGetFloat4(constval->constvalue);
-
-				if (isnan(val) || val == 0.0f)
-					return 0;
-				return (val > 0.0f) ? 1 : -1;
+				if (isnan(val))
+					return NUMERIC_SIGN_NAN;
+				if(isinf(val))
+					return val > 0 ? NUMERIC_SIGN_PINF : NUMERIC_SIGN_NINF;
+				else if(val == 0)
+					return NUMERIC_SIGN_ZERO;
+				else
+					return val > 0 ? NUMERIC_SIGN_POS : NUMERIC_SIGN_NEG;
 			}
 		case FLOAT8OID:
 			{
 				float8		val = DatumGetFloat8(constval->constvalue);
-
-				if (isnan(val) || val == 0.0)
-					return 0;
-				return (val > 0.0) ? 1 : -1;
+				if (isnan(val))
+					return NUMERIC_SIGN_NAN;
+                if(isinf(val))
+					return val > 0 ? NUMERIC_SIGN_PINF : NUMERIC_SIGN_NINF;
+				else if(val == 0)
+					return NUMERIC_SIGN_ZERO;
+				else
+					return val > 0 ? NUMERIC_SIGN_POS : NUMERIC_SIGN_NEG;
 			}
 		case NUMERICOID:
 			{
 				Numeric		num = DatumGetNumeric(constval->constvalue);
 				Datum		result;
 				Numeric		sign_num;
-				int			sign;
+				int			val_sign;
 
-				if (numeric_is_nan(num) || numeric_is_inf(num))
-					return 0;
+				if (numeric_is_nan(num))
+					return NUMERIC_SIGN_NAN;
 
 				result = DirectFunctionCall1(numeric_sign, NumericGetDatum(num));
 				sign_num = DatumGetNumeric(result);
-				sign = numeric_int4_safe(sign_num, NULL);
-				return sign;
+				val_sign = numeric_int4_safe(sign_num, NULL);
+				if(numeric_is_inf(num))
+					return val_sign == 1 ? NUMERIC_SIGN_PINF : NUMERIC_SIGN_NINF;
+				else
+					return (enum NUMERIC_SIGN)val_sign;
 			}
 		default:
 			return 0;
 	}
 }
 
+static const MonotonicFunction asc_slope[2] = {MONOTONICFUNC_INCREASING, MONOTONICFUNC_INCREASING};
+static const MonotonicFunction desc_slope[2] = {MONOTONICFUNC_DECREASING, MONOTONICFUNC_DECREASING};
+static const MonotonicFunction flat_slope[2] = {MONOTONICFUNC_BOTH, MONOTONICFUNC_BOTH};
+static const MonotonicFunction diff_slope[2] = {MONOTONICFUNC_INCREASING,MONOTONICFUNC_DECREASING};
+
+
+
+/*
+ * arg0_asc_slope_support
+ *		Prosupport: f(x, ...) is monotonically increasing in x.
+ */
+ Datum
+ arg0_asc_slope_support(PG_FUNCTION_ARGS)
+ {
+	 static const MonotonicFunction pattern[1] = {MONOTONICFUNC_INCREASING};
+ 
+	 return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
+									lengthof(pattern), pattern);
+ }
+ 
+ /*
+  * arg0_desc_slope_support
+  *		Prosupport: f(x, ...) is monotonically decreasing in x.
+  */
+ Datum
+ arg0_desc_slope_support(PG_FUNCTION_ARGS)
+ {
+	 static const MonotonicFunction pattern[1] = {MONOTONICFUNC_DECREASING};
+ 
+	 return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
+									lengthof(pattern), pattern);
+ }
+ 
+ /*
+  * arg1_asc_slope_support
+  *		Prosupport: f(a, x, ...) is monotonically increasing in x.
+  */
+ Datum
+ arg1_asc_slope_support(PG_FUNCTION_ARGS)
+ {
+	 static const MonotonicFunction pattern[2] = {MONOTONICFUNC_NONE,
+												  MONOTONICFUNC_INCREASING};
+ 
+	 return monotonic_slope_support((Node *) PG_GETARG_POINTER(0),
+									lengthof(pattern), pattern);
+ }
+/*
+* diff_slope_support
+*		Prosupport: f(x, y) = x - y is increasing in x, decreasing in y.
+*/
+Datum
+diff_slope_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+	Node	   *arg0;
+	Node	   *arg1;
+
+	if(!get_2slope_args(rawreq, &arg0, &arg1))
+		PG_RETURN_POINTER(NULL);
+	
+	if(IsA(arg0, Const)){
+		switch (get_const_sign((Const *) arg0)) {
+			case NUMERIC_SIGN_NINF:
+			case NUMERIC_SIGN_PINF:
+			case NUMERIC_SIGN_NAN:
+				return monotonic_slope_support(rawreq, 2, flat_slope);
+			default:
+			    break;
+		}
+	}else if(IsA(arg1, Const)) {
+		switch (get_const_sign((Const *) arg1)) {
+			case NUMERIC_SIGN_NINF:
+			case NUMERIC_SIGN_PINF:
+			case NUMERIC_SIGN_NAN:
+				return monotonic_slope_support(rawreq, 2, flat_slope);
+			default:
+			    break;
+		}
+	}
+
+	return monotonic_slope_support(rawreq, 2, diff_slope);
+}
+
+/*
+* addition_slope_support
+*		Prosupport: f(x, y) = x + y is increasing in both x and y.
+*/
+Datum
+addition_slope_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+	Node	   *arg0;
+	Node	   *arg1;
+
+	if(!get_2slope_args(rawreq, &arg0, &arg1))
+		PG_RETURN_POINTER(NULL);
+	
+	if(IsA(arg0, Const)){
+		switch (get_const_sign((Const *) arg0)) {
+			case NUMERIC_SIGN_PINF:
+			case NUMERIC_SIGN_NINF:
+			case NUMERIC_SIGN_NAN:
+				return monotonic_slope_support(rawreq, 2, flat_slope);
+			default:
+			    break;
+		}
+	}else if(IsA(arg1, Const)) {
+		switch (get_const_sign((Const *) arg1)) {
+			case NUMERIC_SIGN_PINF:
+			case NUMERIC_SIGN_NINF:
+			case NUMERIC_SIGN_NAN:
+				return monotonic_slope_support(rawreq, 2, flat_slope);
+			default:
+			    break;
+		}
+	}
+
+	return monotonic_slope_support(rawreq, 2, asc_slope);
+}
+
 /*
  * multiply_slope_support
  *		Prosupport: x * c is increasing if c > 0, decreasing if c < 0.
@@ -1270,67 +1364,33 @@ Datum
 multiply_slope_support(PG_FUNCTION_ARGS)
 {
 	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
-
-	if (IsA(rawreq, SupportRequestMonotonic))
-	{
-		SupportRequestMonotonic *req = (SupportRequestMonotonic *) rawreq;
-		List	   *args;
-		Node	   *arg0;
-		Node	   *arg1;
-		Const	   *constval = NULL;
-		int			var_argno = -1;
-		int			sign;
-
-		if (IsA(req->expr, FuncExpr))
-			args = ((FuncExpr *) req->expr)->args;
-		else if (IsA(req->expr, OpExpr))
-			args = ((OpExpr *) req->expr)->args;
-		else
-			PG_RETURN_POINTER(NULL);
-
-		if (list_length(args) != 2)
-			PG_RETURN_POINTER(NULL);
-
-		arg0 = (Node *) linitial(args);
-		arg1 = (Node *) lsecond(args);
-
-		if (IsA(arg0, Const))
-		{
-			constval = (Const *) arg0;
-			var_argno = 1;
-		}
-		else if (IsA(arg1, Const))
-		{
-			constval = (Const *) arg1;
-			var_argno = 0;
-		}
-		else
-			PG_RETURN_POINTER(NULL);
-
-		sign = get_const_sign(constval);
-		if (sign == 0)
+	Const	   *constval;
+	Node	   *arg0;
+	Node	   *arg1;
+
+	if(!get_2slope_args(rawreq, &arg0, &arg1))
+		PG_RETURN_POINTER(NULL);
+    
+	if (IsA(arg0, Const))
+		constval = (Const *) arg0;
+	else if (IsA(arg1, Const))
+		constval = (Const *) arg1;
+	else
+		PG_RETURN_POINTER(NULL);
+
+	switch (get_const_sign(constval)) {
+		case NUMERIC_SIGN_POS:
+			return monotonic_slope_support(rawreq, 2, asc_slope);
+		case NUMERIC_SIGN_NEG:
+			return monotonic_slope_support(rawreq, 2, desc_slope);
+		case NUMERIC_SIGN_NAN:
+			return monotonic_slope_support(rawreq, 2, flat_slope);
+		case NUMERIC_SIGN_ZERO:
+			return monotonic_slope_support(rawreq, 2, flat_slope);
+		default:
 			PG_RETURN_POINTER(NULL);
-
-		{
-			static const MonotonicFunction asc_first[2] = {MONOTONICFUNC_INCREASING,
-														   MONOTONICFUNC_NONE};
-			static const MonotonicFunction asc_second[2] = {MONOTONICFUNC_NONE,
-															MONOTONICFUNC_INCREASING};
-			static const MonotonicFunction desc_first[2] = {MONOTONICFUNC_DECREASING,
-															MONOTONICFUNC_NONE};
-			static const MonotonicFunction desc_second[2] = {MONOTONICFUNC_NONE,
-															 MONOTONICFUNC_DECREASING};
-
-			if (var_argno == 0)
-				req->slopes = (sign > 0) ? asc_first : desc_first;
-			else
-				req->slopes = (sign > 0) ? asc_second : desc_second;
-			req->nslopes = 2;
-			PG_RETURN_POINTER(req);
-		}
 	}
-
-	PG_RETURN_POINTER(NULL);
+	
 }
 
 /*
@@ -1346,46 +1406,29 @@ Datum
 divide_slope_support(PG_FUNCTION_ARGS)
 {
 	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
-
-	if (IsA(rawreq, SupportRequestMonotonic))
-	{
-		SupportRequestMonotonic *req = (SupportRequestMonotonic *) rawreq;
-		List	   *args;
-		Node	   *arg1;
-		Const	   *constval;
-		int			sign;
-
-		if (IsA(req->expr, FuncExpr))
-			args = ((FuncExpr *) req->expr)->args;
-		else if (IsA(req->expr, OpExpr))
-			args = ((OpExpr *) req->expr)->args;
-		else
-			PG_RETURN_POINTER(NULL);
-
-		if (list_length(args) != 2)
-			PG_RETURN_POINTER(NULL);
-
-		arg1 = (Node *) lsecond(args);
-
-		if (!IsA(arg1, Const))
-			PG_RETURN_POINTER(NULL);
-
-		constval = (Const *) arg1;
-		sign = get_const_sign(constval);
-		if (sign == 0)
+	Const	   *constval;
+	Node	   *arg0;
+	Node	   *arg1;
+	
+	if(!get_2slope_args(rawreq, &arg0, &arg1))
+		PG_RETURN_POINTER(NULL);
+
+	if (!IsA(arg1, Const))
+		PG_RETURN_POINTER(NULL);
+
+	constval = (Const *) arg1;
+	switch (get_const_sign(constval)) {
+		case NUMERIC_SIGN_POS:
+			return monotonic_slope_support(rawreq, 2, asc_slope);
+		case NUMERIC_SIGN_NEG:
+			return monotonic_slope_support(rawreq, 2, desc_slope);
+		case NUMERIC_SIGN_NAN:
+			return monotonic_slope_support(rawreq, 2, flat_slope);
+		case NUMERIC_SIGN_PINF:
+		case NUMERIC_SIGN_NINF:
+			return monotonic_slope_support(rawreq, 2, flat_slope);
+		default:
 			PG_RETURN_POINTER(NULL);
-
-		{
-			static const MonotonicFunction asc_pattern[2] = {MONOTONICFUNC_INCREASING,
-															 MONOTONICFUNC_NONE};
-			static const MonotonicFunction desc_pattern[2] = {MONOTONICFUNC_DECREASING,
-															  MONOTONICFUNC_NONE};
-
-			req->slopes = (sign > 0) ? asc_pattern : desc_pattern;
-			req->nslopes = 2;
-			PG_RETURN_POINTER(req);
-		}
 	}
-
 	PG_RETURN_POINTER(NULL);
 }
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6a3b611528c..7de5169ac2b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -607,7 +607,8 @@
   prorettype => 'float4', proargtypes => 'float4 float4', prosrc => 'float4mi' },
 { oid => '206',
   proname => 'float4um', prosupport => 'arg0_desc_slope_support',
-  prorettype => 'float4', proargtypes => 'float4', prosrc => 'float4um' },
+  prorettype => 'float4', proargtypes => 'float4',
+  prosrc => 'float4um' },
 { oid => '207',
   proname => 'float4abs', prorettype => 'float4', proargtypes => 'float4',
   prosrc => 'float4abs' },
@@ -648,7 +649,8 @@
   prorettype => 'float8', proargtypes => 'float8 float8', prosrc => 'float8mi' },
 { oid => '220',
   proname => 'float8um', prosupport => 'arg0_desc_slope_support',
-  prorettype => 'float8', proargtypes => 'float8', prosrc => 'float8um' },
+  prorettype => 'float8', proargtypes => 'float8',
+  prosrc => 'float8um' },
 { oid => '221',
   proname => 'float8abs', prorettype => 'float8', proargtypes => 'float8',
   prosrc => 'float8abs' },
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 66b5cceebaa..6b345e41171 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1819,11 +1819,14 @@ typedef struct PathKey
 	 * SLOPE: innermost source of variation, filled by
 	 * precompute_slope_pathkeys().  NULL if this pathkey is a plain Var
 	 * or cannot benefit from SLOPE.  pk_slope stores a MonotonicFunction
-	 * value (from plannodes.h) as int to avoid a header dependency.
+	 * value (from plannodes.h), including MONOTONICFUNC_UNKNOWN until
+	 * computed, as int to avoid a header dependency.  pk_var_cannan is
+	 * true when pk_var has a type that can hold NaN (float or numeric).
 	 */
 	Expr	   *pk_var pg_node_attr(read_write_ignore, equal_ignore);
 	Index		pk_varrelid pg_node_attr(read_write_ignore, equal_ignore);
 	int			pk_slope pg_node_attr(read_write_ignore, equal_ignore);
+	bool		pk_var_cannan pg_node_attr(read_write_ignore, equal_ignore);
 } PathKey;
 
 /*
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c9e374f92bf..9c29a6ebc4b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -1845,6 +1845,12 @@ typedef enum MonotonicFunction
 	MONOTONICFUNC_INCREASING = (1 << 0),
 	MONOTONICFUNC_DECREASING = (1 << 1),
 	MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
+
+	/*
+	 * Planner-internal sentinel: pk_slope has not yet been computed.
+	 * Not returned by prosupport functions.
+	 */
+	MONOTONICFUNC_UNKNOWN = (1 << 2),
 } MonotonicFunction;
 
 /*
diff --git a/src/test/regress/expected/slope.out b/src/test/regress/expected/slope.out
index 74d7f82b94a..f9283493995 100644
--- a/src/test/regress/expected/slope.out
+++ b/src/test/regress/expected/slope.out
@@ -301,7 +301,7 @@ select floor(v_float8 + 1), count(*) from slope_src group by 1;
 -- direction and nulls agree (Forward) or both are flipped (Backward).
 -- When only one differs, a Sort is required.
 --
-CREATE TABLE slope_nulls_tmp (v float8);
+CREATE TABLE slope_nulls_tmp (v int4);
 INSERT INTO slope_nulls_tmp VALUES (1), (NULL), (2);
 ANALYZE slope_nulls_tmp;
 CREATE TEMPORARY TABLE slope_nulls_results (
@@ -346,7 +346,9 @@ BEGIN
             raise exception 'r1 <> r2';
         end if;
         node_type := plan_json->0->'Plan'->>'Node Type';
-        INSERT INTO slope_nulls_results (sign, index_order, query_order, scan_method, example) VALUES (
+        INSERT INTO slope_nulls_results 
+          (sign, index_order, query_order, scan_method, example)
+        VALUES (
             r.sign,
             r.idx_dir || ' NULLS ' || r.idx_nf,
             r.qry_dir || ' NULLS ' || r.qry_nf,
@@ -522,3 +524,113 @@ from slope_src;
 
 -- Cleanup
 RESET enable_hashagg;
+--
+-- Test special numeric values
+--
+CREATE UNLOGGED TABLE slope_numeric_corners (i serial, x float8);
+INSERT INTO slope_numeric_corners (x) 
+SELECT x::float8 as x FROM 
+        unnest(ARRAY['-inf', '-1', '0', '3', 'inf', 'nan', NULL]) x(x)
+ORDER BY 1;
+CREATE INDEX slope_numeric_corners_x_idx_nulllast ON slope_numeric_corners (x ASC NULLS LAST);
+CREATE INDEX slope_numeric_corners_x_idx_nullfirst ON slope_numeric_corners (x ASC NULLS FIRST);
+CREATE TEMPORARY TABLE slope_numeric_corners_results (
+    seq serial,
+    expr text COLLATE "C",
+    sort_order text COLLATE "C",
+    nulls_order text COLLATE "C",
+    result float8[],
+    expected float8[],
+    expected_seq int4[],
+    nan_values float8[],
+    plan1_json json,
+    plan2_json json
+);
+DO $$
+DECLARE
+    r record;
+    result float8[];
+    expected float8[];
+    expected_seq int4[];
+    nan_values float8[];
+    query text;
+    agg_query text;
+    plan_query text;
+    nan_query text;
+    expected_seq_query text;
+    plan1_json json;
+    plan2_json json;
+BEGIN
+
+    SET enable_bitmapscan = off;
+    SET enable_indexscan = off;
+    SET enable_indexonlyscan = off;
+    SET enable_seqscan = off;
+    SET enable_sort = off;
+    FOR r IN
+        SELECT replace(s, 'a', a) as expr, sort_order, nulls_order
+        FROM 
+        unnest(ARRAY['ASC', 'DESC']) WITH ORDINALITY AS so(sort_order, so_i),
+        unnest(ARRAY['FIRST', 'LAST']) WITH ORDINALITY AS nf(nulls_order, no_i),
+        unnest(ARRAY[
+        'x+a', 'x-a', 'x*a', 'x/a',
+        'a+x', 'a-x', 'a*x', '-x'
+        ]) WITH ORDINALITY AS s(s, s_i),
+        unnest(ARRAY['''inf''::float8', '''-inf''::float8', '1::float8']) WITH ORDINALITY AS a(a, a_i)
+        ORDER BY s_i, so_i, no_i
+    LOOP
+        query := 'SELECT *, ' || r.expr || ' as f ' 
+                 || 'FROM slope_numeric_corners '
+                 || 'ORDER BY f ' || r.sort_order || ' NULLS ' || r.nulls_order;
+        nan_query := 'SELECT array_agg(x) FROM slope_numeric_corners 
+        WHERE ' || r.expr || ' = ''nan''::float8 AND x != ''nan''::float8';
+        agg_query := 'SELECT array_agg(f::float8) FROM (' || query || ') tmp';
+        expected_seq_query := 'SELECT array_agg(i::int4) FROM (' || query || ') tmp';
+        plan_query := 'EXPLAIN (FORMAT JSON) ' || query;
+        -- slope optimization disabled
+        SET enable_seqscan = on;
+        SET enable_indexscan = off;
+        SET enable_sort = on;
+        EXECUTE agg_query into expected;
+        EXECUTE expected_seq_query into expected_seq;
+        EXECUTE nan_query into nan_values;
+        EXECUTE plan_query into plan1_json;
+        -- slope optimization enabled
+        SET enable_seqscan = off;
+        SET enable_indexscan = on;
+        SET enable_sort = off;
+        EXECUTE agg_query into result;
+        EXECUTE plan_query into plan2_json;
+        INSERT INTO slope_numeric_corners_results 
+        (expr, sort_order, nulls_order, result, expected, expected_seq, nan_values, plan1_json, plan2_json) 
+        VALUES (r.expr, r.sort_order, r.nulls_order, result, expected, expected_seq, nan_values, plan1_json, plan2_json);
+    END LOOP;
+END;
+$$;
+-- display failing test cases
+SELECT seq, expr
+, sort_order, nulls_order
+, expected, expected_seq, result, nan_values
+, plan2_json->0->'Plan'->>'Node Type' as plan2
+FROM slope_numeric_corners_results
+WHERE expected != result;
+ seq | expr | sort_order | nulls_order | expected | expected_seq | result | nan_values | plan2 
+-----+------+------------+-------------+----------+--------------+--------+------------+-------
+(0 rows)
+
+-- check the number of test cases
+SELECT expected = result as passed, count(1) 
+FROM slope_numeric_corners_results
+GROUP BY 1;
+ passed | count 
+--------+-------
+ t      |    96
+(1 row)
+
+DROP TABLE slope_numeric_corners;
+DROP TABLE slope_numeric_corners_results;
+RESET enable_bitmapscan;
+RESET enable_indexscan;
+RESET enable_indexonlyscan;
+RESET enable_seqscan;
+RESET enable_sort;
diff --git a/src/test/regress/sql/slope.sql b/src/test/regress/sql/slope.sql
index 31eba90695f..89a7a710981 100644
--- a/src/test/regress/sql/slope.sql
+++ b/src/test/regress/sql/slope.sql
@@ -145,7 +145,7 @@ select floor(v_float8 + 1), count(*) from slope_src group by 1;
 -- direction and nulls agree (Forward) or both are flipped (Backward).
 -- When only one differs, a Sort is required.
 --
-CREATE TABLE slope_nulls_tmp (v float8);
+CREATE TABLE slope_nulls_tmp (v int4);
 INSERT INTO slope_nulls_tmp VALUES (1), (NULL), (2);
 ANALYZE slope_nulls_tmp;
 
@@ -193,7 +193,9 @@ BEGIN
             raise exception 'r1 <> r2';
         end if;
         node_type := plan_json->0->'Plan'->>'Node Type';
-        INSERT INTO slope_nulls_results (sign, index_order, query_order, scan_method, example) VALUES (
+        INSERT INTO slope_nulls_results 
+          (sign, index_order, query_order, scan_method, example)
+        VALUES (
             r.sign,
             r.idx_dir || ' NULLS ' || r.idx_nf,
             r.qry_dir || ' NULLS ' || r.qry_nf,
@@ -276,3 +278,113 @@ from slope_src;
 
 -- Cleanup
 RESET enable_hashagg;
+
+
+--
+-- Test special numeric values
+--
+
+CREATE UNLOGGED TABLE slope_numeric_corners (i serial, x float8);
+INSERT INTO slope_numeric_corners (x) 
+SELECT x::float8 as x FROM 
+        unnest(ARRAY['-inf', '-1', '0', '3', 'inf', 'nan', NULL]) x(x)
+ORDER BY 1;
+
+CREATE INDEX slope_numeric_corners_x_idx_nulllast ON slope_numeric_corners (x ASC NULLS LAST);
+CREATE INDEX slope_numeric_corners_x_idx_nullfirst ON slope_numeric_corners (x ASC NULLS FIRST);
+
+CREATE TEMPORARY TABLE slope_numeric_corners_results (
+    seq serial,
+    expr text COLLATE "C",
+    sort_order text COLLATE "C",
+    nulls_order text COLLATE "C",
+    result float8[],
+    expected float8[],
+    expected_seq int4[],
+    nan_values float8[],
+    plan1_json json,
+    plan2_json json
+);
+
+DO $$
+DECLARE
+    r record;
+    result float8[];
+    expected float8[];
+    expected_seq int4[];
+    nan_values float8[];
+    query text;
+    agg_query text;
+    plan_query text;
+    nan_query text;
+    expected_seq_query text;
+    plan1_json json;
+    plan2_json json;
+BEGIN
+
+    SET enable_bitmapscan = off;
+    SET enable_indexscan = off;
+    SET enable_indexonlyscan = off;
+    SET enable_seqscan = off;
+    SET enable_sort = off;
+    FOR r IN
+        SELECT replace(s, 'a', a) as expr, sort_order, nulls_order
+        FROM 
+        unnest(ARRAY['ASC', 'DESC']) WITH ORDINALITY AS so(sort_order, so_i),
+        unnest(ARRAY['FIRST', 'LAST']) WITH ORDINALITY AS nf(nulls_order, no_i),
+        unnest(ARRAY[
+        'x+a', 'x-a', 'x*a', 'x/a',
+        'a+x', 'a-x', 'a*x', '-x'
+        ]) WITH ORDINALITY AS s(s, s_i),
+        unnest(ARRAY['''inf''::float8', '''-inf''::float8', '1::float8']) WITH ORDINALITY AS a(a, a_i)
+        ORDER BY s_i, so_i, no_i
+    LOOP
+        query := 'SELECT *, ' || r.expr || ' as f ' 
+                 || 'FROM slope_numeric_corners '
+                 || 'ORDER BY f ' || r.sort_order || ' NULLS ' || r.nulls_order;
+        nan_query := 'SELECT array_agg(x) FROM slope_numeric_corners 
+        WHERE ' || r.expr || ' = ''nan''::float8 AND x != ''nan''::float8';
+        agg_query := 'SELECT array_agg(f::float8) FROM (' || query || ') tmp';
+        expected_seq_query := 'SELECT array_agg(i::int4) FROM (' || query || ') tmp';
+        plan_query := 'EXPLAIN (FORMAT JSON) ' || query;
+        -- slope optimization disabled
+        SET enable_seqscan = on;
+        SET enable_indexscan = off;
+        SET enable_sort = on;
+        EXECUTE agg_query into expected;
+        EXECUTE expected_seq_query into expected_seq;
+        EXECUTE nan_query into nan_values;
+        EXECUTE plan_query into plan1_json;
+        -- slope optimization enabled
+        SET enable_seqscan = off;
+        SET enable_indexscan = on;
+        SET enable_sort = off;
+        EXECUTE agg_query into result;
+        EXECUTE plan_query into plan2_json;
+        INSERT INTO slope_numeric_corners_results 
+        (expr, sort_order, nulls_order, result, expected, expected_seq, nan_values, plan1_json, plan2_json) 
+        VALUES (r.expr, r.sort_order, r.nulls_order, result, expected, expected_seq, nan_values, plan1_json, plan2_json);
+    END LOOP;
+END;
+$$;
+
+-- display failing test cases
+SELECT seq, expr
+, sort_order, nulls_order
+, expected, expected_seq, result, nan_values
+, plan2_json->0->'Plan'->>'Node Type' as plan2
+FROM slope_numeric_corners_results
+WHERE expected != result;
+
+-- check the number of test cases
+SELECT expected = result as passed, count(1) 
+FROM slope_numeric_corners_results
+GROUP BY 1;
+
+DROP TABLE slope_numeric_corners;
+DROP TABLE slope_numeric_corners_results;
+RESET enable_bitmapscan;
+RESET enable_indexscan;
+RESET enable_indexonlyscan;
+RESET enable_seqscan;
+RESET enable_sort;
\ No newline at end of file
-- 
2.53.0