pg_proc.c 14.6 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * pg_proc.c
4
 *	  routines to support manipulation of the pg_proc relation
5
 *
6
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
Bruce Momjian's avatar
Add:  
Bruce Momjian committed
7
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
Tom Lane's avatar
Tom Lane committed
11
 *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.75 2002/05/22 18:33:15 tgl Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
Bruce Momjian's avatar
Bruce Momjian committed
15 16 17 18 19
#include "postgres.h"

#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
20
#include "catalog/pg_language.h"
Bruce Momjian's avatar
Bruce Momjian committed
21
#include "catalog/pg_proc.h"
22
#include "executor/executor.h"
23
#include "fmgr.h"
Bruce Momjian's avatar
Bruce Momjian committed
24
#include "miscadmin.h"
25
#include "parser/parse_coerce.h"
26
#include "parser/parse_expr.h"
Bruce Momjian's avatar
Bruce Momjian committed
27
#include "parser/parse_type.h"
Bruce Momjian's avatar
Bruce Momjian committed
28 29 30 31 32 33
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/sets.h"
#include "utils/syscache.h"

34

35
static void checkretval(Oid rettype, List *queryTreeList);
36 37 38
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
39 40


41
/* ----------------------------------------------------------------
42
 *		ProcedureCreate
43 44 45
 * ----------------------------------------------------------------
 */
Oid
46
ProcedureCreate(const char *procedureName,
47
				Oid procNamespace,
48
				bool replace,
49
				bool returnsSet,
50
				Oid returnType,
51
				Oid languageObjectId,
52
				Oid languageValidator,
53 54
				const char *prosrc,
				const char *probin,
55
				bool isAgg,
56
				bool security_definer,
57
				bool isImplicit,
58
				bool isStrict,
59
				char volatility,
60 61 62 63
				int32 byte_pct,
				int32 perbyte_cpu,
				int32 percall_cpu,
				int32 outin_ratio,
64 65
				int parameterCount,
				const Oid *parameterTypes)
66
{
67
	int			i;
68
	Relation	rel;
69
	HeapTuple	tup;
70
	HeapTuple	oldtup;
71 72
	char		nulls[Natts_pg_proc];
	Datum		values[Natts_pg_proc];
73
	char		replaces[Natts_pg_proc];
74
	Oid			typev[FUNC_MAX_ARGS];
75
	Oid			relid;
76
	NameData	procname;
77
	TupleDesc	tupDesc;
78
	Oid			retval;
79

80 81
	/*
	 * sanity checks
82
	 */
83 84 85
	Assert(PointerIsValid(prosrc));
	Assert(PointerIsValid(probin));

86 87 88
	if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
		elog(ERROR, "functions cannot have more than %d arguments",
			 FUNC_MAX_ARGS);
89

90 91 92 93
	/* Make sure we have a zero-padded param type array */
	MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
	if (parameterCount > 0)
		memcpy(typev, parameterTypes, parameterCount * sizeof(Oid));
94

95
	if (languageObjectId == SQLlanguageId)
96 97 98 99 100 101 102
	{
		/*
		 * If this call is defining a set, check if the set is already
		 * defined by looking to see whether this call's function text
		 * matches a function already in pg_proc.  If so just return the
		 * OID of the existing set.
		 */
103
		if (strcmp(procedureName, GENERICSETNAME) == 0)
104
		{
105
#ifdef SETS_FIXED
106 107 108 109 110 111 112 113

			/*
			 * The code below doesn't work any more because the PROSRC
			 * system cache and the pg_proc_prosrc_index have been
			 * removed. Instead a sequential heap scan or something better
			 * must get implemented. The reason for removing is that
			 * nbtree index crashes if sources exceed 2K --- what's likely
			 * for procedural languages.
114 115 116 117 118
			 *
			 * 1999/09/30 Jan
			 */
			text	   *prosrctext;

119
			prosrctext = DatumGetTextP(DirectFunctionCall1(textin,
120
											   CStringGetDatum(prosrc)));
121 122 123
			retval = GetSysCacheOid(PROSRC,
									PointerGetDatum(prosrctext),
									0, 0, 0);
Bruce Momjian's avatar
Bruce Momjian committed
124
			pfree(prosrctext);
125 126
			if (OidIsValid(retval))
				return retval;
127 128
#else
			elog(ERROR, "lookup for procedure by source needs fix (Jan)");
129
#endif   /* SETS_FIXED */
130
		}
131
	}
132 133 134 135 136

	/*
	 * don't allow functions of complex types that have the same name as
	 * existing attributes of the type
	 */
137 138
	if (parameterCount == 1 && OidIsValid(typev[0]) &&
		(relid = typeidTypeRelid(typev[0])) != 0 &&
139
		get_attnum(relid, (char *) procedureName) != InvalidAttrNumber)
Bruce Momjian's avatar
Bruce Momjian committed
140
		elog(ERROR, "method %s already an attribute of type %s",
141
			 procedureName, format_type_be(typev[0]));
142

143
	/*
144
	 * All seems OK; prepare the data to be inserted into pg_proc.
145 146
	 */

147
	for (i = 0; i < Natts_pg_proc; ++i)
148
	{
149 150
		nulls[i] = ' ';
		values[i] = (Datum) NULL;
151
		replaces[i] = 'r';
152 153
	}

154
	i = 0;
155
	namestrcpy(&procname, procedureName);
156 157 158 159
	values[i++] = NameGetDatum(&procname);		/* proname */
	values[i++] = ObjectIdGetDatum(procNamespace); /* pronamespace */
	values[i++] = Int32GetDatum(GetUserId());	/* proowner */
	values[i++] = ObjectIdGetDatum(languageObjectId); /* prolang */
160
	values[i++] = BoolGetDatum(isAgg);			/* proisagg */
161
	values[i++] = BoolGetDatum(security_definer); /* prosecdef */
162
	values[i++] = BoolGetDatum(isImplicit);		/* proimplicit */
163
	values[i++] = BoolGetDatum(isStrict);		/* proisstrict */
164
	values[i++] = BoolGetDatum(returnsSet);		/* proretset */
165 166 167 168
	values[i++] = CharGetDatum(volatility);		/* provolatile */
	values[i++] = UInt16GetDatum(parameterCount); /* pronargs */
	values[i++] = ObjectIdGetDatum(returnType);	/* prorettype */
	values[i++] = PointerGetDatum(typev);		/* proargtypes */
169 170 171 172
	values[i++] = Int32GetDatum(byte_pct);		/* probyte_pct */
	values[i++] = Int32GetDatum(perbyte_cpu);	/* properbyte_cpu */
	values[i++] = Int32GetDatum(percall_cpu);	/* propercall_cpu */
	values[i++] = Int32GetDatum(outin_ratio);	/* prooutin_ratio */
173 174 175 176
	values[i++] = DirectFunctionCall1(textin,	/* prosrc */
									  CStringGetDatum(prosrc));
	values[i++] = DirectFunctionCall1(textin,	/* probin */
									  CStringGetDatum(probin));
177 178
	/* proacl will be handled below */

179
	rel = heap_openr(ProcedureRelationName, RowExclusiveLock);
180
	tupDesc = rel->rd_att;
181

182
	/* Check for pre-existing definition */
183
	oldtup = SearchSysCache(PROCNAMENSP,
184 185 186
							PointerGetDatum(procedureName),
							UInt16GetDatum(parameterCount),
							PointerGetDatum(typev),
187
							ObjectIdGetDatum(procNamespace));
188

189 190 191
	if (HeapTupleIsValid(oldtup))
	{
		/* There is one; okay to replace it? */
192
		Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
193 194 195 196 197 198 199

		if (!replace)
			elog(ERROR, "function %s already exists with same argument types",
				 procedureName);
		if (GetUserId() != oldproc->proowner && !superuser())
			elog(ERROR, "ProcedureCreate: you do not have permission to replace function %s",
				 procedureName);
200

201 202 203 204
		/*
		 * Not okay to change the return type of the existing proc, since
		 * existing rules, views, etc may depend on the return type.
		 */
205
		if (returnType != oldproc->prorettype ||
206 207 208 209
			returnsSet != oldproc->proretset)
			elog(ERROR, "ProcedureCreate: cannot change return type of existing function."
				 "\n\tUse DROP FUNCTION first.");

210 211 212 213 214 215 216 217 218 219 220
		/* Can't change aggregate status, either */
		if (oldproc->proisagg != isAgg)
		{
			if (oldproc->proisagg)
				elog(ERROR, "function %s is an aggregate",
					 procedureName);
			else
				elog(ERROR, "function %s is not an aggregate",
					 procedureName);
		}

Tom Lane's avatar
Tom Lane committed
221 222
		/* do not change existing ownership or permissions, either */
		replaces[Anum_pg_proc_proowner-1] = ' ';
223 224
		replaces[Anum_pg_proc_proacl-1] = ' ';

225
		/* Okay, do it... */
226 227
		tup = heap_modifytuple(oldtup, rel, values, nulls, replaces);
		simple_heap_update(rel, &tup->t_self, tup);
228 229 230 231 232 233

		ReleaseSysCache(oldtup);
	}
	else
	{
		/* Creating a new procedure */
234 235 236 237

		/* start out with empty permissions */
		nulls[Anum_pg_proc_proacl-1] = 'n';

238
		tup = heap_formtuple(tupDesc, values, nulls);
239
		simple_heap_insert(rel, tup);
240 241 242
	}

	/* Need to update indices for either the insert or update case */
243
	if (RelationGetForm(rel)->relhasindex)
244
	{
245
		Relation	idescs[Num_pg_proc_indices];
246 247

		CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs);
248
		CatalogIndexInsert(idescs, Num_pg_proc_indices, rel, tup);
249 250
		CatalogCloseIndices(Num_pg_proc_indices, idescs);
	}
251

252 253
	retval = tup->t_data->t_oid;
	heap_freetuple(tup);
254 255 256

	heap_close(rel, RowExclusiveLock);

257 258 259
	/* Verify function body */
	if (OidIsValid(languageValidator))
	{
Tom Lane's avatar
Tom Lane committed
260
		/* Advance command counter so new tuple can be seen by validator */
261
		CommandCounterIncrement();
Tom Lane's avatar
Tom Lane committed
262
		OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
263 264
	}

265
	return retval;
266
}
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

/*
 * checkretval() -- check return value of a list of sql parse trees.
 *
 * The return value of a sql function is the value returned by
 * the final query in the function.  We do some ad-hoc define-time
 * type checking here to be sure that the user is returning the
 * type he claims.
 */
static void
checkretval(Oid rettype, List *queryTreeList)
{
	Query	   *parse;
	int			cmd;
	List	   *tlist;
	List	   *tlistitem;
	int			tlistlen;
284
	Oid			typerelid;
285
	Oid			restype;
286 287 288 289 290
	Relation	reln;
	Oid			relid;
	int			relnatts;
	int			i;

291 292 293 294 295
	/* guard against empty function body; OK only if no return type */
	if (queryTreeList == NIL)
	{
		if (rettype != InvalidOid)
			elog(ERROR, "function declared to return %s, but no SELECT provided",
296
				 format_type_be(rettype));
297 298 299
		return;
	}

300 301 302 303 304 305 306
	/* find the final query */
	parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);

	cmd = parse->commandType;
	tlist = parse->targetList;

	/*
307 308
	 * The last query must be a SELECT if and only if there is a return
	 * type.
309 310 311 312
	 */
	if (rettype == InvalidOid)
	{
		if (cmd == CMD_SELECT)
313
			elog(ERROR, "function declared with no return type, but final statement is a SELECT");
314 315 316 317 318
		return;
	}

	/* by here, the function is declared to return some type */
	if (cmd != CMD_SELECT)
319 320
		elog(ERROR, "function declared to return %s, but final statement is not a SELECT",
			 format_type_be(rettype));
321 322 323 324 325 326 327

	/*
	 * Count the non-junk entries in the result targetlist.
	 */
	tlistlen = ExecCleanTargetListLength(tlist);

	/*
328
	 * For base-type returns, the target list should have exactly one
329 330
	 * entry, and its type should agree with what the user declared. (As
	 * of Postgres 7.2, we accept binary-compatible types too.)
331
	 */
332 333
	typerelid = typeidTypeRelid(rettype);
	if (typerelid == InvalidOid)
334 335
	{
		if (tlistlen != 1)
336
			elog(ERROR, "function declared to return %s returns multiple columns in final SELECT",
337
				 format_type_be(rettype));
338

339
		restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
Tom Lane's avatar
Tom Lane committed
340
		if (!IsBinaryCompatible(restype, rettype))
341
			elog(ERROR, "return type mismatch in function: declared to return %s, returns %s",
342
				 format_type_be(rettype), format_type_be(restype));
343 344 345 346 347 348

		return;
	}

	/*
	 * If the target list is of length 1, and the type of the varnode in
349
	 * the target list matches the declared return type, this is okay.
350 351 352
	 * This can happen, for example, where the body of the function is
	 * 'SELECT func2()', where func2 has the same return type as the
	 * function that's calling it.
353 354 355
	 */
	if (tlistlen == 1)
	{
356
		restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
Tom Lane's avatar
Tom Lane committed
357
		if (IsBinaryCompatible(restype, rettype))
358 359 360 361
			return;
	}

	/*
362 363 364 365
	 * By here, the procedure returns a tuple or set of tuples.  This part
	 * of the typechecking is a hack. We look up the relation that is the
	 * declared return type, and be sure that attributes 1 .. n in the
	 * target list match the declared types.
366
	 */
367
	reln = heap_open(typerelid, AccessShareLock);
368 369 370 371
	relid = reln->rd_id;
	relnatts = reln->rd_rel->relnatts;

	if (tlistlen != relnatts)
372
		elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
373
			 format_type_be(rettype), relnatts);
374 375 376 377 378 379 380

	/* expect attributes 1 .. n in order */
	i = 0;
	foreach(tlistitem, tlist)
	{
		TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
		Oid			tletype;
381
		Oid			atttype;
382 383 384 385

		if (tle->resdom->resjunk)
			continue;
		tletype = exprType(tle->expr);
386
		atttype = reln->rd_att->attrs[i]->atttypid;
Tom Lane's avatar
Tom Lane committed
387
		if (!IsBinaryCompatible(tletype, atttype))
388
			elog(ERROR, "function declared to return %s returns %s instead of %s at column %d",
389 390
				 format_type_be(rettype),
				 format_type_be(tletype),
391
				 format_type_be(atttype),
392
				 i + 1);
393 394 395 396 397
		i++;
	}

	/* this shouldn't happen, but let's just check... */
	if (i != relnatts)
398
		elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)",
399
			 format_type_be(rettype), relnatts);
400 401 402

	heap_close(reln, AccessShareLock);
}
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521



/*
 * Validator for internal functions
 *
 * Check that the given internal function name (the "prosrc" value) is
 * a known builtin function.
 */
Datum
fmgr_internal_validator(PG_FUNCTION_ARGS)
{
	Oid			funcoid = PG_GETARG_OID(0);
	HeapTuple	tuple;
	Form_pg_proc proc;
	bool		isnull;
	Datum		tmp;
	char	   *prosrc;

	tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup of function %u failed", funcoid);
	proc = (Form_pg_proc) GETSTRUCT(tuple);

	tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
	if (isnull)
		elog(ERROR, "null prosrc");
	prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));

	if (fmgr_internal_function(prosrc) == InvalidOid)
		elog(ERROR, "there is no built-in function named \"%s\"", prosrc);

	ReleaseSysCache(tuple);
	PG_RETURN_BOOL(true);
}



/*
 * Validator for C language functions
 *
 * Make sure that the library file exists, is loadable, and contains
 * the specified link symbol. Also check for a valid function
 * information record.
 */
Datum
fmgr_c_validator(PG_FUNCTION_ARGS)
{
	Oid			funcoid = PG_GETARG_OID(0);
	void	   *libraryhandle;
	HeapTuple	tuple;
	Form_pg_proc proc;
	bool		isnull;
	Datum		tmp;
	char	   *prosrc;
	char	   *probin;

	tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup of function %u failed", funcoid);
	proc = (Form_pg_proc) GETSTRUCT(tuple);

	tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
	if (isnull)
		elog(ERROR, "null prosrc");
	prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));

	tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
	if (isnull)
		elog(ERROR, "null probin");
	probin = DatumGetCString(DirectFunctionCall1(textout, tmp));
	
	(void) load_external_function(probin, prosrc, true, &libraryhandle);
	(void) fetch_finfo_record(libraryhandle, prosrc);

	ReleaseSysCache(tuple);
	PG_RETURN_BOOL(true);
}



/*
 * Validator for SQL language functions
 *
 * Parse it here in order to be sure that it contains no syntax
 * errors.
 */
Datum
fmgr_sql_validator(PG_FUNCTION_ARGS)
{
	Oid			funcoid = PG_GETARG_OID(0);
	HeapTuple	tuple;
	Form_pg_proc proc;
	List	   *querytree_list;
	bool		isnull;
	Datum		tmp;
	char	   *prosrc;

	tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup of function %u failed", funcoid);

	proc = (Form_pg_proc) GETSTRUCT(tuple);

	if (!OidIsValid(proc->prorettype))
			elog(ERROR, "SQL functions cannot return type \"opaque\"");

	tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
	if (isnull)
		elog(ERROR, "null prosrc");

	prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));

	querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
	checkretval(proc->prorettype, querytree_list);

	ReleaseSysCache(tuple);
	PG_RETURN_BOOL(true);
}