From 07fba9ad9b71c87a63f87a0ff16b6165ff08fc5e Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 22 Feb 2019 12:23:00 -0500 Subject: Fix plan created for inherited UPDATE/DELETE with all tables excluded. In the case where inheritance_planner() finds that every table has been excluded by constraints, it thought it could get away with making a plan consisting of just a dummy Result node. While certainly there's no updating or deleting to be done, this had two user-visible problems: the plan did not report the correct set of output columns when a RETURNING clause was present, and if there were any statement-level triggers that should be fired, it didn't fire them. Hence, rather than only generating the dummy Result, we need to stick a valid ModifyTable node on top, which requires a tad more effort here. It's been broken this way for as long as inheritance_planner() has known about deleting excluded subplans at all (cf commit 635d42e9c), so back-patch to all supported branches. Amit Langote and Tom Lane, per a report from Petr Fedorov. Discussion: https://postgr.es/m/5da6f0f0-1364-1876-6978-907678f89a3e@phystech.edu --- src/backend/optimizer/plan/planner.c | 68 ++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 22 deletions(-) (limited to 'src/backend/optimizer/plan/planner.c') diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 7f1f962f60a..bbf355dbf2c 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1570,34 +1570,58 @@ inheritance_planner(PlannerInfo *root) * to get control here. */ - /* - * If we managed to exclude every child rel, return a dummy plan; it - * doesn't even need a ModifyTable node. - */ if (subpaths == NIL) { - set_dummy_rel_pathlist(final_rel); - return; - } + /* + * We managed to exclude every child rel, so generate a dummy path + * representing the empty set. Although it's clear that no data will + * be updated or deleted, we will still need to have a ModifyTable + * node so that any statement triggers are executed. (This could be + * cleaner if we fixed nodeModifyTable.c to support zero child nodes, + * but that probably wouldn't be a net win.) + */ + List *tlist; + Path *dummy_path; - /* - * Put back the final adjusted rtable into the master copy of the Query. - * (We mustn't do this if we found no non-excluded children.) - */ - parse->rtable = final_rtable; - root->simple_rel_array_size = save_rel_array_size; - root->simple_rel_array = save_rel_array; - root->append_rel_array = save_append_rel_array; + /* tlist processing never got done, either */ + tlist = root->processed_tlist = preprocess_targetlist(root); + final_rel->reltarget = create_pathtarget(root, tlist); - /* Must reconstruct master's simple_rte_array, too */ - root->simple_rte_array = (RangeTblEntry **) - palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *)); - rti = 1; - foreach(lc, final_rtable) + /* Make a dummy path, cf set_dummy_rel_pathlist() */ + dummy_path = (Path *) create_append_path(NULL, final_rel, NIL, NIL, + NULL, 0, false, NIL, -1); + + /* These lists must be nonempty to make a valid ModifyTable node */ + subpaths = list_make1(dummy_path); + subroots = list_make1(root); + resultRelations = list_make1_int(parse->resultRelation); + if (parse->withCheckOptions) + withCheckOptionLists = list_make1(parse->withCheckOptions); + if (parse->returningList) + returningLists = list_make1(parse->returningList); + } + else { - RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); + /* + * Put back the final adjusted rtable into the master copy of the + * Query. (We mustn't do this if we found no non-excluded children, + * since we never saved an adjusted rtable at all.) + */ + parse->rtable = final_rtable; + root->simple_rel_array_size = save_rel_array_size; + root->simple_rel_array = save_rel_array; + root->append_rel_array = save_append_rel_array; + + /* Must reconstruct master's simple_rte_array, too */ + root->simple_rte_array = (RangeTblEntry **) + palloc0((list_length(final_rtable) + 1) * sizeof(RangeTblEntry *)); + rti = 1; + foreach(lc, final_rtable) + { + RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); - root->simple_rte_array[rti++] = rte; + root->simple_rte_array[rti++] = rte; + } } /* -- cgit v1.2.3