comment.c 22.4 KB
Newer Older
Bruce Momjian's avatar
Bruce Momjian committed
1 2 3
/*-------------------------------------------------------------------------
 *
 * comment.c
4
 *
Bruce Momjian's avatar
Bruce Momjian committed
5 6
 * PostgreSQL object comments utility code.
 *
7
 * Copyright (c) 1999-2001, PostgreSQL Global Development Group
Bruce Momjian's avatar
Bruce Momjian committed
8
 *
9
 * IDENTIFICATION
10
 *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.30 2001/06/13 21:44:40 tgl Exp $
11
 *
Bruce Momjian's avatar
Bruce Momjian committed
12 13 14 15 16
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

17
#include "utils/builtins.h"
Bruce Momjian's avatar
Bruce Momjian committed
18 19 20 21 22
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_database.h"
#include "catalog/pg_description.h"
Bruce Momjian's avatar
Bruce Momjian committed
23
#include "catalog/pg_operator.h"
Bruce Momjian's avatar
Bruce Momjian committed
24 25
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
26
#include "catalog/pg_class.h"
Bruce Momjian's avatar
Bruce Momjian committed
27 28
#include "commands/comment.h"
#include "miscadmin.h"
29
#include "parser/parse.h"
30 31
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
Bruce Momjian's avatar
Bruce Momjian committed
32 33
#include "rewrite/rewriteRemove.h"
#include "utils/acl.h"
34
#include "utils/fmgroids.h"
Bruce Momjian's avatar
Bruce Momjian committed
35 36 37 38 39
#include "utils/syscache.h"


/*------------------------------------------------------------------
 * Static Function Prototypes --
40 41 42 43
 *
 * The following protoypes are declared static so as not to conflict
 * with any other routines outside this module. These routines are
 * called by the public function CommentObject() routine to create
Bruce Momjian's avatar
Bruce Momjian committed
44 45 46 47 48 49 50 51 52
 * the appropriate comment for the specific object type.
 *------------------------------------------------------------------
 */

static void CommentRelation(int objtype, char *relation, char *comment);
static void CommentAttribute(char *relation, char *attrib, char *comment);
static void CommentDatabase(char *database, char *comment);
static void CommentRewrite(char *rule, char *comment);
static void CommentType(char *type, char *comment);
53
static void CommentAggregate(char *aggregate, List *arguments, char *comment);
Bruce Momjian's avatar
Bruce Momjian committed
54 55 56
static void CommentProc(char *function, List *arguments, char *comment);
static void CommentOperator(char *opname, List *arguments, char *comment);
static void CommentTrigger(char *trigger, char *relation, char *comments);
57
static void CreateComments(Oid oid, char *comment);
Bruce Momjian's avatar
Bruce Momjian committed
58 59 60 61

/*------------------------------------------------------------------
 * CommentObject --
 *
62 63 64 65
 * This routine is used to add the associated comment into
 * pg_description for the object specified by the paramters handed
 * to this routine. If the routine cannot determine an Oid to
 * associated with the parameters handed to this routine, an
Bruce Momjian's avatar
Bruce Momjian committed
66 67 68 69 70 71 72
 * error is thrown. Otherwise the comment is added to pg_description
 * by calling the CreateComments() routine. If the comments were
 * empty, CreateComments() will drop any comments associated with
 * the object.
 *------------------------------------------------------------------
*/

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
void
CommentObject(int objtype, char *objname, char *objproperty,
			  List *objlist, char *comment)
{

	switch (objtype)
	{
			case (INDEX):
			case (SEQUENCE):
			case (TABLE):
			case (VIEW):
			CommentRelation(objtype, objname, comment);
			break;
		case (COLUMN):
			CommentAttribute(objname, objproperty, comment);
			break;
		case (DATABASE):
			CommentDatabase(objname, comment);
			break;
		case (RULE):
			CommentRewrite(objname, comment);
			break;
		case (TYPE_P):
			CommentType(objname, comment);
			break;
		case (AGGREGATE):
99
			CommentAggregate(objname, objlist, comment);
100 101 102 103 104 105 106 107 108 109 110 111 112 113
			break;
		case (FUNCTION):
			CommentProc(objname, objlist, comment);
			break;
		case (OPERATOR):
			CommentOperator(objname, objlist, comment);
			break;
		case (TRIGGER):
			CommentTrigger(objname, objproperty, comment);
			break;
		default:
			elog(ERROR, "An attempt was made to comment on a unknown type: %i",
				 objtype);
	}
Bruce Momjian's avatar
Bruce Momjian committed
114

115

Bruce Momjian's avatar
Bruce Momjian committed
116 117 118 119
}

/*------------------------------------------------------------------
 * CreateComments --
120
 *
Bruce Momjian's avatar
Bruce Momjian committed
121
 * This routine is handed the oid and the command associated
122
 * with that id and will insert, update, or delete (if the
Bruce Momjian's avatar
Bruce Momjian committed
123
 * comment is an empty string or a NULL pointer) the associated
124
 * comment from the system cataloge, pg_description.
Bruce Momjian's avatar
Bruce Momjian committed
125 126 127 128
 *
 *------------------------------------------------------------------
 */

129
static void
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
CreateComments(Oid oid, char *comment)
{

	Relation	description;
	TupleDesc	tupDesc;
	HeapScanDesc scan;
	ScanKeyData entry;
	HeapTuple	desctuple = NULL,
				searchtuple;
	Datum		values[Natts_pg_description];
	char		nulls[Natts_pg_description];
	char		replaces[Natts_pg_description];
	bool		modified = false;
	int			i;

	/*** Open pg_description, form a new tuple, if necessary ***/

	description = heap_openr(DescriptionRelationName, RowExclusiveLock);
	tupDesc = description->rd_att;
	if ((comment != NULL) && (strlen(comment) > 0))
	{
		for (i = 0; i < Natts_pg_description; i++)
		{
			nulls[i] = ' ';
			replaces[i] = 'r';
			values[i] = (Datum) NULL;
		}
		i = 0;
		values[i++] = ObjectIdGetDatum(oid);
159
		values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
160 161 162 163 164 165 166 167 168 169 170 171 172 173
	}

	/*** Now, open pg_description and attempt to find the old tuple ***/

	ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
						   ObjectIdGetDatum(oid));
	scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
	searchtuple = heap_getnext(scan, 0);

	/*** If a previous tuple exists, either delete or prep replacement ***/

	if (HeapTupleIsValid(searchtuple))
	{

174
		/*** If the comment is blank, delete old entry, else update it ***/
175 176

		if ((comment == NULL) || (strlen(comment) == 0))
177
			simple_heap_delete(description, &searchtuple->t_self);
178 179 180 181
		else
		{
			desctuple = heap_modifytuple(searchtuple, description, values,
										 nulls, replaces);
182
			simple_heap_update(description, &searchtuple->t_self, desctuple);
183 184 185
			modified = TRUE;
		}

Bruce Momjian's avatar
Bruce Momjian committed
186
	}
187 188 189 190 191 192 193 194 195 196 197
	else
	{

		/*** Only if comment is non-blank do we form a new tuple ***/

		if ((comment != NULL) && (strlen(comment) > 0))
		{
			desctuple = heap_formtuple(tupDesc, values, nulls);
			heap_insert(description, desctuple);
			modified = TRUE;
		}
Bruce Momjian's avatar
Bruce Momjian committed
198

199
	}
Bruce Momjian's avatar
Bruce Momjian committed
200

201
	/*** Complete the scan, update indices, if necessary ***/
Bruce Momjian's avatar
Bruce Momjian committed
202

203
	heap_endscan(scan);
204

205 206 207 208 209
	if (modified)
	{
		if (RelationGetForm(description)->relhasindex)
		{
			Relation	idescs[Num_pg_description_indices];
210

211 212 213 214 215 216 217
			CatalogOpenIndices(Num_pg_description_indices,
							   Name_pg_description_indices, idescs);
			CatalogIndexInsert(idescs, Num_pg_description_indices, description,
							   desctuple);
			CatalogCloseIndices(Num_pg_description_indices, idescs);
		}
		heap_freetuple(desctuple);
Bruce Momjian's avatar
Bruce Momjian committed
218

219
	}
Bruce Momjian's avatar
Bruce Momjian committed
220

221
	heap_close(description, RowExclusiveLock);
Bruce Momjian's avatar
Bruce Momjian committed
222 223 224

}

225
/*------------------------------------------------------------------
Bruce Momjian's avatar
Bruce Momjian committed
226 227
 * DeleteComments --
 *
228
 * This routine is used to purge any comments
Bruce Momjian's avatar
Bruce Momjian committed
229 230 231 232 233 234
 * associated with the Oid handed to this routine,
 * regardless of the actual object type. It is
 * called, for example, when a relation is destroyed.
 *------------------------------------------------------------------
 */

235 236 237
void
DeleteComments(Oid oid)
{
Bruce Momjian's avatar
Bruce Momjian committed
238

239 240 241 242 243
	Relation	description;
	TupleDesc	tupDesc;
	ScanKeyData entry;
	HeapScanDesc scan;
	HeapTuple	searchtuple;
Bruce Momjian's avatar
Bruce Momjian committed
244

245 246
	description = heap_openr(DescriptionRelationName, RowExclusiveLock);
	tupDesc = description->rd_att;
Bruce Momjian's avatar
Bruce Momjian committed
247

248
	/*** Now, open pg_description and attempt to find the old tuple ***/
249

250 251 252 253
	ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
						   ObjectIdGetDatum(oid));
	scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
	searchtuple = heap_getnext(scan, 0);
Bruce Momjian's avatar
Bruce Momjian committed
254

255
	/*** If a previous tuple exists, delete it ***/
Bruce Momjian's avatar
Bruce Momjian committed
256

257
	if (HeapTupleIsValid(searchtuple))
258
		simple_heap_delete(description, &searchtuple->t_self);
259

260
	/*** Complete the scan, update indices, if necessary ***/
Bruce Momjian's avatar
Bruce Momjian committed
261

262 263
	heap_endscan(scan);
	heap_close(description, RowExclusiveLock);
264

Bruce Momjian's avatar
Bruce Momjian committed
265
}
266

Bruce Momjian's avatar
Bruce Momjian committed
267 268 269
/*------------------------------------------------------------------
 * CommentRelation --
 *
270
 * This routine is used to add/drop a comment from a relation, where
Bruce Momjian's avatar
Bruce Momjian committed
271
 * a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
272 273
 * finds the relation name by searching the system cache, locating
 * the appropriate tuple, and inserting a comment using that
Bruce Momjian's avatar
Bruce Momjian committed
274 275 276 277
 * tuple's oid. Its parameters are the relation name and comments.
 *------------------------------------------------------------------
*/

278 279 280 281 282 283 284 285 286
static void
CommentRelation(int reltype, char *relname, char *comment)
{
	HeapTuple	reltuple;
	Oid			oid;
	char		relkind;

	/*** First, check object security ***/

287
	if (!pg_ownercheck(GetUserId(), relname, RELNAME))
288 289 290 291
		elog(ERROR, "you are not permitted to comment on class '%s'", relname);

	/*** Now, attempt to find the oid in the cached version of pg_class ***/

292 293 294
	reltuple = SearchSysCache(RELNAME,
							  PointerGetDatum(relname),
							  0, 0, 0);
295 296 297 298 299 300 301
	if (!HeapTupleIsValid(reltuple))
		elog(ERROR, "relation '%s' does not exist", relname);

	oid = reltuple->t_data->t_oid;

	relkind = ((Form_pg_class) GETSTRUCT(reltuple))->relkind;

302 303 304 305
	ReleaseSysCache(reltuple);

	/*** Next, verify that the relation type matches the intent ***/

306 307 308
	switch (reltype)
	{
		case (INDEX):
309
			if (relkind != RELKIND_INDEX)
310 311 312
				elog(ERROR, "relation '%s' is not an index", relname);
			break;
		case (TABLE):
313
			if (relkind != RELKIND_RELATION)
314 315 316
				elog(ERROR, "relation '%s' is not a table", relname);
			break;
		case (VIEW):
317
			if (relkind != RELKIND_VIEW)
318 319 320
				elog(ERROR, "relation '%s' is not a view", relname);
			break;
		case (SEQUENCE):
321
			if (relkind != RELKIND_SEQUENCE)
322 323 324 325 326 327 328
				elog(ERROR, "relation '%s' is not a sequence", relname);
			break;
	}

	/*** Create the comments using the tuple's oid ***/

	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
329 330 331 332 333 334
}

/*------------------------------------------------------------------
 * CommentAttribute --
 *
 * This routine is used to add/drop a comment from an attribute
335
 * such as a table's column. The routine will check security
Bruce Momjian's avatar
Bruce Momjian committed
336 337
 * restrictions and then attempt to fetch the oid of the associated
 * attribute. If successful, a comment is added/dropped, else an
338
 * elog() exception is thrown.	The parameters are the relation
339
 * and attribute names, and the comments
Bruce Momjian's avatar
Bruce Momjian committed
340 341 342
 *------------------------------------------------------------------
*/

343 344 345 346 347
static void
CommentAttribute(char *relname, char *attrname, char *comment)
{
	Relation	relation;
	Oid			oid;
Bruce Momjian's avatar
Bruce Momjian committed
348

349
	/*** First, check object security ***/
Bruce Momjian's avatar
Bruce Momjian committed
350

351
	if (!pg_ownercheck(GetUserId(), relname, RELNAME))
352
		elog(ERROR, "you are not permitted to comment on class '%s\'", relname);
353

354
	/* Open the containing relation to ensure it won't go away meanwhile */
Bruce Momjian's avatar
Bruce Momjian committed
355

356
	relation = heap_openr(relname, AccessShareLock);
357 358 359 360 361 362 363 364

	/*** Now, fetch the attribute oid from the system cache ***/

	oid = GetSysCacheOid(ATTNAME,
						 ObjectIdGetDatum(relation->rd_id),
						 PointerGetDatum(attrname),
						 0, 0);
	if (!OidIsValid(oid))
365 366
		elog(ERROR, "'%s' is not an attribute of class '%s'",
			 attrname, relname);
367

368
	/*** Call CreateComments() to create/drop the comments ***/
Bruce Momjian's avatar
Bruce Momjian committed
369

370
	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
371

372
	/*** Now, close the heap relation and return ***/
Bruce Momjian's avatar
Bruce Momjian committed
373

374
	heap_close(relation, NoLock);
Bruce Momjian's avatar
Bruce Momjian committed
375 376 377 378 379 380
}

/*------------------------------------------------------------------
 * CommentDatabase --
 *
 * This routine is used to add/drop any user-comments a user might
381 382 383 384
 * have regarding the specified database. The routine will check
 * security for owner permissions, and, if succesful, will then
 * attempt to find the oid of the database specified. Once found,
 * a comment is added/dropped using the CreateComments() routine.
Bruce Momjian's avatar
Bruce Momjian committed
385 386 387
 *------------------------------------------------------------------
*/

388 389 390 391 392 393
static void
CommentDatabase(char *database, char *comment)
{
	Relation	pg_database;
	ScanKeyData entry;
	HeapScanDesc scan;
394
	HeapTuple	dbtuple;
395
	Oid			oid;
Bruce Momjian's avatar
Bruce Momjian committed
396

397
	/*** First find the tuple in pg_database for the database ***/
Bruce Momjian's avatar
Bruce Momjian committed
398

399 400 401 402 403
	pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
	ScanKeyEntryInitialize(&entry, 0, Anum_pg_database_datname,
						   F_NAMEEQ, NameGetDatum(database));
	scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, &entry);
	dbtuple = heap_getnext(scan, 0);
404

405
	/*** Validate database exists, and fetch the db oid ***/
Bruce Momjian's avatar
Bruce Momjian committed
406

407 408 409
	if (!HeapTupleIsValid(dbtuple))
		elog(ERROR, "database '%s' does not exist", database);
	oid = dbtuple->t_data->t_oid;
410

411
	/*** Allow if the user matches the database dba or is a superuser ***/
412

413
	if (!(superuser() || is_dbadmin(oid)))
414 415
		elog(ERROR, "you are not permitted to comment on database '%s'",
			 database);
Bruce Momjian's avatar
Bruce Momjian committed
416

417
	/*** Create the comments with the pg_database oid ***/
418

419
	CreateComments(oid, comment);
420

421
	/*** Complete the scan and close any opened relations ***/
422

423 424
	heap_endscan(scan);
	heap_close(pg_database, AccessShareLock);
Bruce Momjian's avatar
Bruce Momjian committed
425 426 427 428 429 430 431
}

/*------------------------------------------------------------------
 * CommentRewrite --
 *
 * This routine is used to add/drop any user-comments a user might
 * have regarding a specified RULE. The rule is specified by name
432
 * and, if found, and the user has appropriate permissions, a
Bruce Momjian's avatar
Bruce Momjian committed
433 434 435 436
 * comment will be added/dropped using the CreateComments() routine.
 *------------------------------------------------------------------
*/

437 438 439 440
static void
CommentRewrite(char *rule, char *comment)
{
	Oid			oid;
441
	char	   *relation;
442
	int			aclcheck;
Bruce Momjian's avatar
Bruce Momjian committed
443

444
	/*** First, validate user ***/
445

446 447
#ifndef NO_SECURITY
	relation = RewriteGetRuleEventRel(rule);
448
	aclcheck = pg_aclcheck(relation, GetUserId(), ACL_RULE);
449 450 451 452 453 454
	if (aclcheck != ACLCHECK_OK)
	{
		elog(ERROR, "you are not permitted to comment on rule '%s'",
			 rule);
	}
#endif
Bruce Momjian's avatar
Bruce Momjian committed
455

456
	/*** Next, find the rule's oid ***/
457

458 459 460 461
	oid = GetSysCacheOid(RULENAME,
						 PointerGetDatum(rule),
						 0, 0, 0);
	if (!OidIsValid(oid))
462
		elog(ERROR, "rule '%s' does not exist", rule);
Bruce Momjian's avatar
Bruce Momjian committed
463

464
	/*** Call CreateComments() to create/drop the comments ***/
Bruce Momjian's avatar
Bruce Momjian committed
465

466
	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
467 468 469 470 471 472 473
}

/*------------------------------------------------------------------
 * CommentType --
 *
 * This routine is used to add/drop any user-comments a user might
 * have regarding a TYPE. The type is specified by name
474
 * and, if found, and the user has appropriate permissions, a
Bruce Momjian's avatar
Bruce Momjian committed
475 476 477 478 479
 * comment will be added/dropped using the CreateComments() routine.
 * The type's name and the comments are the paramters to this routine.
 *------------------------------------------------------------------
*/

480 481 482 483
static void
CommentType(char *type, char *comment)
{
	Oid			oid;
Bruce Momjian's avatar
Bruce Momjian committed
484

485
	/*** First, validate user ***/
486

487
	if (!pg_ownercheck(GetUserId(), type, TYPENAME))
488 489
		elog(ERROR, "you are not permitted to comment on type '%s'",
			 type);
Bruce Momjian's avatar
Bruce Momjian committed
490

491
	/*** Next, find the type's oid ***/
492

493 494 495 496
	oid = GetSysCacheOid(TYPENAME,
						 PointerGetDatum(type),
						 0, 0, 0);
	if (!OidIsValid(oid))
497
		elog(ERROR, "type '%s' does not exist", type);
Bruce Momjian's avatar
Bruce Momjian committed
498

499
	/*** Call CreateComments() to create/drop the comments ***/
Bruce Momjian's avatar
Bruce Momjian committed
500

501
	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
502 503 504 505 506
}

/*------------------------------------------------------------------
 * CommentAggregate --
 *
507
 * This routine is used to allow a user to provide comments on an
Bruce Momjian's avatar
Bruce Momjian committed
508
 * aggregate function. The aggregate function is determined by both
509
 * its name and its argument type, which, with the comments are
Bruce Momjian's avatar
Bruce Momjian committed
510 511 512 513
 * the three parameters handed to this routine.
 *------------------------------------------------------------------
*/

514
static void
515
CommentAggregate(char *aggregate, List *arguments, char *comment)
516
{
517 518
	TypeName   *aggtype = (TypeName *) lfirst(arguments);
	char	   *aggtypename = NULL;
519 520 521 522 523 524
	Oid			baseoid,
				oid;
	bool		defined;

	/*** First, attempt to determine the base aggregate oid ***/

525
	if (aggtype)
526
	{
527 528
		aggtypename = TypeNameToInternalName(aggtype);
		baseoid = TypeGet(aggtypename, &defined);
529
		if (!OidIsValid(baseoid))
530
			elog(ERROR, "type '%s' does not exist", aggtypename);
531 532 533 534 535 536
	}
	else
		baseoid = 0;

	/*** Next, validate the user's attempt to comment ***/

537
	if (!pg_aggr_ownercheck(GetUserId(), aggregate, baseoid))
538
	{
539
		if (aggtypename)
540
			elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
541
				 aggregate, "with type", aggtypename);
542 543 544 545 546 547 548
		else
			elog(ERROR, "you are not permitted to comment on aggregate '%s'",
				 aggregate);
	}

	/*** Now, attempt to find the actual tuple in pg_aggregate ***/

549 550 551 552 553
	oid = GetSysCacheOid(AGGNAME,
						 PointerGetDatum(aggregate),
						 ObjectIdGetDatum(baseoid),
						 0, 0);
	if (!OidIsValid(oid))
554
	{
555
		if (aggtypename)
556 557
		{
			elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
558
				 aggtypename, aggregate);
559 560 561 562 563 564 565 566
		}
		else
			elog(ERROR, "aggregate '%s' does not exist", aggregate);
	}

	/*** Call CreateComments() to create/drop the comments ***/

	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
567 568 569 570 571
}

/*------------------------------------------------------------------
 * CommentProc --
 *
572
 * This routine is used to allow a user to provide comments on an
Bruce Momjian's avatar
Bruce Momjian committed
573
 * procedure (function). The procedure is determined by both
574
 * its name and its argument list. The argument list is expected to
Bruce Momjian's avatar
Bruce Momjian committed
575 576 577 578 579
 * be a series of parsed nodes pointed to by a List object. If the
 * comments string is empty, the associated comment is dropped.
 *------------------------------------------------------------------
*/

580 581
static void
CommentProc(char *function, List *arguments, char *comment)
582
{
583 584 585 586
	Oid			oid,
				argoids[FUNC_MAX_ARGS];
	int			i,
				argcount;
587 588 589 590 591 592 593 594

	/*** First, initialize function's argument list with their type oids ***/

	MemSet(argoids, 0, FUNC_MAX_ARGS * sizeof(Oid));
	argcount = length(arguments);
	if (argcount > FUNC_MAX_ARGS)
		elog(ERROR, "functions cannot have more than %d arguments",
			 FUNC_MAX_ARGS);
595 596
	for (i = 0; i < argcount; i++)
	{
597 598 599
		TypeName   *t = (TypeName *) lfirst(arguments);
		char	   *typnam = TypeNameToInternalName(t);

600
		arguments = lnext(arguments);
601 602 603

		if (strcmp(typnam, "opaque") == 0)
			argoids[i] = InvalidOid;
604 605
		else
		{
606 607 608 609
			argoids[i] = GetSysCacheOid(TYPENAME,
										PointerGetDatum(typnam),
										0, 0, 0);
			if (!OidIsValid(argoids[i]))
610
				elog(ERROR, "CommentProc: type '%s' not found", typnam);
611
		}
612
	}
Bruce Momjian's avatar
Bruce Momjian committed
613

614
	/*** Now, validate the user's ability to comment on this function ***/
Bruce Momjian's avatar
Bruce Momjian committed
615

616
	if (!pg_func_ownercheck(GetUserId(), function, argcount, argoids))
617 618
		elog(ERROR, "you are not permitted to comment on function '%s'",
			 function);
619

620
	/*** Now, find the corresponding oid for this procedure ***/
621

622 623 624 625 626 627
	oid = GetSysCacheOid(PROCNAME,
						 PointerGetDatum(function),
						 Int32GetDatum(argcount),
						 PointerGetDatum(argoids),
						 0);
	if (!OidIsValid(oid))
628 629
		func_error("CommentProc", function, argcount, argoids, NULL);

630
	/*** Call CreateComments() to create/drop the comments ***/
Bruce Momjian's avatar
Bruce Momjian committed
631

632
	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
633
}
634

Bruce Momjian's avatar
Bruce Momjian committed
635 636 637
/*------------------------------------------------------------------
 * CommentOperator --
 *
638
 * This routine is used to allow a user to provide comments on an
Bruce Momjian's avatar
Bruce Momjian committed
639 640
 * operator. The operator for commenting is determined by both
 * its name and its argument list which defines the left and right
641 642 643
 * hand types the operator will operate on. The argument list is
 * expected to be a couple of parse nodes pointed to be a List
 * object. If the comments string is empty, the associated comment
Bruce Momjian's avatar
Bruce Momjian committed
644 645 646 647
 * is dropped.
 *------------------------------------------------------------------
*/

648 649 650
static void
CommentOperator(char *opername, List *arguments, char *comment)
{
651 652 653 654 655
	TypeName   *typenode1 = (TypeName *) lfirst(arguments);
	TypeName   *typenode2 = (TypeName *) lsecond(arguments);
	char		oprtype = 0,
			   *lefttype = NULL,
			   *righttype = NULL;
656 657 658 659 660 661 662 663 664
	Form_pg_operator data;
	HeapTuple	optuple;
	Oid			oid,
				leftoid = InvalidOid,
				rightoid = InvalidOid;
	bool		defined;

	/*** Initialize our left and right argument types ***/

665 666 667 668
	if (typenode1 != NULL)
		lefttype = TypeNameToInternalName(typenode1);
	if (typenode2 != NULL)
		righttype = TypeNameToInternalName(typenode2);
669 670 671 672 673 674 675 676 677

	/*** Attempt to fetch the left oid, if specified ***/

	if (lefttype != NULL)
	{
		leftoid = TypeGet(lefttype, &defined);
		if (!OidIsValid(leftoid))
			elog(ERROR, "left type '%s' does not exist", lefttype);
	}
Bruce Momjian's avatar
Bruce Momjian committed
678

679
	/*** Attempt to fetch the right oid, if specified ***/
680

681 682 683 684 685 686
	if (righttype != NULL)
	{
		rightoid = TypeGet(righttype, &defined);
		if (!OidIsValid(rightoid))
			elog(ERROR, "right type '%s' does not exist", righttype);
	}
687

688
	/*** Determine operator type ***/
Bruce Momjian's avatar
Bruce Momjian committed
689

690 691 692 693
	if (OidIsValid(leftoid) && (OidIsValid(rightoid)))
		oprtype = 'b';
	else if (OidIsValid(leftoid))
		oprtype = 'r';
694 695
	else if (OidIsValid(rightoid))
		oprtype = 'l';
696 697
	else
		elog(ERROR, "operator '%s' is of an illegal type'", opername);
Bruce Momjian's avatar
Bruce Momjian committed
698

699
	/*** Attempt to fetch the operator oid ***/
700

701 702 703 704 705
	optuple = SearchSysCache(OPERNAME,
							 PointerGetDatum(opername),
							 ObjectIdGetDatum(leftoid),
							 ObjectIdGetDatum(rightoid),
							 CharGetDatum(oprtype));
706 707
	if (!HeapTupleIsValid(optuple))
		elog(ERROR, "operator '%s' does not exist", opername);
Bruce Momjian's avatar
Bruce Momjian committed
708

709
	oid = optuple->t_data->t_oid;
Bruce Momjian's avatar
Bruce Momjian committed
710

711
	/*** Valid user's ability to comment on this operator ***/
712

713
	if (!pg_oper_ownercheck(GetUserId(), oid))
714 715
		elog(ERROR, "you are not permitted to comment on operator '%s'",
			 opername);
Bruce Momjian's avatar
Bruce Momjian committed
716

717
	/*** Get the procedure associated with the operator ***/
Bruce Momjian's avatar
Bruce Momjian committed
718

719
	data = (Form_pg_operator) GETSTRUCT(optuple);
720
	oid = RegprocToOid(data->oprcode);
721 722
	if (oid == InvalidOid)
		elog(ERROR, "operator '%s' does not have an underlying function", opername);
Bruce Momjian's avatar
Bruce Momjian committed
723

724 725
	ReleaseSysCache(optuple);

726
	/*** Call CreateComments() to create/drop the comments ***/
Bruce Momjian's avatar
Bruce Momjian committed
727

728
	CreateComments(oid, comment);
Bruce Momjian's avatar
Bruce Momjian committed
729 730 731 732 733 734 735 736 737 738 739 740 741
}

/*------------------------------------------------------------------
 * CommentTrigger --
 *
 * This routine is used to allow a user to provide comments on a
 * trigger event. The trigger for commenting is determined by both
 * its name and the relation to which it refers. The arguments to this
 * function are the trigger name, the relation name, and the comments
 * to add/drop.
 *------------------------------------------------------------------
*/

742 743 744 745 746 747 748 749 750 751 752 753 754 755
static void
CommentTrigger(char *trigger, char *relname, char *comment)
{

	Form_pg_trigger data;
	Relation	pg_trigger,
				relation;
	HeapTuple	triggertuple;
	HeapScanDesc scan;
	ScanKeyData entry;
	Oid			oid = InvalidOid;

	/*** First, validate the user's action ***/

756
	if (!pg_ownercheck(GetUserId(), relname, RELNAME))
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
		elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
			 trigger, "defined for relation", relname);

	/*** Now, fetch the trigger oid from pg_trigger  ***/

	relation = heap_openr(relname, AccessShareLock);
	pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
	ScanKeyEntryInitialize(&entry, 0, Anum_pg_trigger_tgrelid,
						   F_OIDEQ, RelationGetRelid(relation));
	scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 1, &entry);
	triggertuple = heap_getnext(scan, 0);
	while (HeapTupleIsValid(triggertuple))
	{
		data = (Form_pg_trigger) GETSTRUCT(triggertuple);
		if (namestrcmp(&(data->tgname), trigger) == 0)
		{
			oid = triggertuple->t_data->t_oid;
			break;
		}
		triggertuple = heap_getnext(scan, 0);
	}

	/*** If no trigger exists for the relation specified, notify user ***/

	if (oid == InvalidOid)
	{
		elog(ERROR, "trigger '%s' defined for relation '%s' does not exist",
			 trigger, relname);
	}

	/*** Create the comments with the pg_trigger oid ***/

	CreateComments(oid, comment);

	/*** Complete the scan and close any opened relations ***/

	heap_endscan(scan);
	heap_close(pg_trigger, AccessShareLock);
795
	heap_close(relation, NoLock);
Bruce Momjian's avatar
Bruce Momjian committed
796
}