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/tqual.h"
34
* This routine is used to add the associated comment into
35
* pg_description for the object specified by the given SQL command.
38
CommentObject(CommentStmt *stmt)
40
ObjectAddress address;
44
* When loading a dump, we may see a COMMENT ON DATABASE for the old name
45
* of the database. Erroring out would prevent pg_restore from completing
46
* (which is really pg_restore's fault, but for now we will work around
47
* the problem here). Consensus is that the best fix is to treat wrong
48
* database name as a WARNING not an ERROR; hence, the following special
49
* case. (If the length of stmt->objname is not 1, get_object_address
50
* will throw an error below; that's OK.)
52
if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1)
54
char *database = strVal(linitial(stmt->objname));
56
if (!OidIsValid(get_database_oid(database, true)))
59
(errcode(ERRCODE_UNDEFINED_DATABASE),
60
errmsg("database \"%s\" does not exist", database)));
66
* Translate the parser representation that identifies this object into an
67
* ObjectAddress. get_object_address() will throw an error if the object
68
* does not exist, and will also acquire a lock on the target to guard
69
* against concurrent DROP operations.
71
address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
72
&relation, ShareUpdateExclusiveLock);
74
/* Require ownership of the target object. */
75
check_object_ownership(GetUserId(), stmt->objtype, address,
76
stmt->objname, stmt->objargs, relation);
78
/* Perform other integrity checks as needed. */
79
switch (stmt->objtype)
84
* Allow comments only on columns of tables, views, composite
85
* types, and foreign tables (which are the only relkinds for
86
* which pg_dump will dump per-column comments). In particular we
87
* wish to disallow comments on index columns, because the naming
88
* of an index's columns may change across PG versions, so dumping
89
* per-column comments could create reload failures.
91
if (relation->rd_rel->relkind != RELKIND_RELATION &&
92
relation->rd_rel->relkind != RELKIND_VIEW &&
93
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
94
relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
96
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
97
errmsg("\"%s\" is not a table, view, composite type, or foreign table",
98
RelationGetRelationName(relation))));
105
* Databases, tablespaces, and roles are cluster-wide objects, so any
106
* comments on those objects are recorded in the shared pg_shdescription
107
* catalog. Comments on all other objects are recorded in pg_description.
109
if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE
110
|| stmt->objtype == OBJECT_ROLE)
111
CreateSharedComments(address.objectId, address.classId, stmt->comment);
113
CreateComments(address.objectId, address.classId, address.objectSubId,
117
* If get_object_address() opened the relation for us, we close it to keep
118
* the reference count correct - but we retain any locks acquired by
119
* get_object_address() until commit time, to guard against concurrent
122
if (relation != NULL)
123
relation_close(relation, NoLock);
129
* Create a comment for the specified object descriptor. Inserts a new
130
* pg_description tuple, or replaces an existing one with the same key.
132
* If the comment given is null or an empty string, instead delete any
133
* existing comment for the specified key.
136
CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
138
Relation description;
142
HeapTuple newtuple = NULL;
143
Datum values[Natts_pg_description];
144
bool nulls[Natts_pg_description];
145
bool replaces[Natts_pg_description];
148
/* Reduce empty-string to NULL case */
149
if (comment != NULL && strlen(comment) == 0)
152
/* Prepare to form or update a tuple, if necessary */
155
for (i = 0; i < Natts_pg_description; i++)
161
values[i++] = ObjectIdGetDatum(oid);
162
values[i++] = ObjectIdGetDatum(classoid);
163
values[i++] = Int32GetDatum(subid);
164
values[i++] = 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++)
261
values[i++] = ObjectIdGetDatum(oid);
262
values[i++] = ObjectIdGetDatum(classoid);
263
values[i++] = CStringGetTextDatum(comment);
266
/* Use the index to search for a matching old tuple */
268
ScanKeyInit(&skey[0],
269
Anum_pg_shdescription_objoid,
270
BTEqualStrategyNumber, F_OIDEQ,
271
ObjectIdGetDatum(oid));
272
ScanKeyInit(&skey[1],
273
Anum_pg_shdescription_classoid,
274
BTEqualStrategyNumber, F_OIDEQ,
275
ObjectIdGetDatum(classoid));
277
shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
279
sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
280
SnapshotNow, 2, skey);
282
while ((oldtuple = systable_getnext(sd)) != NULL)
284
/* Found the old tuple, so delete or update it */
287
simple_heap_delete(shdescription, &oldtuple->t_self);
290
newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
291
values, nulls, replaces);
292
simple_heap_update(shdescription, &oldtuple->t_self, newtuple);
295
break; /* Assume there can be only one match */
298
systable_endscan(sd);
300
/* If we didn't find an old tuple, insert a new one */
302
if (newtuple == NULL && comment != NULL)
304
newtuple = heap_form_tuple(RelationGetDescr(shdescription),
306
simple_heap_insert(shdescription, newtuple);
309
/* Update indexes, if necessary */
310
if (newtuple != NULL)
312
CatalogUpdateIndexes(shdescription, newtuple);
313
heap_freetuple(newtuple);
318
heap_close(shdescription, NoLock);
322
* DeleteComments -- remove comments for an object
324
* If subid is nonzero then only comments matching it will be removed.
325
* If subid is zero, all comments matching the oid/classoid will be removed
326
* (this corresponds to deleting a whole object).
329
DeleteComments(Oid oid, Oid classoid, int32 subid)
331
Relation description;
337
/* Use the index to search for all matching old tuples */
339
ScanKeyInit(&skey[0],
340
Anum_pg_description_objoid,
341
BTEqualStrategyNumber, F_OIDEQ,
342
ObjectIdGetDatum(oid));
343
ScanKeyInit(&skey[1],
344
Anum_pg_description_classoid,
345
BTEqualStrategyNumber, F_OIDEQ,
346
ObjectIdGetDatum(classoid));
350
ScanKeyInit(&skey[2],
351
Anum_pg_description_objsubid,
352
BTEqualStrategyNumber, F_INT4EQ,
353
Int32GetDatum(subid));
359
description = heap_open(DescriptionRelationId, RowExclusiveLock);
361
sd = systable_beginscan(description, DescriptionObjIndexId, true,
362
SnapshotNow, nkeys, skey);
364
while ((oldtuple = systable_getnext(sd)) != NULL)
365
simple_heap_delete(description, &oldtuple->t_self);
369
systable_endscan(sd);
370
heap_close(description, RowExclusiveLock);
374
* DeleteSharedComments -- remove comments for a shared object
377
DeleteSharedComments(Oid oid, Oid classoid)
379
Relation shdescription;
384
/* Use the index to search for all matching old tuples */
386
ScanKeyInit(&skey[0],
387
Anum_pg_shdescription_objoid,
388
BTEqualStrategyNumber, F_OIDEQ,
389
ObjectIdGetDatum(oid));
390
ScanKeyInit(&skey[1],
391
Anum_pg_shdescription_classoid,
392
BTEqualStrategyNumber, F_OIDEQ,
393
ObjectIdGetDatum(classoid));
395
shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
397
sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
398
SnapshotNow, 2, skey);
400
while ((oldtuple = systable_getnext(sd)) != NULL)
401
simple_heap_delete(shdescription, &oldtuple->t_self);
405
systable_endscan(sd);
406
heap_close(shdescription, RowExclusiveLock);
410
* GetComment -- get the comment for an object, or null if not found.
413
GetComment(Oid oid, Oid classoid, int32 subid)
415
Relation description;
422
/* Use the index to search for a matching old tuple */
424
ScanKeyInit(&skey[0],
425
Anum_pg_description_objoid,
426
BTEqualStrategyNumber, F_OIDEQ,
427
ObjectIdGetDatum(oid));
428
ScanKeyInit(&skey[1],
429
Anum_pg_description_classoid,
430
BTEqualStrategyNumber, F_OIDEQ,
431
ObjectIdGetDatum(classoid));
432
ScanKeyInit(&skey[2],
433
Anum_pg_description_objsubid,
434
BTEqualStrategyNumber, F_INT4EQ,
435
Int32GetDatum(subid));
437
description = heap_open(DescriptionRelationId, AccessShareLock);
438
tupdesc = RelationGetDescr(description);
440
sd = systable_beginscan(description, DescriptionObjIndexId, true,
441
SnapshotNow, 3, skey);
444
while ((tuple = systable_getnext(sd)) != NULL)
449
/* Found the tuple, get description field */
450
value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
452
comment = TextDatumGetCString(value);
453
break; /* Assume there can be only one match */
456
systable_endscan(sd);
459
heap_close(description, AccessShareLock);