Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbf7ca15

Browse files
committed
Ensure that RowExprs and whole-row Vars produce the expected column names.
At one time it wasn't terribly important what column names were associatedwith the fields of a composite Datum, but since the introduction ofoperations like row_to_json(), it's important that looking up the rowtypeID embedded in the Datum returns the column names that users would expect.That did not work terribly well before this patch: you could get the columnnames of the underlying table, or column aliases from any level of thequery, depending on minor details of the plan tree. You could even gettotally empty field names, which is disastrous for cases like row_to_json().To fix this for whole-row Vars, look to the RTE referenced by the Var, andmake sure its column aliases are applied to the rowtype associated withthe result Datums. This is a tad scary because we might have to returna transient RECORD type even though the Var is declared as having somenamed rowtype. In principle it should be all right because the recordtype will still be physically compatible with the named rowtype; butI had to weaken one Assert in ExecEvalConvertRowtype, and there might bethird-party code containing similar assumptions.Similarly, RowExprs have to be willing to override the column names comingfrom a named composite result type and produce a RECORD when the columnaliases visible at the site of the RowExpr differ from the underlyingtable's column names.In passing, revert the decision made in commit398f70e to addan alias-list argument to ExecTypeFromExprList: better to provide thatfunctionality in a separate function. This also reverts most of the codechanges ind685814, which we don't need because we're no longerdepending on the tupdesc found in the child plan node's result slot to beblessed.Back-patch to 9.4, but not earlier, since this solution changes the resultsin some cases that users might not have realized were buggy. We'll apply amore restricted form of this patch in older branches.
1 parent1e0b436 commitbf7ca15

File tree

8 files changed

+340
-73
lines changed

8 files changed

+340
-73
lines changed

‎src/backend/executor/execQual.c

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include"nodes/nodeFuncs.h"
5151
#include"optimizer/planner.h"
5252
#include"parser/parse_coerce.h"
53+
#include"parser/parsetree.h"
5354
#include"pgstat.h"
5455
#include"utils/acl.h"
5556
#include"utils/builtins.h"
@@ -712,6 +713,8 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
712713
{
713714
Var*variable= (Var*)wrvstate->xprstate.expr;
714715
TupleTableSlot*slot;
716+
TupleDescoutput_tupdesc;
717+
MemoryContextoldcontext;
715718
boolneedslow= false;
716719

717720
if (isDone)
@@ -787,8 +790,6 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
787790
/* If so, build the junkfilter in the query memory context */
788791
if (junk_filter_needed)
789792
{
790-
MemoryContextoldcontext;
791-
792793
oldcontext=MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
793794
wrvstate->wrv_junkFilter=
794795
ExecInitJunkFilter(subplan->plan->targetlist,
@@ -860,10 +861,60 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
860861
needslow= true;/* need runtime check for null */
861862
}
862863

864+
/*
865+
* Use the variable's declared rowtype as the descriptor for the
866+
* output values, modulo possibly assigning new column names below. In
867+
* particular, we *must* absorb any attisdropped markings.
868+
*/
869+
oldcontext=MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
870+
output_tupdesc=CreateTupleDescCopy(var_tupdesc);
871+
MemoryContextSwitchTo(oldcontext);
872+
863873
ReleaseTupleDesc(var_tupdesc);
864874
}
875+
else
876+
{
877+
/*
878+
* In the RECORD case, we use the input slot's rowtype as the
879+
* descriptor for the output values, modulo possibly assigning new
880+
* column names below.
881+
*/
882+
oldcontext=MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
883+
output_tupdesc=CreateTupleDescCopy(slot->tts_tupleDescriptor);
884+
MemoryContextSwitchTo(oldcontext);
885+
}
865886

866-
/* Skip the checking on future executions of node */
887+
/*
888+
* Construct a tuple descriptor for the composite values we'll produce,
889+
* and make sure its record type is "blessed". The main reason to do this
890+
* is to be sure that operations such as row_to_json() will see the
891+
* desired column names when they look up the descriptor from the type
892+
* information embedded in the composite values.
893+
*
894+
* We already got the correct physical datatype info above, but now we
895+
* should try to find the source RTE and adopt its column aliases, in case
896+
* they are different from the original rowtype's names. For example, in
897+
* "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
898+
* output should be named "x" and "y" regardless of tab's column names.
899+
*
900+
* If we can't locate the RTE, assume the column names we've got are OK.
901+
* (As of this writing, the only cases where we can't locate the RTE are
902+
* in execution of trigger WHEN clauses, and then the Var will have the
903+
* trigger's relation's rowtype, so its names are fine.)
904+
*/
905+
if (econtext->ecxt_estate&&
906+
variable->varno <=list_length(econtext->ecxt_estate->es_range_table))
907+
{
908+
RangeTblEntry*rte=rt_fetch(variable->varno,
909+
econtext->ecxt_estate->es_range_table);
910+
911+
ExecTypeSetColNames(output_tupdesc,rte->eref->colnames);
912+
}
913+
914+
/* Bless the tupdesc if needed, and save it in the execution state */
915+
wrvstate->wrv_tupdesc=BlessTupleDesc(output_tupdesc);
916+
917+
/* Skip all the above on future executions of node */
867918
if (needslow)
868919
wrvstate->xprstate.evalfunc= (ExprStateEvalFunc)ExecEvalWholeRowSlow;
869920
else
@@ -886,7 +937,6 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
886937
{
887938
Var*variable= (Var*)wrvstate->xprstate.expr;
888939
TupleTableSlot*slot;
889-
TupleDescslot_tupdesc;
890940
HeapTupleHeaderdtuple;
891941

892942
if (isDone)
@@ -916,34 +966,16 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
916966
if (wrvstate->wrv_junkFilter!=NULL)
917967
slot=ExecFilterJunk(wrvstate->wrv_junkFilter,slot);
918968

919-
/*
920-
* If it's a RECORD Var, we'll use the slot's type ID info. It's likely
921-
* that the slot's type is also RECORD; if so, make sure it's been
922-
* "blessed", so that the Datum can be interpreted later. (Note: we must
923-
* do this here, not in ExecEvalWholeRowVar, because some plan trees may
924-
* return different slots at different times. We have to be ready to
925-
* bless additional slots during the run.)
926-
*/
927-
slot_tupdesc=slot->tts_tupleDescriptor;
928-
if (variable->vartype==RECORDOID&&
929-
slot_tupdesc->tdtypeid==RECORDOID&&
930-
slot_tupdesc->tdtypmod<0)
931-
assign_record_type_typmod(slot_tupdesc);
932-
933969
/*
934970
* Copy the slot tuple and make sure any toasted fields get detoasted.
935971
*/
936972
dtuple=DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
937973

938974
/*
939-
* If the Var identifies a named composite type, label the datum with that
940-
* type; otherwise we'll use the slot's info.
975+
* Label the datum with the composite type info we identified before.
941976
*/
942-
if (variable->vartype!=RECORDOID)
943-
{
944-
HeapTupleHeaderSetTypeId(dtuple,variable->vartype);
945-
HeapTupleHeaderSetTypMod(dtuple,variable->vartypmod);
946-
}
977+
HeapTupleHeaderSetTypeId(dtuple,wrvstate->wrv_tupdesc->tdtypeid);
978+
HeapTupleHeaderSetTypMod(dtuple,wrvstate->wrv_tupdesc->tdtypmod);
947979

948980
returnPointerGetDatum(dtuple);
949981
}
@@ -997,8 +1029,9 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
9971029
tuple=ExecFetchSlotTuple(slot);
9981030
tupleDesc=slot->tts_tupleDescriptor;
9991031

1032+
/* wrv_tupdesc is a good enough representation of the Var's rowtype */
10001033
Assert(variable->vartype!=RECORDOID);
1001-
var_tupdesc=lookup_rowtype_tupdesc(variable->vartype,-1);
1034+
var_tupdesc=wrvstate->wrv_tupdesc;
10021035

10031036
/* Check to see if any dropped attributes are non-null */
10041037
for (i=0;i<var_tupdesc->natts;i++)
@@ -1025,12 +1058,10 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
10251058
dtuple=DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
10261059

10271060
/*
1028-
*Resetdatum's type ID fields to match the Var.
1061+
*Label thedatum with the composite type info we identified before.
10291062
*/
1030-
HeapTupleHeaderSetTypeId(dtuple,variable->vartype);
1031-
HeapTupleHeaderSetTypMod(dtuple,variable->vartypmod);
1032-
1033-
ReleaseTupleDesc(var_tupdesc);
1063+
HeapTupleHeaderSetTypeId(dtuple,wrvstate->wrv_tupdesc->tdtypeid);
1064+
HeapTupleHeaderSetTypMod(dtuple,wrvstate->wrv_tupdesc->tdtypmod);
10341065

10351066
returnPointerGetDatum(dtuple);
10361067
}
@@ -2850,8 +2881,14 @@ ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
28502881
cstate->initialized= false;
28512882
}
28522883

2853-
Assert(HeapTupleHeaderGetTypeId(tuple)==cstate->indesc->tdtypeid);
2854-
Assert(HeapTupleHeaderGetTypMod(tuple)==cstate->indesc->tdtypmod);
2884+
/*
2885+
* We used to be able to assert that incoming tuples are marked with
2886+
* exactly the rowtype of cstate->indesc. However, now that
2887+
* ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
2888+
* due to inserting aliases, we can only make this weak test:
2889+
*/
2890+
Assert(HeapTupleHeaderGetTypeId(tuple)==cstate->indesc->tdtypeid||
2891+
HeapTupleHeaderGetTypeId(tuple)==RECORDOID);
28552892

28562893
/* if first time through, initialize conversion map */
28572894
if (!cstate->initialized)
@@ -4375,6 +4412,7 @@ ExecInitExpr(Expr *node, PlanState *parent)
43754412
WholeRowVarExprState*wstate=makeNode(WholeRowVarExprState);
43764413

43774414
wstate->parent=parent;
4415+
wstate->wrv_tupdesc=NULL;
43784416
wstate->wrv_junkFilter=NULL;
43794417
state= (ExprState*)wstate;
43804418
state->evalfunc= (ExprStateEvalFunc)ExecEvalWholeRowVar;
@@ -4778,17 +4816,18 @@ ExecInitExpr(Expr *node, PlanState *parent)
47784816
/* Build tupdesc to describe result tuples */
47794817
if (rowexpr->row_typeid==RECORDOID)
47804818
{
4781-
/* generic record, use runtime type assignment */
4782-
rstate->tupdesc=ExecTypeFromExprList(rowexpr->args,
4783-
rowexpr->colnames);
4784-
BlessTupleDesc(rstate->tupdesc);
4785-
/* we won't need to redo this at runtime */
4819+
/* generic record, use types of given expressions */
4820+
rstate->tupdesc=ExecTypeFromExprList(rowexpr->args);
47864821
}
47874822
else
47884823
{
47894824
/* it's been cast to a named type, use that */
47904825
rstate->tupdesc=lookup_rowtype_tupdesc_copy(rowexpr->row_typeid,-1);
47914826
}
4827+
/* In either case, adopt RowExpr's column aliases */
4828+
ExecTypeSetColNames(rstate->tupdesc,rowexpr->colnames);
4829+
/* Bless the tupdesc in case it's now of type RECORD */
4830+
BlessTupleDesc(rstate->tupdesc);
47924831
/* Set up evaluation, skipping any deleted columns */
47934832
Assert(list_length(rowexpr->args) <=rstate->tupdesc->natts);
47944833
attrs=rstate->tupdesc->attrs;

‎src/backend/executor/execTuples.c

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -943,28 +943,25 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
943943
/*
944944
* ExecTypeFromExprList - build a tuple descriptor from a list of Exprs
945945
*
946-
* Caller must also supply a list of field names (String nodes).
946+
* This is roughly like ExecTypeFromTL, but we work from bare expressions
947+
* not TargetEntrys. No names are attached to the tupledesc's columns.
947948
*/
948949
TupleDesc
949-
ExecTypeFromExprList(List*exprList,List*namesList)
950+
ExecTypeFromExprList(List*exprList)
950951
{
951952
TupleDesctypeInfo;
952-
ListCell*le;
953-
ListCell*ln;
953+
ListCell*lc;
954954
intcur_resno=1;
955955

956-
Assert(list_length(exprList)==list_length(namesList));
957-
958956
typeInfo=CreateTemplateTupleDesc(list_length(exprList), false);
959957

960-
forboth(le,exprList,ln,namesList)
958+
foreach(lc,exprList)
961959
{
962-
Node*e=lfirst(le);
963-
char*n=strVal(lfirst(ln));
960+
Node*e=lfirst(lc);
964961

965962
TupleDescInitEntry(typeInfo,
966963
cur_resno,
967-
n,
964+
NULL,
968965
exprType(e),
969966
exprTypmod(e),
970967
0);
@@ -977,6 +974,54 @@ ExecTypeFromExprList(List *exprList, List *namesList)
977974
returntypeInfo;
978975
}
979976

977+
/*
978+
* ExecTypeSetColNames - set column names in a TupleDesc
979+
*
980+
* Column names must be provided as an alias list (list of String nodes).
981+
*
982+
* For some callers, the supplied tupdesc has a named rowtype (not RECORD)
983+
* and it is moderately likely that the alias list matches the column names
984+
* already present in the tupdesc. If we do change any column names then
985+
* we must reset the tupdesc's type to anonymous RECORD; but we avoid doing
986+
* so if no names change.
987+
*/
988+
void
989+
ExecTypeSetColNames(TupleDesctypeInfo,List*namesList)
990+
{
991+
boolmodified= false;
992+
intcolno=0;
993+
ListCell*lc;
994+
995+
foreach(lc,namesList)
996+
{
997+
char*cname=strVal(lfirst(lc));
998+
Form_pg_attributeattr;
999+
1000+
/* Guard against too-long names list */
1001+
if (colno >=typeInfo->natts)
1002+
break;
1003+
attr=typeInfo->attrs[colno++];
1004+
1005+
/* Ignore empty aliases (these must be for dropped columns) */
1006+
if (cname[0]=='\0')
1007+
continue;
1008+
1009+
/* Change tupdesc only if alias is actually different */
1010+
if (strcmp(cname,NameStr(attr->attname))!=0)
1011+
{
1012+
namestrcpy(&(attr->attname),cname);
1013+
modified= true;
1014+
}
1015+
}
1016+
1017+
/* If we modified the tupdesc, it's now a new record type */
1018+
if (modified)
1019+
{
1020+
typeInfo->tdtypeid=RECORDOID;
1021+
typeInfo->tdtypmod=-1;
1022+
}
1023+
}
1024+
9801025
/*
9811026
* BlessTupleDesc - make a completed tuple descriptor useful for SRFs
9821027
*

‎src/backend/executor/nodeFunctionscan.c

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
#include"executor/nodeFunctionscan.h"
2727
#include"funcapi.h"
2828
#include"nodes/nodeFuncs.h"
29-
#include"parser/parsetree.h"
3029
#include"utils/builtins.h"
3130
#include"utils/memutils.h"
3231

@@ -279,8 +278,6 @@ FunctionScanState *
279278
ExecInitFunctionScan(FunctionScan*node,EState*estate,inteflags)
280279
{
281280
FunctionScanState*scanstate;
282-
RangeTblEntry*rte=rt_fetch(node->scan.scanrelid,
283-
estate->es_range_table);
284281
intnfuncs=list_length(node->functions);
285282
TupleDescscan_tupdesc;
286283
inti,
@@ -494,22 +491,6 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
494491
Assert(attno==natts);
495492
}
496493

497-
/*
498-
* Make sure the scan result tupdesc has the column names the query
499-
* expects. This affects the output of constructs like row_to_json which
500-
* read the column names from the passed-in tupdesc.
501-
*/
502-
i=0;
503-
foreach(lc,rte->eref->colnames)
504-
{
505-
char*attname=strVal(lfirst(lc));
506-
507-
if (i >=scan_tupdesc->natts)
508-
break;/* shouldn't happen, but just in case */
509-
namestrcpy(&(scan_tupdesc->attrs[i]->attname),attname);
510-
i++;
511-
}
512-
513494
ExecAssignScanType(&scanstate->ss,scan_tupdesc);
514495

515496
/*

‎src/backend/executor/nodeValuesscan.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
#include"executor/executor.h"
2727
#include"executor/nodeValuesscan.h"
28-
#include"parser/parsetree.h"
2928

3029

3130
staticTupleTableSlot*ValuesNext(ValuesScanState*node);
@@ -189,8 +188,6 @@ ValuesScanState *
189188
ExecInitValuesScan(ValuesScan*node,EState*estate,inteflags)
190189
{
191190
ValuesScanState*scanstate;
192-
RangeTblEntry*rte=rt_fetch(node->scan.scanrelid,
193-
estate->es_range_table);
194191
TupleDesctupdesc;
195192
ListCell*vtl;
196193
inti;
@@ -242,8 +239,7 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
242239
/*
243240
* get info about values list
244241
*/
245-
tupdesc=ExecTypeFromExprList((List*)linitial(node->values_lists),
246-
rte->eref->colnames);
242+
tupdesc=ExecTypeFromExprList((List*)linitial(node->values_lists));
247243

248244
ExecAssignScanType(&scanstate->ss,tupdesc);
249245

‎src/include/executor/executor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,8 @@ extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
268268
TupleDesctupType);
269269
externTupleDescExecTypeFromTL(List*targetList,boolhasoid);
270270
externTupleDescExecCleanTypeFromTL(List*targetList,boolhasoid);
271-
externTupleDescExecTypeFromExprList(List*exprList,List*namesList);
271+
externTupleDescExecTypeFromExprList(List*exprList);
272+
externvoidExecTypeSetColNames(TupleDesctypeInfo,List*namesList);
272273
externvoidUpdateChangedParamSet(PlanState*node,Bitmapset*newchg);
273274

274275
typedefstructTupOutputState

‎src/include/nodes/execnodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ typedef struct WholeRowVarExprState
578578
{
579579
ExprStatexprstate;
580580
structPlanState*parent;/* parent PlanState, or NULL if none */
581+
TupleDescwrv_tupdesc;/* descriptor for resulting tuples */
581582
JunkFilter*wrv_junkFilter;/* JunkFilter to remove resjunk cols */
582583
}WholeRowVarExprState;
583584

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp