Skip to content

Commit 12b1b5d

Browse files
committed
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.
1 parent fd536dd commit 12b1b5d

File tree

15 files changed

+356
-71
lines changed

15 files changed

+356
-71
lines changed

src/backend/executor/execQual.c

Lines changed: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.169 2004/09/22 17:41:50 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.170 2004/12/11 23:26:29 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -87,6 +87,9 @@ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
8787
bool *isNull, ExprDoneCond *isDone);
8888
static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
8989
bool *isNull, ExprDoneCond *isDone);
90+
static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
91+
ExprContext *econtext,
92+
bool *isNull, ExprDoneCond *isDone);
9093
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
9194
bool *isNull, ExprDoneCond *isDone);
9295
static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
@@ -428,7 +431,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
428431
*
429432
* Returns a Datum whose value is the value of a range
430433
* variable with respect to given expression context.
431-
* ---------------------------------------------------------------- */
434+
* ----------------------------------------------------------------
435+
*/
432436
static Datum
433437
ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
434438
bool *isNull, ExprDoneCond *isDone)
@@ -1844,6 +1848,75 @@ ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
18441848
return BoolGetDatum(!AnyNull);
18451849
}
18461850

1851+
/* ----------------------------------------------------------------
1852+
* ExecEvalConvertRowtype
1853+
*
1854+
* Evaluate a rowtype coercion operation. This may require
1855+
* rearranging field positions.
1856+
* ----------------------------------------------------------------
1857+
*/
1858+
static Datum
1859+
ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
1860+
ExprContext *econtext,
1861+
bool *isNull, ExprDoneCond *isDone)
1862+
{
1863+
HeapTuple result;
1864+
Datum tupDatum;
1865+
HeapTupleHeader tuple;
1866+
HeapTupleData tmptup;
1867+
AttrNumber *attrMap = cstate->attrMap;
1868+
Datum *invalues = cstate->invalues;
1869+
char *innulls = cstate->innulls;
1870+
Datum *outvalues = cstate->outvalues;
1871+
char *outnulls = cstate->outnulls;
1872+
int i;
1873+
int outnatts = cstate->outdesc->natts;
1874+
1875+
tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
1876+
1877+
/* this test covers the isDone exception too: */
1878+
if (*isNull)
1879+
return tupDatum;
1880+
1881+
tuple = DatumGetHeapTupleHeader(tupDatum);
1882+
1883+
Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid);
1884+
Assert(HeapTupleHeaderGetTypMod(tuple) == cstate->indesc->tdtypmod);
1885+
1886+
/*
1887+
* heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader.
1888+
*/
1889+
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
1890+
tmptup.t_data = tuple;
1891+
1892+
/*
1893+
* Extract all the values of the old tuple, offsetting the arrays
1894+
* so that invalues[0] is NULL and invalues[1] is the first
1895+
* source attribute; this exactly matches the numbering convention
1896+
* in attrMap.
1897+
*/
1898+
heap_deformtuple(&tmptup, cstate->indesc, invalues + 1, innulls + 1);
1899+
invalues[0] = (Datum) 0;
1900+
innulls[0] = 'n';
1901+
1902+
/*
1903+
* Transpose into proper fields of the new tuple.
1904+
*/
1905+
for (i = 0; i < outnatts; i++)
1906+
{
1907+
int j = attrMap[i];
1908+
1909+
outvalues[i] = invalues[j];
1910+
outnulls[i] = innulls[j];
1911+
}
1912+
1913+
/*
1914+
* Now form the new tuple.
1915+
*/
1916+
result = heap_formtuple(cstate->outdesc, outvalues, outnulls);
1917+
1918+
return HeapTupleGetDatum(result);
1919+
}
18471920

18481921
/* ----------------------------------------------------------------
18491922
* ExecEvalCase
@@ -2969,6 +3042,68 @@ ExecInitExpr(Expr *node, PlanState *parent)
29693042
state = (ExprState *) gstate;
29703043
}
29713044
break;
3045+
case T_ConvertRowtypeExpr:
3046+
{
3047+
ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
3048+
ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
3049+
int i;
3050+
int n;
3051+
3052+
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
3053+
cstate->arg = ExecInitExpr(convert->arg, parent);
3054+
/* save copies of needed tuple descriptors */
3055+
cstate->indesc = lookup_rowtype_tupdesc(exprType((Node *) convert->arg), -1);
3056+
cstate->indesc = CreateTupleDescCopy(cstate->indesc);
3057+
cstate->outdesc = lookup_rowtype_tupdesc(convert->resulttype, -1);
3058+
cstate->outdesc = CreateTupleDescCopy(cstate->outdesc);
3059+
/* prepare map from old to new attribute numbers */
3060+
n = cstate->outdesc->natts;
3061+
cstate->attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber));
3062+
for (i = 0; i < n; i++)
3063+
{
3064+
Form_pg_attribute att = cstate->outdesc->attrs[i];
3065+
char *attname;
3066+
Oid atttypid;
3067+
int32 atttypmod;
3068+
int j;
3069+
3070+
if (att->attisdropped)
3071+
continue; /* attrMap[i] is already 0 */
3072+
attname = NameStr(att->attname);
3073+
atttypid = att->atttypid;
3074+
atttypmod = att->atttypmod;
3075+
for (j = 0; j < cstate->indesc->natts; j++)
3076+
{
3077+
att = cstate->indesc->attrs[j];
3078+
if (att->attisdropped)
3079+
continue;
3080+
if (strcmp(attname, NameStr(att->attname)) == 0)
3081+
{
3082+
/* Found it, check type */
3083+
if (atttypid != att->atttypid || atttypmod != att->atttypmod)
3084+
elog(ERROR, "attribute \"%s\" of type %s does not match corresponding attribute of type %s",
3085+
attname,
3086+
format_type_be(cstate->indesc->tdtypeid),
3087+
format_type_be(cstate->outdesc->tdtypeid));
3088+
cstate->attrMap[i] = (AttrNumber) (j + 1);
3089+
break;
3090+
}
3091+
}
3092+
if (cstate->attrMap[i] == 0)
3093+
elog(ERROR, "attribute \"%s\" of type %s does not exist",
3094+
attname,
3095+
format_type_be(cstate->indesc->tdtypeid));
3096+
}
3097+
/* preallocate workspace for Datum arrays */
3098+
n = cstate->indesc->natts + 1; /* +1 for NULL */
3099+
cstate->invalues = (Datum *) palloc(n * sizeof(Datum));
3100+
cstate->innulls = (char *) palloc(n * sizeof(char));
3101+
n = cstate->outdesc->natts;
3102+
cstate->outvalues = (Datum *) palloc(n * sizeof(Datum));
3103+
cstate->outnulls = (char *) palloc(n * sizeof(char));
3104+
state = (ExprState *) cstate;
3105+
}
3106+
break;
29723107
case T_CaseExpr:
29733108
{
29743109
CaseExpr *caseexpr = (CaseExpr *) node;

src/backend/nodes/copyfuncs.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.293 2004/11/05 19:15:59 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.294 2004/12/11 23:26:33 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -876,6 +876,21 @@ _copyRelabelType(RelabelType *from)
876876
return newnode;
877877
}
878878

879+
/*
880+
* _copyConvertRowtypeExpr
881+
*/
882+
static ConvertRowtypeExpr *
883+
_copyConvertRowtypeExpr(ConvertRowtypeExpr *from)
884+
{
885+
ConvertRowtypeExpr *newnode = makeNode(ConvertRowtypeExpr);
886+
887+
COPY_NODE_FIELD(arg);
888+
COPY_SCALAR_FIELD(resulttype);
889+
COPY_SCALAR_FIELD(convertformat);
890+
891+
return newnode;
892+
}
893+
879894
/*
880895
* _copyCaseExpr
881896
*/
@@ -2696,6 +2711,9 @@ copyObject(void *from)
26962711
case T_RelabelType:
26972712
retval = _copyRelabelType(from);
26982713
break;
2714+
case T_ConvertRowtypeExpr:
2715+
retval = _copyConvertRowtypeExpr(from);
2716+
break;
26992717
case T_CaseExpr:
27002718
retval = _copyCaseExpr(from);
27012719
break;

src/backend/nodes/equalfuncs.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Portions Copyright (c) 1994, Regents of the University of California
1919
*
2020
* IDENTIFICATION
21-
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.232 2004/11/05 19:15:59 tgl Exp $
21+
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.233 2004/12/11 23:26:33 tgl Exp $
2222
*
2323
*-------------------------------------------------------------------------
2424
*/
@@ -380,6 +380,24 @@ _equalRelabelType(RelabelType *a, RelabelType *b)
380380
return true;
381381
}
382382

383+
static bool
384+
_equalConvertRowtypeExpr(ConvertRowtypeExpr *a, ConvertRowtypeExpr *b)
385+
{
386+
COMPARE_NODE_FIELD(arg);
387+
COMPARE_SCALAR_FIELD(resulttype);
388+
389+
/*
390+
* Special-case COERCE_DONTCARE, so that planner can build coercion
391+
* nodes that are equal() to both explicit and implicit coercions.
392+
*/
393+
if (a->convertformat != b->convertformat &&
394+
a->convertformat != COERCE_DONTCARE &&
395+
b->convertformat != COERCE_DONTCARE)
396+
return false;
397+
398+
return true;
399+
}
400+
383401
static bool
384402
_equalCaseExpr(CaseExpr *a, CaseExpr *b)
385403
{
@@ -1844,6 +1862,9 @@ equal(void *a, void *b)
18441862
case T_RelabelType:
18451863
retval = _equalRelabelType(a, b);
18461864
break;
1865+
case T_ConvertRowtypeExpr:
1866+
retval = _equalConvertRowtypeExpr(a, b);
1867+
break;
18471868
case T_CaseExpr:
18481869
retval = _equalCaseExpr(a, b);
18491870
break;

src/backend/nodes/outfuncs.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.243 2004/08/29 05:06:43 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.244 2004/12/11 23:26:33 tgl Exp $
1212
*
1313
* NOTES
1414
* Every node type that can appear in stored rules' parsetrees *must*
@@ -767,6 +767,16 @@ _outRelabelType(StringInfo str, RelabelType *node)
767767
WRITE_ENUM_FIELD(relabelformat, CoercionForm);
768768
}
769769

770+
static void
771+
_outConvertRowtypeExpr(StringInfo str, ConvertRowtypeExpr *node)
772+
{
773+
WRITE_NODE_TYPE("CONVERTROWTYPEEXPR");
774+
775+
WRITE_NODE_FIELD(arg);
776+
WRITE_OID_FIELD(resulttype);
777+
WRITE_ENUM_FIELD(convertformat, CoercionForm);
778+
}
779+
770780
static void
771781
_outCaseExpr(StringInfo str, CaseExpr *node)
772782
{
@@ -1728,6 +1738,9 @@ _outNode(StringInfo str, void *obj)
17281738
case T_RelabelType:
17291739
_outRelabelType(str, obj);
17301740
break;
1741+
case T_ConvertRowtypeExpr:
1742+
_outConvertRowtypeExpr(str, obj);
1743+
break;
17311744
case T_CaseExpr:
17321745
_outCaseExpr(str, obj);
17331746
break;

src/backend/nodes/readfuncs.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.173 2004/08/29 04:12:33 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.174 2004/12/11 23:26:34 tgl Exp $
1212
*
1313
* NOTES
1414
* Path and Plan nodes do not have any readfuncs support, because we
@@ -575,6 +575,21 @@ _readRelabelType(void)
575575
READ_DONE();
576576
}
577577

578+
/*
579+
* _readConvertRowtypeExpr
580+
*/
581+
static ConvertRowtypeExpr *
582+
_readConvertRowtypeExpr(void)
583+
{
584+
READ_LOCALS(ConvertRowtypeExpr);
585+
586+
READ_NODE_FIELD(arg);
587+
READ_OID_FIELD(resulttype);
588+
READ_ENUM_FIELD(convertformat, CoercionForm);
589+
590+
READ_DONE();
591+
}
592+
578593
/*
579594
* _readCaseExpr
580595
*/
@@ -971,6 +986,8 @@ parseNodeString(void)
971986
return_value = _readFieldStore();
972987
else if (MATCH("RELABELTYPE", 11))
973988
return_value = _readRelabelType();
989+
else if (MATCH("CONVERTROWTYPEEXPR", 18))
990+
return_value = _readConvertRowtypeExpr();
974991
else if (MATCH("CASE", 4))
975992
return_value = _readCaseExpr();
976993
else if (MATCH("WHEN", 4))

src/backend/optimizer/prep/prepjointree.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
*
1818
* IDENTIFICATION
19-
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.23 2004/08/29 05:06:44 momjian Exp $
19+
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.24 2004/12/11 23:26:37 tgl Exp $
2020
*
2121
*-------------------------------------------------------------------------
2222
*/
@@ -839,6 +839,13 @@ find_nonnullable_rels(Node *node, bool top_level)
839839

840840
result = find_nonnullable_rels((Node *) expr->arg, top_level);
841841
}
842+
else if (IsA(node, ConvertRowtypeExpr))
843+
{
844+
/* not clear this is useful, but it can't hurt */
845+
ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node;
846+
847+
result = find_nonnullable_rels((Node *) expr->arg, top_level);
848+
}
842849
else if (IsA(node, NullTest))
843850
{
844851
NullTest *expr = (NullTest *) node;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy