functioncmds.c 51.2 KB
Newer Older
1 2 3 4
/*-------------------------------------------------------------------------
 *
 * functioncmds.c
 *
5
 *	  Routines for CREATE and DROP FUNCTION commands and CREATE and DROP
6
 *	  CAST commands.
7
 *
Bruce Momjian's avatar
Bruce Momjian committed
8
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
9 10 11 12
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
13
 *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.108 2009/02/24 01:38:09 tgl Exp $
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 * DESCRIPTION
 *	  These routines take the parse tree and pick out the
 *	  appropriate arguments/flags, and pass the results to the
 *	  corresponding "FooDefine" routines (in src/catalog) that do
 *	  the actual catalog-munging.  These routines also verify permission
 *	  of the user to execute the command.
 *
 * NOTES
 *	  These things must be defined and committed in the following order:
 *		"create function":
 *				input/output, recv/send procedures
 *		"create type":
 *				type
 *		"create operator":
 *				operators
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

35
#include "access/genam.h"
36
#include "access/heapam.h"
37
#include "access/sysattr.h"
38
#include "catalog/dependency.h"
39
#include "catalog/indexing.h"
40
#include "catalog/pg_aggregate.h"
41
#include "catalog/pg_cast.h"
42
#include "catalog/pg_language.h"
43
#include "catalog/pg_namespace.h"
44
#include "catalog/pg_proc.h"
45
#include "catalog/pg_proc_fn.h"
46
#include "catalog/pg_type.h"
47
#include "catalog/pg_type_fn.h"
48
#include "commands/defrem.h"
49
#include "commands/proclang.h"
50
#include "miscadmin.h"
51
#include "optimizer/var.h"
52
#include "parser/parse_coerce.h"
53
#include "parser/parse_expr.h"
54 55 56
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
57
#include "utils/builtins.h"
58
#include "utils/fmgroids.h"
59
#include "utils/guc.h"
60
#include "utils/lsyscache.h"
61
#include "utils/rel.h"
62
#include "utils/syscache.h"
63
#include "utils/tqual.h"
64

65 66

static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup,
Bruce Momjian's avatar
Bruce Momjian committed
67
							Oid newOwnerId);
68

69 70

/*
71
 *	 Examine the RETURNS clause of the CREATE FUNCTION statement
72 73 74 75 76 77
 *	 and return information about it as *prorettype_p and *returnsSet.
 *
 * This is more complex than the average typename lookup because we want to
 * allow a shell type to be used, or even created if the specified return type
 * doesn't exist yet.  (Without this, there's no way to define the I/O procs
 * for a new type.)  But SQL function creation won't cope, so error out if
Bruce Momjian's avatar
Bruce Momjian committed
78
 * the target language is SQL.	(We do this here, not in the SQL-function
79
 * validator, so as not to produce a NOTICE and then an ERROR for the same
80
 * condition.)
81 82 83 84 85
 */
static void
compute_return_type(TypeName *returnType, Oid languageOid,
					Oid *prorettype_p, bool *returnsSet_p)
{
Bruce Momjian's avatar
Bruce Momjian committed
86
	Oid			rettype;
87
	Type		typtup;
88

89
	typtup = LookupTypeName(NULL, returnType, NULL);
90

91
	if (typtup)
92
	{
93
		if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
94 95
		{
			if (languageOid == SQLlanguageId)
96 97
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
98 99
						 errmsg("SQL function cannot return shell type %s",
								TypeNameToString(returnType))));
100
			else
101 102 103 104
				ereport(NOTICE,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("return type %s is only a shell",
								TypeNameToString(returnType))));
105
		}
106 107
		rettype = typeTypeId(typtup);
		ReleaseSysCache(typtup);
108 109 110
	}
	else
	{
Bruce Momjian's avatar
Bruce Momjian committed
111
		char	   *typnam = TypeNameToString(returnType);
112 113 114
		Oid			namespaceId;
		AclResult	aclresult;
		char	   *typname;
115

116 117 118 119 120 121 122 123
		/*
		 * Only C-coded functions can be I/O functions.  We enforce this
		 * restriction here mainly to prevent littering the catalogs with
		 * shell types due to simple typos in user-defined function
		 * definitions.
		 */
		if (languageOid != INTERNALlanguageId &&
			languageOid != ClanguageId)
124 125
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
126
					 errmsg("type \"%s\" does not exist", typnam)));
127

128 129 130 131
		/* Reject if there's typmod decoration, too */
		if (returnType->typmods != NIL)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
Bruce Momjian's avatar
Bruce Momjian committed
132 133
			errmsg("type modifier cannot be specified for shell type \"%s\"",
				   typnam)));
134

135
		/* Otherwise, go ahead and make a shell type */
136 137
		ereport(NOTICE,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
138
				 errmsg("type \"%s\" is not yet defined", typnam),
139
				 errdetail("Creating a shell type definition.")));
140 141 142 143 144
		namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
														&typname);
		aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
										  ACL_CREATE);
		if (aclresult != ACLCHECK_OK)
145 146
			aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
						   get_namespace_name(namespaceId));
147
		rettype = TypeShellMake(typname, namespaceId, GetUserId());
148
		Assert(OidIsValid(rettype));
149 150 151 152 153 154 155
	}

	*prorettype_p = rettype;
	*returnsSet_p = returnType->setof;
}

/*
156
 * Interpret the parameter list of the CREATE FUNCTION statement.
157 158 159 160 161
 *
 * Results are stored into output parameters.  parameterTypes must always
 * be created, but the other arrays are set to NULL if not needed.
 * requiredResultType is set to InvalidOid if there are no OUT parameters,
 * else it is set to the OID of the implied result type.
162
 */
163 164
static void
examine_parameter_list(List *parameters, Oid languageOid,
165
					   const char *queryString,
166 167 168 169
					   oidvector **parameterTypes,
					   ArrayType **allParameterTypes,
					   ArrayType **parameterModes,
					   ArrayType **parameterNames,
170
					   List **parameterDefaults,
171
					   Oid *requiredResultType)
172
{
173 174 175 176 177 178 179
	int			parameterCount = list_length(parameters);
	Oid		   *inTypes;
	int			inCount = 0;
	Datum	   *allTypes;
	Datum	   *paramModes;
	Datum	   *paramNames;
	int			outCount = 0;
180
	int			varCount = 0;
181
	bool		have_names = false;
182
	bool		have_defaults = false;
183
	ListCell   *x;
184
	int			i;
185
	ParseState *pstate;
186

187
	*requiredResultType = InvalidOid;	/* default result */
188

189 190 191 192
	inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
	allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
	paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
	paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
193 194
	*parameterDefaults = NIL;

195
	/* may need a pstate for parse analysis of default exprs */
196 197
	pstate = make_parsestate(NULL);
	pstate->p_sourcetext = queryString;
198

199 200 201
	/* Scan the list and extract data into work arrays */
	i = 0;
	foreach(x, parameters)
202
	{
203 204
		FunctionParameter *fp = (FunctionParameter *) lfirst(x);
		TypeName   *t = fp->argType;
205
		bool		isinput = false;
206
		Oid			toid;
207
		Type		typtup;
208

209 210
		typtup = LookupTypeName(NULL, t, NULL);
		if (typtup)
211
		{
212
			if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
213
			{
214
				/* As above, hard error if language is SQL */
215
				if (languageOid == SQLlanguageId)
216 217
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
218 219
						   errmsg("SQL function cannot accept shell type %s",
								  TypeNameToString(t))));
220
				else
221 222 223 224
					ereport(NOTICE,
							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
							 errmsg("argument type %s is only a shell",
									TypeNameToString(t))));
225
			}
226 227
			toid = typeTypeId(typtup);
			ReleaseSysCache(typtup);
228 229 230
		}
		else
		{
231 232 233 234
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("type %s does not exist",
							TypeNameToString(t))));
235
			toid = InvalidOid;	/* keep compiler quiet */
236 237 238
		}

		if (t->setof)
239 240 241
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("functions cannot accept set arguments")));
242

243 244
		/* handle input parameters */
		if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
245
		{
246
			/* other input parameters can't follow a VARIADIC parameter */
247 248 249 250
			if (varCount > 0)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
						 errmsg("VARIADIC parameter must be the last input parameter")));
251
			inTypes[inCount++] = toid;
252
			isinput = true;
253
		}
254

255
		/* handle output parameters */
256
		if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
257
		{
258
			if (outCount == 0)	/* save first output param's type */
259 260 261 262
				*requiredResultType = toid;
			outCount++;
		}

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
		if (fp->mode == FUNC_PARAM_VARIADIC)
		{
			varCount++;
			/* validate variadic parameter type */
			switch (toid)
			{
				case ANYARRAYOID:
				case ANYOID:
					/* okay */
					break;
				default:
					if (!OidIsValid(get_element_type(toid)))
						ereport(ERROR,
								(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
								 errmsg("VARIADIC parameter must be an array")));
					break;
			}
		}

282
		allTypes[i] = ObjectIdGetDatum(toid);
283

284
		paramModes[i] = CharGetDatum(fp->mode);
285

286 287
		if (fp->name && fp->name[0])
		{
288
			paramNames[i] = CStringGetTextDatum(fp->name);
289 290 291
			have_names = true;
		}

292 293
		if (fp->defexpr)
		{
294 295 296
			Node   *def;

			if (!isinput)
297 298
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
						 errmsg("only input parameters can have default values")));

			def = transformExpr(pstate, fp->defexpr);
			def = coerce_to_specific_type(pstate, def, toid, "DEFAULT");

			/*
			 * Make sure no variables are referred to.
			 */
			if (list_length(pstate->p_rtable) != 0 ||
				contain_var_clause(def))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
						 errmsg("cannot use table references in parameter default value")));

			/*
314 315 316
			 * It can't return a set either --- but coerce_to_specific_type
			 * already checked that for us.
			 *
317
			 * No subplans or aggregates, either...
318 319 320 321 322
			 *
			 * Note: the point of these restrictions is to ensure that an
			 * expression that, on its face, hasn't got subplans, aggregates,
			 * etc cannot suddenly have them after function default arguments
			 * are inserted.
323 324 325 326 327 328 329 330 331
			 */
			if (pstate->p_hasSubLinks)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot use subquery in parameter default value")));
			if (pstate->p_hasAggs)
				ereport(ERROR,
						(errcode(ERRCODE_GROUPING_ERROR),
						 errmsg("cannot use aggregate function in parameter default value")));
332 333 334 335
			if (pstate->p_hasWindowFuncs)
				ereport(ERROR,
						(errcode(ERRCODE_WINDOWING_ERROR),
						 errmsg("cannot use window function in parameter default value")));
336

337
			*parameterDefaults = lappend(*parameterDefaults, def);
338 339 340 341
			have_defaults = true;
		}
		else
		{
342
			if (isinput && have_defaults)
343 344
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
345
						 errmsg("input parameters after one with a default value must also have defaults")));
346 347
		}

348
		i++;
349 350
	}

351 352
	free_parsestate(pstate);

353 354 355
	/* Now construct the proper outputs as needed */
	*parameterTypes = buildoidvector(inTypes, inCount);

356
	if (outCount > 0 || varCount > 0)
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
	{
		*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
											 sizeof(Oid), true, 'i');
		*parameterModes = construct_array(paramModes, parameterCount, CHAROID,
										  1, true, 'c');
		if (outCount > 1)
			*requiredResultType = RECORDOID;
		/* otherwise we set requiredResultType correctly above */
	}
	else
	{
		*allParameterTypes = NULL;
		*parameterModes = NULL;
	}

	if (have_names)
	{
		for (i = 0; i < parameterCount; i++)
		{
			if (paramNames[i] == PointerGetDatum(NULL))
377
				paramNames[i] = CStringGetTextDatum("");
378 379 380 381 382 383
		}
		*parameterNames = construct_array(paramNames, parameterCount, TEXTOID,
										  -1, false, 'i');
	}
	else
		*parameterNames = NULL;
384 385
}

386

387 388 389 390 391
/*
 * Recognize one of the options that can be passed to both CREATE
 * FUNCTION and ALTER FUNCTION and return it via one of the out
 * parameters. Returns true if the passed option was recognized. If
 * the out parameter we were going to assign to points to non-NULL,
Bruce Momjian's avatar
Bruce Momjian committed
392
 * raise a duplicate-clause error.	(We don't try to detect duplicate
393
 * SET parameters though --- if you're redundant, the last one wins.)
394 395 396 397 398
 */
static bool
compute_common_attribute(DefElem *defel,
						 DefElem **volatility_item,
						 DefElem **strict_item,
399
						 DefElem **security_item,
400
						 List **set_items,
401 402
						 DefElem **cost_item,
						 DefElem **rows_item)
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
{
	if (strcmp(defel->defname, "volatility") == 0)
	{
		if (*volatility_item)
			goto duplicate_error;

		*volatility_item = defel;
	}
	else if (strcmp(defel->defname, "strict") == 0)
	{
		if (*strict_item)
			goto duplicate_error;

		*strict_item = defel;
	}
	else if (strcmp(defel->defname, "security") == 0)
	{
		if (*security_item)
			goto duplicate_error;

		*security_item = defel;
	}
425 426 427 428
	else if (strcmp(defel->defname, "set") == 0)
	{
		*set_items = lappend(*set_items, defel->arg);
	}
429 430 431 432 433 434 435 436 437 438 439 440 441 442
	else if (strcmp(defel->defname, "cost") == 0)
	{
		if (*cost_item)
			goto duplicate_error;

		*cost_item = defel;
	}
	else if (strcmp(defel->defname, "rows") == 0)
	{
		if (*rows_item)
			goto duplicate_error;

		*rows_item = defel;
	}
443 444 445 446 447 448 449 450 451 452
	else
		return false;

	/* Recognized an option */
	return true;

duplicate_error:
	ereport(ERROR,
			(errcode(ERRCODE_SYNTAX_ERROR),
			 errmsg("conflicting or redundant options")));
453
	return false;				/* keep compiler quiet */
454 455 456 457 458
}

static char
interpret_func_volatility(DefElem *defel)
{
459
	char	   *str = strVal(defel->arg);
460 461 462 463 464 465 466 467 468 469

	if (strcmp(str, "immutable") == 0)
		return PROVOLATILE_IMMUTABLE;
	else if (strcmp(str, "stable") == 0)
		return PROVOLATILE_STABLE;
	else if (strcmp(str, "volatile") == 0)
		return PROVOLATILE_VOLATILE;
	else
	{
		elog(ERROR, "invalid volatility \"%s\"", str);
470
		return 0;				/* keep compiler quiet */
471 472
	}
}
473

474
/*
475
 * Update a proconfig value according to a list of VariableSetStmt items.
476 477 478 479 480 481 482 483 484 485
 *
 * The input and result may be NULL to signify a null entry.
 */
static ArrayType *
update_proconfig_value(ArrayType *a, List *set_items)
{
	ListCell   *l;

	foreach(l, set_items)
	{
486
		VariableSetStmt *sstmt = (VariableSetStmt *) lfirst(l);
487

488 489 490 491
		Assert(IsA(sstmt, VariableSetStmt));
		if (sstmt->kind == VAR_RESET_ALL)
			a = NULL;
		else
492
		{
493
			char	   *valuestr = ExtractSetVariableArgs(sstmt);
494

495
			if (valuestr)
496
				a = GUCArrayAdd(a, sstmt->name, valuestr);
Bruce Momjian's avatar
Bruce Momjian committed
497
			else	/* RESET */
498 499 500 501 502 503 504 505
				a = GUCArrayDelete(a, sstmt->name);
		}
	}

	return a;
}


506 507 508 509 510
/*
 * Dissect the list of options assembled in gram.y into function
 * attributes.
 */
static void
511
compute_attributes_sql_style(List *options,
512 513
							 List **as,
							 char **language,
514
							 bool *windowfunc_p,
515 516
							 char *volatility_p,
							 bool *strict_p,
517
							 bool *security_definer,
518
							 ArrayType **proconfig,
519 520
							 float4 *procost,
							 float4 *prorows)
521
{
522
	ListCell   *option;
Bruce Momjian's avatar
Bruce Momjian committed
523 524
	DefElem    *as_item = NULL;
	DefElem    *language_item = NULL;
525
	DefElem    *windowfunc_item = NULL;
Bruce Momjian's avatar
Bruce Momjian committed
526 527 528
	DefElem    *volatility_item = NULL;
	DefElem    *strict_item = NULL;
	DefElem    *security_item = NULL;
529
	List	   *set_items = NIL;
530 531
	DefElem    *cost_item = NULL;
	DefElem    *rows_item = NULL;
532 533 534 535 536

	foreach(option, options)
	{
		DefElem    *defel = (DefElem *) lfirst(option);

Bruce Momjian's avatar
Bruce Momjian committed
537
		if (strcmp(defel->defname, "as") == 0)
538 539
		{
			if (as_item)
540 541 542
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
543 544
			as_item = defel;
		}
Bruce Momjian's avatar
Bruce Momjian committed
545
		else if (strcmp(defel->defname, "language") == 0)
546 547
		{
			if (language_item)
548 549 550
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
551 552
			language_item = defel;
		}
553 554 555 556 557 558 559 560
		else if (strcmp(defel->defname, "window") == 0)
		{
			if (windowfunc_item)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
			windowfunc_item = defel;
		}
561 562 563
		else if (compute_common_attribute(defel,
										  &volatility_item,
										  &strict_item,
564
										  &security_item,
565
										  &set_items,
566 567
										  &cost_item,
										  &rows_item))
568
		{
569 570
			/* recognized common option */
			continue;
571 572
		}
		else
573 574
			elog(ERROR, "option \"%s\" not recognized",
				 defel->defname);
575 576
	}

577
	/* process required items */
578
	if (as_item)
Bruce Momjian's avatar
Bruce Momjian committed
579
		*as = (List *) as_item->arg;
580
	else
581
	{
582 583 584
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("no function body specified")));
585 586
		*as = NIL;				/* keep compiler quiet */
	}
587 588 589 590

	if (language_item)
		*language = strVal(language_item->arg);
	else
591
	{
592 593 594
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("no language specified")));
595 596
		*language = NULL;		/* keep compiler quiet */
	}
597

598
	/* process optional items */
599 600
	if (windowfunc_item)
		*windowfunc_p = intVal(windowfunc_item->arg);
601
	if (volatility_item)
602
		*volatility_p = interpret_func_volatility(volatility_item);
603 604 605 606
	if (strict_item)
		*strict_p = intVal(strict_item->arg);
	if (security_item)
		*security_definer = intVal(security_item->arg);
607 608
	if (set_items)
		*proconfig = update_proconfig_value(NULL, set_items);
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
	if (cost_item)
	{
		*procost = defGetNumeric(cost_item);
		if (*procost <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("COST must be positive")));
	}
	if (rows_item)
	{
		*prorows = defGetNumeric(rows_item);
		if (*prorows <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("ROWS must be positive")));
	}
625 626 627
}


628
/*-------------
Neil Conway's avatar
Neil Conway committed
629
 *	 Interpret the parameters *parameters and return their contents via
630
 *	 *isStrict_p and *volatility_p.
631 632
 *
 *	These parameters supply optional information about a function.
Neil Conway's avatar
Neil Conway committed
633
 *	All have defaults if not specified. Parameters:
634 635 636 637 638 639 640 641 642
 *
 *	 * isStrict means the function should not be called when any NULL
 *	   inputs are present; instead a NULL result value should be assumed.
 *
 *	 * volatility tells the optimizer whether the function's result can
 *	   be assumed to be repeatable over multiple evaluations.
 *------------
 */
static void
643
compute_attributes_with_style(List *parameters, bool *isStrict_p, char *volatility_p)
644
{
645
	ListCell   *pl;
646 647 648 649 650

	foreach(pl, parameters)
	{
		DefElem    *param = (DefElem *) lfirst(pl);

651
		if (pg_strcasecmp(param->defname, "isstrict") == 0)
652
			*isStrict_p = defGetBoolean(param);
653
		else if (pg_strcasecmp(param->defname, "iscachable") == 0)
654 655
		{
			/* obsolete spelling of isImmutable */
656 657
			if (defGetBoolean(param))
				*volatility_p = PROVOLATILE_IMMUTABLE;
658 659
		}
		else
660 661
			ereport(WARNING,
					(errcode(ERRCODE_SYNTAX_ERROR),
662 663
					 errmsg("unrecognized function attribute \"%s\" ignored",
							param->defname)));
664 665 666 667 668 669 670 671 672 673 674 675 676 677
	}
}


/*
 * For a dynamically linked C language object, the form of the clause is
 *
 *	   AS <object file name> [, <link symbol name> ]
 *
 * In all other cases
 *
 *	   AS <object reference, or sql code>
 */
static void
678 679
interpret_AS_clause(Oid languageOid, const char *languageName,
					char *funcname, List *as,
680 681 682 683 684 685 686
					char **prosrc_str_p, char **probin_str_p)
{
	Assert(as != NIL);

	if (languageOid == ClanguageId)
	{
		/*
687
		 * For "C" language, store the file name in probin and, when given,
688 689 690 691 692
		 * the link symbol name in prosrc.  If link symbol is omitted,
		 * substitute procedure name.  We also allow link symbol to be
		 * specified as "-", since that was the habit in PG versions before
		 * 8.4, and there might be dump files out there that don't translate
		 * that back to "omitted".
693
		 */
694 695
		*probin_str_p = strVal(linitial(as));
		if (list_length(as) == 1)
696
			*prosrc_str_p = funcname;
697
		else
698
		{
699
			*prosrc_str_p = strVal(lsecond(as));
700 701 702
			if (strcmp(*prosrc_str_p, "-") == 0)
				*prosrc_str_p = funcname;
		}
703 704 705 706
	}
	else
	{
		/* Everything else wants the given string in prosrc. */
707
		*prosrc_str_p = strVal(linitial(as));
708
		*probin_str_p = NULL;
709

710
		if (list_length(as) != 1)
711 712 713 714
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("only one AS item needed for language \"%s\"",
							languageName)));
715 716 717 718 719 720 721 722 723 724 725 726 727 728

		if (languageOid == INTERNALlanguageId)
		{
			/*
			 * In PostgreSQL versions before 6.5, the SQL name of the created
			 * function could not be different from the internal name, and
			 * "prosrc" wasn't used.  So there is code out there that does
			 * CREATE FUNCTION xyz AS '' LANGUAGE internal. To preserve some
			 * modicum of backwards compatibility, accept an empty "prosrc"
			 * value as meaning the supplied SQL function name.
			 */
			if (strlen(*prosrc_str_p) == 0)
				*prosrc_str_p = funcname;
		}
729 730 731 732 733 734 735 736 737 738
	}
}



/*
 * CreateFunction
 *	 Execute a CREATE FUNCTION utility statement.
 */
void
739
CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
740 741 742 743 744
{
	char	   *probin_str;
	char	   *prosrc_str;
	Oid			prorettype;
	bool		returnsSet;
745
	char	   *language;
746
	char	   *languageName;
747
	Oid			languageOid;
748
	Oid			languageValidator;
749 750
	char	   *funcname;
	Oid			namespaceId;
751
	AclResult	aclresult;
752 753 754 755
	oidvector  *parameterTypes;
	ArrayType  *allParameterTypes;
	ArrayType  *parameterModes;
	ArrayType  *parameterNames;
756
	List	   *parameterDefaults;
757
	Oid			requiredResultType;
758 759
	bool		isWindowFunc,
				isStrict,
760
				security;
761
	char		volatility;
762
	ArrayType  *proconfig;
763 764
	float4		procost;
	float4		prorows;
765 766
	HeapTuple	languageTuple;
	Form_pg_language languageStruct;
767
	List	   *as_clause;
768 769 770 771 772

	/* Convert list of names to a name and namespace */
	namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
													&funcname);

773 774 775
	/* Check we have creation rights in target namespace */
	aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
	if (aclresult != ACLCHECK_OK)
776 777
		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
					   get_namespace_name(namespaceId));
778

779
	/* default attributes */
780
	isWindowFunc = false;
781
	isStrict = false;
782
	security = false;
783
	volatility = PROVOLATILE_VOLATILE;
784
	proconfig = NULL;
785 786
	procost = -1;				/* indicates not set */
	prorows = -1;				/* indicates not set */
787 788 789

	/* override attributes from explicit list */
	compute_attributes_sql_style(stmt->options,
790
								 &as_clause, &language,
791 792
								 &isWindowFunc, &volatility,
								 &isStrict, &security,
793
								 &proconfig, &procost, &prorows);
794

795
	/* Convert language name to canonical case */
796
	languageName = case_translate_language_name(language);
797 798 799 800 801 802

	/* Look up the language and validate permissions */
	languageTuple = SearchSysCache(LANGNAME,
								   PointerGetDatum(languageName),
								   0, 0, 0);
	if (!HeapTupleIsValid(languageTuple))
803 804
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
805
				 errmsg("language \"%s\" does not exist", languageName),
806 807
				 (PLTemplateExists(languageName) ?
				  errhint("Use CREATE LANGUAGE to load the language into the database.") : 0)));
Bruce Momjian's avatar
Bruce Momjian committed
808

809
	languageOid = HeapTupleGetOid(languageTuple);
810 811
	languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);

812 813 814 815 816 817 818
	if (languageStruct->lanpltrusted)
	{
		/* if trusted language, need USAGE privilege */
		AclResult	aclresult;

		aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE);
		if (aclresult != ACLCHECK_OK)
819 820
			aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
						   NameStr(languageStruct->lanname));
821 822 823 824 825
	}
	else
	{
		/* if untrusted language, must be superuser */
		if (!superuser())
826 827
			aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
						   NameStr(languageStruct->lanname));
828
	}
829

830 831
	languageValidator = languageStruct->lanvalidator;

832 833 834 835 836 837
	ReleaseSysCache(languageTuple);

	/*
	 * Convert remaining parameters of CREATE to form wanted by
	 * ProcedureCreate.
	 */
838
	examine_parameter_list(stmt->parameters, languageOid, queryString,
839 840 841 842
						   &parameterTypes,
						   &allParameterTypes,
						   &parameterModes,
						   &parameterNames,
843 844
						   &parameterDefaults,
						   &requiredResultType);
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871

	if (stmt->returnType)
	{
		/* explicit RETURNS clause */
		compute_return_type(stmt->returnType, languageOid,
							&prorettype, &returnsSet);
		if (OidIsValid(requiredResultType) && prorettype != requiredResultType)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
					 errmsg("function result type must be %s because of OUT parameters",
							format_type_be(requiredResultType))));
	}
	else if (OidIsValid(requiredResultType))
	{
		/* default RETURNS clause from OUT parameters */
		prorettype = requiredResultType;
		returnsSet = false;
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
				 errmsg("function result type must be specified")));
		/* Alternative possibility: default to RETURNS VOID */
		prorettype = VOIDOID;
		returnsSet = false;
	}
872

873
	compute_attributes_with_style(stmt->withClause, &isStrict, &volatility);
874

875
	interpret_AS_clause(languageOid, languageName, funcname, as_clause,
876 877
						&prosrc_str, &probin_str);

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
	/*
	 * Set default values for COST and ROWS depending on other parameters;
	 * reject ROWS if it's not returnsSet.  NB: pg_dump knows these default
	 * values, keep it in sync if you change them.
	 */
	if (procost < 0)
	{
		/* SQL and PL-language functions are assumed more expensive */
		if (languageOid == INTERNALlanguageId ||
			languageOid == ClanguageId)
			procost = 1;
		else
			procost = 100;
	}
	if (prorows < 0)
	{
		if (returnsSet)
			prorows = 1000;
		else
			prorows = 0;		/* dummy value if not returnsSet */
	}
	else if (!returnsSet)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("ROWS is not applicable when function does not return a set")));

904
	/*
905 906
	 * And now that we have all the parameters, and know we're permitted to do
	 * so, go ahead and create the function.
907 908 909 910 911 912 913
	 */
	ProcedureCreate(funcname,
					namespaceId,
					stmt->replace,
					returnsSet,
					prorettype,
					languageOid,
914
					languageValidator,
915 916 917
					prosrc_str, /* converted to text later */
					probin_str, /* converted to text later */
					false,		/* not an aggregate */
918
					isWindowFunc,
919
					security,
920 921
					isStrict,
					volatility,
922
					parameterTypes,
923 924
					PointerGetDatum(allParameterTypes),
					PointerGetDatum(parameterModes),
925
					PointerGetDatum(parameterNames),
926
					parameterDefaults,
927
					PointerGetDatum(proconfig),
928
					procost,
929
					prorows);
930 931 932 933 934 935 936 937
}


/*
 * RemoveFunction
 *		Deletes a function.
 */
void
938
RemoveFunction(RemoveFuncStmt *stmt)
939
{
Tom Lane's avatar
Tom Lane committed
940
	List	   *functionName = stmt->name;
Bruce Momjian's avatar
Bruce Momjian committed
941
	List	   *argTypes = stmt->args;	/* list of TypeName nodes */
942 943
	Oid			funcOid;
	HeapTuple	tup;
944
	ObjectAddress object;
945

946 947 948
	/*
	 * Find the function, do permissions and validity checks
	 */
949
	funcOid = LookupFuncNameTypeNames(functionName, argTypes, stmt->missing_ok);
Bruce Momjian's avatar
Bruce Momjian committed
950
	if (!OidIsValid(funcOid))
951
	{
952
		/* can only get here if stmt->missing_ok */
953
		ereport(NOTICE,
954
				(errmsg("function %s(%s) does not exist, skipping",
955
						NameListToString(functionName),
956
						TypeNameListToString(argTypes))));
957 958 959
		return;
	}

960 961 962
	tup = SearchSysCache(PROCOID,
						 ObjectIdGetDatum(funcOid),
						 0, 0, 0);
Bruce Momjian's avatar
Bruce Momjian committed
963
	if (!HeapTupleIsValid(tup)) /* should not happen */
964
		elog(ERROR, "cache lookup failed for function %u", funcOid);
965

966 967
	/* Permission check: must own func or its namespace */
	if (!pg_proc_ownercheck(funcOid, GetUserId()) &&
968 969
	  !pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
							   GetUserId()))
970 971
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
					   NameListToString(functionName));
972 973

	if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
974 975 976 977
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is an aggregate function",
						NameListToString(functionName)),
978
				 errhint("Use DROP AGGREGATE to drop aggregate functions.")));
979 980 981

	if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
	{
982
		/* "Helpful" NOTICE when removing a builtin function ... */
983 984 985 986
		ereport(NOTICE,
				(errcode(ERRCODE_WARNING),
				 errmsg("removing built-in function \"%s\"",
						NameListToString(functionName))));
987 988
	}

989 990 991 992 993
	ReleaseSysCache(tup);

	/*
	 * Do the deletion
	 */
994
	object.classId = ProcedureRelationId;
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
	object.objectId = funcOid;
	object.objectSubId = 0;

	performDeletion(&object, stmt->behavior);
}

/*
 * Guts of function deletion.
 *
 * Note: this is also used for aggregate deletion, since the OIDs of
 * both functions and aggregates point to pg_proc.
 */
void
RemoveFunctionById(Oid funcOid)
{
	Relation	relation;
	HeapTuple	tup;
	bool		isagg;

	/*
	 * Delete the pg_proc tuple.
	 */
1017
	relation = heap_open(ProcedureRelationId, RowExclusiveLock);
1018 1019 1020 1021

	tup = SearchSysCache(PROCOID,
						 ObjectIdGetDatum(funcOid),
						 0, 0, 0);
Bruce Momjian's avatar
Bruce Momjian committed
1022
	if (!HeapTupleIsValid(tup)) /* should not happen */
1023
		elog(ERROR, "cache lookup failed for function %u", funcOid);
1024 1025

	isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;
1026 1027 1028 1029 1030 1031

	simple_heap_delete(relation, &tup->t_self);

	ReleaseSysCache(tup);

	heap_close(relation, RowExclusiveLock);
1032 1033 1034 1035 1036 1037

	/*
	 * If there's a pg_aggregate tuple, delete that too.
	 */
	if (isagg)
	{
1038
		relation = heap_open(AggregateRelationId, RowExclusiveLock);
1039 1040 1041 1042

		tup = SearchSysCache(AGGFNOID,
							 ObjectIdGetDatum(funcOid),
							 0, 0, 0);
Bruce Momjian's avatar
Bruce Momjian committed
1043
		if (!HeapTupleIsValid(tup))		/* should not happen */
1044
			elog(ERROR, "cache lookup failed for pg_aggregate tuple for function %u", funcOid);
1045 1046 1047 1048 1049 1050 1051

		simple_heap_delete(relation, &tup->t_self);

		ReleaseSysCache(tup);

		heap_close(relation, RowExclusiveLock);
	}
1052
}
1053 1054


1055 1056 1057 1058 1059 1060 1061 1062 1063
/*
 * Rename function
 */
void
RenameFunction(List *name, List *argtypes, const char *newname)
{
	Oid			procOid;
	Oid			namespaceOid;
	HeapTuple	tup;
1064
	Form_pg_proc procForm;
1065 1066 1067
	Relation	rel;
	AclResult	aclresult;

1068
	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1069

1070
	procOid = LookupFuncNameTypeNames(name, argtypes, false);
1071 1072 1073 1074 1075

	tup = SearchSysCacheCopy(PROCOID,
							 ObjectIdGetDatum(procOid),
							 0, 0, 0);
	if (!HeapTupleIsValid(tup)) /* should not happen */
1076 1077
		elog(ERROR, "cache lookup failed for function %u", procOid);
	procForm = (Form_pg_proc) GETSTRUCT(tup);
1078

1079
	if (procForm->proisagg)
1080
		ereport(ERROR,
1081 1082 1083
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is an aggregate function",
						NameListToString(name)),
1084
			 errhint("Use ALTER AGGREGATE to rename aggregate functions.")));
1085

1086
	namespaceOid = procForm->pronamespace;
1087 1088

	/* make sure the new name doesn't exist */
1089
	if (SearchSysCacheExists(PROCNAMEARGSNSP,
1090
							 CStringGetDatum(newname),
1091 1092 1093
							 PointerGetDatum(&procForm->proargtypes),
							 ObjectIdGetDatum(namespaceOid),
							 0))
1094 1095
	{
		ereport(ERROR,
1096
				(errcode(ERRCODE_DUPLICATE_FUNCTION),
1097 1098 1099
				 errmsg("function %s already exists in schema \"%s\"",
						funcname_signature_string(newname,
												  procForm->pronargs,
1100
											   procForm->proargtypes.values),
1101
						get_namespace_name(namespaceOid))));
1102 1103 1104 1105
	}

	/* must be owner */
	if (!pg_proc_ownercheck(procOid, GetUserId()))
1106 1107
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
					   NameListToString(name));
1108 1109 1110 1111

	/* must have CREATE privilege on namespace */
	aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
	if (aclresult != ACLCHECK_OK)
1112 1113
		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
					   get_namespace_name(namespaceOid));
1114 1115

	/* rename */
1116
	namestrcpy(&(procForm->proname), newname);
1117 1118 1119 1120 1121 1122 1123
	simple_heap_update(rel, &tup->t_self, tup);
	CatalogUpdateIndexes(rel, tup);

	heap_close(rel, NoLock);
	heap_freetuple(tup);
}

1124
/*
1125
 * Change function owner by name and args
1126 1127
 */
void
1128
AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
1129
{
1130
	Relation	rel;
1131 1132 1133
	Oid			procOid;
	HeapTuple	tup;

1134
	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1135 1136 1137

	procOid = LookupFuncNameTypeNames(name, argtypes, false);

1138
	tup = SearchSysCache(PROCOID,
Bruce Momjian's avatar
Bruce Momjian committed
1139 1140
						 ObjectIdGetDatum(procOid),
						 0, 0, 0);
1141 1142 1143
	if (!HeapTupleIsValid(tup)) /* should not happen */
		elog(ERROR, "cache lookup failed for function %u", procOid);

1144
	if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
1145 1146 1147 1148
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is an aggregate function",
						NameListToString(name)),
Bruce Momjian's avatar
Bruce Momjian committed
1149
				 errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
1150

1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
	AlterFunctionOwner_internal(rel, tup, newOwnerId);

	heap_close(rel, NoLock);
}

/*
 * Change function owner by Oid
 */
void
AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
{
	Relation	rel;
	HeapTuple	tup;

	rel = heap_open(ProcedureRelationId, RowExclusiveLock);

	tup = SearchSysCache(PROCOID,
						 ObjectIdGetDatum(procOid),
						 0, 0, 0);
	if (!HeapTupleIsValid(tup)) /* should not happen */
		elog(ERROR, "cache lookup failed for function %u", procOid);
	AlterFunctionOwner_internal(rel, tup, newOwnerId);

	heap_close(rel, NoLock);
}

static void
AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
	Form_pg_proc procForm;
	AclResult	aclresult;
	Oid			procOid;

	Assert(RelationGetRelid(rel) == ProcedureRelationId);
	Assert(tup->t_tableOid == ProcedureRelationId);

	procForm = (Form_pg_proc) GETSTRUCT(tup);
	procOid = HeapTupleGetOid(tup);

Bruce Momjian's avatar
Bruce Momjian committed
1190
	/*
1191 1192 1193
	 * If the new owner is the same as the existing owner, consider the
	 * command to have succeeded.  This is for dump restoration purposes.
	 */
1194
	if (procForm->proowner != newOwnerId)
1195
	{
1196
		Datum		repl_val[Natts_pg_proc];
1197 1198
		bool		repl_null[Natts_pg_proc];
		bool		repl_repl[Natts_pg_proc];
Bruce Momjian's avatar
Bruce Momjian committed
1199
		Acl		   *newAcl;
1200 1201 1202 1203
		Datum		aclDatum;
		bool		isNull;
		HeapTuple	newtuple;

1204 1205 1206 1207
		/* Superusers can always do it */
		if (!superuser())
		{
			/* Otherwise, must be owner of the existing object */
1208
			if (!pg_proc_ownercheck(procOid, GetUserId()))
1209
				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
1210
							   NameStr(procForm->proname));
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222

			/* Must be able to become new owner */
			check_is_member_of_role(GetUserId(), newOwnerId);

			/* New owner must have CREATE privilege on namespace */
			aclresult = pg_namespace_aclcheck(procForm->pronamespace,
											  newOwnerId,
											  ACL_CREATE);
			if (aclresult != ACLCHECK_OK)
				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
							   get_namespace_name(procForm->pronamespace));
		}
1223

1224 1225
		memset(repl_null, false, sizeof(repl_null));
		memset(repl_repl, false, sizeof(repl_repl));
1226

1227
		repl_repl[Anum_pg_proc_proowner - 1] = true;
1228
		repl_val[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(newOwnerId);
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239

		/*
		 * Determine the modified ACL for the new owner.  This is only
		 * necessary when the ACL is non-null.
		 */
		aclDatum = SysCacheGetAttr(PROCOID, tup,
								   Anum_pg_proc_proacl,
								   &isNull);
		if (!isNull)
		{
			newAcl = aclnewowner(DatumGetAclP(aclDatum),
1240
								 procForm->proowner, newOwnerId);
1241
			repl_repl[Anum_pg_proc_proacl - 1] = true;
1242 1243 1244
			repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl);
		}

1245
		newtuple = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val,
1246
									repl_null, repl_repl);
1247

1248 1249
		simple_heap_update(rel, &newtuple->t_self, newtuple);
		CatalogUpdateIndexes(rel, newtuple);
1250

1251
		heap_freetuple(newtuple);
1252 1253 1254

		/* Update owner dependency reference */
		changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId);
1255 1256
	}

1257
	ReleaseSysCache(tup);
1258 1259
}

1260 1261 1262 1263 1264 1265 1266 1267
/*
 * Implements the ALTER FUNCTION utility command (except for the
 * RENAME and OWNER clauses, which are handled as part of the generic
 * ALTER framework).
 */
void
AlterFunction(AlterFunctionStmt *stmt)
{
1268 1269
	HeapTuple	tup;
	Oid			funcOid;
1270
	Form_pg_proc procForm;
1271 1272 1273 1274 1275
	Relation	rel;
	ListCell   *l;
	DefElem    *volatility_item = NULL;
	DefElem    *strict_item = NULL;
	DefElem    *security_def_item = NULL;
1276
	List	   *set_items = NIL;
1277 1278
	DefElem    *cost_item = NULL;
	DefElem    *rows_item = NULL;
1279

1280
	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292

	funcOid = LookupFuncNameTypeNames(stmt->func->funcname,
									  stmt->func->funcargs,
									  false);

	tup = SearchSysCacheCopy(PROCOID,
							 ObjectIdGetDatum(funcOid),
							 0, 0, 0);
	if (!HeapTupleIsValid(tup)) /* should not happen */
		elog(ERROR, "cache lookup failed for function %u", funcOid);

	procForm = (Form_pg_proc) GETSTRUCT(tup);
1293

1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
	/* Permission check: must own function */
	if (!pg_proc_ownercheck(funcOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
					   NameListToString(stmt->func->funcname));

	if (procForm->proisagg)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is an aggregate function",
						NameListToString(stmt->func->funcname))));

	/* Examine requested actions. */
1306
	foreach(l, stmt->actions)
1307
	{
1308
		DefElem    *defel = (DefElem *) lfirst(l);
1309 1310 1311 1312

		if (compute_common_attribute(defel,
									 &volatility_item,
									 &strict_item,
1313
									 &security_def_item,
1314
									 &set_items,
1315 1316
									 &cost_item,
									 &rows_item) == false)
1317 1318 1319 1320 1321 1322 1323 1324 1325
			elog(ERROR, "option \"%s\" not recognized", defel->defname);
	}

	if (volatility_item)
		procForm->provolatile = interpret_func_volatility(volatility_item);
	if (strict_item)
		procForm->proisstrict = intVal(strict_item->arg);
	if (security_def_item)
		procForm->prosecdef = intVal(security_def_item->arg);
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
	if (cost_item)
	{
		procForm->procost = defGetNumeric(cost_item);
		if (procForm->procost <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("COST must be positive")));
	}
	if (rows_item)
	{
		procForm->prorows = defGetNumeric(rows_item);
		if (procForm->prorows <= 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("ROWS must be positive")));
		if (!procForm->proretset)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("ROWS is not applicable when function does not return a set")));
	}
1346 1347 1348 1349 1350 1351
	if (set_items)
	{
		Datum		datum;
		bool		isnull;
		ArrayType  *a;
		Datum		repl_val[Natts_pg_proc];
1352 1353
		bool		repl_null[Natts_pg_proc];
		bool		repl_repl[Natts_pg_proc];
1354 1355 1356 1357 1358 1359 1360 1361 1362

		/* extract existing proconfig setting */
		datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proconfig, &isnull);
		a = isnull ? NULL : DatumGetArrayTypeP(datum);

		/* update according to each SET or RESET item, left to right */
		a = update_proconfig_value(a, set_items);

		/* update the tuple */
1363 1364
		memset(repl_repl, false, sizeof(repl_repl));
		repl_repl[Anum_pg_proc_proconfig - 1] = true;
1365 1366 1367 1368

		if (a == NULL)
		{
			repl_val[Anum_pg_proc_proconfig - 1] = (Datum) 0;
1369
			repl_null[Anum_pg_proc_proconfig - 1] = true;
1370 1371 1372 1373
		}
		else
		{
			repl_val[Anum_pg_proc_proconfig - 1] = PointerGetDatum(a);
1374
			repl_null[Anum_pg_proc_proconfig - 1] = false;
1375 1376
		}

1377
		tup = heap_modify_tuple(tup, RelationGetDescr(rel),
1378 1379
							   repl_val, repl_null, repl_repl);
	}
1380 1381 1382 1383 1384 1385 1386 1387

	/* Do the update */
	simple_heap_update(rel, &tup->t_self, tup);
	CatalogUpdateIndexes(rel, tup);

	heap_close(rel, NoLock);
	heap_freetuple(tup);
}
1388

1389 1390 1391 1392 1393
/*
 * SetFunctionReturnType - change declared return type of a function
 *
 * This is presently only used for adjusting legacy functions that return
 * OPAQUE to return whatever we find their correct definition should be.
1394
 * The caller should emit a suitable warning explaining what we did.
1395 1396 1397 1398 1399 1400 1401 1402
 */
void
SetFunctionReturnType(Oid funcOid, Oid newRetType)
{
	Relation	pg_proc_rel;
	HeapTuple	tup;
	Form_pg_proc procForm;

1403
	pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1404 1405 1406 1407 1408

	tup = SearchSysCacheCopy(PROCOID,
							 ObjectIdGetDatum(funcOid),
							 0, 0, 0);
	if (!HeapTupleIsValid(tup)) /* should not happen */
1409
		elog(ERROR, "cache lookup failed for function %u", funcOid);
1410 1411
	procForm = (Form_pg_proc) GETSTRUCT(tup);

Bruce Momjian's avatar
Bruce Momjian committed
1412
	if (procForm->prorettype != OPAQUEOID)		/* caller messed up */
1413
		elog(ERROR, "function %u doesn't return OPAQUE", funcOid);
1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438

	/* okay to overwrite copied tuple */
	procForm->prorettype = newRetType;

	/* update the catalog and its indexes */
	simple_heap_update(pg_proc_rel, &tup->t_self, tup);

	CatalogUpdateIndexes(pg_proc_rel, tup);

	heap_close(pg_proc_rel, RowExclusiveLock);
}


/*
 * SetFunctionArgType - change declared argument type of a function
 *
 * As above, but change an argument's type.
 */
void
SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType)
{
	Relation	pg_proc_rel;
	HeapTuple	tup;
	Form_pg_proc procForm;

1439
	pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1440 1441 1442 1443 1444

	tup = SearchSysCacheCopy(PROCOID,
							 ObjectIdGetDatum(funcOid),
							 0, 0, 0);
	if (!HeapTupleIsValid(tup)) /* should not happen */
1445
		elog(ERROR, "cache lookup failed for function %u", funcOid);
1446 1447 1448
	procForm = (Form_pg_proc) GETSTRUCT(tup);

	if (argIndex < 0 || argIndex >= procForm->pronargs ||
1449
		procForm->proargtypes.values[argIndex] != OPAQUEOID)
1450
		elog(ERROR, "function %u doesn't take OPAQUE", funcOid);
1451 1452

	/* okay to overwrite copied tuple */
1453
	procForm->proargtypes.values[argIndex] = newArgType;
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463

	/* update the catalog and its indexes */
	simple_heap_update(pg_proc_rel, &tup->t_self, tup);

	CatalogUpdateIndexes(pg_proc_rel, tup);

	heap_close(pg_proc_rel, RowExclusiveLock);
}


1464 1465 1466 1467 1468 1469 1470 1471 1472 1473

/*
 * CREATE CAST
 */
void
CreateCast(CreateCastStmt *stmt)
{
	Oid			sourcetypeid;
	Oid			targettypeid;
	Oid			funcid;
1474
	int			nargs;
1475
	char		castcontext;
1476
	char		castmethod;
1477
	Relation	relation;
1478 1479
	HeapTuple	tuple;
	Datum		values[Natts_pg_cast];
1480
	bool		nulls[Natts_pg_cast];
1481
	ObjectAddress myself,
Bruce Momjian's avatar
Bruce Momjian committed
1482
				referenced;
1483

1484 1485
	sourcetypeid = typenameTypeId(NULL, stmt->sourcetype, NULL);
	targettypeid = typenameTypeId(NULL, stmt->targettype, NULL);
1486

1487
	/* No pseudo-types allowed */
1488
	if (get_typtype(sourcetypeid) == TYPTYPE_PSEUDO)
1489 1490 1491 1492
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("source data type %s is a pseudo-type",
						TypeNameToString(stmt->sourcetype))));
1493

1494
	if (get_typtype(targettypeid) == TYPTYPE_PSEUDO)
1495 1496 1497 1498
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("target data type %s is a pseudo-type",
						TypeNameToString(stmt->targettype))));
1499

1500
	/* Permission check */
1501 1502
	if (!pg_type_ownercheck(sourcetypeid, GetUserId())
		&& !pg_type_ownercheck(targettypeid, GetUserId()))
1503 1504 1505
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be owner of type %s or type %s",
1506 1507
						format_type_be(sourcetypeid),
						format_type_be(targettypeid))));
1508

1509
	/* Detemine the cast method */
1510
	if (stmt->func != NULL)
1511 1512 1513 1514 1515 1516 1517
		castmethod = COERCION_METHOD_FUNCTION;
	else if(stmt->inout)
		castmethod = COERCION_METHOD_INOUT;
	else
		castmethod = COERCION_METHOD_BINARY;

	if (castmethod == COERCION_METHOD_FUNCTION)
1518
	{
1519 1520
		Form_pg_proc procstruct;

1521 1522
		funcid = LookupFuncNameTypeNames(stmt->func->funcname,
										 stmt->func->funcargs,
1523
										 false);
1524

1525 1526 1527
		tuple = SearchSysCache(PROCOID,
							   ObjectIdGetDatum(funcid),
							   0, 0, 0);
1528
		if (!HeapTupleIsValid(tuple))
1529
			elog(ERROR, "cache lookup failed for function %u", funcid);
1530 1531

		procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1532 1533
		nargs = procstruct->pronargs;
		if (nargs < 1 || nargs > 3)
1534 1535
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1536
				  errmsg("cast function must take one to three arguments")));
1537
		if (!IsBinaryCoercible(sourcetypeid, procstruct->proargtypes.values[0]))
1538 1539
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1540
			errmsg("argument of cast function must match or be binary-coercible from source data type")));
1541
		if (nargs > 1 && procstruct->proargtypes.values[1] != INT4OID)
1542 1543
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1544
			errmsg("second argument of cast function must be type integer")));
1545
		if (nargs > 2 && procstruct->proargtypes.values[2] != BOOLOID)
1546 1547
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1548
			errmsg("third argument of cast function must be type boolean")));
1549
		if (!IsBinaryCoercible(procstruct->prorettype, targettypeid))
1550 1551
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1552
					 errmsg("return data type of cast function must match or be binary-coercible to target data type")));
Bruce Momjian's avatar
Bruce Momjian committed
1553

1554
		/*
1555 1556
		 * Restricting the volatility of a cast function may or may not be a
		 * good idea in the abstract, but it definitely breaks many old
Bruce Momjian's avatar
Bruce Momjian committed
1557
		 * user-defined types.	Disable this check --- tgl 2/1/03
1558 1559
		 */
#ifdef NOT_USED
1560
		if (procstruct->provolatile == PROVOLATILE_VOLATILE)
1561 1562 1563
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("cast function must not be volatile")));
1564
#endif
1565
		if (procstruct->proisagg)
1566 1567
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1568
				 errmsg("cast function must not be an aggregate function")));
1569 1570 1571 1572
		if (procstruct->proiswindow)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("cast function must not be a window function")));
1573
		if (procstruct->proretset)
1574 1575 1576
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("cast function must not return a set")));
1577 1578 1579 1580

		ReleaseSysCache(tuple);
	}
	else
1581 1582 1583 1584 1585 1586
	{
		funcid = InvalidOid;
		nargs = 0;
	}

	if (castmethod == COERCION_METHOD_BINARY)
1587
	{
Bruce Momjian's avatar
Bruce Momjian committed
1588 1589 1590 1591 1592 1593
		int16		typ1len;
		int16		typ2len;
		bool		typ1byval;
		bool		typ2byval;
		char		typ1align;
		char		typ2align;
1594 1595 1596 1597 1598 1599

		/*
		 * Must be superuser to create binary-compatible casts, since
		 * erroneous casts can easily crash the backend.
		 */
		if (!superuser())
1600 1601
			ereport(ERROR,
					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1602
			 errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
1603 1604 1605

		/*
		 * Also, insist that the types match as to size, alignment, and
1606 1607 1608
		 * pass-by-value attributes; this provides at least a crude check that
		 * they have similar representations.  A pair of types that fail this
		 * test should certainly not be equated.
1609 1610 1611 1612 1613 1614
		 */
		get_typlenbyvalalign(sourcetypeid, &typ1len, &typ1byval, &typ1align);
		get_typlenbyvalalign(targettypeid, &typ2len, &typ2byval, &typ2align);
		if (typ1len != typ2len ||
			typ1byval != typ2byval ||
			typ1align != typ2align)
1615 1616
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1617
					 errmsg("source and target data types are not physically compatible")));
1618 1619
	}

1620 1621 1622 1623 1624 1625 1626
	/*
	 * Allow source and target types to be same only for length coercion
	 * functions.  We assume a multi-arg function does length coercion.
	 */
	if (sourcetypeid == targettypeid && nargs < 2)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1627
			  errmsg("source data type and target data type are the same")));
1628

1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641
	/* convert CoercionContext enum to char value for castcontext */
	switch (stmt->context)
	{
		case COERCION_IMPLICIT:
			castcontext = COERCION_CODE_IMPLICIT;
			break;
		case COERCION_ASSIGNMENT:
			castcontext = COERCION_CODE_ASSIGNMENT;
			break;
		case COERCION_EXPLICIT:
			castcontext = COERCION_CODE_EXPLICIT;
			break;
		default:
1642
			elog(ERROR, "unrecognized CoercionContext: %d", stmt->context);
1643 1644 1645 1646
			castcontext = 0;	/* keep compiler quiet */
			break;
	}

1647
	relation = heap_open(CastRelationId, RowExclusiveLock);
1648 1649

	/*
1650 1651 1652
	 * Check for duplicate.  This is just to give a friendly error message,
	 * the unique index would catch it anyway (so no need to sweat about race
	 * conditions).
1653 1654 1655 1656 1657 1658
	 */
	tuple = SearchSysCache(CASTSOURCETARGET,
						   ObjectIdGetDatum(sourcetypeid),
						   ObjectIdGetDatum(targettypeid),
						   0, 0);
	if (HeapTupleIsValid(tuple))
1659 1660
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
1661
				 errmsg("cast from type %s to type %s already exists",
1662 1663
						format_type_be(sourcetypeid),
						format_type_be(targettypeid))));
1664

1665
	/* ready to go */
Bruce Momjian's avatar
Bruce Momjian committed
1666 1667 1668
	values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
	values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
	values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
1669
	values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
1670
	values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
1671

1672
	MemSet(nulls, false, sizeof(nulls));
1673

1674
	tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
1675

1676
	simple_heap_insert(relation, tuple);
1677

1678
	CatalogUpdateIndexes(relation, tuple);
1679

1680
	/* make dependency entries */
1681
	myself.classId = CastRelationId;
1682
	myself.objectId = HeapTupleGetOid(tuple);
1683 1684 1685
	myself.objectSubId = 0;

	/* dependency on source type */
1686
	referenced.classId = TypeRelationId;
1687 1688 1689 1690 1691
	referenced.objectId = sourcetypeid;
	referenced.objectSubId = 0;
	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

	/* dependency on target type */
1692
	referenced.classId = TypeRelationId;
1693 1694 1695 1696 1697 1698 1699
	referenced.objectId = targettypeid;
	referenced.objectSubId = 0;
	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

	/* dependency on function */
	if (OidIsValid(funcid))
	{
1700
		referenced.classId = ProcedureRelationId;
1701 1702 1703 1704 1705 1706
		referenced.objectId = funcid;
		referenced.objectSubId = 0;
		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
	}

	heap_freetuple(tuple);
1707

1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723
	heap_close(relation, RowExclusiveLock);
}



/*
 * DROP CAST
 */
void
DropCast(DropCastStmt *stmt)
{
	Oid			sourcetypeid;
	Oid			targettypeid;
	HeapTuple	tuple;
	ObjectAddress object;

1724
	/* when dropping a cast, the types must exist even if you use IF EXISTS */
1725 1726
	sourcetypeid = typenameTypeId(NULL, stmt->sourcetype, NULL);
	targettypeid = typenameTypeId(NULL, stmt->targettype, NULL);
1727 1728

	tuple = SearchSysCache(CASTSOURCETARGET,
Bruce Momjian's avatar
Bruce Momjian committed
1729 1730 1731
						   ObjectIdGetDatum(sourcetypeid),
						   ObjectIdGetDatum(targettypeid),
						   0, 0);
1732
	if (!HeapTupleIsValid(tuple))
1733
	{
Bruce Momjian's avatar
Bruce Momjian committed
1734
		if (!stmt->missing_ok)
1735 1736 1737
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("cast from type %s to type %s does not exist",
1738 1739
							format_type_be(sourcetypeid),
							format_type_be(targettypeid))));
1740 1741
		else
			ereport(NOTICE,
Bruce Momjian's avatar
Bruce Momjian committed
1742
			 (errmsg("cast from type %s to type %s does not exist, skipping",
1743 1744
					 format_type_be(sourcetypeid),
					 format_type_be(targettypeid))));
1745 1746 1747 1748

		return;
	}

1749
	/* Permission check */
1750 1751
	if (!pg_type_ownercheck(sourcetypeid, GetUserId())
		&& !pg_type_ownercheck(targettypeid, GetUserId()))
1752 1753 1754
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be owner of type %s or type %s",
1755 1756
						format_type_be(sourcetypeid),
						format_type_be(targettypeid))));
1757 1758 1759 1760

	/*
	 * Do the deletion
	 */
1761
	object.classId = CastRelationId;
1762
	object.objectId = HeapTupleGetOid(tuple);
1763 1764
	object.objectSubId = 0;

1765 1766
	ReleaseSysCache(tuple);

1767 1768 1769 1770 1771 1772 1773
	performDeletion(&object, stmt->behavior);
}


void
DropCastById(Oid castOid)
{
1774
	Relation	relation;
1775
	ScanKeyData scankey;
1776
	SysScanDesc scan;
1777 1778
	HeapTuple	tuple;

1779
	relation = heap_open(CastRelationId, RowExclusiveLock);
1780

1781 1782 1783 1784
	ScanKeyInit(&scankey,
				ObjectIdAttributeNumber,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(castOid));
1785
	scan = systable_beginscan(relation, CastOidIndexId, true,
1786 1787 1788
							  SnapshotNow, 1, &scankey);

	tuple = systable_getnext(scan);
1789
	if (!HeapTupleIsValid(tuple))
1790
		elog(ERROR, "could not find tuple for cast %u", castOid);
1791
	simple_heap_delete(relation, &tuple->t_self);
1792

1793
	systable_endscan(scan);
1794 1795
	heap_close(relation, RowExclusiveLock);
}
1796 1797

/*
Tom Lane's avatar
Tom Lane committed
1798 1799 1800
 * Execute ALTER FUNCTION/AGGREGATE SET SCHEMA
 *
 * These commands are identical except for the lookup procedure, so share code.
1801 1802
 */
void
Tom Lane's avatar
Tom Lane committed
1803 1804
AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
					   const char *newschema)
1805
{
1806 1807 1808 1809 1810 1811
	Oid			procOid;
	Oid			oldNspOid;
	Oid			nspOid;
	HeapTuple	tup;
	Relation	procRel;
	Form_pg_proc proc;
1812 1813 1814 1815

	procRel = heap_open(ProcedureRelationId, RowExclusiveLock);

	/* get function OID */
Tom Lane's avatar
Tom Lane committed
1816 1817 1818 1819
	if (isagg)
		procOid = LookupAggNameTypeNames(name, argtypes, false);
	else
		procOid = LookupFuncNameTypeNames(name, argtypes, false);
1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848

	/* check permissions on function */
	if (!pg_proc_ownercheck(procOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
					   NameListToString(name));

	tup = SearchSysCacheCopy(PROCOID,
							 ObjectIdGetDatum(procOid),
							 0, 0, 0);
	if (!HeapTupleIsValid(tup))
		elog(ERROR, "cache lookup failed for function %u", procOid);
	proc = (Form_pg_proc) GETSTRUCT(tup);

	oldNspOid = proc->pronamespace;

	/* get schema OID and check its permissions */
	nspOid = LookupCreationNamespace(newschema);

	if (oldNspOid == nspOid)
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_FUNCTION),
				 errmsg("function \"%s\" is already in schema \"%s\"",
						NameListToString(name),
						newschema)));

	/* disallow renaming into or out of temp schemas */
	if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1849
			errmsg("cannot move objects into or out of temporary schemas")));
1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886

	/* same for TOAST schema */
	if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot move objects into or out of TOAST schema")));

	/* check for duplicate name (more friendly than unique-index failure) */
	if (SearchSysCacheExists(PROCNAMEARGSNSP,
							 CStringGetDatum(NameStr(proc->proname)),
							 PointerGetDatum(&proc->proargtypes),
							 ObjectIdGetDatum(nspOid),
							 0))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_FUNCTION),
				 errmsg("function \"%s\" already exists in schema \"%s\"",
						NameStr(proc->proname),
						newschema)));

	/* OK, modify the pg_proc row */

	/* tup is a copy, so we can scribble directly on it */
	proc->pronamespace = nspOid;

	simple_heap_update(procRel, &tup->t_self, tup);
	CatalogUpdateIndexes(procRel, tup);

	/* Update dependency on schema */
	if (changeDependencyFor(ProcedureRelationId, procOid,
							NamespaceRelationId, oldNspOid, nspOid) != 1)
		elog(ERROR, "failed to change schema dependency for function \"%s\"",
			 NameListToString(name));

	heap_freetuple(tup);

	heap_close(procRel, RowExclusiveLock);
}