Fix bug in translate_col_privs_multilevel
authorDavid Rowley <[email protected]>
Fri, 23 Dec 2022 11:58:34 +0000 (00:58 +1300)
committerDavid Rowley <[email protected]>
Fri, 23 Dec 2022 11:58:34 +0000 (00:58 +1300)
Fix incorrect code which was trying to convert a Bitmapset of columns at
the attnums according to a parent table and transform them into the
equivalent Bitmapset with same attnums according to the given child table.
This code is new as of a61b1f748 and was failing to do the correct
translation when there was an intermediate parent table between 'rel' and
'top_parent_rel'.

Reported-by: Ranier Vilela
Author: Richard Guo, Amit Langote
Discussion: https://postgr.es/m/CAEudQArohfB_Gy%2BhcH2-bANUkxgjJiP%3DABq01_LgTNTbcNijag%40mail.gmail.com

contrib/postgres_fdw/expected/postgres_fdw.out
contrib/postgres_fdw/sql/postgres_fdw.sql
src/backend/optimizer/util/inherit.c

index bd706d18d1b6543fc041b575faa44d4d4a77bd8d..1a2c2a665c66ab48c03e95a19a04437c0b6c26cf 100644 (file)
@@ -6768,6 +6768,42 @@ DROP TRIGGER row_before_insupd_trigger ON child_tbl;
 DROP TABLE parent_tbl CASCADE;
 NOTICE:  drop cascades to view rw_view
 DROP FUNCTION row_before_insupd_trigfunc;
+-- Try a more complex permutation of WCO where there are multiple levels of
+-- partitioned tables with columns not all in the same order
+CREATE TABLE parent_tbl (a int, b text, c numeric) PARTITION BY RANGE(a);
+CREATE TABLE sub_parent (c numeric, a int, b text) PARTITION BY RANGE(a);
+ALTER TABLE parent_tbl ATTACH PARTITION sub_parent FOR VALUES FROM (1) TO (10);
+CREATE TABLE child_local (b text, c numeric, a int);
+CREATE FOREIGN TABLE child_foreign (b text, c numeric, a int)
+  SERVER loopback OPTIONS (table_name 'child_local');
+ALTER TABLE sub_parent ATTACH PARTITION child_foreign FOR VALUES FROM (1) TO (10);
+CREATE VIEW rw_view AS SELECT * FROM parent_tbl WHERE a < 5 WITH CHECK OPTION;
+INSERT INTO parent_tbl (a) VALUES(1),(5);
+EXPLAIN (VERBOSE, COSTS OFF)
+UPDATE rw_view SET b = 'text', c = 123.456;
+                                           QUERY PLAN                                            
+-------------------------------------------------------------------------------------------------
+ Update on public.parent_tbl
+   Foreign Update on public.child_foreign parent_tbl_1
+     Remote SQL: UPDATE public.child_local SET b = $2, c = $3 WHERE ctid = $1 RETURNING a
+   ->  Foreign Scan on public.child_foreign parent_tbl_1
+         Output: 'text'::text, 123.456, parent_tbl_1.tableoid, parent_tbl_1.ctid, parent_tbl_1.*
+         Remote SQL: SELECT b, c, a, ctid FROM public.child_local WHERE ((a < 5)) FOR UPDATE
+(6 rows)
+
+UPDATE rw_view SET b = 'text', c = 123.456;
+SELECT * FROM parent_tbl ORDER BY a;
+ a |  b   |    c    
+---+------+---------
+ 1 | text | 123.456
+ 5 |      |        
+(2 rows)
+
+DROP VIEW rw_view;
+DROP TABLE child_local;
+DROP FOREIGN TABLE child_foreign;
+DROP TABLE sub_parent;
+DROP TABLE parent_tbl;
 -- ===================================================================
 -- test serial columns (ie, sequence-based defaults)
 -- ===================================================================
index 2e6f7f4852c2133fa9a8a5826c0c52bf0ad573ed..94fe69ed3b616d53a7ea05718dafcd4d83fd163f 100644 (file)
@@ -1580,6 +1580,29 @@ DROP TABLE parent_tbl CASCADE;
 
 DROP FUNCTION row_before_insupd_trigfunc;
 
+-- Try a more complex permutation of WCO where there are multiple levels of
+-- partitioned tables with columns not all in the same order
+CREATE TABLE parent_tbl (a int, b text, c numeric) PARTITION BY RANGE(a);
+CREATE TABLE sub_parent (c numeric, a int, b text) PARTITION BY RANGE(a);
+ALTER TABLE parent_tbl ATTACH PARTITION sub_parent FOR VALUES FROM (1) TO (10);
+CREATE TABLE child_local (b text, c numeric, a int);
+CREATE FOREIGN TABLE child_foreign (b text, c numeric, a int)
+  SERVER loopback OPTIONS (table_name 'child_local');
+ALTER TABLE sub_parent ATTACH PARTITION child_foreign FOR VALUES FROM (1) TO (10);
+CREATE VIEW rw_view AS SELECT * FROM parent_tbl WHERE a < 5 WITH CHECK OPTION;
+
+INSERT INTO parent_tbl (a) VALUES(1),(5);
+EXPLAIN (VERBOSE, COSTS OFF)
+UPDATE rw_view SET b = 'text', c = 123.456;
+UPDATE rw_view SET b = 'text', c = 123.456;
+SELECT * FROM parent_tbl ORDER BY a;
+
+DROP VIEW rw_view;
+DROP TABLE child_local;
+DROP FOREIGN TABLE child_foreign;
+DROP TABLE sub_parent;
+DROP TABLE parent_tbl;
+
 -- ===================================================================
 -- test serial columns (ie, sequence-based defaults)
 -- ===================================================================
index f51ce45cd3b39daab8f696604dafb31274573988..d3b0b0d2af931a7d4c2c5acebf6518d357e99d7b 100644 (file)
@@ -52,7 +52,7 @@ static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
 static Bitmapset *translate_col_privs_multilevel(PlannerInfo *root,
                                                                                                 RelOptInfo *rel,
                                                                                                 RelOptInfo *top_parent_rel,
-                                                                                                Bitmapset *top_parent_cols);
+                                                                                                Bitmapset *parent_cols);
 static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
                                                                          RangeTblEntry *rte, Index rti);
 
@@ -923,28 +923,26 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 
 /*
  * translate_col_privs_multilevel
- *             Recursively translates the column numbers contained in
- *             'top_parent_cols' to the columns numbers of a descendent relation
- *             given by 'relid'
+ *             Recursively translates the column numbers contained in 'parent_cols'
+ *             to the columns numbers of a descendent relation given by 'rel'
  */
 static Bitmapset *
 translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel,
                                                           RelOptInfo *top_parent_rel,
-                                                          Bitmapset *top_parent_cols)
+                                                          Bitmapset *parent_cols)
 {
-       Bitmapset  *result;
        AppendRelInfo *appinfo;
 
-       if (top_parent_cols == NULL)
+       if (parent_cols == NULL)
                return NULL;
 
        /* Recurse if immediate parent is not the top parent. */
        if (rel->parent != top_parent_rel)
        {
                if (rel->parent)
-                       result = translate_col_privs_multilevel(root, rel->parent,
-                                                                                                       top_parent_rel,
-                                                                                                       top_parent_cols);
+                       parent_cols = translate_col_privs_multilevel(root, rel->parent,
+                                                                                                                top_parent_rel,
+                                                                                                                parent_cols);
                else
                        elog(ERROR, "rel with relid %u is not a child rel", rel->relid);
        }
@@ -953,7 +951,5 @@ translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel,
        appinfo = root->append_rel_array[rel->relid];
        Assert(appinfo != NULL);
 
-       result = translate_col_privs(top_parent_cols, appinfo->translated_vars);
-
-       return result;
+       return translate_col_privs(parent_cols, appinfo->translated_vars);
 }