1 /*-------------------------------------------------------------------------
5 * PostgreSQL object comments utility code.
7 * Copyright (c) 1996-2008, PostgreSQL Global Development Group
12 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_authid.h"
21 #include "catalog/pg_cast.h"
22 #include "catalog/pg_constraint.h"
23 #include "catalog/pg_conversion.h"
24 #include "catalog/pg_database.h"
25 #include "catalog/pg_description.h"
26 #include "catalog/pg_language.h"
27 #include "catalog/pg_largeobject.h"
28 #include "catalog/pg_namespace.h"
29 #include "catalog/pg_opclass.h"
30 #include "catalog/pg_operator.h"
31 #include "catalog/pg_opfamily.h"
32 #include "catalog/pg_proc.h"
33 #include "catalog/pg_rewrite.h"
34 #include "catalog/pg_shdescription.h"
35 #include "catalog/pg_tablespace.h"
36 #include "catalog/pg_trigger.h"
37 #include "catalog/pg_ts_config.h"
38 #include "catalog/pg_ts_dict.h"
39 #include "catalog/pg_ts_parser.h"
40 #include "catalog/pg_ts_template.h"
41 #include "catalog/pg_type.h"
42 #include "commands/comment.h"
43 #include "commands/dbcommands.h"
44 #include "commands/tablespace.h"
45 #include "miscadmin.h"
46 #include "nodes/makefuncs.h"
47 #include "parser/parse_func.h"
48 #include "parser/parse_oper.h"
49 #include "parser/parse_type.h"
50 #include "utils/acl.h"
51 #include "utils/builtins.h"
52 #include "utils/fmgroids.h"
53 #include "utils/lsyscache.h"
54 #include "utils/rel.h"
55 #include "utils/syscache.h"
56 #include "utils/tqual.h"
60 * Static Function Prototypes --
62 * The following prototypes are declared static so as not to conflict
63 * with any other routines outside this module. These routines are
64 * called by the public function CommentObject() routine to create
65 * the appropriate comment for the specific object type.
68 static void CommentRelation(int objtype
, List
*relname
, char *comment
);
69 static void CommentAttribute(List
*qualname
, char *comment
);
70 static void CommentDatabase(List
*qualname
, char *comment
);
71 static void CommentNamespace(List
*qualname
, char *comment
);
72 static void CommentRule(List
*qualname
, char *comment
);
73 static void CommentType(List
*typename
, char *comment
);
74 static void CommentAggregate(List
*aggregate
, List
*arguments
, char *comment
);
75 static void CommentProc(List
*function
, List
*arguments
, char *comment
);
76 static void CommentOperator(List
*opername
, List
*arguments
, char *comment
);
77 static void CommentTrigger(List
*qualname
, char *comment
);
78 static void CommentConstraint(List
*qualname
, char *comment
);
79 static void CommentConversion(List
*qualname
, char *comment
);
80 static void CommentLanguage(List
*qualname
, char *comment
);
81 static void CommentOpClass(List
*qualname
, List
*arguments
, char *comment
);
82 static void CommentOpFamily(List
*qualname
, List
*arguments
, char *comment
);
83 static void CommentLargeObject(List
*qualname
, char *comment
);
84 static void CommentCast(List
*qualname
, List
*arguments
, char *comment
);
85 static void CommentTablespace(List
*qualname
, char *comment
);
86 static void CommentRole(List
*qualname
, char *comment
);
87 static void CommentTSParser(List
*qualname
, char *comment
);
88 static void CommentTSDictionary(List
*qualname
, char *comment
);
89 static void CommentTSTemplate(List
*qualname
, char *comment
);
90 static void CommentTSConfiguration(List
*qualname
, char *comment
);
96 * This routine is used to add the associated comment into
97 * pg_description for the object specified by the given SQL command.
100 CommentObject(CommentStmt
*stmt
)
102 switch (stmt
->objtype
)
105 case OBJECT_SEQUENCE
:
108 CommentRelation(stmt
->objtype
, stmt
->objname
, stmt
->comment
);
111 CommentAttribute(stmt
->objname
, stmt
->comment
);
113 case OBJECT_DATABASE
:
114 CommentDatabase(stmt
->objname
, stmt
->comment
);
117 CommentRule(stmt
->objname
, stmt
->comment
);
120 CommentType(stmt
->objname
, stmt
->comment
);
122 case OBJECT_AGGREGATE
:
123 CommentAggregate(stmt
->objname
, stmt
->objargs
, stmt
->comment
);
125 case OBJECT_FUNCTION
:
126 CommentProc(stmt
->objname
, stmt
->objargs
, stmt
->comment
);
128 case OBJECT_OPERATOR
:
129 CommentOperator(stmt
->objname
, stmt
->objargs
, stmt
->comment
);
132 CommentTrigger(stmt
->objname
, stmt
->comment
);
135 CommentNamespace(stmt
->objname
, stmt
->comment
);
137 case OBJECT_CONSTRAINT
:
138 CommentConstraint(stmt
->objname
, stmt
->comment
);
140 case OBJECT_CONVERSION
:
141 CommentConversion(stmt
->objname
, stmt
->comment
);
143 case OBJECT_LANGUAGE
:
144 CommentLanguage(stmt
->objname
, stmt
->comment
);
147 CommentOpClass(stmt
->objname
, stmt
->objargs
, stmt
->comment
);
149 case OBJECT_OPFAMILY
:
150 CommentOpFamily(stmt
->objname
, stmt
->objargs
, stmt
->comment
);
152 case OBJECT_LARGEOBJECT
:
153 CommentLargeObject(stmt
->objname
, stmt
->comment
);
156 CommentCast(stmt
->objname
, stmt
->objargs
, stmt
->comment
);
158 case OBJECT_TABLESPACE
:
159 CommentTablespace(stmt
->objname
, stmt
->comment
);
162 CommentRole(stmt
->objname
, stmt
->comment
);
164 case OBJECT_TSPARSER
:
165 CommentTSParser(stmt
->objname
, stmt
->comment
);
167 case OBJECT_TSDICTIONARY
:
168 CommentTSDictionary(stmt
->objname
, stmt
->comment
);
170 case OBJECT_TSTEMPLATE
:
171 CommentTSTemplate(stmt
->objname
, stmt
->comment
);
173 case OBJECT_TSCONFIGURATION
:
174 CommentTSConfiguration(stmt
->objname
, stmt
->comment
);
177 elog(ERROR
, "unrecognized object type: %d",
178 (int) stmt
->objtype
);
185 * Create a comment for the specified object descriptor. Inserts a new
186 * pg_description tuple, or replaces an existing one with the same key.
188 * If the comment given is null or an empty string, instead delete any
189 * existing comment for the specified key.
192 CreateComments(Oid oid
, Oid classoid
, int32 subid
, char *comment
)
194 Relation description
;
198 HeapTuple newtuple
= NULL
;
199 Datum values
[Natts_pg_description
];
200 bool nulls
[Natts_pg_description
];
201 bool replaces
[Natts_pg_description
];
204 /* Reduce empty-string to NULL case */
205 if (comment
!= NULL
&& strlen(comment
) == 0)
208 /* Prepare to form or update a tuple, if necessary */
211 for (i
= 0; i
< Natts_pg_description
; i
++)
217 values
[i
++] = ObjectIdGetDatum(oid
);
218 values
[i
++] = ObjectIdGetDatum(classoid
);
219 values
[i
++] = Int32GetDatum(subid
);
220 values
[i
++] = CStringGetTextDatum(comment
);
223 /* Use the index to search for a matching old tuple */
225 ScanKeyInit(&skey
[0],
226 Anum_pg_description_objoid
,
227 BTEqualStrategyNumber
, F_OIDEQ
,
228 ObjectIdGetDatum(oid
));
229 ScanKeyInit(&skey
[1],
230 Anum_pg_description_classoid
,
231 BTEqualStrategyNumber
, F_OIDEQ
,
232 ObjectIdGetDatum(classoid
));
233 ScanKeyInit(&skey
[2],
234 Anum_pg_description_objsubid
,
235 BTEqualStrategyNumber
, F_INT4EQ
,
236 Int32GetDatum(subid
));
238 description
= heap_open(DescriptionRelationId
, RowExclusiveLock
);
240 sd
= systable_beginscan(description
, DescriptionObjIndexId
, true,
241 SnapshotNow
, 3, skey
);
243 while ((oldtuple
= systable_getnext(sd
)) != NULL
)
245 /* Found the old tuple, so delete or update it */
248 simple_heap_delete(description
, &oldtuple
->t_self
);
251 newtuple
= heap_modify_tuple(oldtuple
, RelationGetDescr(description
), values
,
253 simple_heap_update(description
, &oldtuple
->t_self
, newtuple
);
256 break; /* Assume there can be only one match */
259 systable_endscan(sd
);
261 /* If we didn't find an old tuple, insert a new one */
263 if (newtuple
== NULL
&& comment
!= NULL
)
265 newtuple
= heap_form_tuple(RelationGetDescr(description
),
267 simple_heap_insert(description
, newtuple
);
270 /* Update indexes, if necessary */
271 if (newtuple
!= NULL
)
273 CatalogUpdateIndexes(description
, newtuple
);
274 heap_freetuple(newtuple
);
279 heap_close(description
, NoLock
);
283 * CreateSharedComments --
285 * Create a comment for the specified shared object descriptor. Inserts a
286 * new pg_shdescription tuple, or replaces an existing one with the same key.
288 * If the comment given is null or an empty string, instead delete any
289 * existing comment for the specified key.
292 CreateSharedComments(Oid oid
, Oid classoid
, char *comment
)
294 Relation shdescription
;
298 HeapTuple newtuple
= NULL
;
299 Datum values
[Natts_pg_shdescription
];
300 bool nulls
[Natts_pg_shdescription
];
301 bool replaces
[Natts_pg_shdescription
];
304 /* Reduce empty-string to NULL case */
305 if (comment
!= NULL
&& strlen(comment
) == 0)
308 /* Prepare to form or update a tuple, if necessary */
311 for (i
= 0; i
< Natts_pg_shdescription
; i
++)
317 values
[i
++] = ObjectIdGetDatum(oid
);
318 values
[i
++] = ObjectIdGetDatum(classoid
);
319 values
[i
++] = CStringGetTextDatum(comment
);
322 /* Use the index to search for a matching old tuple */
324 ScanKeyInit(&skey
[0],
325 Anum_pg_shdescription_objoid
,
326 BTEqualStrategyNumber
, F_OIDEQ
,
327 ObjectIdGetDatum(oid
));
328 ScanKeyInit(&skey
[1],
329 Anum_pg_shdescription_classoid
,
330 BTEqualStrategyNumber
, F_OIDEQ
,
331 ObjectIdGetDatum(classoid
));
333 shdescription
= heap_open(SharedDescriptionRelationId
, RowExclusiveLock
);
335 sd
= systable_beginscan(shdescription
, SharedDescriptionObjIndexId
, true,
336 SnapshotNow
, 2, skey
);
338 while ((oldtuple
= systable_getnext(sd
)) != NULL
)
340 /* Found the old tuple, so delete or update it */
343 simple_heap_delete(shdescription
, &oldtuple
->t_self
);
346 newtuple
= heap_modify_tuple(oldtuple
, RelationGetDescr(shdescription
),
347 values
, nulls
, replaces
);
348 simple_heap_update(shdescription
, &oldtuple
->t_self
, newtuple
);
351 break; /* Assume there can be only one match */
354 systable_endscan(sd
);
356 /* If we didn't find an old tuple, insert a new one */
358 if (newtuple
== NULL
&& comment
!= NULL
)
360 newtuple
= heap_form_tuple(RelationGetDescr(shdescription
),
362 simple_heap_insert(shdescription
, newtuple
);
365 /* Update indexes, if necessary */
366 if (newtuple
!= NULL
)
368 CatalogUpdateIndexes(shdescription
, newtuple
);
369 heap_freetuple(newtuple
);
374 heap_close(shdescription
, NoLock
);
378 * DeleteComments -- remove comments for an object
380 * If subid is nonzero then only comments matching it will be removed.
381 * If subid is zero, all comments matching the oid/classoid will be removed
382 * (this corresponds to deleting a whole object).
385 DeleteComments(Oid oid
, Oid classoid
, int32 subid
)
387 Relation description
;
393 /* Use the index to search for all matching old tuples */
395 ScanKeyInit(&skey
[0],
396 Anum_pg_description_objoid
,
397 BTEqualStrategyNumber
, F_OIDEQ
,
398 ObjectIdGetDatum(oid
));
399 ScanKeyInit(&skey
[1],
400 Anum_pg_description_classoid
,
401 BTEqualStrategyNumber
, F_OIDEQ
,
402 ObjectIdGetDatum(classoid
));
406 ScanKeyInit(&skey
[2],
407 Anum_pg_description_objsubid
,
408 BTEqualStrategyNumber
, F_INT4EQ
,
409 Int32GetDatum(subid
));
415 description
= heap_open(DescriptionRelationId
, RowExclusiveLock
);
417 sd
= systable_beginscan(description
, DescriptionObjIndexId
, true,
418 SnapshotNow
, nkeys
, skey
);
420 while ((oldtuple
= systable_getnext(sd
)) != NULL
)
421 simple_heap_delete(description
, &oldtuple
->t_self
);
425 systable_endscan(sd
);
426 heap_close(description
, RowExclusiveLock
);
430 * DeleteSharedComments -- remove comments for a shared object
433 DeleteSharedComments(Oid oid
, Oid classoid
)
435 Relation shdescription
;
440 /* Use the index to search for all matching old tuples */
442 ScanKeyInit(&skey
[0],
443 Anum_pg_shdescription_objoid
,
444 BTEqualStrategyNumber
, F_OIDEQ
,
445 ObjectIdGetDatum(oid
));
446 ScanKeyInit(&skey
[1],
447 Anum_pg_shdescription_classoid
,
448 BTEqualStrategyNumber
, F_OIDEQ
,
449 ObjectIdGetDatum(classoid
));
451 shdescription
= heap_open(SharedDescriptionRelationId
, RowExclusiveLock
);
453 sd
= systable_beginscan(shdescription
, SharedDescriptionObjIndexId
, true,
454 SnapshotNow
, 2, skey
);
456 while ((oldtuple
= systable_getnext(sd
)) != NULL
)
457 simple_heap_delete(shdescription
, &oldtuple
->t_self
);
461 systable_endscan(sd
);
462 heap_close(shdescription
, RowExclusiveLock
);
468 * This routine is used to add/drop a comment from a relation, where
469 * a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
470 * finds the relation name by searching the system cache, locating
471 * the appropriate tuple, and inserting a comment using that
472 * tuple's oid. Its parameters are the relation name and comments.
475 CommentRelation(int objtype
, List
*relname
, char *comment
)
480 tgtrel
= makeRangeVarFromNameList(relname
);
483 * Open the relation. We do this mainly to acquire a lock that ensures no
484 * one else drops the relation before we commit. (If they did, they'd
485 * fail to remove the entry we are about to make in pg_description.)
487 relation
= relation_openrv(tgtrel
, AccessShareLock
);
489 /* Check object security */
490 if (!pg_class_ownercheck(RelationGetRelid(relation
), GetUserId()))
491 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
492 RelationGetRelationName(relation
));
494 /* Next, verify that the relation type matches the intent */
499 if (relation
->rd_rel
->relkind
!= RELKIND_INDEX
)
501 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
502 errmsg("\"%s\" is not an index",
503 RelationGetRelationName(relation
))));
505 case OBJECT_SEQUENCE
:
506 if (relation
->rd_rel
->relkind
!= RELKIND_SEQUENCE
)
508 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
509 errmsg("\"%s\" is not a sequence",
510 RelationGetRelationName(relation
))));
513 if (relation
->rd_rel
->relkind
!= RELKIND_RELATION
)
515 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
516 errmsg("\"%s\" is not a table",
517 RelationGetRelationName(relation
))));
520 if (relation
->rd_rel
->relkind
!= RELKIND_VIEW
)
522 (errcode(ERRCODE_WRONG_OBJECT_TYPE
),
523 errmsg("\"%s\" is not a view",
524 RelationGetRelationName(relation
))));
528 /* Create the comment using the relation's oid */
529 CreateComments(RelationGetRelid(relation
), RelationRelationId
,
532 /* Done, but hold lock until commit */
533 relation_close(relation
, NoLock
);
537 * CommentAttribute --
539 * This routine is used to add/drop a comment from an attribute
540 * such as a table's column. The routine will check security
541 * restrictions and then attempt to look up the specified
542 * attribute. If successful, a comment is added/dropped, else an
543 * ereport() exception is thrown. The parameters are the relation
544 * and attribute names, and the comment
547 CommentAttribute(List
*qualname
, char *comment
)
556 /* Separate relname and attr name */
557 nnames
= list_length(qualname
);
558 if (nnames
< 2) /* parser messed up */
559 elog(ERROR
, "must specify relation and attribute");
560 relname
= list_truncate(list_copy(qualname
), nnames
- 1);
561 attrname
= strVal(lfirst(list_tail(qualname
)));
563 /* Open the containing relation to ensure it won't go away meanwhile */
564 rel
= makeRangeVarFromNameList(relname
);
565 relation
= relation_openrv(rel
, AccessShareLock
);
567 /* Check object security */
569 if (!pg_class_ownercheck(RelationGetRelid(relation
), GetUserId()))
570 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
571 RelationGetRelationName(relation
));
573 /* Now, fetch the attribute number from the system cache */
575 attnum
= get_attnum(RelationGetRelid(relation
), attrname
);
576 if (attnum
== InvalidAttrNumber
)
578 (errcode(ERRCODE_UNDEFINED_COLUMN
),
579 errmsg("column \"%s\" of relation \"%s\" does not exist",
580 attrname
, RelationGetRelationName(relation
))));
582 /* Create the comment using the relation's oid */
583 CreateComments(RelationGetRelid(relation
), RelationRelationId
,
584 (int32
) attnum
, comment
);
586 /* Done, but hold lock until commit */
588 relation_close(relation
, NoLock
);
594 * This routine is used to add/drop any user-comments a user might
595 * have regarding the specified database. The routine will check
596 * security for owner permissions, and, if successful, will then
597 * attempt to find the oid of the database specified. Once found,
598 * a comment is added/dropped using the CreateSharedComments() routine.
601 CommentDatabase(List
*qualname
, char *comment
)
606 if (list_length(qualname
) != 1)
608 (errcode(ERRCODE_SYNTAX_ERROR
),
609 errmsg("database name cannot be qualified")));
610 database
= strVal(linitial(qualname
));
613 * When loading a dump, we may see a COMMENT ON DATABASE for the old name
614 * of the database. Erroring out would prevent pg_restore from completing
615 * (which is really pg_restore's fault, but for now we will work around
616 * the problem here). Consensus is that the best fix is to treat wrong
617 * database name as a WARNING not an ERROR.
620 /* First get the database OID */
621 oid
= get_database_oid(database
);
622 if (!OidIsValid(oid
))
625 (errcode(ERRCODE_UNDEFINED_DATABASE
),
626 errmsg("database \"%s\" does not exist", database
)));
630 /* Check object security */
631 if (!pg_database_ownercheck(oid
, GetUserId()))
632 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_DATABASE
,
635 /* Call CreateSharedComments() to create/drop the comments */
636 CreateSharedComments(oid
, DatabaseRelationId
, comment
);
640 * CommentTablespace --
642 * This routine is used to add/drop any user-comments a user might
643 * have regarding a tablespace. The tablepace is specified by name
644 * and, if found, and the user has appropriate permissions, a
645 * comment will be added/dropped using the CreateSharedComments() routine.
649 CommentTablespace(List
*qualname
, char *comment
)
654 if (list_length(qualname
) != 1)
656 (errcode(ERRCODE_SYNTAX_ERROR
),
657 errmsg("tablespace name cannot be qualified")));
658 tablespace
= strVal(linitial(qualname
));
660 oid
= get_tablespace_oid(tablespace
);
661 if (!OidIsValid(oid
))
664 (errcode(ERRCODE_UNDEFINED_OBJECT
),
665 errmsg("tablespace \"%s\" does not exist", tablespace
)));
669 /* Check object security */
670 if (!pg_tablespace_ownercheck(oid
, GetUserId()))
671 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TABLESPACE
, tablespace
);
673 /* Call CreateSharedComments() to create/drop the comments */
674 CreateSharedComments(oid
, TableSpaceRelationId
, comment
);
680 * This routine is used to add/drop any user-comments a user might
681 * have regarding a role. The role is specified by name
682 * and, if found, and the user has appropriate permissions, a
683 * comment will be added/dropped using the CreateSharedComments() routine.
686 CommentRole(List
*qualname
, char *comment
)
691 if (list_length(qualname
) != 1)
693 (errcode(ERRCODE_SYNTAX_ERROR
),
694 errmsg("role name cannot be qualified")));
695 role
= strVal(linitial(qualname
));
697 oid
= get_roleid_checked(role
);
699 /* Check object security */
700 if (!has_privs_of_role(GetUserId(), oid
))
702 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
703 errmsg("must be member of role \"%s\" to comment upon it", role
)));
705 /* Call CreateSharedComments() to create/drop the comments */
706 CreateSharedComments(oid
, AuthIdRelationId
, comment
);
710 * CommentNamespace --
712 * This routine is used to add/drop any user-comments a user might
713 * have regarding the specified namespace. The routine will check
714 * security for owner permissions, and, if successful, will then
715 * attempt to find the oid of the namespace specified. Once found,
716 * a comment is added/dropped using the CreateComments() routine.
719 CommentNamespace(List
*qualname
, char *comment
)
724 if (list_length(qualname
) != 1)
726 (errcode(ERRCODE_SYNTAX_ERROR
),
727 errmsg("schema name cannot be qualified")));
728 namespace = strVal(linitial(qualname
));
730 oid
= GetSysCacheOid(NAMESPACENAME
,
731 CStringGetDatum(namespace),
733 if (!OidIsValid(oid
))
735 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
736 errmsg("schema \"%s\" does not exist", namespace)));
738 /* Check object security */
739 if (!pg_namespace_ownercheck(oid
, GetUserId()))
740 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_NAMESPACE
,
743 /* Call CreateComments() to create/drop the comments */
744 CreateComments(oid
, NamespaceRelationId
, 0, comment
);
750 * This routine is used to add/drop any user-comments a user might
751 * have regarding a specified RULE. The rule for commenting is determined by
752 * both its name and the relation to which it refers. The arguments to this
753 * function are the rule name and relation name (merged into a qualified
754 * name), and the comment to add/drop.
756 * Before PG 7.3, rules had unique names across the whole database, and so
757 * the syntax was just COMMENT ON RULE rulename, with no relation name.
758 * For purposes of backwards compatibility, we support that as long as there
759 * is only one rule by the specified name in the database.
762 CommentRule(List
*qualname
, char *comment
)
773 /* Separate relname and trig name */
774 nnames
= list_length(qualname
);
777 /* Old-style: only a rule name is given */
778 Relation RewriteRelation
;
779 HeapScanDesc scanDesc
;
780 ScanKeyData scanKeyData
;
782 rulename
= strVal(linitial(qualname
));
784 /* Search pg_rewrite for such a rule */
785 ScanKeyInit(&scanKeyData
,
786 Anum_pg_rewrite_rulename
,
787 BTEqualStrategyNumber
, F_NAMEEQ
,
788 PointerGetDatum(rulename
));
790 RewriteRelation
= heap_open(RewriteRelationId
, AccessShareLock
);
791 scanDesc
= heap_beginscan(RewriteRelation
, SnapshotNow
,
794 tuple
= heap_getnext(scanDesc
, ForwardScanDirection
);
795 if (HeapTupleIsValid(tuple
))
797 reloid
= ((Form_pg_rewrite
) GETSTRUCT(tuple
))->ev_class
;
798 ruleoid
= HeapTupleGetOid(tuple
);
803 (errcode(ERRCODE_UNDEFINED_OBJECT
),
804 errmsg("rule \"%s\" does not exist", rulename
)));
805 reloid
= ruleoid
= 0; /* keep compiler quiet */
808 if (HeapTupleIsValid(tuple
= heap_getnext(scanDesc
,
809 ForwardScanDirection
)))
811 (errcode(ERRCODE_DUPLICATE_OBJECT
),
812 errmsg("there are multiple rules named \"%s\"", rulename
),
813 errhint("Specify a relation name as well as a rule name.")));
815 heap_endscan(scanDesc
);
816 heap_close(RewriteRelation
, AccessShareLock
);
818 /* Open the owning relation to ensure it won't go away meanwhile */
819 relation
= heap_open(reloid
, AccessShareLock
);
823 /* New-style: rule and relname both provided */
825 relname
= list_truncate(list_copy(qualname
), nnames
- 1);
826 rulename
= strVal(lfirst(list_tail(qualname
)));
828 /* Open the owning relation to ensure it won't go away meanwhile */
829 rel
= makeRangeVarFromNameList(relname
);
830 relation
= heap_openrv(rel
, AccessShareLock
);
831 reloid
= RelationGetRelid(relation
);
833 /* Find the rule's pg_rewrite tuple, get its OID */
834 tuple
= SearchSysCache(RULERELNAME
,
835 ObjectIdGetDatum(reloid
),
836 PointerGetDatum(rulename
),
838 if (!HeapTupleIsValid(tuple
))
840 (errcode(ERRCODE_UNDEFINED_OBJECT
),
841 errmsg("rule \"%s\" for relation \"%s\" does not exist",
842 rulename
, RelationGetRelationName(relation
))));
843 Assert(reloid
== ((Form_pg_rewrite
) GETSTRUCT(tuple
))->ev_class
);
844 ruleoid
= HeapTupleGetOid(tuple
);
845 ReleaseSysCache(tuple
);
848 /* Check object security */
849 if (!pg_class_ownercheck(reloid
, GetUserId()))
850 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
851 get_rel_name(reloid
));
853 /* Call CreateComments() to create/drop the comments */
854 CreateComments(ruleoid
, RewriteRelationId
, 0, comment
);
856 heap_close(relation
, NoLock
);
862 * This routine is used to add/drop any user-comments a user might
863 * have regarding a TYPE. The type is specified by name
864 * and, if found, and the user has appropriate permissions, a
865 * comment will be added/dropped using the CreateComments() routine.
866 * The type's name and the comments are the parameters to this routine.
869 CommentType(List
*typename
, char *comment
)
874 /* XXX a bit of a crock; should accept TypeName in COMMENT syntax */
875 tname
= makeTypeNameFromNameList(typename
);
877 /* Find the type's oid */
879 oid
= typenameTypeId(NULL
, tname
, NULL
);
881 /* Check object security */
883 if (!pg_type_ownercheck(oid
, GetUserId()))
884 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TYPE
,
885 format_type_be(oid
));
887 /* Call CreateComments() to create/drop the comments */
888 CreateComments(oid
, TypeRelationId
, 0, comment
);
892 * CommentAggregate --
894 * This routine is used to allow a user to provide comments on an
895 * aggregate function. The aggregate function is determined by both
896 * its name and its argument type(s).
899 CommentAggregate(List
*aggregate
, List
*arguments
, char *comment
)
903 /* Look up function and make sure it's an aggregate */
904 oid
= LookupAggNameTypeNames(aggregate
, arguments
, false);
906 /* Next, validate the user's attempt to comment */
907 if (!pg_proc_ownercheck(oid
, GetUserId()))
908 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_PROC
,
909 NameListToString(aggregate
));
911 /* Call CreateComments() to create/drop the comments */
912 CreateComments(oid
, ProcedureRelationId
, 0, comment
);
918 * This routine is used to allow a user to provide comments on an
919 * procedure (function). The procedure is determined by both
920 * its name and its argument list. The argument list is expected to
921 * be a series of parsed nodes pointed to by a List object. If the
922 * comments string is empty, the associated comment is dropped.
925 CommentProc(List
*function
, List
*arguments
, char *comment
)
929 /* Look up the procedure */
931 oid
= LookupFuncNameTypeNames(function
, arguments
, false);
933 /* Now, validate the user's ability to comment on this function */
935 if (!pg_proc_ownercheck(oid
, GetUserId()))
936 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_PROC
,
937 NameListToString(function
));
939 /* Call CreateComments() to create/drop the comments */
940 CreateComments(oid
, ProcedureRelationId
, 0, comment
);
946 * This routine is used to allow a user to provide comments on an
947 * operator. The operator for commenting is determined by both
948 * its name and its argument list which defines the left and right
949 * hand types the operator will operate on. The argument list is
950 * expected to be a couple of parse nodes pointed to be a List
954 CommentOperator(List
*opername
, List
*arguments
, char *comment
)
956 TypeName
*typenode1
= (TypeName
*) linitial(arguments
);
957 TypeName
*typenode2
= (TypeName
*) lsecond(arguments
);
960 /* Look up the operator */
961 oid
= LookupOperNameTypeNames(NULL
, opername
,
962 typenode1
, typenode2
,
965 /* Check user's privilege to comment on this operator */
966 if (!pg_oper_ownercheck(oid
, GetUserId()))
967 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_OPER
,
968 NameListToString(opername
));
970 /* Call CreateComments() to create/drop the comments */
971 CreateComments(oid
, OperatorRelationId
, 0, comment
);
977 * This routine is used to allow a user to provide comments on a
978 * trigger event. The trigger for commenting is determined by both
979 * its name and the relation to which it refers. The arguments to this
980 * function are the trigger name and relation name (merged into a qualified
981 * name), and the comment to add/drop.
984 CommentTrigger(List
*qualname
, char *comment
)
992 HeapTuple triggertuple
;
994 ScanKeyData entry
[2];
997 /* Separate relname and trig name */
998 nnames
= list_length(qualname
);
999 if (nnames
< 2) /* parser messed up */
1000 elog(ERROR
, "must specify relation and trigger");
1001 relname
= list_truncate(list_copy(qualname
), nnames
- 1);
1002 trigname
= strVal(lfirst(list_tail(qualname
)));
1004 /* Open the owning relation to ensure it won't go away meanwhile */
1005 rel
= makeRangeVarFromNameList(relname
);
1006 relation
= heap_openrv(rel
, AccessShareLock
);
1008 /* Check object security */
1010 if (!pg_class_ownercheck(RelationGetRelid(relation
), GetUserId()))
1011 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
1012 RelationGetRelationName(relation
));
1015 * Fetch the trigger tuple from pg_trigger. There can be only one because
1016 * of the unique index.
1018 pg_trigger
= heap_open(TriggerRelationId
, AccessShareLock
);
1019 ScanKeyInit(&entry
[0],
1020 Anum_pg_trigger_tgrelid
,
1021 BTEqualStrategyNumber
, F_OIDEQ
,
1022 ObjectIdGetDatum(RelationGetRelid(relation
)));
1023 ScanKeyInit(&entry
[1],
1024 Anum_pg_trigger_tgname
,
1025 BTEqualStrategyNumber
, F_NAMEEQ
,
1026 CStringGetDatum(trigname
));
1027 scan
= systable_beginscan(pg_trigger
, TriggerRelidNameIndexId
, true,
1028 SnapshotNow
, 2, entry
);
1029 triggertuple
= systable_getnext(scan
);
1031 /* If no trigger exists for the relation specified, notify user */
1033 if (!HeapTupleIsValid(triggertuple
))
1035 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1036 errmsg("trigger \"%s\" for table \"%s\" does not exist",
1037 trigname
, RelationGetRelationName(relation
))));
1039 oid
= HeapTupleGetOid(triggertuple
);
1041 systable_endscan(scan
);
1043 /* Call CreateComments() to create/drop the comments */
1044 CreateComments(oid
, TriggerRelationId
, 0, comment
);
1046 /* Done, but hold lock on relation */
1048 heap_close(pg_trigger
, AccessShareLock
);
1049 heap_close(relation
, NoLock
);
1054 * CommentConstraint --
1056 * Enable commenting on constraints held within the pg_constraint
1057 * table. A qualified name is required as constraint names are
1058 * unique per relation.
1061 CommentConstraint(List
*qualname
, char *comment
)
1067 Relation pg_constraint
,
1071 ScanKeyData skey
[1];
1072 Oid conOid
= InvalidOid
;
1074 /* Separate relname and constraint name */
1075 nnames
= list_length(qualname
);
1076 if (nnames
< 2) /* parser messed up */
1077 elog(ERROR
, "must specify relation and constraint");
1078 relName
= list_truncate(list_copy(qualname
), nnames
- 1);
1079 conName
= strVal(lfirst(list_tail(qualname
)));
1081 /* Open the owning relation to ensure it won't go away meanwhile */
1082 rel
= makeRangeVarFromNameList(relName
);
1083 relation
= heap_openrv(rel
, AccessShareLock
);
1085 /* Check object security */
1087 if (!pg_class_ownercheck(RelationGetRelid(relation
), GetUserId()))
1088 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CLASS
,
1089 RelationGetRelationName(relation
));
1092 * Fetch the constraint tuple from pg_constraint. There may be more than
1093 * one match, because constraints are not required to have unique names;
1096 pg_constraint
= heap_open(ConstraintRelationId
, AccessShareLock
);
1098 ScanKeyInit(&skey
[0],
1099 Anum_pg_constraint_conrelid
,
1100 BTEqualStrategyNumber
, F_OIDEQ
,
1101 ObjectIdGetDatum(RelationGetRelid(relation
)));
1103 scan
= systable_beginscan(pg_constraint
, ConstraintRelidIndexId
, true,
1104 SnapshotNow
, 1, skey
);
1106 while (HeapTupleIsValid(tuple
= systable_getnext(scan
)))
1108 Form_pg_constraint con
= (Form_pg_constraint
) GETSTRUCT(tuple
);
1110 if (strcmp(NameStr(con
->conname
), conName
) == 0)
1112 if (OidIsValid(conOid
))
1114 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1115 errmsg("table \"%s\" has multiple constraints named \"%s\"",
1116 RelationGetRelationName(relation
), conName
)));
1117 conOid
= HeapTupleGetOid(tuple
);
1121 systable_endscan(scan
);
1123 /* If no constraint exists for the relation specified, notify user */
1124 if (!OidIsValid(conOid
))
1126 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1127 errmsg("constraint \"%s\" for table \"%s\" does not exist",
1128 conName
, RelationGetRelationName(relation
))));
1130 /* Call CreateComments() to create/drop the comments */
1131 CreateComments(conOid
, ConstraintRelationId
, 0, comment
);
1133 /* Done, but hold lock on relation */
1134 heap_close(pg_constraint
, AccessShareLock
);
1135 heap_close(relation
, NoLock
);
1139 * CommentConversion --
1141 * This routine is used to add/drop any user-comments a user might
1142 * have regarding a CONVERSION. The conversion is specified by name
1143 * and, if found, and the user has appropriate permissions, a
1144 * comment will be added/dropped using the CreateComments() routine.
1145 * The conversion's name and the comment are the parameters to this routine.
1148 CommentConversion(List
*qualname
, char *comment
)
1152 conversionOid
= FindConversionByName(qualname
);
1153 if (!OidIsValid(conversionOid
))
1155 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1156 errmsg("conversion \"%s\" does not exist",
1157 NameListToString(qualname
))));
1159 /* Check object security */
1160 if (!pg_conversion_ownercheck(conversionOid
, GetUserId()))
1161 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_CONVERSION
,
1162 NameListToString(qualname
));
1164 /* Call CreateComments() to create/drop the comments */
1165 CreateComments(conversionOid
, ConversionRelationId
, 0, comment
);
1169 * CommentLanguage --
1171 * This routine is used to add/drop any user-comments a user might
1172 * have regarding a LANGUAGE. The language is specified by name
1173 * and, if found, and the user has appropriate permissions, a
1174 * comment will be added/dropped using the CreateComments() routine.
1175 * The language's name and the comment are the parameters to this routine.
1178 CommentLanguage(List
*qualname
, char *comment
)
1183 if (list_length(qualname
) != 1)
1185 (errcode(ERRCODE_SYNTAX_ERROR
),
1186 errmsg("language name cannot be qualified")));
1187 language
= strVal(linitial(qualname
));
1189 oid
= GetSysCacheOid(LANGNAME
,
1190 CStringGetDatum(language
),
1192 if (!OidIsValid(oid
))
1194 (errcode(ERRCODE_UNDEFINED_SCHEMA
),
1195 errmsg("language \"%s\" does not exist", language
)));
1197 /* Check object security */
1200 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1201 errmsg("must be superuser to comment on procedural language")));
1203 /* Call CreateComments() to create/drop the comments */
1204 CreateComments(oid
, LanguageRelationId
, 0, comment
);
1210 * This routine is used to allow a user to provide comments on an
1211 * operator class. The operator class for commenting is determined by both
1212 * its name and its argument list which defines the index method
1213 * the operator class is used for. The argument list is expected to contain
1214 * a single name (represented as a string Value node).
1217 CommentOpClass(List
*qualname
, List
*arguments
, char *comment
)
1226 Assert(list_length(arguments
) == 1);
1227 amname
= strVal(linitial(arguments
));
1230 * Get the access method's OID.
1232 amID
= GetSysCacheOid(AMNAME
,
1233 CStringGetDatum(amname
),
1235 if (!OidIsValid(amID
))
1237 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1238 errmsg("access method \"%s\" does not exist",
1242 * Look up the opclass.
1245 /* deconstruct the name list */
1246 DeconstructQualifiedName(qualname
, &schemaname
, &opcname
);
1250 /* Look in specific schema only */
1253 namespaceId
= LookupExplicitNamespace(schemaname
);
1254 tuple
= SearchSysCache(CLAAMNAMENSP
,
1255 ObjectIdGetDatum(amID
),
1256 PointerGetDatum(opcname
),
1257 ObjectIdGetDatum(namespaceId
),
1262 /* Unqualified opclass name, so search the search path */
1263 opcID
= OpclassnameGetOpcid(amID
, opcname
);
1264 if (!OidIsValid(opcID
))
1266 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1267 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1269 tuple
= SearchSysCache(CLAOID
,
1270 ObjectIdGetDatum(opcID
),
1274 if (!HeapTupleIsValid(tuple
))
1276 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1277 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1278 NameListToString(qualname
), amname
)));
1280 opcID
= HeapTupleGetOid(tuple
);
1282 /* Permission check: must own opclass */
1283 if (!pg_opclass_ownercheck(opcID
, GetUserId()))
1284 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_OPCLASS
,
1285 NameListToString(qualname
));
1287 ReleaseSysCache(tuple
);
1289 /* Call CreateComments() to create/drop the comments */
1290 CreateComments(opcID
, OperatorClassRelationId
, 0, comment
);
1294 * CommentOpFamily --
1296 * This routine is used to allow a user to provide comments on an
1297 * operator family. The operator family for commenting is determined by both
1298 * its name and its argument list which defines the index method
1299 * the operator family is used for. The argument list is expected to contain
1300 * a single name (represented as a string Value node).
1303 CommentOpFamily(List
*qualname
, List
*arguments
, char *comment
)
1312 Assert(list_length(arguments
) == 1);
1313 amname
= strVal(linitial(arguments
));
1316 * Get the access method's OID.
1318 amID
= GetSysCacheOid(AMNAME
,
1319 CStringGetDatum(amname
),
1321 if (!OidIsValid(amID
))
1323 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1324 errmsg("access method \"%s\" does not exist",
1328 * Look up the opfamily.
1331 /* deconstruct the name list */
1332 DeconstructQualifiedName(qualname
, &schemaname
, &opfname
);
1336 /* Look in specific schema only */
1339 namespaceId
= LookupExplicitNamespace(schemaname
);
1340 tuple
= SearchSysCache(OPFAMILYAMNAMENSP
,
1341 ObjectIdGetDatum(amID
),
1342 PointerGetDatum(opfname
),
1343 ObjectIdGetDatum(namespaceId
),
1348 /* Unqualified opfamily name, so search the search path */
1349 opfID
= OpfamilynameGetOpfid(amID
, opfname
);
1350 if (!OidIsValid(opfID
))
1352 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1353 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
1355 tuple
= SearchSysCache(OPFAMILYOID
,
1356 ObjectIdGetDatum(opfID
),
1360 if (!HeapTupleIsValid(tuple
))
1362 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1363 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
1364 NameListToString(qualname
), amname
)));
1366 opfID
= HeapTupleGetOid(tuple
);
1368 /* Permission check: must own opfamily */
1369 if (!pg_opfamily_ownercheck(opfID
, GetUserId()))
1370 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_OPFAMILY
,
1371 NameListToString(qualname
));
1373 ReleaseSysCache(tuple
);
1375 /* Call CreateComments() to create/drop the comments */
1376 CreateComments(opfID
, OperatorFamilyRelationId
, 0, comment
);
1380 * CommentLargeObject --
1382 * This routine is used to add/drop any user-comments a user might
1383 * have regarding a LARGE OBJECT. The large object is specified by OID
1384 * and, if found, and the user has appropriate permissions, a
1385 * comment will be added/dropped using the CreateComments() routine.
1386 * The large object's OID and the comment are the parameters to this routine.
1389 CommentLargeObject(List
*qualname
, char *comment
)
1394 Assert(list_length(qualname
) == 1);
1395 node
= (Node
*) linitial(qualname
);
1397 switch (nodeTag(node
))
1400 loid
= intVal(node
);
1405 * Values too large for int4 will be represented as Float
1406 * constants by the lexer. Accept these if they are valid OID
1409 loid
= DatumGetObjectId(DirectFunctionCall1(oidin
,
1410 CStringGetDatum(strVal(node
))));
1413 elog(ERROR
, "unrecognized node type: %d",
1414 (int) nodeTag(node
));
1415 /* keep compiler quiet */
1419 /* check that the large object exists */
1420 if (!LargeObjectExists(loid
))
1422 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1423 errmsg("large object %u does not exist", loid
)));
1425 /* Call CreateComments() to create/drop the comments */
1426 CreateComments(loid
, LargeObjectRelationId
, 0, comment
);
1432 * This routine is used to add/drop any user-comments a user might
1433 * have regarding a CAST. The cast is specified by source and destination types
1434 * and, if found, and the user has appropriate permissions, a
1435 * comment will be added/dropped using the CreateComments() routine.
1436 * The cast's source type is passed as the "name", the destination type
1437 * as the "arguments".
1440 CommentCast(List
*qualname
, List
*arguments
, char *comment
)
1442 TypeName
*sourcetype
;
1443 TypeName
*targettype
;
1449 Assert(list_length(qualname
) == 1);
1450 sourcetype
= (TypeName
*) linitial(qualname
);
1451 Assert(IsA(sourcetype
, TypeName
));
1452 Assert(list_length(arguments
) == 1);
1453 targettype
= (TypeName
*) linitial(arguments
);
1454 Assert(IsA(targettype
, TypeName
));
1456 sourcetypeid
= typenameTypeId(NULL
, sourcetype
, NULL
);
1457 targettypeid
= typenameTypeId(NULL
, targettype
, NULL
);
1459 tuple
= SearchSysCache(CASTSOURCETARGET
,
1460 ObjectIdGetDatum(sourcetypeid
),
1461 ObjectIdGetDatum(targettypeid
),
1463 if (!HeapTupleIsValid(tuple
))
1465 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1466 errmsg("cast from type %s to type %s does not exist",
1467 format_type_be(sourcetypeid
),
1468 format_type_be(targettypeid
))));
1470 /* Get the OID of the cast */
1471 castOid
= HeapTupleGetOid(tuple
);
1473 /* Permission check */
1474 if (!pg_type_ownercheck(sourcetypeid
, GetUserId())
1475 && !pg_type_ownercheck(targettypeid
, GetUserId()))
1477 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1478 errmsg("must be owner of type %s or type %s",
1479 format_type_be(sourcetypeid
),
1480 format_type_be(targettypeid
))));
1482 ReleaseSysCache(tuple
);
1484 /* Call CreateComments() to create/drop the comments */
1485 CreateComments(castOid
, CastRelationId
, 0, comment
);
1489 CommentTSParser(List
*qualname
, char *comment
)
1493 prsId
= TSParserGetPrsid(qualname
, false);
1497 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1498 errmsg("must be superuser to comment on text search parser")));
1500 CreateComments(prsId
, TSParserRelationId
, 0, comment
);
1504 CommentTSDictionary(List
*qualname
, char *comment
)
1508 dictId
= TSDictionaryGetDictid(qualname
, false);
1510 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
1511 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
1512 NameListToString(qualname
));
1514 CreateComments(dictId
, TSDictionaryRelationId
, 0, comment
);
1518 CommentTSTemplate(List
*qualname
, char *comment
)
1522 tmplId
= TSTemplateGetTmplid(qualname
, false);
1526 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1527 errmsg("must be superuser to comment on text search template")));
1529 CreateComments(tmplId
, TSTemplateRelationId
, 0, comment
);
1533 CommentTSConfiguration(List
*qualname
, char *comment
)
1537 cfgId
= TSConfigGetCfgid(qualname
, false);
1539 if (!pg_ts_config_ownercheck(cfgId
, GetUserId()))
1540 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1541 NameListToString(qualname
));
1543 CreateComments(cfgId
, TSConfigRelationId
, 0, comment
);