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

Commita477bfc

Browse files
committed
Suppress unnecessary RelabelType nodes in more cases.
eval_const_expressions sometimes produced RelabelType nodes thatwere useless because they just relabeled an expression to the sameexposed type it already had. This is worth avoiding because it cancause two equivalent expressions to not be equal(), preventingrecognition of useful optimizations. In the test case added here,an unpatched planner fails to notice that the "sqli = constant" clauserenders a sort step unnecessary, because one code path produces anextra RelabelType and another doesn't.Fix by ensuring that eval_const_expressions_mutator's T_RelabelTypecase will not add in an unnecessary RelabelType. Also save somecode by sharing a subroutine with the effectively-equivalent casesfor CollateExpr and CoerceToDomain. (CollateExpr had no bug, andI think that the case couldn't arise with CoerceToDomain, butit seems prudent to do the same check for all three cases.)Back-patch to v12. In principle this has been wrong all along,but I haven't seen a case where it causes visible misbehaviorbefore v12, so refrain from changing stable branches unnecessarily.Per investigation of a report from Eric Gillum.Discussion:https://postgr.es/m/CAMmjdmvAZsUEskHYj=KT9sTukVVCiCSoe_PBKOXsncFeAUDPCQ@mail.gmail.com
1 parent3acfe6b commita477bfc

File tree

3 files changed

+101
-97
lines changed

3 files changed

+101
-97
lines changed

‎src/backend/optimizer/util/clauses.c

Lines changed: 82 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ static Node *eval_const_expressions_mutator(Node *node,
119119
staticboolcontain_non_const_walker(Node*node,void*context);
120120
staticboolece_function_is_safe(Oidfuncid,
121121
eval_const_expressions_context*context);
122+
staticNode*apply_const_relabel(Node*arg,Oidrtype,
123+
int32rtypmod,Oidrcollid,
124+
CoercionFormrformat,intrlocation);
122125
staticList*simplify_or_arguments(List*args,
123126
eval_const_expressions_context*context,
124127
bool*haveNull,bool*forceTrue);
@@ -2774,46 +2777,19 @@ eval_const_expressions_mutator(Node *node,
27742777
returnnode;
27752778
caseT_RelabelType:
27762779
{
2777-
/*
2778-
* If we can simplify the input to a constant, then we don't
2779-
* need the RelabelType node anymore: just change the type
2780-
* field of the Const node. Otherwise, must copy the
2781-
* RelabelType node.
2782-
*/
27832780
RelabelType*relabel= (RelabelType*)node;
27842781
Node*arg;
27852782

2783+
/* Simplify the input ... */
27862784
arg=eval_const_expressions_mutator((Node*)relabel->arg,
27872785
context);
2788-
2789-
/*
2790-
* If we find stacked RelabelTypes (eg, from foo :: int ::
2791-
* oid) we can discard all but the top one.
2792-
*/
2793-
while (arg&&IsA(arg,RelabelType))
2794-
arg= (Node*) ((RelabelType*)arg)->arg;
2795-
2796-
if (arg&&IsA(arg,Const))
2797-
{
2798-
Const*con= (Const*)arg;
2799-
2800-
con->consttype=relabel->resulttype;
2801-
con->consttypmod=relabel->resulttypmod;
2802-
con->constcollid=relabel->resultcollid;
2803-
return (Node*)con;
2804-
}
2805-
else
2806-
{
2807-
RelabelType*newrelabel=makeNode(RelabelType);
2808-
2809-
newrelabel->arg= (Expr*)arg;
2810-
newrelabel->resulttype=relabel->resulttype;
2811-
newrelabel->resulttypmod=relabel->resulttypmod;
2812-
newrelabel->resultcollid=relabel->resultcollid;
2813-
newrelabel->relabelformat=relabel->relabelformat;
2814-
newrelabel->location=relabel->location;
2815-
return (Node*)newrelabel;
2816-
}
2786+
/* ... and attach a new RelabelType node, if needed */
2787+
returnapply_const_relabel(arg,
2788+
relabel->resulttype,
2789+
relabel->resulttypmod,
2790+
relabel->resultcollid,
2791+
relabel->relabelformat,
2792+
relabel->location);
28172793
}
28182794
caseT_CoerceViaIO:
28192795
{
@@ -2947,48 +2923,25 @@ eval_const_expressions_mutator(Node *node,
29472923
caseT_CollateExpr:
29482924
{
29492925
/*
2950-
* If we can simplify the input to a constant, then we don't
2951-
* need the CollateExpr node at all: just change the
2952-
* constcollid field of the Const node. Otherwise, replace
2953-
* the CollateExpr with a RelabelType. (We do that so as to
2954-
* improve uniformity of expression representation and thus
2955-
* simplify comparison of expressions.)
2926+
* We replace CollateExpr with RelabelType, so as to improve
2927+
* uniformity of expression representation and thus simplify
2928+
* comparison of expressions. Hence this looks very nearly
2929+
* the same as the RelabelType case, and we can apply the same
2930+
* optimizations to avoid unnecessary RelabelTypes.
29562931
*/
29572932
CollateExpr*collate= (CollateExpr*)node;
29582933
Node*arg;
29592934

2935+
/* Simplify the input ... */
29602936
arg=eval_const_expressions_mutator((Node*)collate->arg,
29612937
context);
2962-
2963-
if (arg&&IsA(arg,Const))
2964-
{
2965-
Const*con= (Const*)arg;
2966-
2967-
con->constcollid=collate->collOid;
2968-
return (Node*)con;
2969-
}
2970-
elseif (collate->collOid==exprCollation(arg))
2971-
{
2972-
/* Don't need a RelabelType either... */
2973-
returnarg;
2974-
}
2975-
else
2976-
{
2977-
RelabelType*relabel=makeNode(RelabelType);
2978-
2979-
relabel->resulttype=exprType(arg);
2980-
relabel->resulttypmod=exprTypmod(arg);
2981-
relabel->resultcollid=collate->collOid;
2982-
relabel->relabelformat=COERCE_IMPLICIT_CAST;
2983-
relabel->location=collate->location;
2984-
2985-
/* Don't create stacked RelabelTypes */
2986-
while (arg&&IsA(arg,RelabelType))
2987-
arg= (Node*) ((RelabelType*)arg)->arg;
2988-
relabel->arg= (Expr*)arg;
2989-
2990-
return (Node*)relabel;
2991-
}
2938+
/* ... and attach a new RelabelType node, if needed */
2939+
returnapply_const_relabel(arg,
2940+
exprType(arg),
2941+
exprTypmod(arg),
2942+
collate->collOid,
2943+
COERCE_IMPLICIT_CAST,
2944+
collate->location);
29922945
}
29932946
caseT_CaseExpr:
29942947
{
@@ -3490,32 +3443,12 @@ eval_const_expressions_mutator(Node *node,
34903443
cdomain->resulttype);
34913444

34923445
/* Generate RelabelType to substitute for CoerceToDomain */
3493-
/* This should match the RelabelType logic above */
3494-
3495-
while (arg&&IsA(arg,RelabelType))
3496-
arg= (Node*) ((RelabelType*)arg)->arg;
3497-
3498-
if (arg&&IsA(arg,Const))
3499-
{
3500-
Const*con= (Const*)arg;
3501-
3502-
con->consttype=cdomain->resulttype;
3503-
con->consttypmod=cdomain->resulttypmod;
3504-
con->constcollid=cdomain->resultcollid;
3505-
return (Node*)con;
3506-
}
3507-
else
3508-
{
3509-
RelabelType*newrelabel=makeNode(RelabelType);
3510-
3511-
newrelabel->arg= (Expr*)arg;
3512-
newrelabel->resulttype=cdomain->resulttype;
3513-
newrelabel->resulttypmod=cdomain->resulttypmod;
3514-
newrelabel->resultcollid=cdomain->resultcollid;
3515-
newrelabel->relabelformat=cdomain->coercionformat;
3516-
newrelabel->location=cdomain->location;
3517-
return (Node*)newrelabel;
3518-
}
3446+
returnapply_const_relabel(arg,
3447+
cdomain->resulttype,
3448+
cdomain->resulttypmod,
3449+
cdomain->resultcollid,
3450+
cdomain->coercionformat,
3451+
cdomain->location);
35193452
}
35203453

35213454
newcdomain=makeNode(CoerceToDomain);
@@ -3648,6 +3581,58 @@ ece_function_is_safe(Oid funcid, eval_const_expressions_context *context)
36483581
return false;
36493582
}
36503583

3584+
/*
3585+
* Subroutine for eval_const_expressions: apply RelabelType if needed
3586+
*/
3587+
staticNode*
3588+
apply_const_relabel(Node*arg,Oidrtype,int32rtypmod,Oidrcollid,
3589+
CoercionFormrformat,intrlocation)
3590+
{
3591+
/*
3592+
* If we find stacked RelabelTypes (eg, from foo::int::oid) we can discard
3593+
* all but the top one, and must do so to ensure that semantically
3594+
* equivalent expressions are equal().
3595+
*/
3596+
while (arg&&IsA(arg,RelabelType))
3597+
arg= (Node*) ((RelabelType*)arg)->arg;
3598+
3599+
if (arg&&IsA(arg,Const))
3600+
{
3601+
/*
3602+
* If it's a Const, just modify it in-place; since this is part of
3603+
* eval_const_expressions, we want to end up with a simple Const not
3604+
* an expression tree. We assume the Const is newly generated and
3605+
* hence safe to modify.
3606+
*/
3607+
Const*con= (Const*)arg;
3608+
3609+
con->consttype=rtype;
3610+
con->consttypmod=rtypmod;
3611+
con->constcollid=rcollid;
3612+
return (Node*)con;
3613+
}
3614+
elseif (exprType(arg)==rtype&&
3615+
exprTypmod(arg)==rtypmod&&
3616+
exprCollation(arg)==rcollid)
3617+
{
3618+
/* Sometimes we find a nest of relabels that net out to nothing. */
3619+
returnarg;
3620+
}
3621+
else
3622+
{
3623+
/* Nope, gotta have a RelabelType. */
3624+
RelabelType*newrelabel=makeNode(RelabelType);
3625+
3626+
newrelabel->arg= (Expr*)arg;
3627+
newrelabel->resulttype=rtype;
3628+
newrelabel->resulttypmod=rtypmod;
3629+
newrelabel->resultcollid=rcollid;
3630+
newrelabel->relabelformat=rformat;
3631+
newrelabel->location=rlocation;
3632+
return (Node*)newrelabel;
3633+
}
3634+
}
3635+
36513636
/*
36523637
* Subroutine for eval_const_expressions: process arguments of an OR clause
36533638
*

‎src/test/regress/expected/equivclass.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,3 +439,15 @@ explain (costs off)
439439
Filter: ((unique1 = unique1) OR (unique2 = unique2))
440440
(2 rows)
441441

442+
-- check that we recognize equivalence with dummy domains in the way
443+
create temp table undername (f1 name, f2 int);
444+
create temp view overview as
445+
select f1::information_schema.sql_identifier as sqli, f2 from undername;
446+
explain (costs off) -- this should not require a sort
447+
select * from overview where sqli = 'foo' order by sqli;
448+
QUERY PLAN
449+
------------------------------
450+
Seq Scan on undername
451+
Filter: (f1 = 'foo'::name)
452+
(2 rows)
453+

‎src/test/regress/sql/equivclass.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,10 @@ explain (costs off)
262262
-- this could be converted, but isn't at present
263263
explain (costs off)
264264
select*from tenk1where unique1= unique1or unique2= unique2;
265+
266+
-- check that we recognize equivalence with dummy domains in the way
267+
create temp table undername (f1 name, f2int);
268+
create temp view overviewas
269+
select f1::information_schema.sql_identifieras sqli, f2from undername;
270+
explain (costs off)-- this should not require a sort
271+
select*from overviewwhere sqli='foo'order by sqli;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp