OSDN Git Service

Remove rel.h from objectaddress.h; only relcache.h is necessary.
[pg-rex/syncrep.git] / src / backend / commands / comment.c
1 /*-------------------------------------------------------------------------
2  *
3  * comment.c
4  *
5  * PostgreSQL object comments utility code.
6  *
7  * Copyright (c) 1996-2011, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *        src/backend/commands/comment.c
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 #include "postgres.h"
16
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"
30
31
32 /*
33  * CommentObject --
34  *
35  * This routine is used to add the associated comment into
36  * pg_description for the object specified by the given SQL command.
37  */
38 void
39 CommentObject(CommentStmt *stmt)
40 {
41         ObjectAddress address;
42         Relation        relation;
43
44         /*
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.)
52          */
53         if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1)
54         {
55                 char       *database = strVal(linitial(stmt->objname));
56
57                 if (!OidIsValid(get_database_oid(database, true)))
58                 {
59                         ereport(WARNING,
60                                         (errcode(ERRCODE_UNDEFINED_DATABASE),
61                                          errmsg("database \"%s\" does not exist", database)));
62                         return;
63                 }
64         }
65
66         /*
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.
71          */
72         address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
73                                                                  &relation, ShareUpdateExclusiveLock, false);
74
75         /* Require ownership of the target object. */
76         check_object_ownership(GetUserId(), stmt->objtype, address,
77                                                    stmt->objname, stmt->objargs, relation);
78
79         /* Perform other integrity checks as needed. */
80         switch (stmt->objtype)
81         {
82                 case OBJECT_COLUMN:
83
84                         /*
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.
91                          */
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)
96                                 ereport(ERROR,
97                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
98                                                  errmsg("\"%s\" is not a table, view, composite type, or foreign table",
99                                                                 RelationGetRelationName(relation))));
100                         break;
101                 default:
102                         break;
103         }
104
105         /*
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.
109          */
110         if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE
111                 || stmt->objtype == OBJECT_ROLE)
112                 CreateSharedComments(address.objectId, address.classId, stmt->comment);
113         else
114                 CreateComments(address.objectId, address.classId, address.objectSubId,
115                                            stmt->comment);
116
117         /*
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
121          * activity.
122          */
123         if (relation != NULL)
124                 relation_close(relation, NoLock);
125 }
126
127 /*
128  * CreateComments --
129  *
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.
132  *
133  * If the comment given is null or an empty string, instead delete any
134  * existing comment for the specified key.
135  */
136 void
137 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
138 {
139         Relation        description;
140         ScanKeyData skey[3];
141         SysScanDesc sd;
142         HeapTuple       oldtuple;
143         HeapTuple       newtuple = NULL;
144         Datum           values[Natts_pg_description];
145         bool            nulls[Natts_pg_description];
146         bool            replaces[Natts_pg_description];
147         int                     i;
148
149         /* Reduce empty-string to NULL case */
150         if (comment != NULL && strlen(comment) == 0)
151                 comment = NULL;
152
153         /* Prepare to form or update a tuple, if necessary */
154         if (comment != NULL)
155         {
156                 for (i = 0; i < Natts_pg_description; i++)
157                 {
158                         nulls[i] = false;
159                         replaces[i] = true;
160                 }
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);
165         }
166
167         /* Use the index to search for a matching old tuple */
168
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));
181
182         description = heap_open(DescriptionRelationId, RowExclusiveLock);
183
184         sd = systable_beginscan(description, DescriptionObjIndexId, true,
185                                                         SnapshotNow, 3, skey);
186
187         while ((oldtuple = systable_getnext(sd)) != NULL)
188         {
189                 /* Found the old tuple, so delete or update it */
190
191                 if (comment == NULL)
192                         simple_heap_delete(description, &oldtuple->t_self);
193                 else
194                 {
195                         newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
196                                                                                  nulls, replaces);
197                         simple_heap_update(description, &oldtuple->t_self, newtuple);
198                 }
199
200                 break;                                  /* Assume there can be only one match */
201         }
202
203         systable_endscan(sd);
204
205         /* If we didn't find an old tuple, insert a new one */
206
207         if (newtuple == NULL && comment != NULL)
208         {
209                 newtuple = heap_form_tuple(RelationGetDescr(description),
210                                                                    values, nulls);
211                 simple_heap_insert(description, newtuple);
212         }
213
214         /* Update indexes, if necessary */
215         if (newtuple != NULL)
216         {
217                 CatalogUpdateIndexes(description, newtuple);
218                 heap_freetuple(newtuple);
219         }
220
221         /* Done */
222
223         heap_close(description, NoLock);
224 }
225
226 /*
227  * CreateSharedComments --
228  *
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.
231  *
232  * If the comment given is null or an empty string, instead delete any
233  * existing comment for the specified key.
234  */
235 void
236 CreateSharedComments(Oid oid, Oid classoid, char *comment)
237 {
238         Relation        shdescription;
239         ScanKeyData skey[2];
240         SysScanDesc sd;
241         HeapTuple       oldtuple;
242         HeapTuple       newtuple = NULL;
243         Datum           values[Natts_pg_shdescription];
244         bool            nulls[Natts_pg_shdescription];
245         bool            replaces[Natts_pg_shdescription];
246         int                     i;
247
248         /* Reduce empty-string to NULL case */
249         if (comment != NULL && strlen(comment) == 0)
250                 comment = NULL;
251
252         /* Prepare to form or update a tuple, if necessary */
253         if (comment != NULL)
254         {
255                 for (i = 0; i < Natts_pg_shdescription; i++)
256                 {
257                         nulls[i] = false;
258                         replaces[i] = true;
259                 }
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);
263         }
264
265         /* Use the index to search for a matching old tuple */
266
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));
275
276         shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
277
278         sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
279                                                         SnapshotNow, 2, skey);
280
281         while ((oldtuple = systable_getnext(sd)) != NULL)
282         {
283                 /* Found the old tuple, so delete or update it */
284
285                 if (comment == NULL)
286                         simple_heap_delete(shdescription, &oldtuple->t_self);
287                 else
288                 {
289                         newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
290                                                                                  values, nulls, replaces);
291                         simple_heap_update(shdescription, &oldtuple->t_self, newtuple);
292                 }
293
294                 break;                                  /* Assume there can be only one match */
295         }
296
297         systable_endscan(sd);
298
299         /* If we didn't find an old tuple, insert a new one */
300
301         if (newtuple == NULL && comment != NULL)
302         {
303                 newtuple = heap_form_tuple(RelationGetDescr(shdescription),
304                                                                    values, nulls);
305                 simple_heap_insert(shdescription, newtuple);
306         }
307
308         /* Update indexes, if necessary */
309         if (newtuple != NULL)
310         {
311                 CatalogUpdateIndexes(shdescription, newtuple);
312                 heap_freetuple(newtuple);
313         }
314
315         /* Done */
316
317         heap_close(shdescription, NoLock);
318 }
319
320 /*
321  * DeleteComments -- remove comments for an object
322  *
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).
326  */
327 void
328 DeleteComments(Oid oid, Oid classoid, int32 subid)
329 {
330         Relation        description;
331         ScanKeyData skey[3];
332         int                     nkeys;
333         SysScanDesc sd;
334         HeapTuple       oldtuple;
335
336         /* Use the index to search for all matching old tuples */
337
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));
346
347         if (subid != 0)
348         {
349                 ScanKeyInit(&skey[2],
350                                         Anum_pg_description_objsubid,
351                                         BTEqualStrategyNumber, F_INT4EQ,
352                                         Int32GetDatum(subid));
353                 nkeys = 3;
354         }
355         else
356                 nkeys = 2;
357
358         description = heap_open(DescriptionRelationId, RowExclusiveLock);
359
360         sd = systable_beginscan(description, DescriptionObjIndexId, true,
361                                                         SnapshotNow, nkeys, skey);
362
363         while ((oldtuple = systable_getnext(sd)) != NULL)
364                 simple_heap_delete(description, &oldtuple->t_self);
365
366         /* Done */
367
368         systable_endscan(sd);
369         heap_close(description, RowExclusiveLock);
370 }
371
372 /*
373  * DeleteSharedComments -- remove comments for a shared object
374  */
375 void
376 DeleteSharedComments(Oid oid, Oid classoid)
377 {
378         Relation        shdescription;
379         ScanKeyData skey[2];
380         SysScanDesc sd;
381         HeapTuple       oldtuple;
382
383         /* Use the index to search for all matching old tuples */
384
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));
393
394         shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
395
396         sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
397                                                         SnapshotNow, 2, skey);
398
399         while ((oldtuple = systable_getnext(sd)) != NULL)
400                 simple_heap_delete(shdescription, &oldtuple->t_self);
401
402         /* Done */
403
404         systable_endscan(sd);
405         heap_close(shdescription, RowExclusiveLock);
406 }
407
408 /*
409  * GetComment -- get the comment for an object, or null if not found.
410  */
411 char *
412 GetComment(Oid oid, Oid classoid, int32 subid)
413 {
414         Relation        description;
415         ScanKeyData skey[3];
416         SysScanDesc sd;
417         TupleDesc       tupdesc;
418         HeapTuple       tuple;
419         char       *comment;
420
421         /* Use the index to search for a matching old tuple */
422
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));
435
436         description = heap_open(DescriptionRelationId, AccessShareLock);
437         tupdesc = RelationGetDescr(description);
438
439         sd = systable_beginscan(description, DescriptionObjIndexId, true,
440                                                         SnapshotNow, 3, skey);
441
442         comment = NULL;
443         while ((tuple = systable_getnext(sd)) != NULL)
444         {
445                 Datum           value;
446                 bool            isnull;
447
448                 /* Found the tuple, get description field */
449                 value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
450                 if (!isnull)
451                         comment = TextDatumGetCString(value);
452                 break;                                  /* Assume there can be only one match */
453         }
454
455         systable_endscan(sd);
456
457         /* Done */
458         heap_close(description, AccessShareLock);
459
460         return comment;
461 }