aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2006-06-27 02:51:40 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2006-06-27 02:51:40 +0000
commit3f50ba27cf417eb57fd310c2a88f76a6ea6b966e (patch)
treee9dec4aaac793ed8efab65488e62532057f91704 /src/backend
parentfe491fb9afd07f3cc9b8aabb17f43049b79258a9 (diff)
downloadpostgresql-3f50ba27cf417eb57fd310c2a88f76a6ea6b966e.tar.gz
postgresql-3f50ba27cf417eb57fd310c2a88f76a6ea6b966e.zip
Create infrastructure for 'MinimalTuple' representation of in-memory
tuples with less header overhead than a regular HeapTuple, per my recent proposal. Teach TupleTableSlot code how to deal with these. As proof of concept, change tuplestore.c to store MinimalTuples instead of HeapTuples. Future patches will expand the concept to other places where it is useful.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/access/common/heaptuple.c178
-rw-r--r--src/backend/commands/portalcmds.c13
-rw-r--r--src/backend/executor/execTuples.c139
-rw-r--r--src/backend/executor/nodeFunctionscan.c15
-rw-r--r--src/backend/executor/nodeMaterial.c44
-rw-r--r--src/backend/executor/tstoreReceiver.c4
-rw-r--r--src/backend/tcop/pquery.c12
-rw-r--r--src/backend/utils/sort/tuplestore.c130
8 files changed, 438 insertions, 97 deletions
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 9d77ab2779d..7ec314379ba 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -16,7 +16,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -1295,6 +1295,8 @@ slot_getattr(TupleTableSlot *slot, int attnum, bool *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_getsysattr(tuple, attnum, tupleDesc, isnull);
}
@@ -1479,6 +1481,8 @@ slot_attisnull(TupleTableSlot *slot, int attnum)
{
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);
}
@@ -1505,9 +1509,8 @@ slot_attisnull(TupleTableSlot *slot, int attnum)
return heap_attisnull(tuple, attnum);
}
-/* ----------------
- * heap_freetuple
- * ----------------
+/*
+ * heap_freetuple
*/
void
heap_freetuple(HeapTuple htup)
@@ -1516,6 +1519,173 @@ 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
*
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 403b5c70645..c4c55c9cf3c 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -411,17 +411,8 @@ PersistHoldablePortal(Portal portal)
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);
}
}
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 971822525f0..f03d738619d 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -25,6 +25,8 @@
* 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
@@ -32,9 +34,11 @@
* 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
*
@@ -150,6 +154,7 @@ ExecCreateTupleTable(int tableSize)
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
+ slot->tts_mintuple = NULL;
}
return newtable;
@@ -227,6 +232,7 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
slot->tts_nvalid = 0;
slot->tts_values = NULL;
slot->tts_isnull = NULL;
+ slot->tts_mintuple = NULL;
ExecSetSlotDescriptor(slot, tupdesc);
@@ -405,7 +411,12 @@ ExecStoreTuple(HeapTuple tuple,
* 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.
@@ -413,6 +424,7 @@ ExecStoreTuple(HeapTuple tuple,
slot->tts_isempty = false;
slot->tts_shouldFree = shouldFree;
slot->tts_tuple = tuple;
+ slot->tts_mintuple = NULL;
/* Mark extracted state invalid */
slot->tts_nvalid = 0;
@@ -439,6 +451,63 @@ ExecStoreTuple(HeapTuple tuple,
}
/* --------------------------------
+ * 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
*
* This function is used to clear out a slot in the tuple table.
@@ -458,9 +527,15 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
* 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;
/*
@@ -540,10 +615,10 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
/* --------------------------------
* 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.
* --------------------------------
*/
@@ -560,7 +635,12 @@ ExecCopySlotTuple(TupleTableSlot *slot)
* 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.
@@ -571,11 +651,46 @@ ExecCopySlotTuple(TupleTableSlot *slot)
}
/* --------------------------------
+ * 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.
@@ -592,9 +707,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
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;
/*
@@ -629,10 +744,10 @@ ExecMaterializeSlot(TupleTableSlot *slot)
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;
/*
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 0c77e821690..90ab0188743 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -48,8 +48,6 @@ FunctionNext(FunctionScanState *node)
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
- bool should_free;
- HeapTuple heapTuple;
/*
* get information from the estate and scan state
@@ -86,14 +84,11 @@ FunctionNext(FunctionScanState *node)
/*
* 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;
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index eca769a86cc..94a07013854 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -44,8 +44,6 @@ ExecMaterial(MaterialState *node)
ScanDirection dir;
bool forward;
Tuplestorestate *tuplestorestate;
- HeapTuple heapTuple = NULL;
- bool should_free = false;
bool eof_tuplestore;
TupleTableSlot *slot;
@@ -80,27 +78,25 @@ ExecMaterial(MaterialState *node)
{
/*
* 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;
}
@@ -128,26 +124,26 @@ ExecMaterial(MaterialState *node)
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);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index dffc8899b23..8ebf5b7fd1d 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -9,7 +9,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -45,7 +45,7 @@ tstoreReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
TStoreState *myState = (TStoreState *) self;
MemoryContext oldcxt = MemoryContextSwitchTo(myState->cxt);
- tuplestore_puttuple(myState->tstore, ExecFetchSlotTuple(slot));
+ tuplestore_puttupleslot(myState->tstore, slot);
MemoryContextSwitchTo(oldcxt);
}
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 060ce567922..c79d0eae18d 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -911,21 +911,17 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
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);
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 6ff6b72082c..a2ed330ccc6 100644
--- a/src/backend/utils/sort/tuplestore.c
+++ b/src/backend/utils/sort/tuplestore.c
@@ -36,7 +36,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
@@ -195,6 +195,7 @@ struct Tuplestorestate
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);
@@ -304,15 +305,45 @@ tuplestore_ateof(Tuplestorestate *state)
* 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:
@@ -389,7 +420,7 @@ tuplestore_puttuple(Tuplestorestate *state, void *tuple)
* 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)
{
@@ -526,6 +557,59 @@ tuplestore_gettuple(Tuplestorestate *state, bool forward,
}
/*
+ * 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
*
* As a side effect, we must set readpos and markpos to the value
@@ -672,34 +756,31 @@ getlen(Tuplestorestate *state, bool eofOK)
/*
* 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,
@@ -707,23 +788,20 @@ writetup_heap(Tuplestorestate *state, void *tup)
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,