From 927e2bf09e853e74d1efaa0d573ea2e90af0b17f Mon Sep 17 00:00:00 2001
From: Alexandre Felipe <o.alexandre.felipe@gmail.com>
Date: Fri, 3 Apr 2026 20:01:39 +0100
Subject: [PATCH 3/5] SLOPE: prosupport definitions

Add support functions to describe monotonicy of buildint functions
These functions will be referenced by pg_proc entries in a subsequent
commit.

New prosupport functions:
- arg0_asc_slope_support: f(x, ...) is increasing in x
- arg0_desc_slope_support: f(x, ...) is decreasing in x
- arg1_asc_slope_support: f(a, x, ...) is increasing in x
- diff_slope_support: x - y is increasing in x, decreasing in y
- addition_slope_support: x + y is increasing in both args
- multiply_slope_support: x * c or c * x depends on sign of constant c
- divide_slope_support: x / c depends on sign of constant c

Also adds SupportRequestMonotonic to supportnodes.h for the planner
to query monotonicity information from prosupport functions.
---
 src/backend/utils/adt/misc.c     | 294 +++++++++++++++++++++++++++++++
 src/include/nodes/supportnodes.h |  25 +++
 2 files changed, 319 insertions(+)

diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index c033e68ba15..19b7b48e80a 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -32,6 +32,7 @@
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/miscnodes.h"
+#include "nodes/supportnodes.h"
 #include "parser/parse_type.h"
 #include "parser/scansup.h"
 #include "pgstat.h"
@@ -43,6 +44,7 @@
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/numeric.h"
 #include "utils/ruleutils.h"
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
@@ -1095,3 +1097,295 @@ any_value_transfn(PG_FUNCTION_ARGS)
 {
 	PG_RETURN_DATUM(PG_GETARG_DATUM(0));
 }
+
+/*
+ * monotonic_slope_support
+ *		Generic helper for prosupport functions that declare monotonic slopes.
+ *
+ * If the request is a SupportRequestMonotonic, fills in nslopes and slopes
+ * and returns the request pointer.  Otherwise returns NULL.
+ */
+static Datum
+monotonic_slope_support(Node *rawreq, int nslopes,
+						const MonotonicFunction *slopes)
+{
+	if (IsA(rawreq, SupportRequestMonotonic))
+	{
+		SupportRequestMonotonic *req = (SupportRequestMonotonic *) rawreq;
+
+		req->nslopes = nslopes;
+		req->slopes = slopes;
+		return PointerGetDatum(req);
+	}
+
+	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);
+}
+
+/*
+ * 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
+get_const_sign(Const *constval)
+{
+	if (constval->constisnull)
+		return 0;
+
+	switch (constval->consttype)
+	{
+		case INT2OID:
+			{
+				int16		val = DatumGetInt16(constval->constvalue);
+
+				return (val > 0) ? 1 : (val < 0) ? -1 : 0;
+			}
+		case INT4OID:
+			{
+				int32		val = DatumGetInt32(constval->constvalue);
+
+				return (val > 0) ? 1 : (val < 0) ? -1 : 0;
+			}
+		case INT8OID:
+			{
+				int64		val = DatumGetInt64(constval->constvalue);
+
+				return (val > 0) ? 1 : (val < 0) ? -1 : 0;
+			}
+		case FLOAT4OID:
+			{
+				float4		val = DatumGetFloat4(constval->constvalue);
+
+				if (isnan(val) || val == 0.0f)
+					return 0;
+				return (val > 0.0f) ? 1 : -1;
+			}
+		case FLOAT8OID:
+			{
+				float8		val = DatumGetFloat8(constval->constvalue);
+
+				if (isnan(val) || val == 0.0)
+					return 0;
+				return (val > 0.0) ? 1 : -1;
+			}
+		case NUMERICOID:
+			{
+				Numeric		num = DatumGetNumeric(constval->constvalue);
+				Datum		result;
+				Numeric		sign_num;
+				int			sign;
+
+				if (numeric_is_nan(num) || numeric_is_inf(num))
+					return 0;
+
+				result = DirectFunctionCall1(numeric_sign, NumericGetDatum(num));
+				sign_num = DatumGetNumeric(result);
+				sign = numeric_int4_safe(sign_num, NULL);
+				return sign;
+			}
+		default:
+			return 0;
+	}
+}
+
+/*
+ * multiply_slope_support
+ *		Prosupport: x * c is increasing if c > 0, decreasing if c < 0.
+ *		Similarly for c * x.
+ *
+ * For multiplication, the monotonicity depends on the sign of the constant:
+ * - x * positive_const: increasing in x
+ * - x * negative_const: decreasing in x
+ * - x * 0: not monotonic (constant result)
+ */
+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)
+			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);
+}
+
+/*
+ * divide_slope_support
+ *		Prosupport: x / c is increasing if c > 0, decreasing if c < 0.
+ *
+ * Division by a constant has the same monotonicity as multiplication:
+ * - x / positive_const: increasing in x
+ * - x / negative_const: decreasing in x
+ * - x / 0: undefined (not monotonic)
+ */
+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)
+			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/nodes/supportnodes.h b/src/include/nodes/supportnodes.h
index 132a2bcd8af..106a369e1cc 100644
--- a/src/include/nodes/supportnodes.h
+++ b/src/include/nodes/supportnodes.h
@@ -445,4 +445,29 @@ typedef struct SupportRequestModifyInPlace
 	int			paramid;		/* ID of Param(s) representing variable */
 } SupportRequestModifyInPlace;
 
+/* ----------
+ * SupportRequestMonotonic: request monotonicity information for SLOPE
+ *
+ * This allows the planner to use an index on 'x' to satisfy ORDER BY f(x)
+ * when f is monotonic.  The support function fills in the slopes array
+ * to indicate the monotonicity of each argument.
+ *
+ *	'expr' is the FuncExpr or OpExpr being analyzed.
+ *	'nslopes' points to a MonotonicFunction array (one per argument up to
+ *	nslopes).  Arguments beyond nslopes are treated as MONOTONICFUNC_NONE.
+ *	Return the request pointer on success, or NULL if not monotonic.
+ * ----------
+ */
+typedef struct SupportRequestMonotonic
+{
+	NodeTag		type;
+
+	/* Input fields: */
+	Node	   *expr;			/* FuncExpr or OpExpr */
+
+	/* Output fields (set by prosupport function): */
+	int			nslopes;		/* number of slopes in array */
+	const MonotonicFunction *slopes;	/* array of slopes, one per arg */
+} SupportRequestMonotonic;
+
 #endif							/* SUPPORTNODES_H */
-- 
2.53.0

