*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.63 2008/10/01 19:51:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.64 2008/12/27 17:39:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* Allocate a second read pointer to serve as the mark.
* We know it must have index 1, so needn't store that.
*/
- int ptrn;
+ int ptrno;
- ptrn = tuplestore_alloc_read_pointer(tuplestorestate,
- node->eflags);
- Assert(ptrn == 1);
+ ptrno = tuplestore_alloc_read_pointer(tuplestorestate,
+ node->eflags);
+ Assert(ptrno == 1);
}
node->tuplestorestate = tuplestorestate;
}
EXEC_FLAG_BACKWARD |
EXEC_FLAG_MARK));
+ /*
+ * Tuplestore's interpretation of the flag bits is subtly different from
+ * the general executor meaning: it doesn't think BACKWARD necessarily
+ * means "backwards all the way to start". If told to support BACKWARD we
+ * must include REWIND in the tuplestore eflags, else tuplestore_trim
+ * might throw away too much.
+ */
+ if (eflags & EXEC_FLAG_BACKWARD)
+ matstate->eflags |= EXEC_FLAG_REWIND;
+
matstate->eof_underlying = false;
matstate->tuplestorestate = NULL;
* copy the active read pointer to the mark.
*/
tuplestore_copy_read_pointer(node->tuplestorestate, 0, 1);
+
+ /*
+ * since we may have advanced the mark, try to truncate the tuplestore.
+ */
+ tuplestore_trim(node->tuplestorestate);
}
/* ----------------------------------------------------------------
* in a format that allows either forward or backward scan. Otherwise, only
* forward scan is allowed. A request for backward scan must be made before
* putting any tuples into the tuplestore. Rewind is normally allowed but
- * can be turned off via tuplestore_set_eflags; turning off both backward
- * scan and rewind for all read pointers enables truncation of the tuplestore
- * at the oldest read point for minimal memory usage.
+ * can be turned off via tuplestore_set_eflags; turning off rewind for all
+ * read pointers enables truncation of the tuplestore at the oldest read point
+ * for minimal memory usage. (The caller must explicitly call tuplestore_trim
+ * at appropriate times for truncation to actually happen.)
*
* Note: in TSS_WRITEFILE state, the temp file's seek position is the
* current write position, and the write-position variables in the tuplestore
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.43 2008/10/28 15:51:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.44 2008/12/27 17:39:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int eflags; /* capability flags (OR of pointers' flags) */
bool backward; /* store extra length words in file? */
bool interXact; /* keep open through transactions? */
+ bool truncated; /* tuplestore_trim has removed tuples? */
long availMem; /* remaining memory available, in bytes */
BufFile *myfile; /* underlying file, or NULL if none */
int maxKBytes);
static void tuplestore_puttuple_common(Tuplestorestate *state, void *tuple);
static void dumptuples(Tuplestorestate *state);
-static void tuplestore_trim(Tuplestorestate *state);
static unsigned int getlen(Tuplestorestate *state, bool eofOK);
static void *copytup_heap(Tuplestorestate *state, void *tup);
static void writetup_heap(Tuplestorestate *state, void *tup);
state->status = TSS_INMEM;
state->eflags = eflags;
state->interXact = interXact;
+ state->truncated = false;
state->availMem = maxKBytes * 1024L;
state->myfile = NULL;
* EXEC_FLAG_BACKWARD need backward fetch
* If tuplestore_set_eflags is not called, REWIND is allowed, and BACKWARD
* is set per "randomAccess" in the tuplestore_begin_xxx call.
+ *
+ * NOTE: setting BACKWARD without REWIND means the pointer can read backwards,
+ * but not further than the truncation point (the furthest-back read pointer
+ * position at the time of the last tuplestore_trim call).
*/
void
tuplestore_set_eflags(Tuplestorestate *state, int eflags)
}
}
state->status = TSS_INMEM;
+ state->truncated = false;
state->memtupcount = 0;
readptr = state->readptrs;
for (i = 0; i < state->readptrcount; readptr++, i++)
return NULL;
if (readptr->current < state->memtupcount)
{
- /*
- * We have another tuple, so return it. Note: in
- * principle we could try tuplestore_trim() here after
- * advancing current, but this would cost cycles with
- * little chance of success, so we don't bother.
- */
+ /* We have another tuple, so return it */
return state->memtuples[readptr->current++];
}
readptr->eof_reached = true;
{
/*
* if all tuples are fetched already then we return last
- * tuple, else - tuple before last returned.
+ * tuple, else tuple before last returned.
*/
if (readptr->eof_reached)
{
else
{
if (readptr->current <= 0)
+ {
+ Assert(!state->truncated);
return NULL;
+ }
readptr->current--; /* last returned tuple */
}
if (readptr->current <= 0)
+ {
+ Assert(!state->truncated);
return NULL;
+ }
return state->memtuples[readptr->current - 1];
}
break;
* Backward.
*
* if all tuples are fetched already then we return last tuple,
- * else - tuple before last returned.
+ * else tuple before last returned.
*
* Back up to fetch previously-returned tuple's ending length
* word. If seek fails, assume we are at start of file.
{
/* even a failed backwards fetch gets you out of eof state */
readptr->eof_reached = false;
+ Assert(!state->truncated);
return NULL;
}
tuplen = getlen(state, false);
-(long) (tuplen + sizeof(unsigned int)),
SEEK_CUR) != 0)
elog(ERROR, "bogus tuple length in backward scan");
+ Assert(!state->truncated);
return NULL;
}
tuplen = getlen(state, false);
* tuplestore_advance - exported function to adjust position without fetching
*
* We could optimize this case to avoid palloc/pfree overhead, but for the
- * moment it doesn't seem worthwhile.
+ * moment it doesn't seem worthwhile. (XXX this probably needs to be
+ * reconsidered given the needs of window functions.)
*/
bool
tuplestore_advance(Tuplestorestate *state, bool forward)
TSReadPointer *readptr = &state->readptrs[state->activeptr];
Assert(readptr->eflags & EXEC_FLAG_REWIND);
+ Assert(!state->truncated);
switch (state->status)
{
switch (state->status)
{
case TSS_INMEM:
- /* We might be able to truncate the tuplestore */
- tuplestore_trim(state);
- break;
case TSS_WRITEFILE:
+ /* no work */
break;
case TSS_READFILE:
/*
/*
* tuplestore_trim - remove all no-longer-needed tuples
+ *
+ * Calling this function authorizes the tuplestore to delete all tuples
+ * before the oldest read pointer, if no read pointer is marked as requiring
+ * REWIND capability.
+ *
+ * Note: this is obviously safe if no pointer has BACKWARD capability either.
+ * If a pointer is marked as BACKWARD but not REWIND capable, it means that
+ * the pointer can be moved backward but not before the oldest other read
+ * pointer.
*/
-static void
+void
tuplestore_trim(Tuplestorestate *state)
{
int oldest;
int i;
/*
- * We can truncate the tuplestore if neither backward scan nor
- * rewind capability are required by any read pointer.
+ * Truncation is disallowed if any read pointer requires rewind capability.
*/
- if (state->eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_REWIND))
+ if (state->eflags & EXEC_FLAG_REWIND)
return;
/*
if (!state->readptrs[i].eof_reached)
state->readptrs[i].current -= nremove;
}
+
+ /* mark tuplestore as truncated (used for Assert crosschecks only) */
+ state->truncated = true;
}