Header And Logo

PostgreSQL
| The world's most advanced open source database.

comment.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * comment.c
00004  *
00005  * PostgreSQL object comments utility code.
00006  *
00007  * Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  *
00009  * IDENTIFICATION
00010  *    src/backend/commands/comment.c
00011  *
00012  *-------------------------------------------------------------------------
00013  */
00014 
00015 #include "postgres.h"
00016 
00017 #include "access/genam.h"
00018 #include "access/heapam.h"
00019 #include "access/htup_details.h"
00020 #include "catalog/indexing.h"
00021 #include "catalog/objectaddress.h"
00022 #include "catalog/pg_description.h"
00023 #include "catalog/pg_shdescription.h"
00024 #include "commands/comment.h"
00025 #include "commands/dbcommands.h"
00026 #include "miscadmin.h"
00027 #include "utils/builtins.h"
00028 #include "utils/fmgroids.h"
00029 #include "utils/rel.h"
00030 #include "utils/tqual.h"
00031 
00032 
00033 /*
00034  * CommentObject --
00035  *
00036  * This routine is used to add the associated comment into
00037  * pg_description for the object specified by the given SQL command.
00038  */
00039 Oid
00040 CommentObject(CommentStmt *stmt)
00041 {
00042     ObjectAddress address;
00043     Relation    relation;
00044 
00045     /*
00046      * When loading a dump, we may see a COMMENT ON DATABASE for the old name
00047      * of the database.  Erroring out would prevent pg_restore from completing
00048      * (which is really pg_restore's fault, but for now we will work around
00049      * the problem here).  Consensus is that the best fix is to treat wrong
00050      * database name as a WARNING not an ERROR; hence, the following special
00051      * case.  (If the length of stmt->objname is not 1, get_object_address
00052      * will throw an error below; that's OK.)
00053      */
00054     if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1)
00055     {
00056         char       *database = strVal(linitial(stmt->objname));
00057 
00058         if (!OidIsValid(get_database_oid(database, true)))
00059         {
00060             ereport(WARNING,
00061                     (errcode(ERRCODE_UNDEFINED_DATABASE),
00062                      errmsg("database \"%s\" does not exist", database)));
00063             return InvalidOid;
00064         }
00065     }
00066 
00067     /*
00068      * Translate the parser representation that identifies this object into an
00069      * ObjectAddress.  get_object_address() will throw an error if the object
00070      * does not exist, and will also acquire a lock on the target to guard
00071      * against concurrent DROP operations.
00072      */
00073     address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs,
00074                                  &relation, ShareUpdateExclusiveLock, false);
00075 
00076     /* Require ownership of the target object. */
00077     check_object_ownership(GetUserId(), stmt->objtype, address,
00078                            stmt->objname, stmt->objargs, relation);
00079 
00080     /* Perform other integrity checks as needed. */
00081     switch (stmt->objtype)
00082     {
00083         case OBJECT_COLUMN:
00084 
00085             /*
00086              * Allow comments only on columns of tables, views, materialized
00087              * views, composite types, and foreign tables (which are the only
00088              * relkinds for which pg_dump will dump per-column comments).  In
00089              * particular we wish to disallow comments on index columns,
00090              * because the naming of an index's columns may change across PG
00091              * versions, so dumping per-column comments could create reload
00092              * failures.
00093              */
00094             if (relation->rd_rel->relkind != RELKIND_RELATION &&
00095                 relation->rd_rel->relkind != RELKIND_VIEW &&
00096                 relation->rd_rel->relkind != RELKIND_MATVIEW &&
00097                 relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
00098                 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
00099                 ereport(ERROR,
00100                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00101                          errmsg("\"%s\" is not a table, view, composite type, or foreign table",
00102                                 RelationGetRelationName(relation))));
00103             break;
00104         default:
00105             break;
00106     }
00107 
00108     /*
00109      * Databases, tablespaces, and roles are cluster-wide objects, so any
00110      * comments on those objects are recorded in the shared pg_shdescription
00111      * catalog.  Comments on all other objects are recorded in pg_description.
00112      */
00113     if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE
00114         || stmt->objtype == OBJECT_ROLE)
00115         CreateSharedComments(address.objectId, address.classId, stmt->comment);
00116     else
00117         CreateComments(address.objectId, address.classId, address.objectSubId,
00118                        stmt->comment);
00119 
00120     /*
00121      * If get_object_address() opened the relation for us, we close it to keep
00122      * the reference count correct - but we retain any locks acquired by
00123      * get_object_address() until commit time, to guard against concurrent
00124      * activity.
00125      */
00126     if (relation != NULL)
00127         relation_close(relation, NoLock);
00128 
00129     return address.objectId;
00130 }
00131 
00132 /*
00133  * CreateComments --
00134  *
00135  * Create a comment for the specified object descriptor.  Inserts a new
00136  * pg_description tuple, or replaces an existing one with the same key.
00137  *
00138  * If the comment given is null or an empty string, instead delete any
00139  * existing comment for the specified key.
00140  */
00141 void
00142 CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
00143 {
00144     Relation    description;
00145     ScanKeyData skey[3];
00146     SysScanDesc sd;
00147     HeapTuple   oldtuple;
00148     HeapTuple   newtuple = NULL;
00149     Datum       values[Natts_pg_description];
00150     bool        nulls[Natts_pg_description];
00151     bool        replaces[Natts_pg_description];
00152     int         i;
00153 
00154     /* Reduce empty-string to NULL case */
00155     if (comment != NULL && strlen(comment) == 0)
00156         comment = NULL;
00157 
00158     /* Prepare to form or update a tuple, if necessary */
00159     if (comment != NULL)
00160     {
00161         for (i = 0; i < Natts_pg_description; i++)
00162         {
00163             nulls[i] = false;
00164             replaces[i] = true;
00165         }
00166         values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(oid);
00167         values[Anum_pg_description_classoid - 1] = ObjectIdGetDatum(classoid);
00168         values[Anum_pg_description_objsubid - 1] = Int32GetDatum(subid);
00169         values[Anum_pg_description_description - 1] = CStringGetTextDatum(comment);
00170     }
00171 
00172     /* Use the index to search for a matching old tuple */
00173 
00174     ScanKeyInit(&skey[0],
00175                 Anum_pg_description_objoid,
00176                 BTEqualStrategyNumber, F_OIDEQ,
00177                 ObjectIdGetDatum(oid));
00178     ScanKeyInit(&skey[1],
00179                 Anum_pg_description_classoid,
00180                 BTEqualStrategyNumber, F_OIDEQ,
00181                 ObjectIdGetDatum(classoid));
00182     ScanKeyInit(&skey[2],
00183                 Anum_pg_description_objsubid,
00184                 BTEqualStrategyNumber, F_INT4EQ,
00185                 Int32GetDatum(subid));
00186 
00187     description = heap_open(DescriptionRelationId, RowExclusiveLock);
00188 
00189     sd = systable_beginscan(description, DescriptionObjIndexId, true,
00190                             SnapshotNow, 3, skey);
00191 
00192     while ((oldtuple = systable_getnext(sd)) != NULL)
00193     {
00194         /* Found the old tuple, so delete or update it */
00195 
00196         if (comment == NULL)
00197             simple_heap_delete(description, &oldtuple->t_self);
00198         else
00199         {
00200             newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
00201                                          nulls, replaces);
00202             simple_heap_update(description, &oldtuple->t_self, newtuple);
00203         }
00204 
00205         break;                  /* Assume there can be only one match */
00206     }
00207 
00208     systable_endscan(sd);
00209 
00210     /* If we didn't find an old tuple, insert a new one */
00211 
00212     if (newtuple == NULL && comment != NULL)
00213     {
00214         newtuple = heap_form_tuple(RelationGetDescr(description),
00215                                    values, nulls);
00216         simple_heap_insert(description, newtuple);
00217     }
00218 
00219     /* Update indexes, if necessary */
00220     if (newtuple != NULL)
00221     {
00222         CatalogUpdateIndexes(description, newtuple);
00223         heap_freetuple(newtuple);
00224     }
00225 
00226     /* Done */
00227 
00228     heap_close(description, NoLock);
00229 }
00230 
00231 /*
00232  * CreateSharedComments --
00233  *
00234  * Create a comment for the specified shared object descriptor.  Inserts a
00235  * new pg_shdescription tuple, or replaces an existing one with the same key.
00236  *
00237  * If the comment given is null or an empty string, instead delete any
00238  * existing comment for the specified key.
00239  */
00240 void
00241 CreateSharedComments(Oid oid, Oid classoid, char *comment)
00242 {
00243     Relation    shdescription;
00244     ScanKeyData skey[2];
00245     SysScanDesc sd;
00246     HeapTuple   oldtuple;
00247     HeapTuple   newtuple = NULL;
00248     Datum       values[Natts_pg_shdescription];
00249     bool        nulls[Natts_pg_shdescription];
00250     bool        replaces[Natts_pg_shdescription];
00251     int         i;
00252 
00253     /* Reduce empty-string to NULL case */
00254     if (comment != NULL && strlen(comment) == 0)
00255         comment = NULL;
00256 
00257     /* Prepare to form or update a tuple, if necessary */
00258     if (comment != NULL)
00259     {
00260         for (i = 0; i < Natts_pg_shdescription; i++)
00261         {
00262             nulls[i] = false;
00263             replaces[i] = true;
00264         }
00265         values[Anum_pg_shdescription_objoid - 1] = ObjectIdGetDatum(oid);
00266         values[Anum_pg_shdescription_classoid - 1] = ObjectIdGetDatum(classoid);
00267         values[Anum_pg_shdescription_description - 1] = CStringGetTextDatum(comment);
00268     }
00269 
00270     /* Use the index to search for a matching old tuple */
00271 
00272     ScanKeyInit(&skey[0],
00273                 Anum_pg_shdescription_objoid,
00274                 BTEqualStrategyNumber, F_OIDEQ,
00275                 ObjectIdGetDatum(oid));
00276     ScanKeyInit(&skey[1],
00277                 Anum_pg_shdescription_classoid,
00278                 BTEqualStrategyNumber, F_OIDEQ,
00279                 ObjectIdGetDatum(classoid));
00280 
00281     shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
00282 
00283     sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
00284                             SnapshotNow, 2, skey);
00285 
00286     while ((oldtuple = systable_getnext(sd)) != NULL)
00287     {
00288         /* Found the old tuple, so delete or update it */
00289 
00290         if (comment == NULL)
00291             simple_heap_delete(shdescription, &oldtuple->t_self);
00292         else
00293         {
00294             newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
00295                                          values, nulls, replaces);
00296             simple_heap_update(shdescription, &oldtuple->t_self, newtuple);
00297         }
00298 
00299         break;                  /* Assume there can be only one match */
00300     }
00301 
00302     systable_endscan(sd);
00303 
00304     /* If we didn't find an old tuple, insert a new one */
00305 
00306     if (newtuple == NULL && comment != NULL)
00307     {
00308         newtuple = heap_form_tuple(RelationGetDescr(shdescription),
00309                                    values, nulls);
00310         simple_heap_insert(shdescription, newtuple);
00311     }
00312 
00313     /* Update indexes, if necessary */
00314     if (newtuple != NULL)
00315     {
00316         CatalogUpdateIndexes(shdescription, newtuple);
00317         heap_freetuple(newtuple);
00318     }
00319 
00320     /* Done */
00321 
00322     heap_close(shdescription, NoLock);
00323 }
00324 
00325 /*
00326  * DeleteComments -- remove comments for an object
00327  *
00328  * If subid is nonzero then only comments matching it will be removed.
00329  * If subid is zero, all comments matching the oid/classoid will be removed
00330  * (this corresponds to deleting a whole object).
00331  */
00332 void
00333 DeleteComments(Oid oid, Oid classoid, int32 subid)
00334 {
00335     Relation    description;
00336     ScanKeyData skey[3];
00337     int         nkeys;
00338     SysScanDesc sd;
00339     HeapTuple   oldtuple;
00340 
00341     /* Use the index to search for all matching old tuples */
00342 
00343     ScanKeyInit(&skey[0],
00344                 Anum_pg_description_objoid,
00345                 BTEqualStrategyNumber, F_OIDEQ,
00346                 ObjectIdGetDatum(oid));
00347     ScanKeyInit(&skey[1],
00348                 Anum_pg_description_classoid,
00349                 BTEqualStrategyNumber, F_OIDEQ,
00350                 ObjectIdGetDatum(classoid));
00351 
00352     if (subid != 0)
00353     {
00354         ScanKeyInit(&skey[2],
00355                     Anum_pg_description_objsubid,
00356                     BTEqualStrategyNumber, F_INT4EQ,
00357                     Int32GetDatum(subid));
00358         nkeys = 3;
00359     }
00360     else
00361         nkeys = 2;
00362 
00363     description = heap_open(DescriptionRelationId, RowExclusiveLock);
00364 
00365     sd = systable_beginscan(description, DescriptionObjIndexId, true,
00366                             SnapshotNow, nkeys, skey);
00367 
00368     while ((oldtuple = systable_getnext(sd)) != NULL)
00369         simple_heap_delete(description, &oldtuple->t_self);
00370 
00371     /* Done */
00372 
00373     systable_endscan(sd);
00374     heap_close(description, RowExclusiveLock);
00375 }
00376 
00377 /*
00378  * DeleteSharedComments -- remove comments for a shared object
00379  */
00380 void
00381 DeleteSharedComments(Oid oid, Oid classoid)
00382 {
00383     Relation    shdescription;
00384     ScanKeyData skey[2];
00385     SysScanDesc sd;
00386     HeapTuple   oldtuple;
00387 
00388     /* Use the index to search for all matching old tuples */
00389 
00390     ScanKeyInit(&skey[0],
00391                 Anum_pg_shdescription_objoid,
00392                 BTEqualStrategyNumber, F_OIDEQ,
00393                 ObjectIdGetDatum(oid));
00394     ScanKeyInit(&skey[1],
00395                 Anum_pg_shdescription_classoid,
00396                 BTEqualStrategyNumber, F_OIDEQ,
00397                 ObjectIdGetDatum(classoid));
00398 
00399     shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
00400 
00401     sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
00402                             SnapshotNow, 2, skey);
00403 
00404     while ((oldtuple = systable_getnext(sd)) != NULL)
00405         simple_heap_delete(shdescription, &oldtuple->t_self);
00406 
00407     /* Done */
00408 
00409     systable_endscan(sd);
00410     heap_close(shdescription, RowExclusiveLock);
00411 }
00412 
00413 /*
00414  * GetComment -- get the comment for an object, or null if not found.
00415  */
00416 char *
00417 GetComment(Oid oid, Oid classoid, int32 subid)
00418 {
00419     Relation    description;
00420     ScanKeyData skey[3];
00421     SysScanDesc sd;
00422     TupleDesc   tupdesc;
00423     HeapTuple   tuple;
00424     char       *comment;
00425 
00426     /* Use the index to search for a matching old tuple */
00427 
00428     ScanKeyInit(&skey[0],
00429                 Anum_pg_description_objoid,
00430                 BTEqualStrategyNumber, F_OIDEQ,
00431                 ObjectIdGetDatum(oid));
00432     ScanKeyInit(&skey[1],
00433                 Anum_pg_description_classoid,
00434                 BTEqualStrategyNumber, F_OIDEQ,
00435                 ObjectIdGetDatum(classoid));
00436     ScanKeyInit(&skey[2],
00437                 Anum_pg_description_objsubid,
00438                 BTEqualStrategyNumber, F_INT4EQ,
00439                 Int32GetDatum(subid));
00440 
00441     description = heap_open(DescriptionRelationId, AccessShareLock);
00442     tupdesc = RelationGetDescr(description);
00443 
00444     sd = systable_beginscan(description, DescriptionObjIndexId, true,
00445                             SnapshotNow, 3, skey);
00446 
00447     comment = NULL;
00448     while ((tuple = systable_getnext(sd)) != NULL)
00449     {
00450         Datum       value;
00451         bool        isnull;
00452 
00453         /* Found the tuple, get description field */
00454         value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
00455         if (!isnull)
00456             comment = TextDatumGetCString(value);
00457         break;                  /* Assume there can be only one match */
00458     }
00459 
00460     systable_endscan(sd);
00461 
00462     /* Done */
00463     heap_close(description, AccessShareLock);
00464 
00465     return comment;
00466 }