OSDN Git Service

pg_type has a typnamespace column; system now supports creating types
[pg-rex/syncrep.git] / src / backend / commands / comment.c
1 /*-------------------------------------------------------------------------
2  *
3  * comment.c
4  *
5  * PostgreSQL object comments utility code.
6  *
7  * Copyright (c) 1999-2001, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.38 2002/03/29 19:06:04 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 #include "postgres.h"
16
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "catalog/catname.h"
20 #include "catalog/indexing.h"
21 #include "catalog/pg_database.h"
22 #include "catalog/pg_description.h"
23 #include "catalog/pg_namespace.h"
24 #include "catalog/pg_operator.h"
25 #include "catalog/pg_rewrite.h"
26 #include "catalog/pg_trigger.h"
27 #include "catalog/pg_type.h"
28 #include "commands/comment.h"
29 #include "miscadmin.h"
30 #include "nodes/makefuncs.h"
31 #include "parser/parse_agg.h"
32 #include "parser/parse_func.h"
33 #include "parser/parse_type.h"
34 #include "parser/parse.h"
35 #include "rewrite/rewriteRemove.h"
36 #include "utils/acl.h"
37 #include "utils/builtins.h"
38 #include "utils/fmgroids.h"
39 #include "utils/lsyscache.h"
40 #include "utils/syscache.h"
41
42
43 /*------------------------------------------------------------------
44  * Static Function Prototypes --
45  *
46  * The following protoypes are declared static so as not to conflict
47  * with any other routines outside this module. These routines are
48  * called by the public function CommentObject() routine to create
49  * the appropriate comment for the specific object type.
50  *------------------------------------------------------------------
51  */
52
53 static void CommentRelation(int objtype, char * schemaname, char *relation,
54                                                         char *comment);
55 static void CommentAttribute(char * schemaname, char *relation,
56                                                          char *attrib, char *comment);
57 static void CommentDatabase(char *database, char *comment);
58 static void CommentRewrite(char *rule, char *comment);
59 static void CommentType(char *type, char *comment);
60 static void CommentAggregate(char *aggregate, List *arguments, char *comment);
61 static void CommentProc(char *function, List *arguments, char *comment);
62 static void CommentOperator(char *opname, List *arguments, char *comment);
63 static void CommentTrigger(char *trigger, char *schemaname, char *relation,
64                                                    char *comments);
65
66
67 /*------------------------------------------------------------------
68  * CommentObject --
69  *
70  * This routine is used to add the associated comment into
71  * pg_description for the object specified by the paramters handed
72  * to this routine. If the routine cannot determine an Oid to
73  * associated with the parameters handed to this routine, an
74  * error is thrown. Otherwise the comment is added to pg_description
75  * by calling the CreateComments() routine. If the comment string is
76  * empty, CreateComments() will drop any comments associated with
77  * the object.
78  *------------------------------------------------------------------
79 */
80
81 void
82 CommentObject(int objtype, char *schemaname, char *objname, char *objproperty,
83                           List *objlist, char *comment)
84 {
85         switch (objtype)
86         {
87                 case INDEX:
88                 case SEQUENCE:
89                 case TABLE:
90                 case VIEW:
91                         CommentRelation(objtype, schemaname, objname, comment);
92                         break;
93                 case COLUMN:
94                         CommentAttribute(schemaname, objname, objproperty, comment);
95                         break;
96                 case DATABASE:
97                         CommentDatabase(objname, comment);
98                         break;
99                 case RULE:
100                         CommentRewrite(objname, comment);
101                         break;
102                 case TYPE_P:
103                         CommentType(objname, comment);
104                         break;
105                 case AGGREGATE:
106                         CommentAggregate(objname, objlist, comment);
107                         break;
108                 case FUNCTION:
109                         CommentProc(objname, objlist, comment);
110                         break;
111                 case OPERATOR:
112                         CommentOperator(objname, objlist, comment);
113                         break;
114                 case TRIGGER:
115                         CommentTrigger(objname, schemaname, objproperty, comment);
116                         break;
117                 default:
118                         elog(ERROR, "An attempt was made to comment on a unknown type: %d",
119                                  objtype);
120         }
121 }
122
123 /*------------------------------------------------------------------
124  * CreateComments --
125  *
126  * Create a comment for the specified object descriptor.  Inserts a new
127  * pg_description tuple, or replaces an existing one with the same key.
128  *
129  * If the comment given is null or an empty string, instead delete any
130  * existing comment for the specified key.
131  *------------------------------------------------------------------
132  */
133
134 void
135 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
136 {
137         Relation        description;
138         Relation        descriptionindex;
139         ScanKeyData skey[3];
140         IndexScanDesc sd;
141         RetrieveIndexResult indexRes;
142         HeapTupleData oldtuple;
143         Buffer          buffer;
144         HeapTuple       newtuple = NULL;
145         Datum           values[Natts_pg_description];
146         char            nulls[Natts_pg_description];
147         char            replaces[Natts_pg_description];
148         int                     i;
149
150         /* Reduce empty-string to NULL case */
151         if (comment != NULL && strlen(comment) == 0)
152                 comment = NULL;
153
154         /* Prepare to form or update a tuple, if necessary */
155         if (comment != NULL)
156         {
157                 for (i = 0; i < Natts_pg_description; i++)
158                 {
159                         nulls[i] = ' ';
160                         replaces[i] = 'r';
161                 }
162                 i = 0;
163                 values[i++] = ObjectIdGetDatum(oid);
164                 values[i++] = ObjectIdGetDatum(classoid);
165                 values[i++] = Int32GetDatum(subid);
166                 values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
167         }
168
169         /* Open pg_description and its index */
170
171         description = heap_openr(DescriptionRelationName, RowExclusiveLock);
172         descriptionindex = index_openr(DescriptionObjIndex);
173
174         /* Use the index to search for a matching old tuple */
175
176         ScanKeyEntryInitialize(&skey[0],
177                                                    (bits16) 0x0,
178                                                    (AttrNumber) 1,
179                                                    (RegProcedure) F_OIDEQ,
180                                                    ObjectIdGetDatum(oid));
181
182         ScanKeyEntryInitialize(&skey[1],
183                                                    (bits16) 0x0,
184                                                    (AttrNumber) 2,
185                                                    (RegProcedure) F_OIDEQ,
186                                                    ObjectIdGetDatum(classoid));
187
188         ScanKeyEntryInitialize(&skey[2],
189                                                    (bits16) 0x0,
190                                                    (AttrNumber) 3,
191                                                    (RegProcedure) F_INT4EQ,
192                                                    Int32GetDatum(subid));
193
194         sd = index_beginscan(descriptionindex, false, 3, skey);
195
196         oldtuple.t_datamcxt = CurrentMemoryContext;
197         oldtuple.t_data = NULL;
198
199         while ((indexRes = index_getnext(sd, ForwardScanDirection)))
200         {
201                 oldtuple.t_self = indexRes->heap_iptr;
202                 heap_fetch(description, SnapshotNow, &oldtuple, &buffer, sd);
203                 pfree(indexRes);
204
205                 if (oldtuple.t_data == NULL)
206                         continue;                       /* time qual failed */
207
208                 /* Found the old tuple, so delete or update it */
209
210                 if (comment == NULL)
211                         simple_heap_delete(description, &oldtuple.t_self);
212                 else
213                 {
214                         newtuple = heap_modifytuple(&oldtuple, description, values,
215                                                                                 nulls, replaces);
216                         simple_heap_update(description, &oldtuple.t_self, newtuple);
217                 }
218
219                 ReleaseBuffer(buffer);
220                 break;                                  /* Assume there can be only one match */
221         }
222
223         index_endscan(sd);
224
225         /* If we didn't find an old tuple, insert a new one */
226
227         if (oldtuple.t_data == NULL && comment != NULL)
228         {
229                 newtuple = heap_formtuple(RelationGetDescr(description),
230                                                                   values, nulls);
231                 heap_insert(description, newtuple);
232         }
233
234         /* Update indexes, if necessary */
235
236         if (newtuple != NULL)
237         {
238                 if (RelationGetForm(description)->relhasindex)
239                 {
240                         Relation        idescs[Num_pg_description_indices];
241
242                         CatalogOpenIndices(Num_pg_description_indices,
243                                                            Name_pg_description_indices, idescs);
244                         CatalogIndexInsert(idescs, Num_pg_description_indices, description,
245                                                            newtuple);
246                         CatalogCloseIndices(Num_pg_description_indices, idescs);
247                 }
248                 heap_freetuple(newtuple);
249         }
250
251         /* Done */
252
253         index_close(descriptionindex);
254         heap_close(description, NoLock);
255 }
256
257 /*------------------------------------------------------------------
258  * DeleteComments --
259  *
260  * This routine is used to purge all comments associated with an object,
261  * regardless of their objsubid.  It is called, for example, when a relation
262  * is destroyed.
263  *------------------------------------------------------------------
264  */
265
266 void
267 DeleteComments(Oid oid, Oid classoid)
268 {
269         Relation        description;
270         Relation        descriptionindex;
271         ScanKeyData skey[2];
272         IndexScanDesc sd;
273         RetrieveIndexResult indexRes;
274         HeapTupleData oldtuple;
275         Buffer          buffer;
276
277         /* Open pg_description and its index */
278
279         description = heap_openr(DescriptionRelationName, RowExclusiveLock);
280         descriptionindex = index_openr(DescriptionObjIndex);
281
282         /* Use the index to search for all matching old tuples */
283
284         ScanKeyEntryInitialize(&skey[0],
285                                                    (bits16) 0x0,
286                                                    (AttrNumber) 1,
287                                                    (RegProcedure) F_OIDEQ,
288                                                    ObjectIdGetDatum(oid));
289
290         ScanKeyEntryInitialize(&skey[1],
291                                                    (bits16) 0x0,
292                                                    (AttrNumber) 2,
293                                                    (RegProcedure) F_OIDEQ,
294                                                    ObjectIdGetDatum(classoid));
295
296         sd = index_beginscan(descriptionindex, false, 2, skey);
297
298         while ((indexRes = index_getnext(sd, ForwardScanDirection)))
299         {
300                 oldtuple.t_self = indexRes->heap_iptr;
301                 heap_fetch(description, SnapshotNow, &oldtuple, &buffer, sd);
302                 pfree(indexRes);
303
304                 if (oldtuple.t_data == NULL)
305                         continue;                       /* time qual failed */
306
307                 simple_heap_delete(description, &oldtuple.t_self);
308
309                 ReleaseBuffer(buffer);
310         }
311
312         /* Done */
313
314         index_endscan(sd);
315         index_close(descriptionindex);
316         heap_close(description, NoLock);
317 }
318
319 /*------------------------------------------------------------------
320  * CommentRelation --
321  *
322  * This routine is used to add/drop a comment from a relation, where
323  * a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
324  * finds the relation name by searching the system cache, locating
325  * the appropriate tuple, and inserting a comment using that
326  * tuple's oid. Its parameters are the relation name and comments.
327  *------------------------------------------------------------------
328  */
329
330 static void
331 CommentRelation(int reltype, char *schemaname, char *relname, char *comment)
332 {
333         Relation        relation;
334         RangeVar   *tgtrel = makeNode(RangeVar);
335         
336         
337         tgtrel->relname = relname;
338         tgtrel->schemaname = schemaname;
339         /* FIXME SCHEMA: Can we add comments to temp relations? */
340         tgtrel->istemp = false;
341
342         /*
343          * Open the relation.  We do this mainly to acquire a lock that
344          * ensures no one else drops the relation before we commit.  (If they
345          * did, they'd fail to remove the entry we are about to make in
346          * pg_description.)
347          */
348         relation = relation_openrv(tgtrel, AccessShareLock);
349
350         /* Check object security */
351         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
352                 elog(ERROR, "you are not permitted to comment on class '%s'", relname);
353
354         /* Next, verify that the relation type matches the intent */
355
356         switch (reltype)
357         {
358                 case INDEX:
359                         if (relation->rd_rel->relkind != RELKIND_INDEX)
360                                 elog(ERROR, "relation '%s' is not an index", relname);
361                         break;
362                 case TABLE:
363                         if (relation->rd_rel->relkind != RELKIND_RELATION)
364                                 elog(ERROR, "relation '%s' is not a table", relname);
365                         break;
366                 case VIEW:
367                         if (relation->rd_rel->relkind != RELKIND_VIEW)
368                                 elog(ERROR, "relation '%s' is not a view", relname);
369                         break;
370                 case SEQUENCE:
371                         if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
372                                 elog(ERROR, "relation '%s' is not a sequence", relname);
373                         break;
374         }
375
376         /* Create the comment using the relation's oid */
377
378         CreateComments(RelationGetRelid(relation), RelOid_pg_class, 0, comment);
379
380         /* Done, but hold lock until commit */
381         relation_close(relation, NoLock);
382 }
383
384 /*------------------------------------------------------------------
385  * CommentAttribute --
386  *
387  * This routine is used to add/drop a comment from an attribute
388  * such as a table's column. The routine will check security
389  * restrictions and then attempt to look up the specified
390  * attribute. If successful, a comment is added/dropped, else an
391  * elog() exception is thrown.  The parameters are the relation
392  * and attribute names, and the comments
393  *------------------------------------------------------------------
394 */
395
396 static void
397 CommentAttribute(char *schemaname, char *relname, char *attrname, char *comment)
398 {
399         RangeVar   *rel = makeNode(RangeVar);
400         Relation        relation;
401         AttrNumber      attnum;
402
403         /* Open the containing relation to ensure it won't go away meanwhile */
404
405         rel->relname = relname;
406         rel->schemaname = schemaname;
407         rel->istemp = false;
408         relation = heap_openrv(rel, AccessShareLock);
409
410         /* Check object security */
411
412         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
413                 elog(ERROR, "you are not permitted to comment on class '%s'", relname);
414
415         /* Now, fetch the attribute number from the system cache */
416
417         attnum = get_attnum(RelationGetRelid(relation), attrname);
418         if (attnum == InvalidAttrNumber)
419                 elog(ERROR, "'%s' is not an attribute of class '%s'",
420                          attrname, relname);
421
422         /* Create the comment using the relation's oid */
423
424         CreateComments(RelationGetRelid(relation), RelOid_pg_class,
425                                    (int32) attnum, comment);
426
427         /* Done, but hold lock until commit */
428
429         heap_close(relation, NoLock);
430 }
431
432 /*------------------------------------------------------------------
433  * CommentDatabase --
434  *
435  * This routine is used to add/drop any user-comments a user might
436  * have regarding the specified database. The routine will check
437  * security for owner permissions, and, if succesful, will then
438  * attempt to find the oid of the database specified. Once found,
439  * a comment is added/dropped using the CreateComments() routine.
440  *------------------------------------------------------------------
441 */
442
443 static void
444 CommentDatabase(char *database, char *comment)
445 {
446         Relation        pg_database;
447         ScanKeyData entry;
448         HeapScanDesc scan;
449         HeapTuple       dbtuple;
450         Oid                     oid;
451
452         /* First find the tuple in pg_database for the database */
453
454         pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
455         ScanKeyEntryInitialize(&entry, 0, Anum_pg_database_datname,
456                                                    F_NAMEEQ, NameGetDatum(database));
457         scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, &entry);
458         dbtuple = heap_getnext(scan, 0);
459
460         /* Validate database exists, and fetch the db oid */
461
462         if (!HeapTupleIsValid(dbtuple))
463                 elog(ERROR, "database '%s' does not exist", database);
464         oid = dbtuple->t_data->t_oid;
465
466         /* Allow if the user matches the database dba or is a superuser */
467
468         if (!(superuser() || is_dbadmin(oid)))
469                 elog(ERROR, "you are not permitted to comment on database '%s'",
470                          database);
471
472         /* Create the comments with the pg_database oid */
473
474         CreateComments(oid, RelOid_pg_database, 0, comment);
475
476         /* Complete the scan and close any opened relations */
477
478         heap_endscan(scan);
479         heap_close(pg_database, AccessShareLock);
480 }
481
482 /*------------------------------------------------------------------
483  * CommentRewrite --
484  *
485  * This routine is used to add/drop any user-comments a user might
486  * have regarding a specified RULE. The rule is specified by name
487  * and, if found, and the user has appropriate permissions, a
488  * comment will be added/dropped using the CreateComments() routine.
489  *------------------------------------------------------------------
490 */
491
492 static void
493 CommentRewrite(char *rule, char *comment)
494 {
495         HeapTuple       tuple;
496         Oid                     reloid;
497         Oid                     ruleoid;
498         Oid                     classoid;
499         int32           aclcheck;
500
501         /* Find the rule's pg_rewrite tuple, get its OID and its table's OID */
502
503         tuple = SearchSysCache(RULENAME,
504                                                    PointerGetDatum(rule),
505                                                    0, 0, 0);
506         if (!HeapTupleIsValid(tuple))
507                 elog(ERROR, "rule '%s' does not exist", rule);
508
509         reloid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
510         ruleoid = tuple->t_data->t_oid;
511
512         ReleaseSysCache(tuple);
513
514         /* Check object security */
515
516         aclcheck = pg_class_aclcheck(reloid, GetUserId(), ACL_RULE);
517         if (aclcheck != ACLCHECK_OK)
518                 elog(ERROR, "you are not permitted to comment on rule '%s'",
519                          rule);
520
521         /* pg_rewrite doesn't have a hard-coded OID, so must look it up */
522
523         classoid = get_relname_relid(RewriteRelationName, PG_CATALOG_NAMESPACE);
524         Assert(OidIsValid(classoid));
525
526         /* Call CreateComments() to create/drop the comments */
527
528         CreateComments(ruleoid, classoid, 0, comment);
529 }
530
531 /*------------------------------------------------------------------
532  * CommentType --
533  *
534  * This routine is used to add/drop any user-comments a user might
535  * have regarding a TYPE. The type is specified by name
536  * and, if found, and the user has appropriate permissions, a
537  * comment will be added/dropped using the CreateComments() routine.
538  * The type's name and the comments are the paramters to this routine.
539  *------------------------------------------------------------------
540 */
541
542 static void
543 CommentType(char *type, char *comment)
544 {
545         Oid                     oid;
546
547         /* Find the type's oid */
548
549         /* XXX WRONG: need to deal with qualified type names */
550         oid = typenameTypeId(makeTypeName(type));
551
552         /* Check object security */
553
554         if (!pg_type_ownercheck(oid, GetUserId()))
555                 elog(ERROR, "you are not permitted to comment on type '%s'",
556                          type);
557
558         /* Call CreateComments() to create/drop the comments */
559
560         CreateComments(oid, RelOid_pg_type, 0, comment);
561 }
562
563 /*------------------------------------------------------------------
564  * CommentAggregate --
565  *
566  * This routine is used to allow a user to provide comments on an
567  * aggregate function. The aggregate function is determined by both
568  * its name and its argument type, which, with the comments are
569  * the three parameters handed to this routine.
570  *------------------------------------------------------------------
571 */
572
573 static void
574 CommentAggregate(char *aggregate, List *arguments, char *comment)
575 {
576         TypeName   *aggtype = (TypeName *) lfirst(arguments);
577         Oid                     baseoid,
578                                 oid;
579         Oid                     classoid;
580
581         /* First, attempt to determine the base aggregate oid */
582         if (aggtype)
583                 baseoid = typenameTypeId(aggtype);
584         else
585                 baseoid = InvalidOid;
586
587         /* Now, attempt to find the actual tuple in pg_aggregate */
588
589         oid = GetSysCacheOid(AGGNAME,
590                                                  PointerGetDatum(aggregate),
591                                                  ObjectIdGetDatum(baseoid),
592                                                  0, 0);
593         if (!OidIsValid(oid))
594                 agg_error("CommentAggregate", aggregate, baseoid);
595
596         /* Next, validate the user's attempt to comment */
597
598         if (!pg_aggr_ownercheck(oid, GetUserId()))
599         {
600                 if (baseoid == InvalidOid)
601                         elog(ERROR, "you are not permitted to comment on aggregate '%s' for all types",
602                                  aggregate);
603                 else
604                         elog(ERROR, "you are not permitted to comment on aggregate '%s' for type %s",
605                                  aggregate, format_type_be(baseoid));
606         }
607
608         /* pg_aggregate doesn't have a hard-coded OID, so must look it up */
609
610         classoid = get_relname_relid(AggregateRelationName, PG_CATALOG_NAMESPACE);
611         Assert(OidIsValid(classoid));
612
613         /* Call CreateComments() to create/drop the comments */
614
615         CreateComments(oid, classoid, 0, comment);
616 }
617
618 /*------------------------------------------------------------------
619  * CommentProc --
620  *
621  * This routine is used to allow a user to provide comments on an
622  * procedure (function). The procedure is determined by both
623  * its name and its argument list. The argument list is expected to
624  * be a series of parsed nodes pointed to by a List object. If the
625  * comments string is empty, the associated comment is dropped.
626  *------------------------------------------------------------------
627 */
628
629 static void
630 CommentProc(char *function, List *arguments, char *comment)
631 {
632         Oid                     oid,
633                                 argoids[FUNC_MAX_ARGS];
634         int                     i,
635                                 argcount;
636
637         /* First, initialize function's argument list with their type oids */
638
639         MemSet(argoids, 0, FUNC_MAX_ARGS * sizeof(Oid));
640         argcount = length(arguments);
641         if (argcount > FUNC_MAX_ARGS)
642                 elog(ERROR, "functions cannot have more than %d arguments",
643                          FUNC_MAX_ARGS);
644         for (i = 0; i < argcount; i++)
645         {
646                 TypeName   *t = (TypeName *) lfirst(arguments);
647
648                 argoids[i] = LookupTypeName(t);
649                 if (!OidIsValid(argoids[i]))
650                 {
651                         char      *typnam = TypeNameToString(t);
652
653                         if (strcmp(typnam, "opaque") == 0)
654                                 argoids[i] = InvalidOid;
655                         else
656                                 elog(ERROR, "Type \"%s\" does not exist", typnam);
657                 }
658
659                 arguments = lnext(arguments);
660         }
661
662         /* Now, find the corresponding oid for this procedure */
663
664         oid = GetSysCacheOid(PROCNAME,
665                                                  PointerGetDatum(function),
666                                                  Int32GetDatum(argcount),
667                                                  PointerGetDatum(argoids),
668                                                  0);
669         if (!OidIsValid(oid))
670                 func_error("CommentProc", function, argcount, argoids, NULL);
671
672         /* Now, validate the user's ability to comment on this function */
673
674         if (!pg_proc_ownercheck(oid, GetUserId()))
675                 elog(ERROR, "you are not permitted to comment on function '%s'",
676                          function);
677
678         /* Call CreateComments() to create/drop the comments */
679
680         CreateComments(oid, RelOid_pg_proc, 0, comment);
681 }
682
683 /*------------------------------------------------------------------
684  * CommentOperator --
685  *
686  * This routine is used to allow a user to provide comments on an
687  * operator. The operator for commenting is determined by both
688  * its name and its argument list which defines the left and right
689  * hand types the operator will operate on. The argument list is
690  * expected to be a couple of parse nodes pointed to be a List
691  * object. If the comments string is empty, the associated comment
692  * is dropped.
693  *
694  * NOTE: we actually attach the comment to the procedure that underlies
695  * the operator.  This is a feature, not a bug: we want the same comment
696  * to be visible for both operator and function.
697  *------------------------------------------------------------------
698 */
699
700 static void
701 CommentOperator(char *opername, List *arguments, char *comment)
702 {
703         TypeName   *typenode1 = (TypeName *) lfirst(arguments);
704         TypeName   *typenode2 = (TypeName *) lsecond(arguments);
705         char            oprtype = 0;
706         Form_pg_operator data;
707         HeapTuple       optuple;
708         Oid                     oid,
709                                 leftoid = InvalidOid,
710                                 rightoid = InvalidOid;
711
712         /* Attempt to fetch the left type oid, if specified */
713         if (typenode1 != NULL)
714                 leftoid = typenameTypeId(typenode1);
715
716         /* Attempt to fetch the right type oid, if specified */
717         if (typenode2 != NULL)
718                 rightoid = typenameTypeId(typenode2);
719
720         /* Determine operator type */
721
722         if (OidIsValid(leftoid) && (OidIsValid(rightoid)))
723                 oprtype = 'b';
724         else if (OidIsValid(leftoid))
725                 oprtype = 'r';
726         else if (OidIsValid(rightoid))
727                 oprtype = 'l';
728         else
729                 elog(ERROR, "operator '%s' is of an illegal type'", opername);
730
731         /* Attempt to fetch the operator oid */
732
733         optuple = SearchSysCache(OPERNAME,
734                                                          PointerGetDatum(opername),
735                                                          ObjectIdGetDatum(leftoid),
736                                                          ObjectIdGetDatum(rightoid),
737                                                          CharGetDatum(oprtype));
738         if (!HeapTupleIsValid(optuple))
739                 elog(ERROR, "operator '%s' does not exist", opername);
740
741         oid = optuple->t_data->t_oid;
742
743         /* Valid user's ability to comment on this operator */
744
745         if (!pg_oper_ownercheck(oid, GetUserId()))
746                 elog(ERROR, "you are not permitted to comment on operator '%s'",
747                          opername);
748
749         /* Get the procedure associated with the operator */
750
751         data = (Form_pg_operator) GETSTRUCT(optuple);
752         oid = data->oprcode;
753         if (oid == InvalidOid)
754                 elog(ERROR, "operator '%s' does not have an underlying function", opername);
755
756         ReleaseSysCache(optuple);
757
758         /* Call CreateComments() to create/drop the comments */
759
760         CreateComments(oid, RelOid_pg_proc, 0, comment);
761 }
762
763 /*------------------------------------------------------------------
764  * CommentTrigger --
765  *
766  * This routine is used to allow a user to provide comments on a
767  * trigger event. The trigger for commenting is determined by both
768  * its name and the relation to which it refers. The arguments to this
769  * function are the trigger name, the relation name, and the comments
770  * to add/drop.
771  *------------------------------------------------------------------
772 */
773
774 static void
775 CommentTrigger(char *trigger, char *schemaname, char *relname, char *comment)
776 {
777         RangeVar   *rel = makeNode(RangeVar);
778         Relation        pg_trigger,
779                                 relation;
780         HeapTuple       triggertuple;
781         HeapScanDesc scan;
782         ScanKeyData entry[2];
783         Oid                     oid;
784
785         /* First, validate the user's action */
786
787         rel->relname = relname;
788         rel->schemaname = schemaname;
789         rel->istemp = false;
790         relation = heap_openrv(rel, AccessShareLock);
791
792         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
793                 elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
794                          trigger, "defined for relation", relname);
795
796         /* Fetch the trigger oid from pg_trigger  */
797
798         pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
799         ScanKeyEntryInitialize(&entry[0], 0x0, Anum_pg_trigger_tgrelid,
800                                                    F_OIDEQ,
801                                                    ObjectIdGetDatum(RelationGetRelid(relation)));
802         ScanKeyEntryInitialize(&entry[1], 0x0, Anum_pg_trigger_tgname,
803                                                    F_NAMEEQ,
804                                                    NameGetDatum(trigger));
805         scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 2, entry);
806         triggertuple = heap_getnext(scan, 0);
807
808         /* If no trigger exists for the relation specified, notify user */
809
810         if (!HeapTupleIsValid(triggertuple))
811                 elog(ERROR, "trigger '%s' defined for relation '%s' does not exist",
812                          trigger, relname);
813
814         oid = triggertuple->t_data->t_oid;
815
816         heap_endscan(scan);
817
818         /* Create the comments with the pg_trigger oid */
819
820         CreateComments(oid, RelationGetRelid(pg_trigger), 0, comment);
821
822         /* Done, but hold lock on relation */
823
824         heap_close(pg_trigger, AccessShareLock);
825         heap_close(relation, NoLock);
826 }