X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Fexecutor%2FexecQual.c;h=8c917c8418978b8406557192c1262348d56507a2;hb=fdf5a5efb7b28c13085fe7313658de8d7b9914f6;hp=69d28e78a4b8336f8ec0f11b66a62b50d844d6a4;hpb=a9545b3aef0d41fdb84bc6a30fa2e563020acad2;p=postgresql diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 69d28e78a4..8c917c8418 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.220 2007/06/11 22:22:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.224 2007/11/15 21:14:34 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -65,9 +65,11 @@ static Datum ExecEvalAggref(AggrefExprState *aggref, static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext, @@ -119,8 +121,8 @@ static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalXml(XmlExprState * xmlExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -145,14 +147,14 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, - ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, - ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalCoerceViaIO(CoerceViaIOState * iostate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState * astate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); + bool *isNull, ExprDoneCond *isDone); /* ---------------------------------------------------------------- @@ -438,7 +440,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, * * Note: ExecEvalVar is executed only the first time through in a given plan; * it changes the ExprState's function pointer to pass control directly to - * ExecEvalScalarVar or ExecEvalWholeRowVar after making one-time checks. + * ExecEvalScalarVar, ExecEvalWholeRowVar, or ExecEvalWholeRowSlow after + * making one-time checks. * ---------------------------------------------------------------- */ static Datum @@ -486,21 +489,21 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, * Scalar variable case. * * If it's a user attribute, check validity (bogus system attnums will - * be caught inside slot_getattr). What we have to check for here - * is the possibility of an attribute having been changed in type - * since the plan tree was created. Ideally the plan would get - * invalidated and not re-used, but until that day arrives, we need - * defenses. Fortunately it's sufficient to check once on the first - * time through. + * be caught inside slot_getattr). What we have to check for here is + * the possibility of an attribute having been changed in type since + * the plan tree was created. Ideally the plan would get invalidated + * and not re-used, but until that day arrives, we need defenses. + * Fortunately it's sufficient to check once on the first time + * through. * * Note: we allow a reference to a dropped attribute. slot_getattr * will force a NULL result in such cases. * * Note: ideally we'd check typmod as well as typid, but that seems - * impractical at the moment: in many cases the tupdesc will have - * been generated by ExecTypeFromTL(), and that can't guarantee to - * generate an accurate typmod in all cases, because some expression - * node types don't carry typmod. + * impractical at the moment: in many cases the tupdesc will have been + * generated by ExecTypeFromTL(), and that can't guarantee to generate + * an accurate typmod in all cases, because some expression node types + * don't carry typmod. */ if (attnum > 0) { @@ -519,9 +522,9 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, if (variable->vartype != attr->atttypid) ereport(ERROR, (errmsg("attribute %d has wrong type", attnum), - errdetail("Table has type %s, but query expects %s.", - format_type_be(attr->atttypid), - format_type_be(variable->vartype)))); + errdetail("Table has type %s, but query expects %s.", + format_type_be(attr->atttypid), + format_type_be(variable->vartype)))); } } @@ -544,6 +547,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, * the actual tuple type is compatible with it. */ TupleDesc slot_tupdesc = slot->tts_tupleDescriptor; + bool needslow = false; if (variable->vartype == RECORDOID) { @@ -561,16 +565,26 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, * Also, we can ignore type mismatch on columns that are dropped * in the destination type, so long as the physical storage * matches. This is helpful in some cases involving out-of-date - * cached plans. + * cached plans. Also, we have to allow the case that the slot + * has more columns than the Var's type, because we might be + * looking at the output of a subplan that includes resjunk + * columns. (XXX it would be nice to verify that the extra + * columns are all marked resjunk, but we haven't got access to + * the subplan targetlist here...) Resjunk columns should always + * be at the end of a targetlist, so it's sufficient to ignore + * them here; but we need to use ExecEvalWholeRowSlow to get rid + * of them in the eventual output tuples. */ var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); - if (var_tupdesc->natts != slot_tupdesc->natts) + if (var_tupdesc->natts > slot_tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail("Table row contains %d attributes, but query expects %d.", slot_tupdesc->natts, var_tupdesc->natts))); + else if (var_tupdesc->natts < slot_tupdesc->natts) + needslow = true; for (i = 0; i < var_tupdesc->natts; i++) { @@ -578,7 +592,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, Form_pg_attribute sattr = slot_tupdesc->attrs[i]; if (vattr->atttypid == sattr->atttypid) - continue; /* no worries */ + continue; /* no worries */ if (!vattr->attisdropped) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), @@ -601,7 +615,10 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, } /* Skip the checking on future executions of node */ - exprstate->evalfunc = ExecEvalWholeRowVar; + if (needslow) + exprstate->evalfunc = ExecEvalWholeRowSlow; + else + exprstate->evalfunc = ExecEvalWholeRowVar; /* Fetch the value */ return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone); @@ -698,6 +715,60 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, return PointerGetDatum(dtuple); } +/* ---------------------------------------------------------------- + * ExecEvalWholeRowSlow + * + * Returns a Datum for a whole-row variable, in the "slow" case where + * we can't just copy the subplan's output. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + Var *variable = (Var *) exprstate->expr; + TupleTableSlot *slot = econtext->ecxt_scantuple; + HeapTuple tuple; + TupleDesc var_tupdesc; + HeapTupleHeader dtuple; + + if (isDone) + *isDone = ExprSingleResult; + *isNull = false; + + /* + * Currently, the only case handled here is stripping of trailing resjunk + * fields, which we do in a slightly chintzy way by just adjusting the + * tuple's natts header field. Possibly there will someday be a need for + * more-extensive rearrangements, in which case it'd be worth + * disassembling and reassembling the tuple (perhaps use a JunkFilter for + * that?) + */ + Assert(variable->vartype != RECORDOID); + var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); + + tuple = ExecFetchSlotTuple(slot); + + /* + * We have to make a copy of the tuple so we can safely insert the Datum + * overhead fields, which are not set in on-disk tuples; not to mention + * fooling with its natts field. + */ + dtuple = (HeapTupleHeader) palloc(tuple->t_len); + memcpy((char *) dtuple, (char *) tuple->t_data, tuple->t_len); + + HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); + HeapTupleHeaderSetTypeId(dtuple, variable->vartype); + HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); + + Assert(HeapTupleHeaderGetNatts(dtuple) >= var_tupdesc->natts); + HeapTupleHeaderSetNatts(dtuple, var_tupdesc->natts); + + ReleaseTupleDesc(var_tupdesc); + + return PointerGetDatum(dtuple); +} + /* ---------------------------------------------------------------- * ExecEvalConst * @@ -2506,9 +2577,9 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, /* * If all items were null or empty arrays, return an empty array; - * otherwise, if some were and some weren't, raise error. (Note: - * we must special-case this somehow to avoid trying to generate - * a 1-D array formed from empty arrays. It's not ideal...) + * otherwise, if some were and some weren't, raise error. (Note: we + * must special-case this somehow to avoid trying to generate a 1-D + * array formed from empty arrays. It's not ideal...) */ if (haveempty) { @@ -2773,17 +2844,17 @@ ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext, * ---------------------------------------------------------------- */ static Datum -ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, +ExecEvalXml(XmlExprState * xmlExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { - XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr; - text *result; - StringInfoData buf; - Datum value; - bool isnull; - ListCell *arg; + XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr; + text *result; + StringInfoData buf; + Datum value; + bool isnull; + ListCell *arg; ListCell *narg; - int i; + int i; if (isDone) *isDone = ExprSingleResult; @@ -2793,11 +2864,11 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, { case IS_XMLCONCAT: { - List *values = NIL; + List *values = NIL; foreach(arg, xmlExpr->args) { - ExprState *e = (ExprState *) lfirst(arg); + ExprState *e = (ExprState *) lfirst(arg); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (!isnull) @@ -2817,8 +2888,8 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, i = 0; forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names) { - ExprState *e = (ExprState *) lfirst(arg); - char *argname = strVal(lfirst(narg)); + ExprState *e = (ExprState *) lfirst(arg); + char *argname = strVal(lfirst(narg)); value = ExecEvalExpr(e, econtext, &isnull, NULL); if (!isnull) @@ -2841,8 +2912,8 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, case IS_XMLPARSE: { - ExprState *e; - text *data; + ExprState *e; + text *data; bool preserve_whitespace; /* arguments are known to be text, bool */ @@ -2870,8 +2941,8 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, case IS_XMLPI: { - ExprState *e; - text *arg; + ExprState *e; + text *arg; /* optional argument is known to be text */ Assert(list_length(xmlExpr->args) <= 1); @@ -2897,9 +2968,9 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, case IS_XMLROOT: { - ExprState *e; - xmltype *data; - text *version; + ExprState *e; + xmltype *data; + text *version; int standalone; /* arguments are known to be xml, text, int */ @@ -2932,7 +3003,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, case IS_XMLSERIALIZE: { - ExprState *e; + ExprState *e; /* argument type is known to be xml */ Assert(list_length(xmlExpr->args) == 1); @@ -2950,7 +3021,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, case IS_DOCUMENT: { - ExprState *e; + ExprState *e; /* optional argument is known to be xml */ Assert(list_length(xmlExpr->args) == 1); @@ -2972,7 +3043,7 @@ ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext, result = NULL; else { - int len = buf.len + VARHDRSZ; + int len = buf.len + VARHDRSZ; result = palloc(len); SET_VARSIZE(result, len); @@ -3360,9 +3431,9 @@ ExecEvalFieldSelect(FieldSelectState *fstate, /* Check for dropped column, and force a NULL result if so */ if (fieldnum <= 0 || - fieldnum > tupDesc->natts) /* should never happen */ - elog(ERROR, "attribute number %d exceeds number of columns %d", - fieldnum, tupDesc->natts); + fieldnum > tupDesc->natts) /* should never happen */ + elog(ERROR, "attribute number %d exceeds number of columns %d", + fieldnum, tupDesc->natts); attr = tupDesc->attrs[fieldnum - 1]; if (attr->attisdropped) { @@ -3516,7 +3587,7 @@ ExecEvalRelabelType(GenericExprState *exprstate, * ---------------------------------------------------------------- */ static Datum -ExecEvalCoerceViaIO(CoerceViaIOState *iostate, +ExecEvalCoerceViaIO(CoerceViaIOState * iostate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { @@ -3550,7 +3621,7 @@ ExecEvalCoerceViaIO(CoerceViaIOState *iostate, * ---------------------------------------------------------------- */ static Datum -ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, +ExecEvalArrayCoerceExpr(ArrayCoerceExprState * astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { @@ -3623,45 +3694,17 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, /* ---------------------------------------------------------------- * ExecEvalCurrentOfExpr * - * Normally, the planner will convert CURRENT OF into a TidScan qualification, - * but we have plain execQual support in case it doesn't. + * The planner must convert CURRENT OF into a TidScan qualification. + * So, we have to be able to do ExecInitExpr on a CurrentOfExpr, + * but we shouldn't ever actually execute it. * ---------------------------------------------------------------- */ static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { - CurrentOfExpr *cexpr = (CurrentOfExpr *) exprstate->expr; - bool result; - bool lisnull; - Oid tableoid; - ItemPointer tuple_tid; - ItemPointerData cursor_tid; - - if (isDone) - *isDone = ExprSingleResult; - *isNull = false; - - Assert(cexpr->cvarno != INNER); - Assert(cexpr->cvarno != OUTER); - Assert(!TupIsNull(econtext->ecxt_scantuple)); - /* Use slot_getattr to catch any possible mistakes */ - tableoid = DatumGetObjectId(slot_getattr(econtext->ecxt_scantuple, - TableOidAttributeNumber, - &lisnull)); - Assert(!lisnull); - tuple_tid = (ItemPointer) - DatumGetPointer(slot_getattr(econtext->ecxt_scantuple, - SelfItemPointerAttributeNumber, - &lisnull)); - Assert(!lisnull); - - if (execCurrentOf(cexpr, econtext, tableoid, &cursor_tid)) - result = ItemPointerEquals(&cursor_tid, tuple_tid); - else - result = false; - - return BoolGetDatum(result); + elog(ERROR, "CURRENT OF cannot be executed"); + return 0; /* keep compiler quiet */ } @@ -3777,7 +3820,7 @@ ExecInitExpr(Expr *node, PlanState *parent) if (naggs != aggstate->numaggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregate function calls cannot be nested"))); + errmsg("aggregate function calls cannot be nested"))); } else { @@ -3937,8 +3980,8 @@ ExecInitExpr(Expr *node, PlanState *parent) { CoerceViaIO *iocoerce = (CoerceViaIO *) node; CoerceViaIOState *iostate = makeNode(CoerceViaIOState); - Oid iofunc; - bool typisvarlena; + Oid iofunc; + bool typisvarlena; iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO; iostate->arg = ExecInitExpr(iocoerce->arg, parent); @@ -4088,7 +4131,7 @@ ExecInitExpr(Expr *node, PlanState *parent) * don't really care what type of NULL it is, so * always make an int4 NULL. */ - e = (Expr *) makeNullConst(INT4OID); + e = (Expr *) makeNullConst(INT4OID, -1); } estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); @@ -4225,11 +4268,11 @@ ExecInitExpr(Expr *node, PlanState *parent) break; case T_XmlExpr: { - XmlExpr *xexpr = (XmlExpr *) node; - XmlExprState *xstate = makeNode(XmlExprState); - List *outlist; - ListCell *arg; - int i; + XmlExpr *xexpr = (XmlExpr *) node; + XmlExprState *xstate = makeNode(XmlExprState); + List *outlist; + ListCell *arg; + int i; xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml; xstate->named_outfuncs = (FmgrInfo *) @@ -4238,8 +4281,8 @@ ExecInitExpr(Expr *node, PlanState *parent) i = 0; foreach(arg, xexpr->named_args) { - Expr *e = (Expr *) lfirst(arg); - ExprState *estate; + Expr *e = (Expr *) lfirst(arg); + ExprState *estate; Oid typOutFunc; bool typIsVarlena; @@ -4256,8 +4299,8 @@ ExecInitExpr(Expr *node, PlanState *parent) outlist = NIL; foreach(arg, xexpr->args) { - Expr *e = (Expr *) lfirst(arg); - ExprState *estate; + Expr *e = (Expr *) lfirst(arg); + ExprState *estate; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate);