OSDN Git Service

First phase of project to use fixed OIDs for all system catalogs and
[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-2005, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.82 2005/04/14 01:38:16 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/namespace.h"
22 #include "catalog/pg_constraint.h"
23 #include "catalog/pg_database.h"
24 #include "catalog/pg_description.h"
25 #include "catalog/pg_largeobject.h"
26 #include "catalog/pg_operator.h"
27 #include "catalog/pg_proc.h"
28 #include "catalog/pg_rewrite.h"
29 #include "catalog/pg_trigger.h"
30 #include "catalog/pg_type.h"
31 #include "commands/comment.h"
32 #include "commands/dbcommands.h"
33 #include "miscadmin.h"
34 #include "parser/parse_func.h"
35 #include "parser/parse_oper.h"
36 #include "parser/parse_type.h"
37 #include "utils/acl.h"
38 #include "utils/builtins.h"
39 #include "utils/fmgroids.h"
40 #include "utils/lsyscache.h"
41 #include "utils/syscache.h"
42
43
44 /*
45  * Static Function Prototypes --
46  *
47  * The following protoypes are declared static so as not to conflict
48  * with any other routines outside this module. These routines are
49  * called by the public function CommentObject() routine to create
50  * the appropriate comment for the specific object type.
51  */
52
53 static void CommentRelation(int objtype, List *relname, char *comment);
54 static void CommentAttribute(List *qualname, char *comment);
55 static void CommentDatabase(List *qualname, char *comment);
56 static void CommentNamespace(List *qualname, char *comment);
57 static void CommentRule(List *qualname, char *comment);
58 static void CommentType(List *typename, char *comment);
59 static void CommentAggregate(List *aggregate, List *arguments, char *comment);
60 static void CommentProc(List *function, List *arguments, char *comment);
61 static void CommentOperator(List *opername, List *arguments, char *comment);
62 static void CommentTrigger(List *qualname, char *comment);
63 static void CommentConstraint(List *qualname, char *comment);
64 static void CommentConversion(List *qualname, char *comment);
65 static void CommentLanguage(List *qualname, char *comment);
66 static void CommentOpClass(List *qualname, List *arguments, char *comment);
67 static void CommentLargeObject(List *qualname, char *comment);
68 static void CommentCast(List *qualname, List *arguments, char *comment);
69
70
71 /*
72  * CommentObject --
73  *
74  * This routine is used to add the associated comment into
75  * pg_description for the object specified by the given SQL command.
76  */
77 void
78 CommentObject(CommentStmt *stmt)
79 {
80         switch (stmt->objtype)
81         {
82                 case OBJECT_INDEX:
83                 case OBJECT_SEQUENCE:
84                 case OBJECT_TABLE:
85                 case OBJECT_VIEW:
86                         CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
87                         break;
88                 case OBJECT_COLUMN:
89                         CommentAttribute(stmt->objname, stmt->comment);
90                         break;
91                 case OBJECT_DATABASE:
92                         CommentDatabase(stmt->objname, stmt->comment);
93                         break;
94                 case OBJECT_RULE:
95                         CommentRule(stmt->objname, stmt->comment);
96                         break;
97                 case OBJECT_TYPE:
98                         CommentType(stmt->objname, stmt->comment);
99                         break;
100                 case OBJECT_AGGREGATE:
101                         CommentAggregate(stmt->objname, stmt->objargs, stmt->comment);
102                         break;
103                 case OBJECT_FUNCTION:
104                         CommentProc(stmt->objname, stmt->objargs, stmt->comment);
105                         break;
106                 case OBJECT_OPERATOR:
107                         CommentOperator(stmt->objname, stmt->objargs, stmt->comment);
108                         break;
109                 case OBJECT_TRIGGER:
110                         CommentTrigger(stmt->objname, stmt->comment);
111                         break;
112                 case OBJECT_SCHEMA:
113                         CommentNamespace(stmt->objname, stmt->comment);
114                         break;
115                 case OBJECT_CONSTRAINT:
116                         CommentConstraint(stmt->objname, stmt->comment);
117                         break;
118                 case OBJECT_CONVERSION:
119                         CommentConversion(stmt->objname, stmt->comment);
120                         break;
121                 case OBJECT_LANGUAGE:
122                         CommentLanguage(stmt->objname, stmt->comment);
123                         break;
124                 case OBJECT_OPCLASS:
125                         CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
126                         break;
127                 case OBJECT_LARGEOBJECT:
128                         CommentLargeObject(stmt->objname, stmt->comment);
129                         break;
130                 case OBJECT_CAST:
131                         CommentCast(stmt->objname, stmt->objargs, stmt->comment);
132                         break;
133                 default:
134                         elog(ERROR, "unrecognized object type: %d",
135                                  (int) stmt->objtype);
136         }
137 }
138
139 /*
140  * CreateComments --
141  *
142  * Create a comment for the specified object descriptor.  Inserts a new
143  * pg_description tuple, or replaces an existing one with the same key.
144  *
145  * If the comment given is null or an empty string, instead delete any
146  * existing comment for the specified key.
147  */
148 void
149 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
150 {
151         Relation        description;
152         ScanKeyData skey[3];
153         SysScanDesc sd;
154         HeapTuple       oldtuple;
155         HeapTuple       newtuple = NULL;
156         Datum           values[Natts_pg_description];
157         char            nulls[Natts_pg_description];
158         char            replaces[Natts_pg_description];
159         int                     i;
160
161         /* Reduce empty-string to NULL case */
162         if (comment != NULL && strlen(comment) == 0)
163                 comment = NULL;
164
165         /* Prepare to form or update a tuple, if necessary */
166         if (comment != NULL)
167         {
168                 for (i = 0; i < Natts_pg_description; i++)
169                 {
170                         nulls[i] = ' ';
171                         replaces[i] = 'r';
172                 }
173                 i = 0;
174                 values[i++] = ObjectIdGetDatum(oid);
175                 values[i++] = ObjectIdGetDatum(classoid);
176                 values[i++] = Int32GetDatum(subid);
177                 values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
178         }
179
180         /* Use the index to search for a matching old tuple */
181
182         ScanKeyInit(&skey[0],
183                                 Anum_pg_description_objoid,
184                                 BTEqualStrategyNumber, F_OIDEQ,
185                                 ObjectIdGetDatum(oid));
186         ScanKeyInit(&skey[1],
187                                 Anum_pg_description_classoid,
188                                 BTEqualStrategyNumber, F_OIDEQ,
189                                 ObjectIdGetDatum(classoid));
190         ScanKeyInit(&skey[2],
191                                 Anum_pg_description_objsubid,
192                                 BTEqualStrategyNumber, F_INT4EQ,
193                                 Int32GetDatum(subid));
194
195         description = heap_openr(DescriptionRelationName, RowExclusiveLock);
196
197         sd = systable_beginscan(description, DescriptionObjIndex, true,
198                                                         SnapshotNow, 3, skey);
199
200         while ((oldtuple = systable_getnext(sd)) != NULL)
201         {
202                 /* Found the old tuple, so delete or update it */
203
204                 if (comment == NULL)
205                         simple_heap_delete(description, &oldtuple->t_self);
206                 else
207                 {
208                         newtuple = heap_modifytuple(oldtuple, RelationGetDescr(description), values,
209                                                                                 nulls, replaces);
210                         simple_heap_update(description, &oldtuple->t_self, newtuple);
211                 }
212
213                 break;                                  /* Assume there can be only one match */
214         }
215
216         systable_endscan(sd);
217
218         /* If we didn't find an old tuple, insert a new one */
219
220         if (newtuple == NULL && comment != NULL)
221         {
222                 newtuple = heap_formtuple(RelationGetDescr(description),
223                                                                   values, nulls);
224                 simple_heap_insert(description, newtuple);
225         }
226
227         /* Update indexes, if necessary */
228         if (newtuple != NULL)
229         {
230                 CatalogUpdateIndexes(description, newtuple);
231                 heap_freetuple(newtuple);
232         }
233
234         /* Done */
235
236         heap_close(description, NoLock);
237 }
238
239 /*
240  * DeleteComments -- remove comments for an object
241  *
242  * If subid is nonzero then only comments matching it will be removed.
243  * If subid is zero, all comments matching the oid/classoid will be removed
244  * (this corresponds to deleting a whole object).
245  */
246 void
247 DeleteComments(Oid oid, Oid classoid, int32 subid)
248 {
249         Relation        description;
250         ScanKeyData skey[3];
251         int                     nkeys;
252         SysScanDesc sd;
253         HeapTuple       oldtuple;
254
255         /* Use the index to search for all matching old tuples */
256
257         ScanKeyInit(&skey[0],
258                                 Anum_pg_description_objoid,
259                                 BTEqualStrategyNumber, F_OIDEQ,
260                                 ObjectIdGetDatum(oid));
261         ScanKeyInit(&skey[1],
262                                 Anum_pg_description_classoid,
263                                 BTEqualStrategyNumber, F_OIDEQ,
264                                 ObjectIdGetDatum(classoid));
265
266         if (subid != 0)
267         {
268                 ScanKeyInit(&skey[2],
269                                         Anum_pg_description_objsubid,
270                                         BTEqualStrategyNumber, F_INT4EQ,
271                                         Int32GetDatum(subid));
272                 nkeys = 3;
273         }
274         else
275                 nkeys = 2;
276
277         description = heap_openr(DescriptionRelationName, RowExclusiveLock);
278
279         sd = systable_beginscan(description, DescriptionObjIndex, true,
280                                                         SnapshotNow, nkeys, skey);
281
282         while ((oldtuple = systable_getnext(sd)) != NULL)
283                 simple_heap_delete(description, &oldtuple->t_self);
284
285         /* Done */
286
287         systable_endscan(sd);
288         heap_close(description, RowExclusiveLock);
289 }
290
291 /*
292  * CommentRelation --
293  *
294  * This routine is used to add/drop a comment from a relation, where
295  * a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
296  * finds the relation name by searching the system cache, locating
297  * the appropriate tuple, and inserting a comment using that
298  * tuple's oid. Its parameters are the relation name and comments.
299  */
300 static void
301 CommentRelation(int objtype, List *relname, char *comment)
302 {
303         Relation        relation;
304         RangeVar   *tgtrel;
305
306         tgtrel = makeRangeVarFromNameList(relname);
307
308         /*
309          * Open the relation.  We do this mainly to acquire a lock that
310          * ensures no one else drops the relation before we commit.  (If they
311          * did, they'd fail to remove the entry we are about to make in
312          * pg_description.)
313          */
314         relation = relation_openrv(tgtrel, AccessShareLock);
315
316         /* Check object security */
317         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
318                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
319                                            RelationGetRelationName(relation));
320
321         /* Next, verify that the relation type matches the intent */
322
323         switch (objtype)
324         {
325                 case OBJECT_INDEX:
326                         if (relation->rd_rel->relkind != RELKIND_INDEX)
327                                 ereport(ERROR,
328                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
329                                                  errmsg("\"%s\" is not an index",
330                                                                 RelationGetRelationName(relation))));
331                         break;
332                 case OBJECT_SEQUENCE:
333                         if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
334                                 ereport(ERROR,
335                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
336                                                  errmsg("\"%s\" is not a sequence",
337                                                                 RelationGetRelationName(relation))));
338                         break;
339                 case OBJECT_TABLE:
340                         if (relation->rd_rel->relkind != RELKIND_RELATION)
341                                 ereport(ERROR,
342                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
343                                                  errmsg("\"%s\" is not a table",
344                                                                 RelationGetRelationName(relation))));
345                         break;
346                 case OBJECT_VIEW:
347                         if (relation->rd_rel->relkind != RELKIND_VIEW)
348                                 ereport(ERROR,
349                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
350                                                  errmsg("\"%s\" is not a view",
351                                                                 RelationGetRelationName(relation))));
352                         break;
353         }
354
355         /* Create the comment using the relation's oid */
356
357         CreateComments(RelationGetRelid(relation), RelationRelationId, 0, comment);
358
359         /* Done, but hold lock until commit */
360         relation_close(relation, NoLock);
361 }
362
363 /*
364  * CommentAttribute --
365  *
366  * This routine is used to add/drop a comment from an attribute
367  * such as a table's column. The routine will check security
368  * restrictions and then attempt to look up the specified
369  * attribute. If successful, a comment is added/dropped, else an
370  * ereport() exception is thrown.       The parameters are the relation
371  * and attribute names, and the comment
372  */
373 static void
374 CommentAttribute(List *qualname, char *comment)
375 {
376         int                     nnames;
377         List       *relname;
378         char       *attrname;
379         RangeVar   *rel;
380         Relation        relation;
381         AttrNumber      attnum;
382
383         /* Separate relname and attr name */
384         nnames = list_length(qualname);
385         if (nnames < 2)                         /* parser messed up */
386                 elog(ERROR, "must specify relation and attribute");
387         relname = list_truncate(list_copy(qualname), nnames - 1);
388         attrname = strVal(lfirst(list_tail(qualname)));
389
390         /* Open the containing relation to ensure it won't go away meanwhile */
391         rel = makeRangeVarFromNameList(relname);
392         relation = relation_openrv(rel, AccessShareLock);
393
394         /* Check object security */
395
396         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
397                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
398                                            RelationGetRelationName(relation));
399
400         /* Now, fetch the attribute number from the system cache */
401
402         attnum = get_attnum(RelationGetRelid(relation), attrname);
403         if (attnum == InvalidAttrNumber)
404                 ereport(ERROR,
405                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
406                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
407                                                 attrname, RelationGetRelationName(relation))));
408
409         /* Create the comment using the relation's oid */
410
411         CreateComments(RelationGetRelid(relation), RelationRelationId,
412                                    (int32) attnum, comment);
413
414         /* Done, but hold lock until commit */
415
416         relation_close(relation, NoLock);
417 }
418
419 /*
420  * CommentDatabase --
421  *
422  * This routine is used to add/drop any user-comments a user might
423  * have regarding the specified database. The routine will check
424  * security for owner permissions, and, if successful, will then
425  * attempt to find the oid of the database specified. Once found,
426  * a comment is added/dropped using the CreateComments() routine.
427  */
428 static void
429 CommentDatabase(List *qualname, char *comment)
430 {
431         char       *database;
432         Oid                     oid;
433
434         if (list_length(qualname) != 1)
435                 ereport(ERROR,
436                                 (errcode(ERRCODE_SYNTAX_ERROR),
437                                  errmsg("database name may not be qualified")));
438         database = strVal(linitial(qualname));
439
440         /*
441          * We cannot currently support cross-database comments (since other
442          * DBs cannot see pg_description of this database).  So, we reject
443          * attempts to comment on a database other than the current one.
444          * Someday this might be improved, but it would take a redesigned
445          * infrastructure.
446          *
447          * When loading a dump, we may see a COMMENT ON DATABASE for the old name
448          * of the database.  Erroring out would prevent pg_restore from
449          * completing (which is really pg_restore's fault, but for now we will
450          * work around the problem here).  Consensus is that the best fix is
451          * to treat wrong database name as a WARNING not an ERROR.
452          */
453
454         /* First get the database OID */
455         oid = get_database_oid(database);
456         if (!OidIsValid(oid))
457         {
458                 ereport(WARNING,
459                                 (errcode(ERRCODE_UNDEFINED_DATABASE),
460                                  errmsg("database \"%s\" does not exist", database)));
461                 return;
462         }
463
464         /* Only allow comments on the current database */
465         if (oid != MyDatabaseId)
466         {
467                 ereport(WARNING,                /* throw just a warning so pg_restore
468                                                                  * doesn't fail */
469                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
470                                  errmsg("database comments may only be applied to the current database")));
471                 return;
472         }
473
474         /* Check object security */
475         if (!pg_database_ownercheck(oid, GetUserId()))
476                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
477                                            database);
478
479         /* Create the comment with the pg_database oid */
480         CreateComments(oid, DatabaseRelationId, 0, comment);
481 }
482
483 /*
484  * CommentNamespace --
485  *
486  * This routine is used to add/drop any user-comments a user might
487  * have regarding the specified namespace. The routine will check
488  * security for owner permissions, and, if successful, will then
489  * attempt to find the oid of the namespace specified. Once found,
490  * a comment is added/dropped using the CreateComments() routine.
491  */
492 static void
493 CommentNamespace(List *qualname, char *comment)
494 {
495         Oid                     oid;
496         Oid                     classoid;
497         char       *namespace;
498
499         if (list_length(qualname) != 1)
500                 ereport(ERROR,
501                                 (errcode(ERRCODE_SYNTAX_ERROR),
502                                  errmsg("schema name may not be qualified")));
503         namespace = strVal(linitial(qualname));
504
505         oid = GetSysCacheOid(NAMESPACENAME,
506                                                  CStringGetDatum(namespace),
507                                                  0, 0, 0);
508         if (!OidIsValid(oid))
509                 ereport(ERROR,
510                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
511                                  errmsg("schema \"%s\" does not exist", namespace)));
512
513         /* Check object security */
514         if (!pg_namespace_ownercheck(oid, GetUserId()))
515                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
516                                            namespace);
517
518         /* pg_namespace doesn't have a hard-coded OID, so must look it up */
519         classoid = get_system_catalog_relid(NamespaceRelationName);
520
521         /* Call CreateComments() to create/drop the comments */
522         CreateComments(oid, classoid, 0, comment);
523 }
524
525 /*
526  * CommentRule --
527  *
528  * This routine is used to add/drop any user-comments a user might
529  * have regarding a specified RULE. The rule for commenting is determined by
530  * both its name and the relation to which it refers. The arguments to this
531  * function are the rule name and relation name (merged into a qualified
532  * name), and the comment to add/drop.
533  *
534  * Before PG 7.3, rules had unique names across the whole database, and so
535  * the syntax was just COMMENT ON RULE rulename, with no relation name.
536  * For purposes of backwards compatibility, we support that as long as there
537  * is only one rule by the specified name in the database.
538  */
539 static void
540 CommentRule(List *qualname, char *comment)
541 {
542         int                     nnames;
543         List       *relname;
544         char       *rulename;
545         RangeVar   *rel;
546         Relation        relation;
547         HeapTuple       tuple;
548         Oid                     reloid;
549         Oid                     ruleoid;
550         Oid                     classoid;
551         AclResult       aclcheck;
552
553         /* Separate relname and trig name */
554         nnames = list_length(qualname);
555         if (nnames == 1)
556         {
557                 /* Old-style: only a rule name is given */
558                 Relation        RewriteRelation;
559                 HeapScanDesc scanDesc;
560                 ScanKeyData scanKeyData;
561
562                 rulename = strVal(linitial(qualname));
563
564                 /* Search pg_rewrite for such a rule */
565                 ScanKeyInit(&scanKeyData,
566                                         Anum_pg_rewrite_rulename,
567                                         BTEqualStrategyNumber, F_NAMEEQ,
568                                         PointerGetDatum(rulename));
569
570                 RewriteRelation = heap_openr(RewriteRelationName, AccessShareLock);
571                 scanDesc = heap_beginscan(RewriteRelation, SnapshotNow,
572                                                                   1, &scanKeyData);
573
574                 tuple = heap_getnext(scanDesc, ForwardScanDirection);
575                 if (HeapTupleIsValid(tuple))
576                 {
577                         reloid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
578                         ruleoid = HeapTupleGetOid(tuple);
579                 }
580                 else
581                 {
582                         ereport(ERROR,
583                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
584                                          errmsg("rule \"%s\" does not exist", rulename)));
585                         reloid = ruleoid = 0;           /* keep compiler quiet */
586                 }
587
588                 if (HeapTupleIsValid(tuple = heap_getnext(scanDesc,
589                                                                                                   ForwardScanDirection)))
590                         ereport(ERROR,
591                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
592                            errmsg("there are multiple rules named \"%s\"", rulename),
593                         errhint("Specify a relation name as well as a rule name.")));
594
595                 heap_endscan(scanDesc);
596                 heap_close(RewriteRelation, AccessShareLock);
597
598                 /* Open the owning relation to ensure it won't go away meanwhile */
599                 relation = heap_open(reloid, AccessShareLock);
600         }
601         else
602         {
603                 /* New-style: rule and relname both provided */
604                 Assert(nnames >= 2);
605                 relname = list_truncate(list_copy(qualname), nnames - 1);
606                 rulename = strVal(lfirst(list_tail(qualname)));
607
608                 /* Open the owning relation to ensure it won't go away meanwhile */
609                 rel = makeRangeVarFromNameList(relname);
610                 relation = heap_openrv(rel, AccessShareLock);
611                 reloid = RelationGetRelid(relation);
612
613                 /* Find the rule's pg_rewrite tuple, get its OID */
614                 tuple = SearchSysCache(RULERELNAME,
615                                                            ObjectIdGetDatum(reloid),
616                                                            PointerGetDatum(rulename),
617                                                            0, 0);
618                 if (!HeapTupleIsValid(tuple))
619                         ereport(ERROR,
620                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
621                                  errmsg("rule \"%s\" for relation \"%s\" does not exist",
622                                                 rulename, RelationGetRelationName(relation))));
623                 Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
624                 ruleoid = HeapTupleGetOid(tuple);
625                 ReleaseSysCache(tuple);
626         }
627
628         /* Check object security */
629         aclcheck = pg_class_aclcheck(reloid, GetUserId(), ACL_RULE);
630         if (aclcheck != ACLCHECK_OK)
631                 aclcheck_error(aclcheck, ACL_KIND_CLASS,
632                                            get_rel_name(reloid));
633
634         /* pg_rewrite doesn't have a hard-coded OID, so must look it up */
635         classoid = get_system_catalog_relid(RewriteRelationName);
636
637         /* Call CreateComments() to create/drop the comments */
638         CreateComments(ruleoid, classoid, 0, comment);
639
640         heap_close(relation, NoLock);
641 }
642
643 /*
644  * CommentType --
645  *
646  * This routine is used to add/drop any user-comments a user might
647  * have regarding a TYPE. The type is specified by name
648  * and, if found, and the user has appropriate permissions, a
649  * comment will be added/dropped using the CreateComments() routine.
650  * The type's name and the comments are the parameters to this routine.
651  */
652 static void
653 CommentType(List *typename, char *comment)
654 {
655         TypeName   *tname;
656         Oid                     oid;
657
658         /* XXX a bit of a crock; should accept TypeName in COMMENT syntax */
659         tname = makeNode(TypeName);
660         tname->names = typename;
661         tname->typmod = -1;
662
663         /* Find the type's oid */
664
665         oid = typenameTypeId(tname);
666
667         /* Check object security */
668
669         if (!pg_type_ownercheck(oid, GetUserId()))
670                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
671                                            TypeNameToString(tname));
672
673         /* Call CreateComments() to create/drop the comments */
674
675         CreateComments(oid, TypeRelationId, 0, comment);
676 }
677
678 /*
679  * CommentAggregate --
680  *
681  * This routine is used to allow a user to provide comments on an
682  * aggregate function. The aggregate function is determined by both
683  * its name and its argument type, which, with the comments are
684  * the three parameters handed to this routine.
685  */
686 static void
687 CommentAggregate(List *aggregate, List *arguments, char *comment)
688 {
689         TypeName   *aggtype = (TypeName *) linitial(arguments);
690         Oid                     baseoid,
691                                 oid;
692
693         /* First, attempt to determine the base aggregate oid */
694         if (aggtype)
695                 baseoid = typenameTypeId(aggtype);
696         else
697                 baseoid = ANYOID;
698
699         /* Now, attempt to find the actual tuple in pg_proc */
700
701         oid = find_aggregate_func(aggregate, baseoid, false);
702
703         /* Next, validate the user's attempt to comment */
704
705         if (!pg_proc_ownercheck(oid, GetUserId()))
706                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
707                                            NameListToString(aggregate));
708
709         /* Call CreateComments() to create/drop the comments */
710
711         CreateComments(oid, ProcedureRelationId, 0, comment);
712 }
713
714 /*
715  * CommentProc --
716  *
717  * This routine is used to allow a user to provide comments on an
718  * procedure (function). The procedure is determined by both
719  * its name and its argument list. The argument list is expected to
720  * be a series of parsed nodes pointed to by a List object. If the
721  * comments string is empty, the associated comment is dropped.
722  */
723 static void
724 CommentProc(List *function, List *arguments, char *comment)
725 {
726         Oid                     oid;
727
728         /* Look up the procedure */
729
730         oid = LookupFuncNameTypeNames(function, arguments, false);
731
732         /* Now, validate the user's ability to comment on this function */
733
734         if (!pg_proc_ownercheck(oid, GetUserId()))
735                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
736                                            NameListToString(function));
737
738         /* Call CreateComments() to create/drop the comments */
739
740         CreateComments(oid, ProcedureRelationId, 0, comment);
741 }
742
743 /*
744  * CommentOperator --
745  *
746  * This routine is used to allow a user to provide comments on an
747  * operator. The operator for commenting is determined by both
748  * its name and its argument list which defines the left and right
749  * hand types the operator will operate on. The argument list is
750  * expected to be a couple of parse nodes pointed to be a List
751  * object.
752  */
753 static void
754 CommentOperator(List *opername, List *arguments, char *comment)
755 {
756         TypeName   *typenode1 = (TypeName *) linitial(arguments);
757         TypeName   *typenode2 = (TypeName *) lsecond(arguments);
758         Oid                     oid;
759         Oid                     classoid;
760
761         /* Look up the operator */
762         oid = LookupOperNameTypeNames(opername, typenode1, typenode2, false);
763
764         /* Valid user's ability to comment on this operator */
765         if (!pg_oper_ownercheck(oid, GetUserId()))
766                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
767                                            NameListToString(opername));
768
769         /* pg_operator doesn't have a hard-coded OID, so must look it up */
770         classoid = get_system_catalog_relid(OperatorRelationName);
771
772         /* Call CreateComments() to create/drop the comments */
773         CreateComments(oid, classoid, 0, comment);
774 }
775
776 /*
777  * CommentTrigger --
778  *
779  * This routine is used to allow a user to provide comments on a
780  * trigger event. The trigger for commenting is determined by both
781  * its name and the relation to which it refers. The arguments to this
782  * function are the trigger name and relation name (merged into a qualified
783  * name), and the comment to add/drop.
784  */
785 static void
786 CommentTrigger(List *qualname, char *comment)
787 {
788         int                     nnames;
789         List       *relname;
790         char       *trigname;
791         RangeVar   *rel;
792         Relation        pg_trigger,
793                                 relation;
794         HeapTuple       triggertuple;
795         SysScanDesc scan;
796         ScanKeyData entry[2];
797         Oid                     oid;
798
799         /* Separate relname and trig name */
800         nnames = list_length(qualname);
801         if (nnames < 2)                         /* parser messed up */
802                 elog(ERROR, "must specify relation and trigger");
803         relname = list_truncate(list_copy(qualname), nnames - 1);
804         trigname = strVal(lfirst(list_tail(qualname)));
805
806         /* Open the owning relation to ensure it won't go away meanwhile */
807         rel = makeRangeVarFromNameList(relname);
808         relation = heap_openrv(rel, AccessShareLock);
809
810         /* Check object security */
811
812         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
813                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
814                                            RelationGetRelationName(relation));
815
816         /*
817          * Fetch the trigger tuple from pg_trigger.  There can be only one
818          * because of the unique index.
819          */
820         pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
821         ScanKeyInit(&entry[0],
822                                 Anum_pg_trigger_tgrelid,
823                                 BTEqualStrategyNumber, F_OIDEQ,
824                                 ObjectIdGetDatum(RelationGetRelid(relation)));
825         ScanKeyInit(&entry[1],
826                                 Anum_pg_trigger_tgname,
827                                 BTEqualStrategyNumber, F_NAMEEQ,
828                                 CStringGetDatum(trigname));
829         scan = systable_beginscan(pg_trigger, TriggerRelidNameIndex, true,
830                                                           SnapshotNow, 2, entry);
831         triggertuple = systable_getnext(scan);
832
833         /* If no trigger exists for the relation specified, notify user */
834
835         if (!HeapTupleIsValid(triggertuple))
836                 ereport(ERROR,
837                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
838                                  errmsg("trigger \"%s\" for table \"%s\" does not exist",
839                                                 trigname, RelationGetRelationName(relation))));
840
841         oid = HeapTupleGetOid(triggertuple);
842
843         systable_endscan(scan);
844
845         /* Create the comment with the pg_trigger oid */
846
847         CreateComments(oid, RelationGetRelid(pg_trigger), 0, comment);
848
849         /* Done, but hold lock on relation */
850
851         heap_close(pg_trigger, AccessShareLock);
852         heap_close(relation, NoLock);
853 }
854
855
856 /*
857  * CommentConstraint --
858  *
859  * Enable commenting on constraints held within the pg_constraint
860  * table.  A qualified name is required as constraint names are
861  * unique per relation.
862  */
863 static void
864 CommentConstraint(List *qualname, char *comment)
865 {
866         int                     nnames;
867         List       *relName;
868         char       *conName;
869         RangeVar   *rel;
870         Relation        pg_constraint,
871                                 relation;
872         HeapTuple       tuple;
873         SysScanDesc scan;
874         ScanKeyData skey[1];
875         Oid                     conOid = InvalidOid;
876
877         /* Separate relname and constraint name */
878         nnames = list_length(qualname);
879         if (nnames < 2)                         /* parser messed up */
880                 elog(ERROR, "must specify relation and constraint");
881         relName = list_truncate(list_copy(qualname), nnames - 1);
882         conName = strVal(lfirst(list_tail(qualname)));
883
884         /* Open the owning relation to ensure it won't go away meanwhile */
885         rel = makeRangeVarFromNameList(relName);
886         relation = heap_openrv(rel, AccessShareLock);
887
888         /* Check object security */
889
890         if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
891                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
892                                            RelationGetRelationName(relation));
893
894         /*
895          * Fetch the constraint tuple from pg_constraint.  There may be more
896          * than one match, because constraints are not required to have unique
897          * names; if so, error out.
898          */
899         pg_constraint = heap_openr(ConstraintRelationName, AccessShareLock);
900
901         ScanKeyInit(&skey[0],
902                                 Anum_pg_constraint_conrelid,
903                                 BTEqualStrategyNumber, F_OIDEQ,
904                                 ObjectIdGetDatum(RelationGetRelid(relation)));
905
906         scan = systable_beginscan(pg_constraint, ConstraintRelidIndex, true,
907                                                           SnapshotNow, 1, skey);
908
909         while (HeapTupleIsValid(tuple = systable_getnext(scan)))
910         {
911                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
912
913                 if (strcmp(NameStr(con->conname), conName) == 0)
914                 {
915                         if (OidIsValid(conOid))
916                                 ereport(ERROR,
917                                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
918                                                  errmsg("table \"%s\" has multiple constraints named \"%s\"",
919                                                    RelationGetRelationName(relation), conName)));
920                         conOid = HeapTupleGetOid(tuple);
921                 }
922         }
923
924         systable_endscan(scan);
925
926         /* If no constraint exists for the relation specified, notify user */
927         if (!OidIsValid(conOid))
928                 ereport(ERROR,
929                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
930                           errmsg("constraint \"%s\" for table \"%s\" does not exist",
931                                          conName, RelationGetRelationName(relation))));
932
933         /* Create the comment with the pg_constraint oid */
934         CreateComments(conOid, RelationGetRelid(pg_constraint), 0, comment);
935
936         /* Done, but hold lock on relation */
937         heap_close(pg_constraint, AccessShareLock);
938         heap_close(relation, NoLock);
939 }
940
941 /*
942  * CommentConversion --
943  *
944  * This routine is used to add/drop any user-comments a user might
945  * have regarding a CONVERSION. The conversion is specified by name
946  * and, if found, and the user has appropriate permissions, a
947  * comment will be added/dropped using the CreateComments() routine.
948  * The conversion's name and the comment are the parameters to this routine.
949  */
950 static void
951 CommentConversion(List *qualname, char *comment)
952 {
953         Oid                     conversionOid;
954         Oid                     classoid;
955
956         conversionOid = FindConversionByName(qualname);
957         if (!OidIsValid(conversionOid))
958                 ereport(ERROR,
959                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
960                                  errmsg("conversion \"%s\" does not exist",
961                                                 NameListToString(qualname))));
962
963         /* Check object security */
964         if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
965                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
966                                            NameListToString(qualname));
967
968         /* pg_conversion doesn't have a hard-coded OID, so must look it up */
969         classoid = get_system_catalog_relid(ConversionRelationName);
970
971         /* Call CreateComments() to create/drop the comments */
972         CreateComments(conversionOid, classoid, 0, comment);
973 }
974
975 /*
976  * CommentLanguage --
977  *
978  * This routine is used to add/drop any user-comments a user might
979  * have regarding a LANGUAGE. The language is specified by name
980  * and, if found, and the user has appropriate permissions, a
981  * comment will be added/dropped using the CreateComments() routine.
982  * The language's name and the comment are the parameters to this routine.
983  */
984 static void
985 CommentLanguage(List *qualname, char *comment)
986 {
987         Oid                     oid;
988         Oid                     classoid;
989         char       *language;
990
991         if (list_length(qualname) != 1)
992                 ereport(ERROR,
993                                 (errcode(ERRCODE_SYNTAX_ERROR),
994                                  errmsg("language name may not be qualified")));
995         language = strVal(linitial(qualname));
996
997         oid = GetSysCacheOid(LANGNAME,
998                                                  CStringGetDatum(language),
999                                                  0, 0, 0);
1000         if (!OidIsValid(oid))
1001                 ereport(ERROR,
1002                                 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1003                                  errmsg("language \"%s\" does not exist", language)));
1004
1005         /* Check object security */
1006         if (!superuser())
1007                 ereport(ERROR,
1008                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1009                  errmsg("must be superuser to comment on procedural language")));
1010
1011         /* pg_language doesn't have a hard-coded OID, so must look it up */
1012         classoid = get_system_catalog_relid(LanguageRelationName);
1013
1014         /* Call CreateComments() to create/drop the comments */
1015         CreateComments(oid, classoid, 0, comment);
1016 }
1017
1018 /*
1019  * CommentOpClass --
1020  *
1021  * This routine is used to allow a user to provide comments on an
1022  * operator class. The operator class for commenting is determined by both
1023  * its name and its argument list which defines the index method
1024  * the operator class is used for. The argument list is expected to contain
1025  * a single name (represented as a string Value node).
1026  */
1027 static void
1028 CommentOpClass(List *qualname, List *arguments, char *comment)
1029 {
1030         char       *amname;
1031         char       *schemaname;
1032         char       *opcname;
1033         Oid                     amID;
1034         Oid                     opcID;
1035         Oid                     classoid;
1036         HeapTuple       tuple;
1037
1038         Assert(list_length(arguments) == 1);
1039         amname = strVal(linitial(arguments));
1040
1041         /*
1042          * Get the access method's OID.
1043          */
1044         amID = GetSysCacheOid(AMNAME,
1045                                                   CStringGetDatum(amname),
1046                                                   0, 0, 0);
1047         if (!OidIsValid(amID))
1048                 ereport(ERROR,
1049                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1050                                  errmsg("access method \"%s\" does not exist",
1051                                                 amname)));
1052
1053         /*
1054          * Look up the opclass.
1055          */
1056
1057         /* deconstruct the name list */
1058         DeconstructQualifiedName(qualname, &schemaname, &opcname);
1059
1060         if (schemaname)
1061         {
1062                 /* Look in specific schema only */
1063                 Oid                     namespaceId;
1064
1065                 namespaceId = LookupExplicitNamespace(schemaname);
1066                 tuple = SearchSysCache(CLAAMNAMENSP,
1067                                                            ObjectIdGetDatum(amID),
1068                                                            PointerGetDatum(opcname),
1069                                                            ObjectIdGetDatum(namespaceId),
1070                                                            0);
1071         }
1072         else
1073         {
1074                 /* Unqualified opclass name, so search the search path */
1075                 opcID = OpclassnameGetOpcid(amID, opcname);
1076                 if (!OidIsValid(opcID))
1077                         ereport(ERROR,
1078                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
1079                                          errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1080                                                         opcname, amname)));
1081                 tuple = SearchSysCache(CLAOID,
1082                                                            ObjectIdGetDatum(opcID),
1083                                                            0, 0, 0);
1084         }
1085
1086         if (!HeapTupleIsValid(tuple))
1087                 ereport(ERROR,
1088                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1089                                  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1090                                                 NameListToString(qualname), amname)));
1091
1092         opcID = HeapTupleGetOid(tuple);
1093
1094         /* Permission check: must own opclass */
1095         if (!pg_opclass_ownercheck(opcID, GetUserId()))
1096                 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
1097                                            NameListToString(qualname));
1098
1099         ReleaseSysCache(tuple);
1100
1101         /* pg_opclass doesn't have a hard-coded OID, so must look it up */
1102         classoid = get_system_catalog_relid(OperatorClassRelationName);
1103
1104         /* Call CreateComments() to create/drop the comments */
1105         CreateComments(opcID, classoid, 0, comment);
1106 }
1107
1108 /*
1109  * CommentLargeObject --
1110  *
1111  * This routine is used to add/drop any user-comments a user might
1112  * have regarding a LARGE OBJECT. The large object is specified by OID
1113  * and, if found, and the user has appropriate permissions, a
1114  * comment will be added/dropped using the CreateComments() routine.
1115  * The large object's OID and the comment are the parameters to this routine.
1116  */
1117 static void
1118 CommentLargeObject(List *qualname, char *comment)
1119 {
1120         Oid                     loid;
1121         Oid                     classoid;
1122         Node       *node;
1123
1124         Assert(list_length(qualname) == 1);
1125         node = (Node *) linitial(qualname);
1126
1127         switch (nodeTag(node))
1128         {
1129                 case T_Integer:
1130                         loid = intVal(node);
1131                         break;
1132                 case T_Float:
1133
1134                         /*
1135                          * Values too large for int4 will be represented as Float
1136                          * constants by the lexer.      Accept these if they are valid OID
1137                          * strings.
1138                          */
1139                         loid = DatumGetObjectId(DirectFunctionCall1(oidin,
1140                                                                                  CStringGetDatum(strVal(node))));
1141                         break;
1142                 default:
1143                         elog(ERROR, "unrecognized node type: %d",
1144                                  (int) nodeTag(node));
1145                         /* keep compiler quiet */
1146                         loid = InvalidOid;
1147         }
1148
1149         /* check that the large object exists */
1150         if (!LargeObjectExists(loid))
1151                 ereport(ERROR,
1152                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1153                                  errmsg("large object %u does not exist", loid)));
1154
1155         /* pg_largeobject doesn't have a hard-coded OID, so must look it up */
1156         classoid = get_system_catalog_relid(LargeObjectRelationName);
1157
1158         /* Call CreateComments() to create/drop the comments */
1159         CreateComments(loid, classoid, 0, comment);
1160 }
1161
1162 /*
1163  * CommentCast --
1164  *
1165  * This routine is used to add/drop any user-comments a user might
1166  * have regarding a CAST. The cast is specified by source and destination types
1167  * and, if found, and the user has appropriate permissions, a
1168  * comment will be added/dropped using the CreateComments() routine.
1169  * The cast's source type is passed as the "name", the destination type
1170  * as the "arguments".
1171  */
1172 static void
1173 CommentCast(List *qualname, List *arguments, char *comment)
1174 {
1175         TypeName   *sourcetype;
1176         TypeName   *targettype;
1177         Oid                     sourcetypeid;
1178         Oid                     targettypeid;
1179         HeapTuple       tuple;
1180         Oid                     castOid;
1181         Oid                     classoid;
1182
1183         Assert(list_length(qualname) == 1);
1184         sourcetype = (TypeName *) linitial(qualname);
1185         Assert(IsA(sourcetype, TypeName));
1186         Assert(list_length(arguments) == 1);
1187         targettype = (TypeName *) linitial(arguments);
1188         Assert(IsA(targettype, TypeName));
1189
1190         sourcetypeid = typenameTypeId(sourcetype);
1191         if (!OidIsValid(sourcetypeid))
1192                 ereport(ERROR,
1193                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1194                                  errmsg("source data type %s does not exist",
1195                                                 TypeNameToString(sourcetype))));
1196
1197         targettypeid = typenameTypeId(targettype);
1198         if (!OidIsValid(targettypeid))
1199                 ereport(ERROR,
1200                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1201                                  errmsg("target data type %s does not exist",
1202                                                 TypeNameToString(targettype))));
1203
1204         tuple = SearchSysCache(CASTSOURCETARGET,
1205                                                    ObjectIdGetDatum(sourcetypeid),
1206                                                    ObjectIdGetDatum(targettypeid),
1207                                                    0, 0);
1208         if (!HeapTupleIsValid(tuple))
1209                 ereport(ERROR,
1210                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
1211                                  errmsg("cast from type %s to type %s does not exist",
1212                                                 TypeNameToString(sourcetype),
1213                                                 TypeNameToString(targettype))));
1214
1215         /* Get the OID of the cast */
1216         castOid = HeapTupleGetOid(tuple);
1217
1218         /* Permission check */
1219         if (!pg_type_ownercheck(sourcetypeid, GetUserId())
1220                 && !pg_type_ownercheck(targettypeid, GetUserId()))
1221                 ereport(ERROR,
1222                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1223                                  errmsg("must be owner of type %s or type %s",
1224                                                 TypeNameToString(sourcetype),
1225                                                 TypeNameToString(targettype))));
1226
1227         ReleaseSysCache(tuple);
1228
1229         /* pg_cast doesn't have a hard-coded OID, so must look it up */
1230         classoid = get_system_catalog_relid(CastRelationName);
1231
1232         /* Call CreateComments() to create/drop the comments */
1233         CreateComments(castOid, classoid, 0, comment);
1234 }