This is an automated email from the ASF dual-hosted git repository. maxyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudberry.git
commit cdad9dc3916e2b342846c9589a21f995132a08af Author: Dean Rasheed <[email protected]> AuthorDate: Thu Feb 23 10:57:46 2023 +0000 Fix multi-row DEFAULT handling for INSERT ... SELECT rules. Given an updatable view with a DO ALSO INSERT ... SELECT rule, a multi-row INSERT ... VALUES query on the view fails if the VALUES list contains any DEFAULTs that are not replaced by view defaults. This manifests as an "unrecognized node type" error, or an Assert failure, in an assert-enabled build. The reason is that when RewriteQuery() attempts to replace the remaining DEFAULT items with NULLs in any product queries, using rewriteValuesRTEToNulls(), it assumes that the VALUES RTE is located at the same rangetable index in each product query. However, if the product query is an INSERT ... SELECT, then the VALUES RTE is actually in the SELECT part of that query (at the same index), rather than the top-level product query itself. Fix, by descending to the SELECT in such cases. Note that we can't simply use getInsertSelectQuery() for this, since that expects to be given a raw rule action with OLD and NEW placeholder entries, so we duplicate its logic instead. While at it, beef up the checks in getInsertSelectQuery() by checking that the jointree->fromlist node is indeed a RangeTblRef, and that the RTE it points to has rtekind == RTE_SUBQUERY. Per bug #17803, from Alexander Lakhin. Back-patch to all supported branches. Dean Rasheed, reviewed by Tom Lane. Discussion: https://postgr.es/m/17803-53c63ed4ecb4eac6%40postgresql.org (cherry picked from commit 98b83b7349821b05134e6f50f516ecac878cb91d) --- src/backend/rewrite/rewriteHandler.c | 32 ++++++++++++++++++++-- src/backend/rewrite/rewriteManip.c | 11 +++++--- src/test/regress/expected/updatable_views.out | 19 +++++++++++++ .../regress/expected/updatable_views_optimizer.out | 19 +++++++++++++ src/test/regress/sql/updatable_views.sql | 10 +++++++ 5 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 4b98f8f28c..33868f5b4e 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1637,7 +1637,6 @@ rewriteValuesRTEToNulls(Query *parsetree, RangeTblEntry *rte) List *newValues; ListCell *lc; - Assert(rte->rtekind == RTE_VALUES); newValues = NIL; foreach(lc, rte->values_lists) { @@ -3894,12 +3893,39 @@ RewriteQuery(Query *parsetree, List *rewrite_events) /* * Each product query has its own copy of the VALUES RTE at the * same index in the rangetable, so we must finalize each one. + * + * Note that if the product query is an INSERT ... SELECT, then + * the VALUES RTE will be at the same index in the SELECT part of + * the product query rather than the top-level product query + * itself. */ foreach(n, product_queries) { Query *pt = (Query *) lfirst(n); - RangeTblEntry *values_rte = rt_fetch(values_rte_index, - pt->rtable); + RangeTblEntry *values_rte; + + if (pt->commandType == CMD_INSERT && + pt->jointree && IsA(pt->jointree, FromExpr) && + list_length(pt->jointree->fromlist) == 1) + { + Node *jtnode = (Node *) linitial(pt->jointree->fromlist); + + if (IsA(jtnode, RangeTblRef)) + { + int rtindex = ((RangeTblRef *) jtnode)->rtindex; + RangeTblEntry *src_rte = rt_fetch(rtindex, pt->rtable); + + if (src_rte->rtekind == RTE_SUBQUERY && + src_rte->subquery && + IsA(src_rte->subquery, Query) && + src_rte->subquery->commandType == CMD_SELECT) + pt = src_rte->subquery; + } + } + + values_rte = rt_fetch(values_rte_index, pt->rtable); + if (values_rte->rtekind != RTE_VALUES) + elog(ERROR, "failed to find VALUES RTE in product query"); rewriteValuesRTEToNulls(pt, values_rte); } diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 50a641945d..ecd7a3ff06 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -1000,12 +1000,15 @@ getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr) if (list_length(parsetree->jointree->fromlist) != 1) elog(ERROR, "expected to find SELECT subquery"); rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist); - Assert(IsA(rtr, RangeTblRef)); + if (!IsA(rtr, RangeTblRef)) + elog(ERROR, "expected to find SELECT subquery"); selectrte = rt_fetch(rtr->rtindex, parsetree->rtable); - selectquery = selectrte->subquery; - if (!(selectquery && IsA(selectquery, Query) && - selectquery->commandType == CMD_SELECT)) + if (!(selectrte->rtekind == RTE_SUBQUERY && + selectrte->subquery && + IsA(selectrte->subquery, Query) && + selectrte->subquery->commandType == CMD_SELECT)) elog(ERROR, "expected to find SELECT subquery"); + selectquery = selectrte->subquery; if (list_length(selectquery->rtable) >= 2 && strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname, "old") == 0 && diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out index 9a46eb0e21..e6642dc4e4 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -3048,6 +3048,25 @@ select * from base_tab_def order by a, c NULLS LAST; | View default | | View default | (22 rows) +-- Test a DO ALSO INSERT ... SELECT rule +drop rule base_tab_def_view_ins_rule on base_tab_def_view; +create rule base_tab_def_view_ins_rule as on insert to base_tab_def_view + do also insert into base_tab_def (a, b, e) select new.a, new.b, 'xxx'; +truncate base_tab_def; +insert into base_tab_def_view values (1, default, default, default, default); +insert into base_tab_def_view values (2, default, default, default, default), + (3, default, default, default, default); +select * from base_tab_def order by a, e nulls first; + a | b | c | d | e +---+--------------+---------------+--------------+----- + 1 | View default | Table default | View default | + 1 | View default | Table default | | xxx + 2 | View default | Table default | View default | + 2 | View default | Table default | | xxx + 3 | View default | Table default | View default | + 3 | View default | Table default | | xxx +(6 rows) + drop view base_tab_def_view; drop table base_tab_def; -- Test defaults with array assignments diff --git a/src/test/regress/expected/updatable_views_optimizer.out b/src/test/regress/expected/updatable_views_optimizer.out index 35838b814d..4f7e45cad0 100644 --- a/src/test/regress/expected/updatable_views_optimizer.out +++ b/src/test/regress/expected/updatable_views_optimizer.out @@ -3076,6 +3076,25 @@ select * from base_tab_def order by a, c NULLS LAST; | View default | | View default | (22 rows) +-- Test a DO ALSO INSERT ... SELECT rule +drop rule base_tab_def_view_ins_rule on base_tab_def_view; +create rule base_tab_def_view_ins_rule as on insert to base_tab_def_view + do also insert into base_tab_def (a, b, e) select new.a, new.b, 'xxx'; +truncate base_tab_def; +insert into base_tab_def_view values (1, default, default, default, default); +insert into base_tab_def_view values (2, default, default, default, default), + (3, default, default, default, default); +select * from base_tab_def order by a, e nulls first; + a | b | c | d | e +---+--------------+---------------+--------------+----- + 1 | View default | Table default | View default | + 1 | View default | Table default | | xxx + 2 | View default | Table default | View default | + 2 | View default | Table default | | xxx + 3 | View default | Table default | View default | + 3 | View default | Table default | | xxx +(6 rows) + drop view base_tab_def_view; drop table base_tab_def; -- Test defaults with array assignments diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql index bbfbf4f1cc..4e3f3c88e5 100644 --- a/src/test/regress/sql/updatable_views.sql +++ b/src/test/regress/sql/updatable_views.sql @@ -1600,6 +1600,16 @@ insert into base_tab_def_view values (15, default, default, default, default), insert into base_tab_def_view values (17), (default); select * from base_tab_def order by a, c NULLS LAST; +-- Test a DO ALSO INSERT ... SELECT rule +drop rule base_tab_def_view_ins_rule on base_tab_def_view; +create rule base_tab_def_view_ins_rule as on insert to base_tab_def_view + do also insert into base_tab_def (a, b, e) select new.a, new.b, 'xxx'; +truncate base_tab_def; +insert into base_tab_def_view values (1, default, default, default, default); +insert into base_tab_def_view values (2, default, default, default, default), + (3, default, default, default, default); +select * from base_tab_def order by a, e nulls first; + drop view base_tab_def_view; drop table base_tab_def; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
