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

Commit72cfc17

Browse files
committed
Improve handling of unknown-type literals in UNION/INTERSECT/EXCEPT.
This patch causes unknown-type Consts to be coerced to the resolved outputtype of the set operation at parse time. Formerly such Consts were leftalone until late in the planning stage. The disadvantage of that approachis that it disables some optimizations, because the planner sees the set-opleaf query as having different output column types than the overall set-op.We saw an example of that in a recent performance gripe from ClaudioFreire.Fixing such a Const requires scribbling on the leaf query intransformSetOperationTree, but that should be all right since if the leafquery's semantics depended on that output column, it would already haveresolved the unknown to something else.Most of the bulk of this patch is a simple adjustment oftransformSetOperationTree's API so that upper levels can get at theTargetEntry containing a Const to be replaced: it now returns a list ofTargetEntries, instead of just the bare expressions.
1 parent898a14e commit72cfc17

File tree

1 file changed

+96
-54
lines changed

1 file changed

+96
-54
lines changed

‎src/backend/parser/analyze.c

Lines changed: 96 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
5252
staticQuery*transformValuesClause(ParseState*pstate,SelectStmt*stmt);
5353
staticQuery*transformSetOperationStmt(ParseState*pstate,SelectStmt*stmt);
5454
staticNode*transformSetOperationTree(ParseState*pstate,SelectStmt*stmt,
55-
boolisTopLevel,List**colInfo);
55+
boolisTopLevel,List**targetlist);
5656
staticvoiddetermineRecursiveColTypes(ParseState*pstate,
57-
Node*larg,List*lcolinfo);
57+
Node*larg,List*nrtargetlist);
5858
staticvoidapplyColumnNames(List*dst,List*src);
5959
staticQuery*transformUpdateStmt(ParseState*pstate,UpdateStmt*stmt);
6060
staticList*transformReturningList(ParseState*pstate,List*returningList);
@@ -1197,7 +1197,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
11971197
intleftmostRTI;
11981198
Query*leftmostQuery;
11991199
SetOperationStmt*sostmt;
1200-
List*socolinfo;
12011200
List*intoColNames=NIL;
12021201
List*sortClause;
12031202
Node*limitOffset;
@@ -1271,7 +1270,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
12711270
*/
12721271
sostmt= (SetOperationStmt*)transformSetOperationTree(pstate,stmt,
12731272
true,
1274-
&socolinfo);
1273+
NULL);
12751274
Assert(sostmt&&IsA(sostmt,SetOperationStmt));
12761275
qry->setOperations= (Node*)sostmt;
12771276

@@ -1425,16 +1424,19 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
14251424
* transformSetOperationTree
14261425
*Recursively transform leaves and internal nodes of a set-op tree
14271426
*
1428-
* In addition to returning the transformed node, we return a list of
1429-
* expression nodes showing the type, typmod, collation, and location (for error messages)
1430-
* of each output column of the set-op node. This is used only during the
1431-
* internal recursion of this function. At the upper levels we use
1432-
* SetToDefault nodes for this purpose, since they carry exactly the fields
1433-
* needed, but any other expression node type would do as well.
1427+
* In addition to returning the transformed node, if targetlist isn't NULL
1428+
* then we return a list of its non-resjunk TargetEntry nodes. For a leaf
1429+
* set-op node these are the actual targetlist entries; otherwise they are
1430+
* dummy entries created to carry the type, typmod, collation, and location
1431+
* (for error messages) of each output column of the set-op node. This info
1432+
* is needed only during the internal recursion of this function, so outside
1433+
* callers pass NULL for targetlist. Note: the reason for passing the
1434+
* actual targetlist entries of a leaf node is so that upper levels can
1435+
* replace UNKNOWN Consts with properly-coerced constants.
14341436
*/
14351437
staticNode*
14361438
transformSetOperationTree(ParseState*pstate,SelectStmt*stmt,
1437-
boolisTopLevel,List**colInfo)
1439+
boolisTopLevel,List**targetlist)
14381440
{
14391441
boolisLeaf;
14401442

@@ -1512,15 +1514,18 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15121514
}
15131515

15141516
/*
1515-
* Extract a list of theresult expressions for upper-levelchecking.
1517+
* Extract a list of thenon-junk TLEs for upper-levelprocessing.
15161518
*/
1517-
*colInfo=NIL;
1518-
foreach(tl,selectQuery->targetList)
1519+
if (targetlist)
15191520
{
1520-
TargetEntry*tle= (TargetEntry*)lfirst(tl);
1521+
*targetlist=NIL;
1522+
foreach(tl,selectQuery->targetList)
1523+
{
1524+
TargetEntry*tle= (TargetEntry*)lfirst(tl);
15211525

1522-
if (!tle->resjunk)
1523-
*colInfo=lappend(*colInfo,tle->expr);
1526+
if (!tle->resjunk)
1527+
*targetlist=lappend(*targetlist,tle);
1528+
}
15241529
}
15251530

15261531
/*
@@ -1546,10 +1551,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15461551
{
15471552
/* Process an internal node (set operation node) */
15481553
SetOperationStmt*op=makeNode(SetOperationStmt);
1549-
List*lcolinfo;
1550-
List*rcolinfo;
1551-
ListCell*lci;
1552-
ListCell*rci;
1554+
List*ltargetlist;
1555+
List*rtargetlist;
1556+
ListCell*ltl;
1557+
ListCell*rtl;
15531558
constchar*context;
15541559

15551560
context= (stmt->op==SETOP_UNION ?"UNION" :
@@ -1564,7 +1569,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15641569
*/
15651570
op->larg=transformSetOperationTree(pstate,stmt->larg,
15661571
false,
1567-
&lcolinfo);
1572+
&ltargetlist);
15681573

15691574
/*
15701575
* If we are processing a recursive union query, now is the time to
@@ -1575,42 +1580,45 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
15751580
if (isTopLevel&&
15761581
pstate->p_parent_cte&&
15771582
pstate->p_parent_cte->cterecursive)
1578-
determineRecursiveColTypes(pstate,op->larg,lcolinfo);
1583+
determineRecursiveColTypes(pstate,op->larg,ltargetlist);
15791584

15801585
/*
15811586
* Recursively transform the right child node.
15821587
*/
15831588
op->rarg=transformSetOperationTree(pstate,stmt->rarg,
15841589
false,
1585-
&rcolinfo);
1590+
&rtargetlist);
15861591

15871592
/*
15881593
* Verify that the two children have the same number of non-junk
15891594
* columns, and determine the types of the merged output columns.
15901595
*/
1591-
if (list_length(lcolinfo)!=list_length(rcolinfo))
1596+
if (list_length(ltargetlist)!=list_length(rtargetlist))
15921597
ereport(ERROR,
15931598
(errcode(ERRCODE_SYNTAX_ERROR),
15941599
errmsg("each %s query must have the same number of columns",
15951600
context),
15961601
parser_errposition(pstate,
1597-
exprLocation((Node*)rcolinfo))));
1602+
exprLocation((Node*)rtargetlist))));
15981603

1599-
*colInfo=NIL;
1604+
if (targetlist)
1605+
*targetlist=NIL;
16001606
op->colTypes=NIL;
16011607
op->colTypmods=NIL;
16021608
op->colCollations=NIL;
16031609
op->groupClauses=NIL;
1604-
forboth(lci,lcolinfo,rci,rcolinfo)
1610+
forboth(ltl,ltargetlist,rtl,rtargetlist)
16051611
{
1606-
Node*lcolnode= (Node*)lfirst(lci);
1607-
Node*rcolnode= (Node*)lfirst(rci);
1612+
TargetEntry*ltle= (TargetEntry*)lfirst(ltl);
1613+
TargetEntry*rtle= (TargetEntry*)lfirst(rtl);
1614+
Node*lcolnode= (Node*)ltle->expr;
1615+
Node*rcolnode= (Node*)rtle->expr;
16081616
Oidlcoltype=exprType(lcolnode);
16091617
Oidrcoltype=exprType(rcolnode);
16101618
int32lcoltypmod=exprTypmod(lcolnode);
16111619
int32rcoltypmod=exprTypmod(rcolnode);
16121620
Node*bestexpr;
1613-
SetToDefault*rescolnode;
1621+
intbestlocation;
16141622
Oidrescoltype;
16151623
int32rescoltypmod;
16161624
Oidrescolcoll;
@@ -1620,6 +1628,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
16201628
list_make2(lcolnode,rcolnode),
16211629
context,
16221630
&bestexpr);
1631+
bestlocation=exprLocation(bestexpr);
16231632
/* if same type and same typmod, use typmod; else default */
16241633
if (lcoltype==rcoltype&&lcoltypmod==rcoltypmod)
16251634
rescoltypmod=lcoltypmod;
@@ -1637,32 +1646,44 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
16371646
* later anyway, but we want to fail now while we have sufficient
16381647
* context to produce an error cursor position.
16391648
*
1640-
* The if-tests might look wrong, but they are correct: we should
1641-
* verify if the input is non-UNKNOWN *or* if it is an UNKNOWN
1642-
* Const (to verify the literal is valid for the target data type)
1643-
* or Param (to possibly resolve the Param's type). We should do
1644-
* nothing if the input is say an UNKNOWN Var, which can happen in
1645-
* some cases.The planner is sometimes able to fold the Var to a
1649+
* For all non-UNKNOWN-type cases, we verify coercibility but we
1650+
* don't modify the child's expression, for fear of changing the
1651+
* child query's semantics.
1652+
*
1653+
* If a child expression is an UNKNOWN-type Const or Param, we
1654+
* want to replace it with the coerced expression. This can only
1655+
* happen when the child is a leaf set-op node. It's safe to
1656+
* replace the expression because if the child query's semantics
1657+
* depended on the type of this output column, it'd have already
1658+
* coerced the UNKNOWN to something else. We want to do this
1659+
* because (a) we want to verify that a Const is valid for the
1660+
* target type, or resolve the actual type of an UNKNOWN Param,
1661+
* and (b) we want to avoid unnecessary discrepancies between the
1662+
* output type of the child query and the resolved target type.
1663+
* Such a discrepancy would disable optimization in the planner.
1664+
*
1665+
* If it's some other UNKNOWN-type node, eg a Var, we do nothing.
1666+
* The planner is sometimes able to fold an UNKNOWN Var to a
16461667
* constant before it has to coerce the type, so failing now would
16471668
* just break cases that might work.
16481669
*/
1649-
if (lcoltype!=UNKNOWNOID||
1650-
IsA(lcolnode,Const)||IsA(lcolnode,Param))
1670+
if (lcoltype!=UNKNOWNOID)
16511671
(void)coerce_to_common_type(pstate,lcolnode,
16521672
rescoltype,context);
1653-
if (rcoltype!=UNKNOWNOID||
1654-
IsA(rcolnode,Const)||IsA(rcolnode,Param))
1673+
elseif (IsA(lcolnode,Const)||IsA(lcolnode,Param))
1674+
ltle->expr= (Expr*)
1675+
coerce_to_common_type(pstate,lcolnode,
1676+
rescoltype,context);
1677+
1678+
if (rcoltype!=UNKNOWNOID)
16551679
(void)coerce_to_common_type(pstate,rcolnode,
16561680
rescoltype,context);
1681+
elseif (IsA(rcolnode,Const)||IsA(rcolnode,Param))
1682+
rtle->expr= (Expr*)
1683+
coerce_to_common_type(pstate,rcolnode,
1684+
rescoltype,context);
16571685

16581686
/* emit results */
1659-
rescolnode=makeNode(SetToDefault);
1660-
rescolnode->typeId=rescoltype;
1661-
rescolnode->typeMod=rescoltypmod;
1662-
rescolnode->collid=rescolcoll;
1663-
rescolnode->location=exprLocation(bestexpr);
1664-
*colInfo=lappend(*colInfo,rescolnode);
1665-
16661687
op->colTypes=lappend_oid(op->colTypes,rescoltype);
16671688
op->colTypmods=lappend_int(op->colTypmods,rescoltypmod);
16681689
op->colCollations=lappend_oid(op->colCollations,rescolcoll);
@@ -1681,7 +1702,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
16811702
ParseCallbackStatepcbstate;
16821703

16831704
setup_parser_errposition_callback(&pcbstate,pstate,
1684-
rescolnode->location);
1705+
bestlocation);
16851706

16861707
/* determine the eqop and optional sortop */
16871708
get_sort_group_operators(rescoltype,
@@ -1700,6 +1721,27 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
17001721

17011722
op->groupClauses=lappend(op->groupClauses,grpcl);
17021723
}
1724+
1725+
/*
1726+
* Construct a dummy tlist entry to return. We use a SetToDefault
1727+
* node for the expression, since it carries exactly the fields
1728+
* needed, but any other expression node type would do as well.
1729+
*/
1730+
if (targetlist)
1731+
{
1732+
SetToDefault*rescolnode=makeNode(SetToDefault);
1733+
TargetEntry*restle;
1734+
1735+
rescolnode->typeId=rescoltype;
1736+
rescolnode->typeMod=rescoltypmod;
1737+
rescolnode->collid=rescolcoll;
1738+
rescolnode->location=bestlocation;
1739+
restle=makeTargetEntry((Expr*)rescolnode,
1740+
0,/* no need to set resno */
1741+
NULL,
1742+
false);
1743+
*targetlist=lappend(*targetlist,restle);
1744+
}
17031745
}
17041746

17051747
return (Node*)op;
@@ -1711,14 +1753,14 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
17111753
* to set up the parent CTE's columns
17121754
*/
17131755
staticvoid
1714-
determineRecursiveColTypes(ParseState*pstate,Node*larg,List*lcolinfo)
1756+
determineRecursiveColTypes(ParseState*pstate,Node*larg,List*nrtargetlist)
17151757
{
17161758
Node*node;
17171759
intleftmostRTI;
17181760
Query*leftmostQuery;
17191761
List*targetList;
17201762
ListCell*left_tlist;
1721-
ListCell*lci;
1763+
ListCell*nrtl;
17221764
intnext_resno;
17231765

17241766
/*
@@ -1740,16 +1782,16 @@ determineRecursiveColTypes(ParseState *pstate, Node *larg, List *lcolinfo)
17401782
left_tlist=list_head(leftmostQuery->targetList);
17411783
next_resno=1;
17421784

1743-
foreach(lci,lcolinfo)
1785+
foreach(nrtl,nrtargetlist)
17441786
{
1745-
Expr*lcolexpr= (Expr*)lfirst(lci);
1787+
TargetEntry*nrtle= (TargetEntry*)lfirst(nrtl);
17461788
TargetEntry*lefttle= (TargetEntry*)lfirst(left_tlist);
17471789
char*colName;
17481790
TargetEntry*tle;
17491791

17501792
Assert(!lefttle->resjunk);
17511793
colName=pstrdup(lefttle->resname);
1752-
tle=makeTargetEntry(lcolexpr,
1794+
tle=makeTargetEntry(nrtle->expr,
17531795
next_resno++,
17541796
colName,
17551797
false);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp