*/
if (lastrowid != NULL)
{
- /*
- * switch to appropriate context while storing the tuple
- */
- SPIcontext = MemoryContextSwitchTo(per_query_ctx);
-
/* rowid changed, flush the previous output row */
tuple = BuildTupleFromCStrings(attinmeta, values);
+
+ /* switch to appropriate context while storing the tuple */
+ SPIcontext = MemoryContextSwitchTo(per_query_ctx);
tuplestore_puttuple(tupstore, tuple);
+ MemoryContextSwitchTo(SPIcontext);
+
for (j = 0; j < result_ncols; j++)
xpfree(values[j]);
-
- /* now reset the context */
- MemoryContextSwitchTo(SPIcontext);
}
values[0] = rowid;
lastrowid = pstrdup(rowid);
}
- /* switch to appropriate context while storing the tuple */
- SPIcontext = MemoryContextSwitchTo(per_query_ctx);
-
/* flush the last output row */
tuple = BuildTupleFromCStrings(attinmeta, values);
- tuplestore_puttuple(tupstore, tuple);
- /* now reset the context */
+ /* switch to appropriate context while storing the tuple */
+ SPIcontext = MemoryContextSwitchTo(per_query_ctx);
+ tuplestore_puttuple(tupstore, tuple);
MemoryContextSwitchTo(SPIcontext);
-
}
if (SPI_finish() != SPI_OK_FINISH)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.106 2006/03/05 15:58:20 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.107 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
if (tuple == NULL) /* internal error */
elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (slot->tts_mintuple) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
}
{
if (tuple == NULL) /* internal error */
elog(ERROR, "cannot extract system attribute from virtual tuple");
+ if (slot->tts_mintuple) /* internal error */
+ elog(ERROR, "cannot extract system attribute from minimal tuple");
return heap_attisnull(tuple, attnum);
}
return heap_attisnull(tuple, attnum);
}
-/* ----------------
- * heap_freetuple
- * ----------------
+/*
+ * heap_freetuple
*/
void
heap_freetuple(HeapTuple htup)
}
+/*
+ * heap_form_minimal_tuple
+ * construct a MinimalTuple from the given values[] and isnull[] arrays,
+ * which are of the length indicated by tupleDescriptor->natts
+ *
+ * This is exactly like heap_form_tuple() except that the result is a
+ * "minimal" tuple lacking a HeapTupleData header as well as room for system
+ * columns.
+ *
+ * The result is allocated in the current memory context.
+ */
+MinimalTuple
+heap_form_minimal_tuple(TupleDesc tupleDescriptor,
+ Datum *values,
+ bool *isnull)
+{
+ MinimalTuple tuple; /* return tuple */
+ unsigned long len;
+ int hoff;
+ bool hasnull = false;
+ Form_pg_attribute *att = tupleDescriptor->attrs;
+ int numberOfAttributes = tupleDescriptor->natts;
+ int i;
+
+ if (numberOfAttributes > MaxTupleAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("number of columns (%d) exceeds limit (%d)",
+ numberOfAttributes, MaxTupleAttributeNumber)));
+
+ /*
+ * Check for nulls and embedded tuples; expand any toasted attributes in
+ * embedded tuples. This preserves the invariant that toasting can only
+ * go one level deep.
+ *
+ * We can skip calling toast_flatten_tuple_attribute() if the attribute
+ * couldn't possibly be of composite type. All composite datums are
+ * varlena and have alignment 'd'; furthermore they aren't arrays. Also,
+ * if an attribute is already toasted, it must have been sent to disk
+ * already and so cannot contain toasted attributes.
+ */
+ for (i = 0; i < numberOfAttributes; i++)
+ {
+ if (isnull[i])
+ hasnull = true;
+ else if (att[i]->attlen == -1 &&
+ att[i]->attalign == 'd' &&
+ att[i]->attndims == 0 &&
+ !VARATT_IS_EXTENDED(values[i]))
+ {
+ values[i] = toast_flatten_tuple_attribute(values[i],
+ att[i]->atttypid,
+ att[i]->atttypmod);
+ }
+ }
+
+ /*
+ * Determine total space needed
+ */
+ len = offsetof(MinimalTupleData, t_bits);
+
+ if (hasnull)
+ len += BITMAPLEN(numberOfAttributes);
+
+ if (tupleDescriptor->tdhasoid)
+ len += sizeof(Oid);
+
+ hoff = len = MAXALIGN(len); /* align user data safely */
+
+ len += heap_compute_data_size(tupleDescriptor, values, isnull);
+
+ /*
+ * Allocate and zero the space needed.
+ */
+ tuple = (MinimalTuple) palloc0(len);
+
+ /*
+ * And fill in the information.
+ */
+ tuple->t_len = len;
+ tuple->t_natts = numberOfAttributes;
+ tuple->t_hoff = hoff + MINIMAL_TUPLE_OFFSET;
+
+ if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
+ tuple->t_infomask = HEAP_HASOID;
+
+ heap_fill_tuple(tupleDescriptor,
+ values,
+ isnull,
+ (char *) tuple + hoff,
+ &tuple->t_infomask,
+ (hasnull ? tuple->t_bits : NULL));
+
+ return tuple;
+}
+
+/*
+ * heap_free_minimal_tuple
+ */
+void
+heap_free_minimal_tuple(MinimalTuple mtup)
+{
+ pfree(mtup);
+}
+
+/*
+ * heap_copy_minimal_tuple
+ * copy a MinimalTuple
+ *
+ * The result is allocated in the current memory context.
+ */
+MinimalTuple
+heap_copy_minimal_tuple(MinimalTuple mtup)
+{
+ MinimalTuple result;
+
+ result = (MinimalTuple) palloc(mtup->t_len);
+ memcpy(result, mtup, mtup->t_len);
+ return result;
+}
+
+/*
+ * heap_tuple_from_minimal_tuple
+ * create a HeapTuple by copying from a MinimalTuple;
+ * system columns are filled with zeroes
+ *
+ * The result is allocated in the current memory context.
+ * The HeapTuple struct, tuple header, and tuple data are all allocated
+ * as a single palloc() block.
+ */
+HeapTuple
+heap_tuple_from_minimal_tuple(MinimalTuple mtup)
+{
+ HeapTuple result;
+ uint32 len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+
+ result = (HeapTuple) palloc(HEAPTUPLESIZE + len);
+ result->t_len = len;
+ ItemPointerSetInvalid(&(result->t_self));
+ result->t_tableOid = InvalidOid;
+ result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE);
+ memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len);
+ memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_natts));
+ return result;
+}
+
+/*
+ * minimal_tuple_from_heap_tuple
+ * create a MinimalTuple by copying from a HeapTuple
+ *
+ * The result is allocated in the current memory context.
+ */
+MinimalTuple
+minimal_tuple_from_heap_tuple(HeapTuple htup)
+{
+ MinimalTuple result;
+ uint32 len;
+
+ Assert(htup->t_len > MINIMAL_TUPLE_OFFSET);
+ len = htup->t_len - MINIMAL_TUPLE_OFFSET;
+ result = (MinimalTuple) palloc(len);
+ memcpy(result, (char *) htup->t_data + MINIMAL_TUPLE_OFFSET, len);
+ result->t_len = len;
+ return result;
+}
+
+
/* ----------------
* heap_addheader
*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.46 2006/03/05 15:58:24 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.47 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
{
- HeapTuple tup;
- bool should_free;
-
- tup = tuplestore_gettuple(portal->holdStore, true,
- &should_free);
-
- if (tup == NULL)
+ if (!tuplestore_advance(portal->holdStore, true))
elog(ERROR, "unexpected end of tuple stream");
-
- if (should_free)
- pfree(tup);
}
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.94 2006/06/16 18:42:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.95 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* TABLE CREATE/DELETE
* ExecCreateTupleTable - create a new tuple table
* ExecDropTupleTable - destroy a table
+ * MakeSingleTupleTableSlot - make a single-slot table
+ * ExecDropSingleTupleTableSlot - destroy same
*
* SLOT RESERVATION
* ExecAllocTableSlot - find an available slot in the table
* SLOT ACCESSORS
* ExecSetSlotDescriptor - set a slot's tuple descriptor
* ExecStoreTuple - store a physical tuple in the slot
+ * ExecStoreMinimalTuple - store a minimal physical tuple in the slot
* ExecClearTuple - clear contents of a slot
* ExecStoreVirtualTuple - mark slot as containing a virtual tuple
* ExecCopySlotTuple - build a physical tuple from a slot
+ * ExecCopySlotMinimalTuple - build a minimal physical tuple from a slot
* ExecMaterializeSlot - convert virtual to physical storage
* ExecCopySlot - copy one slot's contents to another
*
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
+ slot->tts_mintuple = NULL;
}
return newtable;
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
+ slot->tts_mintuple = NULL;
ExecSetSlotDescriptor(slot, tupdesc);
* Free any old physical tuple belonging to the slot.
*/
if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
+ {
+ if (slot->tts_mintuple)
+ heap_free_minimal_tuple(slot->tts_mintuple);
+ else
+ heap_freetuple(slot->tts_tuple);
+ }
/*
* Store the new tuple into the specified slot.
slot->tts_isempty = false;
slot->tts_shouldFree = shouldFree;
slot->tts_tuple = tuple;
+ slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
return slot;
}
+/* --------------------------------
+ * ExecStoreMinimalTuple
+ *
+ * Like ExecStoreTuple, but insert a "minimal" tuple into the slot.
+ *
+ * No 'buffer' parameter since minimal tuples are never stored in relations.
+ * --------------------------------
+ */
+TupleTableSlot *
+ExecStoreMinimalTuple(MinimalTuple mtup,
+ TupleTableSlot *slot,
+ bool shouldFree)
+{
+ /*
+ * sanity checks
+ */
+ Assert(mtup != NULL);
+ Assert(slot != NULL);
+ Assert(slot->tts_tupleDescriptor != NULL);
+
+ /*
+ * Free any old physical tuple belonging to the slot.
+ */
+ if (slot->tts_shouldFree)
+ {
+ if (slot->tts_mintuple)
+ heap_free_minimal_tuple(slot->tts_mintuple);
+ else
+ heap_freetuple(slot->tts_tuple);
+ }
+
+ /*
+ * Drop the pin on the referenced buffer, if there is one.
+ */
+ if (BufferIsValid(slot->tts_buffer))
+ ReleaseBuffer(slot->tts_buffer);
+
+ slot->tts_buffer = InvalidBuffer;
+
+ /*
+ * Store the new tuple into the specified slot.
+ */
+ slot->tts_isempty = false;
+ slot->tts_shouldFree = shouldFree;
+ slot->tts_tuple = &slot->tts_minhdr;
+ slot->tts_mintuple = mtup;
+
+ slot->tts_minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+ slot->tts_minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
+ /* no need to set t_self or t_tableOid since we won't allow access */
+
+ /* Mark extracted state invalid */
+ slot->tts_nvalid = 0;
+
+ return slot;
+}
+
/* --------------------------------
* ExecClearTuple
*
* Free the old physical tuple if necessary.
*/
if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
+ {
+ if (slot->tts_mintuple)
+ heap_free_minimal_tuple(slot->tts_mintuple);
+ else
+ heap_freetuple(slot->tts_tuple);
+ }
slot->tts_tuple = NULL;
+ slot->tts_mintuple = NULL;
slot->tts_shouldFree = false;
/*
/* --------------------------------
* ExecCopySlotTuple
- * Obtain a copy of a slot's physical tuple. The copy is
+ * Obtain a copy of a slot's regular physical tuple. The copy is
* palloc'd in the current memory context.
*
- * This works even if the slot contains a virtual tuple;
+ * This works even if the slot contains a virtual or minimal tuple;
* however the "system columns" of the result will not be meaningful.
* --------------------------------
*/
* If we have a physical tuple then just copy it.
*/
if (slot->tts_tuple)
- return heap_copytuple(slot->tts_tuple);
+ {
+ if (slot->tts_mintuple)
+ return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
+ else
+ return heap_copytuple(slot->tts_tuple);
+ }
/*
* Otherwise we need to build a tuple from the Datum array.
slot->tts_isnull);
}
+/* --------------------------------
+ * ExecCopySlotMinimalTuple
+ * Obtain a copy of a slot's minimal physical tuple. The copy is
+ * palloc'd in the current memory context.
+ * --------------------------------
+ */
+MinimalTuple
+ExecCopySlotMinimalTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!slot->tts_isempty);
+
+ /*
+ * If we have a physical tuple then just copy it.
+ */
+ if (slot->tts_tuple)
+ {
+ if (slot->tts_mintuple)
+ return heap_copy_minimal_tuple(slot->tts_mintuple);
+ else
+ return minimal_tuple_from_heap_tuple(slot->tts_tuple);
+ }
+
+ /*
+ * Otherwise we need to build a tuple from the Datum array.
+ */
+ return heap_form_minimal_tuple(slot->tts_tupleDescriptor,
+ slot->tts_values,
+ slot->tts_isnull);
+}
+
/* --------------------------------
* ExecFetchSlotTuple
- * Fetch the slot's physical tuple.
+ * Fetch the slot's regular physical tuple.
*
* If the slot contains a virtual tuple, we convert it to physical
* form. The slot retains ownership of the physical tuple.
+ * Likewise, if it contains a minimal tuple we convert to regular form.
*
* The difference between this and ExecMaterializeSlot() is that this
* does not guarantee that the contained tuple is local storage.
Assert(!slot->tts_isempty);
/*
- * If we have a physical tuple then just return it.
+ * If we have a regular physical tuple then just return it.
*/
- if (slot->tts_tuple)
+ if (slot->tts_tuple && slot->tts_mintuple == NULL)
return slot->tts_tuple;
/*
Assert(!slot->tts_isempty);
/*
- * If we have a physical tuple, and it's locally palloc'd, we have nothing
- * to do.
+ * If we have a regular physical tuple, and it's locally palloc'd,
+ * we have nothing to do.
*/
- if (slot->tts_tuple && slot->tts_shouldFree)
+ if (slot->tts_tuple && slot->tts_shouldFree && slot->tts_mintuple == NULL)
return slot->tts_tuple;
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.39 2006/06/16 18:42:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.40 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
- bool should_free;
- HeapTuple heapTuple;
/*
* get information from the estate and scan state
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
- heapTuple = tuplestore_getheaptuple(tuplestorestate,
- ScanDirectionIsForward(direction),
- &should_free);
slot = node->ss.ss_ScanTupleSlot;
- if (heapTuple)
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
- else
- return ExecClearTuple(slot);
+ (void) tuplestore_gettupleslot(tuplestorestate,
+ ScanDirectionIsForward(direction),
+ slot);
+ return slot;
}
/* ----------------------------------------------------------------
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.54 2006/03/05 15:58:26 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeMaterial.c,v 1.55 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
ScanDirection dir;
bool forward;
Tuplestorestate *tuplestorestate;
- HeapTuple heapTuple = NULL;
- bool should_free = false;
bool eof_tuplestore;
TupleTableSlot *slot;
{
/*
* When reversing direction at tuplestore EOF, the first
- * getheaptuple call will fetch the last-added tuple; but we want
+ * gettupleslot call will fetch the last-added tuple; but we want
* to return the one before that, if possible. So do an extra
* fetch.
*/
- heapTuple = tuplestore_getheaptuple(tuplestorestate,
- forward,
- &should_free);
- if (heapTuple == NULL)
+ if (!tuplestore_advance(tuplestorestate, forward))
return NULL; /* the tuplestore must be empty */
- if (should_free)
- heap_freetuple(heapTuple);
}
eof_tuplestore = false;
}
+ /*
+ * If we can fetch another tuple from the tuplestore, return it.
+ */
+ slot = node->ss.ps.ps_ResultTupleSlot;
if (!eof_tuplestore)
{
- heapTuple = tuplestore_getheaptuple(tuplestorestate,
- forward,
- &should_free);
- if (heapTuple == NULL && forward)
+ if (tuplestore_gettupleslot(tuplestorestate, forward, slot))
+ return slot;
+ if (forward)
eof_tuplestore = true;
}
node->eof_underlying = true;
return NULL;
}
- heapTuple = ExecFetchSlotTuple(outerslot);
- should_free = false;
/*
- * Append returned tuple to tuplestore, too. NOTE: because the
+ * Append returned tuple to tuplestore. NOTE: because the
* tuplestore is certainly in EOF state, its read position will move
* forward over the added tuple. This is what we want.
*/
if (tuplestorestate)
- tuplestore_puttuple(tuplestorestate, (void *) heapTuple);
+ tuplestore_puttupleslot(tuplestorestate, outerslot);
+
+ /*
+ * And return a copy of the tuple. (XXX couldn't we just return
+ * the outerslot?)
+ */
+ return ExecCopySlot(slot, outerslot);
}
/*
- * Return the obtained tuple, if any.
+ * Nothing left ...
*/
- slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot;
- if (heapTuple)
- return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
- else
- return ExecClearTuple(slot);
+ return ExecClearTuple(slot);
}
/* ----------------------------------------------------------------
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.16 2006/03/05 15:58:27 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/tstoreReceiver.c,v 1.17 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
TStoreState *myState = (TStoreState *) self;
MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt);
- tuplestore_puttuple(myState->tstore, ExecFetchSlotTuple(slot));
+ tuplestore_puttupleslot(myState->tstore, slot);
MemoryContextSwitchTo(oldcxt);
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.101 2006/03/05 15:58:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.102 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
for (;;)
{
MemoryContext oldcontext;
- HeapTuple tup;
- bool should_free;
+ bool ok;
oldcontext = MemoryContextSwitchTo(portal->holdContext);
- tup = tuplestore_getheaptuple(portal->holdStore, forward,
- &should_free);
+ ok = tuplestore_gettupleslot(portal->holdStore, forward, slot);
MemoryContextSwitchTo(oldcontext);
- if (tup == NULL)
+ if (!ok)
break;
- ExecStoreTuple(tup, slot, InvalidBuffer, should_free);
-
(*dest->receiveSlot) (slot, dest);
ExecClearTuple(slot);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.27 2006/03/05 15:58:49 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.28 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
bool interXact,
int maxKBytes);
+static void tuplestore_puttuple_common(Tuplestorestate *state, void *tuple);
static void dumptuples(Tuplestorestate *state);
static unsigned int getlen(Tuplestorestate *state, bool eofOK);
static void *copytup_heap(Tuplestorestate *state, void *tup);
* If the read status is currently "AT EOF" then it remains so (the read
* pointer advances along with the write pointer); otherwise the read
* pointer is unchanged. This is for the convenience of nodeMaterial.c.
+ *
+ * tuplestore_puttupleslot() is a convenience routine to collect data from
+ * a TupleTableSlot without an extra copy operation.
*/
void
-tuplestore_puttuple(Tuplestorestate *state, void *tuple)
+tuplestore_puttupleslot(Tuplestorestate *state,
+ TupleTableSlot *slot)
+{
+ MinimalTuple tuple;
+
+ /*
+ * Form a MinimalTuple in working memory
+ */
+ tuple = ExecCopySlotMinimalTuple(slot);
+ USEMEM(state, GetMemoryChunkSpace(tuple));
+
+ tuplestore_puttuple_common(state, (void *) tuple);
+}
+
+/*
+ * "Standard" case to copy from a HeapTuple. This is actually now somewhat
+ * deprecated, but not worth getting rid of in view of the number of callers.
+ * (Consider adding something that takes a tupdesc+values/nulls arrays so
+ * that we can use heap_form_minimal_tuple() and avoid a copy step.)
+ */
+void
+tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
{
/*
* Copy the tuple. (Must do this even in WRITEFILE case.)
*/
tuple = COPYTUP(state, tuple);
+ tuplestore_puttuple_common(state, (void *) tuple);
+}
+
+static void
+tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
+{
switch (state->status)
{
case TSS_INMEM:
* Returns NULL if no more tuples. If should_free is set, the
* caller must pfree the returned tuple when done with it.
*/
-void *
+static void *
tuplestore_gettuple(Tuplestorestate *state, bool forward,
bool *should_free)
{
}
}
+/*
+ * tuplestore_gettupleslot - exported function to fetch a MinimalTuple
+ *
+ * If successful, put tuple in slot and return TRUE; else, clear the slot
+ * and return FALSE.
+ */
+bool
+tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
+ TupleTableSlot *slot)
+{
+ MinimalTuple tuple;
+ bool should_free;
+
+ tuple = (MinimalTuple) tuplestore_gettuple(state, forward, &should_free);
+
+ if (tuple)
+ {
+ ExecStoreMinimalTuple(tuple, slot, should_free);
+ return true;
+ }
+ else
+ {
+ ExecClearTuple(slot);
+ return 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.
+ */
+bool
+tuplestore_advance(Tuplestorestate *state, bool forward)
+{
+ void *tuple;
+ bool should_free;
+
+ tuple = tuplestore_gettuple(state, forward, &should_free);
+
+ if (tuple)
+ {
+ if (should_free)
+ pfree(tuple);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
/*
* dumptuples - remove tuples from memory and write to tape
*
/*
* Routines specialized for HeapTuple case
+ *
+ * The stored form is actually a MinimalTuple, but for largely historical
+ * reasons we allow COPYTUP to work from a HeapTuple.
+ *
+ * Since MinimalTuple already has length in its first word, we don't need
+ * to write that separately.
*/
static void *
copytup_heap(Tuplestorestate *state, void *tup)
{
- HeapTuple tuple = (HeapTuple) tup;
+ MinimalTuple tuple;
- tuple = heap_copytuple(tuple);
+ tuple = minimal_tuple_from_heap_tuple((HeapTuple) tup);
USEMEM(state, GetMemoryChunkSpace(tuple));
return (void *) tuple;
}
-/*
- * We don't bother to write the HeapTupleData part of the tuple.
- */
-
static void
writetup_heap(Tuplestorestate *state, void *tup)
{
- HeapTuple tuple = (HeapTuple) tup;
- unsigned int tuplen;
+ MinimalTuple tuple = (MinimalTuple) tup;
+ unsigned int tuplen = tuple->t_len;
- tuplen = tuple->t_len + sizeof(tuplen);
- if (BufFileWrite(state->myfile, (void *) &tuplen,
- sizeof(tuplen)) != sizeof(tuplen))
- elog(ERROR, "write failed");
- if (BufFileWrite(state->myfile, (void *) tuple->t_data,
- tuple->t_len) != (size_t) tuple->t_len)
+ if (BufFileWrite(state->myfile, (void *) tuple, tuplen) != (size_t) tuplen)
elog(ERROR, "write failed");
if (state->randomAccess) /* need trailing length word? */
if (BufFileWrite(state->myfile, (void *) &tuplen,
elog(ERROR, "write failed");
FREEMEM(state, GetMemoryChunkSpace(tuple));
- heap_freetuple(tuple);
+ heap_free_minimal_tuple(tuple);
}
static void *
readtup_heap(Tuplestorestate *state, unsigned int len)
{
- unsigned int tuplen = len - sizeof(unsigned int) + HEAPTUPLESIZE;
- HeapTuple tuple = (HeapTuple) palloc(tuplen);
+ MinimalTuple tuple = (MinimalTuple) palloc(len);
+ unsigned int tuplen;
USEMEM(state, GetMemoryChunkSpace(tuple));
- /* reconstruct the HeapTupleData portion */
- tuple->t_len = len - sizeof(unsigned int);
- ItemPointerSetInvalid(&(tuple->t_self));
- tuple->t_data = (HeapTupleHeader) (((char *) tuple) + HEAPTUPLESIZE);
/* read in the tuple proper */
- if (BufFileRead(state->myfile, (void *) tuple->t_data,
- tuple->t_len) != (size_t) tuple->t_len)
+ tuple->t_len = len;
+ if (BufFileRead(state->myfile, (void *) ((char *) tuple + sizeof(int)),
+ len - sizeof(int)) != (size_t) (len - sizeof(int)))
elog(ERROR, "unexpected end of data");
if (state->randomAccess) /* need trailing length word? */
if (BufFileRead(state->myfile, (void *) &tuplen,
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.111 2006/05/10 23:18:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.112 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Datum *values, bool *isnull);
extern void heap_deformtuple(HeapTuple tuple, TupleDesc tupleDesc,
Datum *values, char *nulls);
-extern void heap_freetuple(HeapTuple tuple);
+extern void heap_freetuple(HeapTuple htup);
+extern MinimalTuple heap_form_minimal_tuple(TupleDesc tupleDescriptor,
+ Datum *values, bool *isnull);
+extern void heap_free_minimal_tuple(MinimalTuple mtup);
+extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup);
+extern HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup);
+extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup);
extern HeapTuple heap_addheader(int natts, bool withoid,
Size structlen, void *structure);
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.82 2006/05/10 23:18:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.83 2006/06/27 02:51:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
#define MaxTupleAttributeNumber 1664 /* 8 * 208 */
-/*----------
+/*
* MaxHeapAttributeNumber limits the number of (user) columns in a table.
* This should be somewhat less than MaxTupleAttributeNumber. It must be
* at least one less, else we will fail to do UPDATEs on a maximal-width
* In any case, depending on column data types you will likely be running
* into the disk-block-based limit on overall tuple size if you have more
* than a thousand or so columns. TOAST won't help.
- *----------
*/
#define MaxHeapAttributeNumber 1600 /* 8 * 200 */
-/*----------
+/*
* Heap tuple header. To avoid wasting space, the fields should be
* layed out in such a way to avoid structure padding.
*
* t_infomask), then it is stored just before the user data, which begins at
* the offset shown by t_hoff. Note that t_hoff must be a multiple of
* MAXALIGN.
- *----------
*/
typedef struct HeapTupleFields
ItemPointerData t_ctid; /* current TID of this or newer tuple */
+ /* Fields below here must match MinimalTupleData! */
+
int16 t_natts; /* number of attributes */
uint16 t_infomask; /* various flag bits, see below */
#define FirstLowInvalidHeapAttributeNumber (-8)
+/*
+ * MinimalTuple is an alternate representation that is used for transient
+ * tuples inside the executor, in places where transaction status information
+ * is not required, the tuple rowtype is known, and shaving off a few bytes
+ * is worthwhile because we need to store many tuples. The representation
+ * is chosen so that tuple access routines can work with either full or
+ * minimal tuples via a HeapTupleData pointer structure. The access routines
+ * see no difference, except that they must not access the transaction status
+ * or t_ctid fields because those aren't there.
+ *
+ * For the most part, MinimalTuples should be accessed via TupleTableSlot
+ * routines. These routines will prevent access to the "system columns"
+ * and thereby prevent accidental use of the nonexistent fields.
+ *
+ * MinimalTupleData contains a length word, some padding, and fields matching
+ * HeapTupleHeaderData beginning with t_natts. The padding is chosen so that
+ * offsetof(t_natts) is the same modulo MAXIMUM_ALIGNOF in both structs.
+ * This makes data alignment rules equivalent in both cases.
+ *
+ * When a minimal tuple is accessed via a HeapTupleData pointer, t_data is
+ * set to point MINIMAL_TUPLE_OFFSET bytes before the actual start of the
+ * minimal tuple --- that is, where a full tuple matching the minimal tuple's
+ * data would start. This trick is what makes the structs seem equivalent.
+ *
+ * Note that t_hoff is computed the same as in a full tuple, hence it includes
+ * the MINIMAL_TUPLE_OFFSET distance. t_len does not include that, however.
+ */
+#define MINIMAL_TUPLE_OFFSET \
+ ((offsetof(HeapTupleHeaderData, t_natts) - sizeof(uint32)) / MAXIMUM_ALIGNOF * MAXIMUM_ALIGNOF)
+#define MINIMAL_TUPLE_PADDING \
+ ((offsetof(HeapTupleHeaderData, t_natts) - sizeof(uint32)) % MAXIMUM_ALIGNOF)
+
+typedef struct MinimalTupleData
+{
+ uint32 t_len; /* actual length of minimal tuple */
+
+ char mt_padding[MINIMAL_TUPLE_PADDING];
+
+ /* Fields below here must match HeapTupleHeaderData! */
+
+ int16 t_natts; /* number of attributes */
+
+ uint16 t_infomask; /* various flag bits, see below */
+
+ uint8 t_hoff; /* sizeof header incl. bitmap, padding */
+
+ /* ^ - 27 bytes - ^ */
+
+ bits8 t_bits[1]; /* bitmap of NULLs -- VARIABLE LENGTH */
+
+ /* MORE DATA FOLLOWS AT END OF STRUCT */
+} MinimalTupleData;
+
+typedef MinimalTupleData *MinimalTuple;
+
+
/*
* HeapTupleData is an in-memory data structure that points to a tuple.
*
* it's difficult to tell apart from case #1. It should be used only in
* limited contexts where the code knows that case #1 will never apply.)
*
+ * * Separately allocated minimal tuple: t_data points MINIMAL_TUPLE_OFFSET
+ * bytes before the start of a MinimalTuple. As with the previous case,
+ * this can't be told apart from case #1 by inspection; code setting up
+ * or destroying this representation has to know what it's doing.
+ *
* t_len should always be valid, except in the pointer-to-nothing case.
* t_self and t_tableOid should be valid if the HeapTupleData points to
* a disk buffer, or if it represents a copy of a tuple on disk. They
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.31 2006/06/16 18:42:23 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/tuptable.h,v 1.32 2006/06/27 02:51:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* independent TupleTableSlots. There are several cases we need to handle:
* 1. physical tuple in a disk buffer page
* 2. physical tuple constructed in palloc'ed memory
- * 3. "virtual" tuple consisting of Datum/isnull arrays
+ * 3. "minimal" physical tuple constructed in palloc'ed memory
+ * 4. "virtual" tuple consisting of Datum/isnull arrays
*
* The first two cases are similar in that they both deal with "materialized"
* tuples, but resource management is different. For a tuple in a disk page
* to the tuple is dropped; while for a palloc'd tuple we usually want the
* tuple pfree'd when the TupleTableSlot's reference is dropped.
*
+ * A "minimal" tuple is handled similarly to a palloc'd regular tuple.
+ * At present, minimal tuples never are stored in buffers, so there is no
+ * parallel to case 1. Note that a minimal tuple has no "system columns".
+ * (Actually, it could have an OID, but we have no need to access the OID.)
+ *
* A "virtual" tuple is an optimization used to minimize physical data
* copying in a nest of plan nodes. Any pass-by-reference Datums in the
* tuple point to storage that is not directly associated with the
* tts_values/tts_isnull are allocated when a descriptor is assigned to the
* slot; they are of length equal to the descriptor's natts.
*
+ * tts_mintuple must always be NULL if the slot does not hold a "minimal"
+ * tuple. When it does, tts_mintuple points to the actual MinimalTupleData
+ * object (the thing to be pfree'd if tts_shouldFree is true). In this case
+ * tts_tuple points at tts_minhdr and the fields of that are set correctly
+ * for access to the minimal tuple; in particular, tts_minhdr.t_data points
+ * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. (tts_mintuple is therefore
+ * redundant, but for code simplicity we store it explicitly anyway.) This
+ * case otherwise behaves identically to the regular-physical-tuple case.
+ *
* tts_slow/tts_off are saved state for slot_deform_tuple, and should not
* be touched by any other code.
*----------
int tts_nvalid; /* # of valid values in tts_values */
Datum *tts_values; /* current per-attribute values */
bool *tts_isnull; /* current per-attribute isnull flags */
+ MinimalTuple tts_mintuple; /* set if it's a minimal tuple, else NULL */
+ HeapTupleData tts_minhdr; /* workspace if it's a minimal tuple */
long tts_off; /* saved state for slot_deform_tuple */
} TupleTableSlot;
TupleTableSlot *slot,
Buffer buffer,
bool shouldFree);
+extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
+ TupleTableSlot *slot,
+ bool shouldFree);
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
+extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
* A temporary file is used to handle the data if it exceeds the
* space limit specified by the caller.
*
+ * Beginning in Postgres 8.2, what is stored is just MinimalTuples;
+ * callers cannot expect valid system columns in regurgitated tuples.
+ * Also, we have changed the API to return tuples in TupleTableSlots,
+ * so that there is a check to prevent attempted access to system columns.
+ *
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/tuplestore.h,v 1.17 2006/03/05 15:59:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/tuplestore.h,v 1.18 2006/06/27 02:51:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef TUPLESTORE_H
#define TUPLESTORE_H
-#include "access/htup.h"
+#include "executor/tuptable.h"
+
/* Tuplestorestate is an opaque type whose details are not known outside
* tuplestore.c.
typedef struct Tuplestorestate Tuplestorestate;
/*
- * Currently we only need to store HeapTuples, but it would be easy
+ * Currently we only need to store MinimalTuples, but it would be easy
* to support the same behavior for IndexTuples and/or bare Datums.
*/
bool interXact,
int maxKBytes);
-extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
+extern void tuplestore_puttupleslot(Tuplestorestate *state,
+ TupleTableSlot *slot);
+extern void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple);
/* tuplestore_donestoring() used to be required, but is no longer used */
#define tuplestore_donestoring(state) ((void) 0)
/* backwards scan is only allowed if randomAccess was specified 'true' */
-extern void *tuplestore_gettuple(Tuplestorestate *state, bool forward,
- bool *should_free);
-
-#define tuplestore_getheaptuple(state, forward, should_free) \
- ((HeapTuple) tuplestore_gettuple(state, forward, should_free))
+extern bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward,
+ TupleTableSlot *slot);
+extern bool tuplestore_advance(Tuplestorestate *state, bool forward);
extern void tuplestore_end(Tuplestorestate *state);