summaryrefslogtreecommitdiff
path: root/src/backend/optimizer
AgeCommit message (Collapse)Author
2008-12-31Add some basic support for window frame clauses to the window-functionsTom Lane
patch. This includes the ability to force the frame to cover the whole partition, and the ability to make the frame end exactly on the current row rather than its last ORDER BY peer. Supporting any more of the full SQL frame-clause syntax will require nontrivial hacking on the window aggregate code, so it'll have to wait for 8.5 or beyond.
2008-12-28Support window functions a la SQL:2008.Tom Lane
Hitoshi Harada, with some kibitzing from Heikki and Tom.
2008-12-18Code review for function default parameters patch. Fix numerous problems asTom Lane
per recent discussions. In passing this also fixes a couple of bugs in the previous variadic-parameters patch.
2008-12-08Don't try to optimize EXISTS subqueries with empty FROM-lists: we need toTom Lane
form a join and that case doesn't have anything to join to. (We could probably make it work if we didn't pull up the subquery, but it seems to me that the case isn't worth extra code.) Per report from Greg Stark.
2008-12-01Fix an oversight in the code that makes transitive-equality deductions fromTom Lane
outer join clauses. Given, say, ... from a left join b on a.a1 = b.b1 where a.a1 = 42; we'll deduce a clause b.b1 = 42 and then mark the original join clause redundant (we can't remove it completely for reasons I don't feel like squeezing into this log entry). However the original implementation of that wasn't bulletproof, because clause_selectivity() wouldn't honor this_selec if given nonzero varRelid --- which in practice meant that it worked as desired *except* when considering index scan quals. Which resulted in bogus underestimation of the size of the indexscan result for an inner indexscan in an outer join, and consequently a possibly bad choice of indexscan vs. bitmap scan. Fix by introducing an explicit test into clause_selectivity(). Also, to make sure we don't trigger that test in corner cases, change the convention to be that this_selec > 1, not this_selec = 1, means it's been marked redundant. Per trouble report from Scara Maccai. Back-patch to 8.2, where the problem was introduced.
2008-11-28My recent fix for semijoin planning didn't actually work for a semijoin with aTom Lane
RHS that can't be unique-ified --- join_is_legal has to check that before deciding to build a join, else we'll have an unimplementable joinrel. Per report from Greg Stark.
2008-11-22Switch the planner over to treating qualifications of a JOIN_SEMI join asTom Lane
though it is an inner rather than outer join type. This essentially means that we don't bother to separate "pushed down" qual conditions from actual join quals at a semijoin plan node; which is okay because the restrictions of SQL syntax make it impossible to have a pushed-down qual that references the inner side of a semijoin. This allows noticeably better optimization of IN/EXISTS cases than we had before, since the equivalence-class machinery can now use those quals. Also fix a couple of other mistakes that had essentially disabled the ability to unique-ify the inner relation and then join it to just a subset of the left-hand relations. An example case using the regression database is select * from tenk1 a, tenk1 b where (a.unique1,b.unique2) in (select unique1,unique2 from tenk1 c); which is planned reasonably well by 8.3 and earlier but had been forcing a cartesian join of a/b in CVS HEAD.
2008-11-20Fix breakage of bitmap scan plan creation for special index operators suchTom Lane
as LIKE. I oversimplified this code when removing support for plan-time determination of index operator lossiness back in April --- I had thought create_bitmap_subplan could stop returning two separate lists of qual conditions, but it still must so that we can treat special operators correctly in create_bitmap_scan_plan. Per report from Rushabh Lathia.
2008-11-15Make SELECT FOR UPDATE/SHARE work on inheritance trees, by having the planTom Lane
return the tableoid as well as the ctid for any FOR UPDATE targets that have child tables. All child tables are listed in the ExecRowMark list, but the executor just skips the ones that didn't produce the current row. Curiously, this longstanding restriction doesn't seem to have been documented anywhere; so no doc changes.
2008-11-13Arrange to cache the results of looking up a btree predicate proof comparisonTom Lane
operator. The result depends only on the two input operators and the proof direction (imply or refute), so it's easy to cache. This provides a very large savings in cases such as Sergey Konoplev's long NOT-IN-list example, where predtest spends all its time repeatedly figuring out that the same pair of operators cannot be used to prove anything. (But of course the O(N^2) behavior still catches up with you eventually.) I'm not convinced it buys a whole lot when constraint_exclusion isn't turned on, but it's not a lot of added code so we might as well cache all the time.
2008-11-12In predtest.c, install a limit on the number of branches we will process inTom Lane
AND, OR, or equivalent clauses: if there are too many (more than 100) just exit without proving anything. This ensures that we don't spend O(N^2) time trying (and most likely failing) to prove anything about very long IN lists and similar cases. Also, install a couple of CHECK_FOR_INTERRUPTS calls to ensure that a long proof attempt can be interrupted. Per gripe from Sergey Konoplev. Back-patch the whole patch to 8.2 and just the CHECK_FOR_INTERRUPTS addition to 8.1. (The rest of the patch doesn't apply cleanly, and since 8.1 doesn't show the complained-of behavior anyway, it doesn't seem necessary to work hard on it.)
2008-11-11Ensure that the phrels sets of PlaceHolderVars appearing in an AppendRelInfo'sTom Lane
translated_vars list get updated when pulling up an appendrel member. It's not clear that this really matters at present, since relatively little gets done with the outputs of an appendrel child relation; but it probably will come back to bite us sometime if we leave them with the wrong values.
2008-11-11Get rid of adjust_appendrel_attr_needed(), which has been broken ever sinceTom Lane
we extended the appendrel mechanism to support UNION ALL optimization. The reason nobody noticed was that we are not actually using attr_needed data for appendrel children; hence it seems more reasonable to rip it out than fix it. Back-patch to 8.2 because an Assert failure is possible in corner cases. Per examination of an example from Jim Nasby. In HEAD, also get rid of AppendRelInfo.col_mappings, which is quite inadequate to represent UNION ALL situations; depend entirely on translated_vars instead.
2008-11-02Remove all uses of the deprecated functions heap_formtuple, heap_modifytuple,Tom Lane
and heap_deformtuple in favor of the newer functions heap_form_tuple et al (which do the same things but use bool control flags instead of arbitrary char values). Eliminate the former duplicate coding of these functions, reducing the deprecated functions to mere wrappers around the newer ones. We can't get rid of them entirely because add-on modules probably still contain many instances of the old coding style. Kris Jurka
2008-10-25Be a little smarter about qual handling for semi-joins: a qual that mentionsTom Lane
only the outer side can be pushed down rather than having to be evaluated at the join.
2008-10-22Dept of better ideas: refrain from creating the planner's placeholder_listTom Lane
until vars are distributed to rels during query_planner() startup. We don't really need it before that, and not building it early has some advantages. First, we don't need to put it through the various preprocessing steps, which saves some cycles and eliminates the need for a number of routines to support PlaceHolderInfo nodes at all. Second, this means one less unused plan for any sub-SELECT appearing in a placeholder's expression, since we don't build placeholder_list until after sublink expansion is complete.
2008-10-21Add a concept of "placeholder" variables to the planner. These are variablesTom Lane
that represent some expression that we desire to compute below the top level of the plan, and then let that value "bubble up" as though it were a plain Var (ie, a column value). The immediate application is to allow sub-selects to be flattened even when they are below an outer join and have non-nullable output expressions. Formerly we couldn't flatten because such an expression wouldn't properly go to NULL when evaluated above the outer join. Now, we wrap it in a PlaceHolderVar and arrange for the actual evaluation to occur below the outer join. When the resulting Var bubbles up through the join, it will be set to NULL if necessary, yielding the correct results. This fixes a planner limitation that's existed since 7.1. In future we might want to use this mechanism to re-introduce some form of Hellerstein's "expensive functions" optimization, ie place the evaluation of an expensive function at the most suitable point in the plan tree.
2008-10-17Salvage a little bit of work from a failed patch: simplify and speed upTom Lane
set_rel_width(). The code had been catering for the possibility of different varnos in the relation targetlist, but this is impossible for a base relation (and if it were possible, putting all the widths in the same RelOptInfo would be wrong anyway).
2008-10-09Improve the recently-added code for inlining set-returning functions so thatTom Lane
it can handle functions returning setof record. The case was left undone originally, but it turns out to be simple to fix.
2008-10-07Extend CTE patch to support recursive UNION (ie, without ALL). TheTom Lane
implementation uses an in-memory hash table, so it will poop out for very large recursive results ... but the performance characteristics of a sort-based implementation would be pretty unpleasant too.
2008-10-06When expanding a whole-row Var into a RowExpr during ResolveNew(), attachTom Lane
the column alias names of the RTE referenced by the Var to the RowExpr. This is needed to allow ruleutils.c to correctly deparse FieldSelect nodes referencing such a construct. Per my recent bug report. Adding a field to RowExpr forces initdb (because of stored rules changes) so this solution is not back-patchable; which is unfortunate because 8.2 and 8.3 have this issue. But it only affects EXPLAIN for some pretty odd corner cases, so we can probably live without a solution for the back branches.
2008-10-04Implement SQL-standard WITH clauses, including WITH RECURSIVE.Tom Lane
There are some unimplemented aspects: recursive queries must use UNION ALL (should allow UNION too), and we don't have SEARCH or CYCLE clauses. These might or might not get done for 8.4, but even without them it's a pretty useful feature. There are also a couple of small loose ends and definitional quibbles, which I'll send a memo about to pgsql-hackers shortly. But let's land the patch now so we can get on with other development. Yoshiyuki Asaba, with lots of help from Tatsuo Ishii and Tom Lane
2008-09-12Skip opfamily check in eclass_matches_any_index() when the index isn't aTom Lane
btree. We can't easily tell whether clauses generated from the equivalence class could be used with such an index, so just assume that they might be. This bit of over-optimization prevented use of non-btree indexes for nestloop inner indexscans, in any case where the join uses an equality operator that is also a btree operator --- which in particular is typically true for hash indexes. Noted while trying to test the current hash index patch.
2008-09-09Improve the plan cache invalidation mechanism to make it invalidate plansTom Lane
when user-defined functions used in a plan are modified. Also invalidate plans when schemas, operators, or operator classes are modified; but for these cases we just invalidate everything rather than tracking exact dependencies, since these types of objects seldom change in a production database. Tom Lane; loosely based on a patch by Martin Pihlak.
2008-09-05Fix an oversight in the 8.2 patch that improved mergejoin performance byTom Lane
inserting a materialize node above an inner-side sort node, when the sort is expected to spill to disk. (The materialize protects the sort from having to support mark/restore, allowing it to do its final merge pass on-the-fly.) We neglected to teach cost_mergejoin about that hack, so it was failing to include the materialize's costs in the estimated cost of the mergejoin. The materialize's costs are generally going to be pretty negligible in comparison to the sort's, so this is only a small error and probably not worth back-patching; but it's still wrong. In the similar case where a materialize is inserted to protect an inner-side node that can't do mark/restore at all, it's still true that the materialize should not spill to disk, and so we should cost it cheaply rather than expensively. Noted while thinking about a question from Tom Raney.
2008-09-01Add a bunch of new error location reports to parse-analysis error messages.Tom Lane
There are still some weak spots around JOIN USING and relation alias lists, but most errors reported within backend/parser/ now have locations.
2008-08-28Extend the parser location infrastructure to include a location field inTom Lane
most node types used in expression trees (both before and after parse analysis). This allows us to place an error cursor in many situations where we formerly could not, because the information wasn't available beyond the very first level of parse analysis. There's a fair amount of work still to be done to persuade individual ereport() calls to actually include an error location, but this gets the initdb-forcing part of the work out of the way; and the situation is already markedly better than before for complaints about unimplementable implicit casts, such as CASE and UNION constructs with incompatible alternative data types. Per my proposal of a few days ago.
2008-08-26Teach eval_const_expressions() to simplify an ArrayCoerceExpr to a constantTom Lane
when its input is constant and the element coercion function is immutable (or nonexistent, ie, binary-coercible case). This is an oversight in the 8.3 implementation of ArrayCoerceExpr, and its result is that certain cases involving IN or NOT IN with constants don't get optimized as they should be. Per experimentation with an example from Ow Mun Heng.
2008-08-25Move exprType(), exprTypmod(), expression_tree_walker(), and related routinesTom Lane
into nodes/nodeFuncs, so as to reduce wanton cross-subsystem #includes inside the backend. There's probably more that should be done along this line, but this is a start anyway.
2008-08-22Arrange to convert EXISTS subqueries that are equivalent to hashable INTom Lane
subqueries into the same thing you'd have gotten from IN (except always with unknownEqFalse = true, so as to get the proper semantics for an EXISTS). I believe this fixes the last case within CVS HEAD in which an EXISTS could give worse performance than an equivalent IN subquery. The tricky part of this is that if the upper query probes the EXISTS for only a few rows, the hashing implementation can actually be worse than the default, and therefore we need to make a cost-based decision about which way to use. But at the time when the planner generates plans for subqueries, it doesn't really know how many times the subquery will be executed. The least invasive solution seems to be to generate both plans and postpone the choice until execution. Therefore, in a query that has been optimized this way, EXPLAIN will show two subplans for the EXISTS, of which only one will actually get executed. There is a lot more that could be done based on this infrastructure: in particular it's interesting to consider switching to the hash plan if we start out using the non-hashed plan but find a lot more upper rows going by than we expected. I have therefore left some minor inefficiencies in place, such as initializing both subplans even though we will currently only use one.
2008-08-20Marginal improvement in sublink planning: allow unknownEqFalse optimizationTom Lane
to be used for SubLinks that are underneath a top-level OR clause. Just as at the very top level of WHERE, it's not necessary to be accurate about whether the sublink returns FALSE or NULL, because either result has the same impact on whether the WHERE will succeed.
2008-08-20Fix obsolete comment. It's no longer the case that Param nodes don'tTom Lane
carry typmod.
2008-08-17Add some defenses against constant-FALSE outer join conditions. SinceTom Lane
eval_const_expressions will generally throw away anything that's ANDed with constant FALSE, what we're left with given an example like select * from tenk1 a where (unique1,0) in (select unique2,1 from tenk1 b); is a cartesian product computation, which is really not acceptable. This is a regression in CVS HEAD compared to previous releases, which were able to notice the impossible join condition in this case --- though not in some related cases that are also improved by this patch, such as select * from tenk1 a left join tenk1 b on (a.unique1=b.unique2 and 0=1); Fix by skipping evaluation of the appropriate side of the outer join in cases where it's demonstrably unnecessary.
2008-08-17Remove prohibition against SubLinks in the WHERE clause of an EXISTS subqueryTom Lane
that we're considering pulling up. I hadn't wanted to think through whether that could work during the first pass at this stuff. However, on closer inspection it seems to be safe enough.
2008-08-17Improve sublink pullup code to handle ANY/EXISTS sublinks that are at topTom Lane
level of a JOIN/ON clause, not only at top level of WHERE. (However, we can't do this in an outer join's ON clause, unless the ANY/EXISTS refers only to the nullable side of the outer join, so that it can effectively be pushed down into the nullable side.) Per request from Kevin Grittner. In passing, fix a bug in the initial implementation of EXISTS pullup: it would Assert if the EXIST's WHERE clause used a join alias variable. Since we haven't yet flattened join aliases when this transformation happens, it's necessary to include join relids in the computed set of RHS relids.
2008-08-16Clean up the loose ends in selectivity estimation left by my patch for semiTom Lane
and anti joins. To do this, pass the SpecialJoinInfo struct for the current join as an additional optional argument to operator join selectivity estimation functions. This allows the estimator to tell not only what kind of join is being formed, but which variable is on which side of the join; a requirement long recognized but not dealt with till now. This also leaves the door open for future improvements in the estimators, such as accounting for the null-insertion effects of lower outer joins. I didn't do anything about that in the current patch but the information is in principle deducible from what's passed. The patch also clarifies the definition of join selectivity for semi/anti joins: it's the fraction of the left input that has (at least one) match in the right input. This allows getting rid of some very fuzzy thinking that I had committed in the original 7.4-era IN-optimization patch. There's probably room to estimate this better than the present patch does, but at least we know what to estimate. Since I had to touch CREATE OPERATOR anyway to allow a variant signature for join estimator functions, I took the opportunity to add a couple of additional checks that were missing, per my recent message to -hackers: * Check that estimator functions return float8; * Require execute permission at the time of CREATE OPERATOR on the operator's function as well as the estimator functions; * Require ownership of any pre-existing operator that's modified by the command. I also moved the lookup of the functions out of OperatorCreate() and into operatorcmds.c, since that seemed more consistent with most of the other catalog object creation processes, eg CREATE TYPE.
2008-08-14Fix pull_up_simple_union_all to copy all rtable entries from child subquery toHeikki Linnakangas
parent, not only those with RangeTblRefs. We need them in ExecCheckRTPerms. Report by Brendan O'Shea. Back-patch to 8.2, where pull_up_simple_union_all was introduced.
2008-08-14Implement SEMI and ANTI joins in the planner and executor. (Semijoins replaceTom Lane
the old JOIN_IN code, but antijoins are new functionality.) Teach the planner to convert appropriate EXISTS and NOT EXISTS subqueries into semi and anti joins respectively. Also, LEFT JOINs with suitable upper-level IS NULL filters are recognized as being anti joins. Unify the InClauseInfo and OuterJoinInfo infrastructure into "SpecialJoinInfo". With that change, it becomes possible to associate a SpecialJoinInfo with every join attempt, which permits some cleanup of join selectivity estimation. That needs to be taken much further than this patch does, but the next step is to change the API for oprjoin selectivity functions, which seems like material for a separate patch. So for the moment the output size estimates for semi and especially anti joins are quite bogus.
2008-08-07Improve INTERSECT/EXCEPT hashing by realizing that we don't need to make anyTom Lane
hashtable entries for tuples that are found only in the second input: they can never contribute to the output. Furthermore, this implies that the planner should endeavor to put first the smaller (in number of groups) input relation for an INTERSECT. Implement that, and upgrade prepunion's estimation of the number of rows returned by setops so that there's some amount of sanity in the estimate of which one is smaller.
2008-08-07Support hashing for duplicate-elimination in INTERSECT and EXCEPT queries.Tom Lane
This completes my project of improving usage of hashing for duplicate elimination (aggregate functions with DISTINCT remain undone, but that's for some other day). As with the previous patches, this means we can INTERSECT/EXCEPT on datatypes that can hash but not sort, and it means that INTERSECT/EXCEPT without ORDER BY are no longer certain to produce sorted output.
2008-08-07Teach the system how to use hashing for UNION. (INTERSECT/EXCEPT will follow,Tom Lane
but seem like a separate patch since most of the remaining work is on the executor side.) I took the opportunity to push selection of the grouping operators for set operations into the parser where it belongs. Otherwise this is just a small exercise in making prepunion.c consider both alternatives. As with the recent DISTINCT patch, this means we can UNION on datatypes that can hash but not sort, and it means that UNION without ORDER BY is no longer certain to produce sorted output.
2008-08-05Department of second thoughts: fix newly-added code in planner.c to make realTom Lane
sure that DISTINCT ON does what it's supposed to, ie, sort by the full ORDER BY list before unique-ifying. The error seems masked in simple cases by the fact that query_planner won't return query pathkeys that only partially match the requested sort order, but I wouldn't want to bet that it couldn't be exposed in some way or other.
2008-08-05Improve SELECT DISTINCT to consider hash aggregation, as well as sort/uniq,Tom Lane
as methods for implementing the DISTINCT step. This eliminates the former performance gap between DISTINCT and GROUP BY, and also makes it possible to do SELECT DISTINCT on datatypes that only support hashing not sorting. SELECT DISTINCT ON is still always implemented by sorting; it would take executor changes to support hashing that, and it's not clear it's worth the trouble. This is a release-note-worthy incompatibility from previous PG versions, since SELECT DISTINCT can no longer be counted on to deliver sorted output without explicitly saying ORDER BY. (Anyone who can't cope with that can consider turning off enable_hashagg.) Several regression test queries needed to have ORDER BY added to preserve stable output order. I fixed the ones that manifested here, but there might be some other cases that show up on other platforms.
2008-08-03Make GROUP BY work properly for datatypes that only support hashing and notTom Lane
sorting. The infrastructure for this was all in place already; it's only necessary to fix the planner to not assume that sorting is always an available option.
2008-08-02Rearrange the querytree representation of ORDER BY/GROUP BY/DISTINCT itemsTom Lane
as per my recent proposal: 1. Fold SortClause and GroupClause into a single node type SortGroupClause. We were already relying on them to be struct-equivalent, so using two node tags wasn't accomplishing much except to get in the way of comparing items with equal(). 2. Add an "eqop" field to SortGroupClause to carry the associated equality operator. This is cheap for the parser to get at the same time it's looking up the sort operator, and storing it eliminates the need for repeated not-so-cheap lookups during planning. In future this will also let us represent GROUP/DISTINCT operations on datatypes that have hash opclasses but no btree opclasses (ie, they have equality but no natural sort order). The previous representation simply didn't work for that, since its only indicator of comparison semantics was a sort operator. 3. Add a hasDistinctOn boolean to struct Query to explicitly record whether the distinctClause came from DISTINCT or DISTINCT ON. This allows removing some complicated and not 100% bulletproof code that attempted to figure that out from the distinctClause alone. This patch doesn't in itself create any new capability, but it's necessary infrastructure for future attempts to use hash-based grouping for DISTINCT and UNION/INTERSECT/EXCEPT.
2008-07-31Fix parser so that we don't modify the user-written ORDER BY list in orderTom Lane
to represent DISTINCT or DISTINCT ON. This gets rid of a longstanding annoyance that a view or rule using SELECT DISTINCT will be dumped out with an overspecified ORDER BY list, and is one small step along the way to decoupling DISTINCT and ORDER BY enough so that hash-based implementation of DISTINCT will be possible. In passing, improve transformDistinctClause so that it doesn't reject duplicate DISTINCT ON items, as was reported by Steve Midgley a couple weeks ago.
2008-07-13Clean up the use of some page-header-access macros: principally, useTom Lane
SizeOfPageHeaderData instead of sizeof(PageHeaderData) in places where that makes the code clearer, and avoid casting between Page and PageHeader where possible. Zdenek Kotala, with some additional cleanup by Heikki Linnakangas. I did not apply the parts of the proposed patch that would have resulted in slightly changing the on-disk format of hash indexes; it seems to me that's not a win as long as there's any chance of having in-place upgrade for 8.4.
2008-07-10Tighten up SS_finalize_plan's computation of valid_params to exclude Params ofTom Lane
the current query level that aren't in fact output parameters of the current initPlans. (This means, for example, output parameters of regular subplans.) To make this work correctly for output parameters coming from sibling initplans requires rejiggering the API of SS_finalize_plan just a bit: we need the siblings to be visible to it, rather than hidden as SS_make_initplan_from_plan had been doing. This is really part of my response to bug #4290, but I concluded this part probably shouldn't be back-patched, since all that it's doing is to make a debugging cross-check tighter.
2008-07-10Fix mis-calculation of extParam/allParam sets for plan nodes, as seen inTom Lane
bug #4290. The fundamental bug is that masking extParam by outer_params, as finalize_plan had been doing, caused us to lose the information that an initPlan depended on the output of a sibling initPlan. On reflection the best thing to do seemed to be not to try to adjust outer_params for this case but get rid of it entirely. The only thing it was really doing for us was to filter out param IDs associated with SubPlan nodes, and that can be done (with greater accuracy) while processing individual SubPlan nodes in finalize_primnode. This approach was vindicated by the discovery that the masking method was hiding a second bug: SS_finalize_plan failed to remove extParam bits for initPlan output params that were referenced in the main plan tree (it only got rid of those referenced by other initPlans). It's not clear that this caused any real problems, given the limited use of extParam by the executor, but it's certainly not what was intended. I originally thought that there was also a problem with needing to include indirect dependencies on external params in initPlans' param sets, but it turns out that the executor handles this correctly so long as the depended-on initPlan is earlier in the initPlans list than the one using its output. That seems a bit of a fragile assumption, but it is true at the moment, so I just documented it in some code comments rather than making what would be rather invasive changes to remove the assumption. Back-patch to 8.1. Previous versions don't have the case of initPlans referring to other initPlans' outputs, so while the existing logic is still questionable for them, there are not any known bugs to be fixed. So I'll refrain from changing them for now.
2008-06-27Consider a clause to be outerjoin_delayed if it references the nullable sideTom Lane
of any lower outer join, even if it also references the non-nullable side and so could not get pushed below the outer join anyway. We need this in case the clause is an OR clause: if it doesn't get marked outerjoin_delayed, create_or_index_quals() could pull an indexable restriction for the nullable side out of it, leading to wrong results as demonstrated by today's bug report from toruvinn. (See added regression test case for an example.) In principle this has been wrong for quite a while. In practice I don't think any branch before 8.3 can really show the failure, because create_or_index_quals() will only pull out indexable conditions, and before 8.3 those were always strict. So though we might have improperly generated null-extended rows in the outer join, they'd get discarded from the result anyway. The gating factor that makes the failure visible is that 8.3 considers "col IS NULL" to be indexable. Hence I'm not going to risk back-patching further than 8.3.