OSDN Git Service

pgindent run.
[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) 1996-2001, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.60 2002/09/04 20:31:14 momjian 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/namespace.h"
22 #include "catalog/pg_constraint.h"
23 #include "catalog/pg_description.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 "commands/dbcommands.h"
30 #include "miscadmin.h"
31 #include "parser/parse_func.h"
32 #include "parser/parse_oper.h"
33 #include "parser/parse_type.h"
34 #include "utils/acl.h"
35 #include "utils/builtins.h"
36 #include "utils/fmgroids.h"
37 #include "utils/lsyscache.h"
38 #include "utils/syscache.h"
39
40
41 /*
42  * Static Function Prototypes --
43  *
44  * The following protoypes are declared static so as not to conflict
45  * with any other routines outside this module. These routines are
46  * called by the public function CommentObject() routine to create
47  * the appropriate comment for the specific object type.
48  */
49
50 static void CommentRelation(int objtype, List *relname, char *comment);
51 static void CommentAttribute(List *qualname, char *comment);
52 static void CommentDatabase(List *qualname, char *comment);
53 static void CommentNamespace(List *qualname, char *comment);
54 static void CommentRule(List *qualname, char *comment);
55 static void CommentType(List *typename, char *comment);
56 static void CommentAggregate(List *aggregate, List *arguments, char *comment);
57 static void CommentProc(List *function, List *arguments, char *comment);
58 static void CommentOperator(List *opername, List *arguments, char *comment);
59 static void CommentTrigger(List *qualname, char *comment);
60 static void CommentConstraint(List *qualname, char *comment);
61
62
63 /*
64  * CommentObject --
65  *
66  * This routine is used to add the associated comment into
67  * pg_description for the object specified by the given SQL command.
68  */
69 void
70 CommentObject(CommentStmt *stmt)
71 {
72         switch (stmt->objtype)
73         {
74                 case COMMENT_ON_INDEX:
75                 case COMMENT_ON_SEQUENCE:
76                 case COMMENT_ON_TABLE:
77                 case COMMENT_ON_VIEW:
78                         CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
79                         break;
80                 case COMMENT_ON_COLUMN:
81                         CommentAttribute(stmt->objname, stmt->comment);
82                         break;
83                 case COMMENT_ON_DATABASE:
84                         CommentDatabase(stmt->objname, stmt->comment);
85                         break;
86                 case COMMENT_ON_RULE:
87                         CommentRule(stmt->objname, stmt->comment);
88                         break;
89                 case COMMENT_ON_TYPE:
90                         CommentType(stmt->objname, stmt->comment);
91                         break;
92                 case COMMENT_ON_AGGREGATE:
93                         CommentAggregate(stmt->objname, stmt->objargs, stmt->comment);
94                         break;
95                 case COMMENT_ON_FUNCTION:
96                         CommentProc(stmt->objname, stmt->objargs, stmt->comment);
97                         break;
98                 case COMMENT_ON_OPERATOR:
99                         CommentOperator(stmt->objname, stmt->objargs, stmt->comment);
100                         break;
101                 case COMMENT_ON_TRIGGER:
102                         CommentTrigger(stmt->objname, stmt->comment);
103                         break;
104                 case COMMENT_ON_SCHEMA:
105                         CommentNamespace(stmt->objname, stmt->comment);
106                         break;
107                 case COMMENT_ON_CONSTRAINT:
108                         CommentConstraint(stmt->objname, stmt->comment);
109                         break;
110                 default:
111                         elog(ERROR, "An attempt was made to comment on a unknown type: %d",
112                                  stmt->objtype);
113         }
114 }
115
116 /*
117  * CreateComments --
118  *
119  * Create a comment for the specified object descriptor.  Inserts a new
120  * pg_description tuple, or replaces an existing one with the same key.
121  *
122  * If the comment given is null or an empty string, instead delete any
123  * existing comment for the specified key.
124  */
125 void
126 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
127 {
128         Relation        description;
129         ScanKeyData skey[3];
130         SysScanDesc sd;
131         HeapTuple       oldtuple;
132         HeapTuple       newtuple = NULL;
133         Datum           values[Natts_pg_description];
134         char            nulls[Natts_pg_description];
135         char            replaces[Natts_pg_description];
136         int                     i;
137
138         /* Reduce empty-string to NULL case */
139         if (comment != NULL && strlen(comment) == 0)
140                 comment = NULL;
141
142         /* Prepare to form or update a tuple, if necessary */
143         if (comment != NULL)
144         {
145                 for (i = 0; i < Natts_pg_description; i++)
146                 {
147                         nulls[i] = ' ';
148                         replaces[i] = 'r';
149                 }
150                 i = 0;
151                 values[i++] = ObjectIdGetDatum(oid);
152                 values[i++] = ObjectIdGetDatum(classoid);
153                 values[i++] = Int32GetDatum(subid);
154                 values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
155         }
156
157         /* Use the index to search for a matching old tuple */
158
159         ScanKeyEntryInitialize(&skey[0],
160                                                    (bits16) 0x0,
161                                                    (AttrNumber) 1,
162                                                    (RegProcedure) F_OIDEQ,
163                                                    ObjectIdGetDatum(oid));
164
165         ScanKeyEntryInitialize(&skey[1],
166                                                    (bits16) 0x0,
167                                                    (AttrNumber) 2,
168                                                    (RegProcedure) F_OIDEQ,
169                                                    ObjectIdGetDatum(classoid));
170
171         ScanKeyEntryInitialize(&skey[2],
172                                                    (bits16) 0x0,
173                                                    (AttrNumber) 3,
174                                                    (RegProcedure) F_INT4EQ,
175                                                    Int32GetDatum(subid));
176
177         description = heap_openr(DescriptionRelationName, RowExclusiveLock);
178
179         sd = systable_beginscan(description, DescriptionObjIndex, true,
180                                                         SnapshotNow, 3, skey);
181
182         while ((oldtuple = systable_getnext(sd)) != NULL)
183         {
184                 /* Found the old tuple, so delete or update it */
185
186                 if (comment == NULL)
187                         simple_heap_delete(description, &oldtuple->t_self);
188                 else
189                 {
190                         newtuple = heap_modifytuple(oldtuple, description, values,
191                                                                                 nulls, replaces);
192                         simple_heap_update(description, &oldtuple->t_self, newtuple);
193                 }
194
195                 break;                                  /* Assume there can be only one match */
196         }
197
198         systable_endscan(sd);
199
200         /* If we didn't find an old tuple, insert a new one */
201
202         if (newtuple == NULL && comment != NULL)
203         {
204                 newtuple = heap_formtuple(RelationGetDescr(description),
205                                                                   values, nulls);
206                 simple_heap_insert(description, newtuple);
207         }
208
209         /* Update indexes, if necessary */
210         if (newtuple != NULL)
211         {
212                 CatalogUpdateIndexes(description, newtuple);
213                 heap_freetuple(newtuple);
214         }
215
216         /* Done */
217
218         heap_close(description, NoLock);
219 }
220
221 /*
222  * DeleteComments -- remove comments for an object
223  *
224  * If subid is nonzero then only comments matching it will be removed.
225  * If subid is zero, all comments matching the oid/classoid will be removed
226  * (this corresponds to deleting a whole object).
227  */
228 void
229 DeleteComments(Oid oid, Oid classoid, int32 subid)
230 {
231         Relation        description;
232         ScanKeyData skey[3];
233         int                     nkeys;
234         SysScanDesc sd;
235         HeapTuple       oldtuple;
236
237         /* Use the index to search for all matching old tuples */
238
239         ScanKeyEntryInitialize(&skey[0], 0x0,
240                                                    Anum_pg_description_objoid, F_OIDEQ,
241                                                    ObjectIdGetDatum(oid));
242
243         ScanKeyEntryInitialize(&skey[1], 0x0,
244                                                    Anum_pg_description_classoid, F_OIDEQ,
245                                                    ObjectIdGetDatum(classoid));
246
247         if (subid != 0)
248         {
249                 ScanKeyEntryInitialize(&skey[2], 0x0,
250                                                            Anum_pg_description_objsubid, F_INT4EQ,
251                                                            Int32GetDatum(subid));
252                 nkeys = 3;
253         }
254         else
255                 nkeys = 2;
256
257         description = heap_openr(DescriptionRelationName, RowExclusiveLock);
258
259         sd = systable_beginscan(description, DescriptionObjIndex, true,
260                                                         SnapshotNow, nkeys, skey);
261
262         while ((oldtuple = systable_getnext(sd)) != NULL)
263                 simple_heap_delete(description, &oldtuple->t_self);
264
265         /* Done */
266
267         systable_endscan(sd);
268         heap_close(description, RowExclusiveLock);
269 }
270
271 /*
272  * CommentRelation --
273  *
274  * This routine is used to add/drop a comment from a relation, where
275  * a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
276  * finds the relation name by searching the system cache, locating
277  * the appropriate tuple, and inserting a comment using that
278  * tuple's oid. Its parameters are the relation name and comments.
279  */
280 static void
281 CommentRelation(int objtype, List *relname, char *comment)
282 {
283         Relation        relation;
284         RangeVar   *tgtrel;
285
286         tgtrel = makeRangeVarFromNameList(relname);
287
288         /*
289          * Open the relation.  We do this mainly to acquire a lock that
290          * ensures no one else drops the relation before we commit.  (If they
291          * did, they'd fail to remove the entry we are about to make in
292          * pg_description.)
293          */
294         relation = relation_openrv(tgtrel, AccessShareLock);
295
296         /* Check object security */
297         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
298                 aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
299
300         /* Next, verify that the relation type matches the intent */
301
302         switch (objtype)
303         {
304                 case COMMENT_ON_INDEX:
305                         if (relation->rd_rel->relkind != RELKIND_INDEX)
306                                 elog(ERROR, "relation \"%s\" is not an index",
307                                          RelationGetRelationName(relation));
308                         break;
309                 case COMMENT_ON_SEQUENCE:
310                         if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
311                                 elog(ERROR, "relation \"%s\" is not a sequence",
312                                          RelationGetRelationName(relation));
313                         break;
314                 case COMMENT_ON_TABLE:
315                         if (relation->rd_rel->relkind != RELKIND_RELATION)
316                                 elog(ERROR, "relation \"%s\" is not a table",
317                                          RelationGetRelationName(relation));
318                         break;
319                 case COMMENT_ON_VIEW:
320                         if (relation->rd_rel->relkind != RELKIND_VIEW)
321                                 elog(ERROR, "relation \"%s\" is not a view",
322                                          RelationGetRelationName(relation));
323                         break;
324         }
325
326         /* Create the comment using the relation's oid */
327
328         CreateComments(RelationGetRelid(relation), RelOid_pg_class, 0, comment);
329
330         /* Done, but hold lock until commit */
331         relation_close(relation, NoLock);
332 }
333
334 /*
335  * CommentAttribute --
336  *
337  * This routine is used to add/drop a comment from an attribute
338  * such as a table's column. The routine will check security
339  * restrictions and then attempt to look up the specified
340  * attribute. If successful, a comment is added/dropped, else an
341  * elog() exception is thrown.  The parameters are the relation
342  * and attribute names, and the comments
343  */
344 static void
345 CommentAttribute(List *qualname, char *comment)
346 {
347         int                     nnames;
348         List       *relname;
349         char       *attrname;
350         RangeVar   *rel;
351         Relation        relation;
352         AttrNumber      attnum;
353
354         /* Separate relname and attr name */
355         nnames = length(qualname);
356         if (nnames < 2)
357                 elog(ERROR, "CommentAttribute: must specify relation.attribute");
358         relname = ltruncate(nnames - 1, listCopy(qualname));
359         attrname = strVal(nth(nnames - 1, qualname));
360
361         /* Open the containing relation to ensure it won't go away meanwhile */
362         rel = makeRangeVarFromNameList(relname);
363         relation = relation_openrv(rel, AccessShareLock);
364
365         /* Check object security */
366
367         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
368                 aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
369
370         /* Now, fetch the attribute number from the system cache */
371
372         attnum = get_attnum(RelationGetRelid(relation), attrname);
373         if (attnum == InvalidAttrNumber)
374                 elog(ERROR, "Relation \"%s\" has no column \"%s\"",
375                          RelationGetRelationName(relation), attrname);
376
377         /* Create the comment using the relation's oid */
378
379         CreateComments(RelationGetRelid(relation), RelOid_pg_class,
380                                    (int32) attnum, comment);
381
382         /* Done, but hold lock until commit */
383
384         relation_close(relation, NoLock);
385 }
386
387 /*
388  * CommentDatabase --
389  *
390  * This routine is used to add/drop any user-comments a user might
391  * have regarding the specified database. The routine will check
392  * security for owner permissions, and, if succesful, will then
393  * attempt to find the oid of the database specified. Once found,
394  * a comment is added/dropped using the CreateComments() routine.
395  */
396 static void
397 CommentDatabase(List *qualname, char *comment)
398 {
399         char       *database;
400         Oid                     oid;
401
402         if (length(qualname) != 1)
403                 elog(ERROR, "CommentDatabase: database name may not be qualified");
404         database = strVal(lfirst(qualname));
405
406         /* First get the database OID */
407         oid = get_database_oid(database);
408         if (!OidIsValid(oid))
409                 elog(ERROR, "database \"%s\" does not exist", database);
410
411         /* Allow if the user matches the database dba or is a superuser */
412
413         if (!(superuser() || is_dbadmin(oid)))
414                 elog(ERROR, "you are not permitted to comment on database \"%s\"",
415                          database);
416
417         /* Only allow comments on the current database */
418         if (oid != MyDatabaseId)
419                 elog(ERROR, "Database comments may only be applied to the current database");
420
421         /* Create the comment with the pg_database oid */
422         CreateComments(oid, RelOid_pg_database, 0, comment);
423 }
424
425 /*
426  * CommentNamespace --
427  *
428  * This routine is used to add/drop any user-comments a user might
429  * have regarding the specified namespace. The routine will check
430  * security for owner permissions, and, if succesful, will then
431  * attempt to find the oid of the namespace specified. Once found,
432  * a comment is added/dropped using the CreateComments() routine.
433  */
434 static void
435 CommentNamespace(List *qualname, char *comment)
436 {
437         Oid                     oid;
438         Oid                     classoid;
439         char       *namespace;
440
441         if (length(qualname) != 1)
442                 elog(ERROR, "CommentSchema: schema name may not be qualified");
443         namespace = strVal(lfirst(qualname));
444
445         oid = GetSysCacheOid(NAMESPACENAME,
446                                                  CStringGetDatum(namespace),
447                                                  0, 0, 0);
448         if (!OidIsValid(oid))
449                 elog(ERROR, "CommentSchema: Schema \"%s\" could not be found",
450                          namespace);
451
452         /* Check object security */
453         if (!pg_namespace_ownercheck(oid, GetUserId()))
454                 aclcheck_error(ACLCHECK_NOT_OWNER, namespace);
455
456         /* pg_namespace doesn't have a hard-coded OID, so must look it up */
457         classoid = get_system_catalog_relid(NamespaceRelationName);
458
459         /* Call CreateComments() to create/drop the comments */
460         CreateComments(oid, classoid, 0, comment);
461 }
462
463 /*
464  * CommentRule --
465  *
466  * This routine is used to add/drop any user-comments a user might
467  * have regarding a specified RULE. The rule for commenting is determined by
468  * both its name and the relation to which it refers. The arguments to this
469  * function are the rule name and relation name (merged into a qualified
470  * name), and the comment to add/drop.
471  *
472  * Before PG 7.3, rules had unique names across the whole database, and so
473  * the syntax was just COMMENT ON RULE rulename, with no relation name.
474  * For purposes of backwards compatibility, we support that as long as there
475  * is only one rule by the specified name in the database.
476  */
477 static void
478 CommentRule(List *qualname, char *comment)
479 {
480         int                     nnames;
481         List       *relname;
482         char       *rulename;
483         RangeVar   *rel;
484         Relation        relation;
485         HeapTuple       tuple;
486         Oid                     reloid;
487         Oid                     ruleoid;
488         Oid                     classoid;
489         AclResult       aclcheck;
490
491         /* Separate relname and trig name */
492         nnames = length(qualname);
493         if (nnames == 1)
494         {
495                 /* Old-style: only a rule name is given */
496                 Relation        RewriteRelation;
497                 HeapScanDesc scanDesc;
498                 ScanKeyData scanKeyData;
499
500                 rulename = strVal(lfirst(qualname));
501
502                 /* Search pg_rewrite for such a rule */
503                 ScanKeyEntryInitialize(&scanKeyData,
504                                                            0,
505                                                            Anum_pg_rewrite_rulename,
506                                                            F_NAMEEQ,
507                                                            PointerGetDatum(rulename));
508
509                 RewriteRelation = heap_openr(RewriteRelationName, AccessShareLock);
510                 scanDesc = heap_beginscan(RewriteRelation, SnapshotNow,
511                                                                   1, &scanKeyData);
512
513                 tuple = heap_getnext(scanDesc, ForwardScanDirection);
514                 if (HeapTupleIsValid(tuple))
515                 {
516                         reloid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
517                         ruleoid = HeapTupleGetOid(tuple);
518                 }
519                 else
520                 {
521                         elog(ERROR, "rule \"%s\" does not exist", rulename);
522                         reloid = ruleoid = 0;           /* keep compiler quiet */
523                 }
524
525                 if (HeapTupleIsValid(tuple = heap_getnext(scanDesc,
526                                                                                                   ForwardScanDirection)))
527                         elog(ERROR, "There are multiple rules \"%s\""
528                          "\n\tPlease specify a relation name as well as a rule name",
529                                  rulename);
530
531                 heap_endscan(scanDesc);
532                 heap_close(RewriteRelation, AccessShareLock);
533
534                 /* Open the owning relation to ensure it won't go away meanwhile */
535                 relation = heap_open(reloid, AccessShareLock);
536         }
537         else
538         {
539                 /* New-style: rule and relname both provided */
540                 Assert(nnames >= 2);
541                 relname = ltruncate(nnames - 1, listCopy(qualname));
542                 rulename = strVal(nth(nnames - 1, qualname));
543
544                 /* Open the owning relation to ensure it won't go away meanwhile */
545                 rel = makeRangeVarFromNameList(relname);
546                 relation = heap_openrv(rel, AccessShareLock);
547                 reloid = RelationGetRelid(relation);
548
549                 /* Find the rule's pg_rewrite tuple, get its OID */
550                 tuple = SearchSysCache(RULERELNAME,
551                                                            ObjectIdGetDatum(reloid),
552                                                            PointerGetDatum(rulename),
553                                                            0, 0);
554                 if (!HeapTupleIsValid(tuple))
555                         elog(ERROR, "rule \"%s\" does not exist", rulename);
556                 Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
557                 ruleoid = HeapTupleGetOid(tuple);
558                 ReleaseSysCache(tuple);
559         }
560
561         /* Check object security */
562
563         aclcheck = pg_class_aclcheck(reloid, GetUserId(), ACL_RULE);
564         if (aclcheck != ACLCHECK_OK)
565                 aclcheck_error(aclcheck, rulename);
566
567         /* pg_rewrite doesn't have a hard-coded OID, so must look it up */
568         classoid = get_system_catalog_relid(RewriteRelationName);
569
570         /* Call CreateComments() to create/drop the comments */
571
572         CreateComments(ruleoid, classoid, 0, comment);
573 }
574
575 /*
576  * CommentType --
577  *
578  * This routine is used to add/drop any user-comments a user might
579  * have regarding a TYPE. The type is specified by name
580  * and, if found, and the user has appropriate permissions, a
581  * comment will be added/dropped using the CreateComments() routine.
582  * The type's name and the comments are the paramters to this routine.
583  */
584 static void
585 CommentType(List *typename, char *comment)
586 {
587         TypeName   *tname;
588         Oid                     oid;
589
590         /* XXX a bit of a crock; should accept TypeName in COMMENT syntax */
591         tname = makeNode(TypeName);
592         tname->names = typename;
593         tname->typmod = -1;
594
595         /* Find the type's oid */
596
597         oid = typenameTypeId(tname);
598
599         /* Check object security */
600
601         if (!pg_type_ownercheck(oid, GetUserId()))
602                 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(tname));
603
604         /* Call CreateComments() to create/drop the comments */
605
606         CreateComments(oid, RelOid_pg_type, 0, comment);
607 }
608
609 /*
610  * CommentAggregate --
611  *
612  * This routine is used to allow a user to provide comments on an
613  * aggregate function. The aggregate function is determined by both
614  * its name and its argument type, which, with the comments are
615  * the three parameters handed to this routine.
616  */
617 static void
618 CommentAggregate(List *aggregate, List *arguments, char *comment)
619 {
620         TypeName   *aggtype = (TypeName *) lfirst(arguments);
621         Oid                     baseoid,
622                                 oid;
623
624         /* First, attempt to determine the base aggregate oid */
625         if (aggtype)
626                 baseoid = typenameTypeId(aggtype);
627         else
628                 baseoid = ANYOID;
629
630         /* Now, attempt to find the actual tuple in pg_proc */
631
632         oid = find_aggregate_func("CommentAggregate", aggregate, baseoid);
633
634         /* Next, validate the user's attempt to comment */
635
636         if (!pg_proc_ownercheck(oid, GetUserId()))
637                 aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggregate));
638
639         /* Call CreateComments() to create/drop the comments */
640
641         CreateComments(oid, RelOid_pg_proc, 0, comment);
642 }
643
644 /*
645  * CommentProc --
646  *
647  * This routine is used to allow a user to provide comments on an
648  * procedure (function). The procedure is determined by both
649  * its name and its argument list. The argument list is expected to
650  * be a series of parsed nodes pointed to by a List object. If the
651  * comments string is empty, the associated comment is dropped.
652  */
653 static void
654 CommentProc(List *function, List *arguments, char *comment)
655 {
656         Oid                     oid;
657
658         /* Look up the procedure */
659
660         oid = LookupFuncNameTypeNames(function, arguments,
661                                                                   "CommentProc");
662
663         /* Now, validate the user's ability to comment on this function */
664
665         if (!pg_proc_ownercheck(oid, GetUserId()))
666                 aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(function));
667
668         /* Call CreateComments() to create/drop the comments */
669
670         CreateComments(oid, RelOid_pg_proc, 0, comment);
671 }
672
673 /*
674  * CommentOperator --
675  *
676  * This routine is used to allow a user to provide comments on an
677  * operator. The operator for commenting is determined by both
678  * its name and its argument list which defines the left and right
679  * hand types the operator will operate on. The argument list is
680  * expected to be a couple of parse nodes pointed to be a List
681  * object.
682  */
683 static void
684 CommentOperator(List *opername, List *arguments, char *comment)
685 {
686         TypeName   *typenode1 = (TypeName *) lfirst(arguments);
687         TypeName   *typenode2 = (TypeName *) lsecond(arguments);
688         Oid                     oid;
689         Oid                     classoid;
690
691         /* Look up the operator */
692         oid = LookupOperNameTypeNames(opername, typenode1, typenode2,
693                                                                   "CommentOperator");
694
695         /* Valid user's ability to comment on this operator */
696         if (!pg_oper_ownercheck(oid, GetUserId()))
697                 aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(opername));
698
699         /* pg_operator doesn't have a hard-coded OID, so must look it up */
700         classoid = get_system_catalog_relid(OperatorRelationName);
701
702         /* Call CreateComments() to create/drop the comments */
703         CreateComments(oid, classoid, 0, comment);
704 }
705
706 /*
707  * CommentTrigger --
708  *
709  * This routine is used to allow a user to provide comments on a
710  * trigger event. The trigger for commenting is determined by both
711  * its name and the relation to which it refers. The arguments to this
712  * function are the trigger name and relation name (merged into a qualified
713  * name), and the comment to add/drop.
714  */
715 static void
716 CommentTrigger(List *qualname, char *comment)
717 {
718         int                     nnames;
719         List       *relname;
720         char       *trigname;
721         RangeVar   *rel;
722         Relation        pg_trigger,
723                                 relation;
724         HeapTuple       triggertuple;
725         SysScanDesc scan;
726         ScanKeyData entry[2];
727         Oid                     oid;
728
729         /* Separate relname and trig name */
730         nnames = length(qualname);
731         if (nnames < 2)
732                 elog(ERROR, "CommentTrigger: must specify relation and trigger");
733         relname = ltruncate(nnames - 1, listCopy(qualname));
734         trigname = strVal(nth(nnames - 1, qualname));
735
736         /* Open the owning relation to ensure it won't go away meanwhile */
737         rel = makeRangeVarFromNameList(relname);
738         relation = heap_openrv(rel, AccessShareLock);
739
740         /* Check object security */
741
742         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
743                 aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
744
745         /*
746          * Fetch the trigger tuple from pg_trigger.  There can be only one
747          * because of the unique index.
748          */
749         pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
750         ScanKeyEntryInitialize(&entry[0], 0x0,
751                                                    Anum_pg_trigger_tgrelid,
752                                                    F_OIDEQ,
753                                                    ObjectIdGetDatum(RelationGetRelid(relation)));
754         ScanKeyEntryInitialize(&entry[1], 0x0,
755                                                    Anum_pg_trigger_tgname,
756                                                    F_NAMEEQ,
757                                                    CStringGetDatum(trigname));
758         scan = systable_beginscan(pg_trigger, TriggerRelidNameIndex, true,
759                                                           SnapshotNow, 2, entry);
760         triggertuple = systable_getnext(scan);
761
762         /* If no trigger exists for the relation specified, notify user */
763
764         if (!HeapTupleIsValid(triggertuple))
765                 elog(ERROR, "trigger \"%s\" for relation \"%s\" does not exist",
766                          trigname, RelationGetRelationName(relation));
767
768         oid = HeapTupleGetOid(triggertuple);
769
770         systable_endscan(scan);
771
772         /* Create the comment with the pg_trigger oid */
773
774         CreateComments(oid, RelationGetRelid(pg_trigger), 0, comment);
775
776         /* Done, but hold lock on relation */
777
778         heap_close(pg_trigger, AccessShareLock);
779         heap_close(relation, NoLock);
780 }
781
782
783 /*
784  * CommentConstraint --
785  *
786  * Enable commenting on constraints held within the pg_constraint
787  * table.  A qualified name is required as constraint names are
788  * unique per relation.
789  */
790 static void
791 CommentConstraint(List *qualname, char *comment)
792 {
793         int                     nnames;
794         List       *relName;
795         char       *conName;
796         RangeVar   *rel;
797         Relation        pg_constraint,
798                                 relation;
799         HeapTuple       tuple;
800         SysScanDesc scan;
801         ScanKeyData skey[1];
802         Oid                     conOid = InvalidOid;
803
804         /* Separate relname and constraint name */
805         nnames = length(qualname);
806         if (nnames < 2)
807                 elog(ERROR, "CommentConstraint: must specify relation and constraint");
808         relName = ltruncate(nnames - 1, listCopy(qualname));
809         conName = strVal(nth(nnames - 1, qualname));
810
811         /* Open the owning relation to ensure it won't go away meanwhile */
812         rel = makeRangeVarFromNameList(relName);
813         relation = heap_openrv(rel, AccessShareLock);
814
815         /* Check object security */
816
817         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
818                 aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(relation));
819
820         /*
821          * Fetch the constraint tuple from pg_constraint.  There may be more
822          * than one match, because constraints are not required to have unique
823          * names; if so, error out.
824          */
825         pg_constraint = heap_openr(ConstraintRelationName, AccessShareLock);
826
827         ScanKeyEntryInitialize(&skey[0], 0x0,
828                                                    Anum_pg_constraint_conrelid, F_OIDEQ,
829                                                    ObjectIdGetDatum(RelationGetRelid(relation)));
830
831         scan = systable_beginscan(pg_constraint, ConstraintRelidIndex, true,
832                                                           SnapshotNow, 1, skey);
833
834         while (HeapTupleIsValid(tuple = systable_getnext(scan)))
835         {
836                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
837
838                 if (strcmp(NameStr(con->conname), conName) == 0)
839                 {
840                         if (OidIsValid(conOid))
841                                 elog(ERROR, "Relation \"%s\" has multiple constraints named \"%s\"",
842                                          RelationGetRelationName(relation), conName);
843                         conOid = HeapTupleGetOid(tuple);
844                 }
845         }
846
847         systable_endscan(scan);
848
849         /* If no constraint exists for the relation specified, notify user */
850         if (!OidIsValid(conOid))
851                 elog(ERROR, "constraint \"%s\" for relation \"%s\" does not exist",
852                          conName, RelationGetRelationName(relation));
853
854         /* Create the comment with the pg_constraint oid */
855         CreateComments(conOid, RelationGetRelid(pg_constraint), 0, comment);
856
857         /* Done, but hold lock on relation */
858         heap_close(pg_constraint, AccessShareLock);
859         heap_close(relation, NoLock);
860 }