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 19devel Lines: 90.9 % 33 30
Test Date: 2026-03-24 06:16:10 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit

            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        86827 : test_plan_advice_advisor(PlannerGlobal *glob, Query *parse,
      62              :                          const char *query_string, int cursorOptions,
      63              :                          ExplainState *es)
      64              : {
      65              :     PlannedStmt *pstmt;
      66        86827 :     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        86827 :     if (in_recursion)
      75        43447 :         return NULL;
      76              : 
      77        43380 :     PG_TRY();
      78              :     {
      79        43380 :         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        43380 :         save_nestlevel = NewGUCNestLevel();
      92        43380 :         set_config_option("client_min_messages", "error",
      93              :                           PGC_SUSET, PGC_S_SESSION,
      94              :                           GUC_ACTION_SAVE, true, 0, false);
      95        43380 :         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        43380 :         pstmt = planner(copyObject(parse), query_string, cursorOptions,
     105              :                         glob->boundParams, es);
     106              :     }
     107          764 :     PG_FINALLY();
     108              :     {
     109        43380 :         in_recursion = false;
     110              :     }
     111        43380 :     PG_END_TRY();
     112              : 
     113              :     /* Roll back any GUC changes */
     114        42616 :     if (save_nestlevel > 0)
     115        42616 :         AtEOXact_GUC(false, save_nestlevel);
     116              : 
     117              :     /* Extract and return the advice string */
     118        42616 :     pgpa_item = find_defelem_by_defname(pstmt->extension_state,
     119              :                                         "pg_plan_advice");
     120        42616 :     if (pgpa_item == NULL)
     121            0 :         elog(ERROR, "extension state for pg_plan_advice not found");
     122        42616 :     advice_string_item = find_defelem_by_defname((List *) pgpa_item->arg,
     123              :                                                  "advice_string");
     124        42616 :     if (advice_string_item == NULL)
     125            0 :         elog(ERROR,
     126              :              "advice string for pg_plan_advice not found in extension state");
     127        42616 :     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        85232 : find_defelem_by_defname(List *deflist, char *defname)
     135              : {
     136        85232 :     foreach_node(DefElem, item, deflist)
     137              :     {
     138        85232 :         if (strcmp(item->defname, defname) == 0)
     139        85232 :             return item;
     140              :     }
     141              : 
     142            0 :     return NULL;
     143              : }
        

Generated by: LCOV version 2.0-1