From 5d3f393ea345e33001f0fea79e51ff146fc9fdca Mon Sep 17 00:00:00 2001
From: TsinghuaLucky912 <2903807914@qq.com>
Date: Wed, 27 Nov 2024 01:00:42 -0800
Subject: [PATCH] Added prosupport functions for estimating numeric
 generate_series rows

---
 src/backend/utils/adt/numeric.c | 103 ++++++++++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat |   9 ++-
 2 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 344d7137f9..5c67e1f443 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -32,6 +32,7 @@
 #include "lib/hyperloglog.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
+#include "optimizer/optimizer.h"
 #include "nodes/nodeFuncs.h"
 #include "nodes/supportnodes.h"
 #include "utils/array.h"
@@ -1828,6 +1829,108 @@ generate_series_step_numeric(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * Planner support function for generate_series(numeric, numeric [, numeric])
+ */
+Datum
+generate_series_numeric_support(PG_FUNCTION_ARGS)
+{
+	Node	   *rawreq = (Node *) PG_GETARG_POINTER(0);
+	Node	   *ret = NULL;
+
+	if (IsA(rawreq, SupportRequestRows))
+	{
+		/* Try to estimate the number of rows returned */
+		SupportRequestRows *req = (SupportRequestRows *) rawreq;
+
+		if (is_funcclause(req->node))	/* be paranoid */
+		{
+			List	   *args = ((FuncExpr *) req->node)->args;
+			Node	   *arg1,
+					   *arg2,
+					   *arg3;
+
+			/* We can use estimated argument values here */
+			arg1 = estimate_expression_value(req->root, linitial(args));
+			arg2 = estimate_expression_value(req->root, lsecond(args));
+			if (list_length(args) >= 3)
+				arg3 = estimate_expression_value(req->root, lthird(args));
+			else
+				arg3 = NULL;
+
+			/*
+			 * If any argument is constant NULL, we can safely assume that
+			 * zero rows are returned.  Otherwise, if they're all non-NULL
+			 * constants, we can calculate the number of rows that will be
+			 * returned.  Use double arithmetic to avoid overflow hazards.
+			 */
+			if ((IsA(arg1, Const) &&
+				 ((Const *) arg1)->constisnull) ||
+				(IsA(arg2, Const) &&
+				 ((Const *) arg2)->constisnull) ||
+				(arg3 != NULL && IsA(arg3, Const) &&
+				 ((Const *) arg3)->constisnull))
+			{
+				req->rows = 0;
+				ret = (Node *) req;
+			}
+			else if (IsA(arg1, Const) &&
+					 IsA(arg2, Const) &&
+					 (arg3 == NULL || IsA(arg3, Const)))
+			{
+				Numeric		start,
+							finish,
+							step;
+				NumericVar	var_start,
+							var_finish,
+							var_diff;
+				NumericVar	nstep = const_one;
+
+				init_var(&var_start);
+				init_var(&var_finish);
+				init_var(&var_diff);
+
+				start = DatumGetNumeric(((Const *) arg1)->constvalue);
+				finish = DatumGetNumeric(((Const *) arg2)->constvalue);
+
+				set_var_from_num(start, &var_start);
+				set_var_from_num(finish, &var_finish);
+
+				if(arg3)
+				{
+					step = DatumGetNumeric(((Const *) arg3)->constvalue);
+					init_var_from_num(step, &nstep);
+				}
+
+				sub_var(&var_finish, &var_start, &var_diff);
+
+				/* This equation works for either sign of step */
+				if (cmp_var(&nstep, &const_zero) != 0)
+				{
+					if(nstep.sign != var_diff.sign)
+					{
+						req->rows = 0;
+						ret = (Node *) req;
+					}
+					else
+					{
+						double ddiff;
+						NumericVar q;
+						init_var(&q);
+						div_var(&var_diff, &nstep, &q, 0, false, false);
+						ddiff = numericvar_to_double_no_overflow(&q);
+
+						req->rows = floor(ddiff + 1);
+						ret = (Node *) req;
+					}
+				}
+			}
+		}
+	}
+
+	PG_RETURN_POINTER(ret);
+}
+
 /*
  * Implements the numeric version of the width_bucket() function
  * defined by SQL2003. See also width_bucket_float8().
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index cbbe8acd38..9575524007 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8464,13 +8464,18 @@
   proname => 'generate_series_int8_support', prorettype => 'internal',
   proargtypes => 'internal', prosrc => 'generate_series_int8_support' },
 { oid => '3259', descr => 'non-persistent series generator',
-  proname => 'generate_series', prorows => '1000', proretset => 't',
+  proname => 'generate_series', prorows => '1000',
+  prosupport => 'generate_series_numeric_support', proretset => 't',
   prorettype => 'numeric', proargtypes => 'numeric numeric numeric',
   prosrc => 'generate_series_step_numeric' },
 { oid => '3260', descr => 'non-persistent series generator',
-  proname => 'generate_series', prorows => '1000', proretset => 't',
+  proname => 'generate_series', prorows => '1000',
+  prosupport => 'generate_series_numeric_support', proretset => 't',
   prorettype => 'numeric', proargtypes => 'numeric numeric',
   prosrc => 'generate_series_numeric' },
+{ oid => '8405', descr => 'planner support for generate_series',
+  proname => 'generate_series_numeric_support', prorettype => 'internal',
+  proargtypes => 'internal', prosrc => 'generate_series_numeric_support' },
 { oid => '938', descr => 'non-persistent series generator',
   proname => 'generate_series', prorows => '1000',
   prosupport => 'generate_series_timestamp_support', proretset => 't',
-- 
2.31.1

