Don't reset pg_class.reltuples and relpages in VACUUM, if any pages were
[PostgreSQL.git] / src / backend / commands / comment.c
blob8d249e175d0fa16f30df045789ca344abf12a2a8
1 /*-------------------------------------------------------------------------
3 * comment.c
5 * PostgreSQL object comments utility code.
7 * Copyright (c) 1996-2008, PostgreSQL Global Development Group
9 * IDENTIFICATION
10 * $PostgreSQL$
12 *-------------------------------------------------------------------------
15 #include "postgres.h"
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);
94 * CommentObject --
96 * This routine is used to add the associated comment into
97 * pg_description for the object specified by the given SQL command.
99 void
100 CommentObject(CommentStmt *stmt)
102 switch (stmt->objtype)
104 case OBJECT_INDEX:
105 case OBJECT_SEQUENCE:
106 case OBJECT_TABLE:
107 case OBJECT_VIEW:
108 CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
109 break;
110 case OBJECT_COLUMN:
111 CommentAttribute(stmt->objname, stmt->comment);
112 break;
113 case OBJECT_DATABASE:
114 CommentDatabase(stmt->objname, stmt->comment);
115 break;
116 case OBJECT_RULE:
117 CommentRule(stmt->objname, stmt->comment);
118 break;
119 case OBJECT_TYPE:
120 CommentType(stmt->objname, stmt->comment);
121 break;
122 case OBJECT_AGGREGATE:
123 CommentAggregate(stmt->objname, stmt->objargs, stmt->comment);
124 break;
125 case OBJECT_FUNCTION:
126 CommentProc(stmt->objname, stmt->objargs, stmt->comment);
127 break;
128 case OBJECT_OPERATOR:
129 CommentOperator(stmt->objname, stmt->objargs, stmt->comment);
130 break;
131 case OBJECT_TRIGGER:
132 CommentTrigger(stmt->objname, stmt->comment);
133 break;
134 case OBJECT_SCHEMA:
135 CommentNamespace(stmt->objname, stmt->comment);
136 break;
137 case OBJECT_CONSTRAINT:
138 CommentConstraint(stmt->objname, stmt->comment);
139 break;
140 case OBJECT_CONVERSION:
141 CommentConversion(stmt->objname, stmt->comment);
142 break;
143 case OBJECT_LANGUAGE:
144 CommentLanguage(stmt->objname, stmt->comment);
145 break;
146 case OBJECT_OPCLASS:
147 CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
148 break;
149 case OBJECT_OPFAMILY:
150 CommentOpFamily(stmt->objname, stmt->objargs, stmt->comment);
151 break;
152 case OBJECT_LARGEOBJECT:
153 CommentLargeObject(stmt->objname, stmt->comment);
154 break;
155 case OBJECT_CAST:
156 CommentCast(stmt->objname, stmt->objargs, stmt->comment);
157 break;
158 case OBJECT_TABLESPACE:
159 CommentTablespace(stmt->objname, stmt->comment);
160 break;
161 case OBJECT_ROLE:
162 CommentRole(stmt->objname, stmt->comment);
163 break;
164 case OBJECT_TSPARSER:
165 CommentTSParser(stmt->objname, stmt->comment);
166 break;
167 case OBJECT_TSDICTIONARY:
168 CommentTSDictionary(stmt->objname, stmt->comment);
169 break;
170 case OBJECT_TSTEMPLATE:
171 CommentTSTemplate(stmt->objname, stmt->comment);
172 break;
173 case OBJECT_TSCONFIGURATION:
174 CommentTSConfiguration(stmt->objname, stmt->comment);
175 break;
176 default:
177 elog(ERROR, "unrecognized object type: %d",
178 (int) stmt->objtype);
183 * CreateComments --
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.
191 void
192 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
194 Relation description;
195 ScanKeyData skey[3];
196 SysScanDesc sd;
197 HeapTuple oldtuple;
198 HeapTuple newtuple = NULL;
199 Datum values[Natts_pg_description];
200 bool nulls[Natts_pg_description];
201 bool replaces[Natts_pg_description];
202 int i;
204 /* Reduce empty-string to NULL case */
205 if (comment != NULL && strlen(comment) == 0)
206 comment = NULL;
208 /* Prepare to form or update a tuple, if necessary */
209 if (comment != NULL)
211 for (i = 0; i < Natts_pg_description; i++)
213 nulls[i] = false;
214 replaces[i] = true;
216 i = 0;
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 */
247 if (comment == NULL)
248 simple_heap_delete(description, &oldtuple->t_self);
249 else
251 newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
252 nulls, replaces);
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),
266 values, nulls);
267 simple_heap_insert(description, newtuple);
270 /* Update indexes, if necessary */
271 if (newtuple != NULL)
273 CatalogUpdateIndexes(description, newtuple);
274 heap_freetuple(newtuple);
277 /* Done */
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.
291 void
292 CreateSharedComments(Oid oid, Oid classoid, char *comment)
294 Relation shdescription;
295 ScanKeyData skey[2];
296 SysScanDesc sd;
297 HeapTuple oldtuple;
298 HeapTuple newtuple = NULL;
299 Datum values[Natts_pg_shdescription];
300 bool nulls[Natts_pg_shdescription];
301 bool replaces[Natts_pg_shdescription];
302 int i;
304 /* Reduce empty-string to NULL case */
305 if (comment != NULL && strlen(comment) == 0)
306 comment = NULL;
308 /* Prepare to form or update a tuple, if necessary */
309 if (comment != NULL)
311 for (i = 0; i < Natts_pg_shdescription; i++)
313 nulls[i] = false;
314 replaces[i] = true;
316 i = 0;
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 */
342 if (comment == NULL)
343 simple_heap_delete(shdescription, &oldtuple->t_self);
344 else
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),
361 values, nulls);
362 simple_heap_insert(shdescription, newtuple);
365 /* Update indexes, if necessary */
366 if (newtuple != NULL)
368 CatalogUpdateIndexes(shdescription, newtuple);
369 heap_freetuple(newtuple);
372 /* Done */
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).
384 void
385 DeleteComments(Oid oid, Oid classoid, int32 subid)
387 Relation description;
388 ScanKeyData skey[3];
389 int nkeys;
390 SysScanDesc sd;
391 HeapTuple oldtuple;
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));
404 if (subid != 0)
406 ScanKeyInit(&skey[2],
407 Anum_pg_description_objsubid,
408 BTEqualStrategyNumber, F_INT4EQ,
409 Int32GetDatum(subid));
410 nkeys = 3;
412 else
413 nkeys = 2;
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);
423 /* Done */
425 systable_endscan(sd);
426 heap_close(description, RowExclusiveLock);
430 * DeleteSharedComments -- remove comments for a shared object
432 void
433 DeleteSharedComments(Oid oid, Oid classoid)
435 Relation shdescription;
436 ScanKeyData skey[2];
437 SysScanDesc sd;
438 HeapTuple oldtuple;
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);
459 /* Done */
461 systable_endscan(sd);
462 heap_close(shdescription, RowExclusiveLock);
466 * CommentRelation --
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.
474 static void
475 CommentRelation(int objtype, List *relname, char *comment)
477 Relation relation;
478 RangeVar *tgtrel;
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 */
496 switch (objtype)
498 case OBJECT_INDEX:
499 if (relation->rd_rel->relkind != RELKIND_INDEX)
500 ereport(ERROR,
501 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
502 errmsg("\"%s\" is not an index",
503 RelationGetRelationName(relation))));
504 break;
505 case OBJECT_SEQUENCE:
506 if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
507 ereport(ERROR,
508 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
509 errmsg("\"%s\" is not a sequence",
510 RelationGetRelationName(relation))));
511 break;
512 case OBJECT_TABLE:
513 if (relation->rd_rel->relkind != RELKIND_RELATION)
514 ereport(ERROR,
515 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
516 errmsg("\"%s\" is not a table",
517 RelationGetRelationName(relation))));
518 break;
519 case OBJECT_VIEW:
520 if (relation->rd_rel->relkind != RELKIND_VIEW)
521 ereport(ERROR,
522 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
523 errmsg("\"%s\" is not a view",
524 RelationGetRelationName(relation))));
525 break;
528 /* Create the comment using the relation's oid */
529 CreateComments(RelationGetRelid(relation), RelationRelationId,
530 0, comment);
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
546 static void
547 CommentAttribute(List *qualname, char *comment)
549 int nnames;
550 List *relname;
551 char *attrname;
552 RangeVar *rel;
553 Relation relation;
554 AttrNumber attnum;
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)
577 ereport(ERROR,
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);
592 * CommentDatabase --
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.
600 static void
601 CommentDatabase(List *qualname, char *comment)
603 char *database;
604 Oid oid;
606 if (list_length(qualname) != 1)
607 ereport(ERROR,
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))
624 ereport(WARNING,
625 (errcode(ERRCODE_UNDEFINED_DATABASE),
626 errmsg("database \"%s\" does not exist", database)));
627 return;
630 /* Check object security */
631 if (!pg_database_ownercheck(oid, GetUserId()))
632 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
633 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.
648 static void
649 CommentTablespace(List *qualname, char *comment)
651 char *tablespace;
652 Oid oid;
654 if (list_length(qualname) != 1)
655 ereport(ERROR,
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))
663 ereport(ERROR,
664 (errcode(ERRCODE_UNDEFINED_OBJECT),
665 errmsg("tablespace \"%s\" does not exist", tablespace)));
666 return;
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);
678 * CommentRole --
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.
685 static void
686 CommentRole(List *qualname, char *comment)
688 char *role;
689 Oid oid;
691 if (list_length(qualname) != 1)
692 ereport(ERROR,
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))
701 ereport(ERROR,
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.
718 static void
719 CommentNamespace(List *qualname, char *comment)
721 Oid oid;
722 char *namespace;
724 if (list_length(qualname) != 1)
725 ereport(ERROR,
726 (errcode(ERRCODE_SYNTAX_ERROR),
727 errmsg("schema name cannot be qualified")));
728 namespace = strVal(linitial(qualname));
730 oid = GetSysCacheOid(NAMESPACENAME,
731 CStringGetDatum(namespace),
732 0, 0, 0);
733 if (!OidIsValid(oid))
734 ereport(ERROR,
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,
741 namespace);
743 /* Call CreateComments() to create/drop the comments */
744 CreateComments(oid, NamespaceRelationId, 0, comment);
748 * CommentRule --
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.
761 static void
762 CommentRule(List *qualname, char *comment)
764 int nnames;
765 List *relname;
766 char *rulename;
767 RangeVar *rel;
768 Relation relation;
769 HeapTuple tuple;
770 Oid reloid;
771 Oid ruleoid;
773 /* Separate relname and trig name */
774 nnames = list_length(qualname);
775 if (nnames == 1)
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,
792 1, &scanKeyData);
794 tuple = heap_getnext(scanDesc, ForwardScanDirection);
795 if (HeapTupleIsValid(tuple))
797 reloid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
798 ruleoid = HeapTupleGetOid(tuple);
800 else
802 ereport(ERROR,
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)))
810 ereport(ERROR,
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);
821 else
823 /* New-style: rule and relname both provided */
824 Assert(nnames >= 2);
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),
837 0, 0);
838 if (!HeapTupleIsValid(tuple))
839 ereport(ERROR,
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);
860 * CommentType --
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.
868 static void
869 CommentType(List *typename, char *comment)
871 TypeName *tname;
872 Oid oid;
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).
898 static void
899 CommentAggregate(List *aggregate, List *arguments, char *comment)
901 Oid oid;
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);
916 * CommentProc --
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.
924 static void
925 CommentProc(List *function, List *arguments, char *comment)
927 Oid oid;
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);
944 * CommentOperator --
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
951 * object.
953 static void
954 CommentOperator(List *opername, List *arguments, char *comment)
956 TypeName *typenode1 = (TypeName *) linitial(arguments);
957 TypeName *typenode2 = (TypeName *) lsecond(arguments);
958 Oid oid;
960 /* Look up the operator */
961 oid = LookupOperNameTypeNames(NULL, opername,
962 typenode1, typenode2,
963 false, -1);
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);
975 * CommentTrigger --
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.
983 static void
984 CommentTrigger(List *qualname, char *comment)
986 int nnames;
987 List *relname;
988 char *trigname;
989 RangeVar *rel;
990 Relation pg_trigger,
991 relation;
992 HeapTuple triggertuple;
993 SysScanDesc scan;
994 ScanKeyData entry[2];
995 Oid oid;
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))
1034 ereport(ERROR,
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.
1060 static void
1061 CommentConstraint(List *qualname, char *comment)
1063 int nnames;
1064 List *relName;
1065 char *conName;
1066 RangeVar *rel;
1067 Relation pg_constraint,
1068 relation;
1069 HeapTuple tuple;
1070 SysScanDesc scan;
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;
1094 * if so, error out.
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))
1113 ereport(ERROR,
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))
1125 ereport(ERROR,
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.
1147 static void
1148 CommentConversion(List *qualname, char *comment)
1150 Oid conversionOid;
1152 conversionOid = FindConversionByName(qualname);
1153 if (!OidIsValid(conversionOid))
1154 ereport(ERROR,
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.
1177 static void
1178 CommentLanguage(List *qualname, char *comment)
1180 Oid oid;
1181 char *language;
1183 if (list_length(qualname) != 1)
1184 ereport(ERROR,
1185 (errcode(ERRCODE_SYNTAX_ERROR),
1186 errmsg("language name cannot be qualified")));
1187 language = strVal(linitial(qualname));
1189 oid = GetSysCacheOid(LANGNAME,
1190 CStringGetDatum(language),
1191 0, 0, 0);
1192 if (!OidIsValid(oid))
1193 ereport(ERROR,
1194 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1195 errmsg("language \"%s\" does not exist", language)));
1197 /* Check object security */
1198 if (!superuser())
1199 ereport(ERROR,
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);
1208 * CommentOpClass --
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).
1216 static void
1217 CommentOpClass(List *qualname, List *arguments, char *comment)
1219 char *amname;
1220 char *schemaname;
1221 char *opcname;
1222 Oid amID;
1223 Oid opcID;
1224 HeapTuple tuple;
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),
1234 0, 0, 0);
1235 if (!OidIsValid(amID))
1236 ereport(ERROR,
1237 (errcode(ERRCODE_UNDEFINED_OBJECT),
1238 errmsg("access method \"%s\" does not exist",
1239 amname)));
1242 * Look up the opclass.
1245 /* deconstruct the name list */
1246 DeconstructQualifiedName(qualname, &schemaname, &opcname);
1248 if (schemaname)
1250 /* Look in specific schema only */
1251 Oid namespaceId;
1253 namespaceId = LookupExplicitNamespace(schemaname);
1254 tuple = SearchSysCache(CLAAMNAMENSP,
1255 ObjectIdGetDatum(amID),
1256 PointerGetDatum(opcname),
1257 ObjectIdGetDatum(namespaceId),
1260 else
1262 /* Unqualified opclass name, so search the search path */
1263 opcID = OpclassnameGetOpcid(amID, opcname);
1264 if (!OidIsValid(opcID))
1265 ereport(ERROR,
1266 (errcode(ERRCODE_UNDEFINED_OBJECT),
1267 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
1268 opcname, amname)));
1269 tuple = SearchSysCache(CLAOID,
1270 ObjectIdGetDatum(opcID),
1271 0, 0, 0);
1274 if (!HeapTupleIsValid(tuple))
1275 ereport(ERROR,
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).
1302 static void
1303 CommentOpFamily(List *qualname, List *arguments, char *comment)
1305 char *amname;
1306 char *schemaname;
1307 char *opfname;
1308 Oid amID;
1309 Oid opfID;
1310 HeapTuple tuple;
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),
1320 0, 0, 0);
1321 if (!OidIsValid(amID))
1322 ereport(ERROR,
1323 (errcode(ERRCODE_UNDEFINED_OBJECT),
1324 errmsg("access method \"%s\" does not exist",
1325 amname)));
1328 * Look up the opfamily.
1331 /* deconstruct the name list */
1332 DeconstructQualifiedName(qualname, &schemaname, &opfname);
1334 if (schemaname)
1336 /* Look in specific schema only */
1337 Oid namespaceId;
1339 namespaceId = LookupExplicitNamespace(schemaname);
1340 tuple = SearchSysCache(OPFAMILYAMNAMENSP,
1341 ObjectIdGetDatum(amID),
1342 PointerGetDatum(opfname),
1343 ObjectIdGetDatum(namespaceId),
1346 else
1348 /* Unqualified opfamily name, so search the search path */
1349 opfID = OpfamilynameGetOpfid(amID, opfname);
1350 if (!OidIsValid(opfID))
1351 ereport(ERROR,
1352 (errcode(ERRCODE_UNDEFINED_OBJECT),
1353 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
1354 opfname, amname)));
1355 tuple = SearchSysCache(OPFAMILYOID,
1356 ObjectIdGetDatum(opfID),
1357 0, 0, 0);
1360 if (!HeapTupleIsValid(tuple))
1361 ereport(ERROR,
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.
1388 static void
1389 CommentLargeObject(List *qualname, char *comment)
1391 Oid loid;
1392 Node *node;
1394 Assert(list_length(qualname) == 1);
1395 node = (Node *) linitial(qualname);
1397 switch (nodeTag(node))
1399 case T_Integer:
1400 loid = intVal(node);
1401 break;
1402 case T_Float:
1405 * Values too large for int4 will be represented as Float
1406 * constants by the lexer. Accept these if they are valid OID
1407 * strings.
1409 loid = DatumGetObjectId(DirectFunctionCall1(oidin,
1410 CStringGetDatum(strVal(node))));
1411 break;
1412 default:
1413 elog(ERROR, "unrecognized node type: %d",
1414 (int) nodeTag(node));
1415 /* keep compiler quiet */
1416 loid = InvalidOid;
1419 /* check that the large object exists */
1420 if (!LargeObjectExists(loid))
1421 ereport(ERROR,
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);
1430 * CommentCast --
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".
1439 static void
1440 CommentCast(List *qualname, List *arguments, char *comment)
1442 TypeName *sourcetype;
1443 TypeName *targettype;
1444 Oid sourcetypeid;
1445 Oid targettypeid;
1446 HeapTuple tuple;
1447 Oid castOid;
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),
1462 0, 0);
1463 if (!HeapTupleIsValid(tuple))
1464 ereport(ERROR,
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()))
1476 ereport(ERROR,
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);
1488 static void
1489 CommentTSParser(List *qualname, char *comment)
1491 Oid prsId;
1493 prsId = TSParserGetPrsid(qualname, false);
1495 if (!superuser())
1496 ereport(ERROR,
1497 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1498 errmsg("must be superuser to comment on text search parser")));
1500 CreateComments(prsId, TSParserRelationId, 0, comment);
1503 static void
1504 CommentTSDictionary(List *qualname, char *comment)
1506 Oid dictId;
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);
1517 static void
1518 CommentTSTemplate(List *qualname, char *comment)
1520 Oid tmplId;
1522 tmplId = TSTemplateGetTmplid(qualname, false);
1524 if (!superuser())
1525 ereport(ERROR,
1526 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1527 errmsg("must be superuser to comment on text search template")));
1529 CreateComments(tmplId, TSTemplateRelationId, 0, comment);
1532 static void
1533 CommentTSConfiguration(List *qualname, char *comment)
1535 Oid cfgId;
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);