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:
@ -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;
|
||||
|
Reference in New Issue
Block a user