Header And Logo

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

Functions

timeline.c File Reference

#include "postgres.h"
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include "access/timeline.h"
#include "access/xlog_internal.h"
#include "access/xlogdefs.h"
#include "storage/fd.h"
Include dependency graph for timeline.c:

Go to the source code of this file.

Functions

void restoreTimeLineHistoryFiles (TimeLineID begin, TimeLineID end)
ListreadTimeLineHistory (TimeLineID targetTLI)
bool existsTimeLineHistory (TimeLineID probeTLI)
TimeLineID findNewestTimeLine (TimeLineID startTLI)
void writeTimeLineHistory (TimeLineID newTLI, TimeLineID parentTLI, XLogRecPtr switchpoint, char *reason)
void writeTimeLineHistoryFile (TimeLineID tli, char *content, int size)
bool tliInHistory (TimeLineID tli, List *expectedTLEs)
TimeLineID tliOfPointInHistory (XLogRecPtr ptr, List *history)
XLogRecPtr tliSwitchPoint (TimeLineID tli, List *history, TimeLineID *nextTLI)

Function Documentation

bool existsTimeLineHistory ( TimeLineID  probeTLI  ) 

Definition at line 206 of file timeline.c.

References AllocateFile(), ArchiveRecoveryRequested, ereport, errcode_for_file_access(), errmsg(), FATAL, FreeFile(), NULL, RestoreArchivedFile(), TLHistoryFileName, and TLHistoryFilePath.

Referenced by findNewestTimeLine(), readRecoveryCommandFile(), and WalRcvFetchTimeLineHistoryFiles().

{
    char        path[MAXPGPATH];
    char        histfname[MAXFNAMELEN];
    FILE       *fd;

    /* Timeline 1 does not have a history file, so no need to check */
    if (probeTLI == 1)
        return false;

    if (ArchiveRecoveryRequested)
    {
        TLHistoryFileName(histfname, probeTLI);
        RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
    }
    else
        TLHistoryFilePath(path, probeTLI);

    fd = AllocateFile(path, "r");
    if (fd != NULL)
    {
        FreeFile(fd);
        return true;
    }
    else
    {
        if (errno != ENOENT)
            ereport(FATAL,
                    (errcode_for_file_access(),
                     errmsg("could not open file \"%s\": %m", path)));
        return false;
    }
}

TimeLineID findNewestTimeLine ( TimeLineID  startTLI  ) 

Definition at line 248 of file timeline.c.

References existsTimeLineHistory().

Referenced by readRecoveryCommandFile(), rescanLatestTimeLine(), and StartupXLOG().

{
    TimeLineID  newestTLI;
    TimeLineID  probeTLI;

    /*
     * The algorithm is just to probe for the existence of timeline history
     * files.  XXX is it useful to allow gaps in the sequence?
     */
    newestTLI = startTLI;

    for (probeTLI = startTLI + 1;; probeTLI++)
    {
        if (existsTimeLineHistory(probeTLI))
        {
            newestTLI = probeTLI;       /* probeTLI exists */
        }
        else
        {
            /* doesn't exist, assume we're done */
            break;
        }
    }

    return newestTLI;
}

List* readTimeLineHistory ( TimeLineID  targetTLI  ) 

Definition at line 74 of file timeline.c.

References AllocateFile(), ArchiveRecoveryRequested, TimeLineHistoryEntry::begin, TimeLineHistoryEntry::end, ereport, errcode_for_file_access(), errhint(), errmsg(), FATAL, FreeFile(), KeepFileRestoredFromArchive(), lcons(), list_make1, NULL, palloc(), RestoreArchivedFile(), TLHistoryFileName, TLHistoryFilePath, and TimeLineHistoryEntry::tli.

Referenced by rescanLatestTimeLine(), StartReplication(), WaitForWALToBecomeAvailable(), XLogFileReadAnyTLI(), and XLogSend().

{
    List       *result;
    char        path[MAXPGPATH];
    char        histfname[MAXFNAMELEN];
    char        fline[MAXPGPATH];
    FILE       *fd;
    TimeLineHistoryEntry *entry;
    TimeLineID  lasttli = 0;
    XLogRecPtr  prevend;
    bool        fromArchive = false;

    /* Timeline 1 does not have a history file, so no need to check */
    if (targetTLI == 1)
    {
        entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
        entry->tli = targetTLI;
        entry->begin = entry->end = InvalidXLogRecPtr;
        return list_make1(entry);
    }

    if (ArchiveRecoveryRequested)
    {
        TLHistoryFileName(histfname, targetTLI);
        fromArchive =
            RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
    }
    else
        TLHistoryFilePath(path, targetTLI);

    fd = AllocateFile(path, "r");
    if (fd == NULL)
    {
        if (errno != ENOENT)
            ereport(FATAL,
                    (errcode_for_file_access(),
                     errmsg("could not open file \"%s\": %m", path)));
        /* Not there, so assume no parents */
        entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
        entry->tli = targetTLI;
        entry->begin = entry->end = InvalidXLogRecPtr;
        return list_make1(entry);
    }

    result = NIL;

    /*
     * Parse the file...
     */
    prevend = InvalidXLogRecPtr;
    while (fgets(fline, sizeof(fline), fd) != NULL)
    {
        /* skip leading whitespace and check for # comment */
        char       *ptr;
        TimeLineID  tli;
        uint32      switchpoint_hi;
        uint32      switchpoint_lo;
        int         nfields;

        for (ptr = fline; *ptr; ptr++)
        {
            if (!isspace((unsigned char) *ptr))
                break;
        }
        if (*ptr == '\0' || *ptr == '#')
            continue;

        nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);

        if (nfields < 1)
        {
            /* expect a numeric timeline ID as first field of line */
            ereport(FATAL,
                    (errmsg("syntax error in history file: %s", fline),
                     errhint("Expected a numeric timeline ID.")));
        }
        if (nfields != 3)
            ereport(FATAL,
                    (errmsg("syntax error in history file: %s", fline),
                     errhint("Expected an XLOG switchpoint location.")));

        if (result && tli <= lasttli)
            ereport(FATAL,
                    (errmsg("invalid data in history file: %s", fline),
                   errhint("Timeline IDs must be in increasing sequence.")));

        lasttli = tli;

        entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
        entry->tli = tli;
        entry->begin = prevend;
        entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
        prevend = entry->end;

        /* Build list with newest item first */
        result = lcons(entry, result);

        /* we ignore the remainder of each line */
    }

    FreeFile(fd);

    if (result && targetTLI <= lasttli)
        ereport(FATAL,
                (errmsg("invalid data in history file \"%s\"", path),
            errhint("Timeline IDs must be less than child timeline's ID.")));

    /*
     * Create one more entry for the "tip" of the timeline, which has no
     * entry in the history file.
     */
    entry = (TimeLineHistoryEntry *) palloc(sizeof(TimeLineHistoryEntry));
    entry->tli = targetTLI;
    entry->begin = prevend;
    entry->end = InvalidXLogRecPtr;

    result = lcons(entry, result);

    /*
     * If the history file was fetched from archive, save it in pg_xlog for
     * future reference.
     */
    if (fromArchive)
        KeepFileRestoredFromArchive(path, histfname);

    return result;
}

void restoreTimeLineHistoryFiles ( TimeLineID  begin,
TimeLineID  end 
)

Definition at line 48 of file timeline.c.

References KeepFileRestoredFromArchive(), RestoreArchivedFile(), and TLHistoryFileName.

Referenced by rescanLatestTimeLine(), and StartupXLOG().

{
    char        path[MAXPGPATH];
    char        histfname[MAXFNAMELEN];
    TimeLineID tli;

    for (tli = begin; tli < end; tli++)
    {
        if (tli == 1)
            continue;

        TLHistoryFileName(histfname, tli);
        if (RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false))
            KeepFileRestoredFromArchive(path, histfname);
    }
}

bool tliInHistory ( TimeLineID  tli,
List expectedTLEs 
)

Definition at line 531 of file timeline.c.

References lfirst.

Referenced by checkTimeLineSwitch(), and ReadRecord().

{
    ListCell *cell;

    foreach(cell, expectedTLEs)
    {
        if (((TimeLineHistoryEntry *) lfirst(cell))->tli == tli)
            return true;
    }

    return false;
}

TimeLineID tliOfPointInHistory ( XLogRecPtr  ptr,
List history 
)

Definition at line 549 of file timeline.c.

References TimeLineHistoryEntry::begin, elog, TimeLineHistoryEntry::end, ERROR, lfirst, TimeLineHistoryEntry::tli, and XLogRecPtrIsInvalid.

Referenced by StartupXLOG(), and WaitForWALToBecomeAvailable().

{
    ListCell *cell;

    foreach(cell, history)
    {
        TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);
        if ((XLogRecPtrIsInvalid(tle->begin) || tle->begin <= ptr) &&
            (XLogRecPtrIsInvalid(tle->end) || ptr < tle->end))
        {
            /* found it */
            return tle->tli;
        }
    }

    /* shouldn't happen. */
    elog(ERROR, "timeline history was not contiguous");
    return 0;   /* keep compiler quiet */
}

XLogRecPtr tliSwitchPoint ( TimeLineID  tli,
List history,
TimeLineID nextTLI 
)

Definition at line 576 of file timeline.c.

References TimeLineHistoryEntry::end, ereport, errmsg(), ERROR, lfirst, and TimeLineHistoryEntry::tli.

Referenced by StartReplication(), StartupXLOG(), and XLogSend().

{
    ListCell   *cell;

    if (nextTLI)
        *nextTLI = 0;
    foreach (cell, history)
    {
        TimeLineHistoryEntry *tle = (TimeLineHistoryEntry *) lfirst(cell);

        if (tle->tli == tli)
            return tle->end;
        if (nextTLI)
            *nextTLI = tle->tli;
    }

    ereport(ERROR,
            (errmsg("requested timeline %u is not in this server's history",
                    tli)));
    return InvalidXLogRecPtr; /* keep compiler quiet */
}

void writeTimeLineHistory ( TimeLineID  newTLI,
TimeLineID  parentTLI,
XLogRecPtr  switchpoint,
char *  reason 
)

Definition at line 288 of file timeline.c.

References ArchiveRecoveryRequested, Assert, CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), ERROR, link(), MAXPGPATH, OpenTransientFile(), pg_fsync(), read, RestoreArchivedFile(), snprintf(), TLHistoryFileName, TLHistoryFilePath, unlink(), write, XLogArchiveNotify(), and XLOGDIR.

Referenced by StartupXLOG().

{
    char        path[MAXPGPATH];
    char        tmppath[MAXPGPATH];
    char        histfname[MAXFNAMELEN];
    char        buffer[BLCKSZ];
    int         srcfd;
    int         fd;
    int         nbytes;

    Assert(newTLI > parentTLI); /* else bad selection of newTLI */

    /*
     * Write into a temp file name.
     */
    snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());

    unlink(tmppath);

    /* do not use get_sync_bit() here --- want to fsync only at end of fill */
    fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
                           S_IRUSR | S_IWUSR);
    if (fd < 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not create file \"%s\": %m", tmppath)));

    /*
     * If a history file exists for the parent, copy it verbatim
     */
    if (ArchiveRecoveryRequested)
    {
        TLHistoryFileName(histfname, parentTLI);
        RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
    }
    else
        TLHistoryFilePath(path, parentTLI);

    srcfd = OpenTransientFile(path, O_RDONLY, 0);
    if (srcfd < 0)
    {
        if (errno != ENOENT)
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not open file \"%s\": %m", path)));
        /* Not there, so assume parent has no parents */
    }
    else
    {
        for (;;)
        {
            errno = 0;
            nbytes = (int) read(srcfd, buffer, sizeof(buffer));
            if (nbytes < 0 || errno != 0)
                ereport(ERROR,
                        (errcode_for_file_access(),
                         errmsg("could not read file \"%s\": %m", path)));
            if (nbytes == 0)
                break;
            errno = 0;
            if ((int) write(fd, buffer, nbytes) != nbytes)
            {
                int         save_errno = errno;

                /*
                 * If we fail to make the file, delete it to release disk
                 * space
                 */
                unlink(tmppath);

                /*
                 * if write didn't set errno, assume problem is no disk space
                 */
                errno = save_errno ? save_errno : ENOSPC;

                ereport(ERROR,
                        (errcode_for_file_access(),
                     errmsg("could not write to file \"%s\": %m", tmppath)));
            }
        }
        CloseTransientFile(srcfd);
    }

    /*
     * Append one line with the details of this timeline split.
     *
     * If we did have a parent file, insert an extra newline just in case the
     * parent file failed to end with one.
     */
    snprintf(buffer, sizeof(buffer),
             "%s%u\t%X/%X\t%s\n",
             (srcfd < 0) ? "" : "\n",
             parentTLI,
             (uint32) (switchpoint >> 32), (uint32) (switchpoint),
             reason);

    nbytes = strlen(buffer);
    errno = 0;
    if ((int) write(fd, buffer, nbytes) != nbytes)
    {
        int         save_errno = errno;

        /*
         * If we fail to make the file, delete it to release disk space
         */
        unlink(tmppath);
        /* if write didn't set errno, assume problem is no disk space */
        errno = save_errno ? save_errno : ENOSPC;

        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not write to file \"%s\": %m", tmppath)));
    }

    if (pg_fsync(fd) != 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not fsync file \"%s\": %m", tmppath)));

    if (CloseTransientFile(fd))
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not close file \"%s\": %m", tmppath)));


    /*
     * Now move the completed history file into place with its final name.
     */
    TLHistoryFilePath(path, newTLI);

    /*
     * Prefer link() to rename() here just to be really sure that we don't
     * overwrite an existing file.  However, there shouldn't be one, so
     * rename() is an acceptable substitute except for the truly paranoid.
     */
#if HAVE_WORKING_LINK
    if (link(tmppath, path) < 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not link file \"%s\" to \"%s\": %m",
                        tmppath, path)));
    unlink(tmppath);
#else
    if (rename(tmppath, path) < 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not rename file \"%s\" to \"%s\": %m",
                        tmppath, path)));
#endif

    /* The history file can be archived immediately. */
    TLHistoryFileName(histfname, newTLI);
    XLogArchiveNotify(histfname);
}

void writeTimeLineHistoryFile ( TimeLineID  tli,
char *  content,
int  size 
)

Definition at line 452 of file timeline.c.

References CloseTransientFile(), ereport, errcode_for_file_access(), errmsg(), ERROR, link(), MAXPGPATH, OpenTransientFile(), pg_fsync(), snprintf(), TLHistoryFilePath, unlink(), write, and XLOGDIR.

Referenced by ReceiveXlogStream(), and WalRcvFetchTimeLineHistoryFiles().

{
    char        path[MAXPGPATH];
    char        tmppath[MAXPGPATH];
    int         fd;

    /*
     * Write into a temp file name.
     */
    snprintf(tmppath, MAXPGPATH, XLOGDIR "/xlogtemp.%d", (int) getpid());

    unlink(tmppath);

    /* do not use get_sync_bit() here --- want to fsync only at end of fill */
    fd = OpenTransientFile(tmppath, O_RDWR | O_CREAT | O_EXCL,
                           S_IRUSR | S_IWUSR);
    if (fd < 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not create file \"%s\": %m", tmppath)));

    errno = 0;
    if ((int) write(fd, content, size) != size)
    {
        int         save_errno = errno;

        /*
         * If we fail to make the file, delete it to release disk space
         */
        unlink(tmppath);
        /* if write didn't set errno, assume problem is no disk space */
        errno = save_errno ? save_errno : ENOSPC;

        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not write to file \"%s\": %m", tmppath)));
    }

    if (pg_fsync(fd) != 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not fsync file \"%s\": %m", tmppath)));

    if (CloseTransientFile(fd))
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not close file \"%s\": %m", tmppath)));


    /*
     * Now move the completed history file into place with its final name.
     */
    TLHistoryFilePath(path, tli);

    /*
     * Prefer link() to rename() here just to be really sure that we don't
     * overwrite an existing logfile.  However, there shouldn't be one, so
     * rename() is an acceptable substitute except for the truly paranoid.
     */
#if HAVE_WORKING_LINK
    if (link(tmppath, path) < 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not link file \"%s\" to \"%s\": %m",
                        tmppath, path)));
    unlink(tmppath);
#else
    if (rename(tmppath, path) < 0)
        ereport(ERROR,
                (errcode_for_file_access(),
                 errmsg("could not rename file \"%s\" to \"%s\": %m",
                        tmppath, path)));
#endif
}