drop_func_if_not_exists_fix_v4.patch

application/octet-stream

Filename: drop_func_if_not_exists_fix_v4.patch
Type: application/octet-stream
Part: 0
Message: Re: BUG #15572: Misleading message reported by "Drop function operation" on DB with functions having same name

Patch

Format: unified
Series: patch v4
File+
src/backend/commands/amcmds.c 1 1
src/backend/commands/conversioncmds.c 1 1
src/backend/commands/event_trigger.c 1 1
src/backend/commands/foreigncmds.c 4 2
src/backend/commands/functioncmds.c 1 1
src/backend/commands/operatorcmds.c 5 5
src/backend/commands/proclang.c 10 6
src/backend/commands/trigger.c 1 1
src/backend/commands/tsearchcmds.c 2 2
src/backend/commands/typecmds.c 14 14
src/backend/parser/parse_clause.c 1 1
src/backend/parser/parse_func.c 13 8
src/include/parser/parse_func.h 1 1
src/pl/tcl/pltcl.c 1 1
src/test/regress/expected/drop_if_exists.out 10 0
src/test/regress/sql/drop_if_exists.sql 10 0
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index c84507b5d0..9f2bad2afd 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -254,7 +254,7 @@ lookup_index_am_handler_func(List *handler_name, char amtype)
 				 errmsg("handler function is not specified")));
 
 	/* handlers have one argument of type internal */
-	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false, false);
 
 	/* check that handler has the correct return type */
 	switch (amtype)
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 5afe86712f..122573464f 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -77,7 +77,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
 	 * a qualified name.
 	 */
 	funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
-							 funcargs, false);
+							 funcargs, false, false);
 
 	/* Check it returns VOID, else it's probably the wrong function */
 	if (get_func_rettype(funcoid) != VOIDOID)
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index adb77d8f69..91c1ea08c5 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -238,7 +238,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 						stmt->trigname)));
 
 	/* Find and validate the trigger function. */
-	funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
+	funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false, false);
 	funcrettype = get_func_rettype(funcoid);
 	if (funcrettype != EVTTRIGGEROID)
 		ereport(ERROR,
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 413ce3fcb6..02ed460847 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -484,7 +484,8 @@ lookup_fdw_handler_func(DefElem *handler)
 		return InvalidOid;
 
 	/* handlers have no arguments */
-	handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false);
+	handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false,
+								false);
 
 	/* check that handler has correct return type */
 	if (get_func_rettype(handlerOid) != FDW_HANDLEROID)
@@ -511,7 +512,8 @@ lookup_fdw_validator_func(DefElem *validator)
 	funcargtypes[0] = TEXTARRAYOID;
 	funcargtypes[1] = OIDOID;
 
-	return LookupFuncName((List *) validator->arg, 2, funcargtypes, false);
+	return LookupFuncName((List *) validator->arg, 2, funcargtypes, false,
+						  false);
 	/* validator's return value is ignored, so we don't check the type */
 }
 
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 4f62e48d98..b7f9b46d4d 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -658,7 +658,7 @@ interpret_func_support(DefElem *defel)
 	 */
 	argList[0] = INTERNALOID;
 
-	procOid = LookupFuncName(procName, 1, argList, true);
+	procOid = LookupFuncName(procName, 1, argList, false, true);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 7b98e4b910..86a37e18e3 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -208,7 +208,7 @@ DefineOperator(List *names, List *parameters)
 		typeId[1] = typeId2;
 		nargs = 2;
 	}
-	functionOid = LookupFuncName(functionName, nargs, typeId, false);
+	functionOid = LookupFuncName(functionName, nargs, typeId, false, false);
 
 	/*
 	 * We require EXECUTE rights for the function.  This isn't strictly
@@ -271,7 +271,7 @@ ValidateRestrictionEstimator(List *restrictionName)
 	typeId[2] = INTERNALOID;	/* args list */
 	typeId[3] = INT4OID;		/* varRelid */
 
-	restrictionOid = LookupFuncName(restrictionName, 4, typeId, false);
+	restrictionOid = LookupFuncName(restrictionName, 4, typeId, false, false);
 
 	/* estimators must return float8 */
 	if (get_func_rettype(restrictionOid) != FLOAT8OID)
@@ -312,12 +312,12 @@ ValidateJoinEstimator(List *joinName)
 	 * arguments, but we still allow the old 4-argument form. Try the
 	 * preferred form first.
 	 */
-	joinOid = LookupFuncName(joinName, 5, typeId, true);
+	joinOid = LookupFuncName(joinName, 5, typeId, true, false);
 	if (!OidIsValid(joinOid))
-		joinOid = LookupFuncName(joinName, 4, typeId, true);
+		joinOid = LookupFuncName(joinName, 4, typeId, true, false);
 	/* If not found, reference the 5-argument signature in error msg */
 	if (!OidIsValid(joinOid))
-		joinOid = LookupFuncName(joinName, 5, typeId, false);
+		joinOid = LookupFuncName(joinName, 5, typeId, false, false);
 
 	/* estimators must return float8 */
 	if (get_func_rettype(joinOid) != FLOAT8OID)
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 59c4e8dfd0..f0d0bc374d 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -106,7 +106,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		 * return type.
 		 */
 		funcname = SystemFuncName(pltemplate->tmplhandler);
-		handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
+		handlerOid = LookupFuncName(funcname, 0, funcargtypes, true, false);
 		if (OidIsValid(handlerOid))
 		{
 			funcrettype = get_func_rettype(handlerOid);
@@ -155,7 +155,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		{
 			funcname = SystemFuncName(pltemplate->tmplinline);
 			funcargtypes[0] = INTERNALOID;
-			inlineOid = LookupFuncName(funcname, 1, funcargtypes, true);
+			inlineOid = LookupFuncName(funcname, 1, funcargtypes, true,
+									   false);
 			if (!OidIsValid(inlineOid))
 			{
 				tmpAddr = ProcedureCreate(pltemplate->tmplinline,
@@ -198,7 +199,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		{
 			funcname = SystemFuncName(pltemplate->tmplvalidator);
 			funcargtypes[0] = OIDOID;
-			valOid = LookupFuncName(funcname, 1, funcargtypes, true);
+			valOid = LookupFuncName(funcname, 1, funcargtypes, true, false);
 			if (!OidIsValid(valOid))
 			{
 				tmpAddr = ProcedureCreate(pltemplate->tmplvalidator,
@@ -264,7 +265,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		 * Lookup the PL handler function and check that it is of the expected
 		 * return type
 		 */
-		handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false);
+		handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false,
+									false);
 		funcrettype = get_func_rettype(handlerOid);
 		if (funcrettype != LANGUAGE_HANDLEROID)
 		{
@@ -293,7 +295,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		if (stmt->plinline)
 		{
 			funcargtypes[0] = INTERNALOID;
-			inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false);
+			inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false,
+									   false);
 			/* return value is ignored, so we don't check the type */
 		}
 		else
@@ -303,7 +306,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		if (stmt->plvalidator)
 		{
 			funcargtypes[0] = OIDOID;
-			valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false);
+			valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false,
+									false);
 			/* return value is ignored, so we don't check the type */
 		}
 		else
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 0b245a613e..d6fcac7bdb 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -667,7 +667,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
 	 * Find and validate the trigger function.
 	 */
 	if (!OidIsValid(funcoid))
-		funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);
+		funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false, false);
 	if (!isInternal)
 	{
 		aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE);
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index 8e5eec22b5..7c1c7deb50 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -108,7 +108,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
 			nargs = 0;			/* keep compiler quiet */
 	}
 
-	procOid = LookupFuncName(funcName, nargs, typeId, false);
+	procOid = LookupFuncName(funcName, nargs, typeId, false, false);
 	if (get_func_rettype(procOid) != retTypeId)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -679,7 +679,7 @@ get_ts_template_func(DefElem *defel, int attnum)
 			nargs = 0;			/* keep compiler quiet */
 	}
 
-	procOid = LookupFuncName(funcName, nargs, typeId, false);
+	procOid = LookupFuncName(funcName, nargs, typeId, false, false);
 	if (get_func_rettype(procOid) != retTypeId)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 448926db12..46a1270470 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1700,28 +1700,28 @@ findTypeInputFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = CSTRINGOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	argList[1] = OIDOID;
 	argList[2] = INT4OID;
 
-	procOid = LookupFuncName(procname, 3, argList, true);
+	procOid = LookupFuncName(procname, 3, argList, true, false);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	/* No luck, try it with OPAQUE */
 	argList[0] = OPAQUEOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 
 	if (!OidIsValid(procOid))
 	{
 		argList[1] = OIDOID;
 		argList[2] = INT4OID;
 
-		procOid = LookupFuncName(procname, 3, argList, true);
+		procOid = LookupFuncName(procname, 3, argList, true, false);
 	}
 
 	if (OidIsValid(procOid))
@@ -1766,14 +1766,14 @@ findTypeOutputFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = typeOid;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	/* No luck, try it with OPAQUE */
 	argList[0] = OPAQUEOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 
 	if (OidIsValid(procOid))
 	{
@@ -1815,14 +1815,14 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = INTERNALOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (OidIsValid(procOid))
 		return procOid;
 
 	argList[1] = OIDOID;
 	argList[2] = INT4OID;
 
-	procOid = LookupFuncName(procname, 3, argList, true);
+	procOid = LookupFuncName(procname, 3, argList, true, false);
 	if (OidIsValid(procOid))
 		return procOid;
 
@@ -1845,7 +1845,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = typeOid;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (OidIsValid(procOid))
 		return procOid;
 
@@ -1868,7 +1868,7 @@ findTypeTypmodinFunction(List *procname)
 	 */
 	argList[0] = CSTRINGARRAYOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1895,7 +1895,7 @@ findTypeTypmodoutFunction(List *procname)
 	 */
 	argList[0] = INT4OID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1922,7 +1922,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = INTERNALOID;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -1998,7 +1998,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
 	 */
 	argList[0] = typeOid;
 
-	procOid = LookupFuncName(procname, 1, argList, true);
+	procOid = LookupFuncName(procname, 1, argList, true, false);
 
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
@@ -2040,7 +2040,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype)
 	argList[0] = subtype;
 	argList[1] = subtype;
 
-	procOid = LookupFuncName(procname, 2, argList, true);
+	procOid = LookupFuncName(procname, 2, argList, true, false);
 
 	if (!OidIsValid(procOid))
 		ereport(ERROR,
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index c6ce1011e2..e5cc88b245 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -933,7 +933,7 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
 	 */
 	funcargtypes[0] = INTERNALOID;
 
-	handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true);
+	handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true, false);
 
 	/* we want error to complain about no-such-method, not no-such-function */
 	if (!OidIsValid(handlerOid))
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 5222231b51..c2c7a9cacb 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2032,17 +2032,21 @@ func_signature_string(List *funcname, int nargs,
  * namespace search path.
  *
  * If the function is not found, we return InvalidOid if noError is true,
- * else raise an error.
+ * else raise an error.  missing_ok is similar to noError except we still
+ * raise an error for an ambigious function, regardless of what missing_ok is
+ * set to.
  */
 Oid
-LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
+LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError,
+			   bool missing_ok)
 {
 	FuncCandidateList clist;
 
 	/* Passing NULL for argtypes is no longer allowed */
 	Assert(argtypes);
 
-	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError);
+	clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false,
+								  noError || missing_ok);
 
 	/*
 	 * If no arguments were specified, the name must yield a unique candidate.
@@ -2065,7 +2069,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 		}
 		else
 		{
-			if (!noError)
+			if (!noError && !missing_ok)
 				ereport(ERROR,
 						(errcode(ERRCODE_UNDEFINED_FUNCTION),
 						 errmsg("could not find a function named \"%s\"",
@@ -2080,7 +2084,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 		clist = clist->next;
 	}
 
-	if (!noError)
+	if (!noError && !missing_ok)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
 				 errmsg("function %s does not exist",
@@ -2135,10 +2139,11 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError)
 
 	/*
 	 * When looking for a function or routine, we pass noError through to
-	 * LookupFuncName and let it make any error messages.  Otherwise, we make
-	 * our own errors for the aggregate and procedure cases.
+	 * LookupFuncName's missing_ok parameter and let it make any error
+	 * messages.  Otherwise, we make our own errors for the aggregate and
+	 * procedure cases.
 	 */
-	oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids,
+	oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids, false,
 						 (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true);
 
 	if (objtype == OBJECT_FUNCTION)
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index ff1477e64f..4e8f7af0f3 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -63,7 +63,7 @@ extern const char *func_signature_string(List *funcname, int nargs,
 					  List *argnames, const Oid *argtypes);
 
 extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
-			   bool noError);
+			   bool noError, bool missing_ok);
 extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func,
 				   bool noError);
 
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index bfbf62305c..05a4b2af16 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -616,7 +616,7 @@ call_pltcl_start_proc(Oid prolang, bool pltrusted)
 
 	/* Parse possibly-qualified identifier and look up the function */
 	namelist = stringToQualifiedNameList(start_proc);
-	procOid = LookupFuncName(namelist, 0, fargtypes, false);
+	procOid = LookupFuncName(namelist, 0, fargtypes, false, false);
 
 	/* Current user must have permission to call function */
 	aclresult = pg_proc_aclcheck(procOid, GetUserId(), ACL_EXECUTE);
diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out
index b80c5ed2d8..dd4e6c0f6d 100644
--- a/src/test/regress/expected/drop_if_exists.out
+++ b/src/test/regress/expected/drop_if_exists.out
@@ -138,6 +138,16 @@ DROP FUNCTION test_function_exists(int, text, int[]);
 ERROR:  function test_function_exists(integer, text, integer[]) does not exist
 DROP FUNCTION IF EXISTS test_function_exists(int, text, int[]);
 NOTICE:  function test_function_exists(pg_catalog.int4,text,pg_catalog.int4[]) does not exist, skipping
+-- Ensure we still receive an ambiguous function error when there are
+-- multiple matching functions.
+CREATE FUNCTION test_ambiguous_funcname(int) returns int as $$ select $1; $$ language sql;
+CREATE FUNCTION test_ambiguous_funcname(text) returns text as $$ select $1; $$ language sql;
+DROP FUNCTION IF EXISTS test_ambiguous_funcname;
+ERROR:  function name "test_ambiguous_funcname" is not unique
+HINT:  Specify the argument list to select the function unambiguously.
+-- cleanup
+DROP FUNCTION test_ambiguous_funcname(int);
+DROP FUNCTION test_ambiguous_funcname(text);
 -- aggregate
 DROP AGGREGATE test_aggregate_exists(*);
 ERROR:  aggregate test_aggregate_exists(*) does not exist
diff --git a/src/test/regress/sql/drop_if_exists.sql b/src/test/regress/sql/drop_if_exists.sql
index c1d30bc4a5..ad4ef76460 100644
--- a/src/test/regress/sql/drop_if_exists.sql
+++ b/src/test/regress/sql/drop_if_exists.sql
@@ -153,6 +153,16 @@ DROP FUNCTION IF EXISTS test_function_exists();
 DROP FUNCTION test_function_exists(int, text, int[]);
 DROP FUNCTION IF EXISTS test_function_exists(int, text, int[]);
 
+-- Ensure we still receive an ambiguous function error when there are
+-- multiple matching functions.
+CREATE FUNCTION test_ambiguous_funcname(int) returns int as $$ select $1; $$ language sql;
+CREATE FUNCTION test_ambiguous_funcname(text) returns text as $$ select $1; $$ language sql;
+DROP FUNCTION IF EXISTS test_ambiguous_funcname;
+
+-- cleanup
+DROP FUNCTION test_ambiguous_funcname(int);
+DROP FUNCTION test_ambiguous_funcname(text);
+
 -- aggregate
 DROP AGGREGATE test_aggregate_exists(*);
 DROP AGGREGATE IF EXISTS test_aggregate_exists(*);