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

Commit288f15b

Browse files
committed
Fix some more problems with nested append relations.
As of commita87c729 (which later got backpatched as far as 9.1),we're explicitly supporting the notion that append relations can benested; this can occur when UNION ALL constructs are nested, or whena UNION ALL contains a table with inheritance children.Bug #11457 from Nelson Page, as well as an earlier report from ElvisPranskevichus, showed that there were still nasty bugs associated with suchcases: in particular the EquivalenceClass mechanism could try to generate"join" clauses connecting an appendrel child to some grandparent appendrel,which would result in assertion failures or bogus plans.Upon investigation I concluded that all current callers offind_childrel_appendrelinfo() need to be fixed to explicitly considermultiple levels of parent appendrels. The most complex fix was inprocessing of "broken" EquivalenceClasses, which are ECs for which we havebeen unable to generate all the derived equality clauses we would like tobecause of missing cross-type equality operators in the underlying btreeoperator family. That code path is more or less entirely untested bythe regression tests to date, because no standard opfamilies have suchholes in them. So I wrote a new regression test script to try to exerciseit a bit, which turned out to be quite a worthwhile activity as it exposedexisting bugs in all supported branches.The present patch is essentially the same as far back as 9.2, which iswhere parameterized paths were introduced. In 9.0 and 9.1, we only needto back-patch a small fragment of commit5b7b551, which fixes failure topropagate out the original WHERE clauses when a broken EC contains constantmembers. (The regression test case results show that these older branchesare noticeably stupider than 9.2+ in terms of the quality of the plansgenerated; but we don't really care about plan quality in such cases,only that the plan not be outright wrong. A more invasive fix in theolder branches would not be a good idea anyway from a plan-stabilitystandpoint.)
1 parentbbe3c06 commit288f15b

File tree

5 files changed

+508
-3
lines changed

5 files changed

+508
-3
lines changed

‎src/backend/optimizer/path/equivclass.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,12 @@ generate_base_implied_equalities_no_const(PlannerInfo *root,
744744
* of the EC back into the main restrictinfo datastructures. Multi-relation
745745
* clauses will be regurgitated later by generate_join_implied_equalities().
746746
* (We do it this way to maintain continuity with the case that ec_broken
747-
* becomes set only after we've gone up a join level or two.)
747+
* becomes set only after we've gone up a join level or two.) However, for
748+
* an EC that contains constants, we can adopt a simpler strategy and just
749+
* throw back all the source RestrictInfos immediately; that works because
750+
* we know that such an EC can't become broken later. (This rule justifies
751+
* ignoring ec_has_const ECs in generate_join_implied_equalities, even when
752+
* they are broken.)
748753
*/
749754
staticvoid
750755
generate_base_implied_equalities_broken(PlannerInfo*root,
@@ -756,7 +761,8 @@ generate_base_implied_equalities_broken(PlannerInfo *root,
756761
{
757762
RestrictInfo*restrictinfo= (RestrictInfo*)lfirst(lc);
758763

759-
if (bms_membership(restrictinfo->required_relids)!=BMS_MULTIPLE)
764+
if (ec->ec_has_const||
765+
bms_membership(restrictinfo->required_relids)!=BMS_MULTIPLE)
760766
distribute_restrictinfo_to_rels(root,restrictinfo);
761767
}
762768
}
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
--
2+
-- Tests for the planner's "equivalence class" mechanism
3+
--
4+
-- One thing that's not tested well during normal querying is the logic
5+
-- for handling "broken" ECs. This is because an EC can only become broken
6+
-- if its underlying btree operator family doesn't include a complete set
7+
-- of cross-type equality operators. There are not (and should not be)
8+
-- any such families built into Postgres; so we have to hack things up
9+
-- to create one. We do this by making two alias types that are really
10+
-- int8 (so we need no new C code) and adding only some operators for them
11+
-- into the standard integer_ops opfamily.
12+
create type int8alias1;
13+
create function int8alias1in(cstring) returns int8alias1
14+
strict immutable language internal as 'int8in';
15+
NOTICE: return type int8alias1 is only a shell
16+
create function int8alias1out(int8alias1) returns cstring
17+
strict immutable language internal as 'int8out';
18+
NOTICE: argument type int8alias1 is only a shell
19+
create type int8alias1 (
20+
input = int8alias1in,
21+
output = int8alias1out,
22+
like = int8
23+
);
24+
create type int8alias2;
25+
create function int8alias2in(cstring) returns int8alias2
26+
strict immutable language internal as 'int8in';
27+
NOTICE: return type int8alias2 is only a shell
28+
create function int8alias2out(int8alias2) returns cstring
29+
strict immutable language internal as 'int8out';
30+
NOTICE: argument type int8alias2 is only a shell
31+
create type int8alias2 (
32+
input = int8alias2in,
33+
output = int8alias2out,
34+
like = int8
35+
);
36+
create cast (int8 as int8alias1) without function;
37+
create cast (int8 as int8alias2) without function;
38+
create cast (int8alias1 as int8) without function;
39+
create cast (int8alias2 as int8) without function;
40+
create function int8alias1eq(int8alias1, int8alias1) returns bool
41+
strict immutable language internal as 'int8eq';
42+
create operator = (
43+
procedure = int8alias1eq,
44+
leftarg = int8alias1, rightarg = int8alias1,
45+
commutator = =,
46+
restrict = eqsel, join = eqjoinsel,
47+
merges
48+
);
49+
alter operator family integer_ops using btree add
50+
operator 3 = (int8alias1, int8alias1);
51+
create function int8alias2eq(int8alias2, int8alias2) returns bool
52+
strict immutable language internal as 'int8eq';
53+
create operator = (
54+
procedure = int8alias2eq,
55+
leftarg = int8alias2, rightarg = int8alias2,
56+
commutator = =,
57+
restrict = eqsel, join = eqjoinsel,
58+
merges
59+
);
60+
alter operator family integer_ops using btree add
61+
operator 3 = (int8alias2, int8alias2);
62+
create function int8alias1eq(int8, int8alias1) returns bool
63+
strict immutable language internal as 'int8eq';
64+
create operator = (
65+
procedure = int8alias1eq,
66+
leftarg = int8, rightarg = int8alias1,
67+
restrict = eqsel, join = eqjoinsel,
68+
merges
69+
);
70+
alter operator family integer_ops using btree add
71+
operator 3 = (int8, int8alias1);
72+
create function int8alias1eq(int8alias1, int8alias2) returns bool
73+
strict immutable language internal as 'int8eq';
74+
create operator = (
75+
procedure = int8alias1eq,
76+
leftarg = int8alias1, rightarg = int8alias2,
77+
restrict = eqsel, join = eqjoinsel,
78+
merges
79+
);
80+
alter operator family integer_ops using btree add
81+
operator 3 = (int8alias1, int8alias2);
82+
create function int8alias1lt(int8alias1, int8alias1) returns bool
83+
strict immutable language internal as 'int8lt';
84+
create operator < (
85+
procedure = int8alias1lt,
86+
leftarg = int8alias1, rightarg = int8alias1
87+
);
88+
alter operator family integer_ops using btree add
89+
operator 1 < (int8alias1, int8alias1);
90+
create function int8alias1cmp(int8, int8alias1) returns int
91+
strict immutable language internal as 'btint8cmp';
92+
alter operator family integer_ops using btree add
93+
function 1 int8alias1cmp (int8, int8alias1);
94+
create table ec0 (ff int8 primary key, f1 int8, f2 int8);
95+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ec0_pkey" for table "ec0"
96+
create table ec1 (ff int8 primary key, f1 int8alias1, f2 int8alias2);
97+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ec1_pkey" for table "ec1"
98+
create table ec2 (xf int8 primary key, x1 int8alias1, x2 int8alias2);
99+
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ec2_pkey" for table "ec2"
100+
-- for the moment we only want to look at nestloop plans
101+
set enable_hashjoin = off;
102+
set enable_mergejoin = off;
103+
--
104+
-- Note that for cases where there's a missing operator, we don't care so
105+
-- much whether the plan is ideal as that we don't fail or generate an
106+
-- outright incorrect plan.
107+
--
108+
explain (costs off)
109+
select * from ec0 where ff = f1 and f1 = '42'::int8;
110+
QUERY PLAN
111+
----------------------------------
112+
Index Scan using ec0_pkey on ec0
113+
Index Cond: (ff = 42::bigint)
114+
Filter: (f1 = 42::bigint)
115+
(3 rows)
116+
117+
explain (costs off)
118+
select * from ec0 where ff = f1 and f1 = '42'::int8alias1;
119+
QUERY PLAN
120+
---------------------------------------
121+
Index Scan using ec0_pkey on ec0
122+
Index Cond: (ff = '42'::int8alias1)
123+
Filter: (f1 = '42'::int8alias1)
124+
(3 rows)
125+
126+
explain (costs off)
127+
select * from ec1 where ff = f1 and f1 = '42'::int8alias1;
128+
QUERY PLAN
129+
---------------------------------------
130+
Index Scan using ec1_pkey on ec1
131+
Index Cond: (ff = '42'::int8alias1)
132+
Filter: (f1 = '42'::int8alias1)
133+
(3 rows)
134+
135+
explain (costs off)
136+
select * from ec1 where ff = f1 and f1 = '42'::int8alias2;
137+
QUERY PLAN
138+
---------------------------------------------------
139+
Seq Scan on ec1
140+
Filter: ((ff = f1) AND (f1 = '42'::int8alias2))
141+
(2 rows)
142+
143+
explain (costs off)
144+
select * from ec1, ec2 where ff = x1 and ff = '42'::int8;
145+
QUERY PLAN
146+
---------------------------------------------------------------
147+
Nested Loop
148+
Join Filter: (ec1.ff = ec2.x1)
149+
-> Index Scan using ec1_pkey on ec1
150+
Index Cond: ((ff = 42::bigint) AND (ff = 42::bigint))
151+
-> Seq Scan on ec2
152+
(5 rows)
153+
154+
explain (costs off)
155+
select * from ec1, ec2 where ff = x1 and ff = '42'::int8alias1;
156+
QUERY PLAN
157+
---------------------------------------------
158+
Nested Loop
159+
-> Index Scan using ec1_pkey on ec1
160+
Index Cond: (ff = '42'::int8alias1)
161+
-> Seq Scan on ec2
162+
Filter: (ec2.x1 = '42'::int8alias1)
163+
(5 rows)
164+
165+
explain (costs off)
166+
select * from ec1, ec2 where ff = x1 and '42'::int8 = x1;
167+
QUERY PLAN
168+
----------------------------------------
169+
Nested Loop
170+
Join Filter: (ec1.ff = ec2.x1)
171+
-> Index Scan using ec1_pkey on ec1
172+
Index Cond: (ff = 42::bigint)
173+
-> Seq Scan on ec2
174+
Filter: (42::bigint = ec2.x1)
175+
(6 rows)
176+
177+
explain (costs off)
178+
select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias1;
179+
QUERY PLAN
180+
---------------------------------------------
181+
Nested Loop
182+
-> Index Scan using ec1_pkey on ec1
183+
Index Cond: (ff = '42'::int8alias1)
184+
-> Seq Scan on ec2
185+
Filter: (ec2.x1 = '42'::int8alias1)
186+
(5 rows)
187+
188+
explain (costs off)
189+
select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias2;
190+
QUERY PLAN
191+
-----------------------------------------
192+
Nested Loop
193+
-> Seq Scan on ec2
194+
Filter: (x1 = '42'::int8alias2)
195+
-> Index Scan using ec1_pkey on ec1
196+
Index Cond: (ec1.ff = ec2.x1)
197+
(5 rows)
198+
199+
create unique index ec1_expr1 on ec1((ff + 1));
200+
create unique index ec1_expr2 on ec1((ff + 2 + 1));
201+
create unique index ec1_expr3 on ec1((ff + 3 + 1));
202+
create unique index ec1_expr4 on ec1((ff + 4));
203+
explain (costs off)
204+
select * from ec1,
205+
(select ff + 1 as x from
206+
(select ff + 2 as ff from ec1
207+
union all
208+
select ff + 3 as ff from ec1) ss0
209+
union all
210+
select ff + 4 as x from ec1) as ss1
211+
where ss1.x = ec1.f1 and ec1.ff = 42::int8;
212+
QUERY PLAN
213+
-----------------------------------------------------------------
214+
Nested Loop
215+
Join Filter: ((((public.ec1.ff + 2) + 1)) = public.ec1.f1)
216+
-> Index Scan using ec1_pkey on ec1
217+
Index Cond: (ff = 42::bigint)
218+
-> Append
219+
-> Append
220+
-> Seq Scan on ec1
221+
-> Seq Scan on ec1
222+
-> Index Scan using ec1_expr4 on ec1
223+
Index Cond: ((public.ec1.ff + 4) = public.ec1.f1)
224+
(10 rows)
225+
226+
explain (costs off)
227+
select * from ec1,
228+
(select ff + 1 as x from
229+
(select ff + 2 as ff from ec1
230+
union all
231+
select ff + 3 as ff from ec1) ss0
232+
union all
233+
select ff + 4 as x from ec1) as ss1
234+
where ss1.x = ec1.f1 and ec1.ff = 42::int8 and ec1.ff = ec1.f1;
235+
QUERY PLAN
236+
--------------------------------------------------------------------
237+
Nested Loop
238+
Join Filter: ((((public.ec1.ff + 2) + 1)) = public.ec1.f1)
239+
-> Index Scan using ec1_pkey on ec1
240+
Index Cond: ((ff = 42::bigint) AND (ff = 42::bigint))
241+
Filter: (ff = f1)
242+
-> Append
243+
-> Index Scan using ec1_expr2 on ec1
244+
Index Cond: (((public.ec1.ff + 2) + 1) = 42::bigint)
245+
-> Index Scan using ec1_expr3 on ec1
246+
Index Cond: (((public.ec1.ff + 3) + 1) = 42::bigint)
247+
-> Index Scan using ec1_expr4 on ec1
248+
Index Cond: ((public.ec1.ff + 4) = 42::bigint)
249+
(12 rows)
250+
251+
explain (costs off)
252+
select * from ec1,
253+
(select ff + 1 as x from
254+
(select ff + 2 as ff from ec1
255+
union all
256+
select ff + 3 as ff from ec1) ss0
257+
union all
258+
select ff + 4 as x from ec1) as ss1,
259+
(select ff + 1 as x from
260+
(select ff + 2 as ff from ec1
261+
union all
262+
select ff + 3 as ff from ec1) ss0
263+
union all
264+
select ff + 4 as x from ec1) as ss2
265+
where ss1.x = ec1.f1 and ss1.x = ss2.x and ec1.ff = 42::int8;
266+
QUERY PLAN
267+
-----------------------------------------------------------------------------
268+
Nested Loop
269+
Join Filter: ((((public.ec1.ff + 2) + 1)) = (((public.ec1.ff + 2) + 1)))
270+
-> Append
271+
-> Seq Scan on ec1
272+
-> Seq Scan on ec1
273+
-> Seq Scan on ec1
274+
-> Materialize
275+
-> Nested Loop
276+
Join Filter: ((((public.ec1.ff + 2) + 1)) = public.ec1.f1)
277+
-> Index Scan using ec1_pkey on ec1
278+
Index Cond: (ff = 42::bigint)
279+
-> Append
280+
-> Append
281+
-> Seq Scan on ec1
282+
-> Seq Scan on ec1
283+
-> Index Scan using ec1_expr4 on ec1
284+
Index Cond: ((public.ec1.ff + 4) = public.ec1.f1)
285+
(17 rows)
286+
287+
-- check partially indexed scan
288+
drop index ec1_expr3;
289+
explain (costs off)
290+
select * from ec1,
291+
(select ff + 1 as x from
292+
(select ff + 2 as ff from ec1
293+
union all
294+
select ff + 3 as ff from ec1) ss0
295+
union all
296+
select ff + 4 as x from ec1) as ss1
297+
where ss1.x = ec1.f1 and ec1.ff = 42::int8;
298+
QUERY PLAN
299+
-----------------------------------------------------------------
300+
Nested Loop
301+
Join Filter: ((((public.ec1.ff + 2) + 1)) = public.ec1.f1)
302+
-> Index Scan using ec1_pkey on ec1
303+
Index Cond: (ff = 42::bigint)
304+
-> Append
305+
-> Append
306+
-> Seq Scan on ec1
307+
-> Seq Scan on ec1
308+
-> Index Scan using ec1_expr4 on ec1
309+
Index Cond: ((public.ec1.ff + 4) = public.ec1.f1)
310+
(10 rows)
311+

‎src/test/regress/parallel_schedule

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ test: rules
8484
# ----------
8585
# Another group of parallel tests
8686
# ----------
87-
test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap
87+
test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap equivclass
8888

8989
# ----------
9090
# Another group of parallel tests

‎src/test/regress/serial_schedule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ test: tsdicts
103103
test: foreign_data
104104
test: window
105105
test: xmlmap
106+
test: equivclass
106107
test: plancache
107108
test: limit
108109
test: plpgsql

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp