2002-07-12 20:43:19 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_depend.c
|
|
|
|
* routines to support manipulation of the pg_depend relation
|
|
|
|
*
|
2019-01-02 18:44:25 +01:00
|
|
|
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
2002-07-12 20:43:19 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/catalog/pg_depend.c
|
2002-07-12 20:43:19 +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/table.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "catalog/dependency.h"
|
2006-07-11 19:26:59 +02:00
|
|
|
#include "catalog/indexing.h"
|
2007-12-02 00:44:44 +01:00
|
|
|
#include "catalog/pg_constraint.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "catalog/pg_depend.h"
|
2011-02-08 22:08:41 +01:00
|
|
|
#include "catalog/pg_extension.h"
|
|
|
|
#include "commands/extension.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "utils/fmgroids.h"
|
2007-12-02 00:44:44 +01:00
|
|
|
#include "utils/lsyscache.h"
|
2008-06-19 02:46:06 +02:00
|
|
|
#include "utils/rel.h"
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
static bool isObjectPinned(const ObjectAddress *object, Relation rel);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record a dependency between 2 objects via their respective objectAddress.
|
|
|
|
* The first argument is the dependent object, the second the one it
|
|
|
|
* references.
|
|
|
|
*
|
|
|
|
* This simply creates an entry in pg_depend, without any other processing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
recordDependencyOn(const ObjectAddress *depender,
|
|
|
|
const ObjectAddress *referenced,
|
|
|
|
DependencyType behavior)
|
2002-07-16 07:53:34 +02:00
|
|
|
{
|
|
|
|
recordMultipleDependencies(depender, referenced, 1, behavior);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Record multiple dependencies (of the same kind) for a single dependent
|
2014-05-06 18:12:18 +02:00
|
|
|
* object. This has a little less overhead than recording each separately.
|
2002-07-16 07:53:34 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
recordMultipleDependencies(const ObjectAddress *depender,
|
|
|
|
const ObjectAddress *referenced,
|
|
|
|
int nreferenced,
|
|
|
|
DependencyType behavior)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
Relation dependDesc;
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogIndexState indstate;
|
2002-07-12 20:43:19 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
int i;
|
2008-11-02 02:45:28 +01:00
|
|
|
bool nulls[Natts_pg_depend];
|
2002-07-12 20:43:19 +02:00
|
|
|
Datum values[Natts_pg_depend];
|
2002-07-16 07:53:34 +02:00
|
|
|
|
|
|
|
if (nreferenced <= 0)
|
|
|
|
return; /* nothing to do */
|
2002-07-12 20:43:19 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* During bootstrap, do nothing since pg_depend may not exist yet. initdb
|
|
|
|
* will fill in appropriate pg_depend entries after bootstrap.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
|
|
|
if (IsBootstrapProcessingMode())
|
|
|
|
return;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
dependDesc = table_open(DependRelationId, RowExclusiveLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
/* Don't open indexes unless we need to make an update */
|
|
|
|
indstate = NULL;
|
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
memset(nulls, false, sizeof(nulls));
|
2002-07-16 07:53:34 +02:00
|
|
|
|
|
|
|
for (i = 0; i < nreferenced; i++, referenced++)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If the referenced object is pinned by the system, there's no real
|
|
|
|
* need to record dependencies on it. This saves lots of space in
|
|
|
|
* pg_depend, so it's worth the time taken to check.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
2002-07-16 07:53:34 +02:00
|
|
|
if (!isObjectPinned(referenced, dependDesc))
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2002-07-16 07:53:34 +02:00
|
|
|
/*
|
|
|
|
* Record the Dependency. Note we don't bother to check for
|
|
|
|
* duplicate dependencies; there's no harm in them.
|
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
values[Anum_pg_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
|
|
|
|
values[Anum_pg_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
|
|
|
|
values[Anum_pg_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
values[Anum_pg_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
|
|
|
|
values[Anum_pg_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
|
|
|
|
values[Anum_pg_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
values[Anum_pg_depend_deptype - 1] = CharGetDatum((char) behavior);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
2008-11-02 02:45:28 +01:00
|
|
|
tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
2017-02-01 23:18:36 +01:00
|
|
|
/* fetch index info only when we know we need it */
|
2002-08-05 05:29:17 +02:00
|
|
|
if (indstate == NULL)
|
|
|
|
indstate = CatalogOpenIndexes(dependDesc);
|
|
|
|
|
2017-02-01 23:18:36 +01:00
|
|
|
CatalogTupleInsertWithInfo(dependDesc, tup, indstate);
|
2002-07-16 07:53:34 +02:00
|
|
|
|
|
|
|
heap_freetuple(tup);
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
2002-07-16 07:53:34 +02:00
|
|
|
}
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2002-08-05 05:29:17 +02:00
|
|
|
if (indstate != NULL)
|
|
|
|
CatalogCloseIndexes(indstate);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(dependDesc, RowExclusiveLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/*
|
|
|
|
* If we are executing a CREATE EXTENSION operation, mark the given object
|
2014-05-06 18:12:18 +02:00
|
|
|
* as being a member of the extension. Otherwise, do nothing.
|
2011-02-08 22:08:41 +01:00
|
|
|
*
|
|
|
|
* This must be called during creation of any user-definable object type
|
|
|
|
* that could be a member of an extension.
|
2011-07-23 22:59:39 +02:00
|
|
|
*
|
|
|
|
* If isReplace is true, the object already existed (or might have already
|
|
|
|
* existed), so we must check for a pre-existing extension membership entry.
|
|
|
|
* Passing false is a guarantee that the object is newly created, and so
|
|
|
|
* could not already be a member of any extension.
|
2011-02-08 22:08:41 +01:00
|
|
|
*/
|
|
|
|
void
|
2011-07-23 22:59:39 +02:00
|
|
|
recordDependencyOnCurrentExtension(const ObjectAddress *object,
|
|
|
|
bool isReplace)
|
2011-02-08 22:08:41 +01:00
|
|
|
{
|
2011-07-23 22:59:39 +02:00
|
|
|
/* Only whole objects can be extension members */
|
|
|
|
Assert(object->objectSubId == 0);
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
if (creating_extension)
|
|
|
|
{
|
2011-04-10 17:42:00 +02:00
|
|
|
ObjectAddress extension;
|
2011-02-08 22:08:41 +01:00
|
|
|
|
2011-07-23 22:59:39 +02:00
|
|
|
/* Only need to check for existing membership if isReplace */
|
|
|
|
if (isReplace)
|
|
|
|
{
|
2012-06-10 21:20:04 +02:00
|
|
|
Oid oldext;
|
2011-07-23 22:59:39 +02:00
|
|
|
|
|
|
|
oldext = getExtensionOfObject(object->classId, object->objectId);
|
|
|
|
if (OidIsValid(oldext))
|
|
|
|
{
|
|
|
|
/* If already a member of this extension, nothing to do */
|
|
|
|
if (oldext == CurrentExtensionObject)
|
|
|
|
return;
|
|
|
|
/* Already a member of some other extension, so reject */
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
errmsg("%s is already a member of extension \"%s\"",
|
|
|
|
getObjectDescription(object),
|
|
|
|
get_extension_name(oldext))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, record it as a member of CurrentExtensionObject */
|
2011-02-08 22:08:41 +01:00
|
|
|
extension.classId = ExtensionRelationId;
|
|
|
|
extension.objectId = CurrentExtensionObject;
|
|
|
|
extension.objectSubId = 0;
|
|
|
|
|
|
|
|
recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-17 00:12:20 +02:00
|
|
|
/*
|
|
|
|
* deleteDependencyRecordsFor -- delete all records with given depender
|
2002-08-11 23:17:35 +02:00
|
|
|
* classId/objectId. Returns the number of records deleted.
|
2002-07-17 00:12:20 +02:00
|
|
|
*
|
|
|
|
* This is used when redefining an existing object. Links leading to the
|
|
|
|
* object do not change, and links leading from it will be recreated
|
|
|
|
* (possibly with some differences from before).
|
2011-02-08 22:08:41 +01:00
|
|
|
*
|
|
|
|
* If skipExtensionDeps is true, we do not delete any dependencies that
|
2014-05-06 18:12:18 +02:00
|
|
|
* show that the given object is a member of an extension. This avoids
|
2011-02-08 22:08:41 +01:00
|
|
|
* needing a lot of extra logic to fetch and recreate that dependency.
|
2002-07-17 00:12:20 +02:00
|
|
|
*/
|
2002-08-11 23:17:35 +02:00
|
|
|
long
|
2011-02-08 22:08:41 +01:00
|
|
|
deleteDependencyRecordsFor(Oid classId, Oid objectId,
|
|
|
|
bool skipExtensionDeps)
|
2002-07-17 00:12:20 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
2002-07-17 00:12:20 +02:00
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, RowExclusiveLock);
|
2002-07-17 00:12:20 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
2002-07-17 00:12:20 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, 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, key);
|
2002-07-17 00:12:20 +02:00
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
2011-02-08 22:08:41 +01:00
|
|
|
if (skipExtensionDeps &&
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
2017-06-21 21:35:54 +02:00
|
|
|
((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION)
|
2011-02-08 22:08:41 +01:00
|
|
|
continue;
|
|
|
|
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(depRel, &tup->t_self);
|
2002-08-11 23:17:35 +02:00
|
|
|
count++;
|
2002-07-17 00:12:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, RowExclusiveLock);
|
2002-08-11 23:17:35 +02:00
|
|
|
|
|
|
|
return count;
|
2002-07-17 00:12:20 +02:00
|
|
|
}
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2011-02-10 23:36:44 +01:00
|
|
|
/*
|
|
|
|
* deleteDependencyRecordsForClass -- delete all records with given depender
|
|
|
|
* classId/objectId, dependee classId, and deptype.
|
|
|
|
* Returns the number of records deleted.
|
|
|
|
*
|
|
|
|
* This is a variant of deleteDependencyRecordsFor, useful when revoking
|
|
|
|
* an object property that is expressed by a dependency record (such as
|
|
|
|
* extension membership).
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
deleteDependencyRecordsForClass(Oid classId, Oid objectId,
|
|
|
|
Oid refclassId, char deptype)
|
|
|
|
{
|
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, RowExclusiveLock);
|
2011-02-10 23:36:44 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, 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, key);
|
2011-02-10 23:36:44 +01:00
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (depform->refclassid == refclassId && depform->deptype == deptype)
|
|
|
|
{
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(depRel, &tup->t_self);
|
2011-02-10 23:36:44 +01:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, RowExclusiveLock);
|
2011-02-10 23:36:44 +01:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2005-08-01 06:03:59 +02:00
|
|
|
/*
|
|
|
|
* Adjust dependency record(s) to point to a different object of the same type
|
|
|
|
*
|
|
|
|
* classId/objectId specify the referencing object.
|
|
|
|
* refClassId/oldRefObjectId specify the old referenced object.
|
|
|
|
* newRefObjectId is the new referenced object (must be of class refClassId).
|
|
|
|
*
|
|
|
|
* Note the lack of objsubid parameters. If there are subobject references
|
2019-02-10 00:08:48 +01:00
|
|
|
* they will all be readjusted. Also, there is an expectation that we are
|
|
|
|
* dealing with NORMAL dependencies: if we have to replace an (implicit)
|
|
|
|
* dependency on a pinned object with an explicit dependency on an unpinned
|
|
|
|
* one, the new one will be NORMAL.
|
2005-08-01 06:03:59 +02:00
|
|
|
*
|
2019-02-10 00:08:48 +01:00
|
|
|
* Returns the number of records updated -- zero indicates a problem.
|
2005-08-01 06:03:59 +02:00
|
|
|
*/
|
|
|
|
long
|
|
|
|
changeDependencyFor(Oid classId, Oid objectId,
|
|
|
|
Oid refClassId, Oid oldRefObjectId,
|
|
|
|
Oid newRefObjectId)
|
|
|
|
{
|
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
ObjectAddress objAddr;
|
2019-02-10 00:08:48 +01:00
|
|
|
ObjectAddress depAddr;
|
|
|
|
bool oldIsPinned;
|
2005-08-01 06:03:59 +02:00
|
|
|
bool newIsPinned;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, RowExclusiveLock);
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
/*
|
2019-02-10 00:08:48 +01:00
|
|
|
* Check to see if either oldRefObjectId or newRefObjectId is pinned.
|
|
|
|
* Pinned objects should not have any dependency entries pointing to them,
|
|
|
|
* so in these cases we should add or remove a pg_depend entry, or do
|
|
|
|
* nothing at all, rather than update an entry as in the normal case.
|
2005-08-01 06:03:59 +02:00
|
|
|
*/
|
|
|
|
objAddr.classId = refClassId;
|
|
|
|
objAddr.objectId = oldRefObjectId;
|
|
|
|
objAddr.objectSubId = 0;
|
|
|
|
|
2019-02-10 00:08:48 +01:00
|
|
|
oldIsPinned = isObjectPinned(&objAddr, depRel);
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
objAddr.objectId = newRefObjectId;
|
|
|
|
|
|
|
|
newIsPinned = isObjectPinned(&objAddr, depRel);
|
|
|
|
|
2019-02-10 00:08:48 +01:00
|
|
|
if (oldIsPinned)
|
|
|
|
{
|
|
|
|
table_close(depRel, RowExclusiveLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If both are pinned, we need do nothing. However, return 1 not 0,
|
|
|
|
* else callers will think this is an error case.
|
|
|
|
*/
|
|
|
|
if (newIsPinned)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is no old dependency record, but we should insert a new one.
|
|
|
|
* Assume a normal dependency is wanted.
|
|
|
|
*/
|
|
|
|
depAddr.classId = classId;
|
|
|
|
depAddr.objectId = objectId;
|
|
|
|
depAddr.objectSubId = 0;
|
|
|
|
recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* There should be existing dependency record(s), so search. */
|
2005-08-01 06:03:59 +02:00
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, 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, key);
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (depform->refclassid == refClassId &&
|
|
|
|
depform->refobjid == oldRefObjectId)
|
|
|
|
{
|
|
|
|
if (newIsPinned)
|
2017-02-01 22:13:30 +01:00
|
|
|
CatalogTupleDelete(depRel, &tup->t_self);
|
2005-08-01 06:03:59 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* make a modifiable copy */
|
|
|
|
tup = heap_copytuple(tup);
|
|
|
|
depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
depform->refobjid = newRefObjectId;
|
|
|
|
|
2017-01-31 22:42:24 +01:00
|
|
|
CatalogTupleUpdate(depRel, &tup->t_self, tup);
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, RowExclusiveLock);
|
2005-08-01 06:03:59 +02:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2019-04-12 08:36:05 +02:00
|
|
|
/*
|
|
|
|
* Adjust all dependency records to come from a different object of the same type
|
|
|
|
*
|
|
|
|
* classId/oldObjectId specify the old referencing object.
|
|
|
|
* newObjectId is the new referencing object (must be of class classId).
|
|
|
|
*
|
|
|
|
* Returns the number of records updated.
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
changeDependenciesOf(Oid classId, Oid oldObjectId,
|
|
|
|
Oid newObjectId)
|
|
|
|
{
|
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
|
|
|
depRel = table_open(DependRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oldObjectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, true,
|
|
|
|
NULL, 2, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
/* make a modifiable copy */
|
|
|
|
tup = heap_copytuple(tup);
|
|
|
|
depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
depform->objid = newObjectId;
|
|
|
|
|
|
|
|
CatalogTupleUpdate(depRel, &tup->t_self, tup);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
table_close(depRel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2019-03-29 08:25:20 +01:00
|
|
|
/*
|
|
|
|
* Adjust all dependency records to point to a different object of the same type
|
|
|
|
*
|
|
|
|
* refClassId/oldRefObjectId specify the old referenced object.
|
|
|
|
* newRefObjectId is the new referenced object (must be of class refClassId).
|
|
|
|
*
|
|
|
|
* Returns the number of records updated.
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
|
|
|
|
Oid newRefObjectId)
|
|
|
|
{
|
|
|
|
long count = 0;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
ObjectAddress objAddr;
|
|
|
|
bool newIsPinned;
|
|
|
|
|
|
|
|
depRel = table_open(DependRelationId, RowExclusiveLock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If oldRefObjectId is pinned, there won't be any dependency entries on
|
|
|
|
* it --- we can't cope in that case. (This isn't really worth expending
|
|
|
|
* code to fix, in current usage; it just means you can't rename stuff out
|
|
|
|
* of pg_catalog, which would likely be a bad move anyway.)
|
|
|
|
*/
|
|
|
|
objAddr.classId = refClassId;
|
|
|
|
objAddr.objectId = oldRefObjectId;
|
|
|
|
objAddr.objectSubId = 0;
|
|
|
|
|
|
|
|
if (isObjectPinned(&objAddr, depRel))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("cannot remove dependency on %s because it is a system object",
|
|
|
|
getObjectDescription(&objAddr))));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can handle adding a dependency on something pinned, though, since
|
|
|
|
* that just means deleting the dependency entry.
|
|
|
|
*/
|
|
|
|
objAddr.objectId = newRefObjectId;
|
|
|
|
|
|
|
|
newIsPinned = isObjectPinned(&objAddr, depRel);
|
|
|
|
|
|
|
|
/* Now search for dependency records */
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_refclassid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(refClassId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_refobjid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(oldRefObjectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
|
|
|
|
NULL, 2, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (newIsPinned)
|
|
|
|
CatalogTupleDelete(depRel, &tup->t_self);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* make a modifiable copy */
|
|
|
|
tup = heap_copytuple(tup);
|
|
|
|
depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
depform->refobjid = newRefObjectId;
|
|
|
|
|
|
|
|
CatalogTupleUpdate(depRel, &tup->t_self, tup);
|
|
|
|
|
|
|
|
heap_freetuple(tup);
|
|
|
|
}
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
table_close(depRel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
/*
|
|
|
|
* isObjectPinned()
|
|
|
|
*
|
|
|
|
* Test if an object is required for basic database functionality.
|
|
|
|
* Caller must already have opened pg_depend.
|
|
|
|
*
|
|
|
|
* The passed subId, if any, is ignored; we assume that only whole objects
|
|
|
|
* are pinned (and that this implies pinning their components).
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
isObjectPinned(const ObjectAddress *object, Relation rel)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_refclassid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(object->classId));
|
|
|
|
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_refobjid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(object->objectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(rel, DependReferenceIndexId, 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, key);
|
2007-12-02 00:44:44 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we won't generate additional pg_depend entries for pinned
|
|
|
|
* objects, there can be at most one entry referencing a pinned object.
|
|
|
|
* Hence, it's sufficient to look at the first returned tuple; we don't
|
|
|
|
* need to loop.
|
|
|
|
*/
|
|
|
|
tup = systable_getnext(scan);
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
|
|
{
|
|
|
|
Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (foundDep->deptype == DEPENDENCY_PIN)
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Various special-purpose lookups and manipulations of pg_depend.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
/*
|
|
|
|
* Find the extension containing the specified object, if any
|
|
|
|
*
|
|
|
|
* Returns the OID of the extension, or InvalidOid if the object does not
|
|
|
|
* belong to any extension.
|
|
|
|
*
|
|
|
|
* Extension membership is marked by an EXTENSION dependency from the object
|
|
|
|
* to the extension. Note that the result will be indeterminate if pg_depend
|
|
|
|
* contains links from this object to more than one extension ... but that
|
|
|
|
* should never happen.
|
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
getExtensionOfObject(Oid classId, Oid objectId)
|
|
|
|
{
|
|
|
|
Oid result = InvalidOid;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, AccessShareLock);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(objectId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, 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, key);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (depform->refclassid == ExtensionRelationId &&
|
|
|
|
depform->deptype == DEPENDENCY_EXTENSION)
|
|
|
|
{
|
|
|
|
result = depform->refobjid;
|
|
|
|
break; /* no need to keep scanning */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, AccessShareLock);
|
2011-02-08 22:08:41 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2006-08-21 02:57:26 +02:00
|
|
|
/*
|
|
|
|
* Detect whether a sequence is marked as "owned" by a column
|
|
|
|
*
|
2017-04-06 14:33:16 +02:00
|
|
|
* An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
|
2014-05-06 18:12:18 +02:00
|
|
|
* column. If we find one, store the identity of the owning column
|
2017-08-16 06:22:32 +02:00
|
|
|
* into *tableId and *colId and return true; else return false.
|
2006-08-21 02:57:26 +02:00
|
|
|
*
|
|
|
|
* Note: if there's more than one such pg_depend entry then you get
|
|
|
|
* a random one of them returned into the out parameters. This should
|
|
|
|
* not happen, though.
|
|
|
|
*/
|
|
|
|
bool
|
2017-04-06 14:33:16 +02:00
|
|
|
sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
|
2006-08-21 02:57:26 +02:00
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[2];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, AccessShareLock);
|
2006-08-21 02:57:26 +02:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(RelationRelationId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(seqId));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, 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, key);
|
2006-08-21 02:57:26 +02:00
|
|
|
|
|
|
|
while (HeapTupleIsValid((tup = systable_getnext(scan))))
|
|
|
|
{
|
|
|
|
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
if (depform->refclassid == RelationRelationId &&
|
2017-04-06 14:33:16 +02:00
|
|
|
depform->deptype == deptype)
|
2006-08-21 02:57:26 +02:00
|
|
|
{
|
|
|
|
*tableId = depform->refobjid;
|
|
|
|
*colId = depform->refobjsubid;
|
|
|
|
ret = true;
|
|
|
|
break; /* no need to keep scanning */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, AccessShareLock);
|
2006-08-21 02:57:26 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-04-06 14:33:16 +02:00
|
|
|
* Collect a list of OIDs of all sequences owned by the specified relation,
|
2019-07-22 12:05:03 +02:00
|
|
|
* and column if specified. If deptype is not zero, then only find sequences
|
|
|
|
* with the specified dependency type.
|
2008-05-17 01:36:05 +02:00
|
|
|
*/
|
2019-07-22 12:05:03 +02:00
|
|
|
static List *
|
|
|
|
getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype)
|
2008-05-17 01:36:05 +02:00
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
Relation depRel;
|
2017-04-06 14:33:16 +02:00
|
|
|
ScanKeyData key[3];
|
2009-06-11 16:49:15 +02:00
|
|
|
SysScanDesc scan;
|
2008-05-17 01:36:05 +02:00
|
|
|
HeapTuple tup;
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, AccessShareLock);
|
2008-05-17 01:36:05 +02:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_refclassid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(RelationRelationId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_refobjid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(relid));
|
2017-04-06 14:33:16 +02:00
|
|
|
if (attnum)
|
|
|
|
ScanKeyInit(&key[2],
|
|
|
|
Anum_pg_depend_refobjsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(attnum));
|
2008-05-17 01:36:05 +02:00
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
|
2017-04-06 14:33:16 +02:00
|
|
|
NULL, attnum ? 3 : 2, key);
|
2008-05-17 01:36:05 +02:00
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
/*
|
2017-05-17 22:31:56 +02:00
|
|
|
* We assume any auto or internal dependency of a sequence on a column
|
|
|
|
* must be what we are looking for. (We need the relkind test because
|
|
|
|
* indexes can also have auto dependencies on columns.)
|
2008-05-17 01:36:05 +02:00
|
|
|
*/
|
|
|
|
if (deprec->classid == RelationRelationId &&
|
|
|
|
deprec->objsubid == 0 &&
|
|
|
|
deprec->refobjsubid != 0 &&
|
2017-04-06 14:33:16 +02:00
|
|
|
(deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
|
2008-05-17 01:36:05 +02:00
|
|
|
get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
|
|
|
|
{
|
2019-07-22 12:05:03 +02:00
|
|
|
if (!deptype || deprec->deptype == deptype)
|
|
|
|
result = lappend_oid(result, deprec->objid);
|
2008-05-17 01:36:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, AccessShareLock);
|
2008-05-17 01:36:05 +02:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-04-06 14:33:16 +02:00
|
|
|
/*
|
2019-07-22 12:05:03 +02:00
|
|
|
* Collect a list of OIDs of all sequences owned (identity or serial) by the
|
|
|
|
* specified relation.
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
getOwnedSequences(Oid relid)
|
|
|
|
{
|
|
|
|
return getOwnedSequences_internal(relid, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get owned identity sequence, error if not exactly one.
|
2017-04-06 14:33:16 +02:00
|
|
|
*/
|
|
|
|
Oid
|
2019-07-22 12:05:03 +02:00
|
|
|
getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok)
|
2017-04-06 14:33:16 +02:00
|
|
|
{
|
2019-07-22 12:05:03 +02:00
|
|
|
List *seqlist = getOwnedSequences_internal(relid, attnum, DEPENDENCY_INTERNAL);
|
2017-04-06 14:33:16 +02:00
|
|
|
|
|
|
|
if (list_length(seqlist) > 1)
|
|
|
|
elog(ERROR, "more than one owned sequence found");
|
|
|
|
else if (list_length(seqlist) < 1)
|
2019-07-22 12:05:03 +02:00
|
|
|
{
|
|
|
|
if (missing_ok)
|
|
|
|
return InvalidOid;
|
|
|
|
else
|
|
|
|
elog(ERROR, "no owned sequence found");
|
|
|
|
}
|
2017-04-07 23:37:12 +02:00
|
|
|
|
|
|
|
return linitial_oid(seqlist);
|
2017-04-06 14:33:16 +02:00
|
|
|
}
|
2007-12-02 00:44:44 +01:00
|
|
|
|
2002-07-12 20:43:19 +02:00
|
|
|
/*
|
2007-12-02 00:44:44 +01:00
|
|
|
* get_constraint_index
|
Fix ALTER COLUMN TYPE failure with a partial exclusion constraint.
ATExecAlterColumnType failed to consider the possibility that an index
that needs to be rebuilt might be a child of a constraint that needs to be
rebuilt. We missed this so far because usually a constraint index doesn't
have a direct dependency on its table, just on the constraint object.
But if there's a WHERE clause, then dependency analysis of the WHERE
clause results in direct dependencies on the column(s) mentioned in WHERE.
This led to trying to drop and rebuild both the constraint and its
underlying index.
In v11/HEAD, we successfully drop both the index and the constraint,
and then try to rebuild both, and of course the second rebuild hits a
duplicate-index-name problem. Before v11, it fails with obscure messages
about a missing relation OID, due to trying to drop the index twice.
This is essentially the same kind of problem noted in commit
20bef2c31: the possible dependency linkages are broader than what
ATExecAlterColumnType was designed for. It was probably OK when
written, but it's certainly been broken since the introduction of
partial exclusion constraints. Fix by adding an explicit check
for whether any of the indexes-to-be-rebuilt belong to any of the
constraints-to-be-rebuilt, and ignoring any that do.
In passing, fix a latent bug introduced by commit 8b08f7d48: in
get_constraint_index() we must "continue" not "break" when rejecting
a relation of a wrong relkind. This is harmless today because we don't
expect that code path to be taken anyway; but if there ever were any
relations to be ignored, the existing coding would have an extremely
undesirable dependency on the order of pg_depend entries.
Also adjust a couple of obsolete comments.
Per bug #15835 from Yaroslav Schekin. Back-patch to all supported
branches.
Discussion: https://postgr.es/m/15835-32d9b7a76c06a7a9@postgresql.org
2019-06-12 18:29:24 +02:00
|
|
|
* Given the OID of a unique, primary-key, or exclusion constraint,
|
|
|
|
* return the OID of the underlying index.
|
2002-07-12 20:43:19 +02:00
|
|
|
*
|
2007-12-02 00:44:44 +01:00
|
|
|
* Return InvalidOid if the index couldn't be found; this suggests the
|
|
|
|
* given OID is bogus, but we leave it to caller to decide what to do.
|
2002-07-12 20:43:19 +02:00
|
|
|
*/
|
2007-12-02 00:44:44 +01:00
|
|
|
Oid
|
|
|
|
get_constraint_index(Oid constraintId)
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2007-12-02 00:44:44 +01:00
|
|
|
Oid indexId = InvalidOid;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[3];
|
2002-09-04 22:31:48 +02:00
|
|
|
SysScanDesc scan;
|
2002-07-12 20:43:19 +02:00
|
|
|
HeapTuple tup;
|
2007-12-02 00:44:44 +01:00
|
|
|
|
|
|
|
/* Search the dependency table for the dependent index */
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, AccessShareLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_refclassid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
2007-12-02 00:44:44 +01:00
|
|
|
ObjectIdGetDatum(ConstraintRelationId));
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_refobjid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
2007-12-02 00:44:44 +01:00
|
|
|
ObjectIdGetDatum(constraintId));
|
|
|
|
ScanKeyInit(&key[2],
|
|
|
|
Anum_pg_depend_refobjsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(0));
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
scan = systable_beginscan(depRel, DependReferenceIndexId, 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, key);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
2002-07-12 20:43:19 +02:00
|
|
|
{
|
2007-12-02 00:44:44 +01:00
|
|
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
/*
|
|
|
|
* We assume any internal dependency of an index on the constraint
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
* must be what we are looking for.
|
2007-12-02 00:44:44 +01:00
|
|
|
*/
|
|
|
|
if (deprec->classid == RelationRelationId &&
|
|
|
|
deprec->objsubid == 0 &&
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
deprec->deptype == DEPENDENCY_INTERNAL)
|
2007-12-02 00:44:44 +01:00
|
|
|
{
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
char relkind = get_rel_relkind(deprec->objid);
|
|
|
|
|
Fix ALTER COLUMN TYPE failure with a partial exclusion constraint.
ATExecAlterColumnType failed to consider the possibility that an index
that needs to be rebuilt might be a child of a constraint that needs to be
rebuilt. We missed this so far because usually a constraint index doesn't
have a direct dependency on its table, just on the constraint object.
But if there's a WHERE clause, then dependency analysis of the WHERE
clause results in direct dependencies on the column(s) mentioned in WHERE.
This led to trying to drop and rebuild both the constraint and its
underlying index.
In v11/HEAD, we successfully drop both the index and the constraint,
and then try to rebuild both, and of course the second rebuild hits a
duplicate-index-name problem. Before v11, it fails with obscure messages
about a missing relation OID, due to trying to drop the index twice.
This is essentially the same kind of problem noted in commit
20bef2c31: the possible dependency linkages are broader than what
ATExecAlterColumnType was designed for. It was probably OK when
written, but it's certainly been broken since the introduction of
partial exclusion constraints. Fix by adding an explicit check
for whether any of the indexes-to-be-rebuilt belong to any of the
constraints-to-be-rebuilt, and ignoring any that do.
In passing, fix a latent bug introduced by commit 8b08f7d48: in
get_constraint_index() we must "continue" not "break" when rejecting
a relation of a wrong relkind. This is harmless today because we don't
expect that code path to be taken anyway; but if there ever were any
relations to be ignored, the existing coding would have an extremely
undesirable dependency on the order of pg_depend entries.
Also adjust a couple of obsolete comments.
Per bug #15835 from Yaroslav Schekin. Back-patch to all supported
branches.
Discussion: https://postgr.es/m/15835-32d9b7a76c06a7a9@postgresql.org
2019-06-12 18:29:24 +02:00
|
|
|
/*
|
|
|
|
* This is pure paranoia; there shouldn't be any other relkinds
|
|
|
|
* dependent on a constraint.
|
|
|
|
*/
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
if (relkind != RELKIND_INDEX &&
|
|
|
|
relkind != RELKIND_PARTITIONED_INDEX)
|
Fix ALTER COLUMN TYPE failure with a partial exclusion constraint.
ATExecAlterColumnType failed to consider the possibility that an index
that needs to be rebuilt might be a child of a constraint that needs to be
rebuilt. We missed this so far because usually a constraint index doesn't
have a direct dependency on its table, just on the constraint object.
But if there's a WHERE clause, then dependency analysis of the WHERE
clause results in direct dependencies on the column(s) mentioned in WHERE.
This led to trying to drop and rebuild both the constraint and its
underlying index.
In v11/HEAD, we successfully drop both the index and the constraint,
and then try to rebuild both, and of course the second rebuild hits a
duplicate-index-name problem. Before v11, it fails with obscure messages
about a missing relation OID, due to trying to drop the index twice.
This is essentially the same kind of problem noted in commit
20bef2c31: the possible dependency linkages are broader than what
ATExecAlterColumnType was designed for. It was probably OK when
written, but it's certainly been broken since the introduction of
partial exclusion constraints. Fix by adding an explicit check
for whether any of the indexes-to-be-rebuilt belong to any of the
constraints-to-be-rebuilt, and ignoring any that do.
In passing, fix a latent bug introduced by commit 8b08f7d48: in
get_constraint_index() we must "continue" not "break" when rejecting
a relation of a wrong relkind. This is harmless today because we don't
expect that code path to be taken anyway; but if there ever were any
relations to be ignored, the existing coding would have an extremely
undesirable dependency on the order of pg_depend entries.
Also adjust a couple of obsolete comments.
Per bug #15835 from Yaroslav Schekin. Back-patch to all supported
branches.
Discussion: https://postgr.es/m/15835-32d9b7a76c06a7a9@postgresql.org
2019-06-12 18:29:24 +02:00
|
|
|
continue;
|
Local partitioned indexes
When CREATE INDEX is run on a partitioned table, create catalog entries
for an index on the partitioned table (which is just a placeholder since
the table proper has no data of its own), and recurse to create actual
indexes on the existing partitions; create them in future partitions
also.
As a convenience gadget, if the new index definition matches some
existing index in partitions, these are picked up and used instead of
creating new ones. Whichever way these indexes come about, they become
attached to the index on the parent table and are dropped alongside it,
and cannot be dropped on isolation unless they are detached first.
To support pg_dump'ing these indexes, add commands
CREATE INDEX ON ONLY <table>
(which creates the index on the parent partitioned table, without
recursing) and
ALTER INDEX ATTACH PARTITION
(which is used after the indexes have been created individually on each
partition, to attach them to the parent index). These reconstruct prior
database state exactly.
Reviewed-by: (in alphabetical order) Peter Eisentraut, Robert Haas, Amit
Langote, Jesper Pedersen, Simon Riggs, David Rowley
Discussion: https://postgr.es/m/20171113170646.gzweigyrgg6pwsg4@alvherre.pgsql
2018-01-19 15:49:22 +01:00
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
indexId = deprec->objid;
|
|
|
|
break;
|
|
|
|
}
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, AccessShareLock);
|
2002-07-12 20:43:19 +02:00
|
|
|
|
2007-12-02 00:44:44 +01:00
|
|
|
return indexId;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get_index_constraint
|
Fix ALTER COLUMN TYPE failure with a partial exclusion constraint.
ATExecAlterColumnType failed to consider the possibility that an index
that needs to be rebuilt might be a child of a constraint that needs to be
rebuilt. We missed this so far because usually a constraint index doesn't
have a direct dependency on its table, just on the constraint object.
But if there's a WHERE clause, then dependency analysis of the WHERE
clause results in direct dependencies on the column(s) mentioned in WHERE.
This led to trying to drop and rebuild both the constraint and its
underlying index.
In v11/HEAD, we successfully drop both the index and the constraint,
and then try to rebuild both, and of course the second rebuild hits a
duplicate-index-name problem. Before v11, it fails with obscure messages
about a missing relation OID, due to trying to drop the index twice.
This is essentially the same kind of problem noted in commit
20bef2c31: the possible dependency linkages are broader than what
ATExecAlterColumnType was designed for. It was probably OK when
written, but it's certainly been broken since the introduction of
partial exclusion constraints. Fix by adding an explicit check
for whether any of the indexes-to-be-rebuilt belong to any of the
constraints-to-be-rebuilt, and ignoring any that do.
In passing, fix a latent bug introduced by commit 8b08f7d48: in
get_constraint_index() we must "continue" not "break" when rejecting
a relation of a wrong relkind. This is harmless today because we don't
expect that code path to be taken anyway; but if there ever were any
relations to be ignored, the existing coding would have an extremely
undesirable dependency on the order of pg_depend entries.
Also adjust a couple of obsolete comments.
Per bug #15835 from Yaroslav Schekin. Back-patch to all supported
branches.
Discussion: https://postgr.es/m/15835-32d9b7a76c06a7a9@postgresql.org
2019-06-12 18:29:24 +02:00
|
|
|
* Given the OID of an index, return the OID of the owning unique,
|
|
|
|
* primary-key, or exclusion constraint, or InvalidOid if there
|
|
|
|
* is no owning constraint.
|
2007-12-02 00:44:44 +01:00
|
|
|
*/
|
|
|
|
Oid
|
|
|
|
get_index_constraint(Oid indexId)
|
|
|
|
{
|
|
|
|
Oid constraintId = InvalidOid;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[3];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
|
|
|
/* Search the dependency table for the index */
|
2019-01-21 19:32:19 +01:00
|
|
|
depRel = table_open(DependRelationId, AccessShareLock);
|
2007-12-02 00:44:44 +01:00
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_classid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(RelationRelationId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_objid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(indexId));
|
|
|
|
ScanKeyInit(&key[2],
|
|
|
|
Anum_pg_depend_objsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(0));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependDependerIndexId, 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, key);
|
2007-12-02 00:44:44 +01:00
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* We assume any internal dependency on a constraint must be what we
|
|
|
|
* are looking for.
|
2007-12-02 00:44:44 +01:00
|
|
|
*/
|
|
|
|
if (deprec->refclassid == ConstraintRelationId &&
|
|
|
|
deprec->refobjsubid == 0 &&
|
|
|
|
deprec->deptype == DEPENDENCY_INTERNAL)
|
|
|
|
{
|
|
|
|
constraintId = deprec->refobjid;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
2019-01-21 19:32:19 +01:00
|
|
|
table_close(depRel, AccessShareLock);
|
2007-12-02 00:44:44 +01:00
|
|
|
|
|
|
|
return constraintId;
|
2002-07-12 20:43:19 +02:00
|
|
|
}
|
2019-03-29 08:25:20 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* get_index_ref_constraints
|
|
|
|
* Given the OID of an index, return the OID of all foreign key
|
|
|
|
* constraints which reference the index.
|
|
|
|
*/
|
|
|
|
List *
|
|
|
|
get_index_ref_constraints(Oid indexId)
|
|
|
|
{
|
|
|
|
List *result = NIL;
|
|
|
|
Relation depRel;
|
|
|
|
ScanKeyData key[3];
|
|
|
|
SysScanDesc scan;
|
|
|
|
HeapTuple tup;
|
|
|
|
|
|
|
|
/* Search the dependency table for the index */
|
|
|
|
depRel = table_open(DependRelationId, AccessShareLock);
|
|
|
|
|
|
|
|
ScanKeyInit(&key[0],
|
|
|
|
Anum_pg_depend_refclassid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(RelationRelationId));
|
|
|
|
ScanKeyInit(&key[1],
|
|
|
|
Anum_pg_depend_refobjid,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(indexId));
|
|
|
|
ScanKeyInit(&key[2],
|
|
|
|
Anum_pg_depend_refobjsubid,
|
|
|
|
BTEqualStrategyNumber, F_INT4EQ,
|
|
|
|
Int32GetDatum(0));
|
|
|
|
|
|
|
|
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
|
|
|
|
NULL, 3, key);
|
|
|
|
|
|
|
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
|
|
|
{
|
|
|
|
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We assume any normal dependency from a constraint must be what we
|
|
|
|
* are looking for.
|
|
|
|
*/
|
|
|
|
if (deprec->classid == ConstraintRelationId &&
|
|
|
|
deprec->objsubid == 0 &&
|
|
|
|
deprec->deptype == DEPENDENCY_NORMAL)
|
|
|
|
{
|
|
|
|
result = lappend_oid(result, deprec->objid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
systable_endscan(scan);
|
|
|
|
table_close(depRel, AccessShareLock);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|