LCOV - code coverage report
Current view: top level - src/test/modules/test_plan_advice - test_plan_advice.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.9 % 33 30
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 54.2 % 24 13

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * test_plan_advice.c
       4                 :             :  *    Test pg_plan_advice by planning every query with generated advice.
       5                 :             :  *
       6                 :             :  * With this module loaded, every time a query is executed, we end up
       7                 :             :  * planning it twice. The first time we plan it, we generate plan advice,
       8                 :             :  * which we then feed back to pg_plan_advice as the supplied plan advice.
       9                 :             :  * It is then planned a second time using that advice. This hopefully
      10                 :             :  * allows us to detect cases where the advice is incorrect or causes
      11                 :             :  * failures or plan changes for some reason.
      12                 :             :  *
      13                 :             :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
      14                 :             :  *
      15                 :             :  *    src/test/modules/test_plan_advice/test_plan_advice.c
      16                 :             :  *
      17                 :             :  *-------------------------------------------------------------------------
      18                 :             :  */
      19                 :             : #include "postgres.h"
      20                 :             : 
      21                 :             : #include "access/xact.h"
      22                 :             : #include "fmgr.h"
      23                 :             : #include "optimizer/optimizer.h"
      24                 :             : #include "pg_plan_advice.h"
      25                 :             : #include "utils/guc.h"
      26                 :             : 
      27                 :           1 : PG_MODULE_MAGIC;
      28                 :             : 
      29                 :             : static bool in_recursion = false;
      30                 :             : 
      31                 :             : static char *test_plan_advice_advisor(PlannerGlobal *glob,
      32                 :             :                                       Query *parse,
      33                 :             :                                       const char *query_string,
      34                 :             :                                       int cursorOptions,
      35                 :             :                                       ExplainState *es);
      36                 :             : static DefElem *find_defelem_by_defname(List *deflist, char *defname);
      37                 :             : 
      38                 :             : /*
      39                 :             :  * Initialize this module.
      40                 :             :  */
      41                 :             : void
      42                 :           1 : _PG_init(void)
      43                 :             : {
      44                 :             :     void        (*add_advisor_fn) (pg_plan_advice_advisor_hook hook);
      45                 :             : 
      46                 :             :     /*
      47                 :             :      * Ask pg_plan_advice to get advice strings from test_plan_advice_advisor
      48                 :             :      */
      49                 :           1 :     add_advisor_fn =
      50                 :           1 :         load_external_function("pg_plan_advice", "pg_plan_advice_add_advisor",
      51                 :             :                                true, NULL);
      52                 :             : 
      53                 :           1 :     (*add_advisor_fn) (test_plan_advice_advisor);
      54                 :           1 : }
      55                 :             : 
      56                 :             : /*
      57                 :             :  * Re-plan the given query and return the generated advice string as the
      58                 :             :  * supplied advice.
      59                 :             :  */
      60                 :             : static char *
      61                 :       89200 : test_plan_advice_advisor(PlannerGlobal *glob, Query *parse,
      62                 :             :                          const char *query_string, int cursorOptions,
      63                 :             :                          ExplainState *es)
      64                 :             : {
      65                 :             :     PlannedStmt *pstmt;
      66                 :       89200 :     int         save_nestlevel = 0;
      67                 :             :     DefElem    *pgpa_item;
      68                 :             :     DefElem    *advice_string_item;
      69                 :             : 
      70                 :             :     /*
      71                 :             :      * Since this function is called from the planner and triggers planning,
      72                 :             :      * we need a recursion guard.
      73                 :             :      */
      74         [ +  + ]:       89200 :     if (in_recursion)
      75                 :       44633 :         return NULL;
      76                 :             : 
      77         [ +  + ]:       44567 :     PG_TRY();
      78                 :             :     {
      79                 :       44567 :         in_recursion = true;
      80                 :             : 
      81                 :             :         /*
      82                 :             :          * Planning can trigger expression evaluation, which can result in
      83                 :             :          * sending NOTICE messages or other output to the client. To avoid
      84                 :             :          * that, we set client_min_messages = ERROR in the hopes of getting
      85                 :             :          * the same output with and without this module.
      86                 :             :          *
      87                 :             :          * We also need to set pg_plan_advice.always_store_advice_details so
      88                 :             :          * that pg_plan_advice will generate an advice string, since the whole
      89                 :             :          * point of this function is to get access to that.
      90                 :             :          */
      91                 :       44567 :         save_nestlevel = NewGUCNestLevel();
      92                 :       44567 :         set_config_option("client_min_messages", "error",
      93                 :             :                           PGC_SUSET, PGC_S_SESSION,
      94                 :             :                           GUC_ACTION_SAVE, true, 0, false);
      95                 :       44567 :         set_config_option("pg_plan_advice.always_store_advice_details", "true",
      96                 :             :                           PGC_SUSET, PGC_S_SESSION,
      97                 :             :                           GUC_ACTION_SAVE, true, 0, false);
      98                 :             : 
      99                 :             :         /*
     100                 :             :          * Replan. We must copy the Query, because the planner modifies it.
     101                 :             :          * (As noted elsewhere, that's unfortunate; perhaps it will be fixed
     102                 :             :          * some day.)
     103                 :             :          */
     104                 :       44567 :         pstmt = planner(copyObject(parse), query_string, cursorOptions,
     105                 :             :                         glob->boundParams, es);
     106                 :             :     }
     107                 :         775 :     PG_FINALLY();
     108                 :             :     {
     109                 :       44567 :         in_recursion = false;
     110                 :             :     }
     111         [ +  + ]:       44567 :     PG_END_TRY();
     112                 :             : 
     113                 :             :     /* Roll back any GUC changes */
     114         [ +  - ]:       43792 :     if (save_nestlevel > 0)
     115                 :       43792 :         AtEOXact_GUC(false, save_nestlevel);
     116                 :             : 
     117                 :             :     /* Extract and return the advice string */
     118                 :       43792 :     pgpa_item = find_defelem_by_defname(pstmt->extension_state,
     119                 :             :                                         "pg_plan_advice");
     120         [ -  + ]:       43792 :     if (pgpa_item == NULL)
     121         [ #  # ]:           0 :         elog(ERROR, "extension state for pg_plan_advice not found");
     122                 :       43792 :     advice_string_item = find_defelem_by_defname((List *) pgpa_item->arg,
     123                 :             :                                                  "advice_string");
     124         [ -  + ]:       43792 :     if (advice_string_item == NULL)
     125         [ #  # ]:           0 :         elog(ERROR,
     126                 :             :              "advice string for pg_plan_advice not found in extension state");
     127                 :       43792 :     return strVal(advice_string_item->arg);
     128                 :             : }
     129                 :             : 
     130                 :             : /*
     131                 :             :  * Search a list of DefElem objects for a given defname.
     132                 :             :  */
     133                 :             : static DefElem *
     134                 :       87584 : find_defelem_by_defname(List *deflist, char *defname)
     135                 :             : {
     136   [ +  -  +  -  :       87584 :     foreach_node(DefElem, item, deflist)
                   +  - ]
     137                 :             :     {
     138         [ +  - ]:       87584 :         if (strcmp(item->defname, defname) == 0)
     139                 :       87584 :             return item;
     140                 :             :     }
     141                 :             : 
     142                 :           0 :     return NULL;
     143                 :             : }
        

Generated by: LCOV version 2.0-1