*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.133 2003/06/27 00:33:25 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.137 2003/07/30 19:02:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
bool *isNull);
+static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+ ExprContext *econtext, bool *isNull);
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
foreach(elt, astate->refupperindexpr)
{
if (i >= MAXDIM)
- elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
- MAXDIM);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions exceeds the maximum allowed, %d",
+ MAXDIM)));
upper.indx[i++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
econtext,
foreach(elt, astate->reflowerindexpr)
{
if (j >= MAXDIM)
- elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
- MAXDIM);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions exceeds the maximum allowed, %d",
+ MAXDIM)));
lower.indx[j++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
econtext,
return PointerGetDatum(array_source);
}
}
+ /* this can't happen unless parser messed up */
if (i != j)
- elog(ERROR,
- "ExecEvalArrayRef: upper and lower indices mismatch");
+ elog(ERROR, "upper and lower index lists are not same length");
lIndex = lower.indx;
}
else
ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull)
{
if (econtext->ecxt_aggvalues == NULL) /* safety check */
- elog(ERROR, "ExecEvalAggref: no aggregates in this expression context");
+ elog(ERROR, "no aggregates in this expression context");
*isNull = econtext->ecxt_aggnulls[aggref->aggno];
return econtext->ecxt_aggvalues[aggref->aggno];
matchFound = true;
break;
default:
- elog(ERROR, "ExecEvalParam: invalid paramkind %d",
+ elog(ERROR, "unrecognized paramkind: %d",
thisParamKind);
}
}
if (!matchFound)
{
if (thisParamKind == PARAM_NAMED)
- elog(ERROR, "ExecEvalParam: Unknown value for parameter %s",
- thisParamName);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("no value found for parameter \"%s\"",
+ thisParamName)));
else
- elog(ERROR, "ExecEvalParam: Unknown value for parameter %d",
- thisParamId);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("no value found for parameter %d",
+ thisParamId)));
}
*isNull = paramList->isnull;
Datum retval;
if (!AttributeNumberIsValid(attrno))
- elog(ERROR, "GetAttributeByNum: Invalid attribute number");
-
- if (!AttrNumberIsForUserDefinedAttr(attrno))
- elog(ERROR, "GetAttributeByNum: cannot access system attributes here");
+ elog(ERROR, "invalid attribute number %d", attrno);
if (isNull == (bool *) NULL)
- elog(ERROR, "GetAttributeByNum: a NULL isNull flag was passed");
+ elog(ERROR, "a NULL isNull pointer was passed");
if (TupIsNull(slot))
{
int i;
if (attname == NULL)
- elog(ERROR, "GetAttributeByName: Invalid attribute name");
+ elog(ERROR, "invalid attribute name");
if (isNull == (bool *) NULL)
- elog(ERROR, "GetAttributeByName: a NULL isNull flag was passed");
+ elog(ERROR, "a NULL isNull pointer was passed");
if (TupIsNull(slot))
{
}
if (attrno == InvalidAttrNumber)
- elog(ERROR, "GetAttributeByName: attribute %s not found", attname);
+ elog(ERROR, "attribute \"%s\" does not exist", attname);
retval = heap_getattr(slot->val,
attrno,
/* Safety check (should never fail, as parser should check sooner) */
if (length(fcache->args) > FUNC_MAX_ARGS)
- elog(ERROR, "init_fcache: too many arguments");
+ elog(ERROR, "too many arguments");
/* Set up the primary fmgr lookup information */
fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
* it.
*/
if (argIsDone != ExprSingleResult)
- elog(ERROR, "Functions and operators can take only one set argument");
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("functions and operators can take at most one set argument")));
argIsDone = thisArgIsDone;
}
i++;
if (isDone)
*isDone = ExprEndResult;
else
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
return (Datum) 0;
}
hasSetArg = (argDone != ExprSingleResult);
* to accept one.
*/
if (isDone == NULL)
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
/*
* This loop handles the situation where we have both a set
argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
/* We don't allow sets in the arguments of the table function */
if (argDone != ExprSingleResult)
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
/*
* If function is strict, and there are any NULL arguments, skip
* TupleTableSlot; use its descriptor
*/
slot = (TupleTableSlot *) DatumGetPointer(result);
- if (fcinfo.isnull ||
- !slot ||
- !IsA(slot, TupleTableSlot) ||
+ if (fcinfo.isnull || !slot)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("function returning tuple cannot return NULL")));
+ if (!IsA(slot, TupleTableSlot) ||
!slot->ttc_tupleDescriptor)
- elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("function returning tuple did not return a valid tuple slot")));
tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
returnsTuple = true;
}
!slot ||
!IsA(slot, TupleTableSlot) ||
TupIsNull(slot))
- elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("function returning tuple cannot return NULL")));
tuple = slot->val;
}
else
{
/* check we're on the same page as the function author */
if (!first_time || rsinfo.isDone != ExprSingleResult)
- elog(ERROR, "ExecMakeTableFunctionResult: Materialize-mode protocol not followed");
+ ereport(ERROR,
+ (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+ errmsg("table-function protocol for materialize mode was not followed")));
/* Done evaluating the set result */
break;
}
else
- elog(ERROR, "ExecMakeTableFunctionResult: unknown returnMode %d",
- (int) rsinfo.returnMode);
+ ereport(ERROR,
+ (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+ errmsg("unrecognized table-function returnMode: %d",
+ (int) rsinfo.returnMode)));
first_time = false;
}
/* ----------------------------------------------------------------
* ExecEvalFunc
* ExecEvalOper
- * ExecEvalDistinct
*
* Evaluate the functional result of a list of arguments by calling the
* function manager.
fcinfo.flinfo = &(fcache->func);
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
if (argDone != ExprSingleResult)
- elog(ERROR, "IS DISTINCT FROM does not support set arguments");
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("IS DISTINCT FROM does not support set arguments")));
Assert(fcinfo.nargs == 2);
if (fcinfo.argnull[0] && fcinfo.argnull[1])
return result;
}
+/*
+ * ExecEvalScalarArrayOp
+ *
+ * Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean,
+ * and we combine the results across all array elements using OR and AND
+ * (for ANY and ALL respectively). Of course we short-circuit as soon as
+ * the result is known.
+ */
+static Datum
+ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
+ ExprContext *econtext, bool *isNull)
+{
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr;
+ bool useOr = opexpr->useOr;
+ ArrayType *arr;
+ int nitems;
+ Datum result;
+ bool resultnull;
+ FunctionCallInfoData fcinfo;
+ ExprDoneCond argDone;
+ int i;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char *s;
+
+ /*
+ * Initialize function cache if first time through
+ */
+ if (sstate->fxprstate.func.fn_oid == InvalidOid)
+ {
+ init_fcache(opexpr->opfuncid, &sstate->fxprstate,
+ econtext->ecxt_per_query_memory);
+ Assert(!sstate->fxprstate.func.fn_retset);
+ }
+
+ /* Need to prep callinfo structure */
+ MemSet(&fcinfo, 0, sizeof(fcinfo));
+ fcinfo.flinfo = &(sstate->fxprstate.func);
+ argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext);
+ if (argDone != ExprSingleResult)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("op ANY/ALL (array) does not support set arguments")));
+ Assert(fcinfo.nargs == 2);
+
+ /*
+ * If the array is NULL then we return NULL --- it's not very meaningful
+ * to do anything else, even if the operator isn't strict.
+ */
+ if (fcinfo.argnull[1])
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
+ /* Else okay to fetch and detoast the array */
+ arr = DatumGetArrayTypeP(fcinfo.arg[1]);
+
+ /*
+ * If the array is empty, we return either FALSE or TRUE per the useOr
+ * flag. This is correct even if the scalar is NULL; since we would
+ * evaluate the operator zero times, it matters not whether it would
+ * want to return NULL.
+ */
+ nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+ if (nitems <= 0)
+ return BoolGetDatum(!useOr);
+ /*
+ * If the scalar is NULL, and the function is strict, return NULL.
+ * This is just to avoid having to test for strictness inside the
+ * loop. (XXX but if arrays could have null elements, we'd need a
+ * test anyway.)
+ */
+ if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
+
+ /*
+ * We arrange to look up info about the element type only
+ * once per series of calls, assuming the element type doesn't change
+ * underneath us.
+ */
+ if (sstate->element_type != ARR_ELEMTYPE(arr))
+ {
+ get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+ &sstate->typlen,
+ &sstate->typbyval,
+ &sstate->typalign);
+ sstate->element_type = ARR_ELEMTYPE(arr);
+ }
+ typlen = sstate->typlen;
+ typbyval = sstate->typbyval;
+ typalign = sstate->typalign;
+
+ result = BoolGetDatum(!useOr);
+ resultnull = false;
+
+ /* Loop over the array elements */
+ s = (char *) ARR_DATA_PTR(arr);
+ for (i = 0; i < nitems; i++)
+ {
+ Datum elt;
+ Datum thisresult;
+
+ /* Get array element */
+ elt = fetch_att(s, typbyval, typlen);
+
+ s = att_addlength(s, typlen, PointerGetDatum(s));
+ s = (char *) att_align(s, typalign);
+
+ /* Call comparison function */
+ fcinfo.arg[1] = elt;
+ fcinfo.argnull[1] = false;
+ fcinfo.isnull = false;
+ thisresult = FunctionCallInvoke(&fcinfo);
+
+ /* Combine results per OR or AND semantics */
+ if (fcinfo.isnull)
+ resultnull = true;
+ else if (useOr)
+ {
+ if (DatumGetBool(thisresult))
+ {
+ result = BoolGetDatum(true);
+ resultnull = false;
+ break; /* needn't look at any more elements */
+ }
+ }
+ else
+ {
+ if (!DatumGetBool(thisresult))
+ {
+ result = BoolGetDatum(false);
+ resultnull = false;
+ break; /* needn't look at any more elements */
+ }
+ }
+ }
+
+ *isNull = resultnull;
+ return result;
+}
+
/* ----------------------------------------------------------------
* ExecEvalNot
* ExecEvalOr
/* ----------------------------------------------------------------
* ExecEvalArray - ARRAY[] expressions
+ *
+ * NOTE: currently, if any input value is NULL then we return a NULL array,
+ * so the ARRAY[] construct can be considered strict. Eventually this will
+ * change; when it does, be sure to fix contain_nonstrict_functions().
* ----------------------------------------------------------------
*/
static Datum
dvalues[i++] = ExecEvalExpr(e, econtext, &eisnull, NULL);
if (eisnull)
- elog(ERROR, "Arrays cannot have NULL elements");
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
}
/* setup for 1-D array of the given length */
int i;
if (ndims <= 0 || ndims > MAXDIM)
- elog(ERROR, "Arrays cannot have more than %d dimensions", MAXDIM);
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions exceeds the maximum allowed, %d",
+ MAXDIM)));
/* loop through and get data area from each element */
foreach(element, astate->elements)
arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
if (eisnull)
- elog(ERROR, "Arrays cannot have NULL elements");
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
array = DatumGetArrayTypeP(arraydatum);
else
{
/* Check other sub-arrays are compatible */
- if (elem_ndims != ARR_NDIM(array))
- elog(ERROR, "Multidimensional arrays must have array "
- "expressions with matching number of dimensions");
-
- if (memcmp(elem_dims, ARR_DIMS(array),
- elem_ndims * sizeof(int)) != 0)
- elog(ERROR, "Multidimensional arrays must have array "
- "expressions with matching dimensions");
-
- if (memcmp(elem_lbs, ARR_LBOUND(array),
+ if (elem_ndims != ARR_NDIM(array) ||
+ memcmp(elem_dims, ARR_DIMS(array),
+ elem_ndims * sizeof(int)) != 0 ||
+ memcmp(elem_lbs, ARR_LBOUND(array),
elem_ndims * sizeof(int)) != 0)
- elog(ERROR, "Multidimensional arrays must have array "
- "expressions with matching dimensions");
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("multidimensional arrays must have array "
+ "expressions with matching dimensions")));
}
elem_ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(elem_ndims);
fcinfo.flinfo = &(fcache->func);
argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
if (argDone != ExprSingleResult)
- elog(ERROR, "NULLIF does not support set arguments");
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("NULLIF does not support set arguments")));
Assert(fcinfo.nargs == 2);
/* if either argument is NULL they can't be equal */
else
return BoolGetDatum(true);
default:
- elog(ERROR, "ExecEvalNullTest: unexpected nulltesttype %d",
+ elog(ERROR, "unrecognized nulltesttype: %d",
(int) ntest->nulltesttype);
return (Datum) 0; /* keep compiler quiet */
}
else
return BoolGetDatum(true);
default:
- elog(ERROR, "ExecEvalBooleanTest: unexpected booltesttype %d",
+ elog(ERROR, "unrecognized booltesttype: %d",
(int) btest->booltesttype);
return (Datum) 0; /* keep compiler quiet */
}
{
case DOM_CONSTRAINT_NOTNULL:
if (*isNull)
- elog(ERROR, "Domain %s does not allow NULL values",
- format_type_be(ctest->resulttype));
+ ereport(ERROR,
+ (errcode(ERRCODE_NOT_NULL_VIOLATION),
+ errmsg("domain %s does not allow NULL values",
+ format_type_be(ctest->resulttype))));
break;
case DOM_CONSTRAINT_CHECK:
{
if (!conIsNull &&
!DatumGetBool(conResult))
- elog(ERROR, "ExecEvalCoerceToDomain: Domain %s constraint %s failed",
- format_type_be(ctest->resulttype), con->name);
-
+ ereport(ERROR,
+ (errcode(ERRCODE_CHECK_VIOLATION),
+ errmsg("value for domain %s violates CHECK constraint \"%s\"",
+ format_type_be(ctest->resulttype),
+ con->name)));
econtext->domainValue_datum = save_datum;
econtext->domainValue_isNull = save_isNull;
break;
}
default:
- elog(ERROR, "ExecEvalCoerceToDomain: Constraint type unknown");
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) con->constrainttype);
break;
}
}
* *isDone: set to indicator of set-result status
*
* A caller that can only accept a singleton (non-set) result should pass
- * NULL for isDone; if the expression computes a set result then an elog()
- * error will be reported. If the caller does pass an isDone pointer then
- * *isDone is set to one of these three states:
+ * NULL for isDone; if the expression computes a set result then an error
+ * will be reported via ereport. If the caller does pass an isDone pointer
+ * then *isDone is set to one of these three states:
* ExprSingleResult singleton result (not a set)
* ExprMultipleResult return value is one element of a set
* ExprEndResult there are no more elements in the set
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
isNull);
break;
+ case T_ScalarArrayOpExpr:
+ retDatum = ExecEvalScalarArrayOp((ScalarArrayOpExprState *) expression,
+ econtext, isNull);
+ break;
case T_BoolExpr:
{
BoolExprState *state = (BoolExprState *) expression;
retDatum = ExecEvalNot(state, econtext, isNull);
break;
default:
- elog(ERROR, "ExecEvalExpr: unknown boolop %d",
- ((BoolExpr *) expr)->boolop);
+ elog(ERROR, "unrecognized boolop: %d",
+ (int) ((BoolExpr *) expr)->boolop);
retDatum = 0; /* keep compiler quiet */
break;
}
isNull);
break;
default:
- elog(ERROR, "ExecEvalExpr: unknown expression type %d",
- nodeTag(expression));
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(expression));
retDatum = 0; /* keep compiler quiet */
break;
}
/*
* Complain if the aggregate's argument contains any
* aggregates; nested agg functions are semantically
- * nonsensical. (This probably was caught earlier,
+ * nonsensical. (This should have been caught earlier,
* but we defend against it here anyway.)
*/
if (naggs != aggstate->numaggs)
- elog(ERROR, "Aggregate function calls may not be nested");
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ errmsg("aggregate function calls may not be nested")));
}
else
- elog(ERROR, "ExecInitExpr: Aggref not expected here");
+ {
+ /* planner messed up */
+ elog(ERROR, "aggref found in non-Agg plan node");
+ }
state = (ExprState *) astate;
}
break;
state = (ExprState *) fstate;
}
break;
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+ ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
+
+ sstate->fxprstate.args = (List *)
+ ExecInitExpr((Expr *) opexpr->args, parent);
+ sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
+ sstate->element_type = InvalidOid; /* ditto */
+ state = (ExprState *) sstate;
+ }
+ break;
case T_BoolExpr:
{
BoolExpr *boolexpr = (BoolExpr *) node;
SubPlanState *sstate = makeNode(SubPlanState);
if (!parent)
- elog(ERROR, "ExecInitExpr: SubPlan not expected here");
+ elog(ERROR, "SubPlan found with no parent plan");
/*
* Here we just add the SubPlanState nodes to
return (ExprState *) FastListValue(&outlist);
}
default:
- elog(ERROR, "ExecInitExpr: unknown expression type %d",
- nodeTag(node));
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
state = NULL; /* keep compiler quiet */
break;
}
SubPlanState *sstate = makeNode(SubPlanState);
if (!parent)
- elog(ERROR, "ExecInitExpr: SubPlan not expected here");
+ elog(ERROR, "SubPlan found with no parent plan");
/* The subplan's state will be initialized later */
sstate->sub_estate = NULL;
{
/* We have a set-valued expression in the tlist */
if (isDone == NULL)
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
if (itemIsDone[resind] == ExprMultipleResult)
{
/* we have undone sets in the tlist, set flag */