Skip to content

Commit 38e9348

Browse files
committed
Make a couple of small changes to the tuplestore API, for the benefit of the
upcoming window-functions patch. First, tuplestore_trim is now an exported function that must be explicitly invoked by callers at appropriate times, rather than something that tuplestore tries to do behind the scenes. Second, a read pointer that is marked as allowing backward scan no longer prevents truncation. This means that a read pointer marked as having BACKWARD but not REWIND capability can only safely read backwards as far as the oldest other read pointer. (The expected use pattern for this involves having another read pointer that serves as the truncation fencepost.)
1 parent c8b69ed commit 38e9348

File tree

3 files changed

+65
-27
lines changed

3 files changed

+65
-27
lines changed

src/backend/executor/nodeMaterial.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.63 2008/10/01 19:51:49 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.64 2008/12/27 17:39:00 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -66,11 +66,11 @@ ExecMaterial(MaterialState *node)
6666
* Allocate a second read pointer to serve as the mark.
6767
* We know it must have index 1, so needn't store that.
6868
*/
69-
int ptrn;
69+
int ptrno;
7070

71-
ptrn = tuplestore_alloc_read_pointer(tuplestorestate,
72-
node->eflags);
73-
Assert(ptrn == 1);
71+
ptrno = tuplestore_alloc_read_pointer(tuplestorestate,
72+
node->eflags);
73+
Assert(ptrno == 1);
7474
}
7575
node->tuplestorestate = tuplestorestate;
7676
}
@@ -182,6 +182,16 @@ ExecInitMaterial(Material *node, EState *estate, int eflags)
182182
EXEC_FLAG_BACKWARD |
183183
EXEC_FLAG_MARK));
184184

185+
/*
186+
* Tuplestore's interpretation of the flag bits is subtly different from
187+
* the general executor meaning: it doesn't think BACKWARD necessarily
188+
* means "backwards all the way to start". If told to support BACKWARD we
189+
* must include REWIND in the tuplestore eflags, else tuplestore_trim
190+
* might throw away too much.
191+
*/
192+
if (eflags & EXEC_FLAG_BACKWARD)
193+
matstate->eflags |= EXEC_FLAG_REWIND;
194+
185195
matstate->eof_underlying = false;
186196
matstate->tuplestorestate = NULL;
187197

@@ -278,6 +288,11 @@ ExecMaterialMarkPos(MaterialState *node)
278288
* copy the active read pointer to the mark.
279289
*/
280290
tuplestore_copy_read_pointer(node->tuplestorestate, 0, 1);
291+
292+
/*
293+
* since we may have advanced the mark, try to truncate the tuplestore.
294+
*/
295+
tuplestore_trim(node->tuplestorestate);
281296
}
282297

283298
/* ----------------------------------------------------------------

src/backend/utils/sort/tuplestore.c

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
* in a format that allows either forward or backward scan. Otherwise, only
3131
* forward scan is allowed. A request for backward scan must be made before
3232
* putting any tuples into the tuplestore. Rewind is normally allowed but
33-
* can be turned off via tuplestore_set_eflags; turning off both backward
34-
* scan and rewind for all read pointers enables truncation of the tuplestore
35-
* at the oldest read point for minimal memory usage.
33+
* can be turned off via tuplestore_set_eflags; turning off rewind for all
34+
* read pointers enables truncation of the tuplestore at the oldest read point
35+
* for minimal memory usage. (The caller must explicitly call tuplestore_trim
36+
* at appropriate times for truncation to actually happen.)
3637
*
3738
* Note: in TSS_WRITEFILE state, the temp file's seek position is the
3839
* current write position, and the write-position variables in the tuplestore
@@ -46,7 +47,7 @@
4647
* Portions Copyright (c) 1994, Regents of the University of California
4748
*
4849
* IDENTIFICATION
49-
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.43 2008/10/28 15:51:03 tgl Exp $
50+
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.44 2008/12/27 17:39:00 tgl Exp $
5051
*
5152
*-------------------------------------------------------------------------
5253
*/
@@ -101,6 +102,7 @@ struct Tuplestorestate
101102
int eflags; /* capability flags (OR of pointers' flags) */
102103
bool backward; /* store extra length words in file? */
103104
bool interXact; /* keep open through transactions? */
105+
bool truncated; /* tuplestore_trim has removed tuples? */
104106
long availMem; /* remaining memory available, in bytes */
105107
BufFile *myfile; /* underlying file, or NULL if none */
106108

@@ -220,7 +222,6 @@ static Tuplestorestate *tuplestore_begin_common(int eflags,
220222
int maxKBytes);
221223
static void tuplestore_puttuple_common(Tuplestorestate *state, void *tuple);
222224
static void dumptuples(Tuplestorestate *state);
223-
static void tuplestore_trim(Tuplestorestate *state);
224225
static unsigned int getlen(Tuplestorestate *state, bool eofOK);
225226
static void *copytup_heap(Tuplestorestate *state, void *tup);
226227
static void writetup_heap(Tuplestorestate *state, void *tup);
@@ -242,6 +243,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes)
242243
state->status = TSS_INMEM;
243244
state->eflags = eflags;
244245
state->interXact = interXact;
246+
state->truncated = false;
245247
state->availMem = maxKBytes * 1024L;
246248
state->myfile = NULL;
247249

@@ -319,6 +321,10 @@ tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
319321
* EXEC_FLAG_BACKWARD need backward fetch
320322
* If tuplestore_set_eflags is not called, REWIND is allowed, and BACKWARD
321323
* is set per "randomAccess" in the tuplestore_begin_xxx call.
324+
*
325+
* NOTE: setting BACKWARD without REWIND means the pointer can read backwards,
326+
* but not further than the truncation point (the furthest-back read pointer
327+
* position at the time of the last tuplestore_trim call).
322328
*/
323329
void
324330
tuplestore_set_eflags(Tuplestorestate *state, int eflags)
@@ -397,6 +403,7 @@ tuplestore_clear(Tuplestorestate *state)
397403
}
398404
}
399405
state->status = TSS_INMEM;
406+
state->truncated = false;
400407
state->memtupcount = 0;
401408
readptr = state->readptrs;
402409
for (i = 0; i < state->readptrcount; readptr++, i++)
@@ -723,12 +730,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
723730
return NULL;
724731
if (readptr->current < state->memtupcount)
725732
{
726-
/*
727-
* We have another tuple, so return it. Note: in
728-
* principle we could try tuplestore_trim() here after
729-
* advancing current, but this would cost cycles with
730-
* little chance of success, so we don't bother.
731-
*/
733+
/* We have another tuple, so return it */
732734
return state->memtuples[readptr->current++];
733735
}
734736
readptr->eof_reached = true;
@@ -738,7 +740,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
738740
{
739741
/*
740742
* if all tuples are fetched already then we return last
741-
* tuple, else - tuple before last returned.
743+
* tuple, else tuple before last returned.
742744
*/
743745
if (readptr->eof_reached)
744746
{
@@ -748,11 +750,17 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
748750
else
749751
{
750752
if (readptr->current <= 0)
753+
{
754+
Assert(!state->truncated);
751755
return NULL;
756+
}
752757
readptr->current--; /* last returned tuple */
753758
}
754759
if (readptr->current <= 0)
760+
{
761+
Assert(!state->truncated);
755762
return NULL;
763+
}
756764
return state->memtuples[readptr->current - 1];
757765
}
758766
break;
@@ -795,7 +803,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
795803
* Backward.
796804
*
797805
* if all tuples are fetched already then we return last tuple,
798-
* else - tuple before last returned.
806+
* else tuple before last returned.
799807
*
800808
* Back up to fetch previously-returned tuple's ending length
801809
* word. If seek fails, assume we are at start of file.
@@ -805,6 +813,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
805813
{
806814
/* even a failed backwards fetch gets you out of eof state */
807815
readptr->eof_reached = false;
816+
Assert(!state->truncated);
808817
return NULL;
809818
}
810819
tuplen = getlen(state, false);
@@ -833,6 +842,7 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
833842
-(long) (tuplen + sizeof(unsigned int)),
834843
SEEK_CUR) != 0)
835844
elog(ERROR, "bogus tuple length in backward scan");
845+
Assert(!state->truncated);
836846
return NULL;
837847
}
838848
tuplen = getlen(state, false);
@@ -887,7 +897,8 @@ tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
887897
* tuplestore_advance - exported function to adjust position without fetching
888898
*
889899
* We could optimize this case to avoid palloc/pfree overhead, but for the
890-
* moment it doesn't seem worthwhile.
900+
* moment it doesn't seem worthwhile. (XXX this probably needs to be
901+
* reconsidered given the needs of window functions.)
891902
*/
892903
bool
893904
tuplestore_advance(Tuplestorestate *state, bool forward)
@@ -948,6 +959,7 @@ tuplestore_rescan(Tuplestorestate *state)
948959
TSReadPointer *readptr = &state->readptrs[state->activeptr];
949960

950961
Assert(readptr->eflags & EXEC_FLAG_REWIND);
962+
Assert(!state->truncated);
951963

952964
switch (state->status)
953965
{
@@ -1006,10 +1018,8 @@ tuplestore_copy_read_pointer(Tuplestorestate *state,
10061018
switch (state->status)
10071019
{
10081020
case TSS_INMEM:
1009-
/* We might be able to truncate the tuplestore */
1010-
tuplestore_trim(state);
1011-
break;
10121021
case TSS_WRITEFILE:
1022+
/* no work */
10131023
break;
10141024
case TSS_READFILE:
10151025
/*
@@ -1053,19 +1063,27 @@ tuplestore_copy_read_pointer(Tuplestorestate *state,
10531063

10541064
/*
10551065
* tuplestore_trim - remove all no-longer-needed tuples
1066+
*
1067+
* Calling this function authorizes the tuplestore to delete all tuples
1068+
* before the oldest read pointer, if no read pointer is marked as requiring
1069+
* REWIND capability.
1070+
*
1071+
* Note: this is obviously safe if no pointer has BACKWARD capability either.
1072+
* If a pointer is marked as BACKWARD but not REWIND capable, it means that
1073+
* the pointer can be moved backward but not before the oldest other read
1074+
* pointer.
10561075
*/
1057-
static void
1076+
void
10581077
tuplestore_trim(Tuplestorestate *state)
10591078
{
10601079
int oldest;
10611080
int nremove;
10621081
int i;
10631082

10641083
/*
1065-
* We can truncate the tuplestore if neither backward scan nor
1066-
* rewind capability are required by any read pointer.
1084+
* Truncation is disallowed if any read pointer requires rewind capability.
10671085
*/
1068-
if (state->eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_REWIND))
1086+
if (state->eflags & EXEC_FLAG_REWIND)
10691087
return;
10701088

10711089
/*
@@ -1125,6 +1143,9 @@ tuplestore_trim(Tuplestorestate *state)
11251143
if (!state->readptrs[i].eof_reached)
11261144
state->readptrs[i].current -= nremove;
11271145
}
1146+
1147+
/* mark tuplestore as truncated (used for Assert crosschecks only) */
1148+
state->truncated = true;
11281149
}
11291150

11301151

src/include/utils/tuplestore.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
2525
* Portions Copyright (c) 1994, Regents of the University of California
2626
*
27-
* $PostgreSQL: pgsql/src/include/utils/tuplestore.h,v 1.25 2008/10/04 21:56:55 tgl Exp $
27+
* $PostgreSQL: pgsql/src/include/utils/tuplestore.h,v 1.26 2008/12/27 17:39:00 tgl Exp $
2828
*
2929
*-------------------------------------------------------------------------
3030
*/
@@ -66,6 +66,8 @@ extern void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr);
6666
extern void tuplestore_copy_read_pointer(Tuplestorestate *state,
6767
int srcptr, int destptr);
6868

69+
extern void tuplestore_trim(Tuplestorestate *state);
70+
6971
extern bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
7072
TupleTableSlot *slot);
7173
extern bool tuplestore_advance(Tuplestorestate *state, bool forward);

0 commit comments

Comments
 (0)