1 /*-------------------------------------------------------------------------
5 * PostgreSQL object comments utility code.
7 * Copyright (c) 1996-2011, PostgreSQL Global Development Group
10 * src/backend/commands/comment.c
12 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/heapam.h"
19 #include "catalog/indexing.h"
20 #include "catalog/objectaddress.h"
21 #include "catalog/pg_description.h"
22 #include "catalog/pg_shdescription.h"
23 #include "commands/comment.h"
24 #include "commands/dbcommands.h"
25 #include "miscadmin.h"
26 #include "utils/builtins.h"
27 #include "utils/fmgroids.h"
28 #include "utils/rel.h"
29 #include "utils/tqual.h"
35 * This routine is used to add the associated comment into
36 * pg_description for the object specified by the given SQL command.
39 CommentObject(CommentStmt *stmt)
41 ObjectAddress address;
45 * When loading a dump, we may see a COMMENT ON DATABASE for the old name
46 * of the database. Erroring out would prevent pg_restore from completing
47 * (which is really pg_restore's fault, but for now we will work around
48 * the problem here). Consensus is that the best fix is to treat wrong
49 * database name as a WARNING not an ERROR; hence, the following special
50 * case. (If the length of stmt->objname is not 1, get_object_address
51 * will throw an error below; that's OK.)
53 if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1)
55 char *database = strVal(linitial(stmt->objname));
57 if (!OidIsValid(get_database_oid(database, true)))
60 (errcode(ERRCODE_UNDEFINED_DATABASE),
61 errmsg("database \"%s\" does not exist", database)));
67 * Translate the parser representation that identifies this object into an
68 * ObjectAddress. get_object_address() will throw an error if the object
69 * does not exist, and will also acquire a lock on the target to guard
70 * against concurrent DROP operations.
72 address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
73 &relation, ShareUpdateExclusiveLock, false);
75 /* Require ownership of the target object. */
76 check_object_ownership(GetUserId(), stmt->objtype, address,
77 stmt->objname, stmt->objargs, relation);
79 /* Perform other integrity checks as needed. */
80 switch (stmt->objtype)
85 * Allow comments only on columns of tables, views, composite
86 * types, and foreign tables (which are the only relkinds for
87 * which pg_dump will dump per-column comments). In particular we
88 * wish to disallow comments on index columns, because the naming
89 * of an index's columns may change across PG versions, so dumping
90 * per-column comments could create reload failures.
92 if (relation->rd_rel->relkind != RELKIND_RELATION &&
93 relation->rd_rel->relkind != RELKIND_VIEW &&
94 relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
95 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
97 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
98 errmsg("\"%s\" is not a table, view, composite type, or foreign table",
99 RelationGetRelationName(relation))));
106 * Databases, tablespaces, and roles are cluster-wide objects, so any
107 * comments on those objects are recorded in the shared pg_shdescription
108 * catalog. Comments on all other objects are recorded in pg_description.
110 if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE
111 || stmt->objtype == OBJECT_ROLE)
112 CreateSharedComments(address.objectId, address.classId, stmt->comment);
114 CreateComments(address.objectId, address.classId, address.objectSubId,
118 * If get_object_address() opened the relation for us, we close it to keep
119 * the reference count correct - but we retain any locks acquired by
120 * get_object_address() until commit time, to guard against concurrent
123 if (relation != NULL)
124 relation_close(relation, NoLock);
130 * Create a comment for the specified object descriptor. Inserts a new
131 * pg_description tuple, or replaces an existing one with the same key.
133 * If the comment given is null or an empty string, instead delete any
134 * existing comment for the specified key.
137 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
139 Relation description;
143 HeapTuple newtuple = NULL;
144 Datum values[Natts_pg_description];
145 bool nulls[Natts_pg_description];
146 bool replaces[Natts_pg_description];
149 /* Reduce empty-string to NULL case */
150 if (comment != NULL && strlen(comment) == 0)
153 /* Prepare to form or update a tuple, if necessary */
156 for (i = 0; i < Natts_pg_description; i++)
161 values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(oid);
162 values[Anum_pg_description_classoid - 1] = ObjectIdGetDatum(classoid);
163 values[Anum_pg_description_objsubid - 1] = Int32GetDatum(subid);
164 values[Anum_pg_description_description - 1] = CStringGetTextDatum(comment);
167 /* Use the index to search for a matching old tuple */
169 ScanKeyInit(&skey[0],
170 Anum_pg_description_objoid,
171 BTEqualStrategyNumber, F_OIDEQ,
172 ObjectIdGetDatum(oid));
173 ScanKeyInit(&skey[1],
174 Anum_pg_description_classoid,
175 BTEqualStrategyNumber, F_OIDEQ,
176 ObjectIdGetDatum(classoid));
177 ScanKeyInit(&skey[2],
178 Anum_pg_description_objsubid,
179 BTEqualStrategyNumber, F_INT4EQ,
180 Int32GetDatum(subid));
182 description = heap_open(DescriptionRelationId, RowExclusiveLock);
184 sd = systable_beginscan(description, DescriptionObjIndexId, true,
185 SnapshotNow, 3, skey);
187 while ((oldtuple = systable_getnext(sd)) != NULL)
189 /* Found the old tuple, so delete or update it */
192 simple_heap_delete(description, &oldtuple->t_self);
195 newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
197 simple_heap_update(description, &oldtuple->t_self, newtuple);
200 break; /* Assume there can be only one match */
203 systable_endscan(sd);
205 /* If we didn't find an old tuple, insert a new one */
207 if (newtuple == NULL && comment != NULL)
209 newtuple = heap_form_tuple(RelationGetDescr(description),
211 simple_heap_insert(description, newtuple);
214 /* Update indexes, if necessary */
215 if (newtuple != NULL)
217 CatalogUpdateIndexes(description, newtuple);
218 heap_freetuple(newtuple);
223 heap_close(description, NoLock);
227 * CreateSharedComments --
229 * Create a comment for the specified shared object descriptor. Inserts a
230 * new pg_shdescription tuple, or replaces an existing one with the same key.
232 * If the comment given is null or an empty string, instead delete any
233 * existing comment for the specified key.
236 CreateSharedComments(Oid oid, Oid classoid, char *comment)
238 Relation shdescription;
242 HeapTuple newtuple = NULL;
243 Datum values[Natts_pg_shdescription];
244 bool nulls[Natts_pg_shdescription];
245 bool replaces[Natts_pg_shdescription];
248 /* Reduce empty-string to NULL case */
249 if (comment != NULL && strlen(comment) == 0)
252 /* Prepare to form or update a tuple, if necessary */
255 for (i = 0; i < Natts_pg_shdescription; i++)
260 values[Anum_pg_shdescription_objoid - 1] = ObjectIdGetDatum(oid);
261 values[Anum_pg_shdescription_classoid - 1] = ObjectIdGetDatum(classoid);
262 values[Anum_pg_shdescription_description - 1] = CStringGetTextDatum(comment);
265 /* Use the index to search for a matching old tuple */
267 ScanKeyInit(&skey[0],
268 Anum_pg_shdescription_objoid,
269 BTEqualStrategyNumber, F_OIDEQ,
270 ObjectIdGetDatum(oid));
271 ScanKeyInit(&skey[1],
272 Anum_pg_shdescription_classoid,
273 BTEqualStrategyNumber, F_OIDEQ,
274 ObjectIdGetDatum(classoid));
276 shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
278 sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
279 SnapshotNow, 2, skey);
281 while ((oldtuple = systable_getnext(sd)) != NULL)
283 /* Found the old tuple, so delete or update it */
286 simple_heap_delete(shdescription, &oldtuple->t_self);
289 newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
290 values, nulls, replaces);
291 simple_heap_update(shdescription, &oldtuple->t_self, newtuple);
294 break; /* Assume there can be only one match */
297 systable_endscan(sd);
299 /* If we didn't find an old tuple, insert a new one */
301 if (newtuple == NULL && comment != NULL)
303 newtuple = heap_form_tuple(RelationGetDescr(shdescription),
305 simple_heap_insert(shdescription, newtuple);
308 /* Update indexes, if necessary */
309 if (newtuple != NULL)
311 CatalogUpdateIndexes(shdescription, newtuple);
312 heap_freetuple(newtuple);
317 heap_close(shdescription, NoLock);
321 * DeleteComments -- remove comments for an object
323 * If subid is nonzero then only comments matching it will be removed.
324 * If subid is zero, all comments matching the oid/classoid will be removed
325 * (this corresponds to deleting a whole object).
328 DeleteComments(Oid oid, Oid classoid, int32 subid)
330 Relation description;
336 /* Use the index to search for all matching old tuples */
338 ScanKeyInit(&skey[0],
339 Anum_pg_description_objoid,
340 BTEqualStrategyNumber, F_OIDEQ,
341 ObjectIdGetDatum(oid));
342 ScanKeyInit(&skey[1],
343 Anum_pg_description_classoid,
344 BTEqualStrategyNumber, F_OIDEQ,
345 ObjectIdGetDatum(classoid));
349 ScanKeyInit(&skey[2],
350 Anum_pg_description_objsubid,
351 BTEqualStrategyNumber, F_INT4EQ,
352 Int32GetDatum(subid));
358 description = heap_open(DescriptionRelationId, RowExclusiveLock);
360 sd = systable_beginscan(description, DescriptionObjIndexId, true,
361 SnapshotNow, nkeys, skey);
363 while ((oldtuple = systable_getnext(sd)) != NULL)
364 simple_heap_delete(description, &oldtuple->t_self);
368 systable_endscan(sd);
369 heap_close(description, RowExclusiveLock);
373 * DeleteSharedComments -- remove comments for a shared object
376 DeleteSharedComments(Oid oid, Oid classoid)
378 Relation shdescription;
383 /* Use the index to search for all matching old tuples */
385 ScanKeyInit(&skey[0],
386 Anum_pg_shdescription_objoid,
387 BTEqualStrategyNumber, F_OIDEQ,
388 ObjectIdGetDatum(oid));
389 ScanKeyInit(&skey[1],
390 Anum_pg_shdescription_classoid,
391 BTEqualStrategyNumber, F_OIDEQ,
392 ObjectIdGetDatum(classoid));
394 shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
396 sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
397 SnapshotNow, 2, skey);
399 while ((oldtuple = systable_getnext(sd)) != NULL)
400 simple_heap_delete(shdescription, &oldtuple->t_self);
404 systable_endscan(sd);
405 heap_close(shdescription, RowExclusiveLock);
409 * GetComment -- get the comment for an object, or null if not found.
412 GetComment(Oid oid, Oid classoid, int32 subid)
414 Relation description;
421 /* Use the index to search for a matching old tuple */
423 ScanKeyInit(&skey[0],
424 Anum_pg_description_objoid,
425 BTEqualStrategyNumber, F_OIDEQ,
426 ObjectIdGetDatum(oid));
427 ScanKeyInit(&skey[1],
428 Anum_pg_description_classoid,
429 BTEqualStrategyNumber, F_OIDEQ,
430 ObjectIdGetDatum(classoid));
431 ScanKeyInit(&skey[2],
432 Anum_pg_description_objsubid,
433 BTEqualStrategyNumber, F_INT4EQ,
434 Int32GetDatum(subid));
436 description = heap_open(DescriptionRelationId, AccessShareLock);
437 tupdesc = RelationGetDescr(description);
439 sd = systable_beginscan(description, DescriptionObjIndexId, true,
440 SnapshotNow, 3, skey);
443 while ((tuple = systable_getnext(sd)) != NULL)
448 /* Found the tuple, get description field */
449 value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
451 comment = TextDatumGetCString(value);
452 break; /* Assume there can be only one match */
455 systable_endscan(sd);
458 heap_close(description, AccessShareLock);