Esempio n. 1
0
/*
 * This is a copy of swap_relation_files in cluster.c, but it also swaps
 * relfrozenxid.
 */
static void
swap_heap_or_index_files(Oid r1, Oid r2)
{
    Relation	relRelation;
    HeapTuple	reltup1,
                reltup2;
    Form_pg_class relform1,
                  relform2;
    Oid			swaptemp;
    CatalogIndexState indstate;

    /* We need writable copies of both pg_class tuples. */
    relRelation = heap_open(RelationRelationId, RowExclusiveLock);

    reltup1 = SearchSysCacheCopy(RELOID,
                                 ObjectIdGetDatum(r1),
                                 0, 0, 0);
    if (!HeapTupleIsValid(reltup1))
        elog(ERROR, "cache lookup failed for relation %u", r1);
    relform1 = (Form_pg_class) GETSTRUCT(reltup1);

    reltup2 = SearchSysCacheCopy(RELOID,
                                 ObjectIdGetDatum(r2),
                                 0, 0, 0);
    if (!HeapTupleIsValid(reltup2))
        elog(ERROR, "cache lookup failed for relation %u", r2);
    relform2 = (Form_pg_class) GETSTRUCT(reltup2);

    Assert(relform1->relkind == relform2->relkind);

    /*
     * Actually swap the fields in the two tuples
     */
    swaptemp = relform1->relfilenode;
    relform1->relfilenode = relform2->relfilenode;
    relform2->relfilenode = swaptemp;

    swaptemp = relform1->reltablespace;
    relform1->reltablespace = relform2->reltablespace;
    relform2->reltablespace = swaptemp;

    swaptemp = relform1->reltoastrelid;
    relform1->reltoastrelid = relform2->reltoastrelid;
    relform2->reltoastrelid = swaptemp;

    /* set rel1's frozen Xid to larger one */
    if (TransactionIdIsNormal(relform1->relfrozenxid))
    {
        if (TransactionIdFollows(relform1->relfrozenxid,
                                 relform2->relfrozenxid))
            relform1->relfrozenxid = relform2->relfrozenxid;
        else
            relform2->relfrozenxid = relform1->relfrozenxid;
    }

    /* swap size statistics too, since new rel has freshly-updated stats */
    {
#if PG_VERSION_NUM >= 90300
        int32		swap_pages;
#else
        int4		swap_pages;
#endif
        float4		swap_tuples;

        swap_pages = relform1->relpages;
        relform1->relpages = relform2->relpages;
        relform2->relpages = swap_pages;

        swap_tuples = relform1->reltuples;
        relform1->reltuples = relform2->reltuples;
        relform2->reltuples = swap_tuples;
    }

    /* Update the tuples in pg_class */
    simple_heap_update(relRelation, &reltup1->t_self, reltup1);
    simple_heap_update(relRelation, &reltup2->t_self, reltup2);

    /* Keep system catalogs current */
    indstate = CatalogOpenIndexes(relRelation);
    CatalogIndexInsert(indstate, reltup1);
    CatalogIndexInsert(indstate, reltup2);
    CatalogCloseIndexes(indstate);

    /*
     * If we have toast tables associated with the relations being swapped,
     * change their dependency links to re-associate them with their new
     * owning relations.  Otherwise the wrong one will get dropped ...
     *
     * NOTE: it is possible that only one table has a toast table; this can
     * happen in CLUSTER if there were dropped columns in the old table, and
     * in ALTER TABLE when adding or changing type of columns.
     *
     * NOTE: at present, a TOAST table's only dependency is the one on its
     * owning table.  If more are ever created, we'd need to use something
     * more selective than deleteDependencyRecordsFor() to get rid of only the
     * link we want.
     */
    if (relform1->reltoastrelid || relform2->reltoastrelid)
    {
        ObjectAddress baseobject,
                      toastobject;
        long		count;

        /* Delete old dependencies */
        if (relform1->reltoastrelid)
        {
            count = deleteDependencyRecordsFor(RelationRelationId,
                                               relform1->reltoastrelid,
                                               false);
            if (count != 1)
                elog(ERROR, "expected one dependency record for TOAST table, found %ld",
                     count);
        }
        if (relform2->reltoastrelid)
        {
            count = deleteDependencyRecordsFor(RelationRelationId,
                                               relform2->reltoastrelid,
                                               false);
            if (count != 1)
                elog(ERROR, "expected one dependency record for TOAST table, found %ld",
                     count);
        }

        /* Register new dependencies */
        baseobject.classId = RelationRelationId;
        baseobject.objectSubId = 0;
        toastobject.classId = RelationRelationId;
        toastobject.objectSubId = 0;

        if (relform1->reltoastrelid)
        {
            baseobject.objectId = r1;
            toastobject.objectId = relform1->reltoastrelid;
            recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
        }

        if (relform2->reltoastrelid)
        {
            baseobject.objectId = r2;
            toastobject.objectId = relform2->reltoastrelid;
            recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
        }
    }

    /*
     * Blow away the old relcache entries now.	We need this kluge because
     * relcache.c keeps a link to the smgr relation for the physical file, and
     * that will be out of date as soon as we do CommandCounterIncrement.
     * Whichever of the rels is the second to be cleared during cache
     * invalidation will have a dangling reference to an already-deleted smgr
     * relation.  Rather than trying to avoid this by ordering operations just
     * so, it's easiest to not have the relcache entries there at all.
     * (Fortunately, since one of the entries is local in our transaction,
     * it's sufficient to clear out our own relcache this way; the problem
     * cannot arise for other backends when they see our update on the
     * non-local relation.)
     */
    RelationForgetRelation(r1);
    RelationForgetRelation(r2);

    /* Clean up. */
    heap_freetuple(reltup1);
    heap_freetuple(reltup2);

    heap_close(relRelation, RowExclusiveLock);
}
Esempio n. 2
0
/*
 *		index_drop
 *
 * NOTE: this routine should now only be called through performDeletion(),
 * else associated dependencies won't be cleaned up.
 */
void
index_drop(Oid indexId)
{
	Oid			heapId;
	Relation	userHeapRelation;
	Relation	userIndexRelation;
	Relation	indexRelation;
	HeapTuple	tuple;
	int			i;

	Assert(OidIsValid(indexId));

	/*
	 * To drop an index safely, we must grab exclusive lock on its parent
	 * table; otherwise there could be other backends using the index!
	 * Exclusive lock on the index alone is insufficient because another
	 * backend might be in the midst of devising a query plan that will
	 * use the index.  The parser and planner take care to hold an
	 * appropriate lock on the parent table while working, but having them
	 * hold locks on all the indexes too seems overly complex.	We do grab
	 * exclusive lock on the index too, just to be safe. Both locks must
	 * be held till end of transaction, else other backends will still see
	 * this index in pg_index.
	 */
	heapId = IndexGetRelation(indexId);
	userHeapRelation = heap_open(heapId, AccessExclusiveLock);

	userIndexRelation = index_open(indexId);
	LockRelation(userIndexRelation, AccessExclusiveLock);

	/*
	 * fix RELATION relation
	 */
	DeleteRelationTuple(indexId);

	/*
	 * fix ATTRIBUTE relation
	 */
	DeleteAttributeTuples(indexId);

	/*
	 * fix INDEX relation
	 */
	indexRelation = heap_openr(IndexRelationName, RowExclusiveLock);

	tuple = SearchSysCache(INDEXRELID,
						   ObjectIdGetDatum(indexId),
						   0, 0, 0);
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for index %u", indexId);

	simple_heap_delete(indexRelation, &tuple->t_self);

	ReleaseSysCache(tuple);
	heap_close(indexRelation, RowExclusiveLock);

	/*
	 * flush buffer cache and physically remove the file
	 */
	i = FlushRelationBuffers(userIndexRelation, (BlockNumber) 0);
	if (i < 0)
		elog(ERROR, "FlushRelationBuffers returned %d", i);

	smgrunlink(DEFAULT_SMGR, userIndexRelation);

	/*
	 * We are presently too lazy to attempt to compute the new correct
	 * value of relhasindex (the next VACUUM will fix it if necessary). So
	 * there is no need to update the pg_class tuple for the owning
	 * relation. But we must send out a shared-cache-inval notice on the
	 * owning relation to ensure other backends update their relcache
	 * lists of indexes.
	 */
	CacheInvalidateRelcache(heapId);

	/*
	 * Close rels, but keep locks
	 */
	index_close(userIndexRelation);
	heap_close(userHeapRelation, NoLock);

	RelationForgetRelation(indexId);
}