diff options
Diffstat (limited to 'src/backend/executor/execQual.c')
-rw-r--r-- | src/backend/executor/execQual.c | 238 |
1 files changed, 190 insertions, 48 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 8ada5e6faf2..e4362c38f58 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.163 2004/06/05 19:48:08 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.164 2004/06/09 19:08:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -118,6 +118,9 @@ static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate, static Datum ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalFieldStore(FieldStoreState *fstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -217,6 +220,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ArrayType *array_source; ArrayType *resultArray; bool isAssignment = (arrayRef->refassgnexpr != NULL); + bool eisnull; ListCell *l; int i = 0, j = 0; @@ -224,39 +228,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate, lower; int *lIndex; - /* Set default values for result flags: non-null, not a set result */ - *isNull = false; - if (isDone) - *isDone = ExprSingleResult; + array_source = (ArrayType *) + DatumGetPointer(ExecEvalExpr(astate->refexpr, + econtext, + isNull, + isDone)); - if (arrayRef->refexpr != NULL) + /* + * If refexpr yields NULL, and it's a fetch, then result is NULL. + * In the assignment case, we'll cons up something below. + */ + if (*isNull) { - array_source = (ArrayType *) - DatumGetPointer(ExecEvalExpr(astate->refexpr, - econtext, - isNull, - isDone)); - - /* - * If refexpr yields NULL, result is always NULL, for now anyway. - * (This means you cannot assign to an element or slice of an - * array that's NULL; it'll just stay NULL.) - */ - if (*isNull) + if (isDone && *isDone == ExprEndResult) + return (Datum) NULL; /* end of set result */ + if (!isAssignment) return (Datum) NULL; } - else - { - /* - * Empty refexpr indicates we are doing an INSERT into an array - * column. For now, we just take the refassgnexpr (which the - * parser will have ensured is an array value) and return it - * as-is, ignoring any subscripts that may have been supplied in - * the INSERT column list. This is a kluge, but it's not real - * clear what the semantics ought to be... - */ - array_source = NULL; - } foreach(l, astate->refupperindexpr) { @@ -270,14 +258,16 @@ ExecEvalArrayRef(ArrayRefExprState *astate, upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, - isNull, + &eisnull, NULL)); /* If any index expr yields NULL, result is NULL or source array */ - if (*isNull) + if (eisnull) { - if (!isAssignment || array_source == NULL) + if (!isAssignment) + { + *isNull = true; return (Datum) NULL; - *isNull = false; + } return PointerGetDatum(array_source); } } @@ -296,18 +286,20 @@ ExecEvalArrayRef(ArrayRefExprState *astate, lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, - isNull, + &eisnull, NULL)); /* * If any index expr yields NULL, result is NULL or source * array */ - if (*isNull) + if (eisnull) { - if (!isAssignment || array_source == NULL) + if (!isAssignment) + { + *isNull = true; return (Datum) NULL; - *isNull = false; + } return PointerGetDatum(array_source); } } @@ -321,26 +313,50 @@ ExecEvalArrayRef(ArrayRefExprState *astate, if (isAssignment) { - Datum sourceData = ExecEvalExpr(astate->refassgnexpr, - econtext, - isNull, - NULL); + Datum sourceData; + + /* + * Evaluate the value to be assigned into the array. + * + * XXX At some point we'll need to look into making the old value of + * the array element available via CaseTestExpr, as is done by + * ExecEvalFieldStore. This is not needed now but will be needed + * to support arrays of composite types; in an assignment to a field + * of an array member, the parser would generate a FieldStore that + * expects to fetch its input tuple via CaseTestExpr. + */ + sourceData = ExecEvalExpr(astate->refassgnexpr, + econtext, + &eisnull, + NULL); /* * For now, can't cope with inserting NULL into an array, so make * it a no-op per discussion above... */ + if (eisnull) + return PointerGetDatum(array_source); + + /* + * For an assignment, if all the subscripts and the input expression + * are non-null but the original array is null, then substitute an + * empty (zero-dimensional) array and proceed with the assignment. + * This only works for varlena arrays, though; for fixed-length + * array types we punt and return the null input array. + */ if (*isNull) { - if (array_source == NULL) - return (Datum) NULL; + if (astate->refattrlength > 0) /* fixed-length array? */ + return PointerGetDatum(array_source); + + array_source = construct_md_array(NULL, 0, NULL, NULL, + arrayRef->refelemtype, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign); *isNull = false; - return PointerGetDatum(array_source); } - if (array_source == NULL) - return sourceData; /* XXX do something else? */ - if (lIndex == NULL) resultArray = array_set(array_source, i, upper.indx, @@ -2539,6 +2555,120 @@ ExecEvalFieldSelect(FieldSelectState *fstate, } /* ---------------------------------------------------------------- + * ExecEvalFieldStore + * + * Evaluate a FieldStore node. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalFieldStore(FieldStoreState *fstate, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone) +{ + FieldStore *fstore = (FieldStore *) fstate->xprstate.expr; + HeapTuple tuple; + Datum tupDatum; + TupleDesc tupDesc; + Datum *values; + char *nulls; + Datum save_datum; + bool save_isNull; + ListCell *l1, + *l2; + + tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); + + if (isDone && *isDone == ExprEndResult) + return tupDatum; + + /* Lookup tupdesc if first time through or if type changes */ + tupDesc = fstate->argdesc; + if (tupDesc == NULL || + fstore->resulttype != tupDesc->tdtypeid) + { + MemoryContext oldcontext; + + tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1); + /* 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); + } + + /* Allocate workspace */ + values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); + nulls = (char *) palloc(tupDesc->natts * sizeof(char)); + + if (!*isNull) + { + /* + * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader. + * We set all the fields in the struct just in case. + */ + HeapTupleHeader tuphdr; + HeapTupleData tmptup; + + tuphdr = DatumGetHeapTupleHeader(tupDatum); + tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr); + ItemPointerSetInvalid(&(tmptup.t_self)); + tmptup.t_tableOid = InvalidOid; + tmptup.t_data = tuphdr; + + heap_deformtuple(&tmptup, tupDesc, values, nulls); + } + else + { + /* Convert null input tuple into an all-nulls row */ + memset(nulls, 'n', tupDesc->natts * sizeof(char)); + } + + /* Result is never null */ + *isNull = false; + + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; + + forboth(l1, fstate->newvals, l2, fstore->fieldnums) + { + ExprState *newval = (ExprState *) lfirst(l1); + AttrNumber fieldnum = lfirst_int(l2); + bool eisnull; + + Assert(fieldnum > 0 && fieldnum <= tupDesc->natts); + + /* + * Use the CaseTestExpr mechanism to pass down the old value of the + * field being replaced; this is useful in case we have a nested field + * update situation. It's safe to reuse the CASE mechanism because + * there cannot be a CASE between here and where the value would be + * needed. + */ + econtext->caseValue_datum = values[fieldnum - 1]; + econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n'); + + values[fieldnum - 1] = ExecEvalExpr(newval, + econtext, + &eisnull, + NULL); + nulls[fieldnum - 1] = eisnull ? 'n' : ' '; + } + + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; + + tuple = heap_formtuple(tupDesc, values, nulls); + + pfree(values); + pfree(nulls); + + return HeapTupleGetDatum(tuple); +} + +/* ---------------------------------------------------------------- * ExecEvalRelabelType * * Evaluate a RelabelType node. @@ -2810,6 +2940,18 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) fstate; } break; + case T_FieldStore: + { + FieldStore *fstore = (FieldStore *) node; + FieldStoreState *fstate = makeNode(FieldStoreState); + + fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore; + fstate->arg = ExecInitExpr(fstore->arg, parent); + fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent); + fstate->argdesc = NULL; + state = (ExprState *) fstate; + } + break; case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; |