Header And Logo

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

Functions | Variables

snapmgr.h File Reference

#include "fmgr.h"
#include "utils/resowner.h"
#include "utils/snapshot.h"
Include dependency graph for snapmgr.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

Snapshot GetTransactionSnapshot (void)
Snapshot GetLatestSnapshot (void)
void SnapshotSetCommandId (CommandId curcid)
void PushActiveSnapshot (Snapshot snapshot)
void PushCopiedSnapshot (Snapshot snapshot)
void UpdateActiveSnapshotCommandId (void)
void PopActiveSnapshot (void)
Snapshot GetActiveSnapshot (void)
bool ActiveSnapshotSet (void)
Snapshot RegisterSnapshot (Snapshot snapshot)
void UnregisterSnapshot (Snapshot snapshot)
Snapshot RegisterSnapshotOnOwner (Snapshot snapshot, ResourceOwner owner)
void UnregisterSnapshotFromOwner (Snapshot snapshot, ResourceOwner owner)
void AtSubCommit_Snapshot (int level)
void AtSubAbort_Snapshot (int level)
void AtEOXact_Snapshot (bool isCommit)
Datum pg_export_snapshot (PG_FUNCTION_ARGS)
void ImportSnapshot (const char *idstr)
bool XactHasExportedSnapshots (void)
void DeleteAllExportedSnapshotFiles (void)
bool ThereAreNoPriorRegisteredSnapshots (void)

Variables

bool FirstSnapshotSet
TransactionId TransactionXmin
TransactionId RecentXmin
TransactionId RecentGlobalXmin

Function Documentation

bool ActiveSnapshotSet ( void   ) 
void AtEOXact_Snapshot ( bool  isCommit  ) 

Definition at line 643 of file snapmgr.c.

References ActiveSnapshotElt::as_next, Assert, buf, elog, FirstSnapshotSet, GetTopTransactionId(), i, list_length(), NIL, NULL, SnapshotData::regd_count, RegisteredSnapshots, SnapshotResetXmin(), unlink(), WARNING, and XactExportFilePath.

Referenced by CleanupTransaction(), CommitTransaction(), and PrepareTransaction().

{
    /*
     * In transaction-snapshot mode we must release our privately-managed
     * reference to the transaction snapshot.  We must decrement
     * RegisteredSnapshots to keep the check below happy.  But we don't bother
     * to do FreeSnapshot, for two reasons: the memory will go away with
     * TopTransactionContext anyway, and if someone has left the snapshot
     * stacked as active, we don't want the code below to be chasing through a
     * dangling pointer.
     */
    if (FirstXactSnapshot != NULL)
    {
        Assert(FirstXactSnapshot->regd_count > 0);
        Assert(RegisteredSnapshots > 0);
        RegisteredSnapshots--;
    }
    FirstXactSnapshot = NULL;

    /*
     * If we exported any snapshots, clean them up.
     */
    if (exportedSnapshots != NIL)
    {
        TransactionId myxid = GetTopTransactionId();
        int         i;
        char        buf[MAXPGPATH];

        /*
         * Get rid of the files.  Unlink failure is only a WARNING because (1)
         * it's too late to abort the transaction, and (2) leaving a leaked
         * file around has little real consequence anyway.
         */
        for (i = 1; i <= list_length(exportedSnapshots); i++)
        {
            XactExportFilePath(buf, myxid, i, "");
            if (unlink(buf))
                elog(WARNING, "could not unlink file \"%s\": %m", buf);
        }

        /*
         * As with the FirstXactSnapshot, we needn't spend any effort on
         * cleaning up the per-snapshot data structures, but we do need to
         * adjust the RegisteredSnapshots count to prevent a warning below.
         *
         * Note: you might be thinking "why do we have the exportedSnapshots
         * list at all?  All we need is a counter!".  You're right, but we do
         * it this way in case we ever feel like improving xmin management.
         */
        Assert(RegisteredSnapshots >= list_length(exportedSnapshots));
        RegisteredSnapshots -= list_length(exportedSnapshots);

        exportedSnapshots = NIL;
    }

    /* On commit, complain about leftover snapshots */
    if (isCommit)
    {
        ActiveSnapshotElt *active;

        if (RegisteredSnapshots != 0)
            elog(WARNING, "%d registered snapshots seem to remain after cleanup",
                 RegisteredSnapshots);

        /* complain about unpopped active snapshots */
        for (active = ActiveSnapshot; active != NULL; active = active->as_next)
            elog(WARNING, "snapshot %p still active", active);
    }

    /*
     * And reset our state.  We don't need to free the memory explicitly --
     * it'll go away with TopTransactionContext.
     */
    ActiveSnapshot = NULL;
    RegisteredSnapshots = 0;

    CurrentSnapshot = NULL;
    SecondarySnapshot = NULL;

    FirstSnapshotSet = false;

    SnapshotResetXmin();
}

void AtSubAbort_Snapshot ( int  level  ) 

Definition at line 609 of file snapmgr.c.

References SnapshotData::active_count, ActiveSnapshotElt::as_level, ActiveSnapshotElt::as_next, ActiveSnapshotElt::as_snap, Assert, FreeSnapshot(), pfree(), SnapshotData::regd_count, and SnapshotResetXmin().

Referenced by AbortSubTransaction().

{
    /* Forget the active snapshots set by this subtransaction */
    while (ActiveSnapshot && ActiveSnapshot->as_level >= level)
    {
        ActiveSnapshotElt *next;

        next = ActiveSnapshot->as_next;

        /*
         * Decrement the snapshot's active count.  If it's still registered or
         * marked as active by an outer subtransaction, we can't free it yet.
         */
        Assert(ActiveSnapshot->as_snap->active_count >= 1);
        ActiveSnapshot->as_snap->active_count -= 1;

        if (ActiveSnapshot->as_snap->active_count == 0 &&
            ActiveSnapshot->as_snap->regd_count == 0)
            FreeSnapshot(ActiveSnapshot->as_snap);

        /* and free the stack element */
        pfree(ActiveSnapshot);

        ActiveSnapshot = next;
    }

    SnapshotResetXmin();
}

void AtSubCommit_Snapshot ( int  level  ) 

Definition at line 588 of file snapmgr.c.

References ActiveSnapshotElt::as_level, and ActiveSnapshotElt::as_next.

Referenced by CommitSubTransaction().

{
    ActiveSnapshotElt *active;

    /*
     * Relabel the active snapshots set in this subtransaction as though they
     * are owned by the parent subxact.
     */
    for (active = ActiveSnapshot; active != NULL; active = active->as_next)
    {
        if (active->as_level < level)
            break;
        active->as_level = level - 1;
    }
}

void DeleteAllExportedSnapshotFiles ( void   ) 

Definition at line 1156 of file snapmgr.c.

References AllocateDir(), buf, dirent::d_name, elog, FreeDir(), LOG, MAXPGPATH, NULL, ReadDir(), SNAPSHOT_EXPORT_DIR, snprintf(), and unlink().

Referenced by StartupXLOG().

{
    char        buf[MAXPGPATH];
    DIR        *s_dir;
    struct dirent *s_de;

    if (!(s_dir = AllocateDir(SNAPSHOT_EXPORT_DIR)))
    {
        /*
         * We really should have that directory in a sane cluster setup. But
         * then again if we don't, it's not fatal enough to make it FATAL.
         * Since we're running in the postmaster, LOG is our best bet.
         */
        elog(LOG, "could not open directory \"%s\": %m", SNAPSHOT_EXPORT_DIR);
        return;
    }

    while ((s_de = ReadDir(s_dir, SNAPSHOT_EXPORT_DIR)) != NULL)
    {
        if (strcmp(s_de->d_name, ".") == 0 ||
            strcmp(s_de->d_name, "..") == 0)
            continue;

        snprintf(buf, MAXPGPATH, SNAPSHOT_EXPORT_DIR "/%s", s_de->d_name);
        /* Again, unlink failure is not worthy of FATAL */
        if (unlink(buf))
            elog(LOG, "could not unlink file \"%s\": %m", buf);
    }

    FreeDir(s_dir);
}

Snapshot GetActiveSnapshot ( void   ) 
Snapshot GetLatestSnapshot ( void   ) 
Snapshot GetTransactionSnapshot ( void   ) 

Definition at line 148 of file snapmgr.c.

References Assert, CopySnapshot(), FirstSnapshotSet, GetSerializableTransactionSnapshot(), GetSnapshotData(), IsolationIsSerializable, IsolationUsesXactSnapshot, NULL, SnapshotData::regd_count, and RegisteredSnapshots.

Referenced by _SPI_execute_plan(), AfterTriggerFireDeferred(), AfterTriggerSetState(), BuildCachedPlan(), CheckTargetForConflictsIn(), cluster(), DefineIndex(), enum_endpoint(), enum_range_internal(), exec_bind_message(), exec_eval_simple_expr(), exec_parse_message(), exec_simple_query(), execute_sql_string(), fmgr_sql(), get_database_list(), GetLatestSnapshot(), HandleFunctionRequest(), IndexBuildHeapScan(), initialize_worker_spi(), InitPostgres(), PortalRunMulti(), PortalRunUtility(), PortalStart(), ReindexDatabase(), RevalidateCachedQuery(), ri_PerformCheck(), SPI_cursor_open_internal(), vacuum(), vacuum_rel(), worker_spi_main(), and XidIsConcurrent().

{
    /* First call in transaction? */
    if (!FirstSnapshotSet)
    {
        Assert(RegisteredSnapshots == 0);
        Assert(FirstXactSnapshot == NULL);

        /*
         * In transaction-snapshot mode, the first snapshot must live until
         * end of xact regardless of what the caller does with it, so we must
         * make a copy of it rather than returning CurrentSnapshotData
         * directly.  Furthermore, if we're running in serializable mode,
         * predicate.c needs to wrap the snapshot fetch in its own processing.
         */
        if (IsolationUsesXactSnapshot())
        {
            /* First, create the snapshot in CurrentSnapshotData */
            if (IsolationIsSerializable())
                CurrentSnapshot = GetSerializableTransactionSnapshot(&CurrentSnapshotData);
            else
                CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
            /* Make a saved copy */
            CurrentSnapshot = CopySnapshot(CurrentSnapshot);
            FirstXactSnapshot = CurrentSnapshot;
            /* Mark it as "registered" in FirstXactSnapshot */
            FirstXactSnapshot->regd_count++;
            RegisteredSnapshots++;
        }
        else
            CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);

        FirstSnapshotSet = true;
        return CurrentSnapshot;
    }

    if (IsolationUsesXactSnapshot())
        return CurrentSnapshot;

    CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);

    return CurrentSnapshot;
}

void ImportSnapshot ( const char *  idstr  ) 

Definition at line 972 of file snapmgr.c.

References AllocateFile(), elog, ereport, errcode(), errmsg(), ERROR, FirstSnapshotSet, FreeFile(), GetMaxSnapshotSubxidCount(), GetMaxSnapshotXidCount(), GetTopTransactionIdIfAny(), i, InvalidTransactionId, IsolationIsSerializable, IsolationUsesXactSnapshot, IsSubTransaction(), MAXPGPATH, MyDatabaseId, OidIsValid, palloc(), parseIntFromText(), parseXidFromText(), PG_BINARY_R, SetTransactionSnapshot(), SNAPSHOT_EXPORT_DIR, snprintf(), SnapshotData::suboverflowed, SnapshotData::subxcnt, SnapshotData::subxip, SnapshotData::takenDuringRecovery, TransactionIdIsNormal, XACT_SERIALIZABLE, XactReadOnly, SnapshotData::xcnt, SnapshotData::xip, SnapshotData::xmax, and SnapshotData::xmin.

Referenced by ExecSetVariableStmt().

{
    char        path[MAXPGPATH];
    FILE       *f;
    struct stat stat_buf;
    char       *filebuf;
    int         xcnt;
    int         i;
    TransactionId src_xid;
    Oid         src_dbid;
    int         src_isolevel;
    bool        src_readonly;
    SnapshotData snapshot;

    /*
     * Must be at top level of a fresh transaction.  Note in particular that
     * we check we haven't acquired an XID --- if we have, it's conceivable
     * that the snapshot would show it as not running, making for very screwy
     * behavior.
     */
    if (FirstSnapshotSet ||
        GetTopTransactionIdIfAny() != InvalidTransactionId ||
        IsSubTransaction())
        ereport(ERROR,
                (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
        errmsg("SET TRANSACTION SNAPSHOT must be called before any query")));

    /*
     * If we are in read committed mode then the next query would execute with
     * a new snapshot thus making this function call quite useless.
     */
    if (!IsolationUsesXactSnapshot())
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("a snapshot-importing transaction must have isolation level SERIALIZABLE or REPEATABLE READ")));

    /*
     * Verify the identifier: only 0-9, A-F and hyphens are allowed.  We do
     * this mainly to prevent reading arbitrary files.
     */
    if (strspn(idstr, "0123456789ABCDEF-") != strlen(idstr))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid snapshot identifier: \"%s\"", idstr)));

    /* OK, read the file */
    snprintf(path, MAXPGPATH, SNAPSHOT_EXPORT_DIR "/%s", idstr);

    f = AllocateFile(path, PG_BINARY_R);
    if (!f)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("invalid snapshot identifier: \"%s\"", idstr)));

    /* get the size of the file so that we know how much memory we need */
    if (fstat(fileno(f), &stat_buf))
        elog(ERROR, "could not stat file \"%s\": %m", path);

    /* and read the file into a palloc'd string */
    filebuf = (char *) palloc(stat_buf.st_size + 1);
    if (fread(filebuf, stat_buf.st_size, 1, f) != 1)
        elog(ERROR, "could not read file \"%s\": %m", path);

    filebuf[stat_buf.st_size] = '\0';

    FreeFile(f);

    /*
     * Construct a snapshot struct by parsing the file content.
     */
    memset(&snapshot, 0, sizeof(snapshot));

    src_xid = parseXidFromText("xid:", &filebuf, path);
    /* we abuse parseXidFromText a bit here ... */
    src_dbid = parseXidFromText("dbid:", &filebuf, path);
    src_isolevel = parseIntFromText("iso:", &filebuf, path);
    src_readonly = parseIntFromText("ro:", &filebuf, path);

    snapshot.xmin = parseXidFromText("xmin:", &filebuf, path);
    snapshot.xmax = parseXidFromText("xmax:", &filebuf, path);

    snapshot.xcnt = xcnt = parseIntFromText("xcnt:", &filebuf, path);

    /* sanity-check the xid count before palloc */
    if (xcnt < 0 || xcnt > GetMaxSnapshotXidCount())
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid snapshot data in file \"%s\"", path)));

    snapshot.xip = (TransactionId *) palloc(xcnt * sizeof(TransactionId));
    for (i = 0; i < xcnt; i++)
        snapshot.xip[i] = parseXidFromText("xip:", &filebuf, path);

    snapshot.suboverflowed = parseIntFromText("sof:", &filebuf, path);

    if (!snapshot.suboverflowed)
    {
        snapshot.subxcnt = xcnt = parseIntFromText("sxcnt:", &filebuf, path);

        /* sanity-check the xid count before palloc */
        if (xcnt < 0 || xcnt > GetMaxSnapshotSubxidCount())
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                     errmsg("invalid snapshot data in file \"%s\"", path)));

        snapshot.subxip = (TransactionId *) palloc(xcnt * sizeof(TransactionId));
        for (i = 0; i < xcnt; i++)
            snapshot.subxip[i] = parseXidFromText("sxp:", &filebuf, path);
    }
    else
    {
        snapshot.subxcnt = 0;
        snapshot.subxip = NULL;
    }

    snapshot.takenDuringRecovery = parseIntFromText("rec:", &filebuf, path);

    /*
     * Do some additional sanity checking, just to protect ourselves.  We
     * don't trouble to check the array elements, just the most critical
     * fields.
     */
    if (!TransactionIdIsNormal(src_xid) ||
        !OidIsValid(src_dbid) ||
        !TransactionIdIsNormal(snapshot.xmin) ||
        !TransactionIdIsNormal(snapshot.xmax))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid snapshot data in file \"%s\"", path)));

    /*
     * If we're serializable, the source transaction must be too, otherwise
     * predicate.c has problems (SxactGlobalXmin could go backwards).  Also, a
     * non-read-only transaction can't adopt a snapshot from a read-only
     * transaction, as predicate.c handles the cases very differently.
     */
    if (IsolationIsSerializable())
    {
        if (src_isolevel != XACT_SERIALIZABLE)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("a serializable transaction cannot import a snapshot from a non-serializable transaction")));
        if (src_readonly && !XactReadOnly)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("a non-read-only serializable transaction cannot import a snapshot from a read-only transaction")));
    }

    /*
     * We cannot import a snapshot that was taken in a different database,
     * because vacuum calculates OldestXmin on a per-database basis; so the
     * source transaction's xmin doesn't protect us from data loss.  This
     * restriction could be removed if the source transaction were to mark its
     * xmin as being globally applicable.  But that would require some
     * additional syntax, since that has to be known when the snapshot is
     * initially taken.  (See pgsql-hackers discussion of 2011-10-21.)
     */
    if (src_dbid != MyDatabaseId)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
              errmsg("cannot import a snapshot from a different database")));

    /* OK, install the snapshot */
    SetTransactionSnapshot(&snapshot, src_xid);
}

Datum pg_export_snapshot ( PG_FUNCTION_ARGS   ) 

Definition at line 901 of file snapmgr.c.

References cstring_to_text(), ExportSnapshot(), GetActiveSnapshot(), and PG_RETURN_TEXT_P.

{
    char       *snapshotName;

    snapshotName = ExportSnapshot(GetActiveSnapshot());
    PG_RETURN_TEXT_P(cstring_to_text(snapshotName));
}

void PopActiveSnapshot ( void   ) 
void PushActiveSnapshot ( Snapshot  snapshot  ) 
void PushCopiedSnapshot ( Snapshot  snapshot  ) 
Snapshot RegisterSnapshot ( Snapshot  snapshot  ) 
Snapshot RegisterSnapshotOnOwner ( Snapshot  snapshot,
ResourceOwner  owner 
)

Definition at line 512 of file snapmgr.c.

References SnapshotData::copied, CopySnapshot(), InvalidSnapshot, SnapshotData::regd_count, RegisteredSnapshots, ResourceOwnerEnlargeSnapshots(), and ResourceOwnerRememberSnapshot().

Referenced by inv_open(), and RegisterSnapshot().

{
    Snapshot    snap;

    if (snapshot == InvalidSnapshot)
        return InvalidSnapshot;

    /* Static snapshot?  Create a persistent copy */
    snap = snapshot->copied ? snapshot : CopySnapshot(snapshot);

    /* and tell resowner.c about it */
    ResourceOwnerEnlargeSnapshots(owner);
    snap->regd_count++;
    ResourceOwnerRememberSnapshot(owner, snap);

    RegisteredSnapshots++;

    return snap;
}

void SnapshotSetCommandId ( CommandId  curcid  ) 

Definition at line 214 of file snapmgr.c.

References SnapshotData::curcid, and FirstSnapshotSet.

Referenced by CommandCounterIncrement().

bool ThereAreNoPriorRegisteredSnapshots ( void   ) 

Definition at line 1189 of file snapmgr.c.

References RegisteredSnapshots.

Referenced by CopyFrom().

{
    if (RegisteredSnapshots <= 1)
        return true;

    return false;
}

void UnregisterSnapshot ( Snapshot  snapshot  ) 
void UnregisterSnapshotFromOwner ( Snapshot  snapshot,
ResourceOwner  owner 
)

Definition at line 553 of file snapmgr.c.

References SnapshotData::active_count, Assert, FreeSnapshot(), NULL, SnapshotData::regd_count, RegisteredSnapshots, ResourceOwnerForgetSnapshot(), and SnapshotResetXmin().

Referenced by inv_close(), and UnregisterSnapshot().

{
    if (snapshot == NULL)
        return;

    Assert(snapshot->regd_count > 0);
    Assert(RegisteredSnapshots > 0);

    ResourceOwnerForgetSnapshot(owner, snapshot);
    RegisteredSnapshots--;
    if (--snapshot->regd_count == 0 && snapshot->active_count == 0)
    {
        FreeSnapshot(snapshot);
        SnapshotResetXmin();
    }
}

void UpdateActiveSnapshotCommandId ( void   ) 
bool XactHasExportedSnapshots ( void   ) 

Definition at line 1143 of file snapmgr.c.

References NIL.

Referenced by PrepareTransaction().

{
    return (exportedSnapshots != NIL);
}


Variable Documentation