*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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);
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);
/* ----------------------------------------------------------------
*
* 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
* 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)
{
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))));
}
}
* the actual tuple type is compatible with it.
*/
TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
+ bool needslow = false;
if (variable->vartype == RECORDOID)
{
* 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++)
{
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),
}
/* 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);
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
*
/*
* 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)
{
* ----------------------------------------------------------------
*/
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;
{
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)
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)
case IS_XMLPARSE:
{
- ExprState *e;
- text *data;
+ ExprState *e;
+ text *data;
bool preserve_whitespace;
/* arguments are known to be text, bool */
case IS_XMLPI:
{
- ExprState *e;
- text *arg;
+ ExprState *e;
+ text *arg;
/* optional argument is known to be text */
Assert(list_length(xmlExpr->args) <= 1);
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 */
case IS_XMLSERIALIZE:
{
- ExprState *e;
+ ExprState *e;
/* argument type is known to be xml */
Assert(list_length(xmlExpr->args) == 1);
case IS_DOCUMENT:
{
- ExprState *e;
+ ExprState *e;
/* optional argument is known to be xml */
Assert(list_length(xmlExpr->args) == 1);
result = NULL;
else
{
- int len = buf.len + VARHDRSZ;
+ int len = buf.len + VARHDRSZ;
result = palloc(len);
SET_VARSIZE(result, len);
/* 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)
{
* ----------------------------------------------------------------
*/
static Datum
-ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
+ExecEvalCoerceViaIO(CoerceViaIOState * iostate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
* ----------------------------------------------------------------
*/
static Datum
-ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
+ExecEvalArrayCoerceExpr(ArrayCoerceExprState * astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
/* ----------------------------------------------------------------
* 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 */
}
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
{
{
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);
* 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);
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 *)
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;
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);