Thread
Commits
-
Fix planner failures with overlapping mergejoin clauses in an outer join.
- f6dd08489c80 9.4.17 landed
- e7c02a54a486 9.6.8 landed
- c458970ad5ea 10.3 landed
- 9afd513df042 11.0 landed
- 7dc5f316fd48 9.5.12 landed
- 71a0d0c5aad2 9.3.22 landed
-
ERROR: left and right pathkeys do not match in mergejoin
Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru> — 2018-02-21T21:04:45Z
Hi hackers, I found a bug related to the planning of merge joins. The steps to reproduce are as follows: ``` CREATE TABLE J1_TBL ( i integer, j integer, t text ); CREATE TABLE J2_TBL ( i integer, k integer ); set enable_hashjoin to off; explain select * from j1_tbl full join (select * from j2_tbl order by j2_tbl.i desc, j2_tbl.k) j2_tbl on j1_tbl.i = j2_tbl.i and j1_tbl.i = j2_tbl.k; ERROR: left and right pathkeys do not match in mergejoin ``` It can be reproduced on the latest 9.6, 10 and devel versions. Basically, j2_tbl is used as the outer relation in merge join. We try to use the existing order of j2_tbl, that is, 'i2 desc' and 'k asc'. As a result, there is no order for i1 that would allow it to be merge-joined to both i2 and k. The corresponding code path begins in `generate_mergejoin_pathkeys()`, when it is first called by `match_unsorted_outer()`. `find_mergeclauses_for_pathkeys()` selects both mergeclauses for the given outer pathkeys, and then `make_inner_pathkeys_for_merge()` generates inner pathkey for the first mergeclause. Inner pathkey for the second mergeclause is skipped as redundant. It looks suspicious already, because this new pathkey has different direction and is more like conflicting than redundant. Finally, `create_mergejoin_plan()` detects that the inner pathkey doesn't match the second mergeclause and throws the error. I found this while working on the inequality merge join patch. I don't know yet how to fix this, so any help is welcome. -- Alexander Kuzmenkov Postgres Professional: http://www.postgrespro.com The Russian Postgres Company
-
Re: ERROR: left and right pathkeys do not match in mergejoin
Tom Lane <tgl@sss.pgh.pa.us> — 2018-02-22T18:03:13Z
Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru> writes: > explain select * from j1_tbl full join (select * from j2_tbl order by > j2_tbl.i desc, j2_tbl.k) j2_tbl on j1_tbl.i = j2_tbl.i and j1_tbl.i = > j2_tbl.k; > ERROR: left and right pathkeys do not match in mergejoin Nice example. There are several places that we could consider trying to fix this: The first possibility is to teach find_mergeclauses_for_pathkeys that it should not select both of the join clauses in a case like this, perhaps based on noting that the associated pathkeys have the same eclass but different sort orders. However, that seems like a loser for the reason specified in the comment in that function: we need to select a maximal list of mergeclauses, not a minimal list, because if it's a full join and we aren't able to include all the clauses as mergeclauses then we won't have a valid plan. (At the time this code was written, that could have resulted in failure to produce a plan at all. Now we could fall back to a hash full join ... but that might be inefficient, or we might not have hashable operators.) The next possibility is to fix it in make_inner_pathkeys_for_merge, by having it not suppress lower-order pathkeys unless they match earlier ones in all details (not just eclass). In an example like this, that means emitting a redundant inner pathkey list, equivalent to "ORDER BY j1_tbl.i DESC, j1_tbl.i ASC". That's kind of annoying. It seems to work in a simple test, but it implies doing useless comparisons during the sort (since we'd only reach comparing the second sort column if the first sort column compares equal, whereupon the second must too). And it means representing the inner sort order by a noncanonical pathkey list, which is not nice. For example, even if we have an inner path available that delivers rows ordered by "j1_tbl.i DESC", we'd still think we have to physically sort it to add the extra sort key. (And no, I don't want to change pathkey comparison rules to avoid that.) The third possibility is to decide that create_mergejoin_plan is being overly paranoid and it's okay to extract merge details from a "redundant" path key even though it specifies the opposite sort order from what the current merge clause seems to need. This is scary at first glance, but it seems like it should work. We'd essentially be lying to the executor about the sort ordering of the lower-order merge column, and trusting that it won't take any wrong actions as a result because it should never reach comparison of that lower-order column except when the higher column(s) are equal. I can't see a reason for that to go wrong; it's basically a restatement of the argument why the lower-order sort key can be considered redundant in the first place. But it doesn't leave a warm feeling in the pit of the stomach, especially for a bug fix that needs to be back-patched a long way. On balance, though, this last choice seems like the thing to do. There are clear downsides to the first two, in terms of failing to construct a plan or constructing a needlessly inefficient plan. regards, tom lane
-
Re: ERROR: left and right pathkeys do not match in mergejoin
Tom Lane <tgl@sss.pgh.pa.us> — 2018-02-22T19:52:04Z
I wrote: > The third possibility is to decide that create_mergejoin_plan is being > overly paranoid and it's okay to extract merge details from a "redundant" > path key even though it specifies the opposite sort order from what the > current merge clause seems to need. This is scary at first glance, but > it seems like it should work. BTW, while working through this, I realized that there's an additional problem in the same area, which can be demonstrated thus in the regression database: explain select * from (select * from tenk1 a order by a.unique2) a right join (select * from tenk1 b order by b.thousand, b.twothousand, b.fivethous) b on a.unique2 = b.thousand and a.hundred = b.twothousand and a.unique2 = b.fivethous; ERROR: outer pathkeys do not match mergeclauses The problem here is that find_mergeclauses_for_pathkeys has an API designed on the assumption that the outer and inner pathkeys have identical relationships to the mergeclauses, ie, if you want to find the mergeclauses to use given a particular available sort ordering, it works basically the same way for outer or inner pathkeys. But per this discussion, tain't so. What really happens is that initially we choose a list of mergeclauses that line up with the outer pathkeys, and then we identify inner pathkeys that make sense given that list of mergeclauses; but, in the presence of redundancy, those inner pathkeys are not necessarily one-for-one with the mergeclauses. All fine so far. But then, generate_mergejoin_paths looks at inner paths that have available sort keys that are truncations of the original inner pathkey list, and it uses find_mergeclauses_for_pathkeys to decide which mergeclauses still make sense for that truncated pathkey list. That doesn't work. In my example, we consider the pre-ordered sub-select output for B as the outer side, and we choose all three mergeclauses in the order the example has them, and we arrive at the initial inner pathkey list of ({a.unique2}, {a.hundred}), dropping the redundant second appearance of {a.unique2}. We can successfully make a plan from that. But then we consider the truncated inner pathkey list ({a.unique2}), which this example is set up to ensure will win since it avoids an extra sort step. As the code stands, the mergeclause list that's extracted to use with that pair of input paths is a.unique2 = b.thousand and a.unique2 = b.fivethous (from the find_mergeclauses_for_pathkeys call with outer_keys = false). That's just wrong, because it doesn't match the already-determined outer path order, and create_mergejoin_plan is quite right to whine. So it seems like find_mergeclauses_for_pathkeys should be renamed to find_mergeclauses_for_outer_pathkeys, and then we need a second function for the task of truncating the outer mergeclauses --- not selecting them from scratch --- given a truncated inner pathkey list. In this example, we could keep a.unique2 = b.thousand, but we'd have to drop a.hundred = b.twothousand and then everything after it, since the inner path doesn't have the sort order needed for that mergeclause. regards, tom lane -
Re: ERROR: left and right pathkeys do not match in mergejoin
Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru> — 2018-02-22T23:05:10Z
El 22/02/18 a las 21:03, Tom Lane escribió: > The third possibility is to decide that create_mergejoin_plan is being > overly paranoid and it's okay to extract merge details from a "redundant" > path key even though it specifies the opposite sort order from what the > current merge clause seems to need. This check looks necessary to me, because merge join can't work with such a combination of outer pathkeys and merge clauses. The order of outer and inner column must be the same. Here, two outer columns with different orders are compared to the same inner column, so there will be mismatch one way or another. Consider the original query with some sample data: outer inner desc asc desc i2 k2 i1 ------------------ -> 1 0 1 <- 1 1 0 1 2 0 The merge join starts at tuples marked with arrows. It finds that the outer tuple is less than the inner, so it advances the inner pointer, and loses the tuple with i1 = 1. So, if we can't do a merge join on these clauses with these pathkeys anyway, it is OK to discard some of them in find_mergeclauses_for_pathkey. This helps the case when we use the existing order of outer relation. Also, we should take care not to create such combinations in select_outer_pathkeys_for_merge, when it tries to match root->query_pathkeys. -- Alexander Kuzmenkov Postgres Professional: http://www.postgrespro.com The Russian Postgres Company
-
Re: ERROR: left and right pathkeys do not match in mergejoin
Tom Lane <tgl@sss.pgh.pa.us> — 2018-02-22T23:59:58Z
Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru> writes: >> The third possibility is to decide that create_mergejoin_plan is being >> overly paranoid and it's okay to extract merge details from a "redundant" >> path key even though it specifies the opposite sort order from what the >> current merge clause seems to need. > This check looks necessary to me, because merge join can't work with > such a combination of outer pathkeys and merge clauses. No, I think it's okay, for exactly the same reason that the pathkey got thrown away as redundant in the first place. That is, imagine that we are mergejoining on "o.a = i.x AND o.b = i.x", and we know that the sort ordering of the outer relation is "a ASC, b DESC". It would appear that this means the inner relation needs to be ordered like "x ASC, x DESC", and certainly if we were to play dumb and sort it that way, you'd expect everything to work. The reason that we're having trouble is that the pathkey machinery knows that "x ASC, x DESC" is stupid, and it throws away the second column. It would not matter whether we considered the sort ordering of the inner relation to be "x ASC, x DESC", or just "x ASC", or indeed "x ASC, x ASC": all of those describe exactly the same row ordering, because we only get to comparing the second sort column for rows having equal values in the first sort column, and then our conclusion must still be that the rows' values are equal. This logic still goes through if the sort column contents are not simply duplicate variables but distinct variables that have gotten put into the same eclass, that is "SELECT ... WHERE x = y ORDER BY x ASC, y DESC". Equal is equal. (We go to some pains to be sure this is true, ie that the equality operator's semantics agree with the ordering operator's.) So essentially, we can lie to the executor and say that we sorted the inner rel as "x ASC, x DESC", even though we really did no such thing, because there will be no difference in the actual input row order from what it would be if we had done that. Attached is a completed patch that fixes this and also deals with the overall topic that the inner and outer pathkeys aren't so interchangeable as some parts of the code thought. Maybe the comments could use further improvement but I think it's OK otherwise. I haven't started on back-porting this, but the bugs are demonstrable back to 8.3, so that needs to be done. regards, tom lane
-
Re: ERROR: left and right pathkeys do not match in mergejoin
Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru> — 2018-02-23T11:16:30Z
Ah, I understand now. We can lie to the executor about the order, because when we are moving based on the second outer column, we have a stretch of equal values in the inner column, so we can consider them to be sorted whatever way we need. The patch applies cleanly, make check-world passes. In create_mergejoin_plan: * implied inner ordering is then "ORDER BY x, y, x", but the pathkey * drops the second sort by x as redundant, and this code must cope. -- should this read "the pathkey machinery drops"? -- Alexander Kuzmenkov Postgres Professional: http://www.postgrespro.com The Russian Postgres Company
-
Re: ERROR: left and right pathkeys do not match in mergejoin
Tom Lane <tgl@sss.pgh.pa.us> — 2018-02-23T18:48:47Z
Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru> writes: > In create_mergejoin_plan: > * implied inner ordering is then "ORDER BY x, y, x", but the pathkey > * drops the second sort by x as redundant, and this code must cope. > -- should this read "the pathkey machinery drops"? I changed that and improved some other comments, and pushed it. Thanks for reviewing! regards, tom lane