Header And Logo

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

Data Structures | Defines | Typedefs | Functions

logtape.c File Reference

#include "postgres.h"
#include "storage/buffile.h"
#include "utils/logtape.h"
Include dependency graph for logtape.c:

Go to the source code of this file.

Data Structures

struct  IndirectBlock
struct  LogicalTape
struct  LogicalTapeSet

Defines

#define BLOCKS_PER_INDIR_BLOCK   ((int) (BLCKSZ / sizeof(long)))

Typedefs

typedef struct IndirectBlock IndirectBlock
typedef struct LogicalTape LogicalTape

Functions

static void ltsWriteBlock (LogicalTapeSet *lts, long blocknum, void *buffer)
static void ltsReadBlock (LogicalTapeSet *lts, long blocknum, void *buffer)
static long ltsGetFreeBlock (LogicalTapeSet *lts)
static void ltsReleaseBlock (LogicalTapeSet *lts, long blocknum)
static void ltsRecordBlockNum (LogicalTapeSet *lts, IndirectBlock *indirect, long blocknum)
static long ltsRewindIndirectBlock (LogicalTapeSet *lts, IndirectBlock *indirect, bool freezing)
static long ltsRewindFrozenIndirectBlock (LogicalTapeSet *lts, IndirectBlock *indirect)
static long ltsRecallNextBlockNum (LogicalTapeSet *lts, IndirectBlock *indirect, bool frozen)
static long ltsRecallPrevBlockNum (LogicalTapeSet *lts, IndirectBlock *indirect)
static void ltsDumpBuffer (LogicalTapeSet *lts, LogicalTape *lt)
static int freeBlocks_cmp (const void *a, const void *b)
LogicalTapeSetLogicalTapeSetCreate (int ntapes)
void LogicalTapeSetClose (LogicalTapeSet *lts)
void LogicalTapeSetForgetFreeSpace (LogicalTapeSet *lts)
void LogicalTapeWrite (LogicalTapeSet *lts, int tapenum, void *ptr, size_t size)
void LogicalTapeRewind (LogicalTapeSet *lts, int tapenum, bool forWrite)
size_t LogicalTapeRead (LogicalTapeSet *lts, int tapenum, void *ptr, size_t size)
void LogicalTapeFreeze (LogicalTapeSet *lts, int tapenum)
bool LogicalTapeBackspace (LogicalTapeSet *lts, int tapenum, size_t size)
bool LogicalTapeSeek (LogicalTapeSet *lts, int tapenum, long blocknum, int offset)
void LogicalTapeTell (LogicalTapeSet *lts, int tapenum, long *blocknum, int *offset)
long LogicalTapeSetBlocks (LogicalTapeSet *lts)

Define Documentation

#define BLOCKS_PER_INDIR_BLOCK   ((int) (BLCKSZ / sizeof(long)))

Typedef Documentation

typedef struct IndirectBlock IndirectBlock
typedef struct LogicalTape LogicalTape

Function Documentation

static int freeBlocks_cmp ( const void *  a,
const void *  b 
) [static]

Definition at line 240 of file logtape.c.

Referenced by ltsGetFreeBlock().

{
    long        ablk = *((const long *) a);
    long        bblk = *((const long *) b);

    /* can't just subtract because long might be wider than int */
    if (ablk < bblk)
        return 1;
    if (ablk > bblk)
        return -1;
    return 0;
}

bool LogicalTapeBackspace ( LogicalTapeSet lts,
int  tapenum,
size_t  size 
)

Definition at line 876 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::curBlockNumber, elog, ERROR, LogicalTape::frozen, LogicalTape::indirect, ltsReadBlock(), ltsRecallPrevBlockNum(), LogicalTape::nbytes, LogicalTape::pos, and LogicalTapeSet::tapes.

Referenced by tuplesort_gettuple_common().

{
    LogicalTape *lt;
    long        nblocks;
    int         newpos;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];
    Assert(lt->frozen);

    /*
     * Easy case for seek within current block.
     */
    if (size <= (size_t) lt->pos)
    {
        lt->pos -= (int) size;
        return true;
    }

    /*
     * Not-so-easy case.  Figure out whether it's possible at all.
     */
    size -= (size_t) lt->pos;   /* part within this block */
    nblocks = size / BLCKSZ;
    size = size % BLCKSZ;
    if (size)
    {
        nblocks++;
        newpos = (int) (BLCKSZ - size);
    }
    else
        newpos = 0;
    if (nblocks > lt->curBlockNumber)
        return false;           /* a seek too far... */

    /*
     * OK, we need to back up nblocks blocks.  This implementation would be
     * pretty inefficient for long seeks, but we really aren't expecting that
     * (a seek over one tuple is typical).
     */
    while (nblocks-- > 0)
    {
        long        datablocknum = ltsRecallPrevBlockNum(lts, lt->indirect);

        if (datablocknum == -1L)
            elog(ERROR, "unexpected end of tape");
        lt->curBlockNumber--;
        if (nblocks == 0)
        {
            ltsReadBlock(lts, datablocknum, (void *) lt->buffer);
            lt->nbytes = BLCKSZ;
        }
    }
    lt->pos = newpos;
    return true;
}

void LogicalTapeFreeze ( LogicalTapeSet lts,
int  tapenum 
)

Definition at line 833 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::curBlockNumber, LogicalTape::dirty, LogicalTape::frozen, LogicalTape::indirect, LogicalTape::lastBlockBytes, ltsDumpBuffer(), ltsReadBlock(), ltsRewindIndirectBlock(), LogicalTape::nbytes, LogicalTape::numFullBlocks, LogicalTape::pos, LogicalTapeSet::tapes, and LogicalTape::writing.

Referenced by mergeruns().

{
    LogicalTape *lt;
    long        datablocknum;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];
    Assert(lt->writing);

    /*
     * Completion of a write phase.  Flush last partial data block, flush any
     * partial indirect blocks, rewind for nondestructive read.
     */
    if (lt->dirty)
        ltsDumpBuffer(lts, lt);
    lt->lastBlockBytes = lt->nbytes;
    lt->writing = false;
    lt->frozen = true;
    datablocknum = ltsRewindIndirectBlock(lts, lt->indirect, true);
    /* Read the first block, or reset if tape is empty */
    lt->curBlockNumber = 0L;
    lt->pos = 0;
    lt->nbytes = 0;
    if (datablocknum != -1L)
    {
        ltsReadBlock(lts, datablocknum, (void *) lt->buffer);
        lt->nbytes = (lt->curBlockNumber < lt->numFullBlocks) ?
            BLCKSZ : lt->lastBlockBytes;
    }
}

size_t LogicalTapeRead ( LogicalTapeSet lts,
int  tapenum,
void *  ptr,
size_t  size 
)

Definition at line 773 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::curBlockNumber, LogicalTape::frozen, LogicalTape::indirect, LogicalTape::lastBlockBytes, ltsReadBlock(), ltsRecallNextBlockNum(), ltsReleaseBlock(), LogicalTape::nbytes, LogicalTape::numFullBlocks, LogicalTape::pos, LogicalTapeSet::tapes, and LogicalTape::writing.

Referenced by getlen().

{
    LogicalTape *lt;
    size_t      nread = 0;
    size_t      nthistime;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];
    Assert(!lt->writing);

    while (size > 0)
    {
        if (lt->pos >= lt->nbytes)
        {
            /* Try to load more data into buffer. */
            long        datablocknum = ltsRecallNextBlockNum(lts, lt->indirect,
                                                             lt->frozen);

            if (datablocknum == -1L)
                break;          /* EOF */
            lt->curBlockNumber++;
            lt->pos = 0;
            ltsReadBlock(lts, datablocknum, (void *) lt->buffer);
            if (!lt->frozen)
                ltsReleaseBlock(lts, datablocknum);
            lt->nbytes = (lt->curBlockNumber < lt->numFullBlocks) ?
                BLCKSZ : lt->lastBlockBytes;
            if (lt->nbytes <= 0)
                break;          /* EOF (possible here?) */
        }

        nthistime = lt->nbytes - lt->pos;
        if (nthistime > size)
            nthistime = size;
        Assert(nthistime > 0);

        memcpy(ptr, lt->buffer + lt->pos, nthistime);

        lt->pos += nthistime;
        ptr = (void *) ((char *) ptr + nthistime);
        size -= nthistime;
        nread += nthistime;
    }

    return nread;
}

void LogicalTapeRewind ( LogicalTapeSet lts,
int  tapenum,
bool  forWrite 
)

Definition at line 687 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::curBlockNumber, LogicalTape::dirty, LogicalTape::frozen, LogicalTape::indirect, LogicalTape::lastBlockBytes, ltsDumpBuffer(), ltsReadBlock(), ltsReleaseBlock(), ltsRewindFrozenIndirectBlock(), ltsRewindIndirectBlock(), LogicalTape::nbytes, IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, LogicalTape::numFullBlocks, pfree(), LogicalTape::pos, LogicalTapeSet::tapes, and LogicalTape::writing.

Referenced by mergeruns(), and tuplesort_rescan().

{
    LogicalTape *lt;
    long        datablocknum;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];

    if (!forWrite)
    {
        if (lt->writing)
        {
            /*
             * Completion of a write phase.  Flush last partial data block,
             * flush any partial indirect blocks, rewind for normal
             * (destructive) read.
             */
            if (lt->dirty)
                ltsDumpBuffer(lts, lt);
            lt->lastBlockBytes = lt->nbytes;
            lt->writing = false;
            datablocknum = ltsRewindIndirectBlock(lts, lt->indirect, false);
        }
        else
        {
            /*
             * This is only OK if tape is frozen; we rewind for (another) read
             * pass.
             */
            Assert(lt->frozen);
            datablocknum = ltsRewindFrozenIndirectBlock(lts, lt->indirect);
        }
        /* Read the first block, or reset if tape is empty */
        lt->curBlockNumber = 0L;
        lt->pos = 0;
        lt->nbytes = 0;
        if (datablocknum != -1L)
        {
            ltsReadBlock(lts, datablocknum, (void *) lt->buffer);
            if (!lt->frozen)
                ltsReleaseBlock(lts, datablocknum);
            lt->nbytes = (lt->curBlockNumber < lt->numFullBlocks) ?
                BLCKSZ : lt->lastBlockBytes;
        }
    }
    else
    {
        /*
         * Completion of a read phase.  Rewind and prepare for write.
         *
         * NOTE: we assume the caller has read the tape to the end; otherwise
         * untouched data and indirect blocks will not have been freed. We
         * could add more code to free any unread blocks, but in current usage
         * of this module it'd be useless code.
         */
        IndirectBlock *ib,
                   *nextib;

        Assert(!lt->writing && !lt->frozen);
        /* Must truncate the indirect-block hierarchy down to one level. */
        if (lt->indirect)
        {
            for (ib = lt->indirect->nextup; ib != NULL; ib = nextib)
            {
                nextib = ib->nextup;
                pfree(ib);
            }
            lt->indirect->nextSlot = 0;
            lt->indirect->nextup = NULL;
        }
        lt->writing = true;
        lt->dirty = false;
        lt->numFullBlocks = 0L;
        lt->lastBlockBytes = 0;
        lt->curBlockNumber = 0L;
        lt->pos = 0;
        lt->nbytes = 0;
    }
}

bool LogicalTapeSeek ( LogicalTapeSet lts,
int  tapenum,
long  blocknum,
int  offset 
)

Definition at line 942 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::curBlockNumber, elog, ERROR, LogicalTape::frozen, LogicalTape::indirect, LogicalTape::lastBlockBytes, ltsReadBlock(), ltsRecallNextBlockNum(), ltsRecallPrevBlockNum(), LogicalTape::nbytes, LogicalTape::numFullBlocks, LogicalTape::pos, and LogicalTapeSet::tapes.

Referenced by tuplesort_restorepos().

{
    LogicalTape *lt;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];
    Assert(lt->frozen);
    Assert(offset >= 0 && offset <= BLCKSZ);

    /*
     * Easy case for seek within current block.
     */
    if (blocknum == lt->curBlockNumber && offset <= lt->nbytes)
    {
        lt->pos = offset;
        return true;
    }

    /*
     * Not-so-easy case.  Figure out whether it's possible at all.
     */
    if (blocknum < 0 || blocknum > lt->numFullBlocks ||
        (blocknum == lt->numFullBlocks && offset > lt->lastBlockBytes))
        return false;

    /*
     * OK, advance or back up to the target block.  This implementation would
     * be pretty inefficient for long seeks, but we really aren't expecting
     * that (a seek over one tuple is typical).
     */
    while (lt->curBlockNumber > blocknum)
    {
        long        datablocknum = ltsRecallPrevBlockNum(lts, lt->indirect);

        if (datablocknum == -1L)
            elog(ERROR, "unexpected end of tape");
        if (--lt->curBlockNumber == blocknum)
            ltsReadBlock(lts, datablocknum, (void *) lt->buffer);
    }
    while (lt->curBlockNumber < blocknum)
    {
        long        datablocknum = ltsRecallNextBlockNum(lts, lt->indirect,
                                                         lt->frozen);

        if (datablocknum == -1L)
            elog(ERROR, "unexpected end of tape");
        if (++lt->curBlockNumber == blocknum)
            ltsReadBlock(lts, datablocknum, (void *) lt->buffer);
    }
    lt->nbytes = (lt->curBlockNumber < lt->numFullBlocks) ?
        BLCKSZ : lt->lastBlockBytes;
    lt->pos = offset;
    return true;
}

long LogicalTapeSetBlocks ( LogicalTapeSet lts  ) 

Definition at line 1020 of file logtape.c.

References LogicalTapeSet::nFileBlocks.

Referenced by tuplesort_end(), and tuplesort_get_stats().

{
    return lts->nFileBlocks;
}

void LogicalTapeSetClose ( LogicalTapeSet lts  ) 

Definition at line 567 of file logtape.c.

References LogicalTape::buffer, BufFileClose(), LogicalTapeSet::freeBlocks, i, LogicalTape::indirect, IndirectBlock::nextup, LogicalTapeSet::nTapes, NULL, LogicalTapeSet::pfile, pfree(), and LogicalTapeSet::tapes.

Referenced by tuplesort_end().

{
    LogicalTape *lt;
    IndirectBlock *ib,
               *nextib;
    int         i;

    BufFileClose(lts->pfile);
    for (i = 0; i < lts->nTapes; i++)
    {
        lt = &lts->tapes[i];
        for (ib = lt->indirect; ib != NULL; ib = nextib)
        {
            nextib = ib->nextup;
            pfree(ib);
        }
        if (lt->buffer)
            pfree(lt->buffer);
    }
    pfree(lts->freeBlocks);
    pfree(lts);
}

LogicalTapeSet* LogicalTapeSetCreate ( int  ntapes  ) 

Definition at line 518 of file logtape.c.

References Assert, LogicalTapeSet::blocksSorted, LogicalTape::buffer, BufFileCreateTemp(), LogicalTape::curBlockNumber, LogicalTape::dirty, LogicalTapeSet::forgetFreeSpace, LogicalTapeSet::freeBlocks, LogicalTapeSet::freeBlocksLen, LogicalTape::frozen, i, LogicalTape::indirect, LogicalTape::lastBlockBytes, LogicalTape::nbytes, LogicalTapeSet::nFileBlocks, LogicalTapeSet::nFreeBlocks, LogicalTapeSet::nTapes, LogicalTape::numFullBlocks, palloc(), LogicalTapeSet::pfile, LogicalTape::pos, LogicalTapeSet::tapes, and LogicalTape::writing.

Referenced by inittapes().

{
    LogicalTapeSet *lts;
    LogicalTape *lt;
    int         i;

    /*
     * Create top-level struct including per-tape LogicalTape structs. First
     * LogicalTape struct is already counted in sizeof(LogicalTapeSet).
     */
    Assert(ntapes > 0);
    lts = (LogicalTapeSet *) palloc(sizeof(LogicalTapeSet) +
                                    (ntapes - 1) *sizeof(LogicalTape));
    lts->pfile = BufFileCreateTemp(false);
    lts->nFileBlocks = 0L;
    lts->forgetFreeSpace = false;
    lts->blocksSorted = true;   /* a zero-length array is sorted ... */
    lts->freeBlocksLen = 32;    /* reasonable initial guess */
    lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
    lts->nFreeBlocks = 0;
    lts->nTapes = ntapes;

    /*
     * Initialize per-tape structs.  Note we allocate the I/O buffer and
     * first-level indirect block for a tape only when it is first actually
     * written to.  This avoids wasting memory space when tuplesort.c
     * overestimates the number of tapes needed.
     */
    for (i = 0; i < ntapes; i++)
    {
        lt = &lts->tapes[i];
        lt->indirect = NULL;
        lt->writing = true;
        lt->frozen = false;
        lt->dirty = false;
        lt->numFullBlocks = 0L;
        lt->lastBlockBytes = 0;
        lt->buffer = NULL;
        lt->curBlockNumber = 0L;
        lt->pos = 0;
        lt->nbytes = 0;
    }
    return lts;
}

void LogicalTapeSetForgetFreeSpace ( LogicalTapeSet lts  ) 

Definition at line 600 of file logtape.c.

References LogicalTapeSet::forgetFreeSpace.

Referenced by mergeruns().

{
    lts->forgetFreeSpace = true;
}

void LogicalTapeTell ( LogicalTapeSet lts,
int  tapenum,
long *  blocknum,
int *  offset 
)

Definition at line 1005 of file logtape.c.

References Assert, LogicalTape::curBlockNumber, LogicalTape::pos, and LogicalTapeSet::tapes.

Referenced by tuplesort_markpos().

{
    LogicalTape *lt;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];
    *blocknum = lt->curBlockNumber;
    *offset = lt->pos;
}

void LogicalTapeWrite ( LogicalTapeSet lts,
int  tapenum,
void *  ptr,
size_t  size 
)

Definition at line 626 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::curBlockNumber, LogicalTape::dirty, elog, ERROR, LogicalTape::indirect, ltsDumpBuffer(), LogicalTape::nbytes, IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, LogicalTape::numFullBlocks, palloc(), LogicalTape::pos, LogicalTapeSet::tapes, and LogicalTape::writing.

Referenced by markrunend(), writetup_cluster(), writetup_datum(), writetup_heap(), and writetup_index().

{
    LogicalTape *lt;
    size_t      nthistime;

    Assert(tapenum >= 0 && tapenum < lts->nTapes);
    lt = &lts->tapes[tapenum];
    Assert(lt->writing);

    /* Allocate data buffer and first indirect block on first write */
    if (lt->buffer == NULL)
        lt->buffer = (char *) palloc(BLCKSZ);
    if (lt->indirect == NULL)
    {
        lt->indirect = (IndirectBlock *) palloc(sizeof(IndirectBlock));
        lt->indirect->nextSlot = 0;
        lt->indirect->nextup = NULL;
    }

    while (size > 0)
    {
        if (lt->pos >= BLCKSZ)
        {
            /* Buffer full, dump it out */
            if (lt->dirty)
                ltsDumpBuffer(lts, lt);
            else
            {
                /* Hmm, went directly from reading to writing? */
                elog(ERROR, "invalid logtape state: should be dirty");
            }
            lt->numFullBlocks++;
            lt->curBlockNumber++;
            lt->pos = 0;
            lt->nbytes = 0;
        }

        nthistime = BLCKSZ - lt->pos;
        if (nthistime > size)
            nthistime = size;
        Assert(nthistime > 0);

        memcpy(lt->buffer + lt->pos, ptr, nthistime);

        lt->dirty = true;
        lt->pos += nthistime;
        if (lt->nbytes < lt->pos)
            lt->nbytes = lt->pos;
        ptr = (void *) ((char *) ptr + nthistime);
        size -= nthistime;
    }
}

static void ltsDumpBuffer ( LogicalTapeSet lts,
LogicalTape lt 
) [static]

Definition at line 609 of file logtape.c.

References Assert, LogicalTape::buffer, LogicalTape::dirty, LogicalTape::indirect, ltsGetFreeBlock(), ltsRecordBlockNum(), and ltsWriteBlock().

Referenced by LogicalTapeFreeze(), LogicalTapeRewind(), and LogicalTapeWrite().

{
    long        datablock = ltsGetFreeBlock(lts);

    Assert(lt->dirty);
    ltsWriteBlock(lts, datablock, (void *) lt->buffer);
    ltsRecordBlockNum(lts, lt->indirect, datablock);
    lt->dirty = false;
    /* Caller must do other state update as needed */
}

static long ltsGetFreeBlock ( LogicalTapeSet lts  )  [static]

Definition at line 260 of file logtape.c.

References LogicalTapeSet::blocksSorted, LogicalTapeSet::freeBlocks, freeBlocks_cmp(), LogicalTapeSet::nFileBlocks, LogicalTapeSet::nFreeBlocks, and qsort.

Referenced by ltsDumpBuffer(), ltsRecordBlockNum(), and ltsRewindIndirectBlock().

{
    /*
     * If there are multiple free blocks, we select the one appearing last in
     * freeBlocks[] (after sorting the array if needed).  If there are none,
     * assign the next block at the end of the file.
     */
    if (lts->nFreeBlocks > 0)
    {
        if (!lts->blocksSorted)
        {
            qsort((void *) lts->freeBlocks, lts->nFreeBlocks,
                  sizeof(long), freeBlocks_cmp);
            lts->blocksSorted = true;
        }
        return lts->freeBlocks[--lts->nFreeBlocks];
    }
    else
        return lts->nFileBlocks++;
}

static void ltsReadBlock ( LogicalTapeSet lts,
long  blocknum,
void *  buffer 
) [static]

Definition at line 225 of file logtape.c.

References BufFileRead(), BufFileSeekBlock(), ereport, errcode_for_file_access(), errmsg(), ERROR, and LogicalTapeSet::pfile.

Referenced by LogicalTapeBackspace(), LogicalTapeFreeze(), LogicalTapeRead(), LogicalTapeRewind(), LogicalTapeSeek(), ltsRecallNextBlockNum(), ltsRecallPrevBlockNum(), ltsRewindFrozenIndirectBlock(), and ltsRewindIndirectBlock().

{
    if (BufFileSeekBlock(lts->pfile, blocknum) != 0 ||
        BufFileRead(lts->pfile, buffer, BLCKSZ) != BLCKSZ)
        ereport(ERROR,
        /* XXX is it okay to assume errno is correct? */
                (errcode_for_file_access(),
                 errmsg("could not read block %ld of temporary file: %m",
                        blocknum)));
}

static long ltsRecallNextBlockNum ( LogicalTapeSet lts,
IndirectBlock indirect,
bool  frozen 
) [static]

Definition at line 445 of file logtape.c.

References BLOCKS_PER_INDIR_BLOCK, ltsReadBlock(), ltsReleaseBlock(), IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, and IndirectBlock::ptrs.

Referenced by LogicalTapeRead(), and LogicalTapeSeek().

{
    /* Handle case of never-written-to tape */
    if (indirect == NULL)
        return -1L;

    if (indirect->nextSlot >= BLOCKS_PER_INDIR_BLOCK ||
        indirect->ptrs[indirect->nextSlot] == -1L)
    {
        long        indirblock;

        if (indirect->nextup == NULL)
            return -1L;         /* nothing left at this level */
        indirblock = ltsRecallNextBlockNum(lts, indirect->nextup, frozen);
        if (indirblock == -1L)
            return -1L;         /* nothing left at this level */
        ltsReadBlock(lts, indirblock, (void *) indirect->ptrs);
        if (!frozen)
            ltsReleaseBlock(lts, indirblock);
        indirect->nextSlot = 0;
    }
    if (indirect->ptrs[indirect->nextSlot] == -1L)
        return -1L;
    return indirect->ptrs[indirect->nextSlot++];
}

static long ltsRecallPrevBlockNum ( LogicalTapeSet lts,
IndirectBlock indirect 
) [static]

Definition at line 483 of file logtape.c.

References BLOCKS_PER_INDIR_BLOCK, ltsReadBlock(), IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, and IndirectBlock::ptrs.

Referenced by LogicalTapeBackspace(), and LogicalTapeSeek().

{
    /* Handle case of never-written-to tape */
    if (indirect == NULL)
        return -1L;

    if (indirect->nextSlot <= 1)
    {
        long        indirblock;

        if (indirect->nextup == NULL)
            return -1L;         /* nothing left at this level */
        indirblock = ltsRecallPrevBlockNum(lts, indirect->nextup);
        if (indirblock == -1L)
            return -1L;         /* nothing left at this level */
        ltsReadBlock(lts, indirblock, (void *) indirect->ptrs);

        /*
         * The previous block would only have been written out if full, so we
         * need not search it for a -1 sentinel.
         */
        indirect->nextSlot = BLOCKS_PER_INDIR_BLOCK + 1;
    }
    indirect->nextSlot--;
    return indirect->ptrs[indirect->nextSlot - 1];
}

static void ltsRecordBlockNum ( LogicalTapeSet lts,
IndirectBlock indirect,
long  blocknum 
) [static]

Definition at line 325 of file logtape.c.

References BLOCKS_PER_INDIR_BLOCK, ltsGetFreeBlock(), ltsWriteBlock(), IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, palloc(), and IndirectBlock::ptrs.

Referenced by ltsDumpBuffer(), and ltsRewindIndirectBlock().

{
    if (indirect->nextSlot >= BLOCKS_PER_INDIR_BLOCK)
    {
        /*
         * This indirect block is full, so dump it out and recursively save
         * its address in the next indirection level.  Create a new
         * indirection level if there wasn't one before.
         */
        long        indirblock = ltsGetFreeBlock(lts);

        ltsWriteBlock(lts, indirblock, (void *) indirect->ptrs);
        if (indirect->nextup == NULL)
        {
            indirect->nextup = (IndirectBlock *) palloc(sizeof(IndirectBlock));
            indirect->nextup->nextSlot = 0;
            indirect->nextup->nextup = NULL;
        }
        ltsRecordBlockNum(lts, indirect->nextup, indirblock);

        /*
         * Reset to fill another indirect block at this level.
         */
        indirect->nextSlot = 0;
    }
    indirect->ptrs[indirect->nextSlot++] = blocknum;
}

static void ltsReleaseBlock ( LogicalTapeSet lts,
long  blocknum 
) [static]

Definition at line 285 of file logtape.c.

References LogicalTapeSet::blocksSorted, LogicalTapeSet::forgetFreeSpace, LogicalTapeSet::freeBlocks, LogicalTapeSet::freeBlocksLen, LogicalTapeSet::nFreeBlocks, and repalloc().

Referenced by LogicalTapeRead(), LogicalTapeRewind(), ltsRecallNextBlockNum(), and ltsRewindIndirectBlock().

{
    int         ndx;

    /*
     * Do nothing if we're no longer interested in remembering free space.
     */
    if (lts->forgetFreeSpace)
        return;

    /*
     * Enlarge freeBlocks array if full.
     */
    if (lts->nFreeBlocks >= lts->freeBlocksLen)
    {
        lts->freeBlocksLen *= 2;
        lts->freeBlocks = (long *) repalloc(lts->freeBlocks,
                                          lts->freeBlocksLen * sizeof(long));
    }

    /*
     * Add blocknum to array, and mark the array unsorted if it's no longer in
     * decreasing order.
     */
    ndx = lts->nFreeBlocks++;
    lts->freeBlocks[ndx] = blocknum;
    if (ndx > 0 && lts->freeBlocks[ndx - 1] < blocknum)
        lts->blocksSorted = false;
}

static long ltsRewindFrozenIndirectBlock ( LogicalTapeSet lts,
IndirectBlock indirect 
) [static]

Definition at line 409 of file logtape.c.

References Assert, ltsReadBlock(), IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, and IndirectBlock::ptrs.

Referenced by LogicalTapeRewind().

{
    /* Handle case of never-written-to tape */
    if (indirect == NULL)
        return -1L;

    /*
     * If block is not topmost, recurse to obtain address of first block in
     * this hierarchy level.  Read that one in.
     */
    if (indirect->nextup != NULL)
    {
        long        indirblock;

        indirblock = ltsRewindFrozenIndirectBlock(lts, indirect->nextup);
        Assert(indirblock != -1L);
        ltsReadBlock(lts, indirblock, (void *) indirect->ptrs);
    }

    /*
     * Reset my next-block pointer, and then fetch a block number if any.
     */
    indirect->nextSlot = 0;
    if (indirect->ptrs[0] == -1L)
        return -1L;
    return indirect->ptrs[indirect->nextSlot++];
}

static long ltsRewindIndirectBlock ( LogicalTapeSet lts,
IndirectBlock indirect,
bool  freezing 
) [static]

Definition at line 365 of file logtape.c.

References Assert, BLOCKS_PER_INDIR_BLOCK, ltsGetFreeBlock(), ltsReadBlock(), ltsRecordBlockNum(), ltsReleaseBlock(), ltsWriteBlock(), IndirectBlock::nextSlot, IndirectBlock::nextup, NULL, and IndirectBlock::ptrs.

Referenced by LogicalTapeFreeze(), and LogicalTapeRewind().

{
    /* Handle case of never-written-to tape */
    if (indirect == NULL)
        return -1L;

    /* Insert sentinel if block is not full */
    if (indirect->nextSlot < BLOCKS_PER_INDIR_BLOCK)
        indirect->ptrs[indirect->nextSlot] = -1L;

    /*
     * If block is not topmost, write it out, and recurse to obtain address of
     * first block in this hierarchy level.  Read that one in.
     */
    if (indirect->nextup != NULL)
    {
        long        indirblock = ltsGetFreeBlock(lts);

        ltsWriteBlock(lts, indirblock, (void *) indirect->ptrs);
        ltsRecordBlockNum(lts, indirect->nextup, indirblock);
        indirblock = ltsRewindIndirectBlock(lts, indirect->nextup, freezing);
        Assert(indirblock != -1L);
        ltsReadBlock(lts, indirblock, (void *) indirect->ptrs);
        if (!freezing)
            ltsReleaseBlock(lts, indirblock);
    }

    /*
     * Reset my next-block pointer, and then fetch a block number if any.
     */
    indirect->nextSlot = 0;
    if (indirect->ptrs[0] == -1L)
        return -1L;
    return indirect->ptrs[indirect->nextSlot++];
}

static void ltsWriteBlock ( LogicalTapeSet lts,
long  blocknum,
void *  buffer 
) [static]

Definition at line 206 of file logtape.c.

References BufFileSeekBlock(), BufFileWrite(), ereport, errcode_for_file_access(), errhint(), errmsg(), ERROR, and LogicalTapeSet::pfile.

Referenced by ltsDumpBuffer(), ltsRecordBlockNum(), and ltsRewindIndirectBlock().

{
    if (BufFileSeekBlock(lts->pfile, blocknum) != 0 ||
        BufFileWrite(lts->pfile, buffer, BLCKSZ) != BLCKSZ)
        ereport(ERROR,
        /* XXX is it okay to assume errno is correct? */
                (errcode_for_file_access(),
                 errmsg("could not write block %ld of temporary file: %m",
                        blocknum),
                 errhint("Perhaps out of disk space?")));
}