From 9e0247aba5e2cac1542823a194d4dc2ea1c3abb6 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 14 Nov 2008 19:47:50 +0000
Subject: [PATCH] In CREATE AGGREGATE, allow the transition datatype to be
 "internal", but only if the user is superuser.  This makes available to
 extension modules the same sort of trick being practiced by array_agg().  The
 reason for the superuser restriction is that you could crash the system by
 connecting up an incompatible pair of internal-using functions as an
 aggregate.  It shouldn't interfere with any legitimate use, since you'd have
 to be superuser to create the internal-using transition and final functions
 anyway.

---
 src/backend/catalog/pg_aggregate.c   | 23 ++++++++++++++++++-----
 src/backend/commands/aggregatecmds.c | 21 +++++++++++++++------
 2 files changed, 33 insertions(+), 11 deletions(-)

diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 69bfe4c1578..fc5ed839376 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.96 2008/11/02 01:45:27 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.97 2008/11/14 19:47:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,6 +61,7 @@ AggregateCreate(const char *aggName,
 	Oid			finalfn = InvalidOid;	/* can be omitted */
 	Oid			sortop = InvalidOid;	/* can be omitted */
 	bool		hasPolyArg;
+	bool		hasInternalArg;
 	Oid			rettype;
 	Oid			finaltype;
 	Oid		   *fnArgs;
@@ -78,15 +79,15 @@ AggregateCreate(const char *aggName,
 	if (!aggtransfnName)
 		elog(ERROR, "aggregate must have a transition function");
 
-	/* check for polymorphic arguments */
+	/* check for polymorphic and INTERNAL arguments */
 	hasPolyArg = false;
+	hasInternalArg = false;
 	for (i = 0; i < numArgs; i++)
 	{
 		if (IsPolymorphicType(aggArgTypes[i]))
-		{
 			hasPolyArg = true;
-			break;
-		}
+		else if (aggArgTypes[i] == INTERNALOID)
+			hasInternalArg = true;
 	}
 
 	/*
@@ -177,6 +178,18 @@ AggregateCreate(const char *aggName,
 				 errdetail("An aggregate returning a polymorphic type "
 						   "must have at least one polymorphic argument.")));
 
+	/*
+	 * Also, the return type can't be INTERNAL unless there's at least one
+	 * INTERNAL argument.  This is the same type-safety restriction we
+	 * enforce for regular functions, but at the level of aggregates.  We
+	 * must test this explicitly because we allow INTERNAL as the transtype.
+	 */
+	if (finaltype == INTERNALOID && !hasInternalArg)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+				 errmsg("unsafe use of pseudo-type \"internal\""),
+				 errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
+
 	/* handle sortop, if supplied */
 	if (aggsortopName)
 	{
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index a8c9f1199e4..5bbb4bfdf74 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.46 2008/06/08 21:09:48 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.47 2008/11/14 19:47:50 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -173,15 +173,24 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
 	 *
 	 * transtype can't be a pseudo-type, since we need to be able to store
 	 * values of the transtype.  However, we can allow polymorphic transtype
-	 * in some cases (AggregateCreate will check).
+	 * in some cases (AggregateCreate will check).  Also, we allow "internal"
+	 * for functions that want to pass pointers to private data structures;
+	 * but allow that only to superusers, since you could crash the system
+	 * (or worse) by connecting up incompatible internal-using functions
+	 * in an aggregate.
 	 */
 	transTypeId = typenameTypeId(NULL, transType, NULL);
 	if (get_typtype(transTypeId) == TYPTYPE_PSEUDO &&
 		!IsPolymorphicType(transTypeId))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
-				 errmsg("aggregate transition data type cannot be %s",
-						format_type_be(transTypeId))));
+	{
+		if (transTypeId == INTERNALOID && superuser())
+			/* okay */ ;
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+					 errmsg("aggregate transition data type cannot be %s",
+							format_type_be(transTypeId))));
+	}
 
 	/*
 	 * Most of the argument-checking is done inside of AggregateCreate
-- 
GitLab