1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

Instead of supposing (wrongly, in the general case) that the rowtype

of an inheritance child table is binary-compatible with the rowtype of
its parent, invent an expression node type that does the conversion
correctly.  Fixes the new bug exhibited by Kris Shannon as well as a
lot of old bugs that would only show up when using multiple inheritance
or after altering the parent table.
This commit is contained in:
Tom Lane
2004-12-11 23:26:51 +00:00
parent fd536dd257
commit 12b1b5d837
15 changed files with 356 additions and 71 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.169 2004/09/22 17:41:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.170 2004/12/11 23:26:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -87,6 +87,9 @@ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
@ -428,7 +431,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
*
* Returns a Datum whose value is the value of a range
* variable with respect to given expression context.
* ---------------------------------------------------------------- */
* ----------------------------------------------------------------
*/
static Datum
ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
@ -1844,6 +1848,75 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
return BoolGetDatum(!AnyNull);
}
/* ----------------------------------------------------------------
* ExecEvalConvertRowtype
*
* Evaluate a rowtype coercion operation. This may require
* rearranging field positions.
* ----------------------------------------------------------------
*/
static Datum
ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
HeapTuple result;
Datum tupDatum;
HeapTupleHeader tuple;
HeapTupleData tmptup;
AttrNumber *attrMap = cstate->attrMap;
Datum *invalues = cstate->invalues;
char *innulls = cstate->innulls;
Datum *outvalues = cstate->outvalues;
char *outnulls = cstate->outnulls;
int i;
int outnatts = cstate->outdesc->natts;
tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
/* this test covers the isDone exception too: */
if (*isNull)
return tupDatum;
tuple = DatumGetHeapTupleHeader(tupDatum);
Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
/*
* heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
tmptup.t_data = tuple;
/*
* Extract all the values of the old tuple, offsetting the arrays
* so that invalues[0] is NULL and invalues[1] is the first
* source attribute; this exactly matches the numbering convention
* in attrMap.
*/
heap_deformtuple(&tmptup, cstate->indesc, invalues + 1, innulls + 1);
invalues[0] = (Datum) 0;
innulls[0] = 'n';
/*
* Transpose into proper fields of the new tuple.
*/
for (i = 0; i < outnatts; i++)
{
int j = attrMap[i];
outvalues[i] = invalues[j];
outnulls[i] = innulls[j];
}
/*
* Now form the new tuple.
*/
result = heap_formtuple(cstate->outdesc, outvalues, outnulls);
return HeapTupleGetDatum(result);
}
/* ----------------------------------------------------------------
* ExecEvalCase
@ -2969,6 +3042,68 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) gstate;
}
break;
case T_ConvertRowtypeExpr:
{
ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
int i;
int n;
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
cstate->arg = ExecInitExpr(convert->arg, parent);
/* save copies of needed tuple descriptors */
cstate->indesc = lookup_rowtype_tupdesc(exprType((Node *) convert->arg), -1);
cstate->indesc = CreateTupleDescCopy(cstate->indesc);
cstate->outdesc = lookup_rowtype_tupdesc(convert->resulttype, -1);
cstate->outdesc = CreateTupleDescCopy(cstate->outdesc);
/* prepare map from old to new attribute numbers */
n = cstate->outdesc->natts;
cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
for (i = 0; i < n; i++)
{
Form_pg_attribute att = cstate->outdesc->attrs[i];
char *attname;
Oid atttypid;
int32 atttypmod;
int j;
if (att->attisdropped)
continue; /* attrMap[i] is already 0 */
attname = NameStr(att->attname);
atttypid = att->atttypid;
atttypmod = att->atttypmod;
for (j = 0; j < cstate->indesc->natts; j++)
{
att = cstate->indesc->attrs[j];
if (att->attisdropped)
continue;
if (strcmp(attname, NameStr(att->attname)) == 0)
{
/* Found it, check type */
if (atttypid != att->atttypid || atttypmod != att->atttypmod)
elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
attname,
format_type_be(cstate->indesc->tdtypeid),
format_type_be(cstate->outdesc->tdtypeid));
cstate->attrMap[i] = (AttrNumber) (j + 1);
break;
}
}
if (cstate->attrMap[i] == 0)
elog(ERROR, "attribute \"%s\" of type %s does not exist",
attname,
format_type_be(cstate->indesc->tdtypeid));
}
/* preallocate workspace for Datum arrays */
n = cstate->indesc->natts + 1; /* +1 for NULL */
cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
cstate->innulls = (char *) palloc(n * sizeof(char));
n = cstate->outdesc->natts;
cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
cstate->outnulls = (char *) palloc(n * sizeof(char));
state = (ExprState *) cstate;
}
break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;