1999-10-26 05:12:39 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* comment.c
|
2000-01-10 18:14:46 +01:00
|
|
|
*
|
1999-10-26 05:12:39 +02:00
|
|
|
* PostgreSQL object comments utility code.
|
|
|
|
*
|
2024-01-04 02:49:05 +01:00
|
|
|
* Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
1999-10-26 05:12:39 +02:00
|
|
|
*
|
2001-01-23 05:32:23 +01:00
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/commands/comment.c
|
2001-01-23 05:32:23 +01:00
|
|
|
*
|
1999-10-26 05:12:39 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2019-12-27 00:09:00 +01:00
|
|
|
#include "access/genam.h"
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2019-01-21 19:18:20 +01:00
|
|
|
#include "access/relation.h"
|
|
|
|
#include "access/table.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "catalog/indexing.h"
|
2010-08-27 13:47:41 +02:00
|
|
|
#include "catalog/objectaddress.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "catalog/pg_description.h"
|
2006-02-12 04:22:21 +01:00
|
|
|
#include "catalog/pg_shdescription.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "commands/comment.h"
|
2002-08-09 18:45:16 +02:00
|
|
|
#include "commands/dbcommands.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "miscadmin.h"
|
2001-06-25 23:11:45 +02:00
|
|
|
#include "utils/builtins.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/fmgroids.h"
|
2011-03-26 04:10:07 +01:00
|
|
|
#include "utils/rel.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2002-04-09 22:35:55 +02:00
|
|
|
|
|
|
|
/*
|
1999-10-26 05:12:39 +02:00
|
|
|
* CommentObject --
|
|
|
|
*
|
2000-01-10 18:14:46 +01:00
|
|
|
* This routine is used to add the associated comment into
|
2002-04-09 22:35:55 +02:00
|
|
|
* pg_description for the object specified by the given SQL command.
|
|
|
|
*/
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress
|
2002-04-09 22:35:55 +02:00
|
|
|
CommentObject(CommentStmt *stmt)
|
1999-10-26 05:12:39 +02:00
|
|
|
{
|
2010-08-27 13:47:41 +02:00
|
|
|
Relation relation;
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
ObjectAddress address = InvalidObjectAddress;
|
2010-08-27 13:47:41 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* When loading a dump, we may see a COMMENT ON DATABASE for the old name
|
|
|
|
* of the database. Erroring out would prevent pg_restore from completing
|
|
|
|
* (which is really pg_restore's fault, but for now we will work around
|
|
|
|
* the problem here). Consensus is that the best fix is to treat wrong
|
|
|
|
* database name as a WARNING not an ERROR; hence, the following special
|
Remove objname/objargs split for referring to objects
In simpler times, it might have worked to refer to all kinds of objects
by a list of name components and an optional argument list. But this
doesn't work for all objects, which has resulted in a collection of
hacks to place various other nodes types into these fields, which have
to be unpacked at the other end. This makes it also weird to represent
lists of such things in the grammar, because they would have to be lists
of singleton lists, to make the unpacking work consistently. The other
problem is that keeping separate name and args fields makes it awkward
to deal with lists of functions.
Change that by dropping the objargs field and have objname, renamed to
object, be a generic Node, which can then be flexibly assigned and
managed using the normal Node mechanisms. In many cases it will still
be a List of names, in some cases it will be a string Value, for types
it will be the existing Typename, for functions it will now use the
existing ObjectWithArgs node type. Some of the more obscure object
types still use somewhat arbitrary nested lists.
Reviewed-by: Jim Nasby <Jim.Nasby@BlueTreble.com>
Reviewed-by: Michael Paquier <michael.paquier@gmail.com>
2016-11-12 18:00:00 +01:00
|
|
|
* case.
|
2010-08-27 13:47:41 +02:00
|
|
|
*/
|
Remove objname/objargs split for referring to objects
In simpler times, it might have worked to refer to all kinds of objects
by a list of name components and an optional argument list. But this
doesn't work for all objects, which has resulted in a collection of
hacks to place various other nodes types into these fields, which have
to be unpacked at the other end. This makes it also weird to represent
lists of such things in the grammar, because they would have to be lists
of singleton lists, to make the unpacking work consistently. The other
problem is that keeping separate name and args fields makes it awkward
to deal with lists of functions.
Change that by dropping the objargs field and have objname, renamed to
object, be a generic Node, which can then be flexibly assigned and
managed using the normal Node mechanisms. In many cases it will still
be a List of names, in some cases it will be a string Value, for types
it will be the existing Typename, for functions it will now use the
existing ObjectWithArgs node type. Some of the more obscure object
types still use somewhat arbitrary nested lists.
Reviewed-by: Jim Nasby <Jim.Nasby@BlueTreble.com>
Reviewed-by: Michael Paquier <michael.paquier@gmail.com>
2016-11-12 18:00:00 +01:00
|
|
|
if (stmt->objtype == OBJECT_DATABASE)
|
2010-08-27 13:47:41 +02:00
|
|
|
{
|
2021-09-09 07:58:12 +02:00
|
|
|
char *database = strVal(stmt->object);
|
2011-04-10 17:42:00 +02:00
|
|
|
|
2010-08-27 13:47:41 +02:00
|
|
|
if (!OidIsValid(get_database_oid(database, true)))
|
|
|
|
{
|
|
|
|
ereport(WARNING,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
|
|
errmsg("database \"%s\" does not exist", database)));
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
2010-08-27 13:47:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-03-04 22:08:24 +01:00
|
|
|
* Translate the parser representation that identifies this object into an
|
|
|
|
* ObjectAddress. get_object_address() will throw an error if the object
|
2010-08-27 13:47:41 +02:00
|
|
|
* does not exist, and will also acquire a lock on the target to guard
|
|
|
|
* against concurrent DROP operations.
|
|
|
|
*/
|
Remove objname/objargs split for referring to objects
In simpler times, it might have worked to refer to all kinds of objects
by a list of name components and an optional argument list. But this
doesn't work for all objects, which has resulted in a collection of
hacks to place various other nodes types into these fields, which have
to be unpacked at the other end. This makes it also weird to represent
lists of such things in the grammar, because they would have to be lists
of singleton lists, to make the unpacking work consistently. The other
problem is that keeping separate name and args fields makes it awkward
to deal with lists of functions.
Change that by dropping the objargs field and have objname, renamed to
object, be a generic Node, which can then be flexibly assigned and
managed using the normal Node mechanisms. In many cases it will still
be a List of names, in some cases it will be a string Value, for types
it will be the existing Typename, for functions it will now use the
existing ObjectWithArgs node type. Some of the more obscure object
types still use somewhat arbitrary nested lists.
Reviewed-by: Jim Nasby <Jim.Nasby@BlueTreble.com>
Reviewed-by: Michael Paquier <michael.paquier@gmail.com>
2016-11-12 18:00:00 +01:00
|
|
|
address = get_object_address(stmt->objtype, stmt->object,
|
2011-06-28 03:17:25 +02:00
|
|
|
&relation, ShareUpdateExclusiveLock, false);
|
2010-08-27 13:47:41 +02:00
|
|
|
|
2011-03-04 22:08:24 +01:00
|
|
|
/* Require ownership of the target object. */
|
|
|
|
check_object_ownership(GetUserId(), stmt->objtype, address,
|
Remove objname/objargs split for referring to objects
In simpler times, it might have worked to refer to all kinds of objects
by a list of name components and an optional argument list. But this
doesn't work for all objects, which has resulted in a collection of
hacks to place various other nodes types into these fields, which have
to be unpacked at the other end. This makes it also weird to represent
lists of such things in the grammar, because they would have to be lists
of singleton lists, to make the unpacking work consistently. The other
problem is that keeping separate name and args fields makes it awkward
to deal with lists of functions.
Change that by dropping the objargs field and have objname, renamed to
object, be a generic Node, which can then be flexibly assigned and
managed using the normal Node mechanisms. In many cases it will still
be a List of names, in some cases it will be a string Value, for types
it will be the existing Typename, for functions it will now use the
existing ObjectWithArgs node type. Some of the more obscure object
types still use somewhat arbitrary nested lists.
Reviewed-by: Jim Nasby <Jim.Nasby@BlueTreble.com>
Reviewed-by: Michael Paquier <michael.paquier@gmail.com>
2016-11-12 18:00:00 +01:00
|
|
|
stmt->object, relation);
|
2011-03-04 22:08:24 +01:00
|
|
|
|
|
|
|
/* Perform other integrity checks as needed. */
|
2002-04-09 22:35:55 +02:00
|
|
|
switch (stmt->objtype)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_COLUMN:
|
2011-04-10 17:42:00 +02:00
|
|
|
|
2011-03-04 22:08:24 +01:00
|
|
|
/*
|
2013-03-04 01:23:31 +01:00
|
|
|
* Allow comments only on columns of tables, views, materialized
|
|
|
|
* views, composite types, and foreign tables (which are the only
|
|
|
|
* relkinds for which pg_dump will dump per-column comments). In
|
|
|
|
* particular we wish to disallow comments on index columns,
|
|
|
|
* because the naming of an index's columns may change across PG
|
|
|
|
* versions, so dumping per-column comments could create reload
|
|
|
|
* failures.
|
2011-03-04 22:08:24 +01:00
|
|
|
*/
|
|
|
|
if (relation->rd_rel->relkind != RELKIND_RELATION &&
|
|
|
|
relation->rd_rel->relkind != RELKIND_VIEW &&
|
2013-03-04 01:23:31 +01:00
|
|
|
relation->rd_rel->relkind != RELKIND_MATVIEW &&
|
2011-03-04 22:08:24 +01:00
|
|
|
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
|
2017-04-18 11:42:10 +02:00
|
|
|
relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
|
|
|
|
relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
|
2010-08-27 13:47:41 +02:00
|
|
|
ereport(ERROR,
|
2011-03-04 22:08:24 +01:00
|
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
Improve error messages about mismatching relkind
Most error messages about a relkind that was not supported or
appropriate for the command was of the pattern
"relation \"%s\" is not a table, foreign table, or materialized view"
This style can become verbose and tedious to maintain. Moreover, it's
not very helpful: If I'm trying to create a comment on a TOAST table,
which is not supported, then the information that I could have created
a comment on a materialized view is pointless.
Instead, write the primary error message shorter and saying more
directly that what was attempted is not possible. Then, in the detail
message, explain that the operation is not supported for the relkind
the object was. To simplify that, add a new function
errdetail_relkind_not_supported() that does this.
In passing, make use of RELKIND_HAS_STORAGE() where appropriate,
instead of listing out the relkinds individually.
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Discussion: https://www.postgresql.org/message-id/flat/dc35a398-37d0-75ce-07ea-1dd71d98f8ec@2ndquadrant.com
2021-07-08 09:38:52 +02:00
|
|
|
errmsg("cannot set comment on relation \"%s\"",
|
|
|
|
RelationGetRelationName(relation)),
|
|
|
|
errdetail_relkind_not_supported(relation->rd_rel->relkind)));
|
2007-08-21 03:11:32 +02:00
|
|
|
break;
|
1999-10-26 05:12:39 +02:00
|
|
|
default:
|
2011-03-04 22:08:24 +01:00
|
|
|
break;
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
2010-08-27 13:47:41 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Databases, tablespaces, and roles are cluster-wide objects, so any
|
|
|
|
* comments on those objects are recorded in the shared pg_shdescription
|
|
|
|
* catalog. Comments on all other objects are recorded in pg_description.
|
|
|
|
*/
|
|
|
|
if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE
|
2010-11-23 21:27:50 +01:00
|
|
|
|| stmt->objtype == OBJECT_ROLE)
|
2010-08-27 13:47:41 +02:00
|
|
|
CreateSharedComments(address.objectId, address.classId, stmt->comment);
|
|
|
|
else
|
|
|
|
CreateComments(address.objectId, address.classId, address.objectSubId,
|
|
|
|
stmt->comment);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If get_object_address() opened the relation for us, we close it to keep
|
|
|
|
* the reference count correct - but we retain any locks acquired by
|
|
|
|
* get_object_address() until commit time, to guard against concurrent
|
|
|
|
* activity.
|
|
|
|
*/
|
|
|
|
if (relation != NULL)
|
|
|
|
relation_close(relation, NoLock);
|
2012-12-29 13:55:37 +01:00
|
|
|
|
Change many routines to return ObjectAddress rather than OID
The changed routines are mostly those that can be directly called by
ProcessUtilitySlow; the intention is to make the affected object
information more precise, in support for future event trigger changes.
Originally it was envisioned that the OID of the affected object would
be enough, and in most cases that is correct, but upon actually
implementing the event trigger changes it turned out that ObjectAddress
is more widely useful.
Additionally, some command execution routines grew an output argument
that's an object address which provides further info about the executed
command. To wit:
* for ALTER DOMAIN / ADD CONSTRAINT, it corresponds to the address of
the new constraint
* for ALTER OBJECT / SET SCHEMA, it corresponds to the address of the
schema that originally contained the object.
* for ALTER EXTENSION {ADD, DROP} OBJECT, it corresponds to the address
of the object added to or dropped from the extension.
There's no user-visible change in this commit, and no functional change
either.
Discussion: 20150218213255.GC6717@tamriel.snowman.net
Reviewed-By: Stephen Frost, Andres Freund
2015-03-03 18:10:50 +01:00
|
|
|
return address;
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
2002-04-09 22:35:55 +02:00
|
|
|
/*
|
1999-10-26 05:12:39 +02:00
|
|
|
* CreateComments --
|
2000-01-10 18:14:46 +01:00
|
|
|
*
|
2001-08-10 20:57:42 +02:00
|
|
|
* Create a comment for the specified object descriptor. Inserts a new
|
|
|
|
* pg_description tuple, or replaces an existing one with the same key.
|
1999-10-26 05:12:39 +02:00
|
|
|
*
|
2001-08-10 20:57:42 +02:00
|
|
|
* If the comment given is null or an empty string, instead delete any
|
|
|
|
* existing comment for the specified key.
|
1999-10-26 05:12:39 +02:00
|
|
|
*/
|
2001-08-10 20:57:42 +02:00
|
|
|
void
|
2017-10-31 15:34:31 +01:00
|
|
|
CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
|
1999-10-26 05:12:39 +02:00
|
|
|
{
|
|
|
|
Relation description;
|
2001-08-10 20:57:42 +02:00
|
|
|
ScanKeyData skey[3];
|
2002-05-21 01:51:44 +02:00
|
|
|
SysScanDesc sd;
|
|
|
|
HeapTuple oldtuple;
|
2001-08-10 20:57:42 +02:00
|
|
|
HeapTuple newtuple = NULL;
|
1999-10-26 05:12:39 +02:00
|
|
|
Datum values[Natts_pg_description];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_description];
|
|
|
|
bool replaces[Natts_pg_description];
|
1999-10-26 05:12:39 +02:00
|
|
|
int i;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Reduce empty-string to NULL case */
|
|
|
|
if (comment != NULL && strlen(comment) == 0)
|
|
|
|
comment = NULL;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Prepare to form or update a tuple, if necessary */
|
|
|
|
if (comment != NULL)
|
1999-10-26 05:12:39 +02:00
|
|
|
{
|
|
|
|
for (i = 0; i < Natts_pg_description; i++)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
nulls[i] = false;
|
|
|
|
replaces[i] = true;
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
2011-06-16 23:03:58 +02:00
|
|
|
values[Anum_pg_description_objoid - 1] = ObjectIdGetDatum(oid);
|
|
|
|
values[Anum_pg_description_classoid - 1] = ObjectIdGetDatum(classoid);
|
|
|
|
values[Anum_pg_description_objsubid - 1] = Int32GetDatum(subid);
|
|
|
|
values[Anum_pg_description_description - 1] = CStringGetTextDatum(comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Use the index to search for a matching old tuple */
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_description_objoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oid));
|
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_description_classoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
ScanKeyInit(&skey[2],
|
|
|
|
Anum_pg_description_objsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(subid));
|
2001-08-10 20:57:42 +02:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
description = table_open(DescriptionRelationId, RowExclusiveLock);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
sd = systable_beginscan(description, DescriptionObjIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 3, skey);
|
2001-08-10 20:57:42 +02:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
while ((oldtuple = systable_getnext(sd)) != NULL)
|
1999-10-26 05:12:39 +02:00
|
|
|
{
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Found the old tuple, so delete or update it */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (comment == NULL)
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(description, &oldtuple->t_self);
|
1999-10-26 05:12:39 +02:00
|
|
|
else
|
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(description), values,
|
2001-08-10 20:57:42 +02:00
|
|
|
nulls, replaces);
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(description, &oldtuple->t_self, newtuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
break; /* Assume there can be only one match */
|
2000-01-20 16:13:19 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
systable_endscan(sd);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* If we didn't find an old tuple, insert a new one */
|
2000-01-20 16:13:19 +01:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
if (newtuple == NULL && comment != NULL)
|
2001-08-10 20:57:42 +02:00
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_form_tuple(RelationGetDescr(description),
|
2001-08-10 20:57:42 +02:00
|
|
|
values, nulls);
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleInsert(description, newtuple);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (newtuple != NULL)
|
|
|
|
heap_freetuple(newtuple);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Done */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(description, NoLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
2006-02-12 04:22:21 +01:00
|
|
|
/*
|
|
|
|
* CreateSharedComments --
|
|
|
|
*
|
|
|
|
* Create a comment for the specified shared object descriptor. Inserts a
|
|
|
|
* new pg_shdescription tuple, or replaces an existing one with the same key.
|
|
|
|
*
|
|
|
|
* If the comment given is null or an empty string, instead delete any
|
|
|
|
* existing comment for the specified key.
|
|
|
|
*/
|
|
|
|
void
|
2017-10-31 15:34:31 +01:00
|
|
|
CreateSharedComments(Oid oid, Oid classoid, const char *comment)
|
2006-02-12 04:22:21 +01:00
|
|
|
{
|
|
|
|
Relation shdescription;
|
|
|
|
ScanKeyData skey[2];
|
|
|
|
SysScanDesc sd;
|
|
|
|
HeapTuple oldtuple;
|
|
|
|
HeapTuple newtuple = NULL;
|
|
|
|
Datum values[Natts_pg_shdescription];
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_shdescription];
|
|
|
|
bool replaces[Natts_pg_shdescription];
|
2006-02-12 04:22:21 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Reduce empty-string to NULL case */
|
|
|
|
if (comment != NULL && strlen(comment) == 0)
|
|
|
|
comment = NULL;
|
|
|
|
|
|
|
|
/* Prepare to form or update a tuple, if necessary */
|
|
|
|
if (comment != NULL)
|
|
|
|
{
|
|
|
|
for (i = 0; i < Natts_pg_shdescription; i++)
|
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
nulls[i] = false;
|
|
|
|
replaces[i] = true;
|
2006-02-12 04:22:21 +01:00
|
|
|
}
|
2011-06-16 23:03:58 +02:00
|
|
|
values[Anum_pg_shdescription_objoid - 1] = ObjectIdGetDatum(oid);
|
|
|
|
values[Anum_pg_shdescription_classoid - 1] = ObjectIdGetDatum(classoid);
|
|
|
|
values[Anum_pg_shdescription_description - 1] = CStringGetTextDatum(comment);
|
2006-02-12 04:22:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Use the index to search for a matching old tuple */
|
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_shdescription_objoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oid));
|
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_shdescription_classoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
shdescription = table_open(SharedDescriptionRelationId, RowExclusiveLock);
|
2006-02-12 04:22:21 +01:00
|
|
|
|
|
|
|
sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 2, skey);
|
2006-02-12 04:22:21 +01:00
|
|
|
|
|
|
|
while ((oldtuple = systable_getnext(sd)) != NULL)
|
|
|
|
{
|
|
|
|
/* Found the old tuple, so delete or update it */
|
|
|
|
|
|
|
|
if (comment == NULL)
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(shdescription, &oldtuple->t_self);
|
2006-02-12 04:22:21 +01:00
|
|
|
else
|
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_modify_tuple(oldtuple, RelationGetDescr(shdescription),
|
2006-02-12 04:22:21 +01:00
|
|
|
values, nulls, replaces);
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(shdescription, &oldtuple->t_self, newtuple);
|
2006-02-12 04:22:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break; /* Assume there can be only one match */
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(sd);
|
|
|
|
|
|
|
|
/* If we didn't find an old tuple, insert a new one */
|
|
|
|
|
|
|
|
if (newtuple == NULL && comment != NULL)
|
|
|
|
{
|
2008-11-02 02:45:28 +01:00
|
|
|
newtuple = heap_form_tuple(RelationGetDescr(shdescription),
|
2006-02-12 04:22:21 +01:00
|
|
|
values, nulls);
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleInsert(shdescription, newtuple);
|
2006-02-12 04:22:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (newtuple != NULL)
|
|
|
|
heap_freetuple(newtuple);
|
|
|
|
|
|
|
|
/* Done */
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(shdescription, NoLock);
|
2006-02-12 04:22:21 +01:00
|
|
|
}
|
|
|
|
|
2002-04-09 22:35:55 +02:00
|
|
|
/*
|
2002-07-12 20:43:19 +02:00
|
|
|
* DeleteComments -- remove comments for an object
|
1999-10-26 05:12:39 +02:00
|
|
|
*
|
2002-07-12 20:43:19 +02:00
|
|
|
* If subid is nonzero then only comments matching it will be removed.
|
|
|
|
* If subid is zero, all comments matching the oid/classoid will be removed
|
|
|
|
* (this corresponds to deleting a whole object).
|
1999-10-26 05:12:39 +02:00
|
|
|
*/
|
|
|
|
void
|
2002-07-12 20:43:19 +02:00
|
|
|
DeleteComments(Oid oid, Oid classoid, int32 subid)
|
1999-10-26 05:12:39 +02:00
|
|
|
{
|
|
|
|
Relation description;
|
2002-07-12 20:43:19 +02:00
|
|
|
ScanKeyData skey[3];
|
|
|
|
int nkeys;
|
2002-05-21 01:51:44 +02:00
|
|
|
SysScanDesc sd;
|
|
|
|
HeapTuple oldtuple;
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Use the index to search for all matching old tuples */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_description_objoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oid));
|
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_description_classoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
2001-08-10 20:57:42 +02:00
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
if (subid != 0)
|
|
|
|
{
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&skey[2],
|
|
|
|
Anum_pg_description_objsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(subid));
|
2002-07-12 20:43:19 +02:00
|
|
|
nkeys = 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
nkeys = 2;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
description = table_open(DescriptionRelationId, RowExclusiveLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
sd = systable_beginscan(description, DescriptionObjIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, nkeys, skey);
|
2001-08-10 20:57:42 +02:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
while ((oldtuple = systable_getnext(sd)) != NULL)
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(description, &oldtuple->t_self);
|
2001-08-10 20:57:42 +02:00
|
|
|
|
|
|
|
/* Done */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2002-05-21 01:51:44 +02:00
|
|
|
systable_endscan(sd);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(description, RowExclusiveLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2006-02-12 04:22:21 +01:00
|
|
|
/*
|
|
|
|
* DeleteSharedComments -- remove comments for a shared object
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
DeleteSharedComments(Oid oid, Oid classoid)
|
|
|
|
{
|
|
|
|
Relation shdescription;
|
|
|
|
ScanKeyData skey[2];
|
|
|
|
SysScanDesc sd;
|
|
|
|
HeapTuple oldtuple;
|
|
|
|
|
|
|
|
/* Use the index to search for all matching old tuples */
|
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_shdescription_objoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oid));
|
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_shdescription_classoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
shdescription = table_open(SharedDescriptionRelationId, RowExclusiveLock);
|
2006-02-12 04:22:21 +01:00
|
|
|
|
|
|
|
sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 2, skey);
|
2006-02-12 04:22:21 +01:00
|
|
|
|
|
|
|
while ((oldtuple = systable_getnext(sd)) != NULL)
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(shdescription, &oldtuple->t_self);
|
2006-02-12 04:22:21 +01:00
|
|
|
|
|
|
|
/* Done */
|
|
|
|
|
|
|
|
systable_endscan(sd);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(shdescription, RowExclusiveLock);
|
2006-02-12 04:22:21 +01:00
|
|
|
}
|
|
|
|
|
2009-10-12 21:49:24 +02:00
|
|
|
/*
|
|
|
|
* GetComment -- get the comment for an object, or null if not found.
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
GetComment(Oid oid, Oid classoid, int32 subid)
|
|
|
|
{
|
|
|
|
Relation description;
|
|
|
|
ScanKeyData skey[3];
|
|
|
|
SysScanDesc sd;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
HeapTuple tuple;
|
|
|
|
char *comment;
|
|
|
|
|
|
|
|
/* Use the index to search for a matching old tuple */
|
|
|
|
|
|
|
|
ScanKeyInit(&skey[0],
|
|
|
|
Anum_pg_description_objoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oid));
|
|
|
|
ScanKeyInit(&skey[1],
|
|
|
|
Anum_pg_description_classoid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
ScanKeyInit(&skey[2],
|
|
|
|
Anum_pg_description_objsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(subid));
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
description = table_open(DescriptionRelationId, AccessShareLock);
|
2009-10-12 21:49:24 +02:00
|
|
|
tupdesc = RelationGetDescr(description);
|
|
|
|
|
|
|
|
sd = systable_beginscan(description, DescriptionObjIndexId, true,
|
Use an MVCC snapshot, rather than SnapshotNow, for catalog scans.
SnapshotNow scans have the undesirable property that, in the face of
concurrent updates, the scan can fail to see either the old or the new
versions of the row. In many cases, we work around this by requiring
DDL operations to hold AccessExclusiveLock on the object being
modified; in some cases, the existing locking is inadequate and random
failures occur as a result. This commit doesn't change anything
related to locking, but will hopefully pave the way to allowing lock
strength reductions in the future.
The major issue has held us back from making this change in the past
is that taking an MVCC snapshot is significantly more expensive than
using a static special snapshot such as SnapshotNow. However, testing
of various worst-case scenarios reveals that this problem is not
severe except under fairly extreme workloads. To mitigate those
problems, we avoid retaking the MVCC snapshot for each new scan;
instead, we take a new snapshot only when invalidation messages have
been processed. The catcache machinery already requires that
invalidation messages be sent before releasing the related heavyweight
lock; else other backends might rely on locally-cached data rather
than scanning the catalog at all. Thus, making snapshot reuse
dependent on the same guarantees shouldn't break anything that wasn't
already subtly broken.
Patch by me. Review by Michael Paquier and Andres Freund.
2013-07-02 15:47:01 +02:00
|
|
|
NULL, 3, skey);
|
2009-10-12 21:49:24 +02:00
|
|
|
|
|
|
|
comment = NULL;
|
|
|
|
while ((tuple = systable_getnext(sd)) != NULL)
|
|
|
|
{
|
|
|
|
Datum value;
|
|
|
|
bool isnull;
|
|
|
|
|
|
|
|
/* Found the tuple, get description field */
|
|
|
|
value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
|
|
|
|
if (!isnull)
|
|
|
|
comment = TextDatumGetCString(value);
|
|
|
|
break; /* Assume there can be only one match */
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(sd);
|
|
|
|
|
|
|
|
/* Done */
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(description, AccessShareLock);
|
2009-10-12 21:49:24 +02:00
|
|
|
|
|
|
|
return comment;
|
|
|
|
}
|