diff options
Diffstat (limited to 'src/backend/executor/execQual.c')
-rw-r--r-- | src/backend/executor/execQual.c | 276 |
1 files changed, 159 insertions, 117 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 4c6f95a9a6f..b27e86122bc 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.157 2004/03/24 22:40:28 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.158 2004/04/01 21:28:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,6 +49,7 @@ #include "utils/array.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#include "utils/typcache.h" /* static function decls */ @@ -110,7 +111,7 @@ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate, static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalFieldSelect(GenericExprState *fstate, +static Datum ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalRelabelType(GenericExprState *exprstate, @@ -420,16 +421,25 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, *isDone = ExprSingleResult; /* - * get the slot we want + * Get the slot and attribute number we want + * + * The asserts check that references to system attributes only appear + * at the level of a relation scan; at higher levels, system attributes + * must be treated as ordinary variables (since we no longer have access + * to the original tuple). */ + attnum = variable->varattno; + switch (variable->varno) { case INNER: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; + Assert(attnum > 0); break; case OUTER: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; + Assert(attnum > 0); break; default: /* get the tuple from the relation being @@ -444,8 +454,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, heapTuple = slot->val; tuple_type = slot->ttc_tupleDescriptor; - attnum = variable->varattno; - /* * Some checks that are only applied for user attribute numbers * (bogus system attnums will be caught inside heap_getattr). @@ -481,38 +489,6 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid); } - /* - * If the attribute number is invalid, then we are supposed to return - * the entire tuple; we give back a whole slot so that callers know - * what the tuple looks like. - * - * XXX this is a horrid crock: since the pointer to the slot might live - * longer than the current evaluation context, we are forced to copy - * the tuple and slot into a long-lived context --- we use the - * econtext's per-query memory which should be safe enough. This - * represents a serious memory leak if many such tuples are processed - * in one command, however. We ought to redesign the representation - * of whole-tuple datums so that this is not necessary. - * - * We assume it's OK to point to the existing tupleDescriptor, rather - * than copy that too. - */ - if (attnum == InvalidAttrNumber) - { - MemoryContext oldContext; - TupleTableSlot *tempSlot; - HeapTuple tup; - - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - tempSlot = MakeTupleTableSlot(); - tup = heap_copytuple(heapTuple); - ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - ExecSetSlotDescriptor(tempSlot, tuple_type, false); - MemoryContextSwitchTo(oldContext); - *isNull = false; - return PointerGetDatum(tempSlot); - } - result = heap_getattr(heapTuple, /* tuple containing attribute */ attnum, /* attribute number of desired * attribute */ @@ -656,17 +632,23 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext, * GetAttributeByName * GetAttributeByNum * - * These are functions which return the value of the - * named attribute out of the tuple from the arg slot. User defined + * These functions return the value of the requested attribute + * out of the given tuple Datum. * C functions which take a tuple as an argument are expected - * to use this. Ex: overpaid(EMP) might call GetAttributeByNum(). + * to use these. Ex: overpaid(EMP) might call GetAttributeByNum(). + * Note: these are actually rather slow because they do a typcache + * lookup on each call. */ Datum -GetAttributeByNum(TupleTableSlot *slot, +GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool *isNull) { - Datum retval; + Datum result; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; if (!AttributeNumberIsValid(attrno)) elog(ERROR, "invalid attribute number %d", attrno); @@ -674,29 +656,43 @@ GetAttributeByNum(TupleTableSlot *slot, if (isNull == NULL) elog(ERROR, "a NULL isNull pointer was passed"); - if (TupIsNull(slot)) + if (tuple == NULL) { + /* Kinda bogus but compatible with old behavior... */ *isNull = true; return (Datum) 0; } - retval = heap_getattr(slot->val, + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + + /* + * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set + * all the fields in the struct just in case user tries to inspect + * system columns. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_data = tuple; + + result = heap_getattr(&tmptup, attrno, - slot->ttc_tupleDescriptor, + tupDesc, isNull); - if (*isNull) - return (Datum) 0; - - return retval; + return result; } Datum -GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) +GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull) { AttrNumber attrno; - TupleDesc tupdesc; - Datum retval; - int natts; + Datum result; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; int i; if (attname == NULL) @@ -705,21 +701,23 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) if (isNull == NULL) elog(ERROR, "a NULL isNull pointer was passed"); - if (TupIsNull(slot)) + if (tuple == NULL) { + /* Kinda bogus but compatible with old behavior... */ *isNull = true; return (Datum) 0; } - tupdesc = slot->ttc_tupleDescriptor; - natts = slot->val->t_data->t_natts; + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); attrno = InvalidAttrNumber; - for (i = 0; i < tupdesc->natts; i++) + for (i = 0; i < tupDesc->natts; i++) { - if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) + if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0) { - attrno = tupdesc->attrs[i]->attnum; + attrno = tupDesc->attrs[i]->attnum; break; } } @@ -727,14 +725,21 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) if (attrno == InvalidAttrNumber) elog(ERROR, "attribute \"%s\" does not exist", attname); - retval = heap_getattr(slot->val, + /* + * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set + * all the fields in the struct just in case user tries to inspect + * system columns. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_data = tuple; + + result = heap_getattr(&tmptup, attrno, - tupdesc, + tupDesc, isNull); - if (*isNull) - return (Datum) 0; - - return retval; + return result; } /* @@ -1133,14 +1138,14 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, Tuplestorestate *tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; + bool returnsTuple; FunctionCallInfoData fcinfo; ReturnSetInfo rsinfo; + HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; - TupleTableSlot *slot; bool direct_function_call; bool first_time = true; - bool returnsTuple = false; /* * Normally the passed expression tree will be a FuncExprState, since @@ -1216,6 +1221,9 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, funcrettype = exprType((Node *) funcexpr->expr); + returnsTuple = (funcrettype == RECORDOID || + get_typtype(funcrettype) == 'c'); + /* * Prepare a resultinfo node for communication. We always do this * even if not expecting a set result, so that we can pass @@ -1282,31 +1290,34 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, break; /* + * Can't do anything useful with NULL rowtype values. Currently + * we raise an error, but another alternative is to just ignore + * the result and "continue" to get another row. + */ + if (returnsTuple && fcinfo.isnull) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("function returning row cannot return null value"))); + + /* * If first time through, build tupdesc and tuplestore for * result */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - if (funcrettype == RECORDOID || - get_typtype(funcrettype) == 'c') + if (returnsTuple) { /* - * Composite type, so function should have returned a - * TupleTableSlot; use its descriptor + * Use the type info embedded in the rowtype Datum to + * look up the needed tupdesc. Make a copy for the query. */ - slot = (TupleTableSlot *) DatumGetPointer(result); - if (fcinfo.isnull || !slot) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("function returning row cannot return null value"))); - if (!IsA(slot, TupleTableSlot) || - !slot->ttc_tupleDescriptor) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("function returning row did not return a valid tuple slot"))); - tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor); - returnsTuple = true; + HeapTupleHeader td; + + td = DatumGetHeapTupleHeader(result); + tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td), + HeapTupleHeaderGetTypMod(td)); + tupdesc = CreateTupleDescCopy(tupdesc); } else { @@ -1319,8 +1330,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, "column", funcrettype, -1, - 0, - false); + 0); } tupstore = tuplestore_begin_heap(true, false, work_mem); MemoryContextSwitchTo(oldcontext); @@ -1333,15 +1343,17 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, */ if (returnsTuple) { - slot = (TupleTableSlot *) DatumGetPointer(result); - if (fcinfo.isnull || - !slot || - !IsA(slot, TupleTableSlot) || - TupIsNull(slot)) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("function returning row cannot return null value"))); - tuple = slot->val; + HeapTupleHeader td; + + td = DatumGetHeapTupleHeader(result); + + /* + * tuplestore_puttuple needs a HeapTuple not a bare + * HeapTupleHeader, but it doesn't need all the fields. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(td); + tmptup.t_data = td; + tuple = &tmptup; } else { @@ -2415,26 +2427,62 @@ ExecEvalCoerceToDomainValue(ExprState *exprstate, * ---------------------------------------------------------------- */ static Datum -ExecEvalFieldSelect(GenericExprState *fstate, +ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr; Datum result; - TupleTableSlot *resSlot; + Datum tupDatum; + HeapTupleHeader tuple; + Oid tupType; + int32 tupTypmod; + TupleDesc tupDesc; + HeapTupleData tmptup; - result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); + tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); /* this test covers the isDone exception too: */ if (*isNull) - return result; + return tupDatum; + + tuple = DatumGetHeapTupleHeader(tupDatum); + + tupType = HeapTupleHeaderGetTypeId(tuple); + tupTypmod = HeapTupleHeaderGetTypMod(tuple); + + /* Lookup tupdesc if first time through or if type changes */ + tupDesc = fstate->argdesc; + if (tupDesc == NULL || + tupType != tupDesc->tdtypeid || + tupTypmod != tupDesc->tdtypmod) + { + MemoryContext oldcontext; + + tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + /* Copy the tupdesc into query storage for safety */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + tupDesc = CreateTupleDescCopy(tupDesc); + if (fstate->argdesc) + FreeTupleDesc(fstate->argdesc); + fstate->argdesc = tupDesc; + MemoryContextSwitchTo(oldcontext); + } - resSlot = (TupleTableSlot *) DatumGetPointer(result); - Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot)); - result = heap_getattr(resSlot->val, + /* + * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set + * all the fields in the struct just in case user tries to inspect + * system columns. + */ + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_data = tuple; + + result = heap_getattr(&tmptup, fselect->fieldnum, - resSlot->ttc_tupleDescriptor, + tupDesc, isNull); return result; } @@ -2703,11 +2751,12 @@ ExecInitExpr(Expr *node, PlanState *parent) case T_FieldSelect: { FieldSelect *fselect = (FieldSelect *) node; - GenericExprState *gstate = makeNode(GenericExprState); + FieldSelectState *fstate = makeNode(FieldSelectState); - gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect; - gstate->arg = ExecInitExpr(fselect->arg, parent); - state = (ExprState *) gstate; + fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect; + fstate->arg = ExecInitExpr(fselect->arg, parent); + fstate->argdesc = NULL; + state = (ExprState *) fstate; } break; case T_RelabelType: @@ -3088,8 +3137,6 @@ ExecTargetList(List *targetlist, List *tl; bool isNull; bool haveDoneSets; - static struct tupleDesc NullTupleDesc; /* we assume this inits to - * zeroes */ /* * debugging stuff @@ -3106,13 +3153,8 @@ ExecTargetList(List *targetlist, /* * There used to be some klugy and demonstrably broken code here that * special-cased the situation where targetlist == NIL. Now we just - * fall through and return an empty-but-valid tuple. We do, however, - * have to cope with the possibility that targettype is NULL --- - * heap_formtuple won't like that, so pass a dummy descriptor with - * natts = 0 to deal with it. + * fall through and return an empty-but-valid tuple. */ - if (targettype == NULL) - targettype = &NullTupleDesc; /* * evaluate all the expressions in the target list @@ -3285,8 +3327,8 @@ ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) /* * store the tuple in the projection slot and return the slot. */ - return ExecStoreTuple(newTuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* tuple has no buffer */ + return ExecStoreTuple(newTuple, /* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* tuple has no buffer */ true); } |