#include "postgres.h"
#include "access/htup_details.h"
#include "commands/tablespace.h"
#include "executor/executor.h"
#include "storage/buffile.h"
#include "utils/memutils.h"
#include "utils/resowner.h"
Go to the source code of this file.
Definition at line 179 of file tuplestore.c.
Referenced by tuplestore_puttuple().
Definition at line 184 of file tuplestore.c.
Referenced by grow_memtuples(), tuplestore_clear(), tuplestore_trim(), and writetup_heap().
Definition at line 182 of file tuplestore.c.
Referenced by grow_memtuples(), and tuplestore_puttuple_common().
Definition at line 181 of file tuplestore.c.
Referenced by tuplestore_gettuple().
Definition at line 183 of file tuplestore.c.
Referenced by copytup_heap(), grow_memtuples(), readtup_heap(), tuplestore_begin_common(), tuplestore_puttupleslot(), and tuplestore_putvalues().
Definition at line 180 of file tuplestore.c.
Referenced by dumptuples(), and tuplestore_puttuple_common().
enum TupStoreStatus |
Definition at line 69 of file tuplestore.c.
{ TSS_INMEM, /* Tuples still fit in memory */ TSS_WRITEFILE, /* Writing to temp file */ TSS_READFILE /* Reading from temp file */ } TupStoreStatus;
static void * copytup_heap | ( | Tuplestorestate * | state, | |
void * | tup | |||
) | [static] |
Definition at line 1371 of file tuplestore.c.
References GetMemoryChunkSpace(), minimal_tuple_from_heap_tuple(), and USEMEM.
{ MinimalTuple tuple; tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup); USEMEM(state, GetMemoryChunkSpace(tuple)); return (void *) tuple; }
static void dumptuples | ( | Tuplestorestate * | state | ) | [static] |
Definition at line 1090 of file tuplestore.c.
References BufFileTell(), TSReadPointer::current, TSReadPointer::eof_reached, TSReadPointer::file, i, Tuplestorestate::memtupcount, Tuplestorestate::memtupdeleted, Tuplestorestate::memtuples, Tuplestorestate::myfile, TSReadPointer::offset, Tuplestorestate::readptrcount, Tuplestorestate::readptrs, and WRITETUP.
Referenced by tuplestore_puttuple_common().
{ int i; for (i = state->memtupdeleted;; i++) { TSReadPointer *readptr = state->readptrs; int j; for (j = 0; j < state->readptrcount; readptr++, j++) { if (i == readptr->current && !readptr->eof_reached) BufFileTell(state->myfile, &readptr->file, &readptr->offset); } if (i >= state->memtupcount) break; WRITETUP(state, state->memtuples[i]); } state->memtupdeleted = 0; state->memtupcount = 0; }
static unsigned int getlen | ( | Tuplestorestate * | state, | |
bool | eofOK | |||
) | [static] |
Definition at line 1344 of file tuplestore.c.
References BufFileRead(), elog, ERROR, and Tuplestorestate::myfile.
Referenced by tuplestore_gettuple().
static bool grow_memtuples | ( | Tuplestorestate * | state | ) | [static] |
Definition at line 548 of file tuplestore.c.
References Tuplestorestate::allowedMem, Tuplestorestate::availMem, elog, ERROR, FREEMEM, GetMemoryChunkSpace(), Tuplestorestate::growmemtuples, LACKMEM, MaxAllocSize, Tuplestorestate::memtuples, Tuplestorestate::memtupsize, repalloc(), and USEMEM.
Referenced by tuplestore_puttuple_common().
{ int newmemtupsize; int memtupsize = state->memtupsize; long memNowUsed = state->allowedMem - state->availMem; /* Forget it if we've already maxed out memtuples, per comment above */ if (!state->growmemtuples) return false; /* Select new value of memtupsize */ if (memNowUsed <= state->availMem) { /* * It is surely safe to double memtupsize if we've used no more than * half of allowedMem. * * Note: it might seem that we need to worry about memtupsize * 2 * overflowing an int, but the MaxAllocSize clamp applied below * ensures the existing memtupsize can't be large enough for that. */ newmemtupsize = memtupsize * 2; } else { /* * This will be the last increment of memtupsize. Abandon doubling * strategy and instead increase as much as we safely can. * * To stay within allowedMem, we can't increase memtupsize by more * than availMem / sizeof(void *) elements. In practice, we want * to increase it by considerably less, because we need to leave some * space for the tuples to which the new array slots will refer. We * assume the new tuples will be about the same size as the tuples * we've already seen, and thus we can extrapolate from the space * consumption so far to estimate an appropriate new size for the * memtuples array. The optimal value might be higher or lower than * this estimate, but it's hard to know that in advance. * * This calculation is safe against enlarging the array so much that * LACKMEM becomes true, because the memory currently used includes * the present array; thus, there would be enough allowedMem for the * new array elements even if no other memory were currently used. * * We do the arithmetic in float8, because otherwise the product of * memtupsize and allowedMem could overflow. (A little algebra shows * that grow_ratio must be less than 2 here, so we are not risking * integer overflow this way.) Any inaccuracy in the result should be * insignificant; but even if we computed a completely insane result, * the checks below will prevent anything really bad from happening. */ double grow_ratio; grow_ratio = (double) state->allowedMem / (double) memNowUsed; newmemtupsize = (int) (memtupsize * grow_ratio); /* We won't make any further enlargement attempts */ state->growmemtuples = false; } /* Must enlarge array by at least one element, else report failure */ if (newmemtupsize <= memtupsize) goto noalloc; /* * On a 64-bit machine, allowedMem could be more than MaxAllocSize. Clamp * to ensure our request won't be rejected by palloc. */ if ((Size) newmemtupsize >= MaxAllocSize / sizeof(void *)) { newmemtupsize = (int) (MaxAllocSize / sizeof(void *)); state->growmemtuples = false; /* can't grow any more */ } /* * We need to be sure that we do not cause LACKMEM to become true, else * the space management algorithm will go nuts. The code above should * never generate a dangerous request, but to be safe, check explicitly * that the array growth fits within availMem. (We could still cause * LACKMEM if the memory chunk overhead associated with the memtuples * array were to increase. That shouldn't happen with any sane value of * allowedMem, because at any array size large enough to risk LACKMEM, * palloc would be treating both old and new arrays as separate chunks. * But we'll check LACKMEM explicitly below just in case.) */ if (state->availMem < (long) ((newmemtupsize - memtupsize) * sizeof(void *))) goto noalloc; /* OK, do it */ FREEMEM(state, GetMemoryChunkSpace(state->memtuples)); state->memtupsize = newmemtupsize; state->memtuples = (void **) repalloc(state->memtuples, state->memtupsize * sizeof(void *)); USEMEM(state, GetMemoryChunkSpace(state->memtuples)); if (LACKMEM(state)) elog(ERROR, "unexpected out-of-memory situation during sort"); return true; noalloc: /* If for any reason we didn't realloc, shut off future attempts */ state->growmemtuples = false; return false; }
static void * readtup_heap | ( | Tuplestorestate * | state, | |
unsigned int | len | |||
) | [static] |
Definition at line 1408 of file tuplestore.c.
References Tuplestorestate::backward, BufFileRead(), elog, ERROR, GetMemoryChunkSpace(), Tuplestorestate::myfile, palloc(), MinimalTupleData::t_len, and USEMEM.
{ unsigned int tupbodylen = len - sizeof(int); unsigned int tuplen = tupbodylen + MINIMAL_TUPLE_DATA_OFFSET; MinimalTuple tuple = (MinimalTuple) palloc(tuplen); char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET; USEMEM(state, GetMemoryChunkSpace(tuple)); /* read in the tuple proper */ tuple->t_len = tuplen; if (BufFileRead(state->myfile, (void *) tupbody, tupbodylen) != (size_t) tupbodylen) elog(ERROR, "unexpected end of data"); if (state->backward) /* need trailing length word? */ if (BufFileRead(state->myfile, (void *) &tuplen, sizeof(tuplen)) != sizeof(tuplen)) elog(ERROR, "unexpected end of data"); return (void *) tuple; }
bool tuplestore_advance | ( | Tuplestorestate * | state, | |
bool | forward | |||
) |
Definition at line 1063 of file tuplestore.c.
References pfree(), and tuplestore_gettuple().
Referenced by CteScanNext(), ExecMaterial(), PersistHoldablePortal(), window_gettupleslot(), and WinSetMarkPosition().
{ void *tuple; bool should_free; tuple = tuplestore_gettuple(state, forward, &should_free); if (tuple) { if (should_free) pfree(tuple); return true; } else { return false; } }
int tuplestore_alloc_read_pointer | ( | Tuplestorestate * | state, | |
int | eflags | |||
) |
Definition at line 371 of file tuplestore.c.
References TSReadPointer::eflags, Tuplestorestate::eflags, elog, ERROR, Tuplestorestate::memtupcount, Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::readptrsize, repalloc(), Tuplestorestate::status, and TSS_INMEM.
Referenced by begin_partition(), ExecInitCteScan(), and ExecMaterial().
{ /* Check for possible increase of requirements */ if (state->status != TSS_INMEM || state->memtupcount != 0) { if ((state->eflags | eflags) != state->eflags) elog(ERROR, "too late to require new tuplestore eflags"); } /* Make room for another read pointer if needed */ if (state->readptrcount >= state->readptrsize) { int newcnt = state->readptrsize * 2; state->readptrs = (TSReadPointer *) repalloc(state->readptrs, newcnt * sizeof(TSReadPointer)); state->readptrsize = newcnt; } /* And set it up */ state->readptrs[state->readptrcount] = state->readptrs[0]; state->readptrs[state->readptrcount].eflags = eflags; state->eflags |= eflags; return state->readptrcount++; }
bool tuplestore_ateof | ( | Tuplestorestate * | state | ) |
Definition at line 528 of file tuplestore.c.
References Tuplestorestate::activeptr, TSReadPointer::eof_reached, and Tuplestorestate::readptrs.
Referenced by CteScanNext(), and ExecMaterial().
{ return state->readptrs[state->activeptr].eof_reached; }
static Tuplestorestate * tuplestore_begin_common | ( | int | eflags, | |
bool | interXact, | |||
int | maxKBytes | |||
) | [static] |
Definition at line 249 of file tuplestore.c.
References Tuplestorestate::activeptr, Tuplestorestate::allowedMem, Tuplestorestate::availMem, Tuplestorestate::context, TSReadPointer::current, CurrentMemoryContext, CurrentResourceOwner, TSReadPointer::eflags, Tuplestorestate::eflags, TSReadPointer::eof_reached, GetMemoryChunkSpace(), Tuplestorestate::growmemtuples, Tuplestorestate::interXact, Tuplestorestate::memtupcount, Tuplestorestate::memtupdeleted, Tuplestorestate::memtuples, Tuplestorestate::memtupsize, Tuplestorestate::myfile, palloc(), palloc0(), Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::readptrsize, Tuplestorestate::resowner, Tuplestorestate::status, Tuplestorestate::truncated, and USEMEM.
Referenced by tuplestore_begin_heap().
{ Tuplestorestate *state; state = (Tuplestorestate *) palloc0(sizeof(Tuplestorestate)); state->status = TSS_INMEM; state->eflags = eflags; state->interXact = interXact; state->truncated = false; state->allowedMem = maxKBytes * 1024L; state->availMem = state->allowedMem; state->myfile = NULL; state->context = CurrentMemoryContext; state->resowner = CurrentResourceOwner; state->memtupdeleted = 0; state->memtupcount = 0; state->memtupsize = 1024; /* initial guess */ state->growmemtuples = true; state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *)); USEMEM(state, GetMemoryChunkSpace(state->memtuples)); state->activeptr = 0; state->readptrcount = 1; state->readptrsize = 8; /* arbitrary */ state->readptrs = (TSReadPointer *) palloc(state->readptrsize * sizeof(TSReadPointer)); state->readptrs[0].eflags = eflags; state->readptrs[0].eof_reached = false; state->readptrs[0].current = 0; return state; }
Tuplestorestate* tuplestore_begin_heap | ( | bool | randomAccess, | |
bool | interXact, | |||
int | maxKBytes | |||
) |
Definition at line 306 of file tuplestore.c.
References Tuplestorestate::copytup, EXEC_FLAG_BACKWARD, EXEC_FLAG_REWIND, Tuplestorestate::readtup, tuplestore_begin_common(), and Tuplestorestate::writetup.
Referenced by begin_partition(), connectby(), crosstab(), dblink_get_notify(), deflist_to_tuplestore(), each_worker(), exec_init_tuple_store(), ExecInitCteScan(), ExecInitRecursiveUnion(), ExecMakeTableFunctionResult(), ExecMaterial(), ExecRecursiveUnion(), fmgr_sql(), get_crosstab_tuplestore(), json_array_elements(), json_populate_recordset(), materializeQueryResult(), materializeResult(), pg_available_extension_versions(), pg_available_extensions(), pg_cursor(), pg_event_trigger_dropped_objects(), pg_extension_update_paths(), pg_prepared_statement(), pg_stat_get_wal_senders(), pg_stat_statements(), plperl_return_next(), PortalCreateHoldStore(), storeRow(), and xpath_table().
{ Tuplestorestate *state; int eflags; /* * This interpretation of the meaning of randomAccess is compatible with * the pre-8.3 behavior of tuplestores. */ eflags = randomAccess ? (EXEC_FLAG_BACKWARD | EXEC_FLAG_REWIND) : (EXEC_FLAG_REWIND); state = tuplestore_begin_common(eflags, interXact, maxKBytes); state->copytup = copytup_heap; state->writetup = writetup_heap; state->readtup = readtup_heap; return state; }
void tuplestore_clear | ( | Tuplestorestate * | state | ) |
Definition at line 406 of file tuplestore.c.
References BufFileClose(), TSReadPointer::current, TSReadPointer::eof_reached, FREEMEM, GetMemoryChunkSpace(), i, Tuplestorestate::memtupcount, Tuplestorestate::memtupdeleted, Tuplestorestate::memtuples, Tuplestorestate::myfile, pfree(), Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::status, and Tuplestorestate::truncated.
Referenced by ExecReScanCteScan(), ExecReScanRecursiveUnion(), and fmgr_sql().
{ int i; TSReadPointer *readptr; if (state->myfile) BufFileClose(state->myfile); state->myfile = NULL; if (state->memtuples) { for (i = state->memtupdeleted; i < state->memtupcount; i++) { FREEMEM(state, GetMemoryChunkSpace(state->memtuples[i])); pfree(state->memtuples[i]); } } state->status = TSS_INMEM; state->truncated = false; state->memtupdeleted = 0; state->memtupcount = 0; readptr = state->readptrs; for (i = 0; i < state->readptrcount; readptr++, i++) { readptr->eof_reached = false; readptr->current = 0; } }
void tuplestore_copy_read_pointer | ( | Tuplestorestate * | state, | |
int | srcptr, | |||
int | destptr | |||
) |
Definition at line 1150 of file tuplestore.c.
References Tuplestorestate::activeptr, Assert, BufFileSeek(), BufFileTell(), Tuplestorestate::eflags, TSReadPointer::eflags, elog, TSReadPointer::eof_reached, ERROR, TSReadPointer::file, i, Tuplestorestate::myfile, TSReadPointer::offset, Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::status, TSS_INMEM, TSS_READFILE, TSS_WRITEFILE, Tuplestorestate::writepos_file, and Tuplestorestate::writepos_offset.
Referenced by ExecMaterialMarkPos(), and ExecMaterialRestrPos().
{ TSReadPointer *sptr = &state->readptrs[srcptr]; TSReadPointer *dptr = &state->readptrs[destptr]; Assert(srcptr >= 0 && srcptr < state->readptrcount); Assert(destptr >= 0 && destptr < state->readptrcount); /* Assigning to self is a no-op */ if (srcptr == destptr) return; if (dptr->eflags != sptr->eflags) { /* Possible change of overall eflags, so copy and then recompute */ int eflags; int i; *dptr = *sptr; eflags = state->readptrs[0].eflags; for (i = 1; i < state->readptrcount; i++) eflags |= state->readptrs[i].eflags; state->eflags = eflags; } else *dptr = *sptr; switch (state->status) { case TSS_INMEM: case TSS_WRITEFILE: /* no work */ break; case TSS_READFILE: /* * This case is a bit tricky since the active read pointer's * position corresponds to the seek point, not what is in its * variables. Assigning to the active requires a seek, and * assigning from the active requires a tell, except when * eof_reached. */ if (destptr == state->activeptr) { if (dptr->eof_reached) { if (BufFileSeek(state->myfile, state->writepos_file, state->writepos_offset, SEEK_SET) != 0) elog(ERROR, "tuplestore seek failed"); } else { if (BufFileSeek(state->myfile, dptr->file, dptr->offset, SEEK_SET) != 0) elog(ERROR, "tuplestore seek failed"); } } else if (srcptr == state->activeptr) { if (!dptr->eof_reached) BufFileTell(state->myfile, &dptr->file, &dptr->offset); } break; default: elog(ERROR, "invalid tuplestore state"); break; } }
void tuplestore_end | ( | Tuplestorestate * | state | ) |
Definition at line 440 of file tuplestore.c.
References BufFileClose(), i, Tuplestorestate::memtupdeleted, Tuplestorestate::memtuples, Tuplestorestate::myfile, pfree(), and Tuplestorestate::readptrs.
Referenced by ExecEndCteScan(), ExecEndFunctionScan(), ExecEndMaterial(), ExecEndRecursiveUnion(), ExecMakeFunctionResult(), ExecRecursiveUnion(), ExecReScanFunctionScan(), ExecReScanMaterial(), PortalDrop(), release_partition(), ShutdownFuncExpr(), ShutdownSQLFunction(), and storeRow().
static void* tuplestore_gettuple | ( | Tuplestorestate * | state, | |
bool | forward, | |||
bool * | should_free | |||
) | [static] |
Definition at line 861 of file tuplestore.c.
References Tuplestorestate::activeptr, Assert, BufFileSeek(), BufFileTell(), TSReadPointer::current, TSReadPointer::eflags, elog, TSReadPointer::eof_reached, ERROR, EXEC_FLAG_BACKWARD, TSReadPointer::file, getlen(), Tuplestorestate::memtupcount, Tuplestorestate::memtupdeleted, Tuplestorestate::memtuples, Tuplestorestate::myfile, TSReadPointer::offset, Tuplestorestate::readptrs, READTUP, Tuplestorestate::status, Tuplestorestate::truncated, TSS_INMEM, TSS_READFILE, TSS_WRITEFILE, Tuplestorestate::writepos_file, and Tuplestorestate::writepos_offset.
Referenced by tuplestore_advance(), and tuplestore_gettupleslot().
{ TSReadPointer *readptr = &state->readptrs[state->activeptr]; unsigned int tuplen; void *tup; Assert(forward || (readptr->eflags & EXEC_FLAG_BACKWARD)); switch (state->status) { case TSS_INMEM: *should_free = false; if (forward) { if (readptr->eof_reached) return NULL; if (readptr->current < state->memtupcount) { /* We have another tuple, so return it */ return state->memtuples[readptr->current++]; } readptr->eof_reached = true; return NULL; } else { /* * if all tuples are fetched already then we return last * tuple, else tuple before last returned. */ if (readptr->eof_reached) { readptr->current = state->memtupcount; readptr->eof_reached = false; } else { if (readptr->current <= state->memtupdeleted) { Assert(!state->truncated); return NULL; } readptr->current--; /* last returned tuple */ } if (readptr->current <= state->memtupdeleted) { Assert(!state->truncated); return NULL; } return state->memtuples[readptr->current - 1]; } break; case TSS_WRITEFILE: /* Skip state change if we'll just return NULL */ if (readptr->eof_reached && forward) return NULL; /* * Switch from writing to reading. */ BufFileTell(state->myfile, &state->writepos_file, &state->writepos_offset); if (!readptr->eof_reached) if (BufFileSeek(state->myfile, readptr->file, readptr->offset, SEEK_SET) != 0) elog(ERROR, "tuplestore seek failed"); state->status = TSS_READFILE; /* FALL THRU into READFILE case */ case TSS_READFILE: *should_free = true; if (forward) { if ((tuplen = getlen(state, true)) != 0) { tup = READTUP(state, tuplen); return tup; } else { readptr->eof_reached = true; return NULL; } } /* * Backward. * * if all tuples are fetched already then we return last tuple, * 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. */ if (BufFileSeek(state->myfile, 0, -(long) sizeof(unsigned int), SEEK_CUR) != 0) { /* even a failed backwards fetch gets you out of eof state */ readptr->eof_reached = false; Assert(!state->truncated); return NULL; } tuplen = getlen(state, false); if (readptr->eof_reached) { readptr->eof_reached = false; /* We will return the tuple returned before returning NULL */ } else { /* * Back up to get ending length word of tuple before it. */ if (BufFileSeek(state->myfile, 0, -(long) (tuplen + 2 * sizeof(unsigned int)), SEEK_CUR) != 0) { /* * If that fails, presumably the prev tuple is the first * in the file. Back up so that it becomes next to read * in forward direction (not obviously right, but that is * what in-memory case does). */ if (BufFileSeek(state->myfile, 0, -(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); } /* * Now we have the length of the prior tuple, back up and read it. * Note: READTUP expects we are positioned after the initial * length word of the tuple, so back up to that point. */ if (BufFileSeek(state->myfile, 0, -(long) tuplen, SEEK_CUR) != 0) elog(ERROR, "bogus tuple length in backward scan"); tup = READTUP(state, tuplen); return tup; default: elog(ERROR, "invalid tuplestore state"); return NULL; /* keep compiler quiet */ } }
bool tuplestore_gettupleslot | ( | Tuplestorestate * | state, | |
bool | forward, | |||
bool | copy, | |||
TupleTableSlot * | slot | |||
) |
Definition at line 1030 of file tuplestore.c.
References ExecClearTuple(), ExecStoreMinimalTuple(), heap_copy_minimal_tuple(), and tuplestore_gettuple().
Referenced by CteScanNext(), ExecMakeFunctionResult(), ExecMaterial(), ExecWindowAgg(), fmgr_sql(), FunctionNext(), RunFromStore(), window_gettupleslot(), and WorkTableScanNext().
{ MinimalTuple tuple; bool should_free; tuple = (MinimalTuple) tuplestore_gettuple(state, forward, &should_free); if (tuple) { if (copy && !should_free) { tuple = heap_copy_minimal_tuple(tuple); should_free = true; } ExecStoreMinimalTuple(tuple, slot, should_free); return true; } else { ExecClearTuple(slot); return false; } }
bool tuplestore_in_memory | ( | Tuplestorestate * | state | ) |
Definition at line 1333 of file tuplestore.c.
References Tuplestorestate::status, and TSS_INMEM.
Referenced by spool_tuples().
void tuplestore_puttuple | ( | Tuplestorestate * | state, | |
HeapTuple | tuple | |||
) |
Definition at line 692 of file tuplestore.c.
References Tuplestorestate::context, COPYTUP, MemoryContextSwitchTo(), and tuplestore_puttuple_common().
Referenced by build_tuplestore_recursively(), crosstab(), each_object_field_end(), elements_array_element_end(), exec_stmt_return_next(), exec_stmt_return_query(), ExecMakeTableFunctionResult(), get_crosstab_tuplestore(), materializeQueryResult(), materializeResult(), plperl_return_next(), populate_recordset_object_end(), storeRow(), and xpath_table().
{ MemoryContext oldcxt = MemoryContextSwitchTo(state->context); /* * Copy the tuple. (Must do this even in WRITEFILE case. Note that * COPYTUP includes USEMEM, so we needn't do that here.) */ tuple = COPYTUP(state, tuple); tuplestore_puttuple_common(state, (void *) tuple); MemoryContextSwitchTo(oldcxt); }
static void tuplestore_puttuple_common | ( | Tuplestorestate * | state, | |
void * | tuple | |||
) | [static] |
Definition at line 727 of file tuplestore.c.
References Tuplestorestate::activeptr, Assert, Tuplestorestate::backward, BufFileCreateTemp(), BufFileSeek(), BufFileTell(), TSReadPointer::current, CurrentResourceOwner, dumptuples(), Tuplestorestate::eflags, elog, TSReadPointer::eof_reached, ERROR, TSReadPointer::file, grow_memtuples(), i, Tuplestorestate::interXact, LACKMEM, Tuplestorestate::memtupcount, Tuplestorestate::memtuples, Tuplestorestate::memtupsize, Tuplestorestate::myfile, TSReadPointer::offset, PrepareTempTablespaces(), Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::resowner, Tuplestorestate::status, TSS_INMEM, TSS_READFILE, TSS_WRITEFILE, Tuplestorestate::writepos_file, Tuplestorestate::writepos_offset, and WRITETUP.
Referenced by tuplestore_puttuple(), tuplestore_puttupleslot(), and tuplestore_putvalues().
{ TSReadPointer *readptr; int i; ResourceOwner oldowner; switch (state->status) { case TSS_INMEM: /* * Update read pointers as needed; see API spec above. */ readptr = state->readptrs; for (i = 0; i < state->readptrcount; readptr++, i++) { if (readptr->eof_reached && i != state->activeptr) { readptr->eof_reached = false; readptr->current = state->memtupcount; } } /* * Grow the array as needed. Note that we try to grow the array * when there is still one free slot remaining --- if we fail, * there'll still be room to store the incoming tuple, and then * we'll switch to tape-based operation. */ if (state->memtupcount >= state->memtupsize - 1) { (void) grow_memtuples(state); Assert(state->memtupcount < state->memtupsize); } /* Stash the tuple in the in-memory array */ state->memtuples[state->memtupcount++] = tuple; /* * Done if we still fit in available memory and have array slots. */ if (state->memtupcount < state->memtupsize && !LACKMEM(state)) return; /* * Nope; time to switch to tape-based operation. Make sure that * the temp file(s) are created in suitable temp tablespaces. */ PrepareTempTablespaces(); /* associate the file with the store's resource owner */ oldowner = CurrentResourceOwner; CurrentResourceOwner = state->resowner; state->myfile = BufFileCreateTemp(state->interXact); CurrentResourceOwner = oldowner; /* * Freeze the decision about whether trailing length words will be * used. We can't change this choice once data is on tape, even * though callers might drop the requirement. */ state->backward = (state->eflags & EXEC_FLAG_BACKWARD) != 0; state->status = TSS_WRITEFILE; dumptuples(state); break; case TSS_WRITEFILE: /* * Update read pointers as needed; see API spec above. Note: * BufFileTell is quite cheap, so not worth trying to avoid * multiple calls. */ readptr = state->readptrs; for (i = 0; i < state->readptrcount; readptr++, i++) { if (readptr->eof_reached && i != state->activeptr) { readptr->eof_reached = false; BufFileTell(state->myfile, &readptr->file, &readptr->offset); } } WRITETUP(state, tuple); break; case TSS_READFILE: /* * Switch from reading to writing. */ if (!state->readptrs[state->activeptr].eof_reached) BufFileTell(state->myfile, &state->readptrs[state->activeptr].file, &state->readptrs[state->activeptr].offset); if (BufFileSeek(state->myfile, state->writepos_file, state->writepos_offset, SEEK_SET) != 0) elog(ERROR, "tuplestore seek to EOF failed"); state->status = TSS_WRITEFILE; /* * Update read pointers as needed; see API spec above. */ readptr = state->readptrs; for (i = 0; i < state->readptrcount; readptr++, i++) { if (readptr->eof_reached && i != state->activeptr) { readptr->eof_reached = false; readptr->file = state->writepos_file; readptr->offset = state->writepos_offset; } } WRITETUP(state, tuple); break; default: elog(ERROR, "invalid tuplestore state"); break; } }
void tuplestore_puttupleslot | ( | Tuplestorestate * | state, | |
TupleTableSlot * | slot | |||
) |
Definition at line 670 of file tuplestore.c.
References Tuplestorestate::context, ExecCopySlotMinimalTuple(), GetMemoryChunkSpace(), MemoryContextSwitchTo(), tuplestore_puttuple_common(), and USEMEM.
Referenced by begin_partition(), CteScanNext(), ExecMaterial(), ExecRecursiveUnion(), spool_tuples(), sqlfunction_receive(), and tstoreReceiveSlot_notoast().
{ MinimalTuple tuple; MemoryContext oldcxt = MemoryContextSwitchTo(state->context); /* * Form a MinimalTuple in working memory */ tuple = ExecCopySlotMinimalTuple(slot); USEMEM(state, GetMemoryChunkSpace(tuple)); tuplestore_puttuple_common(state, (void *) tuple); MemoryContextSwitchTo(oldcxt); }
void tuplestore_putvalues | ( | Tuplestorestate * | state, | |
TupleDesc | tdesc, | |||
Datum * | values, | |||
bool * | isnull | |||
) |
Definition at line 712 of file tuplestore.c.
References Tuplestorestate::context, GetMemoryChunkSpace(), heap_form_minimal_tuple(), MemoryContextSwitchTo(), tuplestore_puttuple_common(), and USEMEM.
Referenced by dblink_get_notify(), deflist_to_tuplestore(), exec_stmt_return_next(), ExecMakeTableFunctionResult(), get_available_versions_for_extension(), pg_available_extensions(), pg_cursor(), pg_event_trigger_dropped_objects(), pg_extension_update_paths(), pg_prepared_statement(), pg_stat_get_wal_senders(), pg_stat_statements(), plperl_return_next(), and tstoreReceiveSlot_detoast().
{ MinimalTuple tuple; MemoryContext oldcxt = MemoryContextSwitchTo(state->context); tuple = heap_form_minimal_tuple(tdesc, values, isnull); USEMEM(state, GetMemoryChunkSpace(tuple)); tuplestore_puttuple_common(state, (void *) tuple); MemoryContextSwitchTo(oldcxt); }
void tuplestore_rescan | ( | Tuplestorestate * | state | ) |
Definition at line 1117 of file tuplestore.c.
References Tuplestorestate::activeptr, Assert, BufFileSeek(), TSReadPointer::current, TSReadPointer::eflags, elog, TSReadPointer::eof_reached, ERROR, EXEC_FLAG_REWIND, TSReadPointer::file, Tuplestorestate::myfile, TSReadPointer::offset, Tuplestorestate::readptrs, Tuplestorestate::status, Tuplestorestate::truncated, TSS_INMEM, TSS_READFILE, and TSS_WRITEFILE.
Referenced by DoPortalRewind(), ExecReScanCteScan(), ExecReScanFunctionScan(), ExecReScanMaterial(), ExecReScanWorkTableScan(), and PersistHoldablePortal().
{ TSReadPointer *readptr = &state->readptrs[state->activeptr]; Assert(readptr->eflags & EXEC_FLAG_REWIND); Assert(!state->truncated); switch (state->status) { case TSS_INMEM: readptr->eof_reached = false; readptr->current = 0; break; case TSS_WRITEFILE: readptr->eof_reached = false; readptr->file = 0; readptr->offset = 0L; break; case TSS_READFILE: readptr->eof_reached = false; if (BufFileSeek(state->myfile, 0, 0L, SEEK_SET) != 0) elog(ERROR, "tuplestore seek to start failed"); break; default: elog(ERROR, "invalid tuplestore state"); break; } }
void tuplestore_select_read_pointer | ( | Tuplestorestate * | state, | |
int | ptr | |||
) |
Definition at line 460 of file tuplestore.c.
References Tuplestorestate::activeptr, Assert, BufFileSeek(), BufFileTell(), elog, TSReadPointer::eof_reached, ERROR, TSReadPointer::file, Tuplestorestate::myfile, TSReadPointer::offset, Tuplestorestate::readptrs, Tuplestorestate::status, TSS_INMEM, TSS_READFILE, TSS_WRITEFILE, Tuplestorestate::writepos_file, and Tuplestorestate::writepos_offset.
Referenced by CteScanNext(), ExecReScanCteScan(), ExecWindowAgg(), window_gettupleslot(), and WinSetMarkPosition().
{ TSReadPointer *readptr; TSReadPointer *oldptr; Assert(ptr >= 0 && ptr < state->readptrcount); /* No work if already active */ if (ptr == state->activeptr) return; readptr = &state->readptrs[ptr]; oldptr = &state->readptrs[state->activeptr]; switch (state->status) { case TSS_INMEM: case TSS_WRITEFILE: /* no work */ break; case TSS_READFILE: /* * First, save the current read position in the pointer about to * become inactive. */ if (!oldptr->eof_reached) BufFileTell(state->myfile, &oldptr->file, &oldptr->offset); /* * We have to make the temp file's seek position equal to the * logical position of the new read pointer. In eof_reached * state, that's the EOF, which we have available from the saved * write position. */ if (readptr->eof_reached) { if (BufFileSeek(state->myfile, state->writepos_file, state->writepos_offset, SEEK_SET) != 0) elog(ERROR, "tuplestore seek failed"); } else { if (BufFileSeek(state->myfile, readptr->file, readptr->offset, SEEK_SET) != 0) elog(ERROR, "tuplestore seek failed"); } break; default: elog(ERROR, "invalid tuplestore state"); break; } state->activeptr = ptr; }
void tuplestore_set_eflags | ( | Tuplestorestate * | state, | |
int | eflags | |||
) |
Definition at line 347 of file tuplestore.c.
References Tuplestorestate::eflags, TSReadPointer::eflags, elog, ERROR, i, Tuplestorestate::memtupcount, Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::status, and TSS_INMEM.
Referenced by begin_partition(), ExecInitCteScan(), and ExecMaterial().
void tuplestore_trim | ( | Tuplestorestate * | state | ) |
Definition at line 1238 of file tuplestore.c.
References Assert, TSReadPointer::current, Tuplestorestate::eflags, TSReadPointer::eof_reached, EXEC_FLAG_REWIND, FREEMEM, GetMemoryChunkSpace(), i, memmove, Tuplestorestate::memtupcount, Tuplestorestate::memtupdeleted, Tuplestorestate::memtuples, Min, pfree(), Tuplestorestate::readptrcount, Tuplestorestate::readptrs, Tuplestorestate::status, Tuplestorestate::truncated, and TSS_INMEM.
Referenced by ExecMaterialMarkPos(), and ExecWindowAgg().
{ int oldest; int nremove; int i; /* * Truncation is disallowed if any read pointer requires rewind * capability. */ if (state->eflags & EXEC_FLAG_REWIND) return; /* * We don't bother trimming temp files since it usually would mean more * work than just letting them sit in kernel buffers until they age out. */ if (state->status != TSS_INMEM) return; /* Find the oldest read pointer */ oldest = state->memtupcount; for (i = 0; i < state->readptrcount; i++) { if (!state->readptrs[i].eof_reached) oldest = Min(oldest, state->readptrs[i].current); } /* * Note: you might think we could remove all the tuples before the oldest * "current", since that one is the next to be returned. However, since * tuplestore_gettuple returns a direct pointer to our internal copy of * the tuple, it's likely that the caller has still got the tuple just * before "current" referenced in a slot. So we keep one extra tuple * before the oldest "current". (Strictly speaking, we could require such * callers to use the "copy" flag to tuplestore_gettupleslot, but for * efficiency we allow this one case to not use "copy".) */ nremove = oldest - 1; if (nremove <= 0) return; /* nothing to do */ Assert(nremove >= state->memtupdeleted); Assert(nremove <= state->memtupcount); /* Release no-longer-needed tuples */ for (i = state->memtupdeleted; i < nremove; i++) { FREEMEM(state, GetMemoryChunkSpace(state->memtuples[i])); pfree(state->memtuples[i]); state->memtuples[i] = NULL; } state->memtupdeleted = nremove; /* mark tuplestore as truncated (used for Assert crosschecks only) */ state->truncated = true; /* * If nremove is less than 1/8th memtupcount, just stop here, leaving the * "deleted" slots as NULL. This prevents us from expending O(N^2) time * repeatedly memmove-ing a large pointer array. The worst case space * wastage is pretty small, since it's just pointers and not whole tuples. */ if (nremove < state->memtupcount / 8) return; /* * Slide the array down and readjust pointers. * * In mergejoin's current usage, it's demonstrable that there will always * be exactly one non-removed tuple; so optimize that case. */ if (nremove + 1 == state->memtupcount) state->memtuples[0] = state->memtuples[nremove]; else memmove(state->memtuples, state->memtuples + nremove, (state->memtupcount - nremove) * sizeof(void *)); state->memtupdeleted = 0; state->memtupcount -= nremove; for (i = 0; i < state->readptrcount; i++) { if (!state->readptrs[i].eof_reached) state->readptrs[i].current -= nremove; } }
static void writetup_heap | ( | Tuplestorestate * | state, | |
void * | tup | |||
) | [static] |
Definition at line 1381 of file tuplestore.c.
References Tuplestorestate::backward, BufFileWrite(), elog, ERROR, FREEMEM, GetMemoryChunkSpace(), heap_free_minimal_tuple(), MINIMAL_TUPLE_DATA_OFFSET, Tuplestorestate::myfile, and MinimalTupleData::t_len.
{ MinimalTuple tuple = (MinimalTuple) tup; /* the part of the MinimalTuple we'll write: */ char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET; unsigned int tupbodylen = tuple->t_len - MINIMAL_TUPLE_DATA_OFFSET; /* total on-disk footprint: */ unsigned int tuplen = tupbodylen + sizeof(int); if (BufFileWrite(state->myfile, (void *) &tuplen, sizeof(tuplen)) != sizeof(tuplen)) elog(ERROR, "write failed"); if (BufFileWrite(state->myfile, (void *) tupbody, tupbodylen) != (size_t) tupbodylen) elog(ERROR, "write failed"); if (state->backward) /* need trailing length word? */ if (BufFileWrite(state->myfile, (void *) &tuplen, sizeof(tuplen)) != sizeof(tuplen)) elog(ERROR, "write failed"); FREEMEM(state, GetMemoryChunkSpace(tuple)); heap_free_minimal_tuple(tuple); }