1/*-------------------------------------------------------------------------
5 * PostgreSQL object comments utility code.
7 * Copyright (c) 1996-2025, PostgreSQL Global Development Group
10 * src/backend/commands/comment.c
12 *-------------------------------------------------------------------------
29#include "utils/fmgroids.h"
36 * This routine is used to add the associated comment into
37 * pg_description for the object specified by the given SQL command.
46 * When loading a dump, we may see a COMMENT ON DATABASE for the old name
47 * of the database. Erroring out would prevent pg_restore from completing
48 * (which is really pg_restore's fault, but for now we will work around
49 * the problem here). Consensus is that the best fix is to treat wrong
50 * database name as a WARNING not an ERROR; hence, the following special
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.
75 /* Require ownership of the target object. */
77 stmt->object, relation);
79 /* Perform other integrity checks as needed. */
80 switch (
stmt->objtype)
85 * Allow comments only on columns of tables, views, materialized
86 * views, composite types, and foreign tables (which are the only
87 * relkinds for which pg_dump will dump per-column comments). In
88 * particular we wish to disallow comments on index columns,
89 * because the naming of an index's columns may change across PG
90 * versions, so dumping per-column comments could create reload
93 if (relation->
rd_rel->relkind != RELKIND_RELATION &&
94 relation->
rd_rel->relkind != RELKIND_VIEW &&
95 relation->
rd_rel->relkind != RELKIND_MATVIEW &&
96 relation->
rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
97 relation->
rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
98 relation->
rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
100 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
101 errmsg(
"cannot set comment on relation \"%s\"",
110 * Databases, tablespaces, and roles are cluster-wide objects, so any
111 * comments on those objects are recorded in the shared pg_shdescription
112 * catalog. Comments on all other objects are recorded in pg_description.
122 * If get_object_address() opened the relation for us, we close it to keep
123 * the reference count correct - but we retain any locks acquired by
124 * get_object_address() until commit time, to guard against concurrent
127 if (relation != NULL)
136 * Create a comment for the specified object descriptor. Inserts a new
137 * pg_description tuple, or replaces an existing one with the same key.
139 * If the comment given is null or an empty string, instead delete any
140 * existing comment for the specified key.
151 bool nulls[Natts_pg_description];
152 bool replaces[Natts_pg_description];
155 /* Reduce empty-string to NULL case */
159 /* Prepare to form or update a tuple, if necessary */
162 for (
i = 0;
i < Natts_pg_description;
i++)
173 /* Use the index to search for a matching old tuple */
176 Anum_pg_description_objoid,
180 Anum_pg_description_classoid,
184 Anum_pg_description_objsubid,
195 /* Found the old tuple, so delete or update it */
206 break;
/* Assume there can be only one match */
211 /* If we didn't find an old tuple, insert a new one */
213 if (newtuple == NULL &&
comment != NULL)
220 if (newtuple != NULL)
229 * CreateSharedComments --
231 * Create a comment for the specified shared object descriptor. Inserts a
232 * new pg_shdescription tuple, or replaces an existing one with the same key.
234 * If the comment given is null or an empty string, instead delete any
235 * existing comment for the specified key.
246 bool nulls[Natts_pg_shdescription];
247 bool replaces[Natts_pg_shdescription];
250 /* Reduce empty-string to NULL case */
254 /* Prepare to form or update a tuple, if necessary */
257 for (
i = 0;
i < Natts_pg_shdescription;
i++)
267 /* Use the index to search for a matching old tuple */
270 Anum_pg_shdescription_objoid,
274 Anum_pg_shdescription_classoid,
285 /* Found the old tuple, so delete or update it */
296 break;
/* Assume there can be only one match */
301 /* If we didn't find an old tuple, insert a new one */
303 if (newtuple == NULL &&
comment != NULL)
310 if (newtuple != NULL)
319 * DeleteComments -- remove comments for an object
321 * If subid is nonzero then only comments matching it will be removed.
322 * If subid is zero, all comments matching the oid/classoid will be removed
323 * (this corresponds to deleting a whole object).
334 /* Use the index to search for all matching old tuples */
337 Anum_pg_description_objoid,
341 Anum_pg_description_classoid,
348 Anum_pg_description_objsubid,
371 * DeleteSharedComments -- remove comments for a shared object
381 /* Use the index to search for all matching old tuples */
384 Anum_pg_shdescription_objoid,
388 Anum_pg_shdescription_classoid,
407 * GetComment -- get the comment for an object, or null if not found.
419 /* Use the index to search for a matching old tuple */
422 Anum_pg_description_objoid,
426 Anum_pg_description_classoid,
430 Anum_pg_description_objsubid,
446 /* Found the tuple, get description field */
450 break;
/* Assume there can be only one match */
static Datum values[MAXATTR]
#define CStringGetTextDatum(s)
#define TextDatumGetCString(d)
#define OidIsValid(objectId)
Oid get_database_oid(const char *dbname, bool missing_ok)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
void systable_endscan(SysScanDesc sysscan)
HeapTuple systable_getnext(SysScanDesc sysscan)
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
void heap_freetuple(HeapTuple htup)
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
#define ShareUpdateExclusiveLock
void check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, Node *object, Relation relation)
const ObjectAddress InvalidObjectAddress
ObjectAddress get_object_address(ObjectType objtype, Node *object, Relation *relp, LOCKMODE lockmode, bool missing_ok)
int errdetail_relkind_not_supported(char relkind)
static Datum ObjectIdGetDatum(Oid X)
static Datum Int32GetDatum(int32 X)
#define RelationGetDescr(relation)
#define RelationGetRelationName(relation)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
void relation_close(Relation relation, LOCKMODE lockmode)
#define BTEqualStrategyNumber
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)