LCOV - differential code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_ast.c (source / functions) Coverage Total Hit UBC CBC
Current: 1a1248818fb8b8b0353c292c632d6a6af77e8209 vs 8e82d6610e7280d7870c0c36ff1c797e3cfb3e09 Lines: 97.4 % 194 189 5 189
Current Date: 2026-10-12 11:56:10 +0200 Functions: 100.0 % 8 8 8
Baseline: lcov-20261012-baseline Branches: 90.0 % 150 135 15 135
Baseline Date: 2026-10-12 10:36:06 +0200 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 3 3 3
(30,360] days: 97.4 % 191 186 5 186
Function coverage date bins:
(30,360] days: 100.0 % 8 8 8
Branch coverage date bins:
(7,30] days: 75.0 % 4 3 1 3
(30,360] days: 90.4 % 146 132 14 132

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * pgpa_ast.c
                                  4                 :                :  *    additional supporting code related to plan advice parsing
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  *    contrib/pg_plan_advice/pgpa_ast.c
                                  9                 :                :  *
                                 10                 :                :  *-------------------------------------------------------------------------
                                 11                 :                :  */
                                 12                 :                : 
                                 13                 :                : #include "postgres.h"
                                 14                 :                : 
                                 15                 :                : #include "pgpa_ast.h"
                                 16                 :                : 
                                 17                 :                : #include "funcapi.h"
                                 18                 :                : #include "miscadmin.h"
                                 19                 :                : #include "utils/array.h"
                                 20                 :                : #include "utils/builtins.h"
                                 21                 :                : 
                                 22                 :                : static bool pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                 23                 :                :                                           pgpa_advice_target *target,
                                 24                 :                :                                           bool *rids_used);
                                 25                 :                : 
                                 26                 :                : /*
                                 27                 :                :  * Get a C string that corresponds to the specified advice tag.
                                 28                 :                :  */
                                 29                 :                : char *
  214 rhaas@postgresql.org       30                 :CBC      146551 : pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
                                 31                 :                : {
                                 32   [ +  +  +  +  :         146551 :     switch (advice_tag)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                              +  - ]
                                 33                 :                :     {
                                 34                 :           2283 :         case PGPA_TAG_BITMAP_HEAP_SCAN:
                                 35                 :           2283 :             return "BITMAP_HEAP_SCAN";
  200                            36                 :            434 :         case PGPA_TAG_DO_NOT_SCAN:
                                 37                 :            434 :             return "DO_NOT_SCAN";
  214                            38                 :              1 :         case PGPA_TAG_FOREIGN_JOIN:
                                 39                 :              1 :             return "FOREIGN_JOIN";
                                 40                 :            172 :         case PGPA_TAG_GATHER:
                                 41                 :            172 :             return "GATHER";
                                 42                 :             58 :         case PGPA_TAG_GATHER_MERGE:
                                 43                 :             58 :             return "GATHER_MERGE";
                                 44                 :           4283 :         case PGPA_TAG_HASH_JOIN:
                                 45                 :           4283 :             return "HASH_JOIN";
                                 46                 :           1639 :         case PGPA_TAG_INDEX_ONLY_SCAN:
                                 47                 :           1639 :             return "INDEX_ONLY_SCAN";
                                 48                 :          12917 :         case PGPA_TAG_INDEX_SCAN:
                                 49                 :          12917 :             return "INDEX_SCAN";
                                 50                 :          11002 :         case PGPA_TAG_JOIN_ORDER:
                                 51                 :          11002 :             return "JOIN_ORDER";
                                 52                 :             31 :         case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
                                 53                 :             31 :             return "MERGE_JOIN_MATERIALIZE";
                                 54                 :            558 :         case PGPA_TAG_MERGE_JOIN_PLAIN:
                                 55                 :            558 :             return "MERGE_JOIN_PLAIN";
                                 56                 :            536 :         case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
                                 57                 :            536 :             return "NESTED_LOOP_MATERIALIZE";
                                 58                 :            234 :         case PGPA_TAG_NESTED_LOOP_MEMOIZE:
                                 59                 :            234 :             return "NESTED_LOOP_MEMOIZE";
                                 60                 :           9340 :         case PGPA_TAG_NESTED_LOOP_PLAIN:
                                 61                 :           9340 :             return "NESTED_LOOP_PLAIN";
                                 62                 :          73517 :         case PGPA_TAG_NO_GATHER:
                                 63                 :          73517 :             return "NO_GATHER";
                                 64                 :           2242 :         case PGPA_TAG_PARTITIONWISE:
                                 65                 :           2242 :             return "PARTITIONWISE";
                                 66                 :            623 :         case PGPA_TAG_SEMIJOIN_NON_UNIQUE:
                                 67                 :            623 :             return "SEMIJOIN_NON_UNIQUE";
                                 68                 :             82 :         case PGPA_TAG_SEMIJOIN_UNIQUE:
                                 69                 :             82 :             return "SEMIJOIN_UNIQUE";
                                 70                 :          26181 :         case PGPA_TAG_SEQ_SCAN:
                                 71                 :          26181 :             return "SEQ_SCAN";
                                 72                 :            418 :         case PGPA_TAG_TID_SCAN:
                                 73                 :            418 :             return "TID_SCAN";
                                 74                 :                :     }
                                 75                 :                : 
  214 rhaas@postgresql.org       76                 :UBC           0 :     pg_unreachable();
                                 77                 :                :     return NULL;
                                 78                 :                : }
                                 79                 :                : 
                                 80                 :                : /*
                                 81                 :                :  * Convert an advice tag, formatted as a string that has already been
                                 82                 :                :  * downcased as appropriate, to a pgpa_advice_tag_type.
                                 83                 :                :  *
                                 84                 :                :  * If we succeed, set *fail = false and return the result; if we fail,
                                 85                 :                :  * set *fail = true and return an arbitrary value.
                                 86                 :                :  */
                                 87                 :                : pgpa_advice_tag_type
  214 rhaas@postgresql.org       88                 :CBC      344495 : pgpa_parse_advice_tag(const char *tag, bool *fail)
                                 89                 :                : {
                                 90                 :         344495 :     *fail = false;
                                 91                 :                : 
                                 92   [ +  +  +  +  :         344495 :     switch (tag[0])
                                     +  +  +  +  +  
                                        +  +  +  + ]
                                 93                 :                :     {
                                 94                 :           9005 :         case 'b':
                                 95         [ +  + ]:           9005 :             if (strcmp(tag, "bitmap_heap_scan") == 0)
                                 96                 :           2183 :                 return PGPA_TAG_BITMAP_HEAP_SCAN;
                                 97                 :           6822 :             break;
  200                            98                 :           2847 :         case 'd':
                                 99         [ +  + ]:           2847 :             if (strcmp(tag, "do_not_scan") == 0)
                                100                 :            234 :                 return PGPA_TAG_DO_NOT_SCAN;
                                101                 :           2613 :             break;
  214                           102                 :           5165 :         case 'f':
                                103         [ +  + ]:           5165 :             if (strcmp(tag, "foreign_join") == 0)
                                104                 :             10 :                 return PGPA_TAG_FOREIGN_JOIN;
                                105                 :           5155 :             break;
                                106                 :           2337 :         case 'g':
                                107         [ +  + ]:           2337 :             if (strcmp(tag, "gather") == 0)
                                108                 :            339 :                 return PGPA_TAG_GATHER;
                                109         [ +  + ]:           1998 :             if (strcmp(tag, "gather_merge") == 0)
                                110                 :            112 :                 return PGPA_TAG_GATHER_MERGE;
                                111                 :           1886 :             break;
                                112                 :           7024 :         case 'h':
                                113         [ +  + ]:           7024 :             if (strcmp(tag, "hash_join") == 0)
                                114                 :           6331 :                 return PGPA_TAG_HASH_JOIN;
                                115                 :            693 :             break;
                                116                 :          17476 :         case 'i':
                                117         [ +  + ]:          17476 :             if (strcmp(tag, "index_scan") == 0)
                                118                 :           7788 :                 return PGPA_TAG_INDEX_SCAN;
                                119         [ +  + ]:           9688 :             if (strcmp(tag, "index_only_scan") == 0)
                                120                 :           1425 :                 return PGPA_TAG_INDEX_ONLY_SCAN;
                                121                 :           8263 :             break;
                                122                 :          11770 :         case 'j':
                                123         [ +  + ]:          11770 :             if (strcmp(tag, "join_order") == 0)
                                124                 :          11036 :                 return PGPA_TAG_JOIN_ORDER;
                                125                 :            734 :             break;
                                126                 :           4579 :         case 'm':
                                127         [ +  + ]:           4579 :             if (strcmp(tag, "merge_join_materialize") == 0)
                                128                 :             66 :                 return PGPA_TAG_MERGE_JOIN_MATERIALIZE;
                                129         [ +  + ]:           4513 :             if (strcmp(tag, "merge_join_plain") == 0)
                                130                 :            996 :                 return PGPA_TAG_MERGE_JOIN_PLAIN;
                                131                 :           3517 :             break;
                                132                 :          61417 :         case 'n':
                                133         [ +  + ]:          61417 :             if (strcmp(tag, "nested_loop_materialize") == 0)
                                134                 :            980 :                 return PGPA_TAG_NESTED_LOOP_MATERIALIZE;
                                135         [ +  + ]:          60437 :             if (strcmp(tag, "nested_loop_memoize") == 0)
                                136                 :            444 :                 return PGPA_TAG_NESTED_LOOP_MEMOIZE;
                                137         [ +  + ]:          59993 :             if (strcmp(tag, "nested_loop_plain") == 0)
                                138                 :          11108 :                 return PGPA_TAG_NESTED_LOOP_PLAIN;
                                139         [ +  + ]:          48885 :             if (strcmp(tag, "no_gather") == 0)
                                140                 :          43037 :                 return PGPA_TAG_NO_GATHER;
                                141                 :           5848 :             break;
                                142                 :          88132 :         case 'p':
                                143         [ +  + ]:          88132 :             if (strcmp(tag, "partitionwise") == 0)
                                144                 :           3226 :                 return PGPA_TAG_PARTITIONWISE;
                                145                 :          84906 :             break;
                                146                 :          41632 :         case 's':
                                147         [ +  + ]:          41632 :             if (strcmp(tag, "semijoin_non_unique") == 0)
                                148                 :           1176 :                 return PGPA_TAG_SEMIJOIN_NON_UNIQUE;
                                149         [ +  + ]:          40456 :             if (strcmp(tag, "semijoin_unique") == 0)
                                150                 :            156 :                 return PGPA_TAG_SEMIJOIN_UNIQUE;
                                151         [ +  + ]:          40300 :             if (strcmp(tag, "seq_scan") == 0)
                                152                 :          16514 :                 return PGPA_TAG_SEQ_SCAN;
                                153                 :          23786 :             break;
                                154                 :          25038 :         case 't':
                                155         [ +  + ]:          25038 :             if (strcmp(tag, "tid_scan") == 0)
                                156                 :            401 :                 return PGPA_TAG_TID_SCAN;
                                157                 :          24637 :             break;
                                158                 :                :     }
                                159                 :                : 
                                160                 :                :     /* didn't work out */
                                161                 :         236933 :     *fail = true;
                                162                 :                : 
                                163                 :                :     /* return an arbitrary value to unwind the call stack */
                                164                 :         236933 :     return PGPA_TAG_SEQ_SCAN;
                                165                 :                : }
                                166                 :                : 
                                167                 :                : /*
                                168                 :                :  * Format a pgpa_advice_target as a string and append result to a StringInfo.
                                169                 :                :  */
                                170                 :                : void
                                171                 :         174716 : pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
                                172                 :                : {
   33                           173                 :         174716 :     check_stack_depth();
                                174                 :                : 
  214                           175         [ +  + ]:         174716 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                176                 :                :     {
                                177                 :          12217 :         bool        first = true;
                                178                 :                :         char       *delims;
                                179                 :                : 
                                180         [ +  + ]:          12217 :         if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
                                181                 :             20 :             delims = "{}";
                                182                 :                :         else
                                183                 :          12197 :             delims = "()";
                                184                 :                : 
                                185                 :          12217 :         appendStringInfoChar(str, delims[0]);
                                186   [ +  -  +  +  :          52599 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                187                 :                :         {
                                188         [ +  + ]:          28165 :             if (first)
                                189                 :          12217 :                 first = false;
                                190                 :                :             else
                                191                 :          15948 :                 appendStringInfoChar(str, ' ');
                                192                 :          28165 :             pgpa_format_advice_target(str, child_target);
                                193                 :                :         }
                                194                 :          12217 :         appendStringInfoChar(str, delims[1]);
                                195                 :                :     }
                                196                 :                :     else
                                197                 :                :     {
                                198                 :                :         const char *rt_identifier;
                                199                 :                : 
                                200                 :         162499 :         rt_identifier = pgpa_identifier_string(&target->rid);
                                201                 :         162499 :         appendStringInfoString(str, rt_identifier);
                                202                 :                :     }
                                203                 :         174716 : }
                                204                 :                : 
                                205                 :                : /*
                                206                 :                :  * Format a pgpa_index_target as a string and append result to a StringInfo.
                                207                 :                :  */
                                208                 :                : void
                                209                 :          14556 : pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
                                210                 :                : {
                                211         [ +  + ]:          14556 :     if (itarget->indnamespace != NULL)
                                212                 :          14538 :         appendStringInfo(str, "%s.",
                                213                 :          14538 :                          quote_identifier(itarget->indnamespace));
                                214                 :          14556 :     appendStringInfoString(str, quote_identifier(itarget->indname));
                                215                 :          14556 : }
                                216                 :                : 
                                217                 :                : /*
                                218                 :                :  * Determine whether two pgpa_index_target objects are exactly identical.
                                219                 :                :  */
                                220                 :                : bool
                                221                 :              2 : pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
                                222                 :                : {
                                223                 :                :     /* indnamespace can be NULL, and two NULL values are equal */
                                224   [ +  -  +  + ]:              2 :     if ((i1->indnamespace != NULL || i2->indnamespace != NULL) &&
                                225   [ -  +  -  - ]:              1 :         (i1->indnamespace == NULL || i2->indnamespace == NULL ||
  214 rhaas@postgresql.org      226         [ #  # ]:UBC           0 :          strcmp(i1->indnamespace, i2->indnamespace) != 0))
  214 rhaas@postgresql.org      227                 :CBC           1 :         return false;
                                228         [ -  + ]:              1 :     if (strcmp(i1->indname, i2->indname) != 0)
  214 rhaas@postgresql.org      229                 :UBC           0 :         return false;
                                230                 :                : 
  214 rhaas@postgresql.org      231                 :CBC           1 :     return true;
                                232                 :                : }
                                233                 :                : 
                                234                 :                : /*
                                235                 :                :  * Check whether an identifier matches an any part of an advice target.
                                236                 :                :  */
                                237                 :                : bool
                                238                 :        1305113 : pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
                                239                 :                : {
   33                           240                 :        1305113 :     check_stack_depth();
                                241                 :                : 
                                242                 :                :     /* For non-identifiers, check all descendants. */
  214                           243         [ +  + ]:        1305113 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                244                 :                :     {
                                245   [ +  -  +  +  :         152301 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                246                 :                :         {
                                247         [ +  + ]:         147233 :             if (pgpa_identifier_matches_target(rid, child_target))
                                248                 :          71164 :                 return true;
                                249                 :                :         }
                                250                 :           2534 :         return false;
                                251                 :                :     }
                                252                 :                : 
                                253                 :                :     /* Straightforward comparisons of alias name and occurrence number. */
                                254         [ +  + ]:        1231415 :     if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
                                255                 :         663443 :         return false;
                                256         [ +  + ]:         567972 :     if (rid->occurrence != target->rid.occurrence)
                                257                 :          17842 :         return false;
                                258                 :                : 
                                259                 :                :     /*
                                260                 :                :      * A relation identifier should either include both of partition name and
                                261                 :                :      * partition schema, or neither one.
                                262                 :                :      */
   26                           263         [ -  + ]:         550130 :     Assert((rid->partnsp == NULL) == (rid->partrel == NULL));
                                264                 :                : 
                                265                 :                :     /*
                                266                 :                :      * These fields can be NULL on either side, but NULL only matches another
                                267                 :                :      * NULL.
                                268                 :                :      */
                                269         [ +  + ]:         550130 :     if (!strings_equal_or_both_null(rid->partnsp, target->rid.partnsp))
                                270                 :             11 :         return false;
  214                           271         [ -  + ]:         550119 :     if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
  214 rhaas@postgresql.org      272                 :UBC           0 :         return false;
  214 rhaas@postgresql.org      273         [ -  + ]:CBC      550119 :     if (!strings_equal_or_both_null(rid->plan_name, target->rid.plan_name))
  214 rhaas@postgresql.org      274                 :UBC           0 :         return false;
                                275                 :                : 
  214 rhaas@postgresql.org      276                 :CBC      550119 :     return true;
                                277                 :                : }
                                278                 :                : 
                                279                 :                : /*
                                280                 :                :  * Match identifiers to advice targets and return an enum value indicating
                                281                 :                :  * the relationship between the set of keys and the set of targets.
                                282                 :                :  *
                                283                 :                :  * See the comments for pgpa_itm_type.
                                284                 :                :  */
                                285                 :                : pgpa_itm_type
                                286                 :         338744 : pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
                                287                 :                :                               pgpa_advice_target *target)
                                288                 :                : {
                                289                 :         338744 :     bool        all_rids_used = true;
                                290                 :         338744 :     bool        any_rids_used = false;
                                291                 :                :     bool        all_targets_used;
                                292                 :         338744 :     bool       *rids_used = palloc0_array(bool, nrids);
                                293                 :                : 
                                294                 :                :     /*
                                295                 :                :      * This function is called from within various loops within
                                296                 :                :      * pgpa_planner.c; to avoid needing a separate CHECK_FOR_INTERRUPTS() in
                                297                 :                :      * each one, we check here instead.
                                298                 :                :      */
   33                           299         [ -  + ]:         338744 :     CHECK_FOR_INTERRUPTS();
                                300                 :                : 
                                301                 :                :     all_targets_used =
  214                           302                 :         338744 :         pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
                                303                 :                : 
                                304         [ +  + ]:         929203 :     for (int i = 0; i < nrids; ++i)
                                305                 :                :     {
                                306         [ +  + ]:         590459 :         if (rids_used[i])
                                307                 :         267633 :             any_rids_used = true;
                                308                 :                :         else
                                309                 :         322826 :             all_rids_used = false;
                                310                 :                :     }
                                311                 :                : 
                                312         [ +  + ]:         338744 :     if (all_rids_used)
                                313                 :                :     {
                                314         [ +  + ]:         133457 :         if (all_targets_used)
                                315                 :         121608 :             return PGPA_ITM_EQUAL;
                                316                 :                :         else
                                317                 :          11849 :             return PGPA_ITM_KEYS_ARE_SUBSET;
                                318                 :                :     }
                                319                 :                :     else
                                320                 :                :     {
                                321         [ +  + ]:         205287 :         if (all_targets_used)
                                322                 :          80135 :             return PGPA_ITM_TARGETS_ARE_SUBSET;
                                323         [ +  + ]:         125152 :         else if (any_rids_used)
                                324                 :          11490 :             return PGPA_ITM_INTERSECTING;
                                325                 :                :         else
                                326                 :         113662 :             return PGPA_ITM_DISJOINT;
                                327                 :                :     }
                                328                 :                : }
                                329                 :                : 
                                330                 :                : /*
                                331                 :                :  * Returns true if every target or sub-target is matched by at least one
                                332                 :                :  * identifier, and otherwise false.
                                333                 :                :  *
                                334                 :                :  * Also sets rids_used[i] = true for each identifier that matches at least one
                                335                 :                :  * target.
                                336                 :                :  */
                                337                 :                : static bool
                                338                 :         560492 : pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                339                 :                :                               pgpa_advice_target *target, bool *rids_used)
                                340                 :                : {
                                341                 :         560492 :     bool        result = false;
                                342                 :                : 
   33                           343                 :         560492 :     check_stack_depth();
                                344                 :                : 
  214                           345         [ +  + ]:         560492 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                346                 :                :     {
                                347                 :         132300 :         result = true;
                                348                 :                : 
                                349   [ +  -  +  +  :         486348 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                350                 :                :         {
                                351         [ +  + ]:         221748 :             if (!pgpa_identifiers_cover_target(nrids, rids, child_target,
                                352                 :                :                                                rids_used))
                                353                 :          98559 :                 result = false;
                                354                 :                :         }
                                355                 :                :     }
                                356                 :                :     else
                                357                 :                :     {
                                358         [ +  + ]:        1294918 :         for (int i = 0; i < nrids; ++i)
                                359                 :                :         {
                                360         [ +  + ]:         866726 :             if (pgpa_identifier_matches_target(&rids[i], target))
                                361                 :                :             {
                                362                 :         267633 :                 rids_used[i] = true;
                                363                 :         267633 :                 result = true;
                                364                 :                :             }
                                365                 :                :         }
                                366                 :                :     }
                                367                 :                : 
                                368                 :         560492 :     return result;
                                369                 :                : }
        

Generated by: LCOV version 2.0-1