aboutsummaryrefslogtreecommitdiff
path: root/src/include/nodes
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2019-03-07 14:21:52 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2019-03-07 14:21:52 -0500
commit925f46ffb82f0b25c94e7997caff732eaf14367d (patch)
tree3842ea3cc5a366c2c5229d7a6c49b4fcaf45319d /src/include/nodes
parentdadf9814d0dddaa3e5a301e2b13479431108bc5b (diff)
downloadpostgresql-925f46ffb82f0b25c94e7997caff732eaf14367d.tar.gz
postgresql-925f46ffb82f0b25c94e7997caff732eaf14367d.zip
Fix handling of targetlist SRFs when scan/join relation is known empty.
When we introduced separate ProjectSetPath nodes for application of set-returning functions in v10, we inadvertently broke some cases where we're supposed to recognize that the result of a subquery is known to be empty (contain zero rows). That's because IS_DUMMY_REL was just looking for a childless AppendPath without allowing for a ProjectSetPath being possibly stuck on top. In itself, this didn't do anything much worse than produce slightly worse plans for some corner cases. Then in v11, commit 11cf92f6e rearranged things to allow the scan/join targetlist to be applied directly to partial paths before they get gathered. But it inserted a short-circuit path for dummy relations that was a little too short: it failed to insert a ProjectSetPath node at all for a targetlist containing set-returning functions, resulting in bogus "set-valued function called in context that cannot accept a set" errors, as reported in bug #15669 from Madelaine Thibaut. The best way to fix this mess seems to be to reimplement IS_DUMMY_REL so that it drills down through any ProjectSetPath nodes that might be there (and it seems like we'd better allow for ProjectionPath as well). While we're at it, make it look at rel->pathlist not cheapest_total_path, so that it gives the right answer independently of whether set_cheapest has been done lately. That dependency looks pretty shaky in the context of code like apply_scanjoin_target_to_paths, and even if it's not broken today it'd certainly bite us at some point. (Nastily, unsafe use of the old coding would almost always work; the hazard comes down to possibly looking through a dangling pointer, and only once in a blue moon would you find something there that resulted in the wrong answer.) It now looks like it was a mistake for IS_DUMMY_REL to be a macro: if there are any extensions using it, they'll continue to use the old inadequate logic until they're recompiled, after which they'll fail to load into server versions predating this fix. Hopefully there are few such extensions. Having fixed IS_DUMMY_REL, the special path for dummy rels in apply_scanjoin_target_to_paths is unnecessary as well as being wrong, so we can just drop it. Also change a few places that were testing for partitioned-ness of a planner relation but not using IS_PARTITIONED_REL for the purpose; that seems unsafe as well as inconsistent, plus it required an ugly hack in apply_scanjoin_target_to_paths. In passing, save a few cycles in apply_scanjoin_target_to_paths by skipping processing of pre-existing paths for partitioned rels, and do some cosmetic cleanup and comment adjustment in that function. I renamed IS_DUMMY_PATH to IS_DUMMY_APPEND with the intention of breaking any code that might be using it, since in almost every case that would be wrong; IS_DUMMY_REL is what to be using instead. In HEAD, also make set_dummy_rel_pathlist static (since it's no longer used from outside allpaths.c), and delete is_dummy_plan, since it's no longer used anywhere. Back-patch as appropriate into v11 and v10. Tom Lane and Julien Rouhaud Discussion: https://postgr.es/m/15669-02fb3296cca26203@postgresql.org
Diffstat (limited to 'src/include/nodes')
-rw-r--r--src/include/nodes/relation.h25
1 files changed, 14 insertions, 11 deletions
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index adb42650479..e61c1a2a295 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -711,15 +711,12 @@ typedef struct RelOptInfo
*
* It's not enough to test whether rel->part_scheme is set, because it might
* be that the basic partitioning properties of the input relations matched
- * but the partition bounds did not.
- *
- * We treat dummy relations as unpartitioned. We could alternatively
- * treat them as partitioned, but it's not clear whether that's a useful thing
- * to do.
+ * but the partition bounds did not. Also, if we are able to prove a rel
+ * dummy (empty), we should henceforth treat it as unpartitioned.
*/
#define IS_PARTITIONED_REL(rel) \
((rel)->part_scheme && (rel)->boundinfo && (rel)->nparts > 0 && \
- (rel)->part_rels && !(IS_DUMMY_REL(rel)))
+ (rel)->part_rels && !IS_DUMMY_REL(rel))
/*
* Convenience macro to make sure that a partitioned relation has all the
@@ -1312,6 +1309,9 @@ typedef struct CustomPath
* elements. These cases are optimized during create_append_plan.
* In particular, an AppendPath with no subpaths is a "dummy" path that
* is created to represent the case that a relation is provably empty.
+ * (This is a convenient representation because it means that when we build
+ * an appendrel and find that all its children have been excluded, no extra
+ * action is needed to recognize the relation as dummy.)
*/
typedef struct AppendPath
{
@@ -1324,13 +1324,16 @@ typedef struct AppendPath
int first_partial_path;
} AppendPath;
-#define IS_DUMMY_PATH(p) \
+#define IS_DUMMY_APPEND(p) \
(IsA((p), AppendPath) && ((AppendPath *) (p))->subpaths == NIL)
-/* A relation that's been proven empty will have one path that is dummy */
-#define IS_DUMMY_REL(r) \
- ((r)->cheapest_total_path != NULL && \
- IS_DUMMY_PATH((r)->cheapest_total_path))
+/*
+ * A relation that's been proven empty will have one path that is dummy
+ * (but might have projection paths on top). For historical reasons,
+ * this is provided as a macro that wraps is_dummy_rel().
+ */
+#define IS_DUMMY_REL(r) is_dummy_rel(r)
+extern bool is_dummy_rel(RelOptInfo *rel);
/*
* MergeAppendPath represents a MergeAppend plan, ie, the merging of sorted