From 6b08ebf385327a6b6ab78b15b78abd7b451c1cc8 Mon Sep 17 00:00:00 2001
From: "Paul A. Jungwirth" <pj@illuminatedcomputing.com>
Date: Tue, 2 Dec 2025 21:30:13 -0800
Subject: [PATCH v64 1/7] Add range_get_constructor2

Look up the two-arg constructor for a given rangetype. We need this for
UPDATE/DELETE FOR PORTION OF, so that we can build a range from the FROM/TO
bounds.

There doesn't seem to be an easy way to find the constructor. The rule is that
the function has the same name as the rangetype, with arguments matching the
range's subtype. Ideally we could just use the range's type oid, but I see no
way to do that. Using pg_depend doesn't help: there are no entries for built-in
rangetypes, only user-defined ones.

Author: Paul A. Jungwirth <pj@illuminatedcomputing.com>
---
 src/backend/utils/cache/lsyscache.c | 68 +++++++++++++++++++++++++++++
 src/include/utils/lsyscache.h       |  1 +
 2 files changed, 69 insertions(+)

diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 5aa7a26d95c..59bbdb06e05 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3613,6 +3613,74 @@ get_range_collation(Oid rangeOid)
 		return InvalidOid;
 }
 
+/*
+ * get_range_constructor2
+ *		Gets the 2-arg constructor for the given rangetype.
+ *
+ * It should be the function whose name and namespace match the rangetype,
+ * has 2 args matching the subtype, and returns the rangetype. To be extra sure,
+ * we make sure that prosrc is 'range_constructor2' and probin IS NULL.
+ *
+ * We can't use pg_depend, because built-in rangetypes don't have entries there.
+ *
+ * Domains on rangetypes don't define their own constructors,
+ * so caller should pass the basetype oid.
+ */
+Oid
+get_range_constructor2(Oid rngtypid)
+{
+	Oid			range_typelem = get_range_subtype(rngtypid);
+	char	   *rngname;
+	Oid			rngnamespace;
+	Oid			argoids[2];
+	oidvector  *argtypes;
+	HeapTuple	tp;
+
+	/* Is it really a rangetype? */
+	if (!OidIsValid(range_typelem))
+		elog(ERROR, "cache lookup failed for range %u", rngtypid);
+
+	/* Get the range's name and namespace */
+	tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rngtypid));
+	if (HeapTupleIsValid(tp))
+	{
+		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+
+		rngname = pstrdup(NameStr(typtup->typname));
+		rngnamespace = typtup->typnamespace;
+		ReleaseSysCache(tp);
+	}
+	else
+		elog(ERROR, "cache lookup failed for type %u", rngtypid);
+
+	/* Find the constructor */
+	argoids[0] = range_typelem;
+	argoids[1] = range_typelem;
+	argtypes = buildoidvector(argoids, 2);
+	tp = SearchSysCache3(PROCNAMEARGSNSP,
+						 PointerGetDatum(rngname),
+						 PointerGetDatum(argtypes),
+						 ObjectIdGetDatum(rngnamespace));
+	if (HeapTupleIsValid(tp))
+	{
+		Form_pg_proc proctup = (Form_pg_proc) GETSTRUCT(tp);
+		Oid			result;
+		Datum		prosrc = SysCacheGetAttrNotNull(PROCNAMEARGSNSP, tp,
+													Anum_pg_proc_prosrc);
+
+		/* Sanity-checking */
+		if (proctup->prorettype == rngtypid &&
+			strcmp(TextDatumGetCString(prosrc), "range_constructor2") == 0 &&
+			heap_attisnull(tp, Anum_pg_proc_probin, NULL))
+		{
+			result = proctup->oid;
+			ReleaseSysCache(tp);
+			return result;
+		}
+	}
+	elog(ERROR, "cache lookup failed for procedure %s", rngname);
+}
+
 /*
  * get_range_multirange
  *		Returns the multirange type of a given range type
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 50fb149e9ac..ad3d5f33b5e 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -200,6 +200,7 @@ extern char *get_namespace_name(Oid nspid);
 extern char *get_namespace_name_or_temp(Oid nspid);
 extern Oid	get_range_subtype(Oid rangeOid);
 extern Oid	get_range_collation(Oid rangeOid);
+extern Oid	get_range_constructor2(Oid rangeOid);
 extern Oid	get_range_multirange(Oid rangeOid);
 extern Oid	get_multirange_range(Oid multirangeOid);
 extern Oid	get_index_column_opclass(Oid index_oid, int attno);
-- 
2.47.3

