Header And Logo

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

Data Structures | Defines | Enumerations | Functions

pg_shdepend.c File Reference

#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
#include "commands/alter.h"
#include "commands/dbcommands.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
#include "storage/lmgr.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
Include dependency graph for pg_shdepend.c:

Go to the source code of this file.

Data Structures

struct  remoteDep

Defines

#define MAX_REPORTED_DEPS   100

Enumerations

enum  objectType { LOCAL_OBJECT, SHARED_OBJECT, REMOTE_OBJECT }

Functions

static void getOidListDiff (Oid *list1, int *nlist1, Oid *list2, int *nlist2)
static Oid classIdGetDbId (Oid classId)
static void shdepChangeDep (Relation sdepRel, Oid classid, Oid objid, int32 objsubid, Oid refclassid, Oid refobjid, SharedDependencyType deptype)
static void shdepAddDependency (Relation sdepRel, Oid classId, Oid objectId, int32 objsubId, Oid refclassId, Oid refobjId, SharedDependencyType deptype)
static void shdepDropDependency (Relation sdepRel, Oid classId, Oid objectId, int32 objsubId, bool drop_subobjects, Oid refclassId, Oid refobjId, SharedDependencyType deptype)
static void storeObjectDescription (StringInfo descs, objectType type, ObjectAddress *object, SharedDependencyType deptype, int count)
static bool isSharedObjectPinned (Oid classId, Oid objectId, Relation sdepRel)
void recordSharedDependencyOn (ObjectAddress *depender, ObjectAddress *referenced, SharedDependencyType deptype)
void recordDependencyOnOwner (Oid classId, Oid objectId, Oid owner)
void changeDependencyOnOwner (Oid classId, Oid objectId, Oid newOwnerId)
void updateAclDependencies (Oid classId, Oid objectId, int32 objsubId, Oid ownerId, int noldmembers, Oid *oldmembers, int nnewmembers, Oid *newmembers)
bool checkSharedDependencies (Oid classId, Oid objectId, char **detail_msg, char **detail_log_msg)
void copyTemplateDependencies (Oid templateDbId, Oid newDbId)
void dropDatabaseDependencies (Oid databaseId)
void deleteSharedDependencyRecordsFor (Oid classId, Oid objectId, int32 objectSubId)
void shdepLockAndCheckObject (Oid classId, Oid objectId)
void shdepDropOwned (List *roleids, DropBehavior behavior)
void shdepReassignOwned (List *roleids, Oid newrole)

Define Documentation

#define MAX_REPORTED_DEPS   100

Enumeration Type Documentation

enum objectType
Enumerator:
LOCAL_OBJECT 
SHARED_OBJECT 
REMOTE_OBJECT 

Definition at line 63 of file pg_shdepend.c.

{
    LOCAL_OBJECT,
    SHARED_OBJECT,
    REMOTE_OBJECT
} objectType;


Function Documentation

void changeDependencyOnOwner ( Oid  classId,
Oid  objectId,
Oid  newOwnerId 
)

Definition at line 301 of file pg_shdepend.c.

References AuthIdRelationId, heap_close, heap_open(), RowExclusiveLock, SHARED_DEPENDENCY_ACL, SHARED_DEPENDENCY_OWNER, SharedDependRelationId, shdepChangeDep(), and shdepDropDependency().

Referenced by AlterDatabaseOwner(), AlterEventTriggerOwner_internal(), AlterForeignDataWrapperOwner_internal(), AlterForeignServerOwner_internal(), AlterObjectOwner_internal(), AlterSchemaOwner_internal(), AlterTypeOwner(), AlterTypeOwnerInternal(), and ATExecChangeOwner().

{
    Relation    sdepRel;

    sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);

    /* Adjust the SHARED_DEPENDENCY_OWNER entry */
    shdepChangeDep(sdepRel,
                   classId, objectId, 0,
                   AuthIdRelationId, newOwnerId,
                   SHARED_DEPENDENCY_OWNER);

    /*----------
     * There should never be a SHARED_DEPENDENCY_ACL entry for the owner,
     * so get rid of it if there is one.  This can happen if the new owner
     * was previously granted some rights to the object.
     *
     * This step is analogous to aclnewowner's removal of duplicate entries
     * in the ACL.  We have to do it to handle this scenario:
     *      A grants some rights on an object to B
     *      ALTER OWNER changes the object's owner to B
     *      ALTER OWNER changes the object's owner to C
     * The third step would remove all mention of B from the object's ACL,
     * but we'd still have a SHARED_DEPENDENCY_ACL for B if we did not do
     * things this way.
     *
     * The rule against having a SHARED_DEPENDENCY_ACL entry for the owner
     * allows us to fix things up in just this one place, without having
     * to make the various ALTER OWNER routines each know about it.
     *----------
     */
    shdepDropDependency(sdepRel, classId, objectId, 0, true,
                        AuthIdRelationId, newOwnerId,
                        SHARED_DEPENDENCY_ACL);

    heap_close(sdepRel, RowExclusiveLock);
}

bool checkSharedDependencies ( Oid  classId,
Oid  objectId,
char **  detail_msg,
char **  detail_log_msg 
)

Definition at line 519 of file pg_shdepend.c.

References AccessShareLock, Anum_pg_shdepend_refclassid, Anum_pg_shdepend_refobjid, appendStringInfo(), BTEqualStrategyNumber, remoteDep::count, StringInfoData::data, remoteDep::dbOid, ereport, errcode(), errmsg(), ERROR, getObjectDescription(), GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, initStringInfo(), InvalidOid, lappend(), StringInfoData::len, lfirst, list_free_deep(), LOCAL_OBJECT, MAX_REPORTED_DEPS, MyDatabaseId, ngettext, ObjectIdGetDatum, palloc(), pfree(), REMOTE_OBJECT, ScanKeyInit(), SHARED_DEPENDENCY_INVALID, SHARED_DEPENDENCY_PIN, SHARED_OBJECT, SharedDependReferenceIndexId, SharedDependRelationId, SnapshotNow, storeObjectDescription(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by DropRole().

{
    Relation    sdepRel;
    ScanKeyData key[2];
    SysScanDesc scan;
    HeapTuple   tup;
    int         numReportedDeps = 0;
    int         numNotReportedDeps = 0;
    int         numNotReportedDbs = 0;
    List       *remDeps = NIL;
    ListCell   *cell;
    ObjectAddress object;
    StringInfoData descs;
    StringInfoData alldescs;

    /*
     * We limit the number of dependencies reported to the client to
     * MAX_REPORTED_DEPS, since client software may not deal well with
     * enormous error strings.  The server log always gets a full report.
     */
#define MAX_REPORTED_DEPS 100

    initStringInfo(&descs);
    initStringInfo(&alldescs);

    sdepRel = heap_open(SharedDependRelationId, AccessShareLock);

    ScanKeyInit(&key[0],
                Anum_pg_shdepend_refclassid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(classId));
    ScanKeyInit(&key[1],
                Anum_pg_shdepend_refobjid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(objectId));

    scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
                              SnapshotNow, 2, key);

    while (HeapTupleIsValid(tup = systable_getnext(scan)))
    {
        Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);

        /* This case can be dispatched quickly */
        if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
        {
            object.classId = classId;
            object.objectId = objectId;
            object.objectSubId = 0;
            ereport(ERROR,
                    (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
                     errmsg("cannot drop %s because it is required by the database system",
                            getObjectDescription(&object))));
        }

        object.classId = sdepForm->classid;
        object.objectId = sdepForm->objid;
        object.objectSubId = sdepForm->objsubid;

        /*
         * If it's a dependency local to this database or it's a shared
         * object, describe it.
         *
         * If it's a remote dependency, keep track of it so we can report the
         * number of them later.
         */
        if (sdepForm->dbid == MyDatabaseId)
        {
            if (numReportedDeps < MAX_REPORTED_DEPS)
            {
                numReportedDeps++;
                storeObjectDescription(&descs, LOCAL_OBJECT, &object,
                                       sdepForm->deptype, 0);
            }
            else
                numNotReportedDeps++;
            storeObjectDescription(&alldescs, LOCAL_OBJECT, &object,
                                   sdepForm->deptype, 0);
        }
        else if (sdepForm->dbid == InvalidOid)
        {
            if (numReportedDeps < MAX_REPORTED_DEPS)
            {
                numReportedDeps++;
                storeObjectDescription(&descs, SHARED_OBJECT, &object,
                                       sdepForm->deptype, 0);
            }
            else
                numNotReportedDeps++;
            storeObjectDescription(&alldescs, SHARED_OBJECT, &object,
                                   sdepForm->deptype, 0);
        }
        else
        {
            /* It's not local nor shared, so it must be remote. */
            remoteDep  *dep;
            bool        stored = false;

            /*
             * XXX this info is kept on a simple List.  Maybe it's not good
             * for performance, but using a hash table seems needlessly
             * complex.  The expected number of databases is not high anyway,
             * I suppose.
             */
            foreach(cell, remDeps)
            {
                dep = lfirst(cell);
                if (dep->dbOid == sdepForm->dbid)
                {
                    dep->count++;
                    stored = true;
                    break;
                }
            }
            if (!stored)
            {
                dep = (remoteDep *) palloc(sizeof(remoteDep));
                dep->dbOid = sdepForm->dbid;
                dep->count = 1;
                remDeps = lappend(remDeps, dep);
            }
        }
    }

    systable_endscan(scan);

    heap_close(sdepRel, AccessShareLock);

    /*
     * Summarize dependencies in remote databases.
     */
    foreach(cell, remDeps)
    {
        remoteDep  *dep = lfirst(cell);

        object.classId = DatabaseRelationId;
        object.objectId = dep->dbOid;
        object.objectSubId = 0;

        if (numReportedDeps < MAX_REPORTED_DEPS)
        {
            numReportedDeps++;
            storeObjectDescription(&descs, REMOTE_OBJECT, &object,
                                   SHARED_DEPENDENCY_INVALID, dep->count);
        }
        else
            numNotReportedDbs++;
        storeObjectDescription(&alldescs, REMOTE_OBJECT, &object,
                               SHARED_DEPENDENCY_INVALID, dep->count);
    }

    list_free_deep(remDeps);

    if (descs.len == 0)
    {
        pfree(descs.data);
        pfree(alldescs.data);
        *detail_msg = *detail_log_msg = NULL;
        return false;
    }

    if (numNotReportedDeps > 0)
        appendStringInfo(&descs, ngettext("\nand %d other object "
                                          "(see server log for list)",
                                          "\nand %d other objects "
                                          "(see server log for list)",
                                          numNotReportedDeps),
                         numNotReportedDeps);
    if (numNotReportedDbs > 0)
        appendStringInfo(&descs, ngettext("\nand objects in %d other database "
                                          "(see server log for list)",
                                       "\nand objects in %d other databases "
                                          "(see server log for list)",
                                          numNotReportedDbs),
                         numNotReportedDbs);

    *detail_msg = descs.data;
    *detail_log_msg = alldescs.data;
    return true;
}

static Oid classIdGetDbId ( Oid  classId  )  [static]

Definition at line 969 of file pg_shdepend.c.

References IsSharedRelation(), and MyDatabaseId.

Referenced by shdepAddDependency(), shdepChangeDep(), and shdepDropDependency().

{
    Oid         dbId;

    if (IsSharedRelation(classId))
        dbId = InvalidOid;
    else
        dbId = MyDatabaseId;

    return dbId;
}

void copyTemplateDependencies ( Oid  templateDbId,
Oid  newDbId 
)

Definition at line 708 of file pg_shdepend.c.

References Anum_pg_shdepend_dbid, BTEqualStrategyNumber, CatalogCloseIndexes(), CatalogIndexInsert(), CatalogOpenIndexes(), heap_close, heap_freetuple(), heap_modify_tuple(), heap_open(), HeapTupleIsValid, ObjectIdGetDatum, RelationGetDescr, RowExclusiveLock, ScanKeyInit(), SharedDependDependerIndexId, SharedDependRelationId, simple_heap_insert(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), and values.

Referenced by createdb().

{
    Relation    sdepRel;
    TupleDesc   sdepDesc;
    ScanKeyData key[1];
    SysScanDesc scan;
    HeapTuple   tup;
    CatalogIndexState indstate;
    Datum       values[Natts_pg_shdepend];
    bool        nulls[Natts_pg_shdepend];
    bool        replace[Natts_pg_shdepend];

    sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);
    sdepDesc = RelationGetDescr(sdepRel);

    indstate = CatalogOpenIndexes(sdepRel);

    /* Scan all entries with dbid = templateDbId */
    ScanKeyInit(&key[0],
                Anum_pg_shdepend_dbid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(templateDbId));

    scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
                              SnapshotNow, 1, key);

    /* Set up to copy the tuples except for inserting newDbId */
    memset(values, 0, sizeof(values));
    memset(nulls, false, sizeof(nulls));
    memset(replace, false, sizeof(replace));

    replace[Anum_pg_shdepend_dbid - 1] = true;
    values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(newDbId);

    /*
     * Copy the entries of the original database, changing the database Id to
     * that of the new database.  Note that because we are not copying rows
     * with dbId == 0 (ie, rows describing dependent shared objects) we won't
     * copy the ownership dependency of the template database itself; this is
     * what we want.
     */
    while (HeapTupleIsValid(tup = systable_getnext(scan)))
    {
        HeapTuple   newtup;

        newtup = heap_modify_tuple(tup, sdepDesc, values, nulls, replace);
        simple_heap_insert(sdepRel, newtup);

        /* Keep indexes current */
        CatalogIndexInsert(indstate, newtup);

        heap_freetuple(newtup);
    }

    systable_endscan(scan);

    CatalogCloseIndexes(indstate);
    heap_close(sdepRel, RowExclusiveLock);
}

void deleteSharedDependencyRecordsFor ( Oid  classId,
Oid  objectId,
int32  objectSubId 
)
void dropDatabaseDependencies ( Oid  databaseId  ) 

Definition at line 775 of file pg_shdepend.c.

References Anum_pg_shdepend_dbid, BTEqualStrategyNumber, DatabaseRelationId, heap_close, heap_open(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum, RowExclusiveLock, ScanKeyInit(), SHARED_DEPENDENCY_INVALID, SharedDependDependerIndexId, SharedDependRelationId, shdepDropDependency(), simple_heap_delete(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by dropdb().

{
    Relation    sdepRel;
    ScanKeyData key[1];
    SysScanDesc scan;
    HeapTuple   tup;

    sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);

    /*
     * First, delete all the entries that have the database Oid in the dbid
     * field.
     */
    ScanKeyInit(&key[0],
                Anum_pg_shdepend_dbid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(databaseId));
    /* We leave the other index fields unspecified */

    scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
                              SnapshotNow, 1, key);

    while (HeapTupleIsValid(tup = systable_getnext(scan)))
    {
        simple_heap_delete(sdepRel, &tup->t_self);
    }

    systable_endscan(scan);

    /* Now delete all entries corresponding to the database itself */
    shdepDropDependency(sdepRel, DatabaseRelationId, databaseId, 0, true,
                        InvalidOid, InvalidOid,
                        SHARED_DEPENDENCY_INVALID);

    heap_close(sdepRel, RowExclusiveLock);
}

static void getOidListDiff ( Oid list1,
int *  nlist1,
Oid list2,
int *  nlist2 
) [static]

Definition at line 348 of file pg_shdepend.c.

Referenced by updateAclDependencies().

{
    int         in1,
                in2,
                out1,
                out2;

    in1 = in2 = out1 = out2 = 0;
    while (in1 < *nlist1 && in2 < *nlist2)
    {
        if (list1[in1] == list2[in2])
        {
            /* skip over duplicates */
            in1++;
            in2++;
        }
        else if (list1[in1] < list2[in2])
        {
            /* list1[in1] is not in list2 */
            list1[out1++] = list1[in1++];
        }
        else
        {
            /* list2[in2] is not in list1 */
            list2[out2++] = list2[in2++];
        }
    }

    /* any remaining list1 entries are not in list2 */
    while (in1 < *nlist1)
    {
        list1[out1++] = list1[in1++];
    }

    /* any remaining list2 entries are not in list1 */
    while (in2 < *nlist2)
    {
        list2[out2++] = list2[in2++];
    }

    *nlist1 = out1;
    *nlist2 = out2;
}

static bool isSharedObjectPinned ( Oid  classId,
Oid  objectId,
Relation  sdepRel 
) [static]

Definition at line 1111 of file pg_shdepend.c.

References Anum_pg_shdepend_refclassid, Anum_pg_shdepend_refobjid, BTEqualStrategyNumber, GETSTRUCT, HeapTupleIsValid, ObjectIdGetDatum, ScanKeyInit(), SHARED_DEPENDENCY_PIN, SharedDependReferenceIndexId, SnapshotNow, systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by recordSharedDependencyOn(), shdepChangeDep(), shdepDropOwned(), shdepReassignOwned(), and updateAclDependencies().

{
    bool        result = false;
    ScanKeyData key[2];
    SysScanDesc scan;
    HeapTuple   tup;

    ScanKeyInit(&key[0],
                Anum_pg_shdepend_refclassid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(classId));
    ScanKeyInit(&key[1],
                Anum_pg_shdepend_refobjid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(objectId));

    scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
                              SnapshotNow, 2, key);

    /*
     * Since we won't generate additional pg_shdepend 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_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);

        if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
            result = true;
    }

    systable_endscan(scan);

    return result;
}

void recordDependencyOnOwner ( Oid  classId,
Oid  objectId,
Oid  owner 
)
void recordSharedDependencyOn ( ObjectAddress depender,
ObjectAddress referenced,
SharedDependencyType  deptype 
)

Definition at line 106 of file pg_shdepend.c.

References Assert, ObjectAddress::classId, heap_close, heap_open(), IsBootstrapProcessingMode, isSharedObjectPinned(), ObjectAddress::objectId, ObjectAddress::objectSubId, RowExclusiveLock, SharedDependRelationId, and shdepAddDependency().

Referenced by recordDependencyOnOwner().

{
    Relation    sdepRel;

    /*
     * Objects in pg_shdepend can't have SubIds.
     */
    Assert(depender->objectSubId == 0);
    Assert(referenced->objectSubId == 0);

    /*
     * During bootstrap, do nothing since pg_shdepend may not exist yet.
     * initdb will fill in appropriate pg_shdepend entries after bootstrap.
     */
    if (IsBootstrapProcessingMode())
        return;

    sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);

    /* If the referenced object is pinned, do nothing. */
    if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
                              sdepRel))
    {
        shdepAddDependency(sdepRel, depender->classId, depender->objectId,
                           depender->objectSubId,
                           referenced->classId, referenced->objectId,
                           deptype);
    }

    heap_close(sdepRel, RowExclusiveLock);
}

static void shdepAddDependency ( Relation  sdepRel,
Oid  classId,
Oid  objectId,
int32  objsubId,
Oid  refclassId,
Oid  refobjId,
SharedDependencyType  deptype 
) [static]

Definition at line 845 of file pg_shdepend.c.

References Anum_pg_shdepend_classid, Anum_pg_shdepend_dbid, Anum_pg_shdepend_deptype, Anum_pg_shdepend_objid, Anum_pg_shdepend_objsubid, Anum_pg_shdepend_refclassid, Anum_pg_shdepend_refobjid, CatalogUpdateIndexes(), CharGetDatum, classIdGetDbId(), heap_form_tuple(), heap_freetuple(), Int32GetDatum, ObjectIdGetDatum, RelationData::rd_att, shdepLockAndCheckObject(), simple_heap_insert(), and values.

Referenced by recordSharedDependencyOn(), and updateAclDependencies().

{
    HeapTuple   tup;
    Datum       values[Natts_pg_shdepend];
    bool        nulls[Natts_pg_shdepend];

    /*
     * Make sure the object doesn't go away while we record the dependency on
     * it.  DROP routines should lock the object exclusively before they check
     * shared dependencies.
     */
    shdepLockAndCheckObject(refclassId, refobjId);

    memset(nulls, false, sizeof(nulls));

    /*
     * Form the new tuple and record the dependency.
     */
    values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(classIdGetDbId(classId));
    values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classId);
    values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objectId);
    values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubId);

    values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassId);
    values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjId);
    values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);

    tup = heap_form_tuple(sdepRel->rd_att, values, nulls);

    simple_heap_insert(sdepRel, tup);

    /* keep indexes current */
    CatalogUpdateIndexes(sdepRel, tup);

    /* clean up */
    heap_freetuple(tup);
}

static void shdepChangeDep ( Relation  sdepRel,
Oid  classid,
Oid  objid,
int32  objsubid,
Oid  refclassid,
Oid  refobjid,
SharedDependencyType  deptype 
) [static]

Definition at line 185 of file pg_shdepend.c.

References Anum_pg_shdepend_classid, Anum_pg_shdepend_dbid, Anum_pg_shdepend_deptype, Anum_pg_shdepend_objid, Anum_pg_shdepend_objsubid, Anum_pg_shdepend_refclassid, Anum_pg_shdepend_refobjid, BTEqualStrategyNumber, CatalogUpdateIndexes(), CharGetDatum, classIdGetDbId(), elog, ERROR, GETSTRUCT, heap_copytuple(), heap_form_tuple(), heap_freetuple(), Int32GetDatum, isSharedObjectPinned(), NULL, ObjectIdGetDatum, RelationGetDescr, ScanKeyInit(), SharedDependDependerIndexId, shdepLockAndCheckObject(), simple_heap_delete(), simple_heap_insert(), simple_heap_update(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, and values.

Referenced by changeDependencyOnOwner().

{
    Oid         dbid = classIdGetDbId(classid);
    HeapTuple   oldtup = NULL;
    HeapTuple   scantup;
    ScanKeyData key[4];
    SysScanDesc scan;

    /*
     * Make sure the new referenced object doesn't go away while we record the
     * dependency.
     */
    shdepLockAndCheckObject(refclassid, refobjid);

    /*
     * Look for a previous entry
     */
    ScanKeyInit(&key[0],
                Anum_pg_shdepend_dbid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(dbid));
    ScanKeyInit(&key[1],
                Anum_pg_shdepend_classid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(classid));
    ScanKeyInit(&key[2],
                Anum_pg_shdepend_objid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(objid));
    ScanKeyInit(&key[3],
                Anum_pg_shdepend_objsubid,
                BTEqualStrategyNumber, F_INT4EQ,
                Int32GetDatum(objsubid));

    scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
                              SnapshotNow, 4, key);

    while ((scantup = systable_getnext(scan)) != NULL)
    {
        /* Ignore if not of the target dependency type */
        if (((Form_pg_shdepend) GETSTRUCT(scantup))->deptype != deptype)
            continue;
        /* Caller screwed up if multiple matches */
        if (oldtup)
            elog(ERROR,
               "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
                 classid, objid, objsubid, deptype);
        oldtup = heap_copytuple(scantup);
    }

    systable_endscan(scan);

    if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
    {
        /* No new entry needed, so just delete existing entry if any */
        if (oldtup)
            simple_heap_delete(sdepRel, &oldtup->t_self);
    }
    else if (oldtup)
    {
        /* Need to update existing entry */
        Form_pg_shdepend shForm = (Form_pg_shdepend) GETSTRUCT(oldtup);

        /* Since oldtup is a copy, we can just modify it in-memory */
        shForm->refclassid = refclassid;
        shForm->refobjid = refobjid;

        simple_heap_update(sdepRel, &oldtup->t_self, oldtup);

        /* keep indexes current */
        CatalogUpdateIndexes(sdepRel, oldtup);
    }
    else
    {
        /* Need to insert new entry */
        Datum       values[Natts_pg_shdepend];
        bool        nulls[Natts_pg_shdepend];

        memset(nulls, false, sizeof(nulls));

        values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(dbid);
        values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(classid);
        values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(objid);
        values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(objsubid);

        values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(refclassid);
        values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(refobjid);
        values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(deptype);

        /*
         * we are reusing oldtup just to avoid declaring a new variable, but
         * it's certainly a new tuple
         */
        oldtup = heap_form_tuple(RelationGetDescr(sdepRel), values, nulls);
        simple_heap_insert(sdepRel, oldtup);

        /* keep indexes current */
        CatalogUpdateIndexes(sdepRel, oldtup);
    }

    if (oldtup)
        heap_freetuple(oldtup);
}

static void shdepDropDependency ( Relation  sdepRel,
Oid  classId,
Oid  objectId,
int32  objsubId,
bool  drop_subobjects,
Oid  refclassId,
Oid  refobjId,
SharedDependencyType  deptype 
) [static]

Definition at line 903 of file pg_shdepend.c.

References Anum_pg_shdepend_classid, Anum_pg_shdepend_dbid, Anum_pg_shdepend_objid, Anum_pg_shdepend_objsubid, BTEqualStrategyNumber, classIdGetDbId(), GETSTRUCT, HeapTupleIsValid, Int32GetDatum, ObjectIdGetDatum, OidIsValid, ScanKeyInit(), SHARED_DEPENDENCY_INVALID, SharedDependDependerIndexId, simple_heap_delete(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by changeDependencyOnOwner(), deleteSharedDependencyRecordsFor(), dropDatabaseDependencies(), and updateAclDependencies().

{
    ScanKeyData key[4];
    int         nkeys;
    SysScanDesc scan;
    HeapTuple   tup;

    /* Scan for entries matching the dependent object */
    ScanKeyInit(&key[0],
                Anum_pg_shdepend_dbid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(classIdGetDbId(classId)));
    ScanKeyInit(&key[1],
                Anum_pg_shdepend_classid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(classId));
    ScanKeyInit(&key[2],
                Anum_pg_shdepend_objid,
                BTEqualStrategyNumber, F_OIDEQ,
                ObjectIdGetDatum(objectId));
    if (drop_subobjects)
        nkeys = 3;
    else
    {
        ScanKeyInit(&key[3],
                    Anum_pg_shdepend_objsubid,
                    BTEqualStrategyNumber, F_INT4EQ,
                    Int32GetDatum(objsubId));
        nkeys = 4;
    }

    scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
                              SnapshotNow, nkeys, key);

    while (HeapTupleIsValid(tup = systable_getnext(scan)))
    {
        Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);

        /* Filter entries according to additional parameters */
        if (OidIsValid(refclassId) && shdepForm->refclassid != refclassId)
            continue;
        if (OidIsValid(refobjId) && shdepForm->refobjid != refobjId)
            continue;
        if (deptype != SHARED_DEPENDENCY_INVALID &&
            shdepForm->deptype != deptype)
            continue;

        /* OK, delete it */
        simple_heap_delete(sdepRel, &tup->t_self);
    }

    systable_endscan(scan);
}

void shdepDropOwned ( List roleids,
DropBehavior  behavior 
)

Definition at line 1163 of file pg_shdepend.c.

References add_exact_object_address(), Anum_pg_shdepend_refclassid, Anum_pg_shdepend_refobjid, AuthIdRelationId, BTEqualStrategyNumber, ObjectAddress::classId, elog, ereport, errcode(), errmsg(), ERROR, free_object_addresses(), getObjectDescription(), GETSTRUCT, heap_close, heap_open(), InvalidOid, isSharedObjectPinned(), lfirst_oid, MyDatabaseId, new_object_addresses(), NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, performMultipleDeletions(), RemoveRoleFromObjectACL(), RowExclusiveLock, ScanKeyInit(), SHARED_DEPENDENCY_ACL, SHARED_DEPENDENCY_INVALID, SHARED_DEPENDENCY_OWNER, SHARED_DEPENDENCY_PIN, SharedDependReferenceIndexId, SharedDependRelationId, SnapshotNow, systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by DropOwnedObjects().

{
    Relation    sdepRel;
    ListCell   *cell;
    ObjectAddresses *deleteobjs;

    deleteobjs = new_object_addresses();

    /*
     * We don't need this strong a lock here, but we'll call routines that
     * acquire RowExclusiveLock.  Better get that right now to avoid potential
     * deadlock failures.
     */
    sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);

    /*
     * For each role, find the dependent objects and drop them using the
     * regular (non-shared) dependency management.
     */
    foreach(cell, roleids)
    {
        Oid         roleid = lfirst_oid(cell);
        ScanKeyData key[2];
        SysScanDesc scan;
        HeapTuple   tuple;

        /* Doesn't work for pinned objects */
        if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
        {
            ObjectAddress obj;

            obj.classId = AuthIdRelationId;
            obj.objectId = roleid;
            obj.objectSubId = 0;

            ereport(ERROR,
                    (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
                   errmsg("cannot drop objects owned by %s because they are "
                          "required by the database system",
                          getObjectDescription(&obj))));
        }

        ScanKeyInit(&key[0],
                    Anum_pg_shdepend_refclassid,
                    BTEqualStrategyNumber, F_OIDEQ,
                    ObjectIdGetDatum(AuthIdRelationId));
        ScanKeyInit(&key[1],
                    Anum_pg_shdepend_refobjid,
                    BTEqualStrategyNumber, F_OIDEQ,
                    ObjectIdGetDatum(roleid));

        scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
                                  SnapshotNow, 2, key);

        while ((tuple = systable_getnext(scan)) != NULL)
        {
            Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
            ObjectAddress obj;

            /*
             * We only operate on shared objects and objects in the current
             * database
             */
            if (sdepForm->dbid != MyDatabaseId &&
                sdepForm->dbid != InvalidOid)
                continue;

            switch (sdepForm->deptype)
            {
                    /* Shouldn't happen */
                case SHARED_DEPENDENCY_PIN:
                case SHARED_DEPENDENCY_INVALID:
                    elog(ERROR, "unexpected dependency type");
                    break;
                case SHARED_DEPENDENCY_ACL:
                    RemoveRoleFromObjectACL(roleid,
                                            sdepForm->classid,
                                            sdepForm->objid);
                    break;
                case SHARED_DEPENDENCY_OWNER:
                    /* If a local object, save it for deletion below */
                    if (sdepForm->dbid == MyDatabaseId)
                    {
                        obj.classId = sdepForm->classid;
                        obj.objectId = sdepForm->objid;
                        obj.objectSubId = sdepForm->objsubid;
                        add_exact_object_address(&obj, deleteobjs);
                    }
                    break;
            }
        }

        systable_endscan(scan);
    }

    /* the dependency mechanism does the actual work */
    performMultipleDeletions(deleteobjs, behavior, 0);

    heap_close(sdepRel, RowExclusiveLock);

    free_object_addresses(deleteobjs);
}

void shdepLockAndCheckObject ( Oid  classId,
Oid  objectId 
)

Definition at line 990 of file pg_shdepend.c.

References AccessShareLock, AuthIdRelationId, AUTHOID, DatabaseRelationId, elog, ereport, errcode(), errmsg(), ERROR, get_database_name(), get_tablespace_name(), LockSharedObject(), NULL, ObjectIdGetDatum, pfree(), SearchSysCacheExists1, tablespace, and TableSpaceRelationId.

Referenced by AlterDatabaseSet(), AlterRoleSet(), shdepAddDependency(), and shdepChangeDep().

{
    /* AccessShareLock should be OK, since we are not modifying the object */
    LockSharedObject(classId, objectId, 0, AccessShareLock);

    switch (classId)
    {
        case AuthIdRelationId:
            if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(objectId)))
                ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                         errmsg("role %u was concurrently dropped",
                                objectId)));
            break;

            /*
             * Currently, this routine need not support any other shared
             * object types besides roles.  If we wanted to record explicit
             * dependencies on databases or tablespaces, we'd need code along
             * these lines:
             */
#ifdef NOT_USED
        case TableSpaceRelationId:
            {
                /* For lack of a syscache on pg_tablespace, do this: */
                char       *tablespace = get_tablespace_name(objectId);

                if (tablespace == NULL)
                    ereport(ERROR,
                            (errcode(ERRCODE_UNDEFINED_OBJECT),
                             errmsg("tablespace %u was concurrently dropped",
                                    objectId)));
                pfree(tablespace);
                break;
            }
#endif

        case DatabaseRelationId:
            {
                /* For lack of a syscache on pg_database, do this: */
                char       *database = get_database_name(objectId);

                if (database == NULL)
                    ereport(ERROR,
                            (errcode(ERRCODE_UNDEFINED_OBJECT),
                             errmsg("database %u was concurrently dropped",
                                    objectId)));
                pfree(database);
                break;
            }


        default:
            elog(ERROR, "unrecognized shared classId: %u", classId);
    }
}

void shdepReassignOwned ( List roleids,
Oid  newrole 
)

Definition at line 1273 of file pg_shdepend.c.

References AccessExclusiveLock, AlterEventTriggerOwner_oid(), AlterForeignDataWrapperOwner_oid(), AlterForeignServerOwner_oid(), AlterObjectOwner_internal(), AlterSchemaOwner_oid(), AlterTypeOwnerInternal(), Anum_pg_shdepend_refclassid, Anum_pg_shdepend_refobjid, ATExecChangeOwner(), AuthIdRelationId, BTEqualStrategyNumber, ObjectAddress::classId, CollationRelationId, CommandCounterIncrement(), ConversionRelationId, DatabaseRelationId, DefaultAclRelationId, elog, ereport, errcode(), errmsg(), ERROR, EventTriggerRelationId, ExtensionRelationId, ForeignDataWrapperRelationId, ForeignServerRelationId, getObjectDescription(), GETSTRUCT, heap_close, heap_open(), InvalidOid, isSharedObjectPinned(), LanguageRelationId, LargeObjectRelationId, lfirst_oid, MyDatabaseId, NamespaceRelationId, NoLock, NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OperatorClassRelationId, OperatorFamilyRelationId, OperatorRelationId, ProcedureRelationId, RelationRelationId, RowExclusiveLock, ScanKeyInit(), SHARED_DEPENDENCY_OWNER, SHARED_DEPENDENCY_PIN, SharedDependReferenceIndexId, SharedDependRelationId, SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), TableSpaceRelationId, and TypeRelationId.

Referenced by ReassignOwnedObjects().

{
    Relation    sdepRel;
    ListCell   *cell;

    /*
     * We don't need this strong a lock here, but we'll call routines that
     * acquire RowExclusiveLock.  Better get that right now to avoid potential
     * deadlock problems.
     */
    sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);

    foreach(cell, roleids)
    {
        SysScanDesc scan;
        ScanKeyData key[2];
        HeapTuple   tuple;
        Oid         roleid = lfirst_oid(cell);

        /* Refuse to work on pinned roles */
        if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
        {
            ObjectAddress obj;

            obj.classId = AuthIdRelationId;
            obj.objectId = roleid;
            obj.objectSubId = 0;

            ereport(ERROR,
                    (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
                     errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system",
                            getObjectDescription(&obj))));

            /*
             * There's no need to tell the whole truth, which is that we
             * didn't track these dependencies at all ...
             */
        }

        ScanKeyInit(&key[0],
                    Anum_pg_shdepend_refclassid,
                    BTEqualStrategyNumber, F_OIDEQ,
                    ObjectIdGetDatum(AuthIdRelationId));
        ScanKeyInit(&key[1],
                    Anum_pg_shdepend_refobjid,
                    BTEqualStrategyNumber, F_OIDEQ,
                    ObjectIdGetDatum(roleid));

        scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
                                  SnapshotNow, 2, key);

        while ((tuple = systable_getnext(scan)) != NULL)
        {
            Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);

            /*
             * We only operate on shared objects and objects in the current
             * database
             */
            if (sdepForm->dbid != MyDatabaseId &&
                sdepForm->dbid != InvalidOid)
                continue;

            /* Unexpected because we checked for pins above */
            if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
                elog(ERROR, "unexpected shared pin");

            /* We leave non-owner dependencies alone */
            if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
                continue;

            /* Issue the appropriate ALTER OWNER call */
            switch (sdepForm->classid)
            {
                case TypeRelationId:
                    AlterTypeOwnerInternal(sdepForm->objid, newrole, true);
                    break;

                case NamespaceRelationId:
                    AlterSchemaOwner_oid(sdepForm->objid, newrole);
                    break;

                case RelationRelationId:

                    /*
                     * Pass recursing = true so that we don't fail on indexes,
                     * owned sequences, etc when we happen to visit them
                     * before their parent table.
                     */
                    ATExecChangeOwner(sdepForm->objid, newrole, true, AccessExclusiveLock);
                    break;

                case DefaultAclRelationId:

                    /*
                     * Ignore default ACLs; they should be handled by DROP
                     * OWNED, not REASSIGN OWNED.
                     */
                    break;

                case ForeignServerRelationId:
                    AlterForeignServerOwner_oid(sdepForm->objid, newrole);
                    break;

                case ForeignDataWrapperRelationId:
                    AlterForeignDataWrapperOwner_oid(sdepForm->objid, newrole);
                    break;

                case EventTriggerRelationId:
                    AlterEventTriggerOwner_oid(sdepForm->objid, newrole);
                    break;

                /* Generic alter owner cases */
                case CollationRelationId:
                case ConversionRelationId:
                case OperatorRelationId:
                case ProcedureRelationId:
                case LanguageRelationId:
                case LargeObjectRelationId:
                case OperatorFamilyRelationId:
                case OperatorClassRelationId:
                case ExtensionRelationId:
                case TableSpaceRelationId:
                case DatabaseRelationId:
                    {
                        Oid         classId = sdepForm->classid;
                        Relation    catalog;

                        if (classId == LargeObjectRelationId)
                            classId = LargeObjectMetadataRelationId;

                        catalog = heap_open(classId, RowExclusiveLock);

                        AlterObjectOwner_internal(catalog, sdepForm->objid,
                                                  newrole);

                        heap_close(catalog, NoLock);
                    }
                    break;

                default:
                    elog(ERROR, "unexpected classid %u", sdepForm->classid);
                    break;
            }
            /* Make sure the next iteration will see my changes */
            CommandCounterIncrement();
        }

        systable_endscan(scan);
    }

    heap_close(sdepRel, RowExclusiveLock);
}

static void storeObjectDescription ( StringInfo  descs,
objectType  type,
ObjectAddress object,
SharedDependencyType  deptype,
int  count 
) [static]

Definition at line 1063 of file pg_shdepend.c.

References _, appendStringInfo(), appendStringInfoChar(), elog, ERROR, getObjectDescription(), StringInfoData::len, LOCAL_OBJECT, ngettext, pfree(), REMOTE_OBJECT, SHARED_DEPENDENCY_ACL, SHARED_DEPENDENCY_OWNER, and SHARED_OBJECT.

Referenced by checkSharedDependencies().

{
    char       *objdesc = getObjectDescription(object);

    /* separate entries with a newline */
    if (descs->len != 0)
        appendStringInfoChar(descs, '\n');

    switch (type)
    {
        case LOCAL_OBJECT:
        case SHARED_OBJECT:
            if (deptype == SHARED_DEPENDENCY_OWNER)
                appendStringInfo(descs, _("owner of %s"), objdesc);
            else if (deptype == SHARED_DEPENDENCY_ACL)
                appendStringInfo(descs, _("privileges for %s"), objdesc);
            else
                elog(ERROR, "unrecognized dependency type: %d",
                     (int) deptype);
            break;

        case REMOTE_OBJECT:
            /* translator: %s will always be "database %s" */
            appendStringInfo(descs, ngettext("%d object in %s",
                                             "%d objects in %s",
                                             count),
                             count, objdesc);
            break;

        default:
            elog(ERROR, "unrecognized object type: %d", type);
    }

    pfree(objdesc);
}

void updateAclDependencies ( Oid  classId,
Oid  objectId,
int32  objsubId,
Oid  ownerId,
int  noldmembers,
Oid oldmembers,
int  nnewmembers,
Oid newmembers 
)

Definition at line 418 of file pg_shdepend.c.

References AuthIdRelationId, getOidListDiff(), heap_close, heap_open(), i, isSharedObjectPinned(), pfree(), RowExclusiveLock, SHARED_DEPENDENCY_ACL, SharedDependRelationId, shdepAddDependency(), and shdepDropDependency().

Referenced by ExecGrant_Attribute(), ExecGrant_Database(), ExecGrant_Fdw(), ExecGrant_ForeignServer(), ExecGrant_Function(), ExecGrant_Language(), ExecGrant_Largeobject(), ExecGrant_Namespace(), ExecGrant_Relation(), ExecGrant_Tablespace(), ExecGrant_Type(), heap_create_with_catalog(), ProcedureCreate(), and SetDefaultACL().

{
    Relation    sdepRel;
    int         i;

    /*
     * Remove entries that are common to both lists; those represent existing
     * dependencies we don't need to change.
     *
     * OK to overwrite the inputs since we'll pfree them anyway.
     */
    getOidListDiff(oldmembers, &noldmembers, newmembers, &nnewmembers);

    if (noldmembers > 0 || nnewmembers > 0)
    {
        sdepRel = heap_open(SharedDependRelationId, RowExclusiveLock);

        /* Add new dependencies that weren't already present */
        for (i = 0; i < nnewmembers; i++)
        {
            Oid         roleid = newmembers[i];

            /*
             * Skip the owner: he has an OWNER shdep entry instead. (This is
             * not just a space optimization; it makes ALTER OWNER easier. See
             * notes in changeDependencyOnOwner.)
             */
            if (roleid == ownerId)
                continue;

            /* Skip pinned roles; they don't need dependency entries */
            if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
                continue;

            shdepAddDependency(sdepRel, classId, objectId, objsubId,
                               AuthIdRelationId, roleid,
                               SHARED_DEPENDENCY_ACL);
        }

        /* Drop no-longer-used old dependencies */
        for (i = 0; i < noldmembers; i++)
        {
            Oid         roleid = oldmembers[i];

            /* Skip the owner, same as above */
            if (roleid == ownerId)
                continue;

            /* Skip pinned roles */
            if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
                continue;

            shdepDropDependency(sdepRel, classId, objectId, objsubId,
                                false,  /* exact match on objsubId */
                                AuthIdRelationId, roleid,
                                SHARED_DEPENDENCY_ACL);
        }

        heap_close(sdepRel, RowExclusiveLock);
    }

    if (oldmembers)
        pfree(oldmembers);
    if (newmembers)
        pfree(newmembers);
}