TupleTableSlot abstraction
Hi,
I've recently been discussing with Robert how to abstract
TupleTableSlots in the light of upcoming developments around abstracting
storage away. Besides that aspect I think we also need to make them
more abstract to later deal with vectorized excution, but I'm more fuzzy
on the details there.
I'd previously, in a response to an early version of the pluggable
storage patch, commented [1]http://archives.postgresql.org/message-id/20170815065348.afkj45dgmg22ecfh%40alap3.anarazel.de how I think the abstraction should roughly
look like. I'd privately started to prototype that. After my discussion
with Robert I got that prototype in a state that can run many queries,
but doesn't pass the regression tests.
The pluggable storage patch has also been updated [2]http://archives.postgresql.org/message-id/CAJrrPGdY_hgvu06R_vCibUktKRLp%3Db8nBfeZkh%3DKRc95tq4euA%40mail.gmail.com to abstract slots
more (see patch 0005).
My position is that there's a number of weaknesses with the current
TupleTableSlot API in light of multiple independent, possibly out of
core, storage implementations:
- TupleTableSlots have to contain all the necessary information for each
type of slot. Some implementations might require a buffer pin to be
hold (our heap), others pointers to multiple underlying points of data
(columns store), some need additional metadata (our MinimalTuple).
Unifying the information into one struct makes it much harder to
provide differing implementations without modifying core postgres
and/or increasing overhead for every user of slots.
I think the consequence of that is that we need to allow each type of
slot to have their own TupleTableSlot embedding struct.
Haribabu's patch solved this by adding a tts_storage parameter that
contains additional data, but imo that's unconvincing due to the added
indirection in very performance critical codepaths.
- The way slots currently are implemented each new variant data is
stored in slots adds new branches to hot code paths like
ExecClearTuple(), and they're not extensible.
Therefore I think we need to move to callback based implementation. In
my tests, by moving the callback invocations into very thin inline
functions, the branch prediction accuracy actually goes sligthly
*up*.
- Currently we frequently convert from one way of representing a row
into another, in the same slot. We do so fairly implicilty in
ExecMaterializeSlot(), ExecFetchSlotMinimalTuple(), ExecCopySlot()...
The more we abstract specific storage representation away, the worse I
think that is. I think we should make such conversions explicit by
requiring transfers between two slots if a specific type is required.
- We have a few codepaths that set fields on tuples that can't be
represented in virtual slots. E.g. postgres_fdw sets ctid, oid,
ExecProcessReturning() will set tableoid.
In my POC I turned TupleTableSlot into basically an abstract base class:
struct TupleTableSlot
{
NodeTag type;
uint16 flags;
uint16 nvalid; /* # of valid values in tts_values */
const TupleTableSlotOps *const cb;
TupleDesc tupleDescriptor; /* slot's tuple descriptor */
Datum *values; /* current per-attribute values */
bool *nulls; /* current per-attribute null flags */
/* can we optimize away? */
MemoryContext mcxt; /* slot itself is in this context */
};
which then is inherited from for specific implementations of tuple
slots:
typedef struct HeapTupleTableSlot
{
TupleTableSlot base;
HeapTuple tuple; /* physical tuple */
uint32 off; /* saved state for slot_deform_tuple */
} HeapTupleTableSlot;
...
typedef struct MinimalTupleTableSlot
{
TupleTableSlot base;
HeapTuple tuple; /* tuple wrapper */
MinimalTuple mintuple; /* minimal tuple, or NULL if none */
HeapTupleData minhdr; /* workspace for minimal-tuple-only case */
uint32 off; /* saved state for slot_deform_tuple */
} MinimalTupleTableSlot;
typedef struct TupleTableSlotOps
{
void (*init)(TupleTableSlot *slot);
void (*release)(TupleTableSlot *slot);
void (*clear)(TupleTableSlot *slot);
void (*getsomeattrs)(TupleTableSlot *slot, int natts);
void (*materialize)(TupleTableSlot *slot);
HeapTuple (*get_heap_tuple)(TupleTableSlot *slot);
MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot);
HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot);
MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);
} TupleTableSlotOps;
when creating a slot one has to specify the type of slot one wants to
use:
typedef enum TupleTableSlotType
{
TTS_TYPE_VIRTUAL,
TTS_TYPE_HEAPTUPLE,
TTS_TYPE_MINIMALTUPLE,
TTS_TYPE_BUFFER
} TupleTableSlotType;
extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc, TupleTableSlotType st);
You might notice that I've renamed a few fields (tts_values -> values,
tts_isnull -> nulls, tts_nvalid -> nvalid) that haven't actually changed
in the above structs / the attached patch. I now think that's probably
not a good idea, unnecessarily breaking more code than necessary.
I generally like this scheme because it allows the TupleTableStruct to
be relatively lean, without knowing much about specific implementations
of slots.
After this change functions like slot_getattr are thin inline wrappers:
+static inline Datum
+slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ Assert(attnum > 0);
+
+ if (attnum > slot->nvalid)
+ slot->cb->getsomeattrs(slot, attnum);
+
+ Assert(attnum <= slot->nvalid);
+
+ *isnull = slot->nulls[attnum - 1];
+ return slot->values[attnum - 1];
+}
Note that I've moved system attribute handling out of the slot_getattr
path - there were a few paths accessing them via slot_getattr, and it's
a lot of unnecessary branching.
The fact that slots of different types have different storage means that
we can't just change the type of a slot when needed. In nearly all
places that's not a problem. E.g. ExecHashTableInsert() converts the
"incoming" slot to one containing a minimal tuple with
ExecFetchSlotMinimalTuple(), but that's essentially transient as it's
just used to copy the tuple into the hashtable. We could even gain
quite some efficiency by directly copying into the allocated space
(saving one serialization/copy step for the common case where input is
either a virtual or a heap tuple).
The slightly bigger issue is that several parts of the code currently
require heaptuples they can scribble upon. E.g. nodeModifyTable.c
materializes the tuple - those can be solved by using a local slot to
store the tuple, rather than using the incoming one. In nearly all cases
we'd still be left with the same number of tuple copy steps, as
materializing the tuple with copy into the local storage anyway. A few
other cases, e.g. intorel_receive(), just can use ExecCopySlotTuple() or
such instead of materialize.
The biggest issue I don't have a good answer for, and where I don't like
Haribabu's solution, is how to deal with system columns like oid, ctid,
tableoid. Haribabu's patch adds storage of those to the slot, but that
seems like quite the cludge to me. It appears to me that the best thing
would be to require projections for all accesses to system columns, at
the original level of access. Then we don't need any sort of special
codepaths dealing with them. But that's not exactly a trivial change and
has some added complications too (junkfilter type things).
I made a reference to vectorized execution above. That's because that I
found, while prototyping vectorized execution, that it's a very common
need to interact with parts of the system that aren't vectorized, and
doing so has to be extremely cheap. With the above we can have a
TupleTableSlot type that's essentially just a cheap cursor into a batch
of tuples. Besides making it efficiently possible to hand of slots to
part of the system that can't deal with tuple batches, it allows to do
fun things like having the first slot_deform_tuple() call for one tuple
in a batch to deform a full page's worth of tuples, yielding *much*
better cache access patterns.
I haven't done that, but I think we should split ExecStoreTuple() into
multiple versions, that only work on specific types of tuple
slots. E.g. seqscan et al would call ExecStoreBufferHeapTuple(), other
code dealing with tuples would call ExecStoreHeapTuple(). The relevant
callers already need to know what kind of tuple they're dealing with,
therefore that's not a huge burden.
Please please note that the attached patch is *NOT* meant to be a full
proposal or even a to be reviewed patch, it's just an illustration of
the concepts I'm talking about.
Comments? Better ideas?
[1]: http://archives.postgresql.org/message-id/20170815065348.afkj45dgmg22ecfh%40alap3.anarazel.de
[2]: http://archives.postgresql.org/message-id/CAJrrPGdY_hgvu06R_vCibUktKRLp%3Db8nBfeZkh%3DKRc95tq4euA%40mail.gmail.com
Greetings,
Andres Freund
Attachments:
0001-tupleslot-rewrite.patchtext/x-diff; charset=us-asciiDownload
From 42e39640f24ee15b5596e7cbb6831785883ca627 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 20 Feb 2018 14:40:23 -0800
Subject: [PATCH] tupleslot-rewrite
---
src/backend/access/common/heaptuple.c | 268 +-----
src/backend/access/common/printsimple.c | 6 +-
src/backend/access/common/printtup.c | 24 +-
src/backend/catalog/index.c | 18 +-
src/backend/catalog/indexing.c | 3 +-
src/backend/catalog/partition.c | 2 +-
src/backend/commands/analyze.c | 3 +-
src/backend/commands/constraint.c | 3 +-
src/backend/commands/copy.c | 8 +-
src/backend/commands/createas.c | 2 +-
src/backend/commands/subscriptioncmds.c | 2 +-
src/backend/commands/tablecmds.c | 6 +-
src/backend/commands/trigger.c | 22 +-
src/backend/executor/execCurrent.c | 19 +-
src/backend/executor/execExpr.c | 7 +-
src/backend/executor/execExprInterp.c | 102 +--
src/backend/executor/execGrouping.c | 3 +-
src/backend/executor/execIndexing.c | 3 +-
src/backend/executor/execJunk.c | 12 +-
src/backend/executor/execMain.c | 6 +-
src/backend/executor/execPartition.c | 7 +-
src/backend/executor/execReplication.c | 30 +-
src/backend/executor/execSRF.c | 4 +-
src/backend/executor/execScan.c | 4 +-
src/backend/executor/execTuples.c | 1048 ++++++++++++++++--------
src/backend/executor/execUtils.c | 6 +-
src/backend/executor/functions.c | 5 +-
src/backend/executor/nodeAgg.c | 39 +-
src/backend/executor/nodeAppend.c | 2 +-
src/backend/executor/nodeBitmapHeapscan.c | 6 +-
src/backend/executor/nodeCtescan.c | 5 +-
src/backend/executor/nodeCustom.c | 6 +-
src/backend/executor/nodeForeignscan.c | 9 +-
src/backend/executor/nodeFunctionscan.c | 21 +-
src/backend/executor/nodeGather.c | 5 +-
src/backend/executor/nodeGatherMerge.c | 5 +-
src/backend/executor/nodeGroup.c | 5 +-
src/backend/executor/nodeHash.c | 2 +-
src/backend/executor/nodeHashjoin.c | 13 +-
src/backend/executor/nodeIndexonlyscan.c | 12 +-
src/backend/executor/nodeIndexscan.c | 5 +-
src/backend/executor/nodeLimit.c | 3 +-
src/backend/executor/nodeLockRows.c | 2 +-
src/backend/executor/nodeMaterial.c | 4 +-
src/backend/executor/nodeMergeAppend.c | 3 +-
src/backend/executor/nodeMergejoin.c | 13 +-
src/backend/executor/nodeModifyTable.c | 27 +-
src/backend/executor/nodeNamedtuplestorescan.c | 5 +-
src/backend/executor/nodeNestloop.c | 5 +-
src/backend/executor/nodeProjectSet.c | 6 +-
src/backend/executor/nodeRecursiveunion.c | 2 +-
src/backend/executor/nodeResult.c | 2 +-
src/backend/executor/nodeSamplescan.c | 6 +-
src/backend/executor/nodeSeqscan.c | 5 +-
src/backend/executor/nodeSetOp.c | 2 +-
src/backend/executor/nodeSort.c | 4 +-
src/backend/executor/nodeSubplan.c | 12 +-
src/backend/executor/nodeSubqueryscan.c | 6 +-
src/backend/executor/nodeTableFuncscan.c | 16 +-
src/backend/executor/nodeTidscan.c | 5 +-
src/backend/executor/nodeUnique.c | 2 +-
src/backend/executor/nodeValuesscan.c | 12 +-
src/backend/executor/nodeWindowAgg.c | 24 +-
src/backend/executor/nodeWorktablescan.c | 4 +-
src/backend/executor/tstoreReceiver.c | 8 +-
src/backend/nodes/print.c | 2 +-
src/backend/replication/logical/tablesync.c | 4 +-
src/backend/replication/logical/worker.c | 60 +-
src/backend/tcop/pquery.c | 2 +-
src/backend/utils/adt/orderedsetaggs.c | 34 +-
src/backend/utils/adt/selfuncs.c | 3 +-
src/backend/utils/sort/tuplesort.c | 2 +-
src/include/access/htup_details.h | 5 +
src/include/executor/executor.h | 16 +-
src/include/executor/tuptable.h | 204 ++++-
src/include/nodes/execnodes.h | 3 +
76 files changed, 1306 insertions(+), 965 deletions(-)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 0a13251067f..5ef9ea5c483 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -1032,18 +1032,17 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
* re-computing information about previously extracted attributes.
* slot->tts_nvalid is the number of attributes already extracted.
*/
-static void
-slot_deform_tuple(TupleTableSlot *slot, int natts)
+void
+slot_deform_tuple(TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, int natts)
{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
+ TupleDesc tupleDesc = slot->tupleDescriptor;
+ Datum *values = slot->values;
+ bool *isnull = slot->nulls;
HeapTupleHeader tup = tuple->t_data;
bool hasnulls = HeapTupleHasNulls(tuple);
int attnum;
char *tp; /* ptr to tuple data */
- long off; /* offset in tuple data */
+ uint32 off; /* offset in tuple data */
bits8 *bp = tup->t_bits; /* ptr to null bitmap in tuple */
bool slow; /* can we use/set attcacheoff? */
@@ -1051,7 +1050,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
* Check whether the first call for this tuple, and initialize or restore
* loop state.
*/
- attnum = slot->tts_nvalid;
+ attnum = slot->nvalid;
if (attnum == 0)
{
/* Start from the first attribute */
@@ -1061,8 +1060,8 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
else
{
/* Restore state from previous execution */
- off = slot->tts_off;
- slow = slot->tts_slow;
+ off = *offp;
+ slow = (slot->flags & TTS_SLOW) != 0;
}
tp = (char *) tup + tup->t_hoff;
@@ -1121,249 +1120,12 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
/*
* Save state for next execution
*/
- slot->tts_nvalid = attnum;
- slot->tts_off = off;
- slot->tts_slow = slow;
-}
-
-/*
- * slot_getattr
- * This function fetches an attribute of the slot's current tuple.
- * It is functionally equivalent to heap_getattr, but fetches of
- * multiple attributes of the same tuple will be optimized better,
- * because we avoid O(N^2) behavior from multiple calls of
- * nocachegetattr(), even when attcacheoff isn't usable.
- *
- * A difference from raw heap_getattr is that attnums beyond the
- * slot's tupdesc's last attribute will be considered NULL even
- * when the physical tuple is longer than the tupdesc.
- */
-Datum
-slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
- HeapTupleHeader tup;
-
- /*
- * system attributes are handled by heap_getsysattr
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_getsysattr(tuple, attnum, tupleDesc, isnull);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- {
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
- }
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * return NULL if attnum is out of range according to the tuple
- *
- * (We have to check this separately because of various inheritance and
- * table-alteration scenarios: the tuple could be either longer or shorter
- * than the tupdesc.)
- */
- tup = tuple->t_data;
- if (attnum > HeapTupleHeaderGetNatts(tup))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * check if target attribute is null: no point in groveling through tuple
- */
- if (HeapTupleHasNulls(tuple) && att_isnull(attnum - 1, tup->t_bits))
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * If the attribute's column has been dropped, we force a NULL result.
- * This case should not happen in normal use, but it could happen if we
- * are executing a plan cached before the column was dropped.
- */
- if (TupleDescAttr(tupleDesc, attnum - 1)->attisdropped)
- {
- *isnull = true;
- return (Datum) 0;
- }
-
- /*
- * Extract the attribute, along with any preceding attributes.
- */
- slot_deform_tuple(slot, attnum);
-
- /*
- * The result is acquired from tts_values array.
- */
- *isnull = slot->tts_isnull[attnum - 1];
- return slot->tts_values[attnum - 1];
-}
-
-/*
- * slot_getallattrs
- * This function forces all the entries of the slot's Datum/isnull
- * arrays to be valid. The caller may then extract data directly
- * from those arrays instead of using slot_getattr.
- */
-void
-slot_getallattrs(TupleTableSlot *slot)
-{
- int tdesc_natts = slot->tts_tupleDescriptor->natts;
- int attnum;
- HeapTuple tuple;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid == tdesc_natts)
- return;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attnum = HeapTupleHeaderGetNatts(tuple->t_data);
- attnum = Min(attnum, tdesc_natts);
-
- slot_deform_tuple(slot, attnum);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attnum < tdesc_natts; attnum++)
- {
- slot->tts_values[attnum] = (Datum) 0;
- slot->tts_isnull[attnum] = true;
- }
- slot->tts_nvalid = tdesc_natts;
-}
-
-/*
- * slot_getsomeattrs
- * This function forces the entries of the slot's Datum/isnull
- * arrays to be valid at least up through the attnum'th entry.
- */
-void
-slot_getsomeattrs(TupleTableSlot *slot, int attnum)
-{
- HeapTuple tuple;
- int attno;
-
- /* Quick out if we have 'em all already */
- if (slot->tts_nvalid >= attnum)
- return;
-
- /* Check for caller error */
- if (attnum <= 0 || attnum > slot->tts_tupleDescriptor->natts)
- elog(ERROR, "invalid attribute number %d", attnum);
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- tuple = slot->tts_tuple;
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /*
- * load up any slots available from physical tuple
- */
- attno = HeapTupleHeaderGetNatts(tuple->t_data);
- attno = Min(attno, attnum);
-
- slot_deform_tuple(slot, attno);
-
- /*
- * If tuple doesn't have all the atts indicated by tupleDesc, read the
- * rest as null
- */
- for (; attno < attnum; attno++)
- {
- slot->tts_values[attno] = (Datum) 0;
- slot->tts_isnull[attno] = true;
- }
- slot->tts_nvalid = attnum;
-}
-
-/*
- * slot_attisnull
- * Detect whether an attribute of the slot is null, without
- * actually fetching it.
- */
-bool
-slot_attisnull(TupleTableSlot *slot, int attnum)
-{
- HeapTuple tuple = slot->tts_tuple;
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
-
- /*
- * system attributes are handled by heap_attisnull
- */
- if (attnum <= 0)
- {
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract system attribute from virtual tuple");
- if (tuple == &(slot->tts_minhdr)) /* internal error */
- elog(ERROR, "cannot extract system attribute from minimal tuple");
- return heap_attisnull(tuple, attnum);
- }
-
- /*
- * fast path if desired attribute already cached
- */
- if (attnum <= slot->tts_nvalid)
- return slot->tts_isnull[attnum - 1];
-
- /*
- * return NULL if attnum is out of range according to the tupdesc
- */
- if (attnum > tupleDesc->natts)
- return true;
-
- /*
- * otherwise we had better have a physical tuple (tts_nvalid should equal
- * natts in all virtual-tuple cases)
- */
- if (tuple == NULL) /* internal error */
- elog(ERROR, "cannot extract attribute from empty tuple slot");
-
- /* and let the tuple tell it */
- return heap_attisnull(tuple, attnum);
+ slot->nvalid = attnum;
+ *offp = off;
+ if (slow)
+ slot->flags |= TTS_SLOW;
+ else
+ slot->flags &= ~TTS_SLOW;
}
/*
diff --git a/src/backend/access/common/printsimple.c b/src/backend/access/common/printsimple.c
index 3c4d2277125..b3eb4f96757 100644
--- a/src/backend/access/common/printsimple.c
+++ b/src/backend/access/common/printsimple.c
@@ -58,7 +58,7 @@ printsimple_startup(DestReceiver *self, int operation, TupleDesc tupdesc)
bool
printsimple(TupleTableSlot *slot, DestReceiver *self)
{
- TupleDesc tupdesc = slot->tts_tupleDescriptor;
+ TupleDesc tupdesc = slot->tupleDescriptor;
StringInfoData buf;
int i;
@@ -74,13 +74,13 @@ printsimple(TupleTableSlot *slot, DestReceiver *self)
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
Datum value;
- if (slot->tts_isnull[i])
+ if (slot->nulls[i])
{
pq_sendint32(&buf, -1);
continue;
}
- value = slot->tts_values[i];
+ value = slot->values[i];
/*
* We can't call the regular type output functions here because we
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index a1d44157044..795b3f19a07 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -373,7 +373,7 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
static bool
printtup(TupleTableSlot *slot, DestReceiver *self)
{
- TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ TupleDesc typeinfo = slot->tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self;
MemoryContext oldcontext;
StringInfo buf = &myState->buf;
@@ -403,9 +403,9 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
- Datum attr = slot->tts_values[i];
+ Datum attr = slot->values[i];
- if (slot->tts_isnull[i])
+ if (slot->nulls[i])
{
pq_sendint32(buf, -1);
continue;
@@ -458,7 +458,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
static bool
printtup_20(TupleTableSlot *slot, DestReceiver *self)
{
- TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ TupleDesc typeinfo = slot->tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self;
MemoryContext oldcontext;
StringInfo buf = &myState->buf;
@@ -489,7 +489,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
k = 1 << 7;
for (i = 0; i < natts; ++i)
{
- if (!slot->tts_isnull[i])
+ if (!slot->nulls[i])
j |= k; /* set bit if not null */
k >>= 1;
if (k == 0) /* end of byte? */
@@ -508,10 +508,10 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
- Datum attr = slot->tts_values[i];
+ Datum attr = slot->values[i];
char *outputstr;
- if (slot->tts_isnull[i])
+ if (slot->nulls[i])
continue;
Assert(thisState->format == 0);
@@ -605,7 +605,7 @@ debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
bool
debugtup(TupleTableSlot *slot, DestReceiver *self)
{
- TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ TupleDesc typeinfo = slot->tupleDescriptor;
int natts = typeinfo->natts;
int i;
Datum attr;
@@ -643,7 +643,7 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
static bool
printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
{
- TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ TupleDesc typeinfo = slot->tupleDescriptor;
DR_printtup *myState = (DR_printtup *) self;
MemoryContext oldcontext;
StringInfo buf = &myState->buf;
@@ -674,7 +674,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
k = 1 << 7;
for (i = 0; i < natts; ++i)
{
- if (!slot->tts_isnull[i])
+ if (!slot->nulls[i])
j |= k; /* set bit if not null */
k >>= 1;
if (k == 0) /* end of byte? */
@@ -693,10 +693,10 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
- Datum attr = slot->tts_values[i];
+ Datum attr = slot->values[i];
bytea *outputbytes;
- if (slot->tts_isnull[i])
+ if (slot->nulls[i])
continue;
Assert(thisState->format == 1);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f2cb6d7fb81..8fa19526214 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1964,7 +1964,14 @@ FormIndexDatum(IndexInfo *indexInfo,
Datum iDatum;
bool isNull;
- if (keycol != 0)
+ if (keycol < 0)
+ {
+ iDatum = heap_getsysattr(((HeapTupleTableSlot *) slot)->tuple,
+ keycol,
+ slot->tupleDescriptor,
+ &isNull);
+ }
+ else if (keycol != 0)
{
/*
* Plain index column; get the value we need directly from the
@@ -2445,7 +2452,8 @@ IndexBuildHeapRangeScan(Relation heapRelation,
*/
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
- slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
+ TTS_TYPE_HEAPTUPLE);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
@@ -2900,7 +2908,8 @@ IndexCheckExclusion(Relation heapRelation,
*/
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
- slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
+ TTS_TYPE_HEAPTUPLE);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
@@ -3218,7 +3227,8 @@ validate_index_heapscan(Relation heapRelation,
*/
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
- slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
+ TTS_TYPE_HEAPTUPLE);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index a84b7da114a..748f0a98cdf 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -95,7 +95,8 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
heapRelation = indstate->ri_RelationDesc;
/* Need a slot to hold the tuple being examined */
- slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRelation),
+ TTS_TYPE_HEAPTUPLE);
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
/*
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 31c80c7f1ad..be31dcc57db 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1338,7 +1338,7 @@ check_default_allows_bound(Relation parent, Relation default_rel,
econtext = GetPerTupleExprContext(estate);
snapshot = RegisterSnapshot(GetLatestSnapshot());
scan = heap_beginscan(part_rel, snapshot, 0, NULL);
- tupslot = MakeSingleTupleTableSlot(tupdesc);
+ tupslot = MakeSingleTupleTableSlot(tupdesc, TTS_TYPE_HEAPTUPLE);
/*
* Switch to per-tuple memory context and reset it for each tuple
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5f21fcb5f40..d49068f0966 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -783,7 +783,8 @@ compute_index_stats(Relation onerel, double totalrows,
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
/* Need a slot to hold the current heap tuple, too */
- slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(onerel),
+ TTS_TYPE_HEAPTUPLE);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 90f19ad3dd9..2d77f475319 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -122,7 +122,8 @@ unique_key_recheck(PG_FUNCTION_ARGS)
/*
* The heap tuple must be put into a slot for FormIndexDatum.
*/
- slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(trigdata->tg_relation),
+ TTS_TYPE_HEAPTUPLE);
ExecStoreTuple(new_row, slot, InvalidBuffer, false);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index d5883c98d15..27aae408ac6 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -2444,9 +2444,11 @@ CopyFrom(CopyState cstate)
estate->es_range_table = cstate->range_table;
/* Set up a tuple slot too */
- myslot = ExecInitExtraTupleSlot(estate, tupDesc);
+ myslot = ExecInitExtraTupleSlot(estate, tupDesc,
+ TTS_TYPE_HEAPTUPLE);
/* Triggers might need a slot as well */
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL);
+ estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
+ TTS_TYPE_HEAPTUPLE);
/* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery();
@@ -4747,7 +4749,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self)
slot_getallattrs(slot);
/* And send the data */
- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull);
+ CopyOneRowTo(cstate, InvalidOid, slot->values, slot->nulls);
myState->processed++;
return true;
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 3d82edbf581..fbb84382280 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -588,7 +588,7 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
* get the heap tuple out of the tuple table slot, making sure we have a
* writable copy
*/
- tuple = ExecMaterializeSlot(slot);
+ tuple = ExecCopySlotTuple(slot);
/*
* force assignment of new OID (see comments in ExecInsert)
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 9de59693022..2c2cc4f33d5 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -1148,7 +1148,7 @@ fetch_table_list(WalReceiverConn *wrconn, List *publications)
res->err)));
/* Process tables. */
- slot = MakeSingleTupleTableSlot(res->tupledesc);
+ slot = MakeSingleTupleTableSlot(res->tupledesc, TTS_TYPE_MINIMALTUPLE);
while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
{
char *nspname;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 89454d8e80f..6cca6a4b65e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4588,8 +4588,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
* tuples are the same, the tupDescs might not be (consider ADD COLUMN
* without a default).
*/
- oldslot = MakeSingleTupleTableSlot(oldTupDesc);
- newslot = MakeSingleTupleTableSlot(newTupDesc);
+ oldslot = MakeSingleTupleTableSlot(oldTupDesc, TTS_TYPE_HEAPTUPLE);
+ newslot = MakeSingleTupleTableSlot(newTupDesc, TTS_TYPE_HEAPTUPLE);
/* Preallocate values/isnull arrays */
i = Max(newTupDesc->natts, oldTupDesc->natts);
@@ -8280,7 +8280,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
econtext = GetPerTupleExprContext(estate);
tupdesc = RelationGetDescr(rel);
- slot = MakeSingleTupleTableSlot(tupdesc);
+ slot = MakeSingleTupleTableSlot(tupdesc, TTS_TYPE_HEAPTUPLE);
econtext->ecxt_scantuple = slot;
snapshot = RegisterSnapshot(GetLatestSnapshot());
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index fffc0095a79..aeeae171115 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2342,7 +2342,7 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *newslot = estate->es_trig_tuple_slot;
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
- if (newslot->tts_tupleDescriptor != tupdesc)
+ if (newslot->tupleDescriptor != tupdesc)
ExecSetSlotDescriptor(newslot, tupdesc);
ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
slot = newslot;
@@ -2423,7 +2423,7 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *newslot = estate->es_trig_tuple_slot;
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
- if (newslot->tts_tupleDescriptor != tupdesc)
+ if (newslot->tupleDescriptor != tupdesc)
ExecSetSlotDescriptor(newslot, tupdesc);
ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
slot = newslot;
@@ -2829,7 +2829,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TupleTableSlot *newslot = estate->es_trig_tuple_slot;
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
- if (newslot->tts_tupleDescriptor != tupdesc)
+ if (newslot->tupleDescriptor != tupdesc)
ExecSetSlotDescriptor(newslot, tupdesc);
ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
slot = newslot;
@@ -2937,7 +2937,7 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
TupleTableSlot *newslot = estate->es_trig_tuple_slot;
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
- if (newslot->tts_tupleDescriptor != tupdesc)
+ if (newslot->tupleDescriptor != tupdesc)
ExecSetSlotDescriptor(newslot, tupdesc);
ExecStoreTuple(newtuple, newslot, InvalidBuffer, false);
slot = newslot;
@@ -3252,11 +3252,11 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
{
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
estate->es_trig_oldtup_slot =
- ExecInitExtraTupleSlot(estate, NULL);
+ ExecInitExtraTupleSlot(estate, NULL, TTS_TYPE_HEAPTUPLE);
MemoryContextSwitchTo(oldContext);
}
oldslot = estate->es_trig_oldtup_slot;
- if (oldslot->tts_tupleDescriptor != tupdesc)
+ if (oldslot->tupleDescriptor != tupdesc)
ExecSetSlotDescriptor(oldslot, tupdesc);
ExecStoreTuple(oldtup, oldslot, InvalidBuffer, false);
}
@@ -3266,11 +3266,11 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
{
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
estate->es_trig_newtup_slot =
- ExecInitExtraTupleSlot(estate, NULL);
+ ExecInitExtraTupleSlot(estate, NULL, TTS_TYPE_HEAPTUPLE);
MemoryContextSwitchTo(oldContext);
}
newslot = estate->es_trig_newtup_slot;
- if (newslot->tts_tupleDescriptor != tupdesc)
+ if (newslot->tupleDescriptor != tupdesc)
ExecSetSlotDescriptor(newslot, tupdesc);
ExecStoreTuple(newtup, newslot, InvalidBuffer, false);
}
@@ -4273,8 +4273,10 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
ExecDropSingleTupleTableSlot(slot1);
ExecDropSingleTupleTableSlot(slot2);
}
- slot1 = MakeSingleTupleTableSlot(rel->rd_att);
- slot2 = MakeSingleTupleTableSlot(rel->rd_att);
+ slot1 = MakeSingleTupleTableSlot(rel->rd_att,
+ TTS_TYPE_HEAPTUPLE);
+ slot2 = MakeSingleTupleTableSlot(rel->rd_att,
+ TTS_TYPE_HEAPTUPLE);
}
if (trigdesc == NULL) /* should not happen */
elog(ERROR, "relation %u has no triggers",
diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index ce7d4ac592a..2574639ecef 100644
--- a/src/backend/executor/execCurrent.c
+++ b/src/backend/executor/execCurrent.c
@@ -13,6 +13,7 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "utils/builtins.h"
@@ -183,16 +184,22 @@ execCurrentOf(CurrentOfExpr *cexpr,
if (TupIsNull(scanstate->ss_ScanTupleSlot))
return false;
+#if 0
/* Use slot_getattr to catch any possible mistakes */
tuple_tableoid =
- DatumGetObjectId(slot_getattr(scanstate->ss_ScanTupleSlot,
- TableOidAttributeNumber,
- &lisnull));
+ DatumGetObjectId(heap_getsysattr(scanstate->ss_ScanTupleSlot->tts_tuple,
+ TableOidAttributeNumber,
+ scanstate->ss_ScanTupleSlot->tts_tupleDescriptor,
+ &lisnull));
Assert(!lisnull);
tuple_tid = (ItemPointer)
- DatumGetPointer(slot_getattr(scanstate->ss_ScanTupleSlot,
- SelfItemPointerAttributeNumber,
- &lisnull));
+ DatumGetPointer(heap_getsysattr(scanstate->ss_ScanTupleSlot->tts_tuple,
+ SelfItemPointerAttributeNumber,
+ scanstate->ss_ScanTupleSlot->tts_tupleDescriptor,
+ &lisnull));
+#else
+ elog(ERROR, "not yet");
+#endif
Assert(!lisnull);
Assert(tuple_tableoid == table_oid);
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index db5fcafbfe4..c6e478c3650 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2415,7 +2415,8 @@ ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state)
scratch->d.wholerow.junkFilter =
ExecInitJunkFilter(subplan->plan->targetlist,
ExecGetResultType(subplan)->tdhasoid,
- ExecInitExtraTupleSlot(parent->state, NULL));
+ ExecInitExtraTupleSlot(parent->state, NULL,
+ TTS_TYPE_VIRTUAL));
}
}
}
@@ -2994,8 +2995,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
/*
* DISTINCT and/or ORDER BY case, with multiple columns sorted on.
*/
- Datum *values = pertrans->sortslot->tts_values;
- bool *nulls = pertrans->sortslot->tts_isnull;
+ Datum *values = pertrans->sortslot->values;
+ bool *nulls = pertrans->sortslot->nulls;
strictnulls = nulls;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 771b7e3945c..e7c2b9fb43c 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -455,9 +455,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* directly out of the slot's decomposed-data arrays. But let's
* have an Assert to check that that did happen.
*/
- Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
- *op->resvalue = innerslot->tts_values[attnum];
- *op->resnull = innerslot->tts_isnull[attnum];
+ Assert(attnum >= 0 && attnum < innerslot->nvalid);
+ *op->resvalue = innerslot->values[attnum];
+ *op->resnull = innerslot->nulls[attnum];
EEO_NEXT();
}
@@ -468,9 +468,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* See EEOP_INNER_VAR comments */
- Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
- *op->resvalue = outerslot->tts_values[attnum];
- *op->resnull = outerslot->tts_isnull[attnum];
+ Assert(attnum >= 0 && attnum < outerslot->nvalid);
+ *op->resvalue = outerslot->values[attnum];
+ *op->resnull = outerslot->nulls[attnum];
EEO_NEXT();
}
@@ -481,9 +481,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* See EEOP_INNER_VAR comments */
- Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
- *op->resvalue = scanslot->tts_values[attnum];
- *op->resnull = scanslot->tts_isnull[attnum];
+ Assert(attnum >= 0 && attnum < scanslot->nvalid);
+ *op->resvalue = scanslot->values[attnum];
+ *op->resnull = scanslot->nulls[attnum];
EEO_NEXT();
}
@@ -494,12 +494,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(innerslot->tts_tuple != NULL);
- Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ //Assert(innerslot->tts_tuple != NULL);
+ //Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(innerslot->tts_tuple, attnum,
- innerslot->tts_tupleDescriptor,
+ d = heap_getsysattr(((HeapTupleTableSlot *)innerslot)->tuple, attnum,
+ innerslot->tupleDescriptor,
op->resnull);
*op->resvalue = d;
@@ -512,12 +512,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(outerslot->tts_tuple != NULL);
- Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
+ //Assert(outerslot->tts_tuple != NULL);
+ //Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(outerslot->tts_tuple, attnum,
- outerslot->tts_tupleDescriptor,
+ d = heap_getsysattr(((HeapTupleTableSlot *)outerslot)->tuple, attnum,
+ outerslot->tupleDescriptor,
op->resnull);
*op->resvalue = d;
@@ -530,12 +530,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Datum d;
/* these asserts must match defenses in slot_getattr */
- Assert(scanslot->tts_tuple != NULL);
- Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ //Assert(scanslot->tts_tuple != NULL);
+ //Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
/* heap_getsysattr has sufficient defenses against bad attnums */
- d = heap_getsysattr(scanslot->tts_tuple, attnum,
- scanslot->tts_tupleDescriptor,
+ d = heap_getsysattr(((HeapTupleTableSlot *)scanslot)->tuple, attnum,
+ scanslot->tupleDescriptor,
op->resnull);
*op->resvalue = d;
@@ -559,9 +559,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* We do not need CheckVarSlotCompatibility here; that was taken
* care of at compilation time. But see EEOP_INNER_VAR comments.
*/
- Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
- resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
- resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+ Assert(attnum >= 0 && attnum < innerslot->nvalid);
+ resultslot->values[resultnum] = innerslot->values[attnum];
+ resultslot->nulls[resultnum] = innerslot->nulls[attnum];
EEO_NEXT();
}
@@ -575,9 +575,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* We do not need CheckVarSlotCompatibility here; that was taken
* care of at compilation time. But see EEOP_INNER_VAR comments.
*/
- Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
- resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
- resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+ Assert(attnum >= 0 && attnum < outerslot->nvalid);
+ resultslot->values[resultnum] = outerslot->values[attnum];
+ resultslot->nulls[resultnum] = outerslot->nulls[attnum];
EEO_NEXT();
}
@@ -591,9 +591,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
* We do not need CheckVarSlotCompatibility here; that was taken
* care of at compilation time. But see EEOP_INNER_VAR comments.
*/
- Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
- resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
- resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+ Assert(attnum >= 0 && attnum < scanslot->nvalid);
+ resultslot->values[resultnum] = scanslot->values[attnum];
+ resultslot->nulls[resultnum] = scanslot->nulls[attnum];
EEO_NEXT();
}
@@ -602,8 +602,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
int resultnum = op->d.assign_tmp.resultnum;
- resultslot->tts_values[resultnum] = state->resvalue;
- resultslot->tts_isnull[resultnum] = state->resnull;
+ resultslot->values[resultnum] = state->resvalue;
+ resultslot->nulls[resultnum] = state->resnull;
EEO_NEXT();
}
@@ -612,12 +612,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
int resultnum = op->d.assign_tmp.resultnum;
- resultslot->tts_isnull[resultnum] = state->resnull;
- if (!resultslot->tts_isnull[resultnum])
- resultslot->tts_values[resultnum] =
+ resultslot->nulls[resultnum] = state->resnull;
+ if (!resultslot->nulls[resultnum])
+ resultslot->values[resultnum] =
MakeExpandedObjectReadOnlyInternal(state->resvalue);
else
- resultslot->tts_values[resultnum] = state->resvalue;
+ resultslot->values[resultnum] = state->resvalue;
EEO_NEXT();
}
@@ -1897,7 +1897,7 @@ CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype)
*/
if (attnum > 0)
{
- TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
+ TupleDesc slot_tupdesc = slot->tupleDescriptor;
Form_pg_attribute attr;
if (attnum > slot_tupdesc->natts) /* should never happen */
@@ -2050,8 +2050,8 @@ ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
* step explicitly, and we also needn't Assert that the attnum is in range
* --- slot_getattr() will take care of any problems.
*/
- outslot->tts_values[resultnum] =
- slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ outslot->values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->nulls[resultnum]);
return 0;
}
@@ -2066,8 +2066,8 @@ ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
TupleTableSlot *outslot = state->resultslot;
/* See comments in ExecJustAssignInnerVar */
- outslot->tts_values[resultnum] =
- slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ outslot->values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->nulls[resultnum]);
return 0;
}
@@ -2082,8 +2082,8 @@ ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
TupleTableSlot *outslot = state->resultslot;
/* See comments in ExecJustAssignInnerVar */
- outslot->tts_values[resultnum] =
- slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ outslot->values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->nulls[resultnum]);
return 0;
}
@@ -3858,7 +3858,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
var_tupdesc = lookup_rowtype_tupdesc_domain(variable->vartype,
-1, false);
- slot_tupdesc = slot->tts_tupleDescriptor;
+ slot_tupdesc = slot->tupleDescriptor;
if (var_tupdesc->natts != slot_tupdesc->natts)
ereport(ERROR,
@@ -3911,7 +3911,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
* column names below.
*/
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
+ output_tupdesc = CreateTupleDescCopy(slot->tupleDescriptor);
MemoryContextSwitchTo(oldcontext);
}
@@ -3963,7 +3963,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
if (op->d.wholerow.slow)
{
/* Check to see if any dropped attributes are non-null */
- TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ TupleDesc tupleDesc = slot->tupleDescriptor;
TupleDesc var_tupdesc = op->d.wholerow.tupdesc;
int i;
@@ -3976,7 +3976,7 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
if (!vattr->attisdropped)
continue; /* already checked non-dropped cols */
- if (slot->tts_isnull[i])
+ if (slot->nulls[i])
continue; /* null is always okay */
if (vattr->attlen != sattr->attlen ||
vattr->attalign != sattr->attalign)
@@ -3993,9 +3993,9 @@ ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
*
* (Note: it is critical that we not change the slot's state here.)
*/
- tuple = toast_build_flattened_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
+ tuple = toast_build_flattened_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
dtuple = tuple->t_data;
/*
@@ -4099,7 +4099,7 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
int setno = op->d.agg_trans.setno;
ExecClearTuple(pertrans->sortslot);
- pertrans->sortslot->tts_nvalid = pertrans->numInputs;
+ pertrans->sortslot->nvalid = pertrans->numInputs;
ExecStoreVirtualTuple(pertrans->sortslot);
tuplesort_puttupleslot(pertrans->sortstates[setno], pertrans->sortslot);
}
diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index c4d0e040587..9be9da4b161 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -202,7 +202,8 @@ BuildTupleHashTable(PlanState *parent,
* We copy the input tuple descriptor just for safety --- we assume all
* input tuples will have equivalent descriptors.
*/
- hashtable->tableslot = MakeSingleTupleTableSlot(CreateTupleDescCopy(inputDesc));
+ hashtable->tableslot = MakeSingleTupleTableSlot(CreateTupleDescCopy(inputDesc),
+ TTS_TYPE_MINIMALTUPLE);
/* build comparator for all columns */
hashtable->tab_eq_func = ExecBuildGroupingEqual(inputDesc, inputDesc,
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 62e51f1ef3b..4403fd7fa3e 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -706,7 +706,8 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
* to this slot. Be sure to save and restore caller's value for
* scantuple.
*/
- existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap));
+ existing_slot = MakeSingleTupleTableSlot(RelationGetDescr(heap),
+ TTS_TYPE_HEAPTUPLE);
econtext = GetPerTupleExprContext(estate);
save_scantuple = econtext->ecxt_scantuple;
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index 57d74e57c1a..1abc2630b94 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -78,7 +78,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
if (slot)
ExecSetSlotDescriptor(slot, cleanTupType);
else
- slot = MakeSingleTupleTableSlot(cleanTupType);
+ slot = MakeSingleTupleTableSlot(cleanTupType, TTS_TYPE_VIRTUAL);
/*
* Now calculate the mapping between the original tuple's attributes and
@@ -149,7 +149,7 @@ ExecInitJunkFilterConversion(List *targetList,
if (slot)
ExecSetSlotDescriptor(slot, cleanTupType);
else
- slot = MakeSingleTupleTableSlot(cleanTupType);
+ slot = MakeSingleTupleTableSlot(cleanTupType, TTS_TYPE_VIRTUAL);
/*
* Calculate the mapping between the original tuple's attributes and the
@@ -275,8 +275,8 @@ ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
* Extract all the values of the old tuple.
*/
slot_getallattrs(slot);
- old_values = slot->tts_values;
- old_isnull = slot->tts_isnull;
+ old_values = slot->values;
+ old_isnull = slot->nulls;
/*
* get info from the junk filter
@@ -290,8 +290,8 @@ ExecFilterJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
* Prepare to build a virtual result tuple.
*/
ExecClearTuple(resultSlot);
- values = resultSlot->tts_values;
- isnull = resultSlot->tts_isnull;
+ values = resultSlot->values;
+ isnull = resultSlot->nulls;
/*
* Transpose data into proper fields of the new tuple.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 91ba939bdca..a93cfe963cb 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1073,7 +1073,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
j = ExecInitJunkFilter(planstate->plan->targetlist,
tupType->tdhasoid,
- ExecInitExtraTupleSlot(estate, NULL));
+ ExecInitExtraTupleSlot(estate, NULL, TTS_TYPE_VIRTUAL));
estate->es_junkFilter = j;
/* Want to return the cleaned tuple type */
@@ -2319,7 +2319,7 @@ ExecBuildSlotValueDescription(Oid reloid,
if (table_perm || column_perm)
{
- if (slot->tts_isnull[i])
+ if (slot->nulls[i])
val = "null";
else
{
@@ -2328,7 +2328,7 @@ ExecBuildSlotValueDescription(Oid reloid,
getTypeOutputInfo(att->atttypid,
&foutoid, &typisvarlena);
- val = OidOutputFunctionCall(foutoid, slot->tts_values[i]);
+ val = OidOutputFunctionCall(foutoid, slot->values[i]);
}
if (write_comma)
diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c
index 00523ce250d..04979eb7e75 100644
--- a/src/backend/executor/execPartition.c
+++ b/src/backend/executor/execPartition.c
@@ -93,7 +93,7 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
* We need an additional tuple slot for storing transient tuples that
* are converted to the root table descriptor.
*/
- proute->root_tuple_slot = MakeTupleTableSlot(NULL);
+ proute->root_tuple_slot = MakeTupleTableSlot(NULL, TTS_TYPE_HEAPTUPLE);
}
else
{
@@ -112,7 +112,8 @@ ExecSetupPartitionTupleRouting(ModifyTableState *mtstate,
* (such as ModifyTableState) and released when the node finishes
* processing.
*/
- proute->partition_tuple_slot = MakeTupleTableSlot(NULL);
+ proute->partition_tuple_slot = MakeTupleTableSlot(NULL,
+ TTS_TYPE_HEAPTUPLE);
i = 0;
foreach(cell, leaf_parts)
@@ -590,7 +591,7 @@ get_partition_dispatch_recurse(Relation rel, Relation parent,
* using the correct tuple descriptor when computing its partition key
* for tuple routing.
*/
- pd->tupslot = MakeSingleTupleTableSlot(tupdesc);
+ pd->tupslot = MakeSingleTupleTableSlot(tupdesc, TTS_TYPE_HEAPTUPLE);
pd->tupmap = convert_tuples_by_name(RelationGetDescr(parent),
tupdesc,
gettext_noop("could not convert row type"));
diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 32891abbdf5..daf20589f4f 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -92,10 +92,10 @@ build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel,
pkattno,
BTEqualStrategyNumber,
regop,
- searchslot->tts_values[mainattno - 1]);
+ searchslot->values[mainattno - 1]);
/* Check for null value. */
- if (searchslot->tts_isnull[mainattno - 1])
+ if (searchslot->nulls[mainattno - 1])
{
hasnulls = true;
skey[attoff].sk_flags |= SK_ISNULL;
@@ -171,7 +171,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&((HeapTupleTableSlot *)outslot)->tuple->t_self, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -238,7 +238,7 @@ tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
* If one value is NULL and other is not, then they are certainly not
* equal
*/
- if (isnull[attrnum] != slot->tts_isnull[attrnum])
+ if (isnull[attrnum] != slot->nulls[attrnum])
return false;
/*
@@ -258,7 +258,7 @@ tuple_equals_slot(TupleDesc desc, HeapTuple tup, TupleTableSlot *slot)
if (!DatumGetBool(FunctionCall2(&typentry->eq_opr_finfo,
values[attrnum],
- slot->tts_values[attrnum])))
+ slot->values[attrnum])))
return false;
}
@@ -286,7 +286,7 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
bool found;
TupleDesc desc = RelationGetDescr(rel);
- Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
+ Assert(equalTupleDescs(desc, outslot->tupleDescriptor));
/* Start a heap scan. */
InitDirtySnapshot(snap);
@@ -329,7 +329,7 @@ retry:
HTSU_Result res;
HeapTupleData locktup;
- ItemPointerCopy(&outslot->tts_tuple->t_self, &locktup.t_self);
+ ItemPointerCopy(&((HeapTupleTableSlot *)outslot)->tuple->t_self, &locktup.t_self);
PushActiveSnapshot(GetLatestSnapshot());
@@ -453,7 +453,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_update_before_row)
{
slot = ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ &((HeapTupleTableSlot *) searchslot)->tuple->t_self,
NULL, slot);
if (slot == NULL) /* "do nothing" */
@@ -472,18 +472,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate,
tuple = ExecMaterializeSlot(slot);
/* OK, update the tuple and index entries for it */
- simple_heap_update(rel, &searchslot->tts_tuple->t_self,
- slot->tts_tuple);
+ simple_heap_update(rel, &((HeapTupleTableSlot *) searchslot)->tuple->t_self,
+ ((HeapTupleTableSlot *)slot)->tuple);
if (resultRelInfo->ri_NumIndices > 0 &&
- !HeapTupleIsHeapOnly(slot->tts_tuple))
+ !HeapTupleIsHeapOnly(((HeapTupleTableSlot *)slot)->tuple))
recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
estate, false, NULL,
NIL);
/* AFTER ROW UPDATE Triggers */
ExecARUpdateTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ &((HeapTupleTableSlot *)searchslot)->tuple->t_self,
NULL, tuple, recheckIndexes, NULL);
list_free(recheckIndexes);
@@ -514,7 +514,7 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
resultRelInfo->ri_TrigDesc->trig_delete_before_row)
{
skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
- &searchslot->tts_tuple->t_self,
+ &((HeapTupleTableSlot *)searchslot)->tuple->t_self,
NULL);
}
@@ -523,11 +523,11 @@ ExecSimpleRelationDelete(EState *estate, EPQState *epqstate,
List *recheckIndexes = NIL;
/* OK, delete the tuple */
- simple_heap_delete(rel, &searchslot->tts_tuple->t_self);
+ simple_heap_delete(rel, &((HeapTupleTableSlot *)searchslot)->tuple->t_self);
/* AFTER ROW DELETE Triggers */
ExecARDeleteTriggers(estate, resultRelInfo,
- &searchslot->tts_tuple->t_self, NULL, NULL);
+ &((HeapTupleTableSlot *)searchslot)->tuple->t_self, NULL, NULL);
list_free(recheckIndexes);
}
diff --git a/src/backend/executor/execSRF.c b/src/backend/executor/execSRF.c
index b97b8d797ec..163d8bb1183 100644
--- a/src/backend/executor/execSRF.c
+++ b/src/backend/executor/execSRF.c
@@ -509,7 +509,7 @@ restart:
* clearing the slot could end up trying to free something already
* freed.
*/
- oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
+ oldContext = MemoryContextSwitchTo(slot->mcxt);
foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false,
fcache->funcResultSlot);
MemoryContextSwitchTo(oldContext);
@@ -873,7 +873,7 @@ ExecPrepareTuplestoreResult(SetExprState *sexpr,
slotDesc = NULL; /* keep compiler quiet */
}
- sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
+ sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc, TTS_TYPE_MINIMALTUPLE);
MemoryContextSwitchTo(oldcontext);
}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index caf91730ce1..0cc264f2c41 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -235,7 +235,7 @@ void
ExecAssignScanProjectionInfo(ScanState *node)
{
Scan *scan = (Scan *) node->ps.plan;
- TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
+ TupleDesc tupdesc = node->ss_ScanTupleSlot->tupleDescriptor;
ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, scan->scanrelid);
}
@@ -247,7 +247,7 @@ ExecAssignScanProjectionInfo(ScanState *node)
void
ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno)
{
- TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
+ TupleDesc tupdesc = node->ss_ScanTupleSlot->tupleDescriptor;
ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, varno);
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index c46d65cf938..32bee3acd7e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -101,6 +101,560 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList,
* ----------------------------------------------------------------
*/
+static void
+tts_virtual_init(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_virtual_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_virtual_clear(TupleTableSlot *slot)
+{
+ slot->nvalid = 0;
+ slot->flags |= TTS_ISEMPTY;
+}
+
+static void
+tts_virtual_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ elog(ERROR, "should not be called");
+}
+
+static void
+tts_virtual_materialize(TupleTableSlot *slot)
+{
+ elog(ERROR, "materializing a virtual tuple");
+}
+
+static HeapTuple
+tts_virtual_get_heap_tuple(TupleTableSlot *slot)
+{
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ return heap_form_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+
+}
+
+static MinimalTuple
+tts_virtual_get_minimal_tuple(TupleTableSlot *slot)
+{
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ return heap_form_minimal_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+}
+
+static HeapTuple
+tts_virtual_copy_heap_tuple(TupleTableSlot *slot)
+{
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ return heap_form_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+
+}
+
+static MinimalTuple
+tts_virtual_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ return heap_form_minimal_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+}
+
+
+static void
+tts_heap_init(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_heap_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_heap_clear(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ if (slot->flags & TTS_SHOULDFREE)
+ {
+ heap_freetuple(hslot->tuple);
+ slot->flags &= ~TTS_SHOULDFREE;
+ }
+
+ slot->nvalid = 0;
+ slot->flags |= TTS_ISEMPTY;
+ hslot->off = 0;
+ hslot->tuple = NULL;
+}
+
+static void
+tts_heap_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ slot_deform_tuple(slot, hslot->tuple, &hslot->off, natts);
+}
+
+static void
+tts_heap_materialize(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+ MemoryContext oldContext;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ if (slot->flags & TTS_SHOULDFREE)
+ return;
+
+ slot->flags |= TTS_SHOULDFREE;
+ oldContext = MemoryContextSwitchTo(slot->mcxt);
+
+ if (!hslot->tuple)
+ hslot->tuple = heap_form_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+ else
+ hslot->tuple = heap_copytuple(hslot->tuple);
+
+ slot->nvalid = 0;
+ hslot->off = 0;
+
+ MemoryContextSwitchTo(oldContext);
+}
+
+static HeapTuple
+tts_heap_get_heap_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return hslot->tuple;
+}
+
+static MinimalTuple
+tts_heap_get_minimal_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(hslot->tuple);
+}
+
+static HeapTuple
+tts_heap_copy_heap_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return heap_copytuple(hslot->tuple);
+}
+
+static MinimalTuple
+tts_heap_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ if (!hslot->tuple)
+ tts_heap_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(hslot->tuple);
+}
+
+static void
+tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
+{
+ HeapTupleTableSlot *hslot = (HeapTupleTableSlot *) slot;
+
+ tts_heap_clear(slot);
+
+ slot->nvalid = 0;
+ hslot->tuple = tuple;
+ hslot->off = 0;
+ slot->flags &= ~TTS_ISEMPTY;
+
+ if (shouldFree)
+ slot->flags |= TTS_SHOULDFREE;
+}
+
+
+static void
+tts_minimal_init(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ mslot->tuple = &mslot->minhdr;
+}
+
+static void
+tts_minimal_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_minimal_clear(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ /* FIXME */
+ if (slot->flags & TTS_SHOULDFREE)
+ {
+ heap_free_minimal_tuple(mslot->mintuple);
+ slot->flags &= ~TTS_SHOULDFREE;
+ }
+
+ slot->nvalid = 0;
+ slot->flags |= TTS_ISEMPTY;
+ mslot->off = 0;
+ mslot->mintuple = NULL;
+}
+
+static void
+tts_minimal_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ slot_deform_tuple(slot, mslot->tuple, &mslot->off, natts);
+}
+
+static void
+tts_minimal_materialize(TupleTableSlot *slot)
+{
+ elog(ERROR, "materializing a minimal tuple");
+}
+
+static void
+tts_minimal_localize(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+ MemoryContext oldContext;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ if (slot->flags & TTS_SHOULDFREE)
+ return;
+
+ slot->flags |= TTS_SHOULDFREE;
+ oldContext = MemoryContextSwitchTo(slot->mcxt);
+
+ if (!mslot->mintuple)
+ mslot->mintuple = heap_form_minimal_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+ else
+ mslot->mintuple = heap_copy_minimal_tuple(mslot->mintuple);
+ mslot->tuple = &mslot->minhdr;
+ mslot->minhdr.t_len = mslot->mintuple->t_len + MINIMAL_TUPLE_OFFSET;
+ mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
+
+ MemoryContextSwitchTo(oldContext);
+
+ slot->nvalid = 0;
+ mslot->off = 0;
+}
+
+static HeapTuple
+tts_minimal_get_heap_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!mslot->mintuple)
+ tts_minimal_localize(slot);
+
+ return mslot->tuple;
+}
+
+static MinimalTuple
+tts_minimal_get_minimal_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!mslot->mintuple)
+ tts_minimal_localize(slot);
+
+ return mslot->mintuple;
+}
+
+static HeapTuple
+tts_minimal_copy_heap_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ if (!mslot->mintuple)
+ tts_minimal_localize(slot);
+
+ return heap_tuple_from_minimal_tuple(mslot->mintuple);
+}
+
+static MinimalTuple
+tts_minimal_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ if (!mslot->mintuple)
+ tts_minimal_localize(slot);
+
+ return heap_copy_minimal_tuple(mslot->mintuple);
+}
+
+static void
+tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree)
+{
+ MinimalTupleTableSlot *mslot = (MinimalTupleTableSlot *) slot;
+
+ tts_minimal_clear(slot);
+
+ slot->flags &= ~TTS_ISEMPTY;
+ slot->nvalid = 0;
+ mslot->off = 0;
+
+ mslot->mintuple = mtup;
+ mslot->tuple = &mslot->minhdr;
+ mslot->minhdr.t_len = mtup->t_len + MINIMAL_TUPLE_OFFSET;
+ mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mtup - MINIMAL_TUPLE_OFFSET);
+ /* no need to set t_self or t_tableOid since we won't allow access */
+
+ if (shouldFree)
+ slot->flags |= TTS_SHOULDFREE;
+ else
+ Assert(!(slot->flags & TTS_SHOULDFREE));
+}
+
+
+static void
+tts_buffer_init(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_buffer_release(TupleTableSlot *slot)
+{
+}
+
+static void
+tts_buffer_clear(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (slot->flags & TTS_SHOULDFREE)
+ {
+ heap_freetuple(bslot->base.tuple);
+ slot->flags &= ~TTS_SHOULDFREE;
+ }
+
+ if (BufferIsValid(bslot->buffer))
+ ReleaseBuffer(bslot->buffer);
+
+ slot->nvalid = 0;
+ slot->flags |= TTS_ISEMPTY;
+ bslot->base.tuple = NULL;
+ bslot->buffer = InvalidBuffer;
+
+}
+
+static void
+tts_buffer_getsomeattrs(TupleTableSlot *slot, int natts)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ slot_deform_tuple(slot, bslot->base.tuple, &bslot->base.off, natts);
+}
+
+static void
+tts_buffer_materialize(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+ MemoryContext oldContext;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+
+ if (slot->flags & TTS_SHOULDFREE)
+ return;
+
+ slot->flags |= TTS_SHOULDFREE;
+ oldContext = MemoryContextSwitchTo(slot->mcxt);
+
+ if (!bslot->base.tuple)
+ bslot->base.tuple = heap_form_tuple(slot->tupleDescriptor,
+ slot->values,
+ slot->nulls);
+ else
+ bslot->base.tuple = heap_copytuple(bslot->base.tuple);
+ MemoryContextSwitchTo(oldContext);
+
+
+ if (BufferIsValid(bslot->buffer))
+ {
+ ReleaseBuffer(bslot->buffer);
+ bslot->buffer = InvalidBuffer;
+ }
+
+ bslot->base.off = 0;
+ slot->nvalid = 0;
+
+}
+
+static HeapTuple
+tts_buffer_get_heap_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!bslot->base.tuple)
+ tts_buffer_materialize(slot);
+
+ return bslot->base.tuple;
+}
+
+static MinimalTuple
+tts_buffer_get_minimal_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!bslot->base.tuple)
+ tts_buffer_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(bslot->base.tuple);
+}
+
+static HeapTuple
+tts_buffer_copy_heap_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (!bslot->base.tuple)
+ tts_buffer_materialize(slot);
+
+ return heap_copytuple(bslot->base.tuple);
+}
+
+static MinimalTuple
+tts_buffer_copy_minimal_tuple(TupleTableSlot *slot)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (!bslot->base.tuple)
+ tts_buffer_materialize(slot);
+
+ return minimal_tuple_from_heap_tuple(bslot->base.tuple);
+}
+
+static void
+tts_buffer_store_tuple(TupleTableSlot *slot, HeapTuple tuple, Buffer buffer)
+{
+ BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+ if (slot->flags & TTS_SHOULDFREE)
+ {
+ heap_freetuple(bslot->base.tuple);
+ slot->flags &= ~TTS_SHOULDFREE;
+ }
+
+ slot->flags &= ~TTS_ISEMPTY;
+ slot->nvalid = 0;
+ bslot->base.tuple = tuple;
+ bslot->base.off = 0;
+
+ /*
+ * If tuple is on a disk page, keep the page pinned as long as we hold a
+ * pointer into it. We assume the caller already has such a pin.
+ *
+ * This is coded to optimize the case where the slot previously held a
+ * tuple on the same disk page: in that case releasing and re-acquiring
+ * the pin is a waste of cycles. This is a common situation during
+ * seqscans, so it's worth troubling over.
+ */
+ if (bslot->buffer != buffer)
+ {
+ if (BufferIsValid(bslot->buffer))
+ ReleaseBuffer(bslot->buffer);
+ bslot->buffer = buffer;
+ IncrBufferRefCount(buffer);
+ }
+
+}
+
+
+static const TupleTableSlotOps TTSOpsVirtual = {
+ tts_virtual_init,
+ tts_virtual_release,
+ tts_virtual_clear,
+ tts_virtual_getsomeattrs,
+ tts_virtual_materialize,
+ tts_virtual_get_heap_tuple,
+ tts_virtual_get_minimal_tuple,
+ tts_virtual_copy_heap_tuple,
+ tts_virtual_copy_minimal_tuple,
+};
+
+static const TupleTableSlotOps TTSOpsHeapTuple = {
+ tts_heap_init,
+ tts_heap_release,
+ tts_heap_clear,
+ tts_heap_getsomeattrs,
+ tts_heap_materialize,
+ tts_heap_get_heap_tuple,
+ tts_heap_get_minimal_tuple,
+ tts_heap_copy_heap_tuple,
+ tts_heap_copy_minimal_tuple,
+};
+
+static const TupleTableSlotOps TTSOpsMinimalTuple = {
+ tts_minimal_init,
+ tts_minimal_release,
+ tts_minimal_clear,
+ tts_minimal_getsomeattrs,
+ tts_minimal_materialize,
+ tts_minimal_get_heap_tuple,
+ tts_minimal_get_minimal_tuple,
+ tts_minimal_copy_heap_tuple,
+ tts_minimal_copy_minimal_tuple,
+};
+
+static const TupleTableSlotOps TTSOpsBufferTuple = {
+ tts_buffer_init,
+ tts_buffer_release,
+ tts_buffer_clear,
+ tts_buffer_getsomeattrs,
+ tts_buffer_materialize,
+ tts_buffer_get_heap_tuple,
+ tts_buffer_get_minimal_tuple,
+ tts_buffer_copy_heap_tuple,
+ tts_buffer_copy_minimal_tuple,
+};
+
+
/* --------------------------------
* MakeTupleTableSlot
*
@@ -110,50 +664,75 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList,
* --------------------------------
*/
TupleTableSlot *
-MakeTupleTableSlot(TupleDesc tupleDesc)
+MakeTupleTableSlot(TupleDesc tupleDesc, TupleTableSlotType st)
{
- Size sz;
+ Size basesz, allocsz;
TupleTableSlot *slot;
+ const TupleTableSlotOps *ops;
+
+ switch (st)
+ {
+ case TTS_TYPE_VIRTUAL:
+ ops = &TTSOpsVirtual;
+ basesz = sizeof(TupleTableSlot);
+ break;
+ case TTS_TYPE_HEAPTUPLE:
+ ops = &TTSOpsHeapTuple;
+ basesz = sizeof(HeapTupleTableSlot);
+ break;
+ case TTS_TYPE_MINIMALTUPLE:
+ ops = &TTSOpsMinimalTuple;
+ basesz = sizeof(MinimalTupleTableSlot);
+ break;
+ case TTS_TYPE_BUFFER:
+ ops = &TTSOpsBufferTuple;
+ basesz = sizeof(BufferHeapTupleTableSlot);
+ break;
+ }
+
+ /*
+ * XXX: Main body of this should be in separate initialization
+ * function so external types of slots can reuse.
+ */
/*
* When a fixed descriptor is specified, we can reduce overhead by
* allocating the entire slot in one go.
*/
if (tupleDesc)
- sz = MAXALIGN(sizeof(TupleTableSlot)) +
+ allocsz = MAXALIGN(basesz) +
MAXALIGN(tupleDesc->natts * sizeof(Datum)) +
MAXALIGN(tupleDesc->natts * sizeof(bool));
else
- sz = sizeof(TupleTableSlot);
+ allocsz = basesz;
- slot = palloc0(sz);
+ slot = palloc0(allocsz);
+ *((const TupleTableSlotOps **) &slot->cb) = ops;
slot->type = T_TupleTableSlot;
- slot->tts_isempty = true;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = NULL;
- slot->tts_fixedTupleDescriptor = tupleDesc != NULL;
- slot->tts_tupleDescriptor = tupleDesc;
- slot->tts_mcxt = CurrentMemoryContext;
- slot->tts_buffer = InvalidBuffer;
- slot->tts_nvalid = 0;
- slot->tts_values = NULL;
- slot->tts_isnull = NULL;
- slot->tts_mintuple = NULL;
+ slot->flags = TTS_ISEMPTY;
+ slot->flags |= tupleDesc != NULL ? TTS_FIXED : 0;
+ slot->tupleDescriptor = tupleDesc;
+ slot->mcxt = CurrentMemoryContext;
+ slot->nvalid = 0;
if (tupleDesc != NULL)
{
- slot->tts_values = (Datum *)
+ slot->values = (Datum *)
(((char *) slot)
- + MAXALIGN(sizeof(TupleTableSlot)));
- slot->tts_isnull = (bool *)
+ + MAXALIGN(basesz));
+ slot->nulls = (bool *)
(((char *) slot)
- + MAXALIGN(sizeof(TupleTableSlot))
+ + MAXALIGN(basesz)
+ MAXALIGN(tupleDesc->natts * sizeof(Datum)));
PinTupleDesc(tupleDesc);
}
+ /*
+ * And allow slot type specific initialization.
+ */
+ slot->cb->init(slot);
+
return slot;
}
@@ -164,9 +743,9 @@ MakeTupleTableSlot(TupleDesc tupleDesc)
* --------------------------------
*/
TupleTableSlot *
-ExecAllocTableSlot(List **tupleTable, TupleDesc desc)
+ExecAllocTableSlot(List **tupleTable, TupleDesc desc, TupleTableSlotType st)
{
- TupleTableSlot *slot = MakeTupleTableSlot(desc);
+ TupleTableSlot *slot = MakeTupleTableSlot(desc, st);
*tupleTable = lappend(*tupleTable, slot);
@@ -194,21 +773,21 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */
/* Always release resources and reset the slot to empty */
ExecClearTuple(slot);
- if (slot->tts_tupleDescriptor)
+ if (slot->tupleDescriptor)
{
- ReleaseTupleDesc(slot->tts_tupleDescriptor);
- slot->tts_tupleDescriptor = NULL;
+ ReleaseTupleDesc(slot->tupleDescriptor);
+ slot->tupleDescriptor = NULL;
}
/* If shouldFree, release memory occupied by the slot itself */
if (shouldFree)
{
- if (!slot->tts_fixedTupleDescriptor)
+ if (!(slot->flags & TTS_FIXED))
{
- if (slot->tts_values)
- pfree(slot->tts_values);
- if (slot->tts_isnull)
- pfree(slot->tts_isnull);
+ if (slot->values)
+ pfree(slot->values);
+ if (slot->nulls)
+ pfree(slot->nulls);
}
pfree(slot);
}
@@ -229,9 +808,9 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */
* --------------------------------
*/
TupleTableSlot *
-MakeSingleTupleTableSlot(TupleDesc tupdesc)
+MakeSingleTupleTableSlot(TupleDesc tupdesc, TupleTableSlotType st)
{
- TupleTableSlot *slot = MakeTupleTableSlot(tupdesc);
+ TupleTableSlot *slot = MakeTupleTableSlot(tupdesc, st);
return slot;
}
@@ -246,17 +825,18 @@ MakeSingleTupleTableSlot(TupleDesc tupdesc)
void
ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
{
+ slot->cb->release(slot);
/* This should match ExecResetTupleTable's processing of one slot */
Assert(IsA(slot, TupleTableSlot));
ExecClearTuple(slot);
- if (slot->tts_tupleDescriptor)
- ReleaseTupleDesc(slot->tts_tupleDescriptor);
- if (!slot->tts_fixedTupleDescriptor)
+ if (slot->tupleDescriptor)
+ ReleaseTupleDesc(slot->tupleDescriptor);
+ if (!(slot->flags & TTS_FIXED))
{
- if (slot->tts_values)
- pfree(slot->tts_values);
- if (slot->tts_isnull)
- pfree(slot->tts_isnull);
+ if (slot->values)
+ pfree(slot->values);
+ if (slot->nulls)
+ pfree(slot->nulls);
}
pfree(slot);
}
@@ -281,7 +861,7 @@ void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc) /* new tuple descriptor */
{
- Assert(!slot->tts_fixedTupleDescriptor);
+ Assert(!(slot->flags & TTS_FIXED));
/* For safety, make sure slot is empty before changing it */
ExecClearTuple(slot);
@@ -290,28 +870,28 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
* Release any old descriptor. Also release old Datum/isnull arrays if
* present (we don't bother to check if they could be re-used).
*/
- if (slot->tts_tupleDescriptor)
- ReleaseTupleDesc(slot->tts_tupleDescriptor);
+ if (slot->tupleDescriptor)
+ ReleaseTupleDesc(slot->tupleDescriptor);
- if (slot->tts_values)
- pfree(slot->tts_values);
- if (slot->tts_isnull)
- pfree(slot->tts_isnull);
+ if (slot->values)
+ pfree(slot->values);
+ if (slot->nulls)
+ pfree(slot->nulls);
/*
* Install the new descriptor; if it's refcounted, bump its refcount.
*/
- slot->tts_tupleDescriptor = tupdesc;
+ slot->tupleDescriptor = tupdesc;
PinTupleDesc(tupdesc);
/*
* Allocate Datum/isnull arrays of the appropriate size. These must have
* the same lifetime as the slot, so allocate in the slot's own context.
*/
- slot->tts_values = (Datum *)
- MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
- slot->tts_isnull = (bool *)
- MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool));
+ slot->values = (Datum *)
+ MemoryContextAlloc(slot->mcxt, tupdesc->natts * sizeof(Datum));
+ slot->nulls = (bool *)
+ MemoryContextAlloc(slot->mcxt, tupdesc->natts * sizeof(bool));
}
/* --------------------------------
@@ -363,51 +943,28 @@ ExecStoreTuple(HeapTuple tuple,
*/
Assert(tuple != NULL);
Assert(slot != NULL);
- Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tupleDescriptor != NULL);
/* passing shouldFree=true for a tuple on a disk page is not sane */
Assert(BufferIsValid(buffer) ? (!shouldFree) : true);
- /*
- * Free any old physical tuple belonging to the slot.
- */
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Store the new tuple into the specified slot.
- */
- slot->tts_isempty = false;
- slot->tts_shouldFree = shouldFree;
- slot->tts_shouldFreeMin = false;
- slot->tts_tuple = tuple;
- slot->tts_mintuple = NULL;
-
- /* Mark extracted state invalid */
- slot->tts_nvalid = 0;
-
- /*
- * If tuple is on a disk page, keep the page pinned as long as we hold a
- * pointer into it. We assume the caller already has such a pin.
- *
- * This is coded to optimize the case where the slot previously held a
- * tuple on the same disk page: in that case releasing and re-acquiring
- * the pin is a waste of cycles. This is a common situation during
- * seqscans, so it's worth troubling over.
- */
- if (slot->tts_buffer != buffer)
+ if (BufferIsValid(buffer))
{
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
- slot->tts_buffer = buffer;
- if (BufferIsValid(buffer))
- IncrBufferRefCount(buffer);
+ if (slot->cb != &TTSOpsBufferTuple)
+ elog(ERROR, "trying to store buffer tuple into wrong type of slot");
+ tts_buffer_store_tuple(slot, tuple, buffer);
+ }
+ else
+ {
+ if (slot->cb != &TTSOpsHeapTuple)
+ elog(ERROR, "trying to store heap tuple into wrong type of slot");
+ tts_heap_store_tuple(slot, tuple, shouldFree);
+
}
return slot;
}
+
/* --------------------------------
* ExecStoreMinimalTuple
*
@@ -426,88 +983,15 @@ ExecStoreMinimalTuple(MinimalTuple mtup,
*/
Assert(mtup != NULL);
Assert(slot != NULL);
- Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tupleDescriptor != NULL);
- /*
- * Free any old physical tuple belonging to the slot.
- */
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- /*
- * 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 = false;
- slot->tts_shouldFreeMin = 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;
+ if (slot->cb != &TTSOpsMinimalTuple)
+ elog(ERROR, "trying to store minimal tuple into wrong type of slot");
+ tts_minimal_store_tuple(slot, mtup, shouldFree);
return slot;
}
-/* --------------------------------
- * ExecClearTuple
- *
- * This function is used to clear out a slot in the tuple table.
- *
- * NB: only the tuple is cleared, not the tuple descriptor (if any).
- * --------------------------------
- */
-TupleTableSlot * /* return: slot passed */
-ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
-
- /*
- * Free the old physical tuple if necessary.
- */
- if (slot->tts_shouldFree)
- heap_freetuple(slot->tts_tuple);
- if (slot->tts_shouldFreeMin)
- heap_free_minimal_tuple(slot->tts_mintuple);
-
- slot->tts_tuple = NULL;
- slot->tts_mintuple = NULL;
- slot->tts_shouldFree = false;
- slot->tts_shouldFreeMin = false;
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
- * Mark it empty.
- */
- slot->tts_isempty = true;
- slot->tts_nvalid = 0;
-
- return slot;
-}
/* --------------------------------
* ExecStoreVirtualTuple
@@ -527,11 +1011,11 @@ ExecStoreVirtualTuple(TupleTableSlot *slot)
* sanity checks
*/
Assert(slot != NULL);
- Assert(slot->tts_tupleDescriptor != NULL);
- Assert(slot->tts_isempty);
+ Assert(slot->tupleDescriptor != NULL);
+ Assert(slot->flags & TTS_ISEMPTY);
- slot->tts_isempty = false;
- slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+ slot->flags &= ~TTS_ISEMPTY;
+ slot->nvalid = slot->tupleDescriptor->natts;
return slot;
}
@@ -551,7 +1035,7 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
* sanity checks
*/
Assert(slot != NULL);
- Assert(slot->tts_tupleDescriptor != NULL);
+ Assert(slot->tupleDescriptor != NULL);
/* Clear any old contents */
ExecClearTuple(slot);
@@ -559,82 +1043,14 @@ ExecStoreAllNullTuple(TupleTableSlot *slot)
/*
* Fill all the columns of the virtual tuple with nulls
*/
- MemSet(slot->tts_values, 0,
- slot->tts_tupleDescriptor->natts * sizeof(Datum));
- memset(slot->tts_isnull, true,
- slot->tts_tupleDescriptor->natts * sizeof(bool));
+ MemSet(slot->values, 0,
+ slot->tupleDescriptor->natts * sizeof(Datum));
+ memset(slot->nulls, true,
+ slot->tupleDescriptor->natts * sizeof(bool));
return ExecStoreVirtualTuple(slot);
}
-/* --------------------------------
- * ExecCopySlotTuple
- * Obtain a copy of a slot's regular physical tuple. The copy is
- * palloc'd in the current memory context.
- * The slot itself is undisturbed.
- *
- * This works even if the slot contains a virtual or minimal tuple;
- * however the "system columns" of the result will not be meaningful.
- * --------------------------------
- */
-HeapTuple
-ExecCopySlotTuple(TupleTableSlot *slot)
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(!slot->tts_isempty);
-
- /*
- * If we have a physical tuple (either format) then just copy it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return heap_copytuple(slot->tts_tuple);
- if (slot->tts_mintuple)
- return heap_tuple_from_minimal_tuple(slot->tts_mintuple);
-
- /*
- * Otherwise we need to build a tuple from the Datum array.
- */
- return heap_form_tuple(slot->tts_tupleDescriptor,
- slot->tts_values,
- slot->tts_isnull);
-}
-
-/* --------------------------------
- * ExecCopySlotMinimalTuple
- * Obtain a copy of a slot's minimal physical tuple. The copy is
- * palloc'd in the current memory context.
- * The slot itself is undisturbed.
- * --------------------------------
- */
-MinimalTuple
-ExecCopySlotMinimalTuple(TupleTableSlot *slot)
-{
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(!slot->tts_isempty);
-
- /*
- * If we have a physical tuple then just copy it. Prefer to copy
- * tts_mintuple since that's a tad cheaper.
- */
- if (slot->tts_mintuple)
- return heap_copy_minimal_tuple(slot->tts_mintuple);
- if (slot->tts_tuple)
- 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 regular physical tuple.
@@ -657,18 +1073,9 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
* sanity checks
*/
Assert(slot != NULL);
- Assert(!slot->tts_isempty);
+ Assert(!(slot->flags & TTS_ISEMPTY));
- /*
- * If we have a regular physical tuple then just return it.
- */
- if (TTS_HAS_PHYSICAL_TUPLE(slot))
- return slot->tts_tuple;
-
- /*
- * Otherwise materialize the slot...
- */
- return ExecMaterializeSlot(slot);
+ return slot->cb->get_heap_tuple(slot);
}
/* --------------------------------
@@ -687,14 +1094,23 @@ ExecFetchSlotTuple(TupleTableSlot *slot)
MinimalTuple
ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
{
+#ifdef NOT_ANYMORE
MemoryContext oldContext;
+#endif
/*
* sanity checks
*/
Assert(slot != NULL);
- Assert(!slot->tts_isempty);
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ if (slot->cb == &TTSOpsMinimalTuple)
+ return tts_minimal_get_minimal_tuple(slot);
+ else
+ /* XXX: this'll copy! */
+ return slot->cb->get_minimal_tuple(slot);
+
+#ifdef NOT_ANYMORE
/*
* If we have a minimal physical tuple (local or not) then just return it.
*/
@@ -721,6 +1137,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot)
*/
return slot->tts_mintuple;
+#endif
}
/* --------------------------------
@@ -738,85 +1155,12 @@ ExecFetchSlotTupleDatum(TupleTableSlot *slot)
/* Fetch slot's contents in regular-physical-tuple form */
tup = ExecFetchSlotTuple(slot);
- tupdesc = slot->tts_tupleDescriptor;
+ tupdesc = slot->tupleDescriptor;
/* Convert to Datum form */
return heap_copy_tuple_as_datum(tup, tupdesc);
}
-/* --------------------------------
- * ExecMaterializeSlot
- * Force a slot into the "materialized" state.
- *
- * This causes the slot's tuple to be a local copy not dependent on
- * any external storage. A pointer to the contained tuple is returned.
- *
- * A typical use for this operation is to prepare a computed tuple
- * for being stored on disk. The original data may or may not be
- * virtual, but in any case we need a private copy for heap_insert
- * to scribble on.
- * --------------------------------
- */
-HeapTuple
-ExecMaterializeSlot(TupleTableSlot *slot)
-{
- MemoryContext oldContext;
-
- /*
- * sanity checks
- */
- Assert(slot != NULL);
- Assert(!slot->tts_isempty);
-
- /*
- * 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)
- return slot->tts_tuple;
-
- /*
- * Otherwise, copy or build a physical tuple, and store it into the slot.
- *
- * We may be called in a context that is shorter-lived than the tuple
- * slot, but we have to ensure that the materialized tuple will survive
- * anyway.
- */
- oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
- slot->tts_tuple = ExecCopySlotTuple(slot);
- slot->tts_shouldFree = true;
- MemoryContextSwitchTo(oldContext);
-
- /*
- * Drop the pin on the referenced buffer, if there is one.
- */
- if (BufferIsValid(slot->tts_buffer))
- ReleaseBuffer(slot->tts_buffer);
-
- slot->tts_buffer = InvalidBuffer;
-
- /*
- * Mark extracted state invalid. This is important because the slot is
- * not supposed to depend any more on the previous external data; we
- * mustn't leave any dangling pass-by-reference datums in tts_values.
- * However, we have not actually invalidated any such datums, if there
- * happen to be any previously fetched from the slot. (Note in particular
- * that we have not pfree'd tts_mintuple, if there is one.)
- */
- slot->tts_nvalid = 0;
-
- /*
- * On the same principle of not depending on previous remote storage,
- * forget the mintuple if it's not local storage. (If it is local
- * storage, we must not pfree it now, since callers might have already
- * fetched datum pointers referencing it.)
- */
- if (!slot->tts_shouldFreeMin)
- slot->tts_mintuple = NULL;
-
- return slot->tts_tuple;
-}
-
/* --------------------------------
* ExecCopySlot
* Copy the source slot's contents into the destination slot.
@@ -830,19 +1174,16 @@ ExecMaterializeSlot(TupleTableSlot *slot)
TupleTableSlot *
ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
{
- HeapTuple newTuple;
- MemoryContext oldContext;
+ int natts = srcslot->tupleDescriptor->natts;
- /*
- * There might be ways to optimize this when the source is virtual, but
- * for now just always build a physical copy. Make sure it is in the
- * right context.
- */
- oldContext = MemoryContextSwitchTo(dstslot->tts_mcxt);
- newTuple = ExecCopySlotTuple(srcslot);
- MemoryContextSwitchTo(oldContext);
+ ExecClearTuple(dstslot);
+ /* XXX: optimize when tuple based? */
+ slot_getallattrs(srcslot);
+ memcpy(dstslot->values, srcslot->values, sizeof(Datum) * natts);
+ memcpy(dstslot->nulls, srcslot->nulls, sizeof(bool) * natts);
+ ExecStoreVirtualTuple(dstslot);
- return ExecStoreTuple(newTuple, dstslot, InvalidBuffer, true);
+ return dstslot;
}
@@ -867,7 +1208,7 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
* ----------------
*/
void
-ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
+ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate, TupleTableSlotType st)
{
bool hasoid;
TupleDesc tupDesc;
@@ -884,7 +1225,8 @@ ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
- planstate->ps_ResultTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable, tupDesc);
+ planstate->ps_ResultTupleSlot =
+ ExecAllocTableSlot(&estate->es_tupleTable, tupDesc, st);
}
/* ----------------
@@ -892,10 +1234,10 @@ ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
* ----------------
*/
void
-ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc, TupleTableSlotType st)
{
- scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
- tupledesc);
+ scanstate->ss_ScanTupleSlot =
+ ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, st);
}
/* ----------------
@@ -907,9 +1249,9 @@ ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupledesc)
* ----------------
*/
TupleTableSlot *
-ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc)
+ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, TupleTableSlotType st)
{
- return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc);
+ return ExecAllocTableSlot(&estate->es_tupleTable, tupledesc, st);
}
/* ----------------
@@ -921,9 +1263,9 @@ ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc)
* ----------------
*/
TupleTableSlot *
-ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
+ExecInitNullTupleSlot(EState *estate, TupleDesc tupType, TupleTableSlotType st)
{
- TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType);
+ TupleTableSlot *slot = ExecInitExtraTupleSlot(estate, tupType, st);
return ExecStoreAllNullTuple(slot);
}
@@ -1094,6 +1436,7 @@ BlessTupleDesc(TupleDesc tupdesc)
return tupdesc; /* just for notational convenience */
}
+#ifdef NOT_ANYMORE
/*
* TupleDescGetSlot - Initialize a slot based on the supplied tupledesc
*
@@ -1115,6 +1458,7 @@ TupleDescGetSlot(TupleDesc tupdesc)
/* Return the slot */
return slot;
}
+#endif
/*
* TupleDescGetAttInMetadata - Build an AttInMetadata structure based on the
@@ -1294,7 +1638,7 @@ begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc)
tstate = (TupOutputState *) palloc(sizeof(TupOutputState));
- tstate->slot = MakeSingleTupleTableSlot(tupdesc);
+ tstate->slot = MakeSingleTupleTableSlot(tupdesc, TTS_TYPE_VIRTUAL);
tstate->dest = dest;
tstate->dest->rStartup(tstate->dest, (int) CMD_SELECT, tupdesc);
@@ -1309,14 +1653,14 @@ void
do_tup_output(TupOutputState *tstate, Datum *values, bool *isnull)
{
TupleTableSlot *slot = tstate->slot;
- int natts = slot->tts_tupleDescriptor->natts;
+ int natts = slot->tupleDescriptor->natts;
/* make sure the slot is clear */
ExecClearTuple(slot);
/* insert data */
- memcpy(slot->tts_values, values, natts * sizeof(Datum));
- memcpy(slot->tts_isnull, isnull, natts * sizeof(bool));
+ memcpy(slot->values, values, natts * sizeof(Datum));
+ memcpy(slot->nulls, isnull, natts * sizeof(bool));
/* mark slot as containing a virtual tuple */
ExecStoreVirtualTuple(slot);
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a8ae37ebc80..94e9fbf6766 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -436,7 +436,7 @@ ExecGetResultType(PlanState *planstate)
{
TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
- return slot->tts_tupleDescriptor;
+ return slot->tupleDescriptor;
}
@@ -590,7 +590,7 @@ ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc)
* ----------------
*/
void
-ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate)
+ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, TupleTableSlotType tp)
{
PlanState *outerPlan;
TupleDesc tupDesc;
@@ -598,7 +598,7 @@ ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate)
outerPlan = outerPlanState(scanstate);
tupDesc = ExecGetResultType(outerPlan);
- ExecInitScanTupleSlot(estate, scanstate, tupDesc);
+ ExecInitScanTupleSlot(estate, scanstate, tupDesc, tp);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 7e249f575f3..7f2763d9b21 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -751,7 +751,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
if (fcache->returnsTuple)
{
/* Make sure output rowtype is properly blessed */
- BlessTupleDesc(fcache->junkFilter->jf_resultSlot->tts_tupleDescriptor);
+ BlessTupleDesc(fcache->junkFilter->jf_resultSlot->tupleDescriptor);
}
else if (fcache->returnsSet && type_is_rowtype(fcache->rettype))
{
@@ -1667,7 +1667,8 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
/* Set up junk filter if needed */
if (junkFilter)
- *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
+ *junkFilter = ExecInitJunkFilter(tlist, false,
+ MakeSingleTupleTableSlot(NULL, TTS_TYPE_MINIMALTUPLE));
}
else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
{
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 1b1334006fa..05958fb9ffa 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -843,8 +843,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
/* Start from 1, since the 0th arg will be the transition value */
for (i = 0; i < numTransInputs; i++)
{
- fcinfo->arg[i + 1] = slot1->tts_values[i];
- fcinfo->argnull[i + 1] = slot1->tts_isnull[i];
+ fcinfo->arg[i + 1] = slot1->values[i];
+ fcinfo->argnull[i + 1] = slot1->nulls[i];
}
advance_transition_function(aggstate, pertrans, pergroupstate);
@@ -1080,7 +1080,7 @@ prepare_projection_slot(AggState *aggstate, TupleTableSlot *slot, int currentSet
aggstate->grouped_cols = grouped_cols;
- if (slot->tts_isempty)
+ if (slot->flags & TTS_ISEMPTY)
{
/*
* Force all values to be NULL if working on an empty input tuple
@@ -1101,7 +1101,7 @@ prepare_projection_slot(AggState *aggstate, TupleTableSlot *slot, int currentSet
int attnum = lfirst_int(lc);
if (!bms_is_member(attnum, grouped_cols))
- slot->tts_isnull[attnum - 1] = true;
+ slot->nulls[attnum - 1] = true;
}
}
}
@@ -1278,7 +1278,7 @@ build_hash_table(AggState *aggstate)
Assert(perhash->aggnode->numGroups > 0);
perhash->hashtable = BuildTupleHashTable(&aggstate->ss.ps,
- perhash->hashslot->tts_tupleDescriptor,
+ perhash->hashslot->tupleDescriptor,
perhash->numCols,
perhash->hashGrpColIdxHash,
perhash->eqfuncoids,
@@ -1403,7 +1403,8 @@ find_hash_columns(AggState *aggstate)
&perhash->eqfuncoids,
&perhash->hashfunctions);
perhash->hashslot =
- ExecAllocTableSlot(&estate->es_tupleTable, hashDesc);
+ ExecAllocTableSlot(&estate->es_tupleTable, hashDesc,
+ TTS_TYPE_MINIMALTUPLE);
list_free(hashTlist);
bms_free(colnos);
@@ -1459,8 +1460,8 @@ lookup_hash_entry(AggState *aggstate)
{
int varNumber = perhash->hashGrpColIdxInput[i] - 1;
- hashslot->tts_values[i] = inputslot->tts_values[varNumber];
- hashslot->tts_isnull[i] = inputslot->tts_isnull[varNumber];
+ hashslot->values[i] = inputslot->values[varNumber];
+ hashslot->nulls[i] = inputslot->nulls[varNumber];
}
ExecStoreVirtualTuple(hashslot);
@@ -2030,15 +2031,15 @@ agg_retrieve_hash_table(AggState *aggstate)
slot_getallattrs(hashslot);
ExecClearTuple(firstSlot);
- memset(firstSlot->tts_isnull, true,
- firstSlot->tts_tupleDescriptor->natts * sizeof(bool));
+ memset(firstSlot->nulls, true,
+ firstSlot->tupleDescriptor->natts * sizeof(bool));
for (i = 0; i < perhash->numhashGrpCols; i++)
{
int varNumber = perhash->hashGrpColIdxInput[i] - 1;
- firstSlot->tts_values[varNumber] = hashslot->tts_values[i];
- firstSlot->tts_isnull[varNumber] = hashslot->tts_isnull[i];
+ firstSlot->values[varNumber] = hashslot->values[i];
+ firstSlot->nulls[varNumber] = hashslot->nulls[i];
}
ExecStoreVirtualTuple(firstSlot);
@@ -2212,15 +2213,15 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/*
* initialize source tuple type.
*/
- ExecCreateScanSlotFromOuterPlan(estate, &aggstate->ss);
- scanDesc = aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+ ExecCreateScanSlotFromOuterPlan(estate, &aggstate->ss, TTS_TYPE_HEAPTUPLE);
+ scanDesc = aggstate->ss.ss_ScanTupleSlot->tupleDescriptor;
if (node->chain)
- aggstate->sort_slot = ExecInitExtraTupleSlot(estate, scanDesc);
+ aggstate->sort_slot = ExecInitExtraTupleSlot(estate, scanDesc, TTS_TYPE_MINIMALTUPLE);
/*
* Initialize result type, slot and projection.
*/
- ExecInitResultTupleSlotTL(estate, &aggstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &aggstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&aggstate->ss.ps, NULL);
/*
@@ -3063,7 +3064,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
{
pertrans->sortdesc = ExecTypeFromTL(aggref->args, false);
pertrans->sortslot =
- ExecInitExtraTupleSlot(estate, pertrans->sortdesc);
+ ExecInitExtraTupleSlot(estate, pertrans->sortdesc,
+ TTS_TYPE_MINIMALTUPLE);
}
if (numSortCols > 0)
@@ -3085,7 +3087,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
{
/* we will need an extra slot to store prior values */
pertrans->uniqslot =
- ExecInitExtraTupleSlot(estate, pertrans->sortdesc);
+ ExecInitExtraTupleSlot(estate, pertrans->sortdesc,
+ TTS_TYPE_MINIMALTUPLE);
}
/* Extract the sort information for use later */
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 7a3dd2ee2d0..8e10edbb63c 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -131,7 +131,7 @@ ExecInitAppend(Append *node, EState *estate, int eflags)
/*
* Initialize result tuple type and slot.
*/
- ExecInitResultTupleSlotTL(estate, &appendstate->ps);
+ ExecInitResultTupleSlotTL(estate, &appendstate->ps, TTS_TYPE_VIRTUAL);
/*
* call ExecInitNode on each of the plans to be executed and save the
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 3e1c9e07145..399d67126cb 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -924,13 +924,15 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
* get the scan type from the relation descriptor.
*/
ExecInitScanTupleSlot(estate, &scanstate->ss,
- RelationGetDescr(currentRelation));
+ RelationGetDescr(currentRelation),
+ TTS_TYPE_BUFFER);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps,
+ TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 218619c760d..35b98a93b1b 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -247,12 +247,13 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
* table) is the same as the result rowtype of the CTE query.
*/
ExecInitScanTupleSlot(estate, &scanstate->ss,
- ExecGetResultType(scanstate->cteplanstate));
+ ExecGetResultType(scanstate->cteplanstate),
+ TTS_TYPE_MINIMALTUPLE);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index b816e0b31db..569a050d8f8 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -73,13 +73,13 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
TupleDesc scan_tupdesc;
scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false);
- ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc);
+ ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc, TTS_TYPE_VIRTUAL); /* FIXME */
/* Node's targetlist will contain Vars with varno = INDEX_VAR */
tlistvarno = INDEX_VAR;
}
else
{
- ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel));
+ ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel), TTS_TYPE_VIRTUAL);
/* Node's targetlist will contain Vars with varno = scanrelid */
tlistvarno = scanrelid;
}
@@ -87,7 +87,7 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &css->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &css->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno);
/* initialize child expressions */
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 0084234b350..7cd8657bfa8 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -180,13 +180,16 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
TupleDesc scan_tupdesc;
scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false);
- ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc);
+ ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
+ TTS_TYPE_HEAPTUPLE);
/* Node's targetlist will contain Vars with varno = INDEX_VAR */
tlistvarno = INDEX_VAR;
}
else
{
- ExecInitScanTupleSlot(estate, &scanstate->ss, RelationGetDescr(currentRelation));
+ ExecInitScanTupleSlot(estate, &scanstate->ss,
+ RelationGetDescr(currentRelation),
+ TTS_TYPE_HEAPTUPLE);
/* Node's targetlist will contain Vars with varno = scanrelid */
tlistvarno = scanrelid;
}
@@ -194,7 +197,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
/*
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index fb7c9f67875..be3ed221ac9 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -197,8 +197,8 @@ FunctionNext(FunctionScanState *node)
*/
for (i = 0; i < fs->colcount; i++)
{
- scanslot->tts_values[att] = (Datum) 0;
- scanslot->tts_isnull[att] = true;
+ scanslot->values[att] = (Datum) 0;
+ scanslot->nulls[att] = true;
att++;
}
}
@@ -211,8 +211,8 @@ FunctionNext(FunctionScanState *node)
for (i = 0; i < fs->colcount; i++)
{
- scanslot->tts_values[att] = fs->func_slot->tts_values[i];
- scanslot->tts_isnull[att] = fs->func_slot->tts_isnull[i];
+ scanslot->values[att] = fs->func_slot->values[i];
+ scanslot->nulls[att] = fs->func_slot->nulls[i];
att++;
}
@@ -229,8 +229,8 @@ FunctionNext(FunctionScanState *node)
*/
if (node->ordinality)
{
- scanslot->tts_values[att] = Int64GetDatumFast(node->ordinal);
- scanslot->tts_isnull[att] = false;
+ scanslot->values[att] = Int64GetDatumFast(node->ordinal);
+ scanslot->nulls[att] = false;
}
/*
@@ -424,7 +424,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
*/
if (!scanstate->simple)
{
- fs->func_slot = ExecInitExtraTupleSlot(estate, fs->tupdesc);
+ fs->func_slot = ExecInitExtraTupleSlot(estate, fs->tupdesc,
+ TTS_TYPE_VIRTUAL);
}
else
fs->func_slot = NULL;
@@ -482,12 +483,14 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
/*
* Initialize scan slot and type.
*/
- ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc);
+ ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
+ TTS_TYPE_MINIMALTUPLE);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps,
+ TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index eaf7d2d5632..4a19c589a2d 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -94,13 +94,14 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &gatherstate->ps);
+ ExecInitResultTupleSlotTL(estate, &gatherstate->ps, TTS_TYPE_VIRTUAL);
ExecConditionalAssignProjectionInfo(&gatherstate->ps, tupDesc, OUTER_VAR);
/*
* Initialize funnel slot to same tuple descriptor as outer plan.
*/
- gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate, tupDesc);
+ gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate, tupDesc,
+ TTS_TYPE_HEAPTUPLE);
/*
* Gather doesn't support checking a qual (it's always more efficient to
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 83221cdbaee..5d71b00cfd9 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -119,7 +119,7 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &gm_state->ps);
+ ExecInitResultTupleSlotTL(estate, &gm_state->ps, TTS_TYPE_VIRTUAL);
ExecConditionalAssignProjectionInfo(&gm_state->ps, tupDesc, OUTER_VAR);
/*
@@ -403,7 +403,8 @@ gather_merge_setup(GatherMergeState *gm_state)
/* Initialize tuple slot for worker */
gm_state->gm_slots[i + 1] =
- ExecInitExtraTupleSlot(gm_state->ps.state, gm_state->tupDesc);
+ ExecInitExtraTupleSlot(gm_state->ps.state, gm_state->tupDesc,
+ TTS_TYPE_HEAPTUPLE);
}
/* Allocate the resources for the merge */
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index c6efd64d00d..34b2483a3a2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -189,12 +189,13 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
/*
* Initialize scan slot and type.
*/
- ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss);
+ ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss,
+ TTS_TYPE_VIRTUAL);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &grpstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &grpstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
/*
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 06bb44b1631..fa3490be726 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -382,7 +382,7 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
* initialize our result slot and type. No need to build projection
* because this node doesn't do projections.
*/
- ExecInitResultTupleSlotTL(estate, &hashstate->ps);
+ ExecInitResultTupleSlotTL(estate, &hashstate->ps, TTS_TYPE_MINIMALTUPLE);
hashstate->ps.ps_ProjInfo = NULL;
/*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index ab91eb25273..97f76fc0d29 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -642,13 +642,14 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &hjstate->js.ps);
+ ExecInitResultTupleSlotTL(estate, &hjstate->js.ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&hjstate->js.ps, NULL);
/*
* tuple table initialization
*/
- hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate, outerDesc);
+ hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate, outerDesc,
+ TTS_TYPE_MINIMALTUPLE);
/*
* detect whether we need only consider the first matching inner tuple
@@ -665,17 +666,17 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
case JOIN_LEFT:
case JOIN_ANTI:
hjstate->hj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate, innerDesc);
+ ExecInitNullTupleSlot(estate, innerDesc, TTS_TYPE_VIRTUAL);
break;
case JOIN_RIGHT:
hjstate->hj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate, outerDesc);
+ ExecInitNullTupleSlot(estate, outerDesc, TTS_TYPE_VIRTUAL);
break;
case JOIN_FULL:
hjstate->hj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate, outerDesc);
+ ExecInitNullTupleSlot(estate, outerDesc, TTS_TYPE_VIRTUAL);
hjstate->hj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate, innerDesc);
+ ExecInitNullTupleSlot(estate, innerDesc, TTS_TYPE_VIRTUAL);
break;
default:
elog(ERROR, "unrecognized join type: %d",
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index ddc0ae90615..523ff79989f 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -197,7 +197,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
* exactly the slot's format, but it seems worth doing a quick
* check on the number of fields.
*/
- Assert(slot->tts_tupleDescriptor->natts ==
+ Assert(slot->tupleDescriptor->natts ==
scandesc->xs_hitupdesc->natts);
ExecStoreTuple(scandesc->xs_hitup, slot, InvalidBuffer, false);
}
@@ -268,8 +268,8 @@ static void
StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
{
int nindexatts = itupdesc->natts;
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
+ Datum *values = slot->values;
+ bool *isnull = slot->nulls;
int i;
/*
@@ -279,7 +279,7 @@ StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
* number of columns though, as well as being datatype-compatible which is
* something we can't so easily check.
*/
- Assert(slot->tts_tupleDescriptor->natts == nindexatts);
+ Assert(slot->tupleDescriptor->natts == nindexatts);
ExecClearTuple(slot);
for (i = 0; i < nindexatts; i++)
@@ -534,14 +534,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
* suitable data anyway.)
*/
tupDesc = ExecTypeFromTL(node->indextlist, false);
- ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc);
+ ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc, TTS_TYPE_HEAPTUPLE);
/*
* Initialize result slot, type and projection info. The node's
* targetlist will contain Vars with varno = INDEX_VAR, referencing the
* scan tuple.
*/
- ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
/*
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 01c9de88f4d..6ddec9be18a 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -952,12 +952,13 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* get the scan type from the relation descriptor.
*/
ExecInitScanTupleSlot(estate, &indexstate->ss,
- RelationGetDescr(currentRelation));
+ RelationGetDescr(currentRelation),
+ TTS_TYPE_BUFFER); /* FIXME: wrong for reorder case */
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&indexstate->ss);
/*
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index 56d98b4490b..356ccc64746 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -371,7 +371,8 @@ ExecInitLimit(Limit *node, EState *estate, int eflags)
* Initialize result slot and type. (XXX not actually used, but upper
* nodes access it to get this node's result tupledesc...)
*/
- ExecInitResultTupleSlotTL(estate, &limitstate->ps);
+ ExecInitResultTupleSlotTL(estate, &limitstate->ps,
+ TTS_TYPE_VIRTUAL);
/*
* limit nodes do no projections, so initialize projection info for this
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index b39ccf7dc13..30d8422e45e 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -378,7 +378,7 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags)
* Tuple table initialization (XXX not actually used, but upper nodes
* access it to get this node's result tupledesc...)
*/
- ExecInitResultTupleSlotTL(estate, &lrstate->ps);
+ ExecInitResultTupleSlotTL(estate, &lrstate->ps, TTS_TYPE_VIRTUAL);
/*
* then initialize outer plan
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 8c2e57dbd07..3a106916097 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -223,13 +223,13 @@ ExecInitMaterial(Material *node, EState *estate, int eflags)
*
* material nodes only return tuples from their materialized relation.
*/
- ExecInitResultTupleSlotTL(estate, &matstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &matstate->ss.ps, TTS_TYPE_MINIMALTUPLE);
matstate->ss.ps.ps_ProjInfo = NULL;
/*
* initialize tuple type.
*/
- ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss);
+ ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, TTS_TYPE_MINIMALTUPLE);
return matstate;
}
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 118f4ef07df..7a708901bba 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -109,7 +109,8 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags)
* MergeAppend nodes do have Result slots, which hold pointers to tuples,
* so we have to initialize them.
*/
- ExecInitResultTupleSlotTL(estate, &mergestate->ps);
+ /* FIXME: unused? */
+ ExecInitResultTupleSlotTL(estate, &mergestate->ps, TTS_TYPE_VIRTUAL);
/*
* call ExecInitNode on each of the plans to be executed and save the
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index f3cbe2f889b..fd236e6402b 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1511,13 +1511,14 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &mergestate->js.ps);
+ ExecInitResultTupleSlotTL(estate, &mergestate->js.ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&mergestate->js.ps, NULL);
/*
* tuple table initialization
*/
- mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate, innerDesc);
+ mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate, innerDesc,
+ TTS_TYPE_VIRTUAL);
/*
* initialize child expressions
@@ -1547,13 +1548,13 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
mergestate->mj_FillOuter = true;
mergestate->mj_FillInner = false;
mergestate->mj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate, innerDesc);
+ ExecInitNullTupleSlot(estate, innerDesc, TTS_TYPE_VIRTUAL);
break;
case JOIN_RIGHT:
mergestate->mj_FillOuter = false;
mergestate->mj_FillInner = true;
mergestate->mj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate, outerDesc);
+ ExecInitNullTupleSlot(estate, outerDesc, TTS_TYPE_VIRTUAL);
/*
* Can't handle right or full join with non-constant extra
@@ -1569,9 +1570,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
mergestate->mj_FillOuter = true;
mergestate->mj_FillInner = true;
mergestate->mj_NullOuterTupleSlot =
- ExecInitNullTupleSlot(estate, outerDesc);
+ ExecInitNullTupleSlot(estate, outerDesc, TTS_TYPE_VIRTUAL);
mergestate->mj_NullInnerTupleSlot =
- ExecInitNullTupleSlot(estate, innerDesc);
+ ExecInitNullTupleSlot(estate, innerDesc, TTS_TYPE_VIRTUAL);
/*
* Can't handle right or full join with non-constant extra
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 93c03cfb071..c1316e80a49 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -766,7 +766,7 @@ ExecDelete(ModifyTableState *mtstate,
* the slot's tupdesc so the FDW doesn't need to do that for itself.
*/
slot = estate->es_trig_tuple_slot;
- if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
+ if (slot->tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
slot = resultRelInfo->ri_FdwRoutine->ExecForeignDelete(estate,
@@ -781,7 +781,7 @@ ExecDelete(ModifyTableState *mtstate,
* RETURNING expressions might reference the tableoid column, so
* initialize t_tableOid before evaluating them.
*/
- if (slot->tts_isempty)
+ if (slot->flags & TTS_ISEMPTY)
ExecStoreAllNullTuple(slot);
tuple = ExecMaterializeSlot(slot);
tuple->t_tableOid = RelationGetRelid(resultRelationDesc);
@@ -951,7 +951,7 @@ ldelete:;
elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING");
}
- if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc))
+ if (slot->tupleDescriptor != RelationGetDescr(resultRelationDesc))
ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc));
ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
}
@@ -1944,6 +1944,13 @@ ExecModifyTable(PlanState *pstate)
break;
}
+ if (node->mt_scan->tupleDescriptor != planSlot->tupleDescriptor)
+ ExecSetSlotDescriptor(node->mt_scan, planSlot->tupleDescriptor);
+
+ ExecStoreTuple(ExecCopySlotTuple(planSlot), node->mt_scan,
+ InvalidBuffer, true);
+ planSlot = node->mt_scan;
+
/*
* If resultRelInfo->ri_usesFdwDirectModify is true, all we need to do
* here is compute the RETURNING expressions.
@@ -2139,6 +2146,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, NIL, node->epqParam);
mtstate->fireBSTriggers = true;
+ mtstate->mt_scan = ExecInitExtraTupleSlot(mtstate->ps.state, NULL, TTS_TYPE_HEAPTUPLE);
+
/*
* call ExecInitNode on each of the plans to be executed and save the
* results into the array "mt_plans". This is also a convenient place to
@@ -2367,7 +2376,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->ps.plan->targetlist = (List *) linitial(node->returningLists);
/* Set up a slot for the output of the RETURNING projection(s) */
- ExecInitResultTupleSlotTL(estate, &mtstate->ps);
+ ExecInitResultTupleSlotTL(estate, &mtstate->ps, TTS_TYPE_VIRTUAL);
slot = mtstate->ps.ps_ResultTupleSlot;
/* Need an econtext too */
@@ -2434,7 +2443,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* expects one (maybe should change that?).
*/
mtstate->ps.plan->targetlist = NIL;
- ExecInitResultTupleSlotTL(estate, &mtstate->ps);
+ ExecInitResultTupleSlotTL(estate, &mtstate->ps, TTS_TYPE_VIRTUAL);
mtstate->ps.ps_ExprContext = NULL;
}
@@ -2462,7 +2471,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/* initialize slot for the existing tuple */
mtstate->mt_existing =
- ExecInitExtraTupleSlot(mtstate->ps.state, relationDesc);
+ ExecInitExtraTupleSlot(mtstate->ps.state, relationDesc, TTS_TYPE_BUFFER);
/* carried forward solely for the benefit of explain */
mtstate->mt_excludedtlist = node->exclRelTlist;
@@ -2471,7 +2480,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
tupDesc = ExecTypeFromTL((List *) node->onConflictSet,
relationDesc->tdhasoid);
mtstate->mt_conflproj =
- ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc);
+ ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc, TTS_TYPE_VIRTUAL);
/* build UPDATE SET projection state */
resultRelInfo->ri_onConflictSetProj =
@@ -2582,7 +2591,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
j = ExecInitJunkFilter(subplan->targetlist,
resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
- ExecInitExtraTupleSlot(estate, NULL));
+ ExecInitExtraTupleSlot(estate, NULL, TTS_TYPE_HEAPTUPLE));
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
@@ -2632,7 +2641,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* we keep it in the estate.
*/
if (estate->es_trig_tuple_slot == NULL)
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL);
+ estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL, TTS_TYPE_BUFFER);
/*
* Lastly, if this is not the primary (canSetTag) ModifyTable node, add it
diff --git a/src/backend/executor/nodeNamedtuplestorescan.c b/src/backend/executor/nodeNamedtuplestorescan.c
index 4d898b1f83e..9ba4da2f078 100644
--- a/src/backend/executor/nodeNamedtuplestorescan.c
+++ b/src/backend/executor/nodeNamedtuplestorescan.c
@@ -136,8 +136,9 @@ ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflag
* Tuple table and result type initialization. The scan tuple type is
* specified for the tuplestore.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
- ExecInitScanTupleSlot(estate, &scanstate->ss, scanstate->tupdesc);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps, TTS_TYPE_VIRTUAL);
+ ExecInitScanTupleSlot(estate, &scanstate->ss, scanstate->tupdesc,
+ TTS_TYPE_MINIMALTUPLE);
/*
* initialize child expressions
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 9ae9863226c..9864473693b 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -304,7 +304,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &nlstate->js.ps);
+ ExecInitResultTupleSlotTL(estate, &nlstate->js.ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
/*
@@ -332,7 +332,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
case JOIN_ANTI:
nlstate->nl_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
- ExecGetResultType(innerPlanState(nlstate)));
+ ExecGetResultType(innerPlanState(nlstate)),
+ TTS_TYPE_VIRTUAL);
break;
default:
elog(ERROR, "unrecognized join type: %d",
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index 6d6ed38ceeb..2afe5abedc9 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -154,8 +154,8 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
{
Node *elem = node->elems[argno];
ExprDoneCond *isdone = &node->elemdone[argno];
- Datum *result = &resultSlot->tts_values[argno];
- bool *isnull = &resultSlot->tts_isnull[argno];
+ Datum *result = &resultSlot->values[argno];
+ bool *isnull = &resultSlot->nulls[argno];
if (continuing && *isdone == ExprEndResult)
{
@@ -256,7 +256,7 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
/*
* tuple table and result type initialization
*/
- ExecInitResultTupleSlotTL(estate, &state->ps);
+ ExecInitResultTupleSlotTL(estate, &state->ps, TTS_TYPE_VIRTUAL);
/* Create workspace for per-tlist-entry expr state & SRF-is-done state */
state->nelems = list_length(node->plan.targetlist);
diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c
index 6b3ea5afb31..3e4273eab5c 100644
--- a/src/backend/executor/nodeRecursiveunion.c
+++ b/src/backend/executor/nodeRecursiveunion.c
@@ -229,7 +229,7 @@ ExecInitRecursiveUnion(RecursiveUnion *node, EState *estate, int eflags)
* RecursiveUnion nodes still have Result slots, which hold pointers to
* tuples, so we have to initialize them.
*/
- ExecInitResultTupleSlotTL(estate, &rustate->ps);
+ ExecInitResultTupleSlotTL(estate, &rustate->ps, TTS_TYPE_VIRTUAL);
/*
* Initialize result tuple type. (Note: we have to set up the result type
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index e4418a29bba..b936b4876b8 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -217,7 +217,7 @@ ExecInitResult(Result *node, EState *estate, int eflags)
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &resstate->ps);
+ ExecInitResultTupleSlotTL(estate, &resstate->ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&resstate->ps, NULL);
/*
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 872d6e5735e..de7e976d5af 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -150,13 +150,15 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
/* and create slot with appropriate rowtype */
ExecInitScanTupleSlot(estate, &scanstate->ss,
- RelationGetDescr(scanstate->ss.ss_currentRelation));
+ RelationGetDescr(scanstate->ss.ss_currentRelation),
+ TTS_TYPE_BUFFER);
/*
* Initialize result slot, type and projection.
* tuple table and result tuple initialization
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps,
+ TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 9db368922a3..34d356d4463 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -176,12 +176,13 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
/* and create slot with the appropriate rowtype */
ExecInitScanTupleSlot(estate, &scanstate->ss,
- RelationGetDescr(scanstate->ss.ss_currentRelation));
+ RelationGetDescr(scanstate->ss.ss_currentRelation),
+ TTS_TYPE_BUFFER);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 3fa4a5fcc65..20129f0fa5b 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -533,7 +533,7 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
* Initialize result slot and type. Setop nodes do no projections, so
* initialize projection info for this node appropriately.
*/
- ExecInitResultTupleSlotTL(estate, &setopstate->ps);
+ ExecInitResultTupleSlotTL(estate, &setopstate->ps, TTS_TYPE_VIRTUAL);
setopstate->ps.ps_ProjInfo = NULL;
/*
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 73f16c9abaa..8b1950d4987 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -211,13 +211,13 @@ ExecInitSort(Sort *node, EState *estate, int eflags)
/*
* Initialize scan slot and type.
*/
- ExecCreateScanSlotFromOuterPlan(estate, &sortstate->ss);
+ ExecCreateScanSlotFromOuterPlan(estate, &sortstate->ss, TTS_TYPE_VIRTUAL);
/*
* Initialize return slot and type. No need to initialize projection info because
* this node doesn't do projections.
*/
- ExecInitResultTupleSlotTL(estate, &sortstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &sortstate->ss.ps, TTS_TYPE_MINIMALTUPLE);
sortstate->ss.ps.ps_ProjInfo = NULL;
SO1_printf("ExecInitSort: %s\n",
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index d5411500a2b..fef59dbe1a7 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -314,7 +314,7 @@ ExecScanSubPlan(SubPlanState *node,
!TupIsNull(slot);
slot = ExecProcNode(planstate))
{
- TupleDesc tdesc = slot->tts_tupleDescriptor;
+ TupleDesc tdesc = slot->tupleDescriptor;
Datum rowresult;
bool rownull;
int col;
@@ -721,7 +721,7 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
static bool
slotAllNulls(TupleTableSlot *slot)
{
- int ncols = slot->tts_tupleDescriptor->natts;
+ int ncols = slot->tupleDescriptor->natts;
int i;
for (i = 1; i <= ncols; i++)
@@ -741,7 +741,7 @@ slotAllNulls(TupleTableSlot *slot)
static bool
slotNoNulls(TupleTableSlot *slot)
{
- int ncols = slot->tts_tupleDescriptor->natts;
+ int ncols = slot->tupleDescriptor->natts;
int i;
for (i = 1; i <= ncols; i++)
@@ -957,7 +957,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
* own innerecontext.
*/
tupDescLeft = ExecTypeFromTL(lefttlist, false);
- slot = ExecInitExtraTupleSlot(estate, tupDescLeft);
+ slot = ExecInitExtraTupleSlot(estate, tupDescLeft, TTS_TYPE_VIRTUAL);
sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
NULL,
slot,
@@ -965,7 +965,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
NULL);
sstate->descRight = tupDescRight = ExecTypeFromTL(righttlist, false);
- slot = ExecInitExtraTupleSlot(estate, tupDescRight);
+ slot = ExecInitExtraTupleSlot(estate, tupDescRight, TTS_TYPE_VIRTUAL);
sstate->projRight = ExecBuildProjectionInfo(righttlist,
sstate->innerecontext,
slot,
@@ -1057,7 +1057,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
!TupIsNull(slot);
slot = ExecProcNode(planstate))
{
- TupleDesc tdesc = slot->tts_tupleDescriptor;
+ TupleDesc tdesc = slot->tupleDescriptor;
int i = 1;
if (subLinkType == EXISTS_SUBLINK)
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index fa618847851..a8e5a8c347c 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -129,12 +129,14 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
* Initialize scan slot and type (needed by ExecInitResultTupleSlotTL)
*/
ExecInitScanTupleSlot(estate, &subquerystate->ss,
- ExecGetResultType(subquerystate->subplan));
+ ExecGetResultType(subquerystate->subplan),
+ TTS_TYPE_HEAPTUPLE); /* FIXME */
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &subquerystate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &subquerystate->ss.ps,
+ TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&subquerystate->ss);
/*
diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c
index fed6f2b3a53..9f11732b271 100644
--- a/src/backend/executor/nodeTableFuncscan.c
+++ b/src/backend/executor/nodeTableFuncscan.c
@@ -147,12 +147,14 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
tf->coltypmods,
tf->colcollations);
/* and the corresponding scan slot */
- ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc);
+ ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc,
+ TTS_TYPE_MINIMALTUPLE);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps,
+ TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
@@ -285,7 +287,7 @@ tfuncFetchRows(TableFuncScanState *tstate, ExprContext *econtext)
PG_TRY();
{
routine->InitOpaque(tstate,
- tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->natts);
+ tstate->ss.ss_ScanTupleSlot->tupleDescriptor->natts);
/*
* If evaluating the document expression returns NULL, the table
@@ -380,7 +382,7 @@ tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
* is the column filter.
*/
colno = 0;
- tupdesc = tstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+ tupdesc = tstate->ss.ss_ScanTupleSlot->tupleDescriptor;
foreach(lc1, tstate->colexprs)
{
char *colfilter;
@@ -419,9 +421,9 @@ tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
{
const TableFuncRoutine *routine = tstate->routine;
TupleTableSlot *slot = tstate->ss.ss_ScanTupleSlot;
- TupleDesc tupdesc = slot->tts_tupleDescriptor;
- Datum *values = slot->tts_values;
- bool *nulls = slot->tts_isnull;
+ TupleDesc tupdesc = slot->tupleDescriptor;
+ Datum *values = slot->values;
+ bool *nulls = slot->nulls;
int natts = tupdesc->natts;
MemoryContext oldcxt;
int ordinalitycol;
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index e207b1ffb51..d125611571d 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -549,12 +549,13 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
* get the scan type from the relation descriptor.
*/
ExecInitScanTupleSlot(estate, &tidstate->ss,
- RelationGetDescr(currentRelation));
+ RelationGetDescr(currentRelation),
+ TTS_TYPE_BUFFER);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &tidstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &tidstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&tidstate->ss);
/*
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index 05d65330a0e..6cff86f9091 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -141,7 +141,7 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
* Initialize result slot and type. Unique nodes do no projections, so
* initialize projection info for this node appropriately.
*/
- ExecInitResultTupleSlotTL(estate, &uniquestate->ps);
+ ExecInitResultTupleSlotTL(estate, &uniquestate->ps, TTS_TYPE_VIRTUAL);
uniquestate->ps.ps_ProjInfo = NULL;
/*
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 63b7e7ef5b8..cb0d9643a79 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -133,20 +133,20 @@ ValuesNext(ValuesScanState *node)
node->ss.ps.subPlan = oldsubplans;
/* parser should have checked all sublists are the same length */
- Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
+ Assert(list_length(exprstatelist) == slot->tupleDescriptor->natts);
/*
* Compute the expressions and build a virtual result tuple. We
* already did ExecClearTuple(slot).
*/
- values = slot->tts_values;
- isnull = slot->tts_isnull;
+ values = slot->values;
+ isnull = slot->nulls;
resind = 0;
foreach(lc, exprstatelist)
{
ExprState *estate = (ExprState *) lfirst(lc);
- Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor,
+ Form_pg_attribute attr = TupleDescAttr(slot->tupleDescriptor,
resind);
values[resind] = ExecEvalExpr(estate,
@@ -251,12 +251,12 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
* Get info about values list, initialize scan slot with it.
*/
tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists));
- ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc);
+ ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc, TTS_TYPE_VIRTUAL);
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignScanProjectionInfo(&scanstate->ss);
/*
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index a56c3e89fd5..8768d8c6955 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -2304,16 +2304,20 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
* initialize source tuple type (which is also the tuple type that we'll
* store in the tuplestore and use in all our working slots).
*/
- ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss);
- scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+ ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss, TTS_TYPE_MINIMALTUPLE);
+ scanDesc = winstate->ss.ss_ScanTupleSlot->tupleDescriptor;
/*
* tuple table initialization
*/
- winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc);
- winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc);
- winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc);
- winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc);
+ winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+ TTS_TYPE_VIRTUAL);
+ winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+ TTS_TYPE_VIRTUAL);
+ winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
+ TTS_TYPE_VIRTUAL);
+ winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
+ TTS_TYPE_VIRTUAL);
/*
* create frame head and tail slots only if needed (must match logic in
@@ -2324,15 +2328,17 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
{
if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING))
- winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc);
+ winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+ TTS_TYPE_VIRTUAL);
if (!(frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING))
- winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc);
+ winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+ TTS_TYPE_VIRTUAL);
}
/*
* Initialize result slot, type and projection.
*/
- ExecInitResultTupleSlotTL(estate, &winstate->ss.ps);
+ ExecInitResultTupleSlotTL(estate, &winstate->ss.ps, TTS_TYPE_VIRTUAL);
ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
/* Set up data for comparing tuples */
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index 2ff9a215b12..76a1aad4aa7 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,8 +159,8 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
/*
* tuple table initialization
*/
- ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
- ExecInitScanTupleSlot(estate, &scanstate->ss, NULL);
+ ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps, TTS_TYPE_VIRTUAL);
+ ExecInitScanTupleSlot(estate, &scanstate->ss, NULL, TTS_TYPE_MINIMALTUPLE);
/*
* initialize child expressions
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index d02ca3afd12..a0ec3dcb404 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -109,7 +109,7 @@ static bool
tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
{
TStoreState *myState = (TStoreState *) self;
- TupleDesc typeinfo = slot->tts_tupleDescriptor;
+ TupleDesc typeinfo = slot->tupleDescriptor;
int natts = typeinfo->natts;
int nfree;
int i;
@@ -126,10 +126,10 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
nfree = 0;
for (i = 0; i < natts; i++)
{
- Datum val = slot->tts_values[i];
+ Datum val = slot->values[i];
Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
- if (!attr->attisdropped && attr->attlen == -1 && !slot->tts_isnull[i])
+ if (!attr->attisdropped && attr->attlen == -1 && !slot->nulls[i])
{
if (VARATT_IS_EXTERNAL(DatumGetPointer(val)))
{
@@ -147,7 +147,7 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
*/
oldcxt = MemoryContextSwitchTo(myState->cxt);
tuplestore_putvalues(myState->tstore, typeinfo,
- myState->outvalues, slot->tts_isnull);
+ myState->outvalues, slot->nulls);
MemoryContextSwitchTo(oldcxt);
/* And release any temporary detoasted values */
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index b9bad5eacca..6b69ea8b5f3 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -492,7 +492,7 @@ print_slot(TupleTableSlot *slot)
printf("tuple is null.\n");
return;
}
- if (!slot->tts_tupleDescriptor)
+ if (!slot->tupleDescriptor)
{
printf("no tuple descriptor.\n");
return;
diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c
index e50b9f79056..495d91d555d 100644
--- a/src/backend/replication/logical/tablesync.c
+++ b/src/backend/replication/logical/tablesync.c
@@ -685,7 +685,7 @@ fetch_remote_table_info(char *nspname, char *relname,
(errmsg("could not fetch table info for table \"%s.%s\" from publisher: %s",
nspname, relname, res->err)));
- slot = MakeSingleTupleTableSlot(res->tupledesc);
+ slot = MakeSingleTupleTableSlot(res->tupledesc, TTS_TYPE_MINIMALTUPLE);
if (!tuplestore_gettupleslot(res->tuplestore, true, false, slot))
ereport(ERROR,
(errmsg("table \"%s.%s\" not found on publisher",
@@ -727,7 +727,7 @@ fetch_remote_table_info(char *nspname, char *relname,
lrel->attkeys = NULL;
natt = 0;
- slot = MakeSingleTupleTableSlot(res->tupledesc);
+ slot = MakeSingleTupleTableSlot(res->tupledesc, TTS_TYPE_MINIMALTUPLE);
while (tuplestore_gettupleslot(res->tuplestore, true, false, slot))
{
lrel->attnames[natt] =
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index 04985c9f91d..ee9adf74c31 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -208,7 +208,8 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
/* Triggers might need a slot */
if (resultRelInfo->ri_TrigDesc)
- estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL);
+ estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL,
+ TTS_TYPE_VIRTUAL);
/* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery();
@@ -271,8 +272,8 @@ slot_fill_defaults(LogicalRepRelMapEntry *rel, EState *estate,
}
for (i = 0; i < num_defaults; i++)
- slot->tts_values[defmap[i]] =
- ExecEvalExpr(defexprs[i], econtext, &slot->tts_isnull[defmap[i]]);
+ slot->values[defmap[i]] =
+ ExecEvalExpr(defexprs[i], econtext, &slot->nulls[defmap[i]]);
}
/*
@@ -307,7 +308,7 @@ static void
slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
char **values)
{
- int natts = slot->tts_tupleDescriptor->natts;
+ int natts = slot->tupleDescriptor->natts;
int i;
SlotErrCallbackArg errarg;
ErrorContextCallback errcallback;
@@ -325,7 +326,7 @@ slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
/* Call the "in" function for each non-dropped attribute */
for (i = 0; i < natts; i++)
{
- Form_pg_attribute att = TupleDescAttr(slot->tts_tupleDescriptor, i);
+ Form_pg_attribute att = TupleDescAttr(slot->tupleDescriptor, i);
int remoteattnum = rel->attrmap[i];
if (!att->attisdropped && remoteattnum >= 0 &&
@@ -337,11 +338,11 @@ slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
errarg.attnum = remoteattnum;
getTypeInputInfo(att->atttypid, &typinput, &typioparam);
- slot->tts_values[i] = OidInputFunctionCall(typinput,
- values[remoteattnum],
- typioparam,
- att->atttypmod);
- slot->tts_isnull[i] = false;
+ slot->values[i] = OidInputFunctionCall(typinput,
+ values[remoteattnum],
+ typioparam,
+ att->atttypmod);
+ slot->nulls[i] = false;
}
else
{
@@ -350,8 +351,8 @@ slot_store_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
* values (missing values should be later filled using
* slot_fill_defaults).
*/
- slot->tts_values[i] = (Datum) 0;
- slot->tts_isnull[i] = true;
+ slot->values[i] = (Datum) 0;
+ slot->nulls[i] = true;
}
}
@@ -371,7 +372,7 @@ static void
slot_modify_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
char **values, bool *replaces)
{
- int natts = slot->tts_tupleDescriptor->natts;
+ int natts = slot->tupleDescriptor->natts;
int i;
SlotErrCallbackArg errarg;
ErrorContextCallback errcallback;
@@ -390,7 +391,7 @@ slot_modify_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
/* Call the "in" function for each replaced attribute */
for (i = 0; i < natts; i++)
{
- Form_pg_attribute att = TupleDescAttr(slot->tts_tupleDescriptor, i);
+ Form_pg_attribute att = TupleDescAttr(slot->tupleDescriptor, i);
int remoteattnum = rel->attrmap[i];
if (remoteattnum < 0)
@@ -407,16 +408,16 @@ slot_modify_cstrings(TupleTableSlot *slot, LogicalRepRelMapEntry *rel,
errarg.attnum = remoteattnum;
getTypeInputInfo(att->atttypid, &typinput, &typioparam);
- slot->tts_values[i] = OidInputFunctionCall(typinput,
- values[remoteattnum],
- typioparam,
- att->atttypmod);
- slot->tts_isnull[i] = false;
+ slot->values[i] = OidInputFunctionCall(typinput,
+ values[remoteattnum],
+ typioparam,
+ att->atttypmod);
+ slot->nulls[i] = false;
}
else
{
- slot->tts_values[i] = (Datum) 0;
- slot->tts_isnull[i] = true;
+ slot->values[i] = (Datum) 0;
+ slot->nulls[i] = true;
}
}
@@ -586,7 +587,8 @@ apply_handle_insert(StringInfo s)
/* Initialize the executor state. */
estate = create_estate_for_relation(rel);
remoteslot = ExecInitExtraTupleSlot(estate,
- RelationGetDescr(rel->localrel));
+ RelationGetDescr(rel->localrel),
+ TTS_TYPE_VIRTUAL);
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
@@ -690,9 +692,11 @@ apply_handle_update(StringInfo s)
/* Initialize the executor state. */
estate = create_estate_for_relation(rel);
remoteslot = ExecInitExtraTupleSlot(estate,
- RelationGetDescr(rel->localrel));
+ RelationGetDescr(rel->localrel),
+ TTS_TYPE_VIRTUAL);
localslot = ExecInitExtraTupleSlot(estate,
- RelationGetDescr(rel->localrel));
+ RelationGetDescr(rel->localrel),
+ TTS_TYPE_HEAPTUPLE);
EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1);
PushActiveSnapshot(GetTransactionSnapshot());
@@ -731,7 +735,7 @@ apply_handle_update(StringInfo s)
{
/* Process and store remote tuple in the slot */
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
- ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false);
+ ExecCopySlot(localslot, remoteslot);
slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed);
MemoryContextSwitchTo(oldctx);
@@ -808,9 +812,11 @@ apply_handle_delete(StringInfo s)
/* Initialize the executor state. */
estate = create_estate_for_relation(rel);
remoteslot = ExecInitExtraTupleSlot(estate,
- RelationGetDescr(rel->localrel));
+ RelationGetDescr(rel->localrel),
+ TTS_TYPE_VIRTUAL);
localslot = ExecInitExtraTupleSlot(estate,
- RelationGetDescr(rel->localrel));
+ RelationGetDescr(rel->localrel),
+ TTS_TYPE_HEAPTUPLE);
EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1);
PushActiveSnapshot(GetTransactionSnapshot());
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 66cc5c35c68..8b803c77d5b 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -1071,7 +1071,7 @@ RunFromStore(Portal portal, ScanDirection direction, uint64 count,
uint64 current_tuple_count = 0;
TupleTableSlot *slot;
- slot = MakeSingleTupleTableSlot(portal->tupDesc);
+ slot = MakeSingleTupleTableSlot(portal->tupDesc, TTS_TYPE_MINIMALTUPLE);
dest->rStartup(dest, CMD_SELECT, portal->tupDesc);
diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c
index 50b34fcbc68..e833ed1f852 100644
--- a/src/backend/utils/adt/orderedsetaggs.c
+++ b/src/backend/utils/adt/orderedsetaggs.c
@@ -240,7 +240,8 @@ ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
}
/* Create slot we'll use to store/retrieve rows */
- qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc);
+ qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc,
+ TTS_TYPE_VIRTUAL);
}
else
{
@@ -398,17 +399,17 @@ ordered_set_transition_multi(PG_FUNCTION_ARGS)
nargs = PG_NARGS() - 1;
for (i = 0; i < nargs; i++)
{
- slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
- slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
+ slot->values[i] = PG_GETARG_DATUM(i + 1);
+ slot->nulls[i] = PG_ARGISNULL(i + 1);
}
if (osastate->qstate->aggref->aggkind == AGGKIND_HYPOTHETICAL)
{
/* Add a zero flag value to mark this row as a normal input row */
- slot->tts_values[i] = Int32GetDatum(0);
- slot->tts_isnull[i] = false;
+ slot->values[i] = Int32GetDatum(0);
+ slot->nulls[i] = false;
i++;
}
- Assert(i == slot->tts_tupleDescriptor->natts);
+ Assert(i == slot->tupleDescriptor->natts);
ExecStoreVirtualTuple(slot);
/* Load the row into the tuplesort object */
@@ -1200,11 +1201,11 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
ExecClearTuple(slot);
for (i = 0; i < nargs; i++)
{
- slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
- slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
+ slot->values[i] = PG_GETARG_DATUM(i + 1);
+ slot->nulls[i] = PG_ARGISNULL(i + 1);
}
- slot->tts_values[i] = Int32GetDatum(flag);
- slot->tts_isnull[i] = false;
+ slot->values[i] = Int32GetDatum(flag);
+ slot->nulls[i] = false;
ExecStoreVirtualTuple(slot);
tuplesort_puttupleslot(osastate->sortstate, slot);
@@ -1353,11 +1354,11 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
ExecClearTuple(slot);
for (i = 0; i < nargs; i++)
{
- slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
- slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
+ slot->values[i] = PG_GETARG_DATUM(i + 1);
+ slot->nulls[i] = PG_ARGISNULL(i + 1);
}
- slot->tts_values[i] = Int32GetDatum(-1);
- slot->tts_isnull[i] = false;
+ slot->values[i] = Int32GetDatum(-1);
+ slot->nulls[i] = false;
ExecStoreVirtualTuple(slot);
tuplesort_puttupleslot(osastate->sortstate, slot);
@@ -1371,9 +1372,10 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
* previous row available for comparisons. This is accomplished by
* swapping the slot pointer variables after each row.
*/
- extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc);
+ extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc,
+ TTS_TYPE_MINIMALTUPLE);
slot2 = extraslot;
-
+ /* FIXME: will store minimal into virtual slot! */
/* iterate till we find the hypothetical row */
while (tuplesort_gettupleslot(osastate->sortstate, true, true, slot,
&abbrevVal))
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index fcc8323f626..69d5622ed07 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5504,7 +5504,8 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
indexInfo = BuildIndexInfo(indexRel);
/* some other stuff */
- slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel));
+ slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel),
+ TTS_TYPE_HEAPTUPLE);
econtext->ecxt_scantuple = slot;
get_typlenbyval(vardata->atttype, &typLen, &typByVal);
InitNonVacuumableSnapshot(SnapshotNonVacuumable, RecentGlobalXmin);
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 041bdc2fa7e..ef2669c603b 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -933,7 +933,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc,
* scantuple has to point to that slot, too.
*/
state->estate = CreateExecutorState();
- slot = MakeSingleTupleTableSlot(tupDesc);
+ slot = MakeSingleTupleTableSlot(tupDesc, TTS_TYPE_VIRTUAL);
econtext = GetPerTupleExprContext(state->estate);
econtext->ecxt_scantuple = slot;
}
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index 2ab1815390c..5bcdca99601 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -826,4 +826,9 @@ 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);
+struct TupleTableSlot;
+extern void
+slot_deform_tuple(struct TupleTableSlot *slot, HeapTuple tuple, uint32 *offp, int natts);
+
+
#endif /* HTUP_DETAILS_H */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 45a077a949c..430e3ec6b27 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -342,8 +342,9 @@ ExecProject(ProjectionInfo *projInfo)
* Successfully formed a result row. Mark the result slot as containing a
* valid virtual tuple (inlined version of ExecStoreVirtualTuple()).
*/
- slot->tts_isempty = false;
- slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+ //FIXME: slot->isempty = false;
+ slot->flags &= ~TTS_ISEMPTY;
+ slot->nvalid = slot->tupleDescriptor->natts;
return slot;
}
@@ -431,12 +432,12 @@ extern void ExecScanReScan(ScanState *node);
/*
* prototypes from functions in execTuples.c
*/
-extern void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate);
-extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupleDesc);
+extern void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate, TupleTableSlotType st);
+extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate, TupleDesc tupleDesc, TupleTableSlotType st);
extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate,
- TupleDesc tupleDesc);
+ TupleDesc tupleDesc, TupleTableSlotType st);
extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
- TupleDesc tupType);
+ TupleDesc tupType, TupleTableSlotType st);
extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
extern TupleDesc ExecTypeFromExprList(List *exprList);
@@ -510,7 +511,8 @@ extern void ExecConditionalAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc, Index varno);
extern void ExecFreeExprContext(PlanState *planstate);
extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
-extern void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate);
+extern void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate,
+ TupleTableSlotType tp);
extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 8be0d5edc29..beeb630a4c1 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -110,42 +110,111 @@
* be touched by any other code.
*----------
*/
-typedef struct TupleTableSlot
+
+/* true = slot is empty */
+#define TTS_ISEMPTY (1 << 1)
+/* should pfree tts_tuple? */
+#define TTS_SHOULDFREE (1 << 2)
+/* should pfree tts_mintuple? */
+#define TTS_SHOULDFREEMIN (1 << 3)
+/* saved state for slot_deform_tuple */
+#define TTS_SLOW (1 << 4)
+/* fixed tuple descriptor */
+#define TTS_FIXED (1 << 5)
+
+
+typedef enum TupleTableSlotType
+{
+ TTS_TYPE_VIRTUAL,
+ TTS_TYPE_HEAPTUPLE,
+ TTS_TYPE_MINIMALTUPLE,
+ TTS_TYPE_BUFFER
+} TupleTableSlotType;
+
+struct TupleTableSlot;
+typedef struct TupleTableSlot TupleTableSlot;
+
+typedef struct TupleTableSlotOps
+{
+ void (*init)(TupleTableSlot *slot);
+ void (*release)(TupleTableSlot *slot);
+
+ void (*clear)(TupleTableSlot *slot);
+
+ void (*getsomeattrs)(TupleTableSlot *slot, int natts);
+
+ void (*materialize)(TupleTableSlot *slot);
+
+ HeapTuple (*get_heap_tuple)(TupleTableSlot *slot);
+ MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot);
+
+ HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot);
+ MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);
+
+} TupleTableSlotOps;
+
+extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
+
+extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
+ TupleTableSlot *srcslot);
+
+/* virtual or base type */
+struct TupleTableSlot
{
NodeTag type;
- bool tts_isempty; /* true = slot is empty */
- bool tts_shouldFree; /* should pfree tts_tuple? */
- bool tts_shouldFreeMin; /* should pfree tts_mintuple? */
- bool tts_slow; /* saved state for slot_deform_tuple */
- HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */
- TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */
- MemoryContext tts_mcxt; /* slot itself is in this context */
- Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */
- 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; /* minimal tuple, or NULL if none */
- HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
- long tts_off; /* saved state for slot_deform_tuple */
- bool tts_fixedTupleDescriptor; /* descriptor can't be changed */
-} TupleTableSlot;
-#define TTS_HAS_PHYSICAL_TUPLE(slot) \
- ((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))
+ uint16 flags;
+ uint16 nvalid; /* # of valid values in tts_values */
+
+ const TupleTableSlotOps *const cb;
+
+ TupleDesc tupleDescriptor; /* slot's tuple descriptor */
+
+ Datum *values; /* current per-attribute values */
+ bool *nulls; /* current per-attribute null flags */
+
+ /* can we optimize away? */
+ MemoryContext mcxt; /* slot itself is in this context */
+};
+
+
+typedef struct HeapTupleTableSlot
+{
+ TupleTableSlot base;
+ HeapTuple tuple; /* physical tuple */
+ uint32 off; /* saved state for slot_deform_tuple */
+} HeapTupleTableSlot;
+
+/* heap tuple residing in a buffer */
+typedef struct BufferHeapTupleTableSlot
+{
+ HeapTupleTableSlot base;
+ Buffer buffer; /* tuple's buffer, or InvalidBuffer */
+} BufferHeapTupleTableSlot;
+
+typedef struct MinimalTupleTableSlot
+{
+ TupleTableSlot base;
+ HeapTuple tuple; /* tuple wrapper */
+ MinimalTuple mintuple; /* minimal tuple, or NULL if none */
+ HeapTupleData minhdr; /* workspace for minimal-tuple-only case */
+ uint32 off; /* saved state for slot_deform_tuple */
+} MinimalTupleTableSlot;
/*
* TupIsNull -- is a TupleTableSlot empty?
*/
#define TupIsNull(slot) \
- ((slot) == NULL || (slot)->tts_isempty)
+ ((slot) == NULL || (slot)->flags & TTS_ISEMPTY)
/* in executor/execTuples.c */
-extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc);
-extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc);
+extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc, TupleTableSlotType st);
+extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc, TupleTableSlotType st);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
-extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
+extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc, TupleTableSlotType st);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
+
extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
TupleTableSlot *slot,
Buffer buffer,
@@ -153,22 +222,95 @@ extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
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 MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
-extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
+
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
TupleTableSlot *srcslot);
+
+static inline TupleTableSlot *
+ExecClearTuple(TupleTableSlot *slot)
+{
+ slot->cb->clear(slot);
+ return slot;
+}
+
+static inline HeapTuple
+ExecMaterializeSlot(TupleTableSlot *slot)
+{
+ slot->cb->materialize(slot);
+ return slot->cb->get_heap_tuple(slot);
+}
+
+static inline HeapTuple
+ExecCopySlotTuple(TupleTableSlot *slot)
+{
+ /*
+ * sanity checks
+ */
+ Assert(slot != NULL);
+ Assert(!(slot->flags & TTS_ISEMPTY));
+ return slot->cb->copy_heap_tuple(slot);
+}
+
+static inline MinimalTuple
+ExecCopySlotMinimalTuple(TupleTableSlot *slot)
+{
+ return slot->cb->copy_minimal_tuple(slot);
+}
+
/* in access/common/heaptuple.c */
-extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull);
-extern void slot_getallattrs(TupleTableSlot *slot);
-extern void slot_getsomeattrs(TupleTableSlot *slot, int attnum);
-extern bool slot_attisnull(TupleTableSlot *slot, int attnum);
+
+static inline Datum
+slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
+{
+ Assert(attnum > 0);
+
+ if (attnum > slot->nvalid)
+ slot->cb->getsomeattrs(slot, attnum);
+
+ Assert(attnum <= slot->nvalid);
+
+ *isnull = slot->nulls[attnum - 1];
+ return slot->values[attnum - 1];
+}
+
+static inline void
+slot_getallattrs(TupleTableSlot *slot)
+{
+ int tdesc_natts = slot->tupleDescriptor->natts;
+
+ /* Quick out if we have 'em all already */
+ if (slot->nvalid == tdesc_natts)
+ return;
+ slot->cb->getsomeattrs(slot, tdesc_natts);
+}
+
+
+static inline void
+slot_getsomeattrs(TupleTableSlot *slot, int attnum)
+{
+ /* FIXME: we're sometimes called with attnum == 0 which is wrong */
+ Assert(attnum >= 0);
+
+ if (attnum > slot->nvalid)
+ slot->cb->getsomeattrs(slot, attnum);
+}
+
+static inline bool
+slot_attisnull(TupleTableSlot *slot, int attnum)
+{
+ bool isnull;
+
+ slot_getattr(slot, attnum, &isnull);
+
+ return isnull;
+}
#endif /* TUPTABLE_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a953820f43a..d7c54057ae5 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -992,6 +992,9 @@ typedef struct ModifyTableState
OnConflictAction mt_onconflict; /* ON CONFLICT type */
List *mt_arbiterindexes; /* unique index OIDs to arbitrate taking
* alt path */
+
+ TupleTableSlot *mt_scan; /* input */
+
TupleTableSlot *mt_existing; /* slot to store existing target tuple in */
List *mt_excludedtlist; /* the excluded pseudo relation's tlist */
TupleTableSlot *mt_conflproj; /* CONFLICT ... SET ... projection target */
--
2.15.1.354.g95ec6b1b33.dirty
Hi!
Thank you for working on this subject. See some my comments below.
On Wed, Feb 21, 2018 at 1:43 AM, Andres Freund <andres@anarazel.de> wrote:
- TupleTableSlots have to contain all the necessary information for each
type of slot. Some implementations might require a buffer pin to be
hold (our heap), others pointers to multiple underlying points of data
(columns store), some need additional metadata (our MinimalTuple).Unifying the information into one struct makes it much harder to
provide differing implementations without modifying core postgres
and/or increasing overhead for every user of slots.I think the consequence of that is that we need to allow each type of
slot to have their own TupleTableSlot embedding struct.
+1, I think that it's reasonable to keep minimal common slot information in
TupleTableSlot struct and to let particular slot implementations to extend
it.
Haribabu's patch solved this by adding a tts_storage parameter that
contains additional data, but imo that's unconvincing due to the added
indirection in very performance critical codepaths.
+1
- The way slots currently are implemented each new variant data is
stored in slots adds new branches to hot code paths like
ExecClearTuple(), and they're not extensible.Therefore I think we need to move to callback based implementation. In
my tests, by moving the callback invocations into very thin inline
functions, the branch prediction accuracy actually goes sligthly
*up*.
Sounds interesting. Besides branch prediction accuracy, what can you
say about overall performance?
- Currently we frequently convert from one way of representing a row
into another, in the same slot. We do so fairly implicilty in
ExecMaterializeSlot(), ExecFetchSlotMinimalTuple(), ExecCopySlot()...The more we abstract specific storage representation away, the worse I
think that is. I think we should make such conversions explicit by
requiring transfers between two slots if a specific type is required.- We have a few codepaths that set fields on tuples that can't be
represented in virtual slots. E.g. postgres_fdw sets ctid, oid,
ExecProcessReturning() will set tableoid.In my POC I turned TupleTableSlot into basically an abstract base class:
struct TupleTableSlot
{
NodeTag type;uint16 flags;
uint16 nvalid; /* # of valid values in tts_values
*/const TupleTableSlotOps *const cb;
TupleDesc tupleDescriptor; /* slot's tuple descriptor
*/Datum *values; /* current per-attribute values */
bool *nulls; /* current per-attribute null
flags *//* can we optimize away? */
MemoryContext mcxt; /* slot itself is in this context
*/
};which then is inherited from for specific implementations of tuple
slots:typedef struct HeapTupleTableSlot
{
TupleTableSlot base;
HeapTuple tuple; /* physical tuple */
uint32 off; /* saved state for
slot_deform_tuple */
} HeapTupleTableSlot;...
typedef struct MinimalTupleTableSlot
{
TupleTableSlot base;
HeapTuple tuple; /* tuple wrapper */
MinimalTuple mintuple; /* minimal tuple, or NULL if none */
HeapTupleData minhdr; /* workspace for minimal-tuple-only case */
uint32 off; /* saved state for
slot_deform_tuple */
} MinimalTupleTableSlot;typedef struct TupleTableSlotOps
{
void (*init)(TupleTableSlot *slot);
void (*release)(TupleTableSlot *slot);void (*clear)(TupleTableSlot *slot);
void (*getsomeattrs)(TupleTableSlot *slot, int natts);
void (*materialize)(TupleTableSlot *slot);
HeapTuple (*get_heap_tuple)(TupleTableSlot *slot);
MinimalTuple (*get_minimal_tuple)(TupleTableSlot *slot);HeapTuple (*copy_heap_tuple)(TupleTableSlot *slot);
MinimalTuple (*copy_minimal_tuple)(TupleTableSlot *slot);} TupleTableSlotOps;
when creating a slot one has to specify the type of slot one wants to
use:typedef enum TupleTableSlotType
{
TTS_TYPE_VIRTUAL,
TTS_TYPE_HEAPTUPLE,
TTS_TYPE_MINIMALTUPLE,
TTS_TYPE_BUFFER
} TupleTableSlotType;extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc,
TupleTableSlotType st);
Can user define his own slot type? If so, I assume that hard-coded
enum is a limitation of current prototype, and eventually we would have
an extendable array of slot types. Is it correct?
You might notice that I've renamed a few fields (tts_values -> values,
tts_isnull -> nulls, tts_nvalid -> nvalid) that haven't actually changed
in the above structs / the attached patch. I now think that's probably
not a good idea, unnecessarily breaking more code than necessary.
For me "tss_" prefix looks a bit weird, but I agree that we probably should
refrain from renaming this in order to break as small number of things as
possible.
I generally like this scheme because it allows the TupleTableStruct to
be relatively lean, without knowing much about specific implementations
of slots.After this change functions like slot_getattr are thin inline wrappers: +static inline Datum +slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull) +{ + Assert(attnum > 0); + + if (attnum > slot->nvalid) + slot->cb->getsomeattrs(slot, attnum); + + Assert(attnum <= slot->nvalid); + + *isnull = slot->nulls[attnum - 1]; + return slot->values[attnum - 1]; +}Note that I've moved system attribute handling out of the slot_getattr
path - there were a few paths accessing them via slot_getattr, and it's
a lot of unnecessary branching.The fact that slots of different types have different storage means that
we can't just change the type of a slot when needed. In nearly all
places that's not a problem. E.g. ExecHashTableInsert() converts the
"incoming" slot to one containing a minimal tuple with
ExecFetchSlotMinimalTuple(), but that's essentially transient as it's
just used to copy the tuple into the hashtable. We could even gain
quite some efficiency by directly copying into the allocated space
(saving one serialization/copy step for the common case where input is
either a virtual or a heap tuple).The slightly bigger issue is that several parts of the code currently
require heaptuples they can scribble upon. E.g. nodeModifyTable.c
materializes the tuple - those can be solved by using a local slot to
store the tuple, rather than using the incoming one. In nearly all cases
we'd still be left with the same number of tuple copy steps, as
materializing the tuple with copy into the local storage anyway. A few
other cases, e.g. intorel_receive(), just can use ExecCopySlotTuple() or
such instead of materialize.The biggest issue I don't have a good answer for, and where I don't like
Haribabu's solution, is how to deal with system columns like oid, ctid,
tableoid. Haribabu's patch adds storage of those to the slot, but that
seems like quite the cludge to me. It appears to me that the best thing
would be to require projections for all accesses to system columns, at
the original level of access. Then we don't need any sort of special
codepaths dealing with them. But that's not exactly a trivial change and
has some added complications too (junkfilter type things).
I also think that projections for accessing system columns is probably
the best solution for them.
I made a reference to vectorized execution above. That's because that I
found, while prototyping vectorized execution, that it's a very common
need to interact with parts of the system that aren't vectorized, and
doing so has to be extremely cheap. With the above we can have a
TupleTableSlot type that's essentially just a cheap cursor into a batch
of tuples. Besides making it efficiently possible to hand of slots to
part of the system that can't deal with tuple batches, it allows to do
fun things like having the first slot_deform_tuple() call for one tuple
in a batch to deform a full page's worth of tuples, yielding *much*
better cache access patterns.
Are you planning to use custom slots only for interaction between
vectorized and non-vectorized parts of executor, or also for interaction
inside vectorized part of executor. It seems that proposed interface
isn't suitable for interaction between two vectorized nodes. Are you
planning to introduce special kind of vectorized slots or whatever?
I haven't done that, but I think we should split ExecStoreTuple() into
multiple versions, that only work on specific types of tuple
slots. E.g. seqscan et al would call ExecStoreBufferHeapTuple(), other
code dealing with tuples would call ExecStoreHeapTuple(). The relevant
callers already need to know what kind of tuple they're dealing with,
therefore that's not a huge burden.Please please note that the attached patch is *NOT* meant to be a full
proposal or even a to be reviewed patch, it's just an illustration of
the concepts I'm talking about.Comments? Better ideas?
As I get, your primary interest in this topic is vectorized execution. When
passing data from vectorized to non-vectorized parts of executor tuples are
materialized by moving them from vectorized slot to regular slot.
But what do you thing about alternative tuple formats in custom row-oriented
table access methods? It's likely we should try to minimize tuple format
conversion. Thus, some executor nodes can switch their tuple format to
match table access method tuple format. For instance, we're running
aggregate over table with custom table access method. Then hash
aggregate node may accumulate tuples in the same format as they are
received from table access method. Does it make sense?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Andres Freund wrote:
Hi,
I've recently been discussing with Robert how to abstract
TupleTableSlots in the light of upcoming developments around abstracting
storage away. Besides that aspect I think we also need to make them
more abstract to later deal with vectorized excution, but I'm more fuzzy
on the details there.
Hi, is this patch proposed for pg11?
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2018-03-13 15:18:34 -0300, Alvaro Herrera wrote:
Andres Freund wrote:
Hi,
I've recently been discussing with Robert how to abstract
TupleTableSlots in the light of upcoming developments around abstracting
storage away. Besides that aspect I think we also need to make them
more abstract to later deal with vectorized excution, but I'm more fuzzy
on the details there.
Hi, is this patch proposed for pg11?
I wish, but I don't see it happening unless I get a time compression
device from somewhere :(
Greetings,
Andres Freund
On Wed, Feb 21, 2018 at 4:13 AM, Andres Freund <andres@anarazel.de> wrote:
In my POC I turned TupleTableSlot into basically an abstract base class:
Here's set of patches which move this POC forward towards completion,
not yet complete though.
Here's what has changed.
1. Fixed all the regression failures while running make check from
regress and postgres_fdw.
2. Added/modified a bunch of comments about the new TupleTableSlot
structure. Modified comments in existing functions working with
TupleTableSlots as per the new implementation. Added comments
explaining each callback in TupleTableSlotOps, and their
implementations.
3. Improved error messages in some TupleTableTypeSlot specific implementations.
4. Fixed some coding errors in the POC patch (which didn't show up as
a failure in regression)
You might notice that I've renamed a few fields (tts_values -> values,
tts_isnull -> nulls, tts_nvalid -> nvalid) that haven't actually changed
in the above structs / the attached patch. I now think that's probably
not a good idea, unnecessarily breaking more code than necessary.
Done that. All members of TupleTableSlot have their names starting with tts_
After this change functions like slot_getattr are thin inline wrappers: +static inline Datum +slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull) +{ + Assert(attnum > 0); + + if (attnum > slot->nvalid) + slot->cb->getsomeattrs(slot, attnum); + + Assert(attnum <= slot->nvalid); + + *isnull = slot->nulls[attnum - 1]; + return slot->values[attnum - 1]; +}Note that I've moved system attribute handling out of the slot_getattr
path - there were a few paths accessing them via slot_getattr, and it's
a lot of unnecessary branching.
The system attributes are accessed via heap_getsysattr() by passing
that function a tuple from HeapTupleTableSlot or
BufferHeapTupleTableSlot. Those places Assert that the slot being used
is either HeapTupleTableSlot or BufferHeapTupleTableSlot.
slot_getsysattr() which accessed system attributes through a lot is
now removed and replaced with heap_getsysattr as described.
The slightly bigger issue is that several parts of the code currently
require heaptuples they can scribble upon. E.g. nodeModifyTable.c
materializes the tuple - those can be solved by using a local slot to
store the tuple, rather than using the incoming one. In nearly all cases
we'd still be left with the same number of tuple copy steps, as
materializing the tuple with copy into the local storage anyway. A few
other cases, e.g. intorel_receive(), just can use ExecCopySlotTuple() or
such instead of materialize.
There is a patch changing callers of ExecMaterializeSlot() to use
ExecGetHeapTupleFromSlot() or ExecSaveSlot(), new functions added by
me. But I think that needs more work. I don't think the new functions
are necessary. We could use ExecCopySlotTuple(), ExecFetchSlotTuple()
in place of ExecGetHeapTupleFromSlot() appropriately. The callers who
just want the tuple and not necessarily a writable one, can use
ExecFetchSlotTuple(). Those who want a writable copy can use
ExecCopySlotTuple(). When the slot is *not* heap or buffer heap tuple
table slot, we materialize a heap tuple every time we want to get the
heap tuple out of the slot. If we are doing this multiple times
without changing the contents of the slot, we will leak memory. I have
TODOs to check whether that's a problem in practice. May be we should
use ExecCopySlotTuple() explicitly while fetching tuples from those
kinds of slot. Same is case while fetching a minimal heap tuple from
non minimal heap tuple slots.
The biggest issue I don't have a good answer for, and where I don't like
Haribabu's solution, is how to deal with system columns like oid, ctid,
tableoid. Haribabu's patch adds storage of those to the slot, but that
seems like quite the cludge to me. It appears to me that the best thing
would be to require projections for all accesses to system columns, at
the original level of access. Then we don't need any sort of special
codepaths dealing with them. But that's not exactly a trivial change and
has some added complications too (junkfilter type things).
I studied the code extensively to check whether there are any cases
where we fetch system columns using (Var nodes with varattno < 0) from
(headers of ) tuples produced by non-scan nodes (e.g. join,
aggregation). That's not the case. We do have instances of INNER,
OUTER Var with varattno < 0, but those are the cases when trigger
accesses OLD and NEW tuples. I have a patch to add tests for the same.
I haven't done that, but I think we should split ExecStoreTuple() into
multiple versions, that only work on specific types of tuple
slots. E.g. seqscan et al would call ExecStoreBufferHeapTuple(), other
code dealing with tuples would call ExecStoreHeapTuple(). The relevant
callers already need to know what kind of tuple they're dealing with,
therefore that's not a huge burden.
I thought so too, but haven't done that change right now. Will work on
that. That's in my TODO list.
Here's description of patches.
0001, 0002 are the same patches as submitted in [1]/messages/by-id/CAFjFpRerUFX=T0nSnCoroXAJMoo-xah9J+pi7+xDUx86PtQmew@mail.gmail.com.
0003 changes tts_nvalid from int to AttrNumber. int would take at
least 4 bytes whereas the maximum value that tts_nvalid can take is
restricted by AttrNumber which 2 byte long.
0004 removed TupleDescGetSlot() function, which is obsolete, but
retained for extensions. With TupleTableSlot abstraction, I think the
extensions need to change anyway, so removing that function
altogether.
0005 packs various boolean members from TupleTableSlot structure into
a uint16 tts_flags, with each bit for each one bool member. I think we
can remove SHOULDFREEMIN since SHOULDFREE on MinimalHeapTupleTableSlot
can be used in place of SHOULDFREEMIN. I will do that in the next
version. This reduces the size of TupleTableSlot structure and can be
committed as an independent patch. There's also possibility that only
the BufferHeapTupleTableSlot will have ~SHOULDFREE but all other slots
have SHOULDFREE set, so we can get rid of that flag altogether. But I
need to verify that.
0006 This is improved version of your POC patch. I have changed the
tuple table slot type in many places which showed up as regression
failures. Added/modified a bunch of comments, improved error messages,
corrected code at some places, addressed FIXME's at some places. Also
added macros to check the TupleTableSlot's type based on
TupleTableSlotOps set.
0007 adds tts_type member indicating TupleTableSlot type readily
avaialble for debugging. We may or may not commit this patch.
0008 changes the place where we reset expression context used for
processing RETURNING and ON CONFLICT clause. If a query has both the
clauses, we reset the same expression twice while processing same
input/output tuple. This means that some memory allocated while
processing ON CONFLICT and required by RETURNING will get freed before
the later gets at it. This looks like a bug in the existing code, but
I have not been able to create a reproduction for the same. It showed
up as a regression failure with TupleTableAbstraction changes.
0009 Replaces ExecMaterializeSlot as described above. I will work on
this again and examine all the instances of ExecMaterializeSlot as
described above.
0010 Index scan uses reorder queue in some cases. Tuples are stored
reorder queue as heap tuples without any associated buffer. While
returning a tuple from reorder queue, IndexNextWithReorder() should
use TupleTableSlot of type HeapTupleTableSlot. A tuple returned
directly from an index scan has a buffer associated with it, so should
use
TupleTableSlot of type BufferHeapTupleTableSlot. So, use different
kinds of slots for an index scan.
Next steps
1. Address TODO in the code. I have listed some of those above.
2. Right now we are using TupleTableSlotType, an enum, to create slots
of required type. But extensions which want to add their own slot
types won't be able to add a type in that enum. So, they will need to
write their own MakeTupleTableSlot. That function uses the
TupleTableSlotType to set TupleTableSlotOps and calculate the minimum
size of slot. We could change the function to accept TupleTableSlotOps
and the minimum size and it just works for all the extensions. Or it
could just accept TupleTableSlotOps and there's a callback to
calculate minimum memory required for the slot (say based on the tuple
descriptor available).
3. compile with LLVM and fix any compilation and regression errors.
4. We need to do something with the name ExecStoreVirtualSlot(), which
is being (and will be) used for all kinds of TupleTableSlot type.
Right now I don't have any bright ideas.
5. ExecFetch* functions are now one liners, so we could make those
inline and keep those in header file like, say slot_getattr.
6. ExecCopySlot can be a thin wrapper if we add a callback copyslot()
and invoked on the destination slot type.
7. slot_attisnull() deforms a heap/minimal tuple if that status for
given attribute is not available tts_isnull. Should we instead add a
callback attisnull() which can use something like heap_isnull()?
There are TODOs in the patches for 4, 5, 6, 7.
I will continue to work on these steps.
[1]: /messages/by-id/CAFjFpRerUFX=T0nSnCoroXAJMoo-xah9J+pi7+xDUx86PtQmew@mail.gmail.com
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
On Mon, Jun 11, 2018 at 6:13 PM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:
The slightly bigger issue is that several parts of the code currently
require heaptuples they can scribble upon. E.g. nodeModifyTable.c
materializes the tuple - those can be solved by using a local slot to
store the tuple, rather than using the incoming one. In nearly all cases
we'd still be left with the same number of tuple copy steps, as
materializing the tuple with copy into the local storage anyway. A few
other cases, e.g. intorel_receive(), just can use ExecCopySlotTuple() or
such instead of materialize.There is a patch changing callers of ExecMaterializeSlot() to use
ExecGetHeapTupleFromSlot() or ExecSaveSlot(), new functions added by
me. But I think that needs more work. I don't think the new functions
are necessary. We could use ExecCopySlotTuple(), ExecFetchSlotTuple()
in place of ExecGetHeapTupleFromSlot() appropriately. The callers who
just want the tuple and not necessarily a writable one, can use
ExecFetchSlotTuple(). Those who want a writable copy can use
ExecCopySlotTuple(). When the slot is *not* heap or buffer heap tuple
table slot, we materialize a heap tuple every time we want to get the
heap tuple out of the slot. If we are doing this multiple times
without changing the contents of the slot, we will leak memory. I have
TODOs to check whether that's a problem in practice. May be we should
use ExecCopySlotTuple() explicitly while fetching tuples from those
kinds of slot. Same is case while fetching a minimal heap tuple from
non minimal heap tuple slots.
In the attached set of patches, the ExecMaterializeSlot(),
ExecFetchSlotTuple() and ExecFetchSlotMinimalTuple() have been
changed.
Here's excerpt of the commit messages included in 0006 in the attached
patch-set.
---
Before this work, the contents of the slot could be in virtual form
(in tts_datums and tts_isnull) and/or be stored as a heap tuple and/or
a minimal tuple. When the contents were in heap or minimal tuple
form, those tuples need not be "owned" by the slot. When a tuple is
owned by a slot, it's slot's responsibility to free the memory
consumed by the tuple when the tuple is not needed. That typically
happened when the slot got a new tuple to store in it or when the slot
itself was discarded, mostly along-with the other slots in tuple
table.
Then ExecMaterializeSlot() served two purposes: 1. If the contents of
the slot were not "owned" by the slot, or if there was no heap tuple
in the slot, it created a heap tuple which it "owned" from those
contents. 2. It returned the heap tuple, which the slot "owned". This
was typically used when the caller wanted, from the slot, a heap tuple
which it can upon (esp. system attributes like tableOid) and expected
it to be visible from within the slot.
Since a single slot contained a tuple in various forms, it could "own"
multiple forms at a time and return whichever was requested when
ExecFetchSlotTuple() or ExecFetchSlotMinimalTuple() was called. Those
functions, however, differed from ExecMaterializeSlot() in the sense
that they returned a tuple even when the slot didn't own it.
With this work, every slot contains a tuple in "virtual" form, but
only one of the other two forms. Thus ExecMaterializeSlot() can not
create a heap tuple, and let the slot "own" it, in a slot that can not
contain a heap tuple. Neither ExecFetchSlotTuple() can return a tuple
from a slot which can not contain a heap tuple without consuming
memory (slots with minimal tuple can do some pointer swizzling and
save memory but that lump of memory is not exactly same as a heap
tuple memory lump.). Since such a heap tuple can not be "owned" by the
slot, the memory consumed by the tuple needs to be freed by the
caller. Similarly for ExecFetchSlotMinimalTuple().
Hence, in the new scheme the meaning of ExecMaterializeSlot(),
ExecFetchSlotTuple() and ExecFetchSlotMinimalTuple() needs to change.
ExecMaterializeSlot()
---------------------
ExecMaterializeSlot() should save the contents of the slot into its
native tuple format, whichever it is, so that it "owns" the tuple.
ExecFetchSlotTuple()
--------------------
ExecFetchSlotTuple() should just return the heap tuple when the
underlying slot can "own" a heap tuple and error out when slot can not
contain the requested format e.g. when ExecFetchSlotTuple() is called
on a "virtual" or "minimal" tuple slot. This is in-line with what we
are doing with ExecStore* functions. The earlier callers of
ExecMaterializeSlot(), which expected a heap tuple to be returned,
require to call ExecMaterializeSlot() (no return value) followed by
ExecFetchSlotTuple(). Instead of that ExecFetchSlotTuple() is changed
to accept a second argument "materialize". When it's true, the
returned heap tuple is materialized and "owned" by the slot. When it's
false, the returned heap tuple may not be "owned" by the slot. All the
callers of ExecFetchSlotTuple() can be managed to pass a
HeapTupleTableSlot or a BufferHeapTupleTableSlot. Instead of relying
on the TupleTableSlotType (which may be expanded later for user
defined TupleTableSlots) we rely on the presence get_heap_tuple()
callback in the given slot. That callback is expected to return a heap
tuple as is from the slot.
In a few cases, ExecFetchSlotTuple() is called with a slot which is
filled by tuplestore_gettupleslot(). We use a separate tuple store to
store foreign table tuples for processing AFTER triggers and for
storing tuples returned by a function. They are stored in the tuple
store as minimal tuples. In both the cases, the minimal tuples are
converted to heap tuple (using earlier version of
ExecFetchSlotTuple()) before they are processed further after
obtaining those from a tuple store using tuplestore_gettupleslot().
With the tuple table store abstraction a minimal tuple can be stored
in a MinimalTupleTableSlot. Thus we have two options
1. Pass a MinimalTupleTableSlot to tuplestore_gettupleslot() and get a
minimal tuple in this slot. Convert the minimal tuple to a heap tuple
by allocating memory for the converted tuple. We have to explicitly
release the memory allocated for the heap tuple after it's been
processed. When do we do that exactly in AfterTriggerExecute() is not
clear. ExecFetchSlotTupleDatum() could do that right away.
2. Pass a HeapTupleTableSlot to tuplestore_gettupleslot() and let it
store the heap tuple crafted from minimal tuple, which can be freed
within the same function, if necessary. The heap tuple gets freed when
the slot is used to store the next tuple to be processed.
The second option looks better since it continues to use slot's
mechanism to "own" and free tuples that it "owns". Hence implemented
the same.
ExecFetchSlotMinimalTuple()
---------------------------
Before this work, a TupleTableSlot could "own" a minimal tuple as
well. Thus ExecFetchSlotMinimalTuple() returned a minimal tuple after
constructing and "owning" it if it was not readily available. When the
slot "owned" the minimal tuple, the memory consumed by the tuple was
freed when a new tuple was stored in it or the slot was cleared.
With this work, not all TupleTableSlot types can "own" a minimal
tuple. When fetching a minimal tuple from a slot that can not "own" a
tuple, memory is allocated to construct the minimal tuple, which needs
to be freed explicitly. Hence ExecFetchSlotMinimalTuple() is modified
to flag the caller whether it should free the memory consumed by the
returned minimal tuple.
Right now only a MinimalTupleTableSlot can own a minimal tuple. But we
may change that in future or a user defined TupleTableSlot type (added
through an extension) may be able to "own" a minimal tuple in it.
Hence instead of relying upon TTS_IS_MINIMAL() macro to tell us
whether the TupleTableSlot can "own" a minimal tuple or not, we rely
on the set of callbacks. A TupleTableSlot which can hold a minimal
tuple implements get_minimal_tuple callback. Other TupleTableSlot
types leave the callback NULL.
The minimal tuple returned by this function is usually copied into a
hash table or a file. But, unlike ExecFetchSlotTuple() it's never
written to. Hence the difference between signatures of the two
functions.
---
I haven't done that, but I think we should split ExecStoreTuple() into
multiple versions, that only work on specific types of tuple
slots. E.g. seqscan et al would call ExecStoreBufferHeapTuple(), other
code dealing with tuples would call ExecStoreHeapTuple(). The relevant
callers already need to know what kind of tuple they're dealing with,
therefore that's not a huge burden.I thought so too, but haven't done that change right now. Will work on
that. That's in my TODO list.
This is still a TODO.
0001-0005 are same as the previous patch set.
0006 This is improved version of your POC patch. I have changed the
tuple table slot type in many places which showed up as regression
failures. Added/modified a bunch of comments, improved error messages,
corrected code at some places, addressed FIXME's at some places. Also
added macros to check the TupleTableSlot's type based on
TupleTableSlotOps set.
The changes to ExecMaterializeSlot(), ExecFetchSlotTuple() and
ExecFetchSlotMinimalTuple() discussed above are included in this
patch.
0007, 0008 are same as previous patch set.
0009 Replaces ExecMaterializeSlot as described above. I will work on
this again and examine all the instances of ExecMaterializeSlot as
described above.
This is merged with 0006.
0010 Index scan uses reorder queue in some cases. Tuples are stored
reorder queue as heap tuples without any associated buffer. While
returning a tuple from reorder queue, IndexNextWithReorder() should
use TupleTableSlot of type HeapTupleTableSlot. A tuple returned
directly from an index scan has a buffer associated with it, so should
use
TupleTableSlot of type BufferHeapTupleTableSlot. So, use different
kinds of slots for an index scan.
This is 0009
Next steps
1. Address TODO in the code. I have listed some of those above.
There are still a handful of TODOs in the patches. I will work on those next.
2. Right now we are using TupleTableSlotType, an enum, to create slots
of required type. But extensions which want to add their own slot
types won't be able to add a type in that enum. So, they will need to
write their own MakeTupleTableSlot. That function uses the
TupleTableSlotType to set TupleTableSlotOps and calculate the minimum
size of slot. We could change the function to accept TupleTableSlotOps
and the minimum size and it just works for all the extensions. Or it
could just accept TupleTableSlotOps and there's a callback to
calculate minimum memory required for the slot (say based on the tuple
descriptor available).
This is still a TODO.
3. compile with LLVM and fix any compilation and regression errors.
When I compiled server with just 0003 applied with LLVM, the
compilation went well, but there was a server crash. That patch
changes type of tts_nvalid from int32 to AttrNumber. I tried debugging
the crash with a debug LLVM build, but couldn't complete the work.
Attached patch attrnumber_llvm_type.patch is my incomplete attempt to
fix that crash. I think, we should make it easy to change the data
types of the members in structures shared by JIT and non-JIT code, may
be automatically create both copies of the code somehow. I will get
back to this after addressing other TODOs.
4. We need to do something with the name ExecStoreVirtualSlot(), which
is being (and will be) used for all kinds of TupleTableSlot type.
Right now I don't have any bright ideas.
Still a TODO.
5. ExecFetch* functions are now one liners, so we could make those
inline and keep those in header file like, say slot_getattr.
This is still a TODO.
6. ExecCopySlot can be a thin wrapper if we add a callback copyslot()
and invoked on the destination slot type.
This is still a TODO.
7. slot_attisnull() deforms a heap/minimal tuple if that status for
given attribute is not available tts_isnull. Should we instead add a
callback attisnull() which can use something like heap_isnull()?
This is still a TODO.
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
pg_abstract_tts_patches_v2.tar.zipapplication/zip; name=pg_abstract_tts_patches_v2.tar.zipDownload
� H�=[ �\{s�F���������| ��;�ci�;��J�z���Xx%D$�@���>��c�"�vl�f/���AOOwOO�oz]Om'Ic�M�i�L#;uodr��3~t��F�6F���}�i���9��tc���b�9�h�,����x`'7�4Ln���w����:�t�&������Tv�e4�m�M�a�M�n,�e���;��E�G*}���C�I�#�����:�3��`�@�_C ���
:�g:�7������g�;�������������`2�a .e$����C�O�A��9�JF��
V ��d�s��3�2�b?���s��S��!R���!��P3�����C�m}`�����I�����7�W?�(������ ��u
2AA�)�:E
�N���N
���'c��K��=M;<�A$��e�*���$\H��p�'���CiK/k�qs;7������&\��Vp+ ]8�����d��n|`����kak<d��GG��������{�}� ����k������D���������c%-�8���;_i�5��/�r����Q����v@@;�����c�2����� �n���D=�����h�jho���Z&�&J�8��p�e���n���$v����w ?H��A�I�/w��L�]�t��O��;_z�J��������hk�/f>�{c�����1$2F�V{�#���s������"���&s���N�4��nX���z��2=��e�B��������g�DJD�_Cj��.^�C�)N��Hh_��CJ [W<x���O�)�|���tCp)��A��"�oz�!���GZw�<B���C��fm��?���'����/���_�����<$k ci�-�3��!������}�~���O�r��V1NE��8����_���^u(�
y,�v/
�E�`��j@���E���'iS�G�f�����; <���,����n��f�|�T��S'j����c�Wv�� qu�1x
� g\���-zB����/de�*jU8��a.m�K�
F ��i��j_O����IA��l���,����Ih��U ��j(��`�)]�v�����
O3�'Loz-S���{G����{4�h= �������J�tsd�h��Lq�����a��wR@g�0�"��-�$��x����+=\J�5.�=`�0�0)�M��G�Q������ j$%*�0�GhOvH�,�����g���5`���$r6���}5D��������xD2��d�� |���s5�����[�2��5�����@m��.�W�xs2}�����b��$N��~~r(��B\���c!����8�6Y6�
�����3��r������ �o�?���H��>�:���!���!�����^�P��^-��}��s��$����C�,���U#I`����� ���p~1H4�j����%6��n�J�0�0.�����a�
�Q��4h�R]PSw�F�
�����BQ���Oc/y4�z$�c2��y���l�e�,��m����7Wo��<!��[fb!Ss|�l�������]������8�d3Hj�PDG�m��M��A}�����I�x�x����R�H��P�K�{�����f���1�i�Rt���������`�qC8��CG��Dw�BZ�7�Y�����������z�
.�vc �#I��La@~����$,��� ����?�1tkh��?�p�'��5>��8�p�w��7l���'��2\C:�Lz��t�e���s����/�������?l����N�����p�@���S(�D0��G�g���FbfsC?�A�(�X�2�Bd ��tBw�
�����X�X�L�3��x�W�h�Y�O�l���q�W��BB���~�V�e������&Y[�����+���`�'��_��*,0�h���w���9Y�@ b��]L9�%��L�����}��B��3�xL q��r;�d ��s�/I�kd!%�E
B5t<�O.�<Z��x|�LB���,�tLNRJK ��6:���4��8�[������u�����P��P"�L�V�����4Vb���?E=�s� W+4VB43?
A���t�$rz/���R�����Q��FB�;�m�S��j�n-9[�I$J�*t��6&�]�n���LU3��ZN����7���I����@�L������U�k���m2��f�I�'ug<6G;�h*[@�J;�*-�����s�VV�R�~b(��zA�|���ZI��
��<�y�������!�rTi�p a����s��[+7�Cb�AS���u���$?{>����f��������o��E�}cLu��C��R�K��9���gd��b���H*!Q�3t��YCfo`��w�t���� ������������x��+rl!�@�LW�������2l�?���������{u�Py��X2����3R����*���q�������T/N.O���+�����G� ��f��>��<M�����:?���B�%��CS:��xb��1kN]�hl&�u��0>Qh/8 �[�!ia�*�T��+,Z�)+,=2 �Mi���"i{�|�F-p�C��=pA�l�+�2��"E�x��`��7),����D ��+H���k����gI�c@��o��B�lZ���yD��D"�ZV���]��V=�-����D�H�wG��5���S�G�D�c#PQ����Pq_���?�G�D��hT�����&=4�-C�����_�f�A�s�wggC��<n��q�G�W*}���"R ,R���'|�+�xD0��pS�5�a6I�-����G�$X������\FSa��=�Z�L�:`��K� �$��h�%��l�X��#4?���9�,
���pW��'j*7�
#��h��^}����w"�X;� �i"�3��4��;����ic��� Mk�!+"����~ 7 m}��a������N�����=�/��Y��u��-�_����4��>���F�|�����3���X
������9���� �Z�}�����:���#}6L��7rf�91<����
�3������������l���A^���P���03NA�z�85�M�>�e 0[}�����(����� ���t%�a�� ��F �{W
�a(3A�-�# �?���Y��rx��3��(��;O�|,�"X��x����R�� ��.�� ��8�q�bi�(�N0��q�����vPs����uF�K����{lB��"��Tj���dTJ���� N���u��� 51\G�&��/}�"�f
9��Si`_�#9�{=�;#{�O5S�����Ro�#�~P����4����A+��na�}z��K�V�B2�A���������#o�� �rV������
�p��e���%!�/_�+ �XN#� ����R��h��@gzC�y��1��
�����A*�D�U�G��X��H�l��>7�F�mS��-��� ���/�:��5�z��������Q�>(� xI�D����
����*Do%?��%�
�`�XU9��a���R�).G�����4�?�e*o�����eu������''�����*4��%+:=����mfTi��7&Y��� �\��pt�H
���_�"gp
<�h�H��RyW_�]��dZh����E�q��}Yb��f%��9���2��R|��Y�U���Z����R�o�K���
�,��+���2s#�'���z(�A��w�� k`�z���\�k'^
�[_�'��J��]�8�qoi������H��\'���.�1�j�d�>�=N�/e�)�hXM��N
D��pq^��!��OD�[��:hE� F0�����F,Y �����I,�]�� ���M�:^���)��O���M��S�cP��������%��n���nV b�J��]X�����y�iU��)��k������q��R2���c��vR�?JT�z�a�N�D��en��"��3tIUq�-r[W1R�;.u
�;����E��'��Z��0������j�����e��_�2������Z����j�H����������}���<%���v;�g6��o������Jn����Q������G����W��[`�������b��;���2?Pi5�L0������_yZ4E�[=�g�C~�o-��C��� ��bIG���d��!��O���
88����kd�v|�Q M���������\F�o;�� _/70��Fv*����F��sG����T����X��*�.�V����3}�'Wn�I�"��`<*�*K�O��>p��H�6�G�u�s���i�T�����>{����O���OU
��%$�.� �'h�C�B{hL6]Y#�Hc��o�GP��x�CR�pXR8���h<����7�^�x���A�o*o���N����g�l�&��s>)+�.S'9�V��w����s�����rWay!v?�;��B�8��������#
�Z=���������G�t���Ne����B2������X�B�2_)��[��=����nT���MA!+��#Q��,��i�-t,�L��*�������0����9���rw0������:6 ��O�s��$�/�_`y;aq��]G�����F0W���ci{+&4C'�����'�>�c'yt��Uo�bJ�4L�u$�)��,�F[������E� ���I���{��=������-$�ya7R�~�{�o��x�AN��������u����V�}���TE��L��}�L��u.#�M��� �����9�y.:![�pe���^U@_��O�i�U���q$��N���-d<v��P?���5 ;*��?�E���J�#<�0�zF��c��Q�UR�(����� Gi�����V����"eEk���������^�E�`&I���j��9#�"��P�r��j��A�)5{�{-QWvV'������F���n��9:_���+��u��
w�s~���S�����S��e������&����Y�q��^o���=�m4���Z�o�k�/e*o�+�����,���1z�K��K�[�-1 �� i���d��3���� �m��������'��9���S�k���$`UB8k����w��xN�E���db:����X}�K�=h'���=��|�0��;�D �'=�*UjAx��=���j��HR�:��!6���6Q��k��%�]k]hp���`%-��\�C��������&��`O~B��D����t�W]2����\����=���wqa�����`�Lj}#�N���:0�I����U�e��(0r$���n)�#�����\�[��Wv�;D�^dQ���2�$�����t����o���)BvD,/��W�[��NA�,�{�M�$^�����H��jD�h��F�=�^���
-�*;�QB��=�P
��CL�!�t�������k:�7��s���i��g>�u&����|��{5�Y�_�N�9����'N��O�����|����"�����<8���v�N
����m�t5���H�Q4��c<�f=�Y�hS<]6��{��E<,Wp��8����������&�����0��W(Z3,�ykJ����R&���!�O���� f���Z������f��DFm
��� �;�^��b�N��5x@���a�`�1����A�uv~�rJ����>=���Js�,,�a�WAt�.Q�%�)�*t\��X��k>�������H��&+�;����������
�Rp�)�/PF�������=�V2a�4�IeY�;����n������V6�a�\.���4Z��W$��"x���*"kd������2�w� c?o!6#�����#Q�Sz�&5�������X����'��$H J��Z����52(�@&���6�#EQ����.d�Hj�4onM�T�t�9�]@0����m"*v�w�4�*���6dU��uu7�1A�}>�Nv���e���{��4�sa��+>E��{l� �#��s5�<���I�InN.��n���fh�FI|?�}����I�$�J�HtUu-O=��}�7������]���}���7�w���/[��[b�����M/��v�_55�X�����d�L7�M+W��)��<��$(zo�����4�q��������hq6���^��9��n��5�/oNO���3�:��� �����
t��h�"�`��O��PN�7o��t� ��9U��>+��7�D��Lp�����vu�#yy���V���J�Qt��'�G����/�^��=���?,.��c>����F�Ko��*��0!W������;���"
{h1[��g�m<��FYJtM�2�,��6#x������)I�~�!���szI`�ln���<(=8H@�t�3qA��6���p.�?�b���j�}�Ps-�1cN2���A%J��; p8�����fM�b�v�A#�v�f"����u7o�J� p�]����bTsG�������w�<=R2��)�%p�4��~v��m|u�����(�DR�D4���A�E����/�_H������_�I<
u~F�L�>e������I��1�c���w�!��U;���[�� ��f�Gy��P�fM~{rr�~xzvvrQ;�pur�{��v��J����/�������}�3����`G�
�~w�5F����w'M�5�����n�U�UF x�e� �� oQ 8@'`�NG��U?
:KN�/<������� �z(]j]�XD�S��m�k���a�����m�b�_�]�.|������5&I���y*�]�~I�d2�!��2��:�_�7F���=.{)cl���Vd����?�Z��xc�\�O��~kol��c������I�r�R}��
1N�Om�����_�<(�J��C�W�'�L��9���>�K���?# ����)0�� o� ��q
��<;>�3��4�����b
(��6L�z��� p�
6`�������~���n�
�T��5��4G�I^���KZ�>�+��v� n�z���nA?��a{�3�s���U5�z����}4X�}�"������|\������I�b$���4��_:N��g�0a����_��@����Z������qII��Y����W��B���
F��������k���2"*�i`�l��Y��R��� �� Xv���@��_�����~���=o�iL���m���{0����1i�n�`�xn������@M��-�i�J�N��E*��@gIc�:�r��Yl�$E���c2^����*
QFq�J�X������*���GS�F�B�N�7�~�{w�p��15J�LGR4��* ��F@2��%�U��K�Y���9.Q�5$�(�.��?���f�_Z��]��#��y����3bk�������dt�~��=��bgwOg���D?>4���M�[�;�qA���*�����a�/��t2�I�wo=[y��w�x��L�S��4�����Pv�{K��C���H�E�p�s�k"���!�6W%z�r�/���.����D������Oi�_�#������S ��l#[��(�"���+
�������tq
@O�����(}���eP��2��������.���V�r�Z�������'�F9=�'nw����m[.lj����jG����5��_��~���Y���_����
�_�[&���m��Q-6b��~��-�_��h��_���������~��4&����u�
���t='~k�mk�M&����9��v��O������A���&���u���91p���S��P��L�����::;k�]@�����Jvnwmy��Q\9��C�Vg�gh:fwve����3&�(%�|� ���@2��8%~��}�s�{d����\y-�����V���Ur��w���x/�������A�/���$9�(�Ki�(����f�I}�(�������r���\Q*�V�,��n!�����un����wsW�����9��.�KP���?�)�@�j����X��j�~�F�qm�m� �u��]��k�Z����F�����!����Q��=i4;�^s��G�`�����h29�u~�g��x`���^vr�b :�`�)9n:��qH�np�o#��\:x������"�c��Q�3���5��\�zQ$�-Nc<u�������L�5����,#��J�U�*x#�=�-.�� @��%|t>?��?����
����)�m��R����tt~������LI����\�8�[��U��I����s��PU]R���I����H8�~�b�p���Q(�MjA�������N��&r5�
h�Dm;7�S T����0_M9��X;�v.��RpMR:R��{%�x�SrXR�? ~��M�],g��[B���a�^�d�M�w��t�p>�V���U����p�z���8���]#�������� ��?������Zr@|�jg�o�KLV��V�vc�>@�'�%��7�E��?^>��?v;�I��hz�I'����������M`���gD��W�B�s����=f����o= @����z��^Q���=��:�L�m��"�qcN���9��<����Z��CNTvS�����G�D.��cqQ�����k^O�5VPV@�J�J����5��8v��{�+���Op�U�7#� s�:a�����S������b�u��A7T0����F������.��/a����J# [�Q<���:z����I�,���A|b�0�j�� ��M~�'����q�q ,N�����Z���n�)�PeXF�����������T����&w�F�Ymi_7��x?u�r|:��J*B�aY�}�Wi-��rQJ��������tL��0��� <R�3���vv��d���G�������K�U���N��a�a�� 9E�)X�� ���R���J���(� cm�2*�-W|����������������q��<�-0L��Fom��u���E5x�9����~����xp�xE�Y"��4�b�\-�*��W�o��v�p<h:E��R���*O�B� ��,�H����\�to��=��da Y9Z��Q��#?v�W=+��$����g���'����[�R��&��H��j�(�H�0�����X��������_�����m����7r;����x��@ �F�u�����V��i�k�^b��l�����t�
k1t�W�dZv�P-��F���G ��+����*��N��s�4YYJ{Em��A�h�HU�9��)����~Az��6�����A2�������,�4�t����~��M�T�W q�����DR��y�L5V�����y��}?U����
��2��J��*�����q�]|���!�����z]���|5�� �>~4{�!��S9 �c����TEp�G��9�\��9S}X�j�<a�:��� g��:`�h��P�r�q����*�T�|�1��o*A��"���q#�.S�����n��ZF��������������������\��c�(3~Q�T������^��n��?�/������������Z����qr0��;�I���&�I��K��m�l4_��y�_O�p&q?b3N���\S ����c�U�l���0��_���
����,��FxV��)���MTwNH_����4@D�}I��"���(���[-��>��cf:0�O�9,>XQ0D��l�`�!V4�X�� Y�U1e���LHX��,��a�2��(�R-e������W9����B�}����N��(~��$�+�]rB��K����-OC��+��Ho�C��t��=�],�!�.�T
���]�
����\x�*����WJ|���]���~�{sD��wx��1T��q?�K8�������3�g��`����+x�$�D�]uA����#��Je��%���������F� -q�^�:�NT�vb����T7��q@�F�?�����p� &)R�4cH�K�W�( �:GQ9.���#x�����2H�G ����9���i �������r��3�m�P�t��3�5��_lU��\�6�R���1������+&P��iW*�t@� �����9����p�j!h��������E�g3�OQ��1��AU3�u��Q���� ;1�j�0���er�/����S���k��,�yD�3u��E��@��XAA)�QF C�������)�<�6���3S�
w������->f&�T������RP1{��R�I�����r��t`�hz���>�J��}�RY�w�����&��99u�q��S���8l��T�Q=�[**6��qB����8<a*�Hn��t���B�J���_R�*�tH�+� 7�b}e�!oQ2��PC���N<�N��mW8%���Lw�J���L��\��2e?U��$p3��%�FDt���]z����$�I��"eM#hf��%��*�&k��gC+�����
��z��}�^:MGi�Q)��Q_e9?��q�������q��`� �'�
�H�ptyQ3�;-��*�Y��W;�I�PuB�%��>/����K�e��q�u�r��9���(z���2�W]E�d��JRN�n���
��T�Pz%��w.�����T���J+��� Z������6���=xR�s�amF���5�1')�<�0�n�"�R�lR)���o�o41��x������o�m�RQ�l�U��D���~w1 ����(h�@/����;f/���GcN6>����D����m����)K]D�O��B��W�P���E��Z���1�W6?�yW
n�����)[��n�I)��c��r������W]���xw�_@p����7��������P���x��O���q�����rh6�H�eV�a��z��m���?�cD���� S�2�c�:�X|��5��Z�Z`���������&�m�syKF&�f���
d�M!~fYUpN�2\���R.�H�4��}=�6�O� fg�=3c�l>%��b��0s���[E���
>�
���U����e�:DO�S�I'��9���j�f��S�Ji=�|8O
��J���I����K8t�$�[77/-{*<��w��u��d�6P8��e��o{"W��r�`��2�� �t;�[���:{9������Xn��Z@S��;�a��(Rgo�rEa�]-� OP��k6P���:�9+�Y��b���}���;Xk�n�����-����k��T���,���>���u
J��������ggq��* rcx�#*I?�/>�J|H�$`����kq���q��B���Ck��>���T�Pt�6�� [����DJ�cE�� QE���r�?Ev#���$��`]R��B���-�{t��S5�bH]�1]4����&Oh�[M Qc��(G���o�Ea�%�S�.��^<V�/��:�����Q"���\P9,�/(�se]bA*��v�S��a�s�:4 �����fhD�q��C:�I��M�#���7��� ��v3r#K�R� �2)�24D�7��K���������"�����*B���Jic�.�9�p7��D��y/���NsCZ�.{��hVRU�D3\R.�@b{O�6�+����8�5�����8���I�L�A�}9a$�M�bj,��?���x���8 n�dTQ#���j,���^����E�� T`7oI�D�_����C�x����S�]%��z�z�u��7���.�a����u� '���d�����������K-q�"#%�+���DK� �<���T�}��6��;&��[H�������OoN��Y��
@BFb��r�F�������`.�����dU)�ing�<�
���KT�i��?�%W�e��R)�
I;u���t�O��L����lb�V�E�F,���}����'����������}]+�J�b$}��=���Q��Tn��c����X��� �[I)���_������f�t���FUd����u�\k��j���:/c�Cu�e��v�� Q%S
�1��M��<"����R� S#u���=����)�����-�.���T��!�C�,I)v�O����������QD��f��s�L�?p5�����*��"���b��L��
X�#�=�~�R����T�/CB�pa�%��iV5�b�������*V=�a���I����4�������Yy8T�\w�i���%�&p�����[tsdl������6����#�Zu,���7��k)Ug=']g6��y��|�v ��
^�fP� ���O �>2����l��p>��0�J����+�� �9���T:k��4����e+�A�,1^�
xO�������l`�J-)^8��TzC�+�T���^��D��0�1� ��Q�x(�V�f#���n@l6w5��'�z�iA��aJ�P)���Q,�3
H=q�#,�vp���i���N{�~��?�������TS/��_r���j +�`�p1�^ pC/Xb�
fs�K�����b F�A���)�V.#&!���������_1.$��B����I5�Q�-Z/?��K���������b���v"��]���i���F��9Z�D[�*��2C��v�xM�@"3���d�I�$�TJ�d�o�f��MW�y����,��H�3��Y-Q����O�_G���T��/��T��"�6���J��h�g��8�R����Hd=�r(����;av�*Z25.,I
�!y�(\��A��a�?f�#u���RZ������gm�D 5��*�#f�{�����2y�Dx���$"�`����t��kG��I ���fm����! ��p�q�C���,�
mM��%��U��/���OA&T[�MI�P�����\���<�M|xO�N#�&}���ZV�����%+��x8�aO���#�f��X�~@V<B8(u��/�_�1i�b�����*n4��%��eX����#aw @ ��V ���?�"��:�K��R 4g��HI��z����@����o������J�����e���K�q���~������i���c���T#�~I�����5�=�<y{rt�,��G����\8���c���/���]�5��g�V�����U�y5� ���x����w��k�������U�Dx��y�PR^���~�����p ���Nc���0�b�T!�7������[!���J�j ���U��[���������j���i62'K�����L�[@J�G�X���0a�Z�T�u�LC�$�EC���8 �ro��{">��N����HD��S�fmP�����bT>AV����=���
���&��-��-�u*�S���+T�+��8~�i3iZCE��|J���J�swzZ��8
�#��]����n���C��
*?V��I���|���5�u�:����*���r�_~���9140p��*��q��MW��%�S�r� ��K6�4,���gKQ�ut��+/I�#��TB�dvt��t�;�xBe��tn`�!��8��=��F/��7q�x��v����J(7Z�C\���M�=95����<���}���p�����.���e������;�G?���J��������D���'�F�r�b�c���d3N������8�R�Z<�^f��s���A}�� @�� W)#=��7n0 ��f����� >��C�;\�)� ���{�EJ��6�5G�j[z�`�J��)�9
1j0�� 1�z�l��5A�&
n���|��e��XJ�eoB�M @7�%��������3�}xXR���uyK�q��������%�Uo'�&��R�K%� -Li����>���)�����3�4l��FB+s�LB
X~��G)Y���k6�e9���u��� ��*��H �B?����S���"���TsIB"����8������a�y#9�4�&�0����%�����9�x|����(jc������������������zF���2~) ��Y�Z��_��W�������T�4��}��;v�C&�ZP�!�{W%��-TH��� 6v,@-���}�����MdZSy_��7����&��������uVB�rEC��|o�T v��\@�g�G$)��� ��FI�9R.����fVp��Y��{�`����^Q�!q%�m���!�Z��q L*g� v>��`�w%<px��J!�9�
q�W��y��N���t���q���[����Q��-�M���Gs��Y�����zU�r���t��8���}��,VFZ�d����:b�0G9�+�:>��%����7Ij?��g����Gkz�
)��9(���4<�S�*4���G��fJ��oN�����M��U����t3�����7������`�z2���YI17�|.� �#�O��n�����
�'�f��V�������s/��l�YA��U�N��>}V)mK�X��U�}��u*F�^��
^)V�K}���NZ�l ��,8�>ZV������a`��$�!fA� �E�
���Db������[�'��
����h<��f�)��+��i���@~���q$A��L�"e(��P�s�8����IB/Nk'��1��i]R�(WJoR��U
4����P��������p
��k��4�ai�Na1+@�k��u���l�1M�S�|�|U~.z��,�����Kia��{�9�-���N�)c7J\�M!�XE%��A�T|
|��]���8���ywxurqz����>9v�?=���}���3�fy/%FF4���+d����+�/R��oSI���=C��V$�t��r����<�M;uNb^q���#jT3�r�U���]K�����=Q�d����I�p��3����i�Z����� ��=���M����rx���'��N�Su��A?��������*���c�Ze������Mq�r��a�4�K zdvo��%��x7�'U�_(j�U���I ���C$��1|P0���H�c4� Y��-!C:L��/�����9�s��v�UE��g���MG����7��7��Q����Put��/�v�+����s��:z��H\)~����An���W2�Z�����1_���g���_�g������F4F��0
���H�BP��8G��.��0�kq�����_ 3������n6������?��~�zo;�^`��ix��rJ�:�������uMw�{',�����j�����D���{.���I����n��{�;3vH�T"�x�E�p��S���J����GZ�f�w�l ����z�6��,{���;��rGl�K�&�K�PF!��JCB��)��I�����V�~�������H�?��s�����\����-�|�=�����;�{�9
X4�b����^^�)���=��F3]�V�����A+�lXC�Y�����'Z���pi3}����3����N{~�f��
�5�F���V�# �F��gE=����U�w�����{s���S���u4c�o]������ur�w�^N�vkO�1�����wnt�}�[[��;����S{��^C_pks���3EE��6�)`+.�����Ex�E����L���e��KZd���q"�C�m=��F�&fm������^��!T�X���R���zJ��K�:�"�r�/������XO����6/]��$!��:/���6ak�s�������R�''������d��@�[�,��4:��3�������>��=�����e�~ W�$�������#1�;!\Vef�@�N�dp�8@�Y����
��e�� QS��t����?g�Y=QfP`��4IZ���bA0�-���v����[��/�W��u�D��
=1��lQ��=���#���8[�'��4�5��J������������m77���%�-E�6�JO�l����'z�+�A����h��������7Q�j�;�T���NgT��*a0jL:�z�5���D��"�p��"-1gw{�iT� ��/-L���s���=��f���7����{��
Tf}2���d��K�a�<r~�(Y��UI�,��3�z�K_Sgl�s���Gc�&���J�H������I5���U��;������M���]R��pa����i���R���+I;�����z&L�q�*%
QU[�v�*��V�5W���W���k!G�� ��;�����^������#�+���]�V��%������Y=3K����r�Tpy�@p��D���&]�jw��S�?�w�U�..���JN���+�b[S��Jc�(& 1��b�{�.}���<����W���\AD�Q(%���T�^�FQ�4� g���E�vft=Y���g������J��@�y���>=��pd�!����K�u��a5�$�E*-�s`�U Q��V�Cn�P{-��_�����������8Zw�Dw���rh�4*���b���#��qe���?b�D���7�Q��X����S3UK6'e�QA�J���*���N/���������7�F�[D7��mB[n����h����8{�
�y9>w���g���k<��Z����4���n�5n�7x�:p���;���#�v&F�*o���A>��}FH��t���)o�z�c�Sw������1���d�w�M�����r`���B{)���B�6�UTm5��Fu���^j�mi��|���j��x���)���
�*��T�z8�V�a��w�=LUp���=��b�:�������jH���c`)kQ�(j�������E{�6 �V�[��p2YX;u1=�3J!#,q��yO��&����|.7�l/����Xj%��l�_����U�o��=v�a��<3�J��*#��.�=��z�>kv�714�=�..���8��"9�o���� )����]k�^��
��������3�g�9� 2a��g- � ��hE�OG��[���:�\Z^�����x�;�>�*�Y���(��7�����\'���Ki�� ������AN�k[�B�-\���G���M �)f@�)��:S����1���T�j>����S
���� ���j�x���������\ )�Ni���()���<�0%�%���Y]�z�gH ;�no��L(A�Y�����!AP��uw!�����a�!�3B�q@�f6p�<���_��E���`g������-��#L�1'� [�8I@<
��W �vG����������S�d:2�'�r=�u�H�����T\��NCs��x����b�� �\�x�.�;v��.��Xjf������W>5�:��Q�<���L����%��T�� ���p��q�+:��A� -X���&�t����Hb\}4�������4�f�(�&����Nt������7w�(
�3�%��wX�SY�yoZ�����bPB7'-���H��r?���n\�E%���e�j�t���X4�^`�5�j �i����W"$7Ad��b��H��s���b����<��^������=�u�-�S2��#%x\vW�dd��5�e{2���!zid2��$Y�`f�� ���M�%1u|+R��h �$K�J�[_~�������*���\����'���������1�!��b5��s�'�&%!W|*
���T\8b��8O���r���E&��2
�z=�~I��^r>��q9���-�����6R�|�PV�q�s)�B�|��bwq#�(Sat���� �UTt�HH��+�,�����=��I�x�uk[5�=���Qy����k*�$���'r����rQ IO;�C�a��U5H5�4���O9�WqTR�)�Y�I2b�PY��8I)FQ��o��R�*��F�t%t��(�|�.��i� �
��M�2o�D~����@4�%�g����q�G�3����/�fk��2�F�
Y��Bl6d��QkGMb%a,l%xLu���� !$Alx����C_��<p��@��~�w7�kQ�i�b�+��`f�8�����K��
4*�l�[�.��yY���a��
��X�� �W)
��$~!Y�,R���T��I�RJ1/�J)�/S�Kdcg��LBb�f�\]�~$TF�,��vm@SCQ�I���+�� ���J�M����1M\Wl���-��%|,������I��{D�/W2!bT����a�J�Js���.����'9�B{����B�������B�����2Q8���0����4K��0�q� ����iZS�UEA��%e�� ��4�J,���<�U*6O�vV
��K��~�=Q�ci�J�������������8O��*��2G�h�+h���D ����/O:��ib��T� ��[v�L�L)�7m����t��Sh��]U�� <I�|�)D#�N�&
Y1�r�R�� 1���S�M5����`~v<��k���Z(9�Ul��6Z@����9h�Qy���F���F��pF�\)v�8���*cJ�(
/ �����Ff���NI9{��[ ���A�F
)WL{�|��������]�����(��V{�u����������f� �>fv34�v:�Z��� ���@�+����x��/��_���sN1���f��������
���)�&�(�����Nz�+����|B���3�A���xen��by�Y����w�0�{���M�<`Es1�;�������j�v��1q��x����x�U����I�5�hg�=">��/�c�F��N��O�<|w���O�r���/���j��� ������V�7��~�u:�|�N�Ot��r��j#,�g���
H$_\���z> �WtV��'�t����G/Ta�>�wc��_��o}�;���tpi���4��V�!��lz���F*t)��$guDF�E��T��@��(���o>�ysra<�����3��� P���\<7 X��S�M� ���!Jm����:���`��T����-��0�}&�\jT���:�A�@��h3�#P�fL=<�Ds��S��7A�Q�^5�Q�N�gg�`��1���0(���s�duBj�X;3n I
�����
�E�wNz�^���VC�ir/����tN�A�8'�G��$�9����zF�;���MaH��tA��d��.���T�ns��
������^�w ��o�[�qA��Cl!���k�E�� �����m�o�W�#pL����������;���^/����S��O��T:��08����;��������8��.�-�J�G+�n�%�8*��ioF[D���A�����v�5� Q�!2 *�!�@L!~L)�v�U Q����x���5��`���$n�W� ,�F�5��0�sbNd�w�^��{[I.�{���M)V�6����9p�z�����N� XZ�l�L�yM��-�N.�1v�!"�S~��������������������1�^cV[���t������T��=0fCB����:!�=QX�X��h�����39o�eE��2i�� >U��n�7u�u�`�mx�B�B�7��`�������f�(��=J9� ��h�
nL4���d�Z� ������{�����fv'g�2��{m9��/���:x�>(�R�O���sm��l~*Ec�y��N�������T�l�%�C����O7
U2{T��9���: �w��O�� �o���ov8�d#��i���� {��3��P�\��@4>R8��2���
�Z�N���>���VZg��F����.(��\��Q��D��"�r��M98(�)�s�����J����U_�+�'�~��z��?$o��WDh�6*��r�a)�B�B���{�z}2t�Epa<�&|�����!�w�Y"�3�g�X ��.T�����Bb�����br4�cFAG���*�8������:�������l��#������a�W*�N�����1\f��R��`��i�7���et�9��R��:���������-p��q6�@�%�j�>��V�'AB�$�����:���;C;)��[~�y�]��`l� ^0-P�������:<��TPRK���G) ���q�@�*FY�1F�������a���do�4
�y`>\zh��i1�k����g��x�+�$���E8�st�I6#'^��E�AI
�i�������>a�KU��o }~jd�����w�`�
]2D�J��\?'k�O�p�!�\��u��8�� n��Vv�H�.t�x�����gy����=N��s�t��~����z}0����F�K��~���&y�!����0�Y��s�<Y_8�Oy�� �G�T���V�wl�A\����1����������(��,x��2�-8
%�:q�C6� _��#,Y����=�
4;u�}���6����Sl�v��H�v����f���5�����5}� �g���3Zs��q7� _����!���?�Su\G�|�<�]���� w�/�#�a,�����@�Y.+�X �^����e�b� ���S��n]�q�w�g����R����}4A�_�@��yb�,�W**�8�%y������E���:���&�Qc�O^��q��z�A6@����xo0���O�����G\��]>�8>��?��
CT�������� ��������3
�g��2��3d7L�^U��t-zs���
�v���������H�r�#��*u~�9��6��&WZ� ] �l�M��no�f��avzs�0J)��&��u$q*�I�[��~2����3��
������������Hk-?�^��I������ ���7����iN�(�P�m�foW�����nB�D���//��2��-��\�o*���8M|��V
�E��m&~�>�FMo�Az�M�C7"���d��?E*H$-����X�=N�q��_�Y�Y�e�q�dH�N^���n��Mn�7#R�����YO�c5
J�c�`�6��#KB9�m86����&q��=NV������A���!���8Q^+��'����Y��s?�G����4����o� ���{n0�5`~��_�nYqg�����ub�����/���Q�(��;nj�^��M�f�IN/��o����r���]�,����)��w���'|���Q}[o�-g�B|��fp�Q�F��x���UX6L@�ty8��^����C<a�������`�\n~��Q�}�c�x�*������ot���=v>%6��w���t��.{�K��Y���>�) sk���k�b��`'�.)�Z��^���90��O���j_5�����]J;����e���������[Lg�O����:?>�.1����K�h����}�4m�s��� ��F%h��5#w�*��7���<&����p���.g� ��]�8 e���$�Q�(�����#.����e;��h����z�i��Y+M��r6��u��qR6�=��n�X����^1m�X.��V�
@��I`�!] ��suq���'���O������^���|-��?�?>�:�8�/�m���R���.�7�%�R����
�2P�(�����R��la&U��n��~� 3Did�
C��Hq$���e�����n��Vfqd��p
��������^����wW
�s�B�������R4�f���tb&���0*���?kJ5��F�������^7*�H���
������q��
�>9���9
9�*�>����K������FP-��'%�����$����y��n�'�f���s���� M��#��X��T[d�1�{>)��P�<���WW���`������_���������.
Wh���D6��&�?q>\�//O.��G������[���w3��]q���5bp�����V�U��
i:� f���,�+��W�x)�f�|~��9-�?i������P�
�������l�_.�����G��'�������>\\�_/��6���;���O�,�
��������\��}�������ch���������8�9������W���-ak��7*pph>��!g�0b�����@h�T���#�W���U6�|�%�N�2/�B �h�B*{��7 �`�.�"��tb\�{��Z�v����B���F(,(��n��_t4��kd���(h�/0m�U���T��@.�����h�����zk����
a<Ycl!���}�[��A�Za��7���o�w�e�p<���_8�a]���<�������c+�5�n������������k�����?�G����^�����^D������*�zV���n���K�JE � _�"f&w��!l�*�WL��gRe(]<x�a�v*t�����[�wZ��7j� \z� �����>mt~��X3KM�q9 =O�l�L:
�L����p �u������1S�T4���-W��;T��9zB��e|�fu����H
"C���4��V��*��i�������\W^��~��3t;NV�z�P�'/8��W�#��� z��%�acgX�~�<Ilxm �ct�����vQ���\O�����2hV�v�l%4��^6K�Zk|�5�I�tC��oR����J���v��l����l��{�B�?�Y��
�z�mP7�<���n��y-�&��.�����Z��oV���Y��Y�~���,b��d�!�tN���������Cs����b%|�J6��J��q�k��N�>���^�7�^�h)��$},��������{`��b2_��,�W�����1 �a��[+E��n�;�Ww.��.�zF)Ny$��$�#5*�cFq/@�
tx�.���nt�~������j�����C���J:�2�F=
�GC�z����))��)��*$+:?u�'�!+�N��-��y)3rZ�p�v����H�;�z��tS�������A�~}�a�t�*y�0\%�|��zCN��OZ��8���J#S��q��9:��\�9���j�[�Y$���,JChg�na�h��n��������|'���bqO,��<���H��A�[h�R���1�`��9����[@NZqz
��a8E��63���E�'y�N:2��
���0pL��i<�I_����.IW IF�c��f�jZ�b�i���x��)R�3w��3���R�,\��\yR��13"�=U9��6�����+��� {y��}�Dp���;@����P��7t:�z��s�����Nc�=�V
��&��-X!;R��I�O���WN+�O��uT>��F����>S���� ����2U���K?O�Y�Kl;j�Q�Z_��_���U�!��h}4��z�VY������r ��$t3�-��-����?�gx��G������V&
�oq��$��o����O������s�-vC��1�;����|%���F���)���D[wjooO�0��=U>y�]����q{w����7�����u���m�6��m�����sG�'�^�]vS6s����V������e�u��
�!X���=�X������s;����4hf��$V0O�R��p����{�����^��� �]F�� �?R�?��U,n `*�s�v������rz�&u
�0` ��]���_�4O)��D���1[weK����3dI���)�Dg��������`���������������LE���^�<I���]�U�'1����M%pY����w����!�EH� ��,G�g+�DG(���$�/VG����6���H�s��*�]�P�;YB��1��}��bL���/!�����%j��CC;r� �6�/��[���g�2��)���Y�q���F��
f�Jv��|�u�`�t��y��|�����x�G��FBK�j/j����~]��&'_����n3��t�7����v`��!�4�4R����j� �����������hA:�����L2�H�4�0c�P@�f ��]��}H�}"s��� ��tjA=p8�K%IQD�S� �A��i]���b��'��*���OD)��b�+�Wzd�����i�+A)����H����i�e$��w����������+#�y�>�&2�������ptGRi��R��d�S1����^3���pQ~2�^��&��[���l������P�'6-��rv��7i���H��^lj��>�c�}��� +.�^�$���q��z�i�n��q�����!_z��_�.�]aS���������\�5�k�4�O��a���m����<�7���+)����{�ck�����x�X��wF��M�����y��n�+L��98_b4��i��n��W_N��N�?�N������}�N��Bx�<)V��������r�D3��I������d %�8����0�Ku"�!;�^ �:b��|d+uNL���U�M.q+d�8�L
f���H��_�<t���l��-�QQ�E=J+�k���S�KUP�KU6u�6�Lv�$?�Y��[�#���;��~��g���m����0���8������Q��q��E_����Rfn�A!���-��K��@�!�c�d����a��z�N�N�X�H1�����(�����A����<=;��!��V���S��A#��{i(Qa��j-����?�B��Y:+{A*7�z�~���(���O����FK
m�pc�`#a���3�S7�cf������Y���V�l����C����< ����k6.�������$0� .o�M�c~k�N����1�|���_;
���|�1�i��"�RNN���2�Z�g�-��O%��2b!��hU��i�ZPB��R�$��X[�ltv��8�8�2�
���������A
(z�{r�E��7#�� G��x��K���x�Rn���J�R�&<�$�����x�������-ZXH�>�[�qf����������MX�}@P�Ts3O$�SP]��c�s��xx|����Ds�/@���vI� j��t���$�&�}���_~����8u'dc�ab�E6T��y���^��n��83.�����.m��m��4��&E(]��Y}�7*�3f���S�'
|{�
.����x�Y!G
D7�G^�^��=��(�x�ClQ�q#�Z���]�����*�(%��~��_�.�:���NU1��\�.w(�x2�3�|��%4�(R�X�}0��8��)��]b:{�E�,������P{�����|8�,G�3}N���t�������W�W}A
� K?��|��,���Y���[t��-�,�6`�/s$t������m��2��t
fp��U�U�l�n��6������e�
Sm��X{<��Z�z��O�]��%��l�g��R��-9R���P����@��N�N.������D�����6 ���L�jQzT�cWb��|{~�]�������������{i���^M)���:��������1wnf{��8R����-�Cu@r�A����u�w��5fx��H@�W�J�qN ��^0��m��#�A�]���_�zLV��2�<�}b��Y�����?��85�"�V�@qI���*I�#��F�������x��E��6#�������
�N�Q�f��'����� RR)R�8/��r��kzL��
R�Y��j
�^@Q���B�;��it�]���S�5���0cG���H"0��[
����7�}��Y���rv���HV^@ �po���?���72M����I���d�`�Qb�i���7��p�a_�WsLFB��X���x����;D��Q.����'�����JQ&���1xuh���i�Oc�q�����Rw@���p�e��6L�����|�OG�{:�h4Y�
G9���Ci�W�������e��+�}����&��>=��pdE��9����*5�J?��U{�(��&��[;�^����|���`�k�Z�h)'9 �;*���K�'yG:C 7b_�5��&��X��oo\C��9N9�?�f6�`�H�?�p��(�^ 9�/h���e<>�%'��xog �Q ��m����UT���a-QgtG�7���������|��g�V����Qk>�����
����������������b �|]���l�
��K=�������������-1��ZN����E�"z����R����h�A�/�6
g�|]�3 ���X���o QA ���6�T8�����h�P����I4�Rf�a�9���r�pcc����:~���I�c�*��*�B��.���}�4P�#���N�� ��J1�tt��3��:��xO�)A��}���6����' �"q%�X�X� y#�x,�!!�4��Y���\U��Z�hx���.�ds��%�,������J'@��S]iW�'��NCX!Vv�3?�� �/�6����������x��[u�k���(��R=�s:�0���� ��df ��tP&a�^��D�����IID<���|<t�A����Y<'�Kgy(�$^l�Z����� ��4�����mR��U}YH�|KA��s��O
��KiV����$j"����X�3-1��lp����������OYeBl4�X]�XD���T��;�T�����e`��r�<����}yr5D�����W�:s�:�WL�}�� ]e��ta0&dl����4���G�\�Y|���1C��8E���3�g@&�'q*�2�+�f�a�K��H�r=���"yO�����uI��|
<�M���4\��Gu��||�%eu�](Y�o�I|������i�a�f�i4��b�h��Vh���H�1o����,�\�VB�"U~�4GXv��\ #5F#�kI�(]!t�9ve��l��*N��W�����HKd�w�(n$y�JA��0h���;j�]�v�e�}//���p <�;���Xm��J�wz�D��%������os~?������L'���������O���K��������]�.��@1m+��h
,�;���z�z��������L�D,�.��� ��IH��4T� \~w�����������DM��W4�8Q59���;��=y p����6Gk@6l�=h���a���y"v���������j^�])Y�=�8d�]���������\@������i?����a�Rv�-eE���� �e�����J
6W������za�a�����@y��N��N���;Fw������V����*,����W����_���*���(����2�9��8������`1H�H�K\'��1"@��!I�y��D5�m�h)������.����s�(|�����RWG��q���+�H5��Z>��dq�zp���)I�����-�_��6��d�/uK
n���}.g��}��m��w���6��Xs��.o�������n�J��l�P�?P����d�c)����Xu������q���;0����1����������*�x?��t
����&\m:�K��4�f��J��C�p-X�*Wio}NM7��aQ�t���������w��'��.�D�x6��R���8��@`�b�(�BO�q������������|������,R�rf��������y���"5��/1E�Z�Xiz���+YH-Vu���������8as|Oi4z������3?>B��"�#� E��-B����39�M@����b���}eQ��8�+D�����N�#2��Y�D�s���� �xW��U�?Y�mZC��M��oh�5����*���N�I��9����G��c����
5�4�9��e5(,0'hC|�?��<K!������j���t"H����U� m�7���K&$��)?�!�tY/��/�����7�J_��E�\����G���'�yJ
o�+l.�S���<��!�q�c!�p9l�;'l�.d(�b���p��������"��f��
R01�����*�X�C m{b,�����V�L�?���X�f��$�5�u��������{�{����F�aNH�Q�������RE���9�:��2���Z*�$�<d~���@��T4
C�n�#e�I��R�z!�S��b��4������JKIr�/N��� X��I)�hPD-����d��-mg���DJ*'�O%��nz����C�X���=���j8�y}�$�Cg|���W������o� 4f
C����>(�\�`��J�N-s�Jq���)�!�(��%t.G�v"i*���l�>Zq��/��I�C�[�����(.�db����5O�d�����-�+O���5D�rp�N������j9�o6k���V\�8�f�Lg2�G:�x������6jh�����GA%m�=�M���(�]�nlRn$g��P�A���+j���+��83h��|��Mkj������t�� \������� `�
���(g9TOr�+�f�!9����o�$'����� %q�QG���$AL1��qk��;9����~�-���JE��U���*��H]��&6�b�]������]]5!��&�<o
c|Y 9� ��W:��0O����Q���Hu�&R�S�>W���
��:�������cGg~%�x�~��i+x"e�W�������#��<����p�Q� .�;�,q��H1�q�D��|LM�������OyD~y�*T����G���CSRu����f\�d���goA�s:����|�oK����i%
���nC��s7�(7+��9n��V��&�-^!��T�n���5;�vO��X�P(0{��Qy���<=[]l`�xlF�`�S����<��asyO
��?��u"������b�*�����M�Y�$�&
�i�(|?X�������S�X|g���oe(���H�\6��j(3a@`: �!9�����6��F��Y��m% B��UQ�J����S����_�"R�X��#UZ�QP��+0�W��P]d�������S�}2��W"��R�����f�waU���o�|�
���r7�B�`4n.����[Y��L\`���/Gn��������I>&�8f^��<3�++�L-3�Lmc
���2�M)dj�R��Z�]RO��.U ?�A�H(���Lx�j�e)'y�<���T���[����Ya��
+���[9��r�x��������p��OG�)�QMC��o�M��&%���D[w�s�e���A��1�`��`���0���fz���`��j����U�}��?���������?���zu/������<��SWs��_�H7����������r���T��u�5+(�u<J��H���E~���p�GS�Z>)��B�6���1�a�/�7��Wi&}�t>^r�r�������� ����Z��K�a��X;O�~z��LW7~�>L���
�P�����`�<�8+"AlK���n���
�V�Slf��j�;Q'�&����T�%���j ���1�����'����-�'������!P�sR���3[���L�����Il��:�j~����S��&k?�1 ����;4wn��&���}������n���.�������#/���zv�=��s�6����w���,n�/��<���j�����g�;�� 4G\KL����!�6k���z��L5%������ 1}��R�������������> ����",cTGF��lo���",��^��y���1=���q}#YU�v��Z�SE��9�X��;��Y��-�L�};��cLK��e��4N������[��OP�"0aK��<X��J��5L|�u�"X�S6����a gW��I�>g��v�����>�A0���G_H�k�"��;����?8hU=L�{���
���RF�>�x��T��)�d� ��f�i�A�cDL?�O�,2����? � �rL|t��:X�q�Zc2 ���;��g~=��%��
lr�I��!�q� H|��� �Jg�y�m�t��lX����1�xT��d�M������*(�oL���f��{��n~�r(q!Y;��������K�
��v����u�jr&>&�S yh����'����c�a�z
d����:9���~�������/���F� ��8��M��� ��e@��|)Em"����\&� �|�����l�i A�D�x�\
kc~4h��A$7��q���D�D��������<�����T�� jl���2};�E����<�;]��(�*��j�^ct��1H��&��xDX-��^��Q��<�w�`D���:>p��sTF�<��<\Db�'��S���MZ ��������o95:tC�.K�#�X&�i�8�����4���*���=�-Z�~OZm��]f.�����q�3�����K�d��������M���%���~���bm�����h�Yc��JW��q`#��Q�����LT�T7XG��5�����vW��M�5q��5�5`[��� ��� b�\�P�Y�[d��������2����|�����r6dV�^��d�����X�@�_!�HS���wP�~6��o�B�4yv\�G4�z�(������V�����r.jx^q��
���c�p���++\
��� S_[����~
�T5H�U�Zu|s�������c*��5C�������$��>J��js�n2��h���A���~W����Qh�R�!z���_H��T���i;u]��+���\����1������� �2[�
�3�Z]���7��4�����]Q5d��&��Q�:5���a��tq��h7� �w����r�m�Q�I��}�J-�^k�
P�C!�(�� 6(]
C���+��j�3E����E��~�Kbtm���`q[9|c��~� ��^���BC\����6{�>�
�E��\���oR�v<
�:L!�`'�;�M,��V�9�����@��U����]��)r�wEl^�N� '6���p"�&�|�*
�+��&(?�l"�u�S�/ %^��!*S����!��kt��e�;���d/����T�bj�N�t,o�o2�Y(��E�b���w�v��$�MNbR.P4��e�u���.��Nt��AZ�����!m��h��qfk ��G}�B��<OI��_^����a�~~��cA0��Y�
L��vB�8��Y �4:?�$AF:������Y����j�g���/�^�O5�b~,�)����^5���I�u��1��
d�gT�����`�P�D�/���f��J��x ����/����0S��6��I������P���)P�E (���>DC>��,,�k���kqw\Y���'���pb���i�S���k�N4�������n���D@��(��;�l���P0�3��h����Z����%4��q�?����21����b~&����J9=�9�cl;m��w�����=����;�9�>y���
����nH�Ro�SKDT�z���D���gK��V����+8�+�?�*����AS?=m:+�� ���7V�-q��h�����&�1#4VQSa���N��8\�N����G��� p�W�,/����ph�
�5�}`>
�2u��C�S�fL��"�)� ?M�[�$6b���
��T7�;��&�[��5HCT�$�C+�5@�d=���� 1�d�W��Q���>&��`��}��[SF�9�>aVPf��"�����)�ufn���4Dk��"%���LE"{��DQ�SdG��V���*(�����R�JF�����_��1����+�W3�#�����K�zc�cl�@g�f=^`Bw �C�K���� �ab>!�Z�����5��k.�!�Pj���z���-E�8���/!~5f�_�0d��u����g%'(���dW��$�e��r�%<]Q�����#���!O� �Q(���3���|~�G�?Iw^�$�F��T�j�������I1'��j�V��G-K]����#���cw;��Xi:��v^:8~E�8�T�/V�@�d���E�c�������e[Ky�&���d�B+6�Qr �j��b�r�W��kyl=����O[�3����<U-l����YZ�>6�,y� �-?��W���.X]J��Q���8��G���1x�����=��o�Q<|k�x��7.�+��|3�{�c/*�kY����%��S�^�-&��W�����^��S��S�� �`� $��^;������YK�N��%B���������L#o��c��9em�->����J�l��@�������q�Ya�i&_�Y�N�Y=h#!�4�j����u������n��������9��A�Pm��O���Uf_�*F�Z�+r��_�B����"o��[��>����(?������hN�SK>�5�Of�g��=R��G�?,�T���AK5��bM)~gXp����]�G���jW�8����0g�����V�=��Z"�6�'�Gs���*�N,�������Oy{^
wC�
j�Pg�����V) #��9�@�4�)��Acm�H_�l]K"]��H������q\��������� +���g�'Ue��}��2��^j+��:/����U�������<�X��$�b���6��z��I��"���5�$-��D�j�z����*Y�G�E&��z��$�e' �s���Q���'h�#xs�x7�%��p�j�VbpReW�t�����N��'�X���
��>��X5^��%{�NB"O��
�!l�$)�I+��2��N!�R0�^�T����v���Y)��� F�����������a�g�>���tj7[�V�b���3 �
��!�?&�����;�A$N��*G������� I�����
{�|�L����I,���[_)x
V���DpG����?��KB��H�b��Tq�������(7�-%���O$"�i=�GOx,�h=]��PTO������;�8�H-m9�>����9%:!uy�U���k�+����0|� �
_������w�<=R�A��s���
����Jz�U��3O��1GA"A����~��^�����`�3���>�@�%�o��������-������}J��yO��Jr-
H�e�L�� 6]��.�u���%#�M�EI�G�J��T���:C��d�|�`������!.��.���i������o��*.1:�3��y��6YX����B
W�����L�N�^���J&}i�������f2-�M���a^�������D�F�t'���X/�e ���b� |%�f#&^���"��\��7�������0�*�c���T��D)Q.��lU!�7�LDl���j]��W�p�Wk"r$�gu�+��zv�� R/������5p�0?v��,iE �I�k.�y^5�'��qVZ�� Z�j�wI���jo��X��CR����L7r��D�-����(y#_U�N{J� ����XX �M��k_��L]b,
85
��/��?Z��;�LI�����*N"�/����j}H�
��r����2�/���A�Yh�����{�|If��3��q��X������m����n[Jbc{��5��9��z�A����]�S�jo�B��jF��M�<�$[<���r.��<��;�\�xzI\PtO��r���7����p�|�����*�!j���1n�
���I���9�Zb��NO������Ey"'���dlWv\u*������G�m��U���?|hb:�UeP�77��C�����u�,�B��%�3r:�L]�������������^t��$
km�\Y�b����wf�T�(C^�I�����h
Ue�hj�]x4{�G����+����E�;T��E��������������������I���A��He�B�
�h���1)��Ty�F�%�����#�^W�{���|Q�W�Fo�k�6���p6�E�b}����+��a"�2����~����eG%�9T���"����:�x�';�78j �1��������������:
�.\/�r���`%lgZ+`dr��2�K�=g����Rs�.i*���{gz[�]�fa�,�3���7]�9NV=J���yc�#����.��T%�����+��O�v6��Y��B��+8�r�X�k���h3�M��2�
�
�����M7���Ost��.�:'��5����y�����A������7�A����B.k�����f< k*����
^�D/
��*����)����'�Gr�w���>�q��<W���[q�0��a��+���2�AI�3��T7VO�l����Zaza�����Z����09���;�����`��TK�.�|z�2b�TdVj�`V�XIH������K0'K��E^,�����v�I�~��c���D�_3A%o�\7���t���ED�?l��Z�-�A�(��5�hc�4��)�w�+�_)q
���Y1���yo��5���9�)h��\��+�G�[�RB%����E���0�L
���I�z�G1;��j5#��A����VT�����
� �J�Ho��� ������+��4��7{��p�q��1���{�e0*a�ng !2E,��)�I�"$�yy>fJ@Y8�Q��B:�s�}�e"jW��h��=]�g���e� �.$��5L�����[517%v�3V}�N�1�I�s�J"@����.�����b�R���{+pKE@��.�� ���N������;�!Z�������Q)�e��vC� �u��
)%1�������H �V<���e�.�_� �=����_3��;���V�R�-�r-�1�?��4�r�X�rG]%�a��/�?e����<�]R,�e�������i1O�������($����Z��3�i�r��[�4�!��+�_�;\�~�i��&"?��*fB�������Z����X�r����O�~����i!�t#�J+��h��\��t)� ��r�YC���*M��y�,�{+�������p$���g�1�a����z��X;�)�1r�9�|j��@�P��|e�����VG�� �7u�M�=V����{'�����{'�c�Cqs��W�rW�3��M��=������j���x �m.T |5 4W�T�l����.��S�B����������8K7��W(�'r�S�y��p+����XM�L@T��_��;�(z��+44��U����V[�P�8^��O�%�j����#IX<_�g�pc����_~������J5]��+4����Y������K�W��E��]�6����������f���9�o�G�5~2�}SX���P���jD�o�����Q�#a��%v��r�T�i��#�x�h�1���'�R�@��xO����j�>*$�Ao+�E�;vN�����d;����A��<Z|�y�����hk���3K�1t��]�i'�
��{�g����3��?�<�e-�^*���"�ez�\T�9p�b������d���W"n?a4��A�������%9.,���v����� 1}�2��D����;���c��f2�NY�����MbAm����:s���FN��$�c)p�m�X� ]��������@��M��c�\��g�,��������h���`����8�����y�F�p����Y�`�������l7�{��6�����+����
����"z�{���X{�xmY��N�`������hG�������-�`7�%���3����4����������i
)(e��c������I`5�HF�v�M���`w���Nr�}P��%%v�kt:Vd��qhO��O.��z��Uu�O��|��_�[1\� �F:2i:{�0��q��WI==g���4x�Z�x��:�����t����u��k���
�L������+j����������O}��a�D�2��-�*�md�M4O����U�
����9���.
��cw��O
�O�+����jxa����|�l���7%�u����Q4�^�$�A8�xa����13>[s����]I75q���������<�Il8�l��o?A1����|���S�4��u3����]�N���7���>�x�������[k���=���9g;�K��6�g.6B:Wsv��r�z�M�����f������p�Z�WC�������~�A����Ol�!6V�|M���i����\���s���Rhl�Tg��^��w�SM��a;����C�|�2��j��0]�me���S���;^��=9��:�1�`���_ �
��{����/��u}���6)�YrF�^{�����?5���l4z�N .N�����������]���{~E����5�`� b����"�G��Hc��!k��/��" r�:L��_-���,��<T�n`${
�g�����/������3�wjX���J1���������f�c5c�L�q�NU��<����i��c�M3��Mk������^o
:��7.p��A6�4�!�~�OH?I� S�0�q:�����A�+c2&�9����|�y2�<��kyw��}�G��X^`m���^8X��S��8!G�#�� ����e�<�� I�_�E:�i�������0��8%�,SVQ�ebul�Wb�������������T������������cN��'�g2��7���c ��3;���y����'U�C�u�:���>�XS��Gx�$��:;��&�D���J7���:
��?���
Z�{|@�n�����S�)�b��c���t�9f���S�2�(�Yl���M&�<��|�r������b|��%:�A��45mU������N����kcig��,�]bqU�c�1�t�$4�A�X��)������}|h��~]�h�&=�0������'q'���qo��X6���:�.�B��n��oO�u�?q&�T9bM�(v�m�O��x����n�����1s��5_�����_bs��Dh��Y���������Y���0w���f���3&� !y�������������"��lFr�[�&`�������_������s<�����A�r�����*FJ\����^��j����T'��4��s����3������/�)��nO����
�DE�����i�LF4D��c��s;��Mp�qz5:8.������ ���<��"�����r4��^��t> ���:91-�
4z�W���x��aC������a��?�������g�jTU�(�|b^�i���v��� ���@2N<�R������MFhA�zv ���i��v���.�������7i��x�����=��s ��.�A�t�
��w����GcO�������j����s.o�9�U��e�K��{q��:�~�B��������LF�z�9����-J��(���j��&���a .Y����t���k�W�.��f��+���-��<����Q1[��+o�����(^T�F�J!�'��7�j�.�k)�f(I�hi����o���������%5��I�'#H�Sm��}����7Gb^��,O J�-_���}���T�I�-E����{jY���2��>��*K�>�}%
46-�v�w���WF�$���'`-t �����p��I��.��]�#��[��n����~��y�zw����A�K��6�
���5P��O�m�1�\����O���xL<,���n�oUg�����!���w �7Z�W~�``^����n�N��o�f���a
��l�u�6�Xv��������mtEo�e�eP�(�M�?��@��2�_��c��m�0*ase����*��� ��Cnkh�8����2�"e�W�.o����� �9�����Q�[������q tu�w��S���<�@����?�?��1�S��i%P���"E�hY@�+��F�89��^���������!��
+|����?7�
��=���~�����e�������
z��?a�_D1��%�7 ^�4����<���F�z}�4��NA�g�������"#w�!H����\��
36�P���z�y�D��d�{��
��4B?O w�J�'x/7�Zf����@�C��wP������xyD�j�$�������"5v���bl�C�O{����"����taC�L^�V�����5�!�|�3��sQt�4����"#S�
���t�A<)���Z-aq+�hJ�q���H�y�����<�[f��_��`.��������E)�2m'yVK�y�Q<���'���:^Q�g��������a����6���;���a�����`������N�!Kx1[
6�.�H�{�*����~)��~���~��-��G����H�5mhJ[
�'���~E���.�oF&�����7���z��t��~A4�G��@t;��g�q'�����
ZsMs���������.�4���J{���:�@�-�f �����x�*E������hE���^�D�����GI�s&c��^�������8U�����L���Iy��$��l�t������o�P���Q�����}�;hVX�C�y�11}������~��l������������+h����!g�)�<�7`8��FX���]�T��X\26�o,��Gcq76FC�xn�4��8�N�o��%�e������!����8��������J.��L�$��T��EuH�.��b�m�&�p��
� x�q������A��@i�:�����R�S�2����u������{z��=M��c���Q�1��D��=�R�mO�b ���l�`n! ��L\�� v�#�����~�����?��&e�������e7>�]�,L�!�G�3Q2��D����NpC~����S~���g5�"� �hi��%�����S������zJI;i��s��\XW��+Y������>���R_��X��$��Yy��%�@���rZ�>�SV�aI~7Zx����
&C�/x�z��"��� ��yL�!���nZ{ '����&���y�M���$
4�������0YK�\���H���]b��t�5m��8�"w�r G7�C�tp\L�M��o0l,���������T6���a�8~�O���M$��������Ng�A�&� o��o=��������l��m�
�-��^3���D1/Jt��;���>N{�g]�L����ky\6Q
�5�����*0�[_Q��l@�q`���L:--�(�z����%�L?@�����0�
��!Mu \��H���=�@J�q�
�<���P�H�`,T��W�
�-�1�>o�����Ff���L���I~-�I���y:/1�)�7�����������s+a�=o�O&�z�����.l��S�5���0�g?[-���`Og���0���j�,
Hn�`.�n�x�����r���}��! ,�o�2��{z��������O~~����'W��sy�����2����o��!=�����;���{�/�x���1#����0Z-��('�'�x�c5�������/1O��
K���Y)q���$��q?�8V���8X��k��[�z�u@lc��o� �����&���g��a�8�Z+��f������%���O�=7*f�'��
����.����;��axvrr��0$��$������������a��^g�0$���8�������~�Ea��Y��nk#�87���r���l\�������\Z��(#�0K����`c0w#GU���js\�2�S�m��B<R��Wm������ ���=����������7W/����NYH1�}�Nqb����Vv����s�0�7�s��w���=���o����s��������w�����)�����'. 0�@���z������
c �Dh���?���
7o`�C�7���`�w�=��F�D�����,�qq��R
��+�'��e~2X��Ez�"���+f����1�o�h����I��Z�Bd��U4\�WLl7i*��(O�n��������z��lf����N��8|s~1A �������b� ���#&��E�h���F��������~AQ!9�y!��R����g_>
rB���5y�X��G�� ;�p�4��*/++�7)?�->>��t�+�/+�k^:���l�+E���a)l��^�U+C<fl�M��E\�N����X�T���6&OA/wW+�l�i�F��h��p�Nw�,��/9P��h�v���>��[���B(k��~�XV����]� �h�_���h.W�������&yWv� o�Y��|9������%������^����r)T3�e�I���/�)����A��������g���:B=&][/�����E�f�G�xV��0��Ri�8� ��z]S�B��U�T����d����i�
�(��B,�kX��Xa�0�'H��3Y������vCH������"g���M:e��
����D���A��1P������w����x|6<��x�{�N�y��:�Xa9](�������6CO�J��`���}��l�E]��q�@����k�RP>
�Q
�������
<�e%�Q"|IH{���A*J�Y��C��69�$�nrEIz����~�e�{w�/3{nXYI'W��T����H��� {�y0���z������&{�m�n5&J��y�jF���{a�i����]�D�����&D�,U8�*���%��,�j���?��b��lT���9���s�����D1���^��2g�����*�����Q-�
��|�n7x��w[�A�u������L�fSvQk�����tRH
q��o�c��N��T���1#��$c4���=���[�h����[.���a��0������+~�����|�Y�H��8;sV���~>�6�a�gnA�>�6�&%�hX���G.:� ����TT
� ��v R���y�b�� *�>�7��K�iW~�!�7������H`Q������|���r���'[�0n��-�v�E���P�����cT�x}(c�2D��B%�.3����$/<_���*!1��DFX�#4$Ec�9���
G�k�$(�T?����?�(}�J�Q�����l(�]S�T�Q�:��R���l)���^/��j" -������R�C�g&9��}*���V��h����3c!O�|~�'�`�:d=R8������6�Z�L�6���K��*��d[�S��rI�-���P�K1Ma
L8��|���S���;~p:�\�Z^�y\��US�7���@Js�X4H����E����@�? o��T�R,{X��#Eg���Z]@�U�����i:5�~��+���D�����S[dl�GCo6s)m5���o$���A/��3 ����� t���%.������q
X7��PiO��iB�*�c���+��I�wfp7��4,#@��������83*AZw������|a���`�K�\����������>T�U�Ls��TA�YQ��-���/^� Z+C0qq���Cq��Q�Dk}b����"]������f��.�|����s�kE|s A���pU��O��l���p��Mf�v�Y�}X�Q���
'?R�V7���wt=W�?��W����
�^$�8�P��9����2�UtvN����n����6���1�."+�P���1�_������l!�����8@��������9�\���(:W<+��D�n�����7^4�|���"-����f��v�c ��GY���~�2��}��m�L�a���8��pqvz�������<X�E��_�7�9������#F�@6�sN����K����.�U��g��Y�6S�p����z�j[����u_,:�Q�\~x|xu�x�%�Q�0/��I���j�IZ����y*�k�)?�.C���E�
���}�����_��_���"��C�O5w�A�~�X1� ���2y���<���a?��������q���P}s������`v�����4��6��8�+�,~���Y�2\��\5f�)kQ�S�B������r�J��s�u���7_u\�3
o�� Z���+zr���9�� �������dO�K,X0�_� �*��4M�o��>^�<@?Ql���X�0�\�z�U�_�s��Sl��-�p��s)U+S�J�QU.��v�o��^��fp��)[�3�a��|���of���O>�2���J��x���l�%M+������(/��J*!��
T���kf�`�w��1,�����%7��}�����������Pi�w�����-�E�o�I.�5J����;���YO&xe��S}�2Z�J�� pf�|S��8X+�[N�T����*�N�e$`�|l5g��0G��I��|c����'�m�36~/f<��@������`��3��;���{��\^�����ch��R3��R}'��\j$�-�h�7�'��\�,vl�[�������2s�x��K.�r��9O`�4�
��CDa9u�o��R9��������m�#Q}�A�T�fqf#��k��Uz+�TlcruqY|�^�5�p�g����\��2���U�qjI8<?��u>X�&�������V�BBJZ,@�W>�w
���B�#4x/L��:�Y ��53b���g
(�|
���k��C^�T���G��_�%��+���z�f�h���%���g�<��F��U-2\�KV�|��s��xx|����������y�c���h��X������b�����jKB�N7�\���KWGq�<s���8�P
�����_F@���{Cv<�\?���� �9�m�>����:�>����1���4?����Z+_Q�nu7\��]Q
,��X�=���K�4==�[T�AIF�@!P]x��]= ���N���?�'�1 �)��C{1��=�S�"�����<�5�a}��m5~�s!#="�_�`VV>���y>@i>-v0����t~��y�h�Qr�G�/O��(��7Pp�h�9����;���g��/G�Q��Q�[����7tW�p{7n����q�9]���y*�d�n�5�$���������N*���������|-o����U%{����F����CG�}a>�U�����9��A v1���h
��(C1���'62�:����{L������YGPs
8Le4 E�G��0"�)e���I+du#��M�
��CG��*l���f����@i�I!-����������������$0�6rs����D1�I/��#6�s��(m�(5�u{|���d�����m���F��]U�+��3'Yo����S�u�^WR�{���M1��3w��N��3;�r��^��Z�z�����qA_��!��\fw"m\������eL=�t
G��n+-�"Q�V��UaG0^��H ���%g�����E=���}*�����z4�u��6�tv�o�1,�����M�pK*�������z�z��t�E~�q����j�5�H�b3�WV��6b��S3z�(���v�Jv�C���������v13���|���O�F�|����vir��*�����dr�w�Z�~�<V���*P��7�l��{=�k u��q��-x'����J�-T=�k{�����_w����h'*b�hv��!�S
L2_�Ir��Y#y���W�� rq��sy��D5���"�����?�g������?�^���
0��$��`]���(������6W���Q�w�.X��/��*5� K�g�
+PZ ��n���?�1'�1W��-�p0�����������0kt���&�1Ww��Y�f�%b�J���+Y
��6��6u~��<p��
��Q�h,=�Vh�veMV�6�I(�F���`1���.�>;�v����t�>�v�l�����~����^��E��i Xm9��!!��D�:7%+���������M��� $`�v�|I�e�|�2LFY�������� ��Y�r� �Cx /�� ~`��K���H5R2�7j�@�;^��+Z8f��
�g��U������v|u�������_��o����pV�[��D�*���;������M���z ���������@�2��x��`8��7d�K���R�q�4�JM�v�%���r���L��rF����;�b_�\��~wx��������43�����;7e�*��ro�-^��z��7n:n�($�� �[1W��|g�iB"4*�?�n9�f`"��}Y]wv�i9q��j��7�������N����-�6�/��C� ����������D�;/���d�������t&�n�.���v�����l��!g�%��?�������\��U�t�u)JM|���9'Q�O���x.��'��f������<����k�1��R�[�o�� B�y�. B/�pp��@��r�G�PC�x��^�<�2��rAN���D��8J�������q �����s�:c���Q,XM5�*|�\������r�'n�yp��������&��~����1`9������b�q�-M'�������\g��&���eH3iKH�5_*K�)�)Kv1�,���J����]�hF�m��� 7�!wp����z>�~�M�������t�w�Q�Q���\�ds.�����������rY���G���^��) �,��j� ���l�x�EH�}�/�h�,�Y�;������W<��<D!pxn�j���F���(�lu{��)��a�����9�\���o��E$V0�"������/��*w �D�{���1v�� (�f8Wm�������v��O&���@���u;F��"�����Fe�x<����[P��i*;k��r�)���J���XW�e��g��
�^o,��E������V�� w��X���g�ka �V��8�����Y���d��d|��Q�,/]'�u0b����oN)��N��=vw��q�8���x{x}�����
�w�u=@�M�0>�1�@[.�����=*8M�����m��������z96+>8�8q�422 �SJ{s�?�N�E���c{�L��V�|4�:���"n=q*�P5�����i6{nT��0�W���f?�ZfF����=~U������j��s�q��k���AV�lA#��0Fk�1�cS��
N� ���}K�'�����9��f��v8����
g�a�tg`�Q�+7�pl)�LT6��l�*0;��:�����E��n$�/��a|
M�|A���)��8|wr�����lxqx����/�������/+�cGr~���pyuxq5�p�����������������o+�e�5����5�����?N���O�����9������#P<���1�n���0R�)�G%�����o9�8<**���Oq9w�x\^Av�>Q��RJ���]��d����z��6]���Sr�m�W�9;(X��(��m9M�+�_�w�TW�]���X���L"j����Z� �I���+�����I�����n!�_���t�(��<:|�����?��O�k�F�F�<��V�j ��� ��������G��4YO+�Et���.�]�h�8���p��Pgq�<^.��A��U����p@�N �2s������1*��T`/�x�$/AK���l�hy����������]���F*(L����A�����g�
f"���$�+���3��Sn�����|O�q��K_$�_K�c�gXS9v��|���~j����cc
��?7�Bb���cs������~��<�����L����~=�/����{7|��������w'�dT� ��\L�c ���p=��ESs��x��`�^��,s�#�1D~[��yZ�|1��JL���C�����;�fQ�
}GuZ�SNI=�ii�����?#_��nqc�����X��:� �.��2t
����"�8
��z���2�w8G
Zu�"q�I�����K���o#���F������q*��� �@$Fw��;j�����|�������!�l�t/Hr�/����S������;�:�`�:}�����6^2���D���_L1��������)Ydy|���G�9��U���?�\���Yt]~2�P�e�F�������������D���Mg��hD7����h��~�z�Z�5��R��l� �:��j����&� Z�mx���/{zX�����$1�A
=C9�5�����mE� 1��=a��~�����9��k�����Yv�9| _4^��;��/���{O��Z\~�W\����w��)�����j�%���n�>i6��7V���������;�Og�������N[~��/���w���Bldt���ii$eu�������9pR��������j�!K��H8�� ����A2�'tQ*W8[Y�A �z�X��XL��\�n�9���+����(�����5"�px������p�x��F"�(�MF�����]�i��#[Y�5�v~��Sj}NKJ��{F ��p��W?!�!&@�#�����<Y
�$���k2��*�����i����h����st2�W6�r{s;�D^XJ:�������@��ut���@�7�+LT������ ����W~�E ������� ��Tji}��5���YM��V�e������>Ru���8�����+H������:[�������^7o! \����[�qr�/��������<�j.^,�9�Z�T���x������F{�����i��~NJ�F�}����X���N7��dw�,���������A�Vu��|�����S����'�C�6��,!8�Z5;��6�
���E��D���i�/ �-/W.�t-��UJ�yy�������g���zL���z.=����_���iU~C���� �7�u���6{���JC����1������h���lz�#$�S� 6�0Y��gS�a�����J��n\��Bn&����@��'
%�$E����"�]5d4�UJ`�/B��J�;H'��L�M8A�L��
8��n�X�Dx�! ���I0w�����|8;"���������X���p��8�'7��*���E�h$�����0����f*�U��@� �E��U`���JU���^��Y��!��������~V�#:�0�(@[��&`�Y�n �0�lK� �V*���A�J����bE8O����{�\������7�&*vs�8S��I�?��E0�1�&\a4#�W�m�'� ��!�E��CN��\Te�/�y�����
��h�~}�A[F��Z
�;U_����J
�D�|7�m�
�M�9�[��@��E������7r���dQq��+3��Gh�����o��;`d��Q��B����[�Ec���Bw�<��{w�^�h��aQ�XO��0���4��?���&��A����#9��l�\2'�v{4���Q��l<�&^S
)��sB�j��&s�p<]�%����F���%��s�R]��\���%ah�k����L�NDD 7������{�l����\d����X?� -��v+�p+��n��p�~�>���NwC���q
��f��������2t�0�*��F�/$5 ����%k���?����f�^�����.@�� %
�O&������FIQ]Xe��a���[/0Iy0���d]
��O}��G�0-��p<<f�;� k����9�e�q�����H���Ji���%T�a�d{.�b��Z��*�R5�[h8��D.�UcK��U�����S
3�I�s� ���IU���TP�q�%���,%�,���<����%���C��~�m��
��Z!�j����(�^RtG&c{[�;���<�Dqr����(�D�us�>n�9D#��PLX��y���[�@ �! ���i���������Q��A����\�Fu���n�"���#��}����8��*�&�!&�hT��1���q����0����������{&x��"{�ub���c+�eb:d���RO]�2�w����������j�,���6o�z?R�����u��m����RkM(���\V����N)R�-. ��r����� �.���v���o��ll��-��n�Q�wF-�=��K�y���mn���^�����@�}L��=J���;�+
�y��=�Ft���?-��'�?���+�b������CQ(�!���#8����1 ����P��a����&������ p�z�v�yN&vE�q����7p�D� �S���FnO�o��ez��s�����e���N<j��~���2��Mc�a(�BPGxttcsI+�
�I�9�Yc�� �tH��B$R�(�Q�r�#Z L��Z�%��I^3k�T�W�����zM�d�D������M������G���{,��o�(^�8b��]�~����33��z�U�*����dg�����J��O�Y��P�D�#�S��o)���.>�;����~�(��Y��?�l�e�nS��I�;��,gdF.dv��(�����%�R�yhL_��
�kSv����~q�*%�)p�U�\j���\�����1*�4H��B�����R�4�"R�����/\�S���������Y�B�<i
:C�����^F
�Mcl�gF+�������8��� ��s_�i~s���c�4/�'�)<�����nU�{_����k��5(��0�(N&cR�W���z��xJ���'�@U�;-X��������K��3�FlO��q!�����31)W5;\���6~��xLc�P�h�t���R�������W�:��������!ckA?U]���c������*+%FR�l�#IYfU��M#����
D� �����]�Q-B�Lh9�F�S�[u���Lc���J��R�S��bt������.�A;
��I�����<�21n\�A���g���s���j<�4��y��8_��h^��/��i������.�L�� ���'��y<�g�_�+:��0�y)�;��A&�Z�kq��fy'�5���;���x�����TI��Q���^�m��<������i�?]�mJ�C�,
F�4X���~Ih����3��Gv<����BZ�G|�U�1n\E�xl�/Hb�����I��+�MTk+��Y���0�| <W��������f���+��Y"Jz�c���q�bv;[���1��x��&��'!�E����'x<�H�aX�
>K������n�K#"a��R�M#�n^��J6�\ -�^?\>�Ph�><I��� oQX��I����U��A�`V#,�� B������� b�`��z�<����?�N��y�������r�(�����^�>)�:�����M��A:�m�`�ER���2�������� ���?c ��_�"�}�;��5��=��w6�|�ttIF�IaO���~�3����EEgt�a
c��\f4� F^oI*taa��"C��:�{������}{������I+�
||,�W��=��;p���Y��?k�U������!N��5a��Sk��� �p��!�6�I[�6�c�Hz�T�����������+��������;�����^�b��}r�J�����4��O��k2��oU>&[�,9�F)o���V$q��~�9=y{|v>���:�������������^:�R��T�=����W���&Q�����t�eH�m��!��{���.n�"r'�H�I0�k.���������W�N^�R{YI�������NQ�'< M��O+�t�="(K��x��9����[Gb���Yf�"�=��������I��N�v������=���o�����p���X����'�N��uz�����A����D���x��"E�1n��`�^�N���^��K(��z����!R4+�hL�I0��a�o��kl������q����;e8��Q�)J
Q`��o�8Mu��-�p2�����������^�$���n���/����������������7[��G���f�9 ��S������ 1��.V�eu�����05���� '�fi/Q �xq�+���E}<Wu���^� )�aILQ@����h��Mru������^�S!�], ��Y���������.��B���4��ry���>���)d��1<9�����j����:��bu�{)���6�~�`����Q�����Lm���� S�x������h�!���Wk[9�����:����������,N����/[���tK����F����I[�}s���4�[Q����w������l[,��a��M���QkmD�},6��<^���A��{��_a��;�Qfg�t��p�J����;8@_��v�Um�z��x�����\�l�C(;s�B���e}g�+_��:q�F7�u�b��!o��xo����t����m��N�O���u�{��r��sDb�@b$��Y��z��{�C����N���n� 8�5 �V���H;�:��wI�nP3��^�T�a�1�+Z�BO�����\�Sb�+�������J�R{�oS��g�7�??������}b"o������k��(�}t�_�F�����T{c�;�L*��~lhI���g����i:�/�.��#�"X��h���mq�G5��/���VI��� �A��v���cc���P�u^��SK��"mSA��%6������Q��q`
z���vJI�"� ?�(�A������W�m�
�O�i�^�x��[J�3��7�z{�Q/������a�������3���5����$
]�+��lp:�DU�������I:�����jm�V'���u��@���'u��8������&��sR���%Ks{mt�j(_��8�j�0�a�SQ��)�q�{��x@3�X7�XQ��|������
�.����%2�@���9h������5�b��s8���y�Vo��d(CB��\�F�}��?�{�3_;)�����v�f �%S��d����P�7�nj���G���Q��I�B+.�>�Vv��]/���_��V��Y/����O�����s��`+zm`V!UT�g�b���[�o�9����c��I=B�
������ez��$��"5w���c6���*}kN�o���a=��s����w����� ��c�%&}a{����X�l�l�FO+�>��FY����0��`�� ��UP�y!61:���\��
��Pd�����Z���55�~�Z �� ��g��gxp[��w1�&��c�� |�X�[A���s�����~�I�-@w�����n���Q�`������0P1����m�s�e0��k�W��d+K������T>GK<�����.C���+��/������k�A���B�v{�S�ji&�
���<�i�K���:\@-^b��1 %xA������r�i��� am�$ y!O]�]�|���%LNX�J�=U� ��(��f� ��[����J6�P����L �[������yNU���M)���~6�9"�{�`���Vg�*�b
w���`�dD����d��MG� I%O��bR�%��s��nk\�O&�;nfd��!w�nC!5MJ2���G]d$�R9M��0JIM�(��I0z2������s�����PWlq�e��b-0}���.0�N@��d���M���������w�D�=�O��<�u����{2��}���&��Z�@�"c��S�=� �QbJ�`����'��2��,�����������ex;s��uh}�������"�}���L����h�=�������q�.9
��� ����Cp�� ��o.U�^���V�DD�j���k���q�3���h2@���N�v^ik^�j~�UB�x��Q��f�\�zdKe�c�
��F�\��������������N����@�L�(�ut~������S��1�>�"�2=�kN�U��;��}��%~�Cw/>DFc������htj�,����A��;k�[�i+��l�S��w�X�^����~�a~�O����W��n���F����F��o���i��F��8��F7�U�.b����t�����Z�`��N<�9���X�����������^��y�\���w�����Y�a^:��G�7. ��;�g����H� ��Q}�^����/����N���z�5�f�e���u��7��F�r=BL����������F�E��7���Q��2I�,WJ��w��9��JiM'�F9���Q8/�J��������������
P���GD����B�.?VK�6��%D�5E2�j�RZ�*]����1�[�9�@[^�9�#�Z�h)���B(�;��uA@�������~@�������u:)O�JK������3��I�!�����EM���-I^(�������#$��V ����(_2Z��,�e�^��E�U�R/�x*��k�q����Y��rS���j���Sr��Lu�Y�RNd���5����������>HY��?�O����`�t
���F�P��jG�]�I�����V��F3<AJ�g�G�)���X�ff�:pyJ �)��X�����h����J}�`�W'�qZk� L�}���hdq���*�����6����&����b)q$}�����2��hm����|��!�����wCa*�( �����-�>���\��x�q�W��s���F�@}��B��d0��1���l�o�3�����A�=������5Nn����kX� (4�=l=�y�����
��
!�����T���-DF�[�����Wd�S�������/?_~��|����������/?_~��|����������/?_~��|����������/?_~��|����������/?_~��|�������~�?/Uv � attrnumber_llvm_type.patchtext/x-patch; charset=US-ASCII; name=attrnumber_llvm_type.patchDownload
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index daae964..46b4c5d 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -49,6 +49,7 @@ LLVMTypeRef TypeSizeT;
LLVMTypeRef TypeParamBool;
LLVMTypeRef TypeStorageBool;
LLVMTypeRef TypePGFunction;
+LLVMTypeRef TypeAttrNumber;
LLVMTypeRef StructHeapTupleFieldsField3;
LLVMTypeRef StructHeapTupleFields;
LLVMTypeRef StructHeapTupleHeaderData;
@@ -798,6 +799,7 @@ llvm_create_types(void)
TypeParamBool = load_return_type(mod, "FunctionReturningBool");
TypeStorageBool = load_type(mod, "TypeStorageBool");
TypePGFunction = load_type(mod, "TypePGFunction");
+ TypeAttrNumber = load_type(mod, "TypeAttrNumber");
StructExprContext = load_type(mod, "StructExprContext");
StructExprEvalStep = load_type(mod, "StructExprEvalStep");
StructExprState = load_type(mod, "StructExprState");
diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c
index 795f671..804bb94 100644
--- a/src/backend/jit/llvm/llvmjit_deform.c
+++ b/src/backend/jit/llvm/llvmjit_deform.c
@@ -691,7 +691,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts)
{
LLVMValueRef v_off = LLVMBuildLoad(b, v_offp, "");
- LLVMBuildStore(b, l_int32_const(natts), v_nvalidp);
+ LLVMBuildStore(b, l_attrnumber_const(natts), v_nvalidp);
v_off = LLVMBuildTrunc(b, v_off, LLVMInt32Type(), "");
LLVMBuildStore(b, v_off, v_slotoffp);
LLVMBuildStore(b, l_int8_const(1), v_slowp);
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 36c5f7d..aaa5bac 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -324,7 +324,7 @@ llvm_compile_expr(ExprState *state)
"");
LLVMBuildCondBr(b,
LLVMBuildICmp(b, LLVMIntUGE, v_nvalid,
- l_int32_const(op->d.fetch.last_var),
+ l_attrnumber_const(op->d.fetch.last_var),
""),
opblocks[i + 1], b_fetch);
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 42304d0..19e7aaa 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -48,6 +48,7 @@
PGFunction TypePGFunction;
size_t TypeSizeT;
bool TypeStorageBool;
+AttrNumber TypeAttrNumber;
AggState StructAggState;
AggStatePerGroupData StructAggStatePerGroupData;
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index b0093db..b65b6df 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -61,6 +61,7 @@ extern LLVMTypeRef TypeParamBool;
extern LLVMTypeRef TypePGFunction;
extern LLVMTypeRef TypeSizeT;
extern LLVMTypeRef TypeStorageBool;
+extern LLVMTypeRef TypeAttrNumber;
extern LLVMTypeRef StructtupleDesc;
extern LLVMTypeRef StructHeapTupleData;
diff --git a/src/include/jit/llvmjit_emit.h b/src/include/jit/llvmjit_emit.h
index 0d1b246..a80380d 100644
--- a/src/include/jit/llvmjit_emit.h
+++ b/src/include/jit/llvmjit_emit.h
@@ -97,6 +97,15 @@ l_pbool_const(bool i)
}
/*
+ * Emit constant AttrNumber.
+ */
+static inline LLVMValueRef
+l_attrnumber_const(AttrNumber i)
+{
+ return LLVMConstInt(TypeAttrNumber, (int) i, false);
+}
+
+/*
* Load a pointer member idx from a struct.
*/
static inline LLVMValueRef
On Thu, Jul 5, 2018 at 4:07 PM, Ashutosh Bapat
<ashutosh.bapat@enterprisedb.com> wrote:
I haven't done that, but I think we should split ExecStoreTuple() into
multiple versions, that only work on specific types of tuple
slots. E.g. seqscan et al would call ExecStoreBufferHeapTuple(), other
code dealing with tuples would call ExecStoreHeapTuple(). The relevant
callers already need to know what kind of tuple they're dealing with,
therefore that's not a huge burden.I thought so too, but haven't done that change right now. Will work on
that. That's in my TODO list.
Done. Added that as a separate patch 0001 since that change adds value
by itself.
0001 in the earlier patch set got committed, 0002 in that patch set is
not required anymore.
0002 - 0004 in this patch set are same as 0003-0005 in the previous patch set.
0005 in this patch set is 0006 in the previous one with a bunch of
TODO's addressed. An important change is virtual tuple slot contents
are never required to be freed when slot is cleared.
0006-0009 are same as 0007 - 0010 in the previous patch set.
Next steps
1. Address TODO in the code. I have listed some of those above.There are still a handful of TODOs in the patches. I will work on those next.
The number of TODOs has reduced, but there are still some that I am working on.
2. Right now we are using TupleTableSlotType, an enum, to create slots
of required type. But extensions which want to add their own slot
types won't be able to add a type in that enum. So, they will need to
write their own MakeTupleTableSlot. That function uses the
TupleTableSlotType to set TupleTableSlotOps and calculate the minimum
size of slot. We could change the function to accept TupleTableSlotOps
and the minimum size and it just works for all the extensions. Or it
could just accept TupleTableSlotOps and there's a callback to
calculate minimum memory required for the slot (say based on the tuple
descriptor available).
This is still TODO.
3. compile with LLVM and fix any compilation and regression errors.
When I compiled server with just 0003 applied with LLVM, the
compilation went well, but there was a server crash. That patch
changes type of tts_nvalid from int32 to AttrNumber. I tried debugging
the crash with a debug LLVM build, but couldn't complete the work.
Attached patch attrnumber_llvm_type.patch is my incomplete attempt to
fix that crash. I think, we should make it easy to change the data
types of the members in structures shared by JIT and non-JIT code, may
be automatically create both copies of the code somehow. I will get
back to this after addressing other TODOs.
This is still a TODO
4. We need to do something with the name ExecStoreVirtualSlot(), which
is being (and will be) used for all kinds of TupleTableSlot type.
Right now I don't have any bright ideas.
Done and added as a separate patch 0010. Separate patch so that we can
discard this change, if we don't agree on it.
5. ExecFetch* functions are now one liners, so we could make those
inline and keep those in header file like, say slot_getattr.
Done.
6. ExecCopySlot can be a thin wrapper if we add a callback copyslot()
and invoked on the destination slot type.
This is still a TODO
7. slot_attisnull() deforms a heap/minimal tuple if that status for
given attribute is not available tts_isnull. Should we instead add a
callback attisnull() which can use something like heap_isnull()?
This is still a TODO.
--
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
Attachments:
pg_abstract_tts_patches_v3.tar.zipapplication/zip; name=pg_abstract_tts_patches_v3.tar.zipDownload
� JyH[ �[ks���g�
�������e�*���m�F[~�$����b�3 9�p���b*?~Ow� ���Jm�����@?OwC���L]Y�������L-�;��/�9���g�����I�w�����������|>9}zv��+}�Wnb�O�JSh��q����b���{��g�G��������**���W����ij���<*�#��������(/�8*f�:k���>}�/����O�=��������W����������:z6}]<}�4��'O��4��7�R0�������ob{���s�&���]��g������g��"2����H?7��]��xJ���f�-VE�l<G����)�%Q�g������7������������������?�����������������uK9uW9u�kVN-��Y95�3S�kV<\������2)��c����Z���n���"Oc�����*7+|Z�(�%����������[l�7�aOD ��S�Rc����kvY����lf��7�M�%K�n?^/�h����g�����)��,���l�M��
�sWZ�|�W�}H����]Nm�y
��H��)�L>/A��01�``���^�������o �"1i���2�W�&�����-MY#UKSz���._��#|N\VA"�I=�m
�c�4�,M2�gU�I���i��"�B���VE�����JcD��R��mnua�&�&KJhl����|�G����d�������������������yoq��y����y��d����^�J���(EvUn����Y��,-�2�/���II�� >\��
�C/a�~/�C=A�(�)��tl]��P���9.����!2����uR.j#�6�|^�Y�ds�JM�I�j�`�
��6z��,����V��U�2��az�)���wV��p�o2
�Mx�����i�h�`8�CB�����-���f�"���,/�b\����x����3&��2�'h��2W�^�Y@���J��WQt�p�--���x�6wjc��I���V�]W�b������F/�^th���CR��I�V�2��8M�(�Y;=�
\�R5�9��� �8�.;$u��q6s��)�����%[)m�!�wZr��1|�W))���vf�
��FaQ�Cc�^�<+
�V�n*X�R�Q%��{<+���� g�s%��7v^�c �p�'�zNc9��u^@�rt���� [�C����E����a��)������!���z,>��)���
����Yw?k��Pf�hg����r���
v!�3X� _C�uy��k����14D<��t��#~��r�4I�rC:5+�ej�y�Q������t�� a�������a�( ��F-�,m������a�k"X\� �������Hg���8q�)��H/sW�8\�g���A��iN�*q�$��h ���
�*�E�s�����.�)�h��F�>����#�#��D[������%�`t��7�������X+T��X�a�%J ��[�PY�iOj��^��jy �y���0X��k�=�7���=�
�����wl���&�c,�wI���#<7�`��!�!�b���$���w��,"�K�*�M:�(�@�S0�$kr�f�|���Z%4�_c�45Y
�v
����1�B���z����d$�q�%I�Xp����F�s�9!���l`
(�a��i!0�-����
� ����|j�i9�h3���z����?��<�������������$�5�#%F�Q�K#�eW���Q���k2��p��MxO�b��^�&�`b���/��`=�+~ �I�T��7b�D@��`a�1$Z'��J�L�
���,�L������{lMD�����~(�b��
g��!p'�(1�&<���@��y+��R�kq&��2Ik7����c��(�9��0�e�R&#.�����<I�>g���b���q���z�0���?�V;��2y%�1!�fG/�P���2���)�Mo��w��C���\�,��08Ue��<I�0�Cb���H�����Hxv=�m��L�g�����c����$�Tx!���'�v�M���C���9�_v� 9[���F�]@��cZ�� ��M�N��� �h7��=`XHu]����=R>�g-���_� ^4�s�,*B���J�I
X�p���d���$A���k�0�Z�l�t��'#���,�
U���aA�5Qj@��,T3�:���(�L.��%�FH�C���ef.�:�D�������K;|.,�i&m���1��c`y�X���FP��,+b�7[9(��5�_`dO~��� 'z>�V!u�<x#����M-�&.��a��`B��5��� ����V��N�$�%��D��%���u��EC�JIy�usQ�J>PF��3�!�"���<�����W���P�d>'� }�&@���Z�8�
n�����{>#LZ���X� ��k;��g,�U�:��,�E8}/����x�S!��|$�!;��� [��"��)ya>8'���++�N@�A&�|���I�Q���:�V�z�JB��B3_�SV
�{6������g��,��1�i���KdW`�hgE�*T�D<�<n"�� V���p���/M�h.4����@S��vX�����E�R��;T��5W�����^��{�R!dkb����>�1��T(!��p!����6���Z$=~�w�A�?)�%m��
:���:�� {@���HFF<�<=��&Sl$��V���`Y����$a���R�NXo
FEcu���HR%CF���IFed� ?'E$����c�������.&k��:Y�%Em�����(��q}�6���h2�K7N�m��r�}p���n�`oEI7�|��������+�B�&N�^�`��L���mFv���,���h�&6Hy�� Q5�l��c;�m�������~���,�K�/[�k����9�S��Mz�x����}���$J��������� #�q2K��R3�T��#��������� ���*u���s\��6�CT,�)�>�8a ���b�`�=]��t?�ci���c6�"���D��B�0 C�0��J����}~JQ���nrs7ys�����������0��T���P�O���F��_A��:�n��iWj}�M�q����R��o9����$�!<��x�]�J�����n�~x��;��9���0Dq��H�|��-S�W����^E+F������)>g���u����cy������W��%� k�[��@>M����������&�*,�t�.N��+�o�-)��mOX�]�}��
Nf�a*F�����G�l�j���@�]�S'%�3_�i+���Wn����Zp"�R]��F��s'�-�2�B��ldWx0���Hq�~�WY\'2��"�D����)x�OIf� �'B���a�OJ�o�kp�2��o���lu��ZHD��V�`�������6���|5�n n';� ��O�QA��� �TA|� gK
Wzc����C�D���j�����r�I������e"�Q��U�U��u{,�?��S�Pk�L"�-�C������J�B����.X�d�Hig��� �X����\ ���B�lE����,��w���<��}����B�!Le��2 �a����;�"N
�����e�:"1R��N����D�-*u��9�s=�r���v��`w+W�Nf�)����R���("�p$�o��&��nZ-���T#�&���6(��v�g�v������b�3����,�����QI�i�����,h�^ }h�=�#g�!���a���yZ�?87ru������t3���guw�����.L� N�����z���%~=�I��wG��V�><=;=�xz8�H�z��sAz�W����_*Oh�I9M����p����A�%�T�t�s�����\wA�7A2���l� ���gS���+��2)�Ot�T��w�@��T����:��������+$�B��[��
%�^$wL_�Uq���\@���k�s^�:��q�V�A5��'�S�jm1�p��+i�1�9����G4�|��P�>e���H58���3������p���o����RO��M���/�������G]e&���`������Dljwz'��|U��.j).qh}C����� �76t/
~�2x���cel#�].")�=��&�Pjn��Z9+$
�
�J )��KZ� ,���t�f���S5)�q��M]�Bm����������TQj��5~;���r5XZ��U���HM���C!��G���d�r�+c�2�y^��k�[�F�a�j8�x��p����I,M�p?��$��m�m�xU�o��(��T7;��}Y��w��X,t����GFM�O/��S���?�O�����*������{�����X�^�WII^��(���:�����N��2��Q��T�5�a��t1[Ue��^�Z4Duop��C�jo����.��TQ� �-���a�)��s�E4w�\��P��,��Y�~�|�QLuGw^�Y�V���_����SD�Z5��������2��w_GU�eZ������e�����({�����u��B��L�;�w�������IOu:LU������~���t�Gj�&>oUY��� B ����"���p�8g�87��s|�y�Xv�"QQ�@_��Rl���������b��?�L����sY�k����7%��
���&��/e��}aR]�����%P#7���+@7L6#�������g�v_Pr�3�����-������A��B�j��rB�Fs�����Wg��/Y���}}{���R{t*�(�wI��\D �G� ��[Ln�������U�#�����|@��5�������R��l���M��D�1V����m���ZHl�J�|��^~w&������F� $��^�O�v���M�}4��'��I�.���Y�����n�S��=X#
i�h" �+J�RS�%��Y�k.q�N<��r;�(8V�H�z���n�u4>�4��C:3�o�$��o������h�L�r������]�
�gs���aX�N�p&�~0���7����n;���M'`�����w������d��AWE�V�E��]�q������X�["��]D�0�V��{k�p�|
Rk>�Z���u��'aA�����Y�
��n�Q8�a�������]`D��+����#[_��F�u� 0��d�K[�ix�6>
qz���J�F~�i����3'�?��4U���H^!�������;���CjB�)z��n���%�`�?��+~�� T�]b����4�� �h'0� ���P�w�������::�����n,���(��'w��%Qc�5TP�9�)��[� u@�?�Dd��?�L��l��S=0Q)]�C��Od���D�;�VN�P�6������%�3��k�D��:��&���u�)�`�^�T��vT�Z�g����������?�S���aH4�o�y�c~���p������W�7?�2��h8�+�r���c8�a�BP��
��$��*�H���6 �>��8���1�%�%o��A �������������������7�y�R�xs�Q/h��\�h��=�(��@�<�- pMf��X
���� �������!�F�`��d�V^G�����mw�h�{A��a'���( ������u..������%Y�6R7�2�� �%5A�B�`7d)XI��u��j��t�@�c%%���j����z�������__�����on�?\�I����H�W�VT�o�����1����3)��� ����F.��������Ru�z��g9?���V�~j���4$��R��g B�|�a6�/���������m[��.���<4�_<t��C�/�PKC��!R���������\_�;�����twHq�2�2���a����,�u9�u}� UG�h�/���=��o���#��������2��O��T�AE����s��������"���c�]��15������`��3���39/��M����.�=�Y ����$��;���gg��IU������!
�������m�H�8��J���=�PI�~�'�ul9��my$%�=���4�M��$�a�V�3����n@}�����lH54P(
��O�hF�?�5c�(Y����6�D������(]���A!US\�0Nu��-�(��
j*E ��i�5��T�������c��������u����g�k/}y��}�������'?�3^�C�T�S�I�Q����U�+�.�4�V����V���v��9���|����VJ���i��;��5�� ����K���[������o������4iw;P3�O['���fG��uP@���+�f��&�����������kb|��)Kfk~l������Z�Y�V�W��U*�����������
��$��#���jf��e��5�r�+1��%�����0��miM�1��WR�k7��^���~-���t�������Z�-[+�f�����m�R�C�,�����yx����e���Y2N���"���w�Z#E�vY��.�~gw��������n"2AU�����TR���p@D�����*�r�7Q������ZM<S�El^�h�Irn�q^���Qli�r�B �]���Z�Vs7E�������{��["��eQ^��IY���#m�!'��p��t����
}GNr�M?��f�7IY�K��4��D�$�
��:+�A>xQ����5��p�DX>]D�����mVM��;0Q(�%�5o0���3�P���p�|�&n�>Z��s�Z�S��>
��/����U���������^u3��x���j7��pm�&��>e����4j������b+�v��
�6��8�U_g���]����K���m?����wkF�������xGm��t����Q�"Et�}�i�}��Bm�v|�Z��������!��8����=�b��V)uH��=�����lv���7c�������R�����N�;�����s�_:<��"�p���yM��������[��M��9�(
��R���hm?z~�@��O����pu���f�b�\E�Kb����i�l�U�4q[�������3#�e%?`����T��������6|
�
�NMh�xq�FT�U����� ���F�D:_d�
X4<.���,�+� B� ���C��]����_n������jF�i�r����h:t�f����o�{������E�o�;�g� ����5���
�g�s��tC�vy�!0��cE�eN�gI�=�@�T�E9�?�3��5�D���x��������B��R:LE�"�s��F��N��38E_�e��2|)D��������Oa�6p%�p,�iO�����Sf��~��7����������� 6�-��W�s�*�C�q�[�3|:g����;�Z��������di��V���T}l�%��Zbj����)��\��
q2���{�%��EaX�D�1i��&\���`k���sf�#�R
�y����:(����xA{�L�p������>f�F��Wa6��j�#��$� �l��dLd�Z<�8qo�s���`�V���c���A�������&�|�t�\�y�&g�����`��-5�#��G�4���`pU��>�[�$8�tp��u������9���#�LP�T�{��Sm������vw���Q��K-|W�w(��l�����
�/s7CA�������^��*���HLMv�U��@������N���Z����5ps
f�!��A�������jW��"���v�'�|��v������(%�`�=�t>�t.�ZX��:��'���������R���R����GwP"{�G� ��@I�E�0�H��
��Uw�>�5zd^�@>�w�7)4����B����t��x{� 9T_p+����4�^|����� 0_�>���������R�L����kH���F��t��v��=�8�����o���!���`���]hj.j{��.-OO�v��jQ�n<����>Qw��Sr2W��+�� �|v@Qp���N!�M����P��0P/F��X�p�>a�,���[���R�`rq�;g����B/�I!@Gv��Q�"�i��U8s�K�K���a;� ��s���.bD[Q�^�����{c
�*�5x�g/7!��b |�-��5z��-X��~r:`}}�&�G�^I�����-J�#I��&��[<�I<J���WA6��F�����q�8�F��pJ�'`f �m��T�@�e|���&��e����w����r
�mxU�����h���6�T��=,��G��Y�|l�u�R���!;`��J$
0 O.����XOpd�E[4�=�W�UF��`c*z��e���ER�������&�N����RZa���y�D�+�������YT��pC �{W�T�h^�Dk������N�����=$p���������D���f�Zr:�.�-��^���
]�h�#k@�%���E�`E����W�)���� �v����qcr�>p������|��\C��c:4��U$�������������LBef�
+D��*�}/h�"�1>�:��g95����4l��C�O�Y�y����+����������#W��?^��1��t�n�OO0�A�gx�ft�9���� .���-�a���Y��y���_�2 &$�4��u��%1�z���m��e`?����5Z�oHm$@t�P6�5�!��A����8%
�(#!Vx��`J��"�������[��L�R*��r�����&a�s�Y���RKv�1!�/(����%Od�[��s� �]���a��R5���i��u�r�/c�4R�(*[��!rh��_Q}K�������vK
�L�9(�yr0� ����MT~���Fh���<A+k�6��F�-y���D�[n#�i�Hs�;�@�za��P�-Sk���}[�f�t
�vT$6����������. !�[@�|�����C/��y�:��!$��
��m������ F|C�Y�Q� )�P���`��w6�-u�w����Pc��g��DlpL0����@n��c��������Y�n$��R^��2_�����������L��9�<z$LF�)�3����(��#���G+ LU���'`����&NUv@�>��%|(������I���"����X.K2q�� 8���I����V��j-�����G9���Z��<
q&��H���\�)a�d�/Q@Y�Ei�a!H0c5�����YZS�UYY��M�l�j�,��XRWQ�"�T(���
�J
��K��~�E�CY��5����Uh���8���3��g����/`�R=��/����[��K�h�N�����*S)SF��K�4��_�|������`+HN���X�4r��(�xE!#��1NMR�'�=��9�AS5�ax������]�����_����/-�����'���:z����v9������a�
��a�l�>L,�z*O%y�`���4�4L�L���fI9{��% VB�B�
)WDia=����n�����������(G��l��:�f�o�~����n$���.����������9�K[���p���������c�2�b����0���4�:o�h�w��xD�����^��j�q�Y>!�M���iP+����Xo^18*� ��9��Q��
i-&�&Q�u?��.�.P3:N|w�|��::��0G����6�H���q S��}��_��u���_'>�����u�����2������*\=w���7l6{���v
�:Y?������V���L�mb�7p"!���3���y(��_�X�+�Or�`�d�G/TQ�>��3�����yl=��<�'M\J��k��Z��G�#��iz������pNngMFXE���l�L��xJ�_������s��#�R}�8��R��k�g��5K=fj�O�����Rdu�Yu3� �� ������9e^�}�7��%�m�1�<�Fo�!����,����'�����%A�Q�^5��h/���x0{��~M��-a��y�amBj���D�$�Y�U&?�1��V��?�a;�����._��pL�4��y"{��%����s���#[�����AiJ��F���_r����Q��R��{�ao���{�������{s%8B�������X���l�����8�Z��C�V�p71' (��7^l��z���'��O ��zd<����. $U����y/�w�d�i2����Wq�S>��y�%��f#�;��M��g�Y���`�t!d���4A� FR~�,�p��X:�+�����;������}�� ����x�����g������u���1$W�=�X��\�D|����:����n��l����jU`K�������t�tQ� ���!���U����&o�y���������WG��0bf3�������4��NoT����.�m(�W���r+��J�g�[�7�
"N'XV!���i�_�=��i0��o���QwX�=�~!cP >���1����n���@Q �������V�`�D�[�/�lm�S��a�&�.4���xY��D��E4�i�e���b�=��N�=
z�p]�qe�JQ�b;4H�LP�!�����H�HS��I���6�g7
�>��������9�S�2��8j�f������h�B~9�N �E��C�_ST��S|$�aO����&4�/��%�P(�%�\�<�$L���L?�\"$���
>�p��,���e4`��F�����B��C�C��O0RL�Q:����c�q'�7`N=~~�]r�l|�A�+2U�y:�0OJ��8��:�7m6�����U��qE�0.����I<�������5��MA���
<�s��R�"c���E�&�����0���sS��Sc��M���T��x��B�t#��I�3�9%
>;}
������]��o� >j2g2t��a����f�k
\�St�K7Q0�q!Z�^��A�� ���L�}p7���'k�R�b�D���9��*�?a�\l1�j�b#������a+>�Ch4�HfU��S�*X���p��\>�0���#=�nW����t�6,#j
p�HE���8�Gu��o_����9;��rry��K�y^8A
[u���p��m���E��������T�4g��R�P��S��i6�]o�����D;�(Il������S��_���RM}QL��.���n0�b����
����<Hi�����2�2]��'� j[����O��P�� ��N�I^4�pk�Lq�Gr-Q�����Y��s�Hp�eA�-a/���d��2�N�����j{� p���B�{��;A�)���I�U9���9\��m:�uA����a�`�D�����u�����(m�x���],1hQm���
�Y�a�Sl���b�1~�����w>b�������b��
'4����P$�?m�ouD�K^2��
_�����m��n@a��\����v��������A��l��^�w+�yF[�Q�qlz���'<"G >�N�+����P��*\��'7��aP�����*��H��������(��?($�kV3yPC4?ZH�W�K�w��O��|k9�2O?���*�e7D?�!@ll�8:R���S�*Z���_E�x�9�fQ������l;�ik���(f#b�%>�a>�Ox����GY�����E��?����;g2����[����?�;{}[��#l�|�S!��a�"��J8���m
�5��"����_;���~�����sx8�e����0\��+�(r�-(P�NY(ri]�{K={�>���9�(�����I��# �PZ�E/��hW\3�N�c������[����l�;�W��j�(�WAl~ew������l%��S��Q��$P�l����8�h��������@r�B%�j�����s��T��b���%�
������7�5�3w������n�Ht�B����|\�OQ���ak'rfT�;>_���,�`d���v��Lp�s M����c3wHt��e�6�t��F*�Q�U�
cq=v��\��ZF v���Y�
g�������~�'��N�{�x<���}j>N$:K����_���������E�4�������������#0�Z�D������3���
�bB��I��� �@R+� T���D�������d�O��K�S��
H25����2
Dx��B��'<R��(�����c��J+8�(���}����2�w^d��~`,����}wX.������Z������@<F)�#�7��F�(�3���_)���~����`��:�Qa��<�ed���}BK=��m��OBWa��������7�����|sq�Ln�(���l�P�g�`8r6�8��k2���j�_E������&Z���F�
�>������� �����Y�QP�.%s����������T"��g�<c��!��:�]���z]�VB5���V(]=b8/���B%q=�����pP������|W,����
������Y8��^8,��D��8�3�Z���*3�����Qm�ML��=�^"h��v#l�n��"�T�V�����h��5����W�����&L�[�M�d�E����o�vR^��T��t�`�r9��������be�Ej���F�.P�� ����QB���������^^����B�%~������'G�+��s�< �cU��@Q~t��Pe����"���Y����"�~C�C���O�>JK� �S*&���&\gS�T.����;��b�TSCeY�W�:�k��������O���R����
�4���2J�3���vY�
�d���-T9��
���a������x�o��|KUN3ikUNA��,q��5����y$!�@J�s�(�G ���S?��O����~
��v�'~���b��IY�;8�+j���@a������9c�g�����U����^�l��t��D�-�3>2.�y[�l,�0��n��3�{��ep������r����W>}��O�[���HrQ]�h��`Ze��U�K�wF '8��=���E���Ge|d��Y�=���c�������N#� /�����
*��l��'[��Z�5������_��>����������W��_��O..qo��1�|]�a�4P����E����LPQ����_��5*DxqMq�G��?�v&+����C�~�0��X��9��\���ybK�#sy�����G�0��ZG��O�g�Oi�}�o�������� �<������ ��9;��k�\����t��R\��������\X�Q�)-#���:Z��22zu�(n���
����E�(l�\B]AwF�9�;��x���7K%��b�{���O���������|�njX�t����v� }]������g�8��������h,�/��
�� o��)X���O��Z��/��X���\D� �
s��*tS5�����t��U������ c�R�L�[5����a4�I?�m�:C���%�����o�w������>��[��b�,;3u��N�>cS;���$�0?��-��0:b���<kd8`��$3��2e*w
V+P���u�!�~D�� P"m��]�����|rQV[
Ce��4���]y^����/
���]��<D��U`�v�,x����O�r����:��L*�\���aPF�;��
�
�����yp�\�1r���S;�\x{V�5��/�����Z^e5jnY� 2��7w�'�HA�xl]��[���p������OV��Cs�n��k��G��u��<����/���ZYY�����j��+�3�����ote����[�j���s
>N��g��Q���3��;3W�(����u0FN)���Z~��l����s{�I��]�()XWu�B2"������m�(�*\��r�^A�w���+�`��TH+ #1��;������nIp���Q[��G�lZq�Q~w��G^�����/��.��cxA�^MN��`��\���g7;��i�#� L����0�*PC%�%�������o����O�� �����;���\|���SJ��������|0���O��^�JLG��7��a:��w�+��I��8�,��������4aq�}�N����|�s�<�\�����`���<1��&�wm���u�}���qW&V3�=��93�+{/7g���M��h\U���������]�
{~�go��0a��(a9)��0
cx����fd~�E�'y�k:������)�u�W�K�l����3qhel�D�
x.f�zf��ah�>��u0�-p��}a��Q!
��/Y��Mp��/��{��,���r�Gd��O��"�[����|u_:��I�u�.+�;�������v��l�F��T������J��O> �/��:R�Hnv��^}ed�p��#�S�a�o��b�.0���v���i��7_�y&�jU2xC�Q�/���:�0���\�>�1�O�M�����<�P$�|�J��n�n����"�s-��h������~"�[��y�������9~�L���
�#� b�n��qT�X���3�X������j�XmnT����RJh���:P�����>5,�R� ���������s�<��l�?��d ��r����5��{��������������~l�i��Q�lT�����Ar;�?>�i����@���u���p�3���;�����N�/��~�����;�_��U-��(�j�n<m/����VCrp�d@�wMK�~�<%Pd�bF
�Y��$��WK�
dC��^�H{b3�L�x ���y�_��[���&��X1��N2�M������f-�W����P�bb�Ht D�������<�!e���p��,T�r�b��j���yg7���}�4&
�@�P�1W�h�10z��;�&D�6�K�-+f�z�`X�P�F3C�6���{W���Q�#v��_�?���>����w9Y�H����
H����B���*�����X3T Z$�)_�b��X��w���6��`u���S��um�cR(�|�/�M�:�����z�d/��]�u��&�d��� ����������5#�d��w���VAb��9x\��g:��8����Debz��K����bbQu�x1���� ��n���s�WlpE6����+o�>mq%.e�Z��9���/-Q\������G������I��N~����,��c�8�Lo�A*m�b*��,*��~�x[�
�u��L{���7t>zT�n���Z��oZVA��:��;��p�����W Z�T��e&y:D��c�`�P8+nn�!�p�p��7X�6>#^��M����Lx����\Ua�
�"h��>��ssm\�^cdY,?�8�_!y��cy��w��^|x������w�����������6/���K���.�~9���� ���p&��a���}&r&�����������T�+�'���cE�=��2��SKl��j�]5��7G�-��bG��/����g���n
RF�s��?�����N����g.*j��%�]T�^��N���D����8R�E� teA�g�U��]*�sP�3�������`�0��r�/v:����*��x�e��)t��^H����F�en��q�Wk,{�v�M;(�&(����)�K��'��\���B 8������ QJ�a.TEG���@s:����������|�H��C�s����^�x������y���<�������G��Po��r��U�q<Yr�(�GC���
\[-�B�������f.�,���j�Y�s��xX��b�m%23OB$����-��.���<�8�dar��l��OM�I���;��`.�N���?�i�w�W�Sj��q�VfQ���qK�SA�T�X(G�5��I?�YJ���V��
���B�O��c�s�p�k;�2O�����{ �Y�61 �j�X���*9M�����6kv)�K����,��� �����_Y.p��%�$C�oJ]����_m�<./G��Hbx�����d�\zr�>E(�`{�"������'oO.O����[z�g�1��5�[�G�en�&�}�;;��.;;����; Z���,
�.�D�x�h������R=��;�d���)Y
���y��1�r�*{`z����/S�3�f�~��p���*�{�\�dwq���TG�D7�#tf��������M������[x��
m���G�&��x����v�oMgy���*�M�
f���105���Gi����4W)2Y N�*n}4��&��2�=|�)�+�"Z�/���9_M�h��2r�P���B=�O��V}M�� ��|e(���VF������z���BDy3[B���j:���;�r|3M�a�w�_]O�Qz�M�����U-nq5OK�
Sed���?�ta�������d�R��t9������|�Hr��� ��|ub�����'���3���>{�����oS$����N��#V��U���������������O..����l��A�:�����Y�v��z{K���pF�������7d>��!�������S�4~l��&����G��%P������,@� \�M��"i���������aai(��ZoBD �+���^.��-y
�o?��1h�:��W��$�J��@L�%�z���6t�8�;�9w$�f�����q���j'��b����Nw��U�ui������6���%f�
������<�9Z��XE��i�.�R\��]d��+���i�?P�B�D���dI 4���M��CN���<�9�`��v�'�|�fGO��aK���S^�o0xY*��xA�@����(�oqa�����Z��'��������4<����j4:�H���-43�YL��_��"3���C�
���D��<���c:!
��!����o�CD$����n����`|�cry��x'tz��a�j���9��:4�*�Zf,U��v2vM�Vh��a�/�L����)W@@���2��hy�� "�c���O���}�n�|A��Tx[c$���M�3 p{-���0a�G�~8g��L�@��/h��\e�>��'O��6�\���d��F�|�Uj�k�yk��-}0m��k������a��1~���Z�_�����Ij�-.�wx��p�q�]r������>�kb
{����|?b�w���"�KSt�4Y6��Q��
��m�"(�m����jJ-S`G^�Gzj��(\���s�V��v�V�!DE �M���S�TC��D�ChC}g��]0�H��/���s���<X�2����a��_��8�d5�m���TeQ�;�&nM1�Nm2���"q�I�].\ ��!��0q������}��5��MIv#OXB��*
Gt��1f�>��q[�+B@�O�f�4�"�=GTL
[�2����l�&��!�1=Y�H5F:�]����|��"�bB������@/"Aj���/1�����=�9�LK����`E&��^r����
U�
��$��<�+m6�������^��dp��UA�|r&�I8irS��D��m1kM#��&�g��<�<RX�I-�� �e ��5�C���y��<�\�����jSR4�H��L5�)�X��hh����my����)�����Z���fd/���(�Y=���H�� -�O�
���U�n��
:����-R�)��X���7o_�9?���s���+��������p�?���d��o�>qp��3zA[����XA!M�Z�d68a�FK.�[�l
]V��KY�� �U[%��I:�!��
�m�Q���T�t=��\x�$k&
� %��w/�x�������\�����j|��9�.<N��S��KZ��S����)k�a�-qd4�WK#(����1�/��u��N�Q�h�YS�A����3����3!���Lb���[����Q���4���>����{�&T��g�I�tg��a�����c���� �O1D����6w���Y��o��,����u���j�u��������S��:��C2t���7�!A� c��|�k"y��:<&�K&�!�����{�m�Yt�]�@^��*h�h3D���]�kJ�U� �;b#�<�G_wT�:kyX)�y����*@e�FA�@����F��!�sE:���<�;
�7I��MR�>[��}��ZE�A�x��^�za�aR���B��xM���6nYk������o?�B9O*'�H�_�Q�2�M�_�J�F��qn��������5|+*#71(�Mi�j0/&7�J!
��[I$[�"p�����D���.n�9�X��DW����X��<��7E�� �TX�j
�D�7�3e���47�;�����SD����6�B��
K�����O(asVN��R���v�z���2���H��<��"��4�S[+-�.���Ru���K����If;���%�LM�������;��7�v��\�7�v��.Y�kug�#|�����I���UV��5I�������WE�r�C)X�f��=�j21|�
X�*W�Kn|u�����n�}���.a�<�����P��p>��)�0���%�/P�~�IL��*�L����S�~+��*�L��@�4��+�����J�P�Z1J,�r~�O4b��Wek�k��#t�2��7� ��
�5��s����m�[���X�f(��C���]O��~�����10!�bj��G�e��o.;�n��`� $����d���������Z����cRi���������GE�$��8�0�c���a��1Q�����%K�����w:DJKt�t�S���}O�9��P�D��&�;Or�6��5��t�'�6^����8�N�Str��
����f��Tj������f����(�����Q�3p����9�������.��l�����UB�PL�K�32����������wr�H�\�8_���v��9}�+���y�-9�9F#u��D�p:�"����$������Q�K�p�4���W���/�sF���q�TnL*L�a3���6��5���������x�=���^�ue�D����/xVWC/?��Cq�P2��� ��>���w����e�dpH��Q�� ������p��/����4,����-j;!w�3���� �*D���TGz����|�T�*�4{�8s������T�n7�R���^j@���d�VI~�,�M�u�}�o�_�M��Y�~sS�n������b�|�#��{�:�yR�A-FKl�q.�O���-��yevF*1���Vk�5����J�^��=M_P%p�>c��F����My1rCl(q�
_4���� �H
1�q�D�bE=G�:l�X�;�,���}s��1�}�:)������_������i�ah� ��y�)�<��,9�%����4���$��0sIjeu�Vd��Q���Pxb L�[-p�%s�m �����!���eI�N�2�Y+���mg��J3���X�G��;�=U�wz��n
�:�6�=��y�?��rIt��I��Bo�~��#��%���B��!%�e��X��^,s��k.������m"AT�� g��m�\��^E��������xQ��:����D(��\I4l��� g�I�S�y�zW���OR���u1�����A��1��s���%�V�z#3t�Q��(]o��7�B�S?Z���_���b�w~^����{}�Z}5���V��'�q�Z��9h����[9m���l?�LN��8�����{x0���������Joh���!bo�i���6���0#�����^}t� �������:b��o��:� _������A���H�th�����!q��b�k�j���`��17��%f����p�
��`O�Rn�L��z�
�]��f��G<�p������"s��[�*O:g����HK�D � ��=�������t�]���@7�v�Ro���S^]��#�� 7���M��.H���,sJ����g_����L%�U�)"u��������j������U�[mkF�$,@�b��&�@�������M��7x��f G��s�`GL(�yV����������?���S.+�`�z������H�|h�������8oV��5%m�i���A���,�����"���$�)o1muY�|��j��i=��g�}7Y��0��Q@_K�m�t�s3O�v�Y�s1�L�z�Y����[�a!3�Y���)��U�J��t^���
��������h��������o+������Ed���0�m�V����HW3�W�������������(�YA��I���;��DO��*Xc:�x`
]�����0"|�( =��v*_��gnk�q����$�]sC���<O[)��q[��!]�����&�f�� ����B��$��K���(�
�+����5�>��+e�
F�d[?�~� ���{��^�q�P�h�� �*��H�����1�l��%y��[�����`�����`�����_�
Z���� �6��?��'P�<23B�Z�?�D =m�}�AAa�A��������, )p+;k�V�`����A�� ���=�����\7c�'G����*��?@n�`��[��"�t��
�0�,�d���1�� �������E���7cur�&_j����Z�����bwu�K�
20@u,�++�X�i��^%s�#�����q���[��0�}x��"��������Ml�w�^ ���f��7W;�2J��XY�zrE�&7w
�z��O,�������4�����<_�^��`������a�"C>I��N0)'bAY�
�H�Y�n�8���D�D��������*�T�:t���wM���F)#�Y'5h:�.��YI�==L�:ld$#���^�pp�V0����#�tF���
�������1 ��:{���A����9o�#����6����)������a��uD�3�t
!���~�����<�
��}\�~O��T�.R8�u�����L����2�~��M*�my�������_�9�V�����V0c}~� :�
H3�[-���$ ��1"��#�)��T3}�w����m�����V����H[4�\����r##�������I`��*��������^���������9��!�,&�<O��p���~� ���0�WP�����,����;N:!��5���A����x*U��'�Z�hxr�<�m����A��Q&L������@���Qh7��V��r�'�J��*�9�����Q�1��V��a�UW��r����C�*� �m����n�)9-G�]yw�N�m��B�m�`A3JE:E�����d�v�p��Z�.��~x��n�%�L$�YRV�Y��h����yo�F����H%9Vz7$�B�7�3����/�EZ8"�f ������xA.����QwcrM��$hF���R�������4(��lu��q��X)�K�b)K�q2sF����U���2�F'��������u}UJ33�����(Nrq��J�F��&�za��i{��NW���bl"L��%/��b&�� �R�Zj��D��o �63�2C#���Y���8���C:n�>[e���@�v�5�/�!�^V�������Z�> S�:��4\���S''�����.PMIL���-,�81��Tn��@+�&�^�>VZ�`�T�(����U11e�u�N<��Ca����qc��%���������G,A���j�J�m�L�{�2�]r����o�i�v{,��y�V��M��h�c��'��4N'KL���?��;���� @c��7S�M�N�������P�� ���n������8k9���[|ZU�t�hJ{n?��G���w���T�q ����7��&��589W��G#�#�����Mh]�n%7h��
l#��y���G��yb��=�o��u���G'�t�<s�ut�:hl��_����a�n>*ps��O�|B��e���+;�o(~�����X��+�o6�1b^���w&�.�o3r1_��Z�zJ{j@�Z; �G�
*])9n�������R�-.�VnR��H��)���@��Qqc�}���n�!���FE��v7�����*v�2WS��bxR5P7���JV�e�p�JzN����F�T5�]�cJ�t2�r���M�>�8�w����Y��AC�������8�8���>��H��z m�,o�9��w���Gm[�6����0�x��d��V�$���We��/�Jv�_j*���d��{%��ck�</g�b��F��}�
2�����@��1[���#>�"�J,�'8|f�r�+v����%{r��R��������_>�$�1.s�m�}=�_�k���n���JC%������L�|�,|�h���y���j�|W���36�Oc�F�*��:�w8�1[B7�BI�!t�'�8�������p�r_���C���l%�{�w"�p�5/��g3�����,�������������i��by���6P�z��{��v�U��'�J������`��9�����X���L������}p��G��p^?�%�� 11h�o�����NA�
�T/;��K6n��g!Wg���)��`
�e-0��#�N9�C����y�B ��JDmQ����C*��VEa����}�}����FH�7�F������ �9UQ.�{�1C#�
Z��x��6��;mK�~�X�f(`9��+����K�i����t6����*����[�k�t\�����<`_��u����S��\�R�k�%=% �� ��8�P0{�G�> ��uL�x�����\�7���U�[:��n��)��PRr�[�O��EC�|��k��c�)�����7��SS���1� !��y���d��iy�+|��//&�����WJ#5��� ���?cO6BO���JOZ1����Ud��M`3i)��7��)� N�~� �w������|��Vk��X�UW�=����J�Sz�8�������L��g����z�gw�oP�].���%�~�2�%N�:Uj��N�J��
Io�\"\��a������W���X9y�G6>��W-=K*�������z�E����D�����u� p�
=4�r������x��&9��`c����j�AJ�L��6�`����Mt�������B`��>K���Q�T�OCL�1P�E9�y�������V��+��:�3wI|s1F �����b
^s>VTk���#�1R9�t/��9=���@Ga�q
H6Qa�HAb�h����v��Cg|��e�!hC ��.t��@�wuSy�t��8m9�xU�@�K�i�F+I�Uvq[�_�^w������K� ��Po�KU��`M ���&�N���H�`�����%���+�)|����v1+\�[��&~�����H�.3\��`)ZA?��`A*p������� L�$KT�f
�}<M+���y���9��^���&��Ob����*�t�r,��bIA:1���_����0�d7l
������z_��U�V�5J�L�n�-�
� ��G�������v�J:�$Nst�o�r1��:L�?�~��0*�}�X=D�2��yY�pZ��L�e-�I�]\��L�Z���WU'r�U��re���Y��g�@��-�� ����������R����7W �]��hk���Va�56�(C�KHM��j�T���T�=N:�?��M5iU,yM���������)jC7^Y3�#�s3%��3��>�����*fV��b�2b�������$������ENX��S
wNA3�E�lk�(����V �H*��Nb�A���[%���u���(^�V��[A���{��Ol5h���V�u��q��H���f��~��d:�|�o��'��2L��7BOnU)����`U�C*����zu��T4sr���������^��6���JG_�eF�f�`�X`c���V���f0��J��I8o��MsQ�.����5zx�k��d���4�{����r� � ^B��{��i�U�8����V���������Q�����������f�PN� ��W�ex�QJ��_�_s��ua����$$��k��b���rj�����B\*Py
��Y���aAn�?�~
b��Y���k��1N�l�:�-�P��|���%wu{c�����\���`�Ep�N
�������2+���I�k";Q�<��doDL��Q9I�Y�E�ir�,[8�,��������S~�jZB���c_s�")|S��
�
���(���P�9J��O��B�8z~m��$���o��@�����)�3W�M[15���h�n}~�9�ki��Mg�1F����n�g��I��=�_�sn�Gs����{�=/X)����!BT_i=����� =��u����);�;����Uj�P�!�F�A}8FJ
1<hJ��8S���Z�� ���|�����I����h����$�|T�<�?�+\q+�l5\���q^��}sL�J�0���H�`8��B��G�����.9����z�U�j�� �+:c�G9y���*�����H��vo����Q'�iE����}
����[���R�L�������S�E��]x�(]p{W����w{��3V� �D��+�������#G�anz��)r����TSq�������5
4�;�5�u���`#�$X� �]m���:��5�'U��e���2��E������s� nM,��#���hX������mC�V2�����P����� �.c��Pt�����AA?�Z<O���>���������j� ���SCE�{�6���� ��{��S����6���#�*���EP��E��f��!��]�3N�S�v��K �}���v ��,�)����V@u�(B�$���z�XN u���V���Z H%(Kz#��9�Mz�pDp�<��T:1��~�y$�#�rE��w�J��W��f@��S7(�[P����������6)x�n���4���i�EKP�jy��E}���LZ��=��Y�7���� �V�����4����9�8�aG��G���2i
�LMI�4;�!);R[)�������V�\-���
�%����k )��W��%D��1'���Gq��T0�*����^��'h���`E�;.���Y�����4��mV|vu��[�����qp���~$�dv�1����$�$�P��;��f����q�����Y��Z���I#�6������G�Q�r)�\#�\���#�)8�����!�|[;� ]��O:Q���pW�h
_���u��@� ���7�A��F�4p���I3���z}�^�S�>�s�&��m68qM�k������O�@|bI(#lb��&��H� ���T�e�<'�
�QI�jm����[�3�0����Rz�1"J�E���z1sW��T���<��n~������u��m����Ps��F�EM�W�� ��(�$�s��R?��5;�������tQ�m<#�[�7���:s���4���-���S��b,$c#������G�j�d��q����P�N��#����5�j��w��!�z��T�U��`V��Q�I�^����"�zm�I{��=;�\�:h���p�\��`lA��P8�S(���9����/O��E
��9/�]���n��me.S�*X!NB�a"���l 3?�`�'�W%�L���������+� �d��4C<'�C�����j {�+g����
j� @�5 ���S���\��� ���
��x��n�ap]3��(�G��w�''oO^]���g��
��
C+��B�}+���Ta���o0G`s�LK�����L��������?�����n���!��
�9<>>��.�z�6�"�^FQp���(X��G�c����W�Em����,�oP���;���o3�p����/�I��-��-�j<�u ��@��nDtV��`-.�<���
�OW�������2��}+���{�?�����t���f�����{������{��7����F
V�Y���a{H/����^M����j;�k���5'�h���J�P���������%����^�[g�v0!�3�f5�������
��
���XO����'��&5q�����kZ�v����=���G
�96� ��2�m��5����w�.N/O��K??yuv����5�f��[<#CP:5%8�����d��}f�MTy��j:�4bP����J�G�;�9Aj�?��{u4��\����HEE��<}���N�c7 wPv���VRn{��f���=M��}����O��e�T��(�����j>�>�<�2�����S�v��%�����5��DK�e����p�Z�a�2e�eLW$4��ax8�Ih�-���)�c��y<i?jz��xe�o9g�Z~�����8�3
��.������U���������+��;��~�3��+�M��q!b��m�I;�^?�v����D���(f�����M��
o���GnEp�z���_�0����0N��
��U�;���f��<�O���I8L5�{��8�����Va$���i���w��K�@�G��"a�'8u'�
�y����*:2��c�pu{��I�/����m�����XM'Z��}k�,}6�Fn�o&��y�{$��<��k7X�.b��vR���z�<r�&�I�R�0j�N�y�r�^MI�7�?|NfMc��<s���:�T�VO��:R��|��{w����t}�_IC&`����r>{,W��x���`�l���/��%���Bu�@B=��2oY���ws����� !S0���"f�z�I����w���*\D��%`��Q�i:���LT��n����!W���e���z�G���z�q�
�7hzg�����d5��v���[1��9��=���#���S6���]����������l����s�U�v�J�������Gn�07 N��mC�LoC�Y�.8{2���i�,��j������%"��2nc�������)���k�8�d�D�~l������sI��������w���h�w[nEnI�V�5���g���������f�Y���������G�n���p��O��Yl�{l]��S��&��`�5�tR���CC���S��4���p����Z2w� ����$��:p�6�$OjVs�&Pm���Y�6t!e��
[-�k6�S��wGE�L�j��&?h�y��>��2x��rM>+3>�� 6�8���B� o�'>}uy�����k m7J��~�ka.����V�^���le���a���d9H�E���5��0��~��jP��-U�b�xG�s%P!^���d��([��W��r�6�z� �s�e
K#u���Y�Q�|��]jf�� �9���������p-�����Y9��F` �{�
1>�����N�}y.mP����-���j��a�V|�5���YOw�6
m^=��Lr'Ux������?���!��w�\�u�E��������i��]�[��""D�o����
/����UPD��q;������Y�WQ��M��=�0�r�U�������\���56�pth&zf��ytA8�p�����`O#t�Q!�f-�'�{��<��3�h���$��7�4 _�����l�T�����5"|X�UL
US���+p����w[{�_(���k����|w`� ����ps&}�(�p�����"S�L����p��/< ���{o`p[ehJ�q�-P�#���t������j��������o��������x���|��J��7�g�� }��n��U�����6=�4%����?lI!E�w�?*�w�:�3����}Q�T� �vW����{��=����v�_��D�yk�F]#u�P4c9�eM_4�o
�!@&���~A��W.F@UF����;�����;v�q���J� ��8������s��:��+�����j;��p�p��"�b5���$=���������f�/��;4)������������HL���PEQi:������z��d@!�Q�@��������wU�p|�d&�>f��l����;4�*�M
�E�v�mo��^�=r{-:�T���
�����O��G%k��_�B\N�_�F�{-A�����I��{J;O�
�,���������W��%���bi��}4����h(�
�A��o�s���^��`�)�����~M���9���lG������rd�)
A-��������u�j�"���wG-6��x������P)[�R�m"#����2�S���'��*�q]m�w� 3k�b��T��>���%�����o�CU����������p�`>w�aN�nw
G��\,m�0��"���q�:���Ej������V�9�1;��D�����U�0=_��^h���r>�w��*�
8VA�� �r�>J�/F9��gm$49��4�Oa��g����Xov[���V��`b��C.]
.����=(aB��==�����j�o�TV���&�v����B��m���`�����A?X��^�hj�X���h�E ���K|��`��M��w��b��C|�����k���X�K1�����n0�b��������^�Net� �+q�t�����yc�o7��������M�����&c�D�f��]����J'Z"����'�t�CS�K�����r��������{�::OR����8A9l���{}����v�s�5���h�������IyE5�B�����Nw��k}�Q����� J���U���r��G�9�cN��.@�Q;R�g����w�=���h�PI��4��p���kw�X�
�;�$�����&*�
�5�p���>��U7OW�
�g��|�b0n����:��M�J��T��7����f��i{no�X���KIV���s����9�8������^��H)�"$�
I*�q{����6������ �����2����B��=}������'>�n��7��|r9y&/Q!�YK�e��2&j�8i�-�ZW�2������E�_��v�y�2���Z�����O�h��/�P|�z�YS��h0������I����T��6
����]J���� ����x%!`��TU�h�3Z��qA�1�]�*��f������%c����undV�g�y�kIp��6.%�����O��''��?�M��&��zVTB�?�`z�z8��� �Z��:������?��G���6G$���v>�5E��>����������9�����O�����RJ�T�3y�����9*�w�%��qZ�`m�{v��R���n0����}^Sf�;��������������g����/O��b��N�c���+�*~��������4��||nw�����/�����*}��z���������������� ha��V^��u%������U�y� 1�|h�E�#\K���^b���>�%�����4�����8W��P}�?2�;�����e�����#e��{��Xu���a�����m4Y����Y*��xS6�>/,9SQf-���>�T���G���'��<ysv>��20�7./jb��v@8E}%y�+U��������f��w�N�]���l����,Nxc]��9�Oc;��gP����*���]�;�F�|��6��D���l��U2��*�f�U�����FQ��:fEn����Z[������N��8���C{�{������oL�����X��W�������1zG��V�
�_��*+��l��}vS�0^���x����:~��s� ����~��T�9�������]4����y,�m���"
*`����5���`6�UuR��,
U�lYC2�����Kej���� {�����_��W;���r���z�o�|]ef~IZf^ar��yd!��j6�nW��8��o�Ze������2./�����u����-LgS��l���yxS i����<��,n
���UY.n�����lDeX�0O������w�k�(g<�n��e��]Men�����XN���"��P�b��K ��f�?�@`���`\U�6�)��$�l�QP>
�Qk�KU�a�|l�X�j��a��=�����N���-�eSKT+rDI-rEIz����nNf�{�s23k��P���
t�T�����
��UP����{������h\���n�����d@i�Sokl��F������4���R����8f�����!�g�c��Kl|"
�\�&+]�������*�[���#6Vm�X��
QD>���=L��0z��`� �y��F@Z����|��_x��~��;�q���<w<�,n�����q����;�d���RHq���qc��^�1��{��d�p�+���?&������L����Z�����L���Yo$l��}�a�����b����s
��=�
�Q�Wh����)�@��L����qwr�����b��E��k���K��5���r��|�p���s��J ����5��m;�/���r��Z��E��<�r���U����R��gcP*��i:���l7U��[�9Z����[��v�7�sA��'�O��Jbd��?��;�M�m����*_���R�����������% ����l��2��(�USY6)yc��^��b���bLTaii��G�Ku�K���I��K?��X�����������U��/���%���L�[��'L���u�4k���*�S����kK��/)+��&�M�l�~
���d`���~�j�I����y��m����5�Z7����QHV�c��� �����9Ggp�����U�mF�d����y�H�b�+��6�/�������4�1-f������~n��~�$4�Go��+I���'L7:�q�A/�vK���M���Nu6Q�7���h��~R7���(iw�\jBt-�i����Q���t�w��6(�6#@������8K���t^.��}-��~�x�+P'�����x��.?��X2rs���C�YV��*����T %�-������x�z���f�(I���^$���2�^�3����E~����8��e���]������ r�/WU{��I�F~�[$��<��lG����t|�����?�F��5�NrkU�����0%2�@���2��Ry&:t�L����4N�Hu6ew���5M��Z����ph���'{ga��aJ6��8��������<y��9{���}�+:��+�h�Aw�~����/_�7����rv�3=���@�a�A���w�� Y��~�}nqN�d��=?��������,8?��r{`������1�JS�����~ E�6(sN�}�����H�����'��=U�5A���}E��Q������=��.��(�]~�������mp��������~&��Q}Kk@�;O�ai��N�?MT� �����g�k�����������~
����>���;���S?��X����M�:�=�On��A����|
��f���,�Y������� m�"�Me�C�����\���0@�6��n3a��d�p������c�9T��~M�Z�v�AP�����~����~�9��ws�V�������Lx�a&��@im�����
"�b��f�3 5���U��>u��b���9����}��T���^:+��
a�T#��r����H�n;g�z�O����^��^s�������:��B&T~U�(���D�}����a7��y�\/�um��f�4Rz�ry|�������.�P@o��7��u������ �������3���[�����V��5:��P��Rc����%9����#M�K��������`5_/iG4} �X5SE�'�����x���Tmh�2&���Oz5�N�dl@Mg�R��
��k�
i���Bi JK��%(g��g
�������3N��i|�v�y��] �e�.�������
�h�Z�6'�}�V��`@��]�T�9���-��m(:4���}��2��mZ���a���@�^p�#�������uA���G���%<���WXIba+o��HQ��^?�c�^��4&�8�6��S�\�G�B��d����;��7���n�q������b����O-:���N����/n���-�`|����i�:�)�"�p����b�~b
����=5�F��LX����p�C�����)�����c7yRBCc���M�t%�����R�hZ����K��I�4���JdV�+'f��7l(��������'oO@���Y(�8�g9��X�M����*����-Cu|�����3n����]]:�:�kK���#*�D�1��G���������=�/��"A��o^ Y���(e���h������N������\iGP�%A��
�UT��=6�7�6������o�^,�%���+��3�!�r����~.���cS���c�V4-&8�h1[�<�S�"-<���Y��A����~��S�9 ���e&fk��f�������b����pF^��:i��O%#[����������OU�A�����&�?��O��V�v��\���K,�����[T��k7
�D�q�2�R��5g�b�L�M�}�)#�����_y��FM��������2����,�n����N��b���-|p��*Rp��|����=-���w?!F�h�v����3�!�l����)���)���2�V�� �d+�P$>V�O����7�b�,���d����� 9��X%�#��o+����"�}*�v�Q�dl�b���wo�5E�/����q �lAq�2=8�"Xw��j$=d����s�5�����QW%W �����]�n�sP3������:�]�r C���z�`�nn�1��K��(E-��G>D��F��K��i���PI�YA%����:�f�����YE?��&K�I�+���;��)~X�T�R��x��g�2�0���m��a#v8���A�H�g*9��)�*�}S��%�������s��Ww��cwg�,>�!�tn�%��q)Y c���f���
�F4���\���Z��JD4c1����/������O��dv��������(�+c�+�= 3�t/V��8� ,�K�i�),A��k�����[�b���SuU[�2����H���`�
|�;����f�~�$TVK%��*K:<�(wlO�`��_�[����^��e���BAh���d�Q�i��A������V��Y������7� j���>�:j����x�>Y�������)�T|��>Tc�s����O�n������e�M���w��z�a���`��J�,U�/U(B�?F�����5���q��j��G[t9��mp-��<P�6�|��g�*���-0V���S{nA�c��x%�������X����������#������ioT����Rn�r�em�g��a! ��u�����F�����]2�\�����f�S����M}`���=lU�7��J�*���c�N�#sQ:`�_��
a��i�I:lSh��N�0>��7�U���CE��}��H>^�lVZ����0f9Z��!�C��R!�3E���}�e�
��gn��j6����U�U�f��.(�A��
?-�(�K�?��n������+���,��Y��B�(0�~�?m�a+�����V"��..���t���iKx@�������98����LFyXx��XR���x {��9��e�����t���-Z�/N.�>L�~y���k��!�hf:5�4�w�wU��pS��r ����qg�A���Z��[5q��Q��\���6�����P����e�fF�S �hfU���C���Ud�K�_)�?]�Dk8���D�"��d�\��8kO@����#�c�9�����I��o�K�*$k������i���������6t3e�C�C:����>vSb7�t��m~�1�q]����f��)����6����\��x�x�
7��n���������|3��8�����r�����H�)G�����?�1��*�5�e9'�l�G��LLA��s>��;L��2#f�����M�����B4A����;S!}�]R�����h�k6���wGU�Vm��z�4t����I���0V�?�����I�&!��['W��i�N2jd���MH�4/*�S�R���P�4����f���>�hF�2(���M��}8���f���/�DQ����?��V:�{���j&�dc%K+Y�-hC^[�i�-]���WU�7�����b.�i���������M�WV�t^�x�Z�V�&?��`��{^���� �\3�1\^���
��`����l:�Y�V]#������a a]��u �r�"�������nW1��d92~�7G��'����e��/��
`g)�sUFY�����6����i��Zt+%\�������^"�������_���� ����mKT+:�'���r�-�$����YN���x<����h0��zX-���U�]��$@���..J24�Z������b�%j�>Y�<�@�Hu�
�Kg��������&
���g�����~e*���h|�����*^2F1u����A�9����~�DfC%�,�9��J�M_:-s���5���z1�D�����N-F&ZDT�J�>7����� �x��.��ZhfEh�����������st{�M`���f�f����,�
��Y�23d~o���w��*��������q����~�l���Z�����^m?Q��
N������K�����D���*�NQ�UV����o�%�Zs���
80[2s��U[�����0 "��c� �h{� r�bmj5�����K�����������g�'�/�/'��9??y99?��H��6��{�[�
�p�D�~.o�������#��2Y�!��_���P:�`�+������& 6YD>d�!���|?�,���qUW����3L��\
Ut��������CJ���jm����DQe4���n�
����vALU�����r���FcL!`���e�%<