OSDN Git Service

Fix crash bug caused by plancache invalidation
authorKyotaro Horiguchi <horikyota.ntt@gmail.com>
Fri, 14 Feb 2020 03:34:55 +0000 (12:34 +0900)
committerKyotaro Horiguchi <horikyota.ntt@gmail.com>
Fri, 14 Feb 2020 07:31:26 +0000 (16:31 +0900)
https://github.com/ossc-db/pg_hint_plan/issues/41

After plancache is invalidated then revaliated, get_query_string
accesses query_list of invalid plansource then crash. Ignore invalid
plancache and get the correct hint string and Query node at the next
planning time during revalidation.

On the way fixing this, a bug related to planner reentrance is
fixed. That fix causes behavioral change for nested
planning. Previously outer-level hint (wrongly) overrides inner-level
query but currenlty outer-level hint no longer affects inner-level
query.

expected/ut-A.out
pg_hint_plan.c
sql/ut-A.sql

index c35545f..b3a1870 100644 (file)
@@ -4571,3 +4571,93 @@ error hint:
                ->  Seq Scan on t2 t_2
 (7 rows)
 
+--No.14-1-1 plancache invalidation
+CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a;
+CREATE INDEX ON s1.tpc(a);
+PREPARE p1 AS SELECT * FROM s1.tpc WHERE a < 999;
+/*+ IndexScan(tpc) */PREPARE p2 AS SELECT * FROM s1.tpc WHERE a < 999;
+/*+ SeqScan(tpc) */PREPARE p3(int) AS SELECT * FROM s1.tpc WHERE a = $1;
+EXPLAIN EXECUTE p1;
+                      QUERY PLAN                      
+------------------------------------------------------
+ Seq Scan on tpc  (cost=0.00..17.50 rows=333 width=4)
+   Filter: (a < 999)
+(2 rows)
+
+EXPLAIN EXECUTE p2;
+LOG:  pg_hint_plan:
+used hint:
+IndexScan(tpc)
+not used hint:
+duplication hint:
+error hint:
+
+                               QUERY PLAN                               
+------------------------------------------------------------------------
+ Index Scan using tpc_a_idx on tpc  (cost=0.28..34.10 rows=333 width=4)
+   Index Cond: (a < 999)
+(2 rows)
+
+EXPLAIN EXECUTE p3(500);
+LOG:  pg_hint_plan:
+used hint:
+SeqScan(tpc)
+not used hint:
+duplication hint:
+error hint:
+
+                     QUERY PLAN                     
+----------------------------------------------------
+ Seq Scan on tpc  (cost=0.00..17.50 rows=5 width=4)
+   Filter: (a = 500)
+(2 rows)
+
+-- The DROP invalidates the plan caches
+DROP TABLE s1.tpc;
+EXPLAIN EXECUTE p1;
+ERROR:  relation "s1.tpc" does not exist
+EXPLAIN EXECUTE p2;
+ERROR:  relation "s1.tpc" does not exist
+EXPLAIN EXECUTE p3(500);
+ERROR:  relation "s1.tpc" does not exist
+CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a;
+CREATE INDEX ON s1.tpc(a);
+EXPLAIN EXECUTE p1;
+                      QUERY PLAN                      
+------------------------------------------------------
+ Seq Scan on tpc  (cost=0.00..17.50 rows=333 width=4)
+   Filter: (a < 999)
+(2 rows)
+
+EXPLAIN EXECUTE p2;
+LOG:  pg_hint_plan:
+used hint:
+IndexScan(tpc)
+not used hint:
+duplication hint:
+error hint:
+
+                               QUERY PLAN                               
+------------------------------------------------------------------------
+ Index Scan using tpc_a_idx on tpc  (cost=0.28..34.10 rows=333 width=4)
+   Index Cond: (a < 999)
+(2 rows)
+
+EXPLAIN EXECUTE p3(500);
+LOG:  pg_hint_plan:
+used hint:
+SeqScan(tpc)
+not used hint:
+duplication hint:
+error hint:
+
+                     QUERY PLAN                     
+----------------------------------------------------
+ Seq Scan on tpc  (cost=0.00..17.50 rows=5 width=4)
+   Filter: (a = 500)
+(2 rows)
+
+DEALLOCATE p1;
+DEALLOCATE p2;
+DEALLOCATE p3;
+DROP TABLE s1.tpc;
index a1c525a..eab627d 100644 (file)
@@ -1892,13 +1892,24 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
                        PreparedStatement  *entry;
 
                        entry = FetchPreparedStatement(stmt->name, true);
-                       p = entry->plansource->query_string;
-                       target_query = (Query *) linitial (entry->plansource->query_list);
+
+                       if (entry->plansource->is_valid)
+                       {
+                               p = entry->plansource->query_string;
+                               target_query = (Query *) linitial (entry->plansource->query_list);
+                       }
+                       else
+                       {
+                               /* igonre the hint for EXECUTE if invalidated */
+                               p = NULL;
+                               target_query = NULL;
+                       }
                }
                        
                /* JumbleQuery accespts only a non-utility Query */
-               if (!IsA(target_query, Query) ||
-                       target_query->utilityStmt != NULL)
+               if (target_query &&
+                       (!IsA(target_query, Query) ||
+                        target_query->utilityStmt != NULL))
                        target_query = NULL;
 
                if (jumblequery)
@@ -2925,6 +2936,14 @@ get_current_hint_string(ParseState *pstate, Query *query)
                current_hint_str = get_hints_from_comment(query_str);
                MemoryContextSwitchTo(oldcontext);
        }
+       else
+       {
+               /*
+                * Failed to get query. We would be in fetching invalidated
+                * plancache. Try the next chance.
+                */
+               current_hint_retrieved = false;
+       }
 
        if (debug_level > 1)
        {
@@ -2994,7 +3013,7 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
        int                             save_nestlevel;
        PlannedStmt        *result;
        HintState          *hstate;
-       const char         *prev_hint_str;
+       const char         *prev_hint_str = NULL;
 
        /*
         * Use standard planner if pg_hint_plan is disabled or current nesting 
index d25137a..38cecb7 100644 (file)
@@ -1228,3 +1228,27 @@ EXPLAIN (COSTS false)
  SELECT recall_planner() FROM s1.t1 t_1
    JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1)
   ORDER BY t_1.c1;
+
+--No.14-1-1 plancache invalidation
+CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a;
+CREATE INDEX ON s1.tpc(a);
+PREPARE p1 AS SELECT * FROM s1.tpc WHERE a < 999;
+/*+ IndexScan(tpc) */PREPARE p2 AS SELECT * FROM s1.tpc WHERE a < 999;
+/*+ SeqScan(tpc) */PREPARE p3(int) AS SELECT * FROM s1.tpc WHERE a = $1;
+EXPLAIN EXECUTE p1;
+EXPLAIN EXECUTE p2;
+EXPLAIN EXECUTE p3(500);
+-- The DROP invalidates the plan caches
+DROP TABLE s1.tpc;
+EXPLAIN EXECUTE p1;
+EXPLAIN EXECUTE p2;
+EXPLAIN EXECUTE p3(500);
+CREATE TABLE s1.tpc AS SELECT a FROM generate_series(0, 999) a;
+CREATE INDEX ON s1.tpc(a);
+EXPLAIN EXECUTE p1;
+EXPLAIN EXECUTE p2;
+EXPLAIN EXECUTE p3(500);
+DEALLOCATE p1;
+DEALLOCATE p2;
+DEALLOCATE p3;
+DROP TABLE s1.tpc;