1 /*-------------------------------------------------------------------------
5 * PostgreSQL object comments utility code.
7 * Copyright (c) 1996-2005, PostgreSQL Global Development Group
10 * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.82 2005/04/14 01:38:16 tgl Exp $
12 *-------------------------------------------------------------------------
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"
45 * Static Function Prototypes --
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.
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);
74 * This routine is used to add the associated comment into
75 * pg_description for the object specified by the given SQL command.
78 CommentObject(CommentStmt *stmt)
80 switch (stmt->objtype)
86 CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
89 CommentAttribute(stmt->objname, stmt->comment);
92 CommentDatabase(stmt->objname, stmt->comment);
95 CommentRule(stmt->objname, stmt->comment);
98 CommentType(stmt->objname, stmt->comment);
100 case OBJECT_AGGREGATE:
101 CommentAggregate(stmt->objname, stmt->objargs, stmt->comment);
103 case OBJECT_FUNCTION:
104 CommentProc(stmt->objname, stmt->objargs, stmt->comment);
106 case OBJECT_OPERATOR:
107 CommentOperator(stmt->objname, stmt->objargs, stmt->comment);
110 CommentTrigger(stmt->objname, stmt->comment);
113 CommentNamespace(stmt->objname, stmt->comment);
115 case OBJECT_CONSTRAINT:
116 CommentConstraint(stmt->objname, stmt->comment);
118 case OBJECT_CONVERSION:
119 CommentConversion(stmt->objname, stmt->comment);
121 case OBJECT_LANGUAGE:
122 CommentLanguage(stmt->objname, stmt->comment);
125 CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
127 case OBJECT_LARGEOBJECT:
128 CommentLargeObject(stmt->objname, stmt->comment);
131 CommentCast(stmt->objname, stmt->objargs, stmt->comment);
134 elog(ERROR, "unrecognized object type: %d",
135 (int) stmt->objtype);
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.
145 * If the comment given is null or an empty string, instead delete any
146 * existing comment for the specified key.
149 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
151 Relation description;
155 HeapTuple newtuple = NULL;
156 Datum values[Natts_pg_description];
157 char nulls[Natts_pg_description];
158 char replaces[Natts_pg_description];
161 /* Reduce empty-string to NULL case */
162 if (comment != NULL && strlen(comment) == 0)
165 /* Prepare to form or update a tuple, if necessary */
168 for (i = 0; i < Natts_pg_description; i++)
174 values[i++] = ObjectIdGetDatum(oid);
175 values[i++] = ObjectIdGetDatum(classoid);
176 values[i++] = Int32GetDatum(subid);
177 values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
180 /* Use the index to search for a matching old tuple */
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));
195 description = heap_openr(DescriptionRelationName, RowExclusiveLock);
197 sd = systable_beginscan(description, DescriptionObjIndex, true,
198 SnapshotNow, 3, skey);
200 while ((oldtuple = systable_getnext(sd)) != NULL)
202 /* Found the old tuple, so delete or update it */
205 simple_heap_delete(description, &oldtuple->t_self);
208 newtuple = heap_modifytuple(oldtuple, RelationGetDescr(description), values,
210 simple_heap_update(description, &oldtuple->t_self, newtuple);
213 break; /* Assume there can be only one match */
216 systable_endscan(sd);
218 /* If we didn't find an old tuple, insert a new one */
220 if (newtuple == NULL && comment != NULL)
222 newtuple = heap_formtuple(RelationGetDescr(description),
224 simple_heap_insert(description, newtuple);
227 /* Update indexes, if necessary */
228 if (newtuple != NULL)
230 CatalogUpdateIndexes(description, newtuple);
231 heap_freetuple(newtuple);
236 heap_close(description, NoLock);
240 * DeleteComments -- remove comments for an object
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).
247 DeleteComments(Oid oid, Oid classoid, int32 subid)
249 Relation description;
255 /* Use the index to search for all matching old tuples */
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));
268 ScanKeyInit(&skey[2],
269 Anum_pg_description_objsubid,
270 BTEqualStrategyNumber, F_INT4EQ,
271 Int32GetDatum(subid));
277 description = heap_openr(DescriptionRelationName, RowExclusiveLock);
279 sd = systable_beginscan(description, DescriptionObjIndex, true,
280 SnapshotNow, nkeys, skey);
282 while ((oldtuple = systable_getnext(sd)) != NULL)
283 simple_heap_delete(description, &oldtuple->t_self);
287 systable_endscan(sd);
288 heap_close(description, RowExclusiveLock);
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.
301 CommentRelation(int objtype, List *relname, char *comment)
306 tgtrel = makeRangeVarFromNameList(relname);
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
314 relation = relation_openrv(tgtrel, AccessShareLock);
316 /* Check object security */
317 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
318 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
319 RelationGetRelationName(relation));
321 /* Next, verify that the relation type matches the intent */
326 if (relation->rd_rel->relkind != RELKIND_INDEX)
328 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
329 errmsg("\"%s\" is not an index",
330 RelationGetRelationName(relation))));
332 case OBJECT_SEQUENCE:
333 if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
335 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
336 errmsg("\"%s\" is not a sequence",
337 RelationGetRelationName(relation))));
340 if (relation->rd_rel->relkind != RELKIND_RELATION)
342 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
343 errmsg("\"%s\" is not a table",
344 RelationGetRelationName(relation))));
347 if (relation->rd_rel->relkind != RELKIND_VIEW)
349 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
350 errmsg("\"%s\" is not a view",
351 RelationGetRelationName(relation))));
355 /* Create the comment using the relation's oid */
357 CreateComments(RelationGetRelid(relation), RelationRelationId, 0, comment);
359 /* Done, but hold lock until commit */
360 relation_close(relation, NoLock);
364 * CommentAttribute --
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
374 CommentAttribute(List *qualname, char *comment)
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)));
390 /* Open the containing relation to ensure it won't go away meanwhile */
391 rel = makeRangeVarFromNameList(relname);
392 relation = relation_openrv(rel, AccessShareLock);
394 /* Check object security */
396 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
397 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
398 RelationGetRelationName(relation));
400 /* Now, fetch the attribute number from the system cache */
402 attnum = get_attnum(RelationGetRelid(relation), attrname);
403 if (attnum == InvalidAttrNumber)
405 (errcode(ERRCODE_UNDEFINED_COLUMN),
406 errmsg("column \"%s\" of relation \"%s\" does not exist",
407 attrname, RelationGetRelationName(relation))));
409 /* Create the comment using the relation's oid */
411 CreateComments(RelationGetRelid(relation), RelationRelationId,
412 (int32) attnum, comment);
414 /* Done, but hold lock until commit */
416 relation_close(relation, NoLock);
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.
429 CommentDatabase(List *qualname, char *comment)
434 if (list_length(qualname) != 1)
436 (errcode(ERRCODE_SYNTAX_ERROR),
437 errmsg("database name may not be qualified")));
438 database = strVal(linitial(qualname));
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
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.
454 /* First get the database OID */
455 oid = get_database_oid(database);
456 if (!OidIsValid(oid))
459 (errcode(ERRCODE_UNDEFINED_DATABASE),
460 errmsg("database \"%s\" does not exist", database)));
464 /* Only allow comments on the current database */
465 if (oid != MyDatabaseId)
467 ereport(WARNING, /* throw just a warning so pg_restore
469 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
470 errmsg("database comments may only be applied to the current database")));
474 /* Check object security */
475 if (!pg_database_ownercheck(oid, GetUserId()))
476 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
479 /* Create the comment with the pg_database oid */
480 CreateComments(oid, DatabaseRelationId, 0, comment);
484 * CommentNamespace --
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.
493 CommentNamespace(List *qualname, char *comment)
499 if (list_length(qualname) != 1)
501 (errcode(ERRCODE_SYNTAX_ERROR),
502 errmsg("schema name may not be qualified")));
503 namespace = strVal(linitial(qualname));
505 oid = GetSysCacheOid(NAMESPACENAME,
506 CStringGetDatum(namespace),
508 if (!OidIsValid(oid))
510 (errcode(ERRCODE_UNDEFINED_SCHEMA),
511 errmsg("schema \"%s\" does not exist", namespace)));
513 /* Check object security */
514 if (!pg_namespace_ownercheck(oid, GetUserId()))
515 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
518 /* pg_namespace doesn't have a hard-coded OID, so must look it up */
519 classoid = get_system_catalog_relid(NamespaceRelationName);
521 /* Call CreateComments() to create/drop the comments */
522 CreateComments(oid, classoid, 0, comment);
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.
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.
540 CommentRule(List *qualname, char *comment)
553 /* Separate relname and trig name */
554 nnames = list_length(qualname);
557 /* Old-style: only a rule name is given */
558 Relation RewriteRelation;
559 HeapScanDesc scanDesc;
560 ScanKeyData scanKeyData;
562 rulename = strVal(linitial(qualname));
564 /* Search pg_rewrite for such a rule */
565 ScanKeyInit(&scanKeyData,
566 Anum_pg_rewrite_rulename,
567 BTEqualStrategyNumber, F_NAMEEQ,
568 PointerGetDatum(rulename));
570 RewriteRelation = heap_openr(RewriteRelationName, AccessShareLock);
571 scanDesc = heap_beginscan(RewriteRelation, SnapshotNow,
574 tuple = heap_getnext(scanDesc, ForwardScanDirection);
575 if (HeapTupleIsValid(tuple))
577 reloid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
578 ruleoid = HeapTupleGetOid(tuple);
583 (errcode(ERRCODE_UNDEFINED_OBJECT),
584 errmsg("rule \"%s\" does not exist", rulename)));
585 reloid = ruleoid = 0; /* keep compiler quiet */
588 if (HeapTupleIsValid(tuple = heap_getnext(scanDesc,
589 ForwardScanDirection)))
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.")));
595 heap_endscan(scanDesc);
596 heap_close(RewriteRelation, AccessShareLock);
598 /* Open the owning relation to ensure it won't go away meanwhile */
599 relation = heap_open(reloid, AccessShareLock);
603 /* New-style: rule and relname both provided */
605 relname = list_truncate(list_copy(qualname), nnames - 1);
606 rulename = strVal(lfirst(list_tail(qualname)));
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);
613 /* Find the rule's pg_rewrite tuple, get its OID */
614 tuple = SearchSysCache(RULERELNAME,
615 ObjectIdGetDatum(reloid),
616 PointerGetDatum(rulename),
618 if (!HeapTupleIsValid(tuple))
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);
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));
634 /* pg_rewrite doesn't have a hard-coded OID, so must look it up */
635 classoid = get_system_catalog_relid(RewriteRelationName);
637 /* Call CreateComments() to create/drop the comments */
638 CreateComments(ruleoid, classoid, 0, comment);
640 heap_close(relation, NoLock);
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.
653 CommentType(List *typename, char *comment)
658 /* XXX a bit of a crock; should accept TypeName in COMMENT syntax */
659 tname = makeNode(TypeName);
660 tname->names = typename;
663 /* Find the type's oid */
665 oid = typenameTypeId(tname);
667 /* Check object security */
669 if (!pg_type_ownercheck(oid, GetUserId()))
670 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
671 TypeNameToString(tname));
673 /* Call CreateComments() to create/drop the comments */
675 CreateComments(oid, TypeRelationId, 0, comment);
679 * CommentAggregate --
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.
687 CommentAggregate(List *aggregate, List *arguments, char *comment)
689 TypeName *aggtype = (TypeName *) linitial(arguments);
693 /* First, attempt to determine the base aggregate oid */
695 baseoid = typenameTypeId(aggtype);
699 /* Now, attempt to find the actual tuple in pg_proc */
701 oid = find_aggregate_func(aggregate, baseoid, false);
703 /* Next, validate the user's attempt to comment */
705 if (!pg_proc_ownercheck(oid, GetUserId()))
706 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
707 NameListToString(aggregate));
709 /* Call CreateComments() to create/drop the comments */
711 CreateComments(oid, ProcedureRelationId, 0, comment);
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.
724 CommentProc(List *function, List *arguments, char *comment)
728 /* Look up the procedure */
730 oid = LookupFuncNameTypeNames(function, arguments, false);
732 /* Now, validate the user's ability to comment on this function */
734 if (!pg_proc_ownercheck(oid, GetUserId()))
735 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
736 NameListToString(function));
738 /* Call CreateComments() to create/drop the comments */
740 CreateComments(oid, ProcedureRelationId, 0, comment);
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
754 CommentOperator(List *opername, List *arguments, char *comment)
756 TypeName *typenode1 = (TypeName *) linitial(arguments);
757 TypeName *typenode2 = (TypeName *) lsecond(arguments);
761 /* Look up the operator */
762 oid = LookupOperNameTypeNames(opername, typenode1, typenode2, false);
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));
769 /* pg_operator doesn't have a hard-coded OID, so must look it up */
770 classoid = get_system_catalog_relid(OperatorRelationName);
772 /* Call CreateComments() to create/drop the comments */
773 CreateComments(oid, classoid, 0, comment);
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.
786 CommentTrigger(List *qualname, char *comment)
794 HeapTuple triggertuple;
796 ScanKeyData entry[2];
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)));
806 /* Open the owning relation to ensure it won't go away meanwhile */
807 rel = makeRangeVarFromNameList(relname);
808 relation = heap_openrv(rel, AccessShareLock);
810 /* Check object security */
812 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
813 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
814 RelationGetRelationName(relation));
817 * Fetch the trigger tuple from pg_trigger. There can be only one
818 * because of the unique index.
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);
833 /* If no trigger exists for the relation specified, notify user */
835 if (!HeapTupleIsValid(triggertuple))
837 (errcode(ERRCODE_UNDEFINED_OBJECT),
838 errmsg("trigger \"%s\" for table \"%s\" does not exist",
839 trigname, RelationGetRelationName(relation))));
841 oid = HeapTupleGetOid(triggertuple);
843 systable_endscan(scan);
845 /* Create the comment with the pg_trigger oid */
847 CreateComments(oid, RelationGetRelid(pg_trigger), 0, comment);
849 /* Done, but hold lock on relation */
851 heap_close(pg_trigger, AccessShareLock);
852 heap_close(relation, NoLock);
857 * CommentConstraint --
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.
864 CommentConstraint(List *qualname, char *comment)
870 Relation pg_constraint,
875 Oid conOid = InvalidOid;
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)));
884 /* Open the owning relation to ensure it won't go away meanwhile */
885 rel = makeRangeVarFromNameList(relName);
886 relation = heap_openrv(rel, AccessShareLock);
888 /* Check object security */
890 if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
891 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
892 RelationGetRelationName(relation));
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.
899 pg_constraint = heap_openr(ConstraintRelationName, AccessShareLock);
901 ScanKeyInit(&skey[0],
902 Anum_pg_constraint_conrelid,
903 BTEqualStrategyNumber, F_OIDEQ,
904 ObjectIdGetDatum(RelationGetRelid(relation)));
906 scan = systable_beginscan(pg_constraint, ConstraintRelidIndex, true,
907 SnapshotNow, 1, skey);
909 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
911 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
913 if (strcmp(NameStr(con->conname), conName) == 0)
915 if (OidIsValid(conOid))
917 (errcode(ERRCODE_DUPLICATE_OBJECT),
918 errmsg("table \"%s\" has multiple constraints named \"%s\"",
919 RelationGetRelationName(relation), conName)));
920 conOid = HeapTupleGetOid(tuple);
924 systable_endscan(scan);
926 /* If no constraint exists for the relation specified, notify user */
927 if (!OidIsValid(conOid))
929 (errcode(ERRCODE_UNDEFINED_OBJECT),
930 errmsg("constraint \"%s\" for table \"%s\" does not exist",
931 conName, RelationGetRelationName(relation))));
933 /* Create the comment with the pg_constraint oid */
934 CreateComments(conOid, RelationGetRelid(pg_constraint), 0, comment);
936 /* Done, but hold lock on relation */
937 heap_close(pg_constraint, AccessShareLock);
938 heap_close(relation, NoLock);
942 * CommentConversion --
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.
951 CommentConversion(List *qualname, char *comment)
956 conversionOid = FindConversionByName(qualname);
957 if (!OidIsValid(conversionOid))
959 (errcode(ERRCODE_UNDEFINED_OBJECT),
960 errmsg("conversion \"%s\" does not exist",
961 NameListToString(qualname))));
963 /* Check object security */
964 if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
965 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
966 NameListToString(qualname));
968 /* pg_conversion doesn't have a hard-coded OID, so must look it up */
969 classoid = get_system_catalog_relid(ConversionRelationName);
971 /* Call CreateComments() to create/drop the comments */
972 CreateComments(conversionOid, classoid, 0, comment);
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.
985 CommentLanguage(List *qualname, char *comment)
991 if (list_length(qualname) != 1)
993 (errcode(ERRCODE_SYNTAX_ERROR),
994 errmsg("language name may not be qualified")));
995 language = strVal(linitial(qualname));
997 oid = GetSysCacheOid(LANGNAME,
998 CStringGetDatum(language),
1000 if (!OidIsValid(oid))
1002 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1003 errmsg("language \"%s\" does not exist", language)));
1005 /* Check object security */
1008 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1009 errmsg("must be superuser to comment on procedural language")));
1011 /* pg_language doesn't have a hard-coded OID, so must look it up */
1012 classoid = get_system_catalog_relid(LanguageRelationName);
1014 /* Call CreateComments() to create/drop the comments */
1015 CreateComments(oid, classoid, 0, comment);
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).
1028 CommentOpClass(List *qualname, List *arguments, char *comment)
1038 Assert(list_length(arguments) == 1);
1039 amname = strVal(linitial(arguments));
1042 * Get the access method's OID.
1044 amID = GetSysCacheOid(AMNAME,
1045 CStringGetDatum(amname),
1047 if (!OidIsValid(amID))
1049 (errcode(ERRCODE_UNDEFINED_OBJECT),
1050 errmsg("access method \"%s\" does not exist",
1054 * Look up the opclass.
1057 /* deconstruct the name list */
1058 DeconstructQualifiedName(qualname, &schemaname, &opcname);
1062 /* Look in specific schema only */
1065 namespaceId = LookupExplicitNamespace(schemaname);
1066 tuple = SearchSysCache(CLAAMNAMENSP,
1067 ObjectIdGetDatum(amID),
1068 PointerGetDatum(opcname),
1069 ObjectIdGetDatum(namespaceId),
1074 /* Unqualified opclass name, so search the search path */
1075 opcID = OpclassnameGetOpcid(amID, opcname);
1076 if (!OidIsValid(opcID))
1078 (errcode(ERRCODE_UNDEFINED_OBJECT),
1079 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1081 tuple = SearchSysCache(CLAOID,
1082 ObjectIdGetDatum(opcID),
1086 if (!HeapTupleIsValid(tuple))
1088 (errcode(ERRCODE_UNDEFINED_OBJECT),
1089 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1090 NameListToString(qualname), amname)));
1092 opcID = HeapTupleGetOid(tuple);
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));
1099 ReleaseSysCache(tuple);
1101 /* pg_opclass doesn't have a hard-coded OID, so must look it up */
1102 classoid = get_system_catalog_relid(OperatorClassRelationName);
1104 /* Call CreateComments() to create/drop the comments */
1105 CreateComments(opcID, classoid, 0, comment);
1109 * CommentLargeObject --
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.
1118 CommentLargeObject(List *qualname, char *comment)
1124 Assert(list_length(qualname) == 1);
1125 node = (Node *) linitial(qualname);
1127 switch (nodeTag(node))
1130 loid = intVal(node);
1135 * Values too large for int4 will be represented as Float
1136 * constants by the lexer. Accept these if they are valid OID
1139 loid = DatumGetObjectId(DirectFunctionCall1(oidin,
1140 CStringGetDatum(strVal(node))));
1143 elog(ERROR, "unrecognized node type: %d",
1144 (int) nodeTag(node));
1145 /* keep compiler quiet */
1149 /* check that the large object exists */
1150 if (!LargeObjectExists(loid))
1152 (errcode(ERRCODE_UNDEFINED_OBJECT),
1153 errmsg("large object %u does not exist", loid)));
1155 /* pg_largeobject doesn't have a hard-coded OID, so must look it up */
1156 classoid = get_system_catalog_relid(LargeObjectRelationName);
1158 /* Call CreateComments() to create/drop the comments */
1159 CreateComments(loid, classoid, 0, comment);
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".
1173 CommentCast(List *qualname, List *arguments, char *comment)
1175 TypeName *sourcetype;
1176 TypeName *targettype;
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));
1190 sourcetypeid = typenameTypeId(sourcetype);
1191 if (!OidIsValid(sourcetypeid))
1193 (errcode(ERRCODE_UNDEFINED_OBJECT),
1194 errmsg("source data type %s does not exist",
1195 TypeNameToString(sourcetype))));
1197 targettypeid = typenameTypeId(targettype);
1198 if (!OidIsValid(targettypeid))
1200 (errcode(ERRCODE_UNDEFINED_OBJECT),
1201 errmsg("target data type %s does not exist",
1202 TypeNameToString(targettype))));
1204 tuple = SearchSysCache(CASTSOURCETARGET,
1205 ObjectIdGetDatum(sourcetypeid),
1206 ObjectIdGetDatum(targettypeid),
1208 if (!HeapTupleIsValid(tuple))
1210 (errcode(ERRCODE_UNDEFINED_OBJECT),
1211 errmsg("cast from type %s to type %s does not exist",
1212 TypeNameToString(sourcetype),
1213 TypeNameToString(targettype))));
1215 /* Get the OID of the cast */
1216 castOid = HeapTupleGetOid(tuple);
1218 /* Permission check */
1219 if (!pg_type_ownercheck(sourcetypeid, GetUserId())
1220 && !pg_type_ownercheck(targettypeid, GetUserId()))
1222 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1223 errmsg("must be owner of type %s or type %s",
1224 TypeNameToString(sourcetype),
1225 TypeNameToString(targettype))));
1227 ReleaseSysCache(tuple);
1229 /* pg_cast doesn't have a hard-coded OID, so must look it up */
1230 classoid = get_system_catalog_relid(CastRelationName);
1232 /* Call CreateComments() to create/drop the comments */
1233 CreateComments(castOid, classoid, 0, comment);