OSDN Git Service

Follow the semantic change of pg_list part 2
[pghintplan/pg_hint_plan.git] / pg_hint_plan.c
index a625896..29e3e6a 100644 (file)
@@ -3,7 +3,7 @@
  * pg_hint_plan.c
  *               hinting on how to execute a query for PostgreSQL
  *
- * Copyright (c) 2012-2019, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+ * Copyright (c) 2012-2020, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #include "partitioning/partbounds.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
+#include "utils/float.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
@@ -363,8 +364,8 @@ struct HintState
        int                             init_min_para_tablescan_size;
        /* min_parallel_index_scan_size*/
        int                             init_min_para_indexscan_size;
-       int                             init_paratup_cost;      /* parallel_tuple_cost */
-       int                             init_parasetup_cost;/* parallel_setup_cost */
+       double                  init_paratup_cost;      /* parallel_tuple_cost */
+       double                  init_parasetup_cost;/* parallel_setup_cost */
 
        PlannerInfo        *current_root;               /* PlannerInfo for the followings */
        Index                   parent_relid;           /* inherit parent of table relid */
@@ -404,8 +405,9 @@ static void pg_hint_plan_ProcessUtility(PlannedStmt *pstmt,
                                        const char *queryString,
                                        ProcessUtilityContext context,
                                        ParamListInfo params, QueryEnvironment *queryEnv,
-                                       DestReceiver *dest, char *completionTag);
-static PlannedStmt *pg_hint_plan_planner(Query *parse, int cursorOptions,
+                                       DestReceiver *dest, QueryCompletion *qc);
+static PlannedStmt *pg_hint_plan_planner(Query *parse, const char *query_string,
+                                                                                int cursorOptions,
                                                                                 ParamListInfo boundParams);
 static RelOptInfo *pg_hint_plan_join_search(PlannerInfo *root,
                                                                                        int levels_needed,
@@ -479,10 +481,11 @@ void pg_hint_plan_set_rel_pathlist(PlannerInfo * root, RelOptInfo *rel,
 static void create_plain_partial_paths(PlannerInfo *root,
                                                                                                        RelOptInfo *rel);
 static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel,
+                                                                         List *other_rels_list,
                                                                          ListCell *other_rels);
 static void make_rels_by_clauseless_joins(PlannerInfo *root,
                                                                                  RelOptInfo *old_rel,
-                                                                                 ListCell *other_rels);
+                                                                                 List *other_rels);
 static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
 static void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                                                   RangeTblEntry *rte);
@@ -506,6 +509,8 @@ static void setup_scan_method_enforcement(ScanMethodHint *scanhint,
                                                                                  HintState *state);
 static int set_config_int32_option(const char *name, int32 value,
                                                                        GucContext context);
+static int set_config_double_option(const char *name, double value,
+                                                                       GucContext context);
 
 /* GUC variables */
 static bool    pg_hint_plan_enable_hint = true;
@@ -1180,7 +1185,8 @@ RowsHintDesc(RowsHint *hint, StringInfo buf, bool nolf)
                        quote_value(buf, hint->relnames[i]);
                }
        }
-       appendStringInfo(buf, " %s", hint->rows_str);
+       if (hint->rows_str != NULL)
+               appendStringInfo(buf, " %s", hint->rows_str);
        appendStringInfoString(buf, ")");
        if (!nolf)
                appendStringInfoChar(buf, '\n');
@@ -1820,7 +1826,7 @@ get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
         * case of DESCRIBE message handling or EXECUTE command. We may still see a
         * candidate top-level query in pstate in the case.
         */
-       if (!p && pstate)
+       if (pstate && pstate->p_sourcetext)
                p = pstate->p_sourcetext;
 
        /* We don't see a query string, return NULL */
@@ -1887,13 +1893,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)
@@ -2064,7 +2081,7 @@ create_hintstate(Query *parse, const char *hints)
                hstate->num_hints[HINT_TYPE_LEADING]);
        hstate->rows_hints = (RowsHint **) (hstate->set_hints +
                hstate->num_hints[HINT_TYPE_SET]);
-       hstate->parallel_hints = (ParallelHint **) (hstate->set_hints +
+       hstate->parallel_hints = (ParallelHint **) (hstate->rows_hints +
                hstate->num_hints[HINT_TYPE_ROWS]);
 
        return hstate;
@@ -2358,6 +2375,8 @@ RowsHintParse(RowsHint *hint, HintState *hstate, Query *parse,
        List               *name_list = NIL;
        char               *rows_str;
        char               *end_ptr;
+       ListCell   *l;
+       int                     i = 0;
 
        if ((str = parse_parentheses(str, &name_list, hint_keyword)) == NULL)
                return NULL;
@@ -2365,23 +2384,28 @@ RowsHintParse(RowsHint *hint, HintState *hstate, Query *parse,
        /* Last element must be rows specification */
        hint->nrels = list_length(name_list) - 1;
 
-       if (hint->nrels > 0)
+       if (hint->nrels < 1)
        {
-               ListCell   *l;
-               int                     i = 0;
+               hint_ereport(str,
+                                        ("%s hint needs at least one relation followed by one correction term.",
+                                         hint->base.keyword));
+               hint->base.state = HINT_STATE_ERROR;
 
-               /*
-                * Transform relation names from list to array to sort them with qsort
-                * after.
-                */
-               hint->relnames = palloc(sizeof(char *) * hint->nrels);
-               foreach (l, name_list)
-               {
-                       if (hint->nrels <= i)
-                               break;
-                       hint->relnames[i] = lfirst(l);
-                       i++;
-               }
+               return str;
+       }
+       
+
+       /*
+        * Transform relation names from list to array to sort them with qsort
+        * after.
+        */
+       hint->relnames = palloc(sizeof(char *) * hint->nrels);
+       foreach (l, name_list)
+       {
+               if (hint->nrels <= i)
+                       break;
+               hint->relnames[i] = lfirst(l);
+               i++;
        }
 
        /* Retieve rows estimation */
@@ -2615,6 +2639,23 @@ set_config_int32_option(const char *name, int32 value, GucContext context)
                                                                  pg_hint_plan_parse_message_level);
 }
 
+/*
+ * Sets GUC parameter of double type without throwing exceptions. Returns false
+ * if something wrong.
+ */
+static int
+set_config_double_option(const char *name, double value, GucContext context)
+{
+       char *buf = float8out_internal(value);
+       int       result;
+
+       result = set_config_option_noerror(name, buf, context,
+                                                                          PGC_S_SESSION, GUC_ACTION_SAVE, true,
+                                                                          pg_hint_plan_parse_message_level);
+       pfree(buf);
+       return result;
+}
+
 /* setup scan method enforcement according to given options */
 static void
 setup_guc_enforcement(SetHint **options, int noptions, GucContext context)
@@ -2662,8 +2703,8 @@ setup_parallel_plan_enforcement(ParallelHint *hint, HintState *state)
        /* force means that enforce parallel as far as possible */
        if (hint && hint->force_parallel && hint->nworkers > 0)
        {
-               set_config_int32_option("parallel_tuple_cost", 0, state->context);
-               set_config_int32_option("parallel_setup_cost", 0, state->context);
+               set_config_double_option("parallel_tuple_cost", 0.0, state->context);
+               set_config_double_option("parallel_setup_cost", 0.0, state->context);
                set_config_int32_option("min_parallel_table_scan_size", 0,
                                                                state->context);
                set_config_int32_option("min_parallel_index_scan_size", 0,
@@ -2671,9 +2712,9 @@ setup_parallel_plan_enforcement(ParallelHint *hint, HintState *state)
        }
        else
        {
-               set_config_int32_option("parallel_tuple_cost",
+               set_config_double_option("parallel_tuple_cost",
                                                                state->init_paratup_cost, state->context);
-               set_config_int32_option("parallel_setup_cost",
+               set_config_double_option("parallel_setup_cost",
                                                                state->init_parasetup_cost, state->context);
                set_config_int32_option("min_parallel_table_scan_size",
                                                                state->init_min_para_tablescan_size,
@@ -2857,9 +2898,7 @@ get_current_hint_string(ParseState *pstate, Query *query)
                         */
                        query_len = strlen(query_str) + 1;
                        normalized_query =
-                               generate_normalized_query(&jstate, query_str,
-                                                                                 query->stmt_location,
-                                                                                 &query_len,
+                               generate_normalized_query(&jstate, query_str, 0, &query_len,
                                                                                  GetDatabaseEncoding());
 
                        /*
@@ -2920,6 +2959,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)
        {
@@ -2967,14 +3014,14 @@ static void
 pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                        ProcessUtilityContext context,
                                        ParamListInfo params, QueryEnvironment *queryEnv,
-                                       DestReceiver *dest, char *completionTag)
+                                       DestReceiver *dest, QueryCompletion *qc)
 {
        if (prev_ProcessUtility_hook)
                prev_ProcessUtility_hook(pstmt, queryString, context, params, queryEnv,
-                                                                dest, completionTag);
+                                                                dest, qc);
        else
                standard_ProcessUtility(pstmt, queryString, context, params, queryEnv,
-                                                                dest, completionTag);
+                                                                dest, qc);
 
        if (plpgsql_recurse_level == 0)
                current_hint_retrieved = false;
@@ -2984,12 +3031,12 @@ pg_hint_plan_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
  * Read and set up hint information
  */
 static PlannedStmt *
-pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
+pg_hint_plan_planner(Query *parse, const char *query_string, 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 
@@ -3014,7 +3061,8 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
         * Support for nested plpgsql functions. This is quite ugly but this is the
         * only point I could find where I can get the query string.
         */
-       if (plpgsql_recurse_level > 0)
+       if (plpgsql_recurse_level > 0 &&
+               error_context_stack && error_context_stack->arg)
        {
                MemoryContext oldcontext;
 
@@ -3088,6 +3136,7 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
         */
        recurse_level++;
        prev_hint_str = current_hint_str;
+       current_hint_str = NULL;
        
        /*
         * Use PG_TRY mechanism to recover GUC parameters and current_hint_state to
@@ -3096,9 +3145,11 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
        PG_TRY();
        {
                if (prev_planner)
-                       result = (*prev_planner) (parse, cursorOptions, boundParams);
+                       result = (*prev_planner) (parse, query_string,
+                                                                         cursorOptions, boundParams);
                else
-                       result = standard_planner(parse, cursorOptions, boundParams);
+                       result = standard_planner(parse, query_string,
+                                                                         cursorOptions, boundParams);
 
                current_hint_str = prev_hint_str;
                recurse_level--;
@@ -3154,9 +3205,17 @@ standard_planner_proc:
        }
        current_hint_state = NULL;
        if (prev_planner)
-               return (*prev_planner) (parse, cursorOptions, boundParams);
+               result =  (*prev_planner) (parse, query_string,
+                                                                  cursorOptions, boundParams);
        else
-               return standard_planner(parse, cursorOptions, boundParams);
+               result = standard_planner(parse, query_string,
+                                                                 cursorOptions, boundParams);
+
+       /* The upper-level planner still needs the current hint state */
+       if (HintStateStack != NIL)
+               current_hint_state = (HintState *) lfirst(list_head(HintStateStack));
+
+       return result;
 }
 
 /*
@@ -3337,7 +3396,6 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel,
                           bool using_parent_hint)
 {
        ListCell           *cell;
-       ListCell           *prev;
        ListCell           *next;
        StringInfoData  buf;
        RangeTblEntry  *rte = root->simple_rte_array[rel->relid];
@@ -3367,7 +3425,6 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel,
         * Leaving only an specified index, we delete it from a IndexOptInfo list
         * other than it.
         */
-       prev = NULL;
        if (debug_level > 0)
                initStringInfo(&buf);
 
@@ -3378,8 +3435,7 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel,
                ListCell           *l;
                bool                    use_index = false;
 
-               next = lnext(cell);
-
+               next = lnext(rel->indexlist, cell);
                foreach(l, hint->indexnames)
                {
                        char   *hintname = (char *) lfirst(l);
@@ -3549,10 +3605,18 @@ restrict_indexes(PlannerInfo *root, ScanMethodHint *hint, RelOptInfo *rel,
                }
 
                if (!use_index)
-                       rel->indexlist = list_delete_cell(rel->indexlist, cell, prev);
-               else
-                       prev = cell;
+               {
+                       rel->indexlist = list_delete_cell(rel->indexlist, cell);
 
+                       /*
+                        * The cells after the deleted cell have been moved towards the
+                        * list head by 1 element.  the next iteration should visit the
+                        * cell at the same address if any.
+                        */
+                       if (next)
+                               next = cell;
+               }
+                       
                pfree(indexname);
        }
 
@@ -3732,32 +3796,50 @@ setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel,
                return 0;
        }
 
-       /* Forget about the parent of another subquery */
-       if (root != current_hint_state->current_root)
-               current_hint_state->parent_relid = 0;
-
-       /* Find the parent for this relation other than the registered parent */
-       foreach (l, root->append_rel_list)
+       /*
+        * Forget about the parent of another subquery, but don't forget if the
+        * inhTargetkind of the root is not INHKIND_NONE, which signals the root
+        * contains only appendrel members. See inheritance_planner for details.
+        *
+        * (PG12.0) 428b260f87 added one more planning cycle for updates on
+        * partitioned tables and hints set up in the cycle are overriden by the
+        * second cycle. Since I didn't find no apparent distinction between the
+        * PlannerRoot of the cycle and that of ordinary CMD_SELECT, pg_hint_plan
+        * accepts both cycles and the later one wins. In the second cycle root
+        * doesn't have inheritance information at all so use the parent_relid set
+        * in the first cycle.
+        */
+       if (root->inhTargetKind == INHKIND_NONE)
        {
-               AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+               if (root != current_hint_state->current_root)
+                       current_hint_state->parent_relid = 0;
 
-               if (appinfo->child_relid == rel->relid)
+               /* Find the parent for this relation other than the registered parent */
+               foreach (l, root->append_rel_list)
                {
-                       if (current_hint_state->parent_relid != appinfo->parent_relid)
+                       AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+
+                       if (appinfo->child_relid == rel->relid)
                        {
-                               new_parent_relid = appinfo->parent_relid;
-                               current_hint_state->current_root = root;
+                               if (current_hint_state->parent_relid != appinfo->parent_relid)
+                               {
+                                       new_parent_relid = appinfo->parent_relid;
+                                       current_hint_state->current_root = root;
+                               }
+                               break;
                        }
-                       break;
                }
-       }
 
-       if (!l)
-       {
-               /* This relation doesn't have a parent. Cancel current_hint_state. */
-               current_hint_state->parent_relid = 0;
-               current_hint_state->parent_scan_hint = NULL;
-               current_hint_state->parent_parallel_hint = NULL;
+               if (!l)
+               {
+                       /*
+                        * This relation doesn't have a parent. Cancel
+                        * current_hint_state.
+                        */
+                       current_hint_state->parent_relid = 0;
+                       current_hint_state->parent_scan_hint = NULL;
+                       current_hint_state->parent_parallel_hint = NULL;
+               }
        }
 
        if (new_parent_relid > 0)
@@ -3794,7 +3876,7 @@ setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel,
 
                                parentrel_oid =
                                        root->simple_rte_array[current_hint_state->parent_relid]->relid;
-                               parent_rel = heap_open(parentrel_oid, NoLock);
+                               parent_rel = table_open(parentrel_oid, NoLock);
 
                                /* Search the parent relation for indexes match the hint spec */
                                foreach(l, RelationGetIndexList(parent_rel))
@@ -3818,7 +3900,7 @@ setup_hint_enforcement(PlannerInfo *root, RelOptInfo *rel,
                                                lappend(current_hint_state->parent_index_infos,
                                                                parent_index_info);
                                }
-                               heap_close(parent_rel, NoLock);
+                               table_close(parent_rel, NoLock);
                        }
                }
        }
@@ -3998,8 +4080,8 @@ OuterInnerJoinCreate(OuterInnerRels *outer_inner, LeadingHint *leading_hint,
                                                                                 leading_hint->base.hint_str));
        }
 
-       outer_rels = lfirst(outer_inner->outer_inner_pair->head);
-       inner_rels = lfirst(outer_inner->outer_inner_pair->tail);
+       outer_rels = linitial(outer_inner->outer_inner_pair);
+       inner_rels = llast(outer_inner->outer_inner_pair);
 
        outer_relids = OuterInnerJoinCreate(outer_rels,
                                                                                leading_hint,
@@ -4306,14 +4388,13 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
                {
                        if (hstate->join_hint_level[i] != NIL)
                        {
-                               ListCell *prev = NULL;
                                ListCell *next = NULL;
                                for(l = list_head(hstate->join_hint_level[i]); l; l = next)
                                {
 
                                        JoinMethodHint *hint = (JoinMethodHint *)lfirst(l);
 
-                                       next = lnext(l);
+                                       next = lnext(hstate->join_hint_level[i], l);
 
                                        if (hint->inner_nrels == 0 &&
                                                !(bms_intersect(hint->joinrelids, joinrelids) == NULL ||
@@ -4321,11 +4402,16 @@ transform_join_hints(HintState *hstate, PlannerInfo *root, int nbaserel,
                                                  hint->joinrelids)))
                                        {
                                                hstate->join_hint_level[i] =
-                                                       list_delete_cell(hstate->join_hint_level[i], l,
-                                                                                        prev);
+                                                       list_delete_cell(hstate->join_hint_level[i], l);
+                                               /*
+                                                * The cells after the deleted cell have been moved
+                                                * towards the list head by 1 element.  the next
+                                                * iteration should visit the cell at the same address
+                                                * if any.
+                                                */
+                                               if (next)
+                                                       next = l;
                                        }
-                                       else
-                                               prev = l;
                                }
                        }
                }