Re: Wrong rows estimations with joins of CTEs slows queries by more than factor 500

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Wrong rows estimations with joins of CTEs slows queries by more than factor 500
Дата
Msg-id 1450797.1704580874@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Wrong rows estimations with joins of CTEs slows queries by more than factor 500  (Alexander Lakhin <exclusion@gmail.com>)
Ответы Re: Wrong rows estimations with joins of CTEs slows queries by more than factor 500  (Richard Guo <guofenglinux@gmail.com>)
Список pgsql-hackers
Alexander Lakhin <exclusion@gmail.com> writes:
> Please look at the following query:
> CREATE TABLE t(i int);
> INSERT INTO t VALUES (1);
> VACUUM ANALYZE t;

> WITH ir AS (INSERT INTO t VALUES (2) RETURNING i)
> SELECT * FROM ir WHERE i = 2;

> which produces ERROR:  no relation entry for relid 1
> starting from f7816aec2.

Thanks for the report!  I guess we need something like the attached.
I'm surprised that this hasn't been noticed before; was the case
really unreachable before?

            regards, tom lane

diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5a9ce44532..22d01cef5b 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -420,6 +420,19 @@ find_base_rel(PlannerInfo *root, int relid)
     return NULL;                /* keep compiler quiet */
 }

+/*
+ * find_base_rel_noerr
+ *      Find a base or otherrel relation entry, returning NULL if there's none
+ */
+RelOptInfo *
+find_base_rel_noerr(PlannerInfo *root, int relid)
+{
+    /* use an unsigned comparison to prevent negative array element access */
+    if ((uint32) relid < (uint32) root->simple_rel_array_size)
+        return root->simple_rel_array[relid];
+    return NULL;
+}
+
 /*
  * find_base_rel_ignore_join
  *      Find a base or otherrel relation entry, which must already exist.
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index dbcd98d985..cea777e9d4 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -119,6 +119,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "parser/parse_clause.h"
+#include "parser/parse_relation.h"
 #include "parser/parsetree.h"
 #include "statistics/statistics.h"
 #include "storage/bufmgr.h"
@@ -5434,17 +5435,30 @@ examine_simple_variable(PlannerInfo *root, Var *var,

         if (HeapTupleIsValid(vardata->statsTuple))
         {
-            RelOptInfo *onerel = find_base_rel(root, var->varno);
+            RelOptInfo *onerel = find_base_rel_noerr(root, var->varno);
             Oid            userid;

             /*
              * Check if user has permission to read this column.  We require
              * all rows to be accessible, so there must be no securityQuals
-             * from security barrier views or RLS policies.  Use
-             * onerel->userid if it's set, in case we're accessing the table
-             * via a view.
+             * from security barrier views or RLS policies.
+             *
+             * Normally the Var will have an associated RelOptInfo from which
+             * we can find out which userid to do the check as; but it might
+             * not if it's a RETURNING Var for an INSERT target relation.  In
+             * that case use the RTEPermissionInfo associated with the RTE.
              */
-            userid = OidIsValid(onerel->userid) ? onerel->userid : GetUserId();
+            if (onerel)
+                userid = onerel->userid;
+            else
+            {
+                RTEPermissionInfo *perminfo;
+
+                perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
+                userid = perminfo->checkAsUser;
+            }
+            if (!OidIsValid(userid))
+                userid = GetUserId();

             vardata->acl_ok =
                 rte->securityQuals == NIL &&
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 03a0e46e70..c43d97b48a 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -307,6 +307,7 @@ extern void expand_planner_arrays(PlannerInfo *root, int add_size);
 extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
                                     RelOptInfo *parent);
 extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
+extern RelOptInfo *find_base_rel_noerr(PlannerInfo *root, int relid);
 extern RelOptInfo *find_base_rel_ignore_join(PlannerInfo *root, int relid);
 extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids);
 extern RelOptInfo *build_join_rel(PlannerInfo *root,
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 69c56ce207..3cf969d938 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -654,6 +654,24 @@ select count(*) from tenk1 a
                ->  CTE Scan on x
 (8 rows)

+explain (costs off)
+with x as materialized (insert into tenk1 default values returning unique1)
+select count(*) from tenk1 a
+  where unique1 in (select * from x);
+                         QUERY PLAN
+------------------------------------------------------------
+ Aggregate
+   CTE x
+     ->  Insert on tenk1
+           ->  Result
+   ->  Nested Loop
+         ->  HashAggregate
+               Group Key: x.unique1
+               ->  CTE Scan on x
+         ->  Index Only Scan using tenk1_unique1 on tenk1 a
+               Index Cond: (unique1 = x.unique1)
+(10 rows)
+
 -- SEARCH clause
 create temp table graph0( f int, t int, label text );
 insert into graph0 values
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index 3ef9898866..ff68e21e2e 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -354,6 +354,11 @@ with x as materialized (select unique1 from tenk1 b)
 select count(*) from tenk1 a
   where unique1 in (select * from x);

+explain (costs off)
+with x as materialized (insert into tenk1 default values returning unique1)
+select count(*) from tenk1 a
+  where unique1 in (select * from x);
+
 -- SEARCH clause

 create temp table graph0( f int, t int, label text );

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: Fix bogus Asserts in calc_non_nestloop_required_outer
Следующее
От: Geoff Winkless
Дата:
Сообщение: Re: weird GROUPING SETS and ORDER BY behaviour