LCOV - differential code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_ast.c (source / functions) Coverage Total Hit UNC GNC
Current: 0e5ff9b9b45a657aea12440478dc002e9b01f138 vs 0123ce131fca454009439dfa3b2266d1d40737d7 Lines: 94.1 % 185 174 11 174
Current Date: 2026-03-14 14:10:32 -0400 Functions: 100.0 % 8 8 8
Baseline: lcov-20260315-024220-baseline Branches: 86.0 % 150 129 21 129
Baseline Date: 2026-03-14 15:27:56 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 94.1 % 185 174 11 174
Function coverage date bins:
(1,7] days: 100.0 % 8 8 8
Branch coverage date bins:
(1,7] days: 86.0 % 150 129 21 129

 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 "utils/array.h"
                                 19                 :                : #include "utils/builtins.h"
                                 20                 :                : 
                                 21                 :                : static bool pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                 22                 :                :                                           pgpa_advice_target *target,
                                 23                 :                :                                           bool *rids_used);
                                 24                 :                : 
                                 25                 :                : /*
                                 26                 :                :  * Get a C string that corresponds to the specified advice tag.
                                 27                 :                :  */
                                 28                 :                : char *
    3 rhaas@postgresql.org       29                 :GNC         133 : pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
                                 30                 :                : {
                                 31   [ +  +  +  +  :            133 :     switch (advice_tag)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                                 - ]
                                 32                 :                :     {
                                 33                 :              3 :         case PGPA_TAG_BITMAP_HEAP_SCAN:
                                 34                 :              3 :             return "BITMAP_HEAP_SCAN";
                                 35                 :              1 :         case PGPA_TAG_FOREIGN_JOIN:
                                 36                 :              1 :             return "FOREIGN_JOIN";
                                 37                 :              8 :         case PGPA_TAG_GATHER:
                                 38                 :              8 :             return "GATHER";
                                 39                 :              6 :         case PGPA_TAG_GATHER_MERGE:
                                 40                 :              6 :             return "GATHER_MERGE";
                                 41                 :              6 :         case PGPA_TAG_HASH_JOIN:
                                 42                 :              6 :             return "HASH_JOIN";
                                 43                 :              6 :         case PGPA_TAG_INDEX_ONLY_SCAN:
                                 44                 :              6 :             return "INDEX_ONLY_SCAN";
                                 45                 :             15 :         case PGPA_TAG_INDEX_SCAN:
                                 46                 :             15 :             return "INDEX_SCAN";
                                 47                 :             19 :         case PGPA_TAG_JOIN_ORDER:
                                 48                 :             19 :             return "JOIN_ORDER";
                                 49                 :              2 :         case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
                                 50                 :              2 :             return "MERGE_JOIN_MATERIALIZE";
                                 51                 :              2 :         case PGPA_TAG_MERGE_JOIN_PLAIN:
                                 52                 :              2 :             return "MERGE_JOIN_PLAIN";
                                 53                 :              3 :         case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
                                 54                 :              3 :             return "NESTED_LOOP_MATERIALIZE";
                                 55                 :              2 :         case PGPA_TAG_NESTED_LOOP_MEMOIZE:
                                 56                 :              2 :             return "NESTED_LOOP_MEMOIZE";
                                 57                 :              5 :         case PGPA_TAG_NESTED_LOOP_PLAIN:
                                 58                 :              5 :             return "NESTED_LOOP_PLAIN";
                                 59                 :              7 :         case PGPA_TAG_NO_GATHER:
                                 60                 :              7 :             return "NO_GATHER";
                                 61                 :              8 :         case PGPA_TAG_PARTITIONWISE:
                                 62                 :              8 :             return "PARTITIONWISE";
                                 63                 :              6 :         case PGPA_TAG_SEMIJOIN_NON_UNIQUE:
                                 64                 :              6 :             return "SEMIJOIN_NON_UNIQUE";
                                 65                 :              7 :         case PGPA_TAG_SEMIJOIN_UNIQUE:
                                 66                 :              7 :             return "SEMIJOIN_UNIQUE";
                                 67                 :             23 :         case PGPA_TAG_SEQ_SCAN:
                                 68                 :             23 :             return "SEQ_SCAN";
                                 69                 :              4 :         case PGPA_TAG_TID_SCAN:
                                 70                 :              4 :             return "TID_SCAN";
                                 71                 :                :     }
                                 72                 :                : 
    3 rhaas@postgresql.org       73                 :UNC           0 :     pg_unreachable();
                                 74                 :                :     return NULL;
                                 75                 :                : }
                                 76                 :                : 
                                 77                 :                : /*
                                 78                 :                :  * Convert an advice tag, formatted as a string that has already been
                                 79                 :                :  * downcased as appropriate, to a pgpa_advice_tag_type.
                                 80                 :                :  *
                                 81                 :                :  * If we succeed, set *fail = false and return the result; if we fail,
                                 82                 :                :  * set *fail = true and return an arbitrary value.
                                 83                 :                :  */
                                 84                 :                : pgpa_advice_tag_type
    3 rhaas@postgresql.org       85                 :GNC         777 : pgpa_parse_advice_tag(const char *tag, bool *fail)
                                 86                 :                : {
                                 87                 :            777 :     *fail = false;
                                 88                 :                : 
                                 89   [ +  +  +  +  :            777 :     switch (tag[0])
                                     +  +  +  +  +  
                                           +  +  + ]
                                 90                 :                :     {
                                 91                 :             10 :         case 'b':
                                 92         [ +  + ]:             10 :             if (strcmp(tag, "bitmap_heap_scan") == 0)
                                 93                 :              6 :                 return PGPA_TAG_BITMAP_HEAP_SCAN;
                                 94                 :              4 :             break;
                                 95                 :             76 :         case 'f':
                                 96         [ +  + ]:             76 :             if (strcmp(tag, "foreign_join") == 0)
                                 97                 :              8 :                 return PGPA_TAG_FOREIGN_JOIN;
                                 98                 :             68 :             break;
                                 99                 :             55 :         case 'g':
                                100         [ +  + ]:             55 :             if (strcmp(tag, "gather") == 0)
                                101                 :             29 :                 return PGPA_TAG_GATHER;
                                102         [ +  + ]:             26 :             if (strcmp(tag, "gather_merge") == 0)
                                103                 :             20 :                 return PGPA_TAG_GATHER_MERGE;
                                104                 :              6 :             break;
                                105                 :             24 :         case 'h':
                                106         [ +  - ]:             24 :             if (strcmp(tag, "hash_join") == 0)
                                107                 :             24 :                 return PGPA_TAG_HASH_JOIN;
    3 rhaas@postgresql.org      108                 :UNC           0 :             break;
    3 rhaas@postgresql.org      109                 :GNC          38 :         case 'i':
                                110         [ +  + ]:             38 :             if (strcmp(tag, "index_scan") == 0)
                                111                 :             26 :                 return PGPA_TAG_INDEX_SCAN;
                                112         [ +  - ]:             12 :             if (strcmp(tag, "index_only_scan") == 0)
                                113                 :             12 :                 return PGPA_TAG_INDEX_ONLY_SCAN;
    3 rhaas@postgresql.org      114                 :UNC           0 :             break;
    3 rhaas@postgresql.org      115                 :GNC          40 :         case 'j':
                                116         [ +  - ]:             40 :             if (strcmp(tag, "join_order") == 0)
                                117                 :             40 :                 return PGPA_TAG_JOIN_ORDER;
    3 rhaas@postgresql.org      118                 :UNC           0 :             break;
    3 rhaas@postgresql.org      119                 :GNC          16 :         case 'm':
                                120         [ +  + ]:             16 :             if (strcmp(tag, "merge_join_materialize") == 0)
                                121                 :              8 :                 return PGPA_TAG_MERGE_JOIN_MATERIALIZE;
                                122         [ +  - ]:              8 :             if (strcmp(tag, "merge_join_plain") == 0)
                                123                 :              8 :                 return PGPA_TAG_MERGE_JOIN_PLAIN;
    3 rhaas@postgresql.org      124                 :UNC           0 :             break;
    3 rhaas@postgresql.org      125                 :GNC          58 :         case 'n':
                                126         [ +  + ]:             58 :             if (strcmp(tag, "nested_loop_materialize") == 0)
                                127                 :             12 :                 return PGPA_TAG_NESTED_LOOP_MATERIALIZE;
                                128         [ +  + ]:             46 :             if (strcmp(tag, "nested_loop_memoize") == 0)
                                129                 :              8 :                 return PGPA_TAG_NESTED_LOOP_MEMOIZE;
                                130         [ +  + ]:             38 :             if (strcmp(tag, "nested_loop_plain") == 0)
                                131                 :             20 :                 return PGPA_TAG_NESTED_LOOP_PLAIN;
                                132         [ +  + ]:             18 :             if (strcmp(tag, "no_gather") == 0)
                                133                 :             12 :                 return PGPA_TAG_NO_GATHER;
                                134                 :              6 :             break;
                                135                 :             78 :         case 'p':
                                136         [ +  + ]:             78 :             if (strcmp(tag, "partitionwise") == 0)
                                137                 :             16 :                 return PGPA_TAG_PARTITIONWISE;
                                138                 :             62 :             break;
                                139                 :            231 :         case 's':
                                140         [ +  + ]:            231 :             if (strcmp(tag, "semijoin_non_unique") == 0)
                                141                 :             24 :                 return PGPA_TAG_SEMIJOIN_NON_UNIQUE;
                                142         [ +  + ]:            207 :             if (strcmp(tag, "semijoin_unique") == 0)
                                143                 :             28 :                 return PGPA_TAG_SEMIJOIN_UNIQUE;
                                144         [ +  + ]:            179 :             if (strcmp(tag, "seq_scan") == 0)
                                145                 :             51 :                 return PGPA_TAG_SEQ_SCAN;
                                146                 :            128 :             break;
                                147                 :              7 :         case 't':
                                148         [ +  - ]:              7 :             if (strcmp(tag, "tid_scan") == 0)
                                149                 :              7 :                 return PGPA_TAG_TID_SCAN;
    3 rhaas@postgresql.org      150                 :UNC           0 :             break;
                                151                 :                :     }
                                152                 :                : 
                                153                 :                :     /* didn't work out */
    3 rhaas@postgresql.org      154                 :GNC         418 :     *fail = true;
                                155                 :                : 
                                156                 :                :     /* return an arbitrary value to unwind the call stack */
                                157                 :            418 :     return PGPA_TAG_SEQ_SCAN;
                                158                 :                : }
                                159                 :                : 
                                160                 :                : /*
                                161                 :                :  * Format a pgpa_advice_target as a string and append result to a StringInfo.
                                162                 :                :  */
                                163                 :                : void
                                164                 :            202 : pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
                                165                 :                : {
                                166         [ +  + ]:            202 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                167                 :                :     {
                                168                 :             33 :         bool        first = true;
                                169                 :                :         char       *delims;
                                170                 :                : 
                                171         [ +  + ]:             33 :         if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
                                172                 :              1 :             delims = "{}";
                                173                 :                :         else
                                174                 :             32 :             delims = "()";
                                175                 :                : 
                                176                 :             33 :         appendStringInfoChar(str, delims[0]);
                                177   [ +  -  +  +  :            135 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                178                 :                :         {
                                179         [ +  + ]:             69 :             if (first)
                                180                 :             33 :                 first = false;
                                181                 :                :             else
                                182                 :             36 :                 appendStringInfoChar(str, ' ');
                                183                 :             69 :             pgpa_format_advice_target(str, child_target);
                                184                 :                :         }
                                185                 :             33 :         appendStringInfoChar(str, delims[1]);
                                186                 :                :     }
                                187                 :                :     else
                                188                 :                :     {
                                189                 :                :         const char *rt_identifier;
                                190                 :                : 
                                191                 :            169 :         rt_identifier = pgpa_identifier_string(&target->rid);
                                192                 :            169 :         appendStringInfoString(str, rt_identifier);
                                193                 :                :     }
                                194                 :            202 : }
                                195                 :                : 
                                196                 :                : /*
                                197                 :                :  * Format a pgpa_index_target as a string and append result to a StringInfo.
                                198                 :                :  */
                                199                 :                : void
                                200                 :             21 : pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
                                201                 :                : {
                                202         [ +  + ]:             21 :     if (itarget->indnamespace != NULL)
                                203                 :              4 :         appendStringInfo(str, "%s.",
                                204                 :              4 :                          quote_identifier(itarget->indnamespace));
                                205                 :             21 :     appendStringInfoString(str, quote_identifier(itarget->indname));
                                206                 :             21 : }
                                207                 :                : 
                                208                 :                : /*
                                209                 :                :  * Determine whether two pgpa_index_target objects are exactly identical.
                                210                 :                :  */
                                211                 :                : bool
                                212                 :              2 : pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
                                213                 :                : {
                                214                 :                :     /* indnamespace can be NULL, and two NULL values are equal */
                                215   [ +  -  +  + ]:              2 :     if ((i1->indnamespace != NULL || i2->indnamespace != NULL) &&
                                216   [ -  +  -  - ]:              1 :         (i1->indnamespace == NULL || i2->indnamespace == NULL ||
    3 rhaas@postgresql.org      217         [ #  # ]:UNC           0 :          strcmp(i1->indnamespace, i2->indnamespace) != 0))
    3 rhaas@postgresql.org      218                 :GNC           1 :         return false;
                                219         [ -  + ]:              1 :     if (strcmp(i1->indname, i2->indname) != 0)
    3 rhaas@postgresql.org      220                 :UNC           0 :         return false;
                                221                 :                : 
    3 rhaas@postgresql.org      222                 :GNC           1 :     return true;
                                223                 :                : }
                                224                 :                : 
                                225                 :                : /*
                                226                 :                :  * Check whether an identifier matches an any part of an advice target.
                                227                 :                :  */
                                228                 :                : bool
                                229                 :           1629 : pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
                                230                 :                : {
                                231                 :                :     /* For non-identifiers, check all descendants. */
                                232         [ +  + ]:           1629 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                233                 :                :     {
                                234   [ +  -  +  -  :            297 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  - ]
                                235                 :                :         {
                                236         [ +  + ]:            297 :             if (pgpa_identifier_matches_target(rid, child_target))
                                237                 :            175 :                 return true;
                                238                 :                :         }
    3 rhaas@postgresql.org      239                 :UNC           0 :         return false;
                                240                 :                :     }
                                241                 :                : 
                                242                 :                :     /* Straightforward comparisons of alias name and occurrence number. */
    3 rhaas@postgresql.org      243         [ +  + ]:GNC        1454 :     if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
                                244                 :            733 :         return false;
                                245         [ +  + ]:            721 :     if (rid->occurrence != target->rid.occurrence)
                                246                 :              4 :         return false;
                                247                 :                : 
                                248                 :                :     /*
                                249                 :                :      * If a relation identifier mentions a partition name, it should also
                                250                 :                :      * specify a partition schema. But the target may leave the schema NULL to
                                251                 :                :      * match anything.
                                252                 :                :      */
                                253   [ +  +  -  + ]:            717 :     Assert(rid->partnsp != NULL || rid->partrel == NULL);
                                254   [ +  +  +  + ]:            717 :     if (rid->partnsp != NULL && target->rid.partnsp != NULL &&
                                255         [ -  + ]:             20 :         strcmp(rid->partnsp, target->rid.partnsp) != 0)
    3 rhaas@postgresql.org      256                 :UNC           0 :         return false;
                                257                 :                : 
                                258                 :                :     /*
                                259                 :                :      * These fields can be NULL on either side, but NULL only matches another
                                260                 :                :      * NULL.
                                261                 :                :      */
    3 rhaas@postgresql.org      262         [ +  + ]:GNC         717 :     if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
                                263                 :             11 :         return false;
                                264         [ -  + ]:            706 :     if (!strings_equal_or_both_null(rid->plan_name, target->rid.plan_name))
    3 rhaas@postgresql.org      265                 :UNC           0 :         return false;
                                266                 :                : 
    3 rhaas@postgresql.org      267                 :GNC         706 :     return true;
                                268                 :                : }
                                269                 :                : 
                                270                 :                : /*
                                271                 :                :  * Match identifiers to advice targets and return an enum value indicating
                                272                 :                :  * the relationship between the set of keys and the set of targets.
                                273                 :                :  *
                                274                 :                :  * See the comments for pgpa_itm_type.
                                275                 :                :  */
                                276                 :                : pgpa_itm_type
                                277                 :            561 : pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
                                278                 :                :                               pgpa_advice_target *target)
                                279                 :                : {
                                280                 :            561 :     bool        all_rids_used = true;
                                281                 :            561 :     bool        any_rids_used = false;
                                282                 :                :     bool        all_targets_used;
                                283                 :            561 :     bool       *rids_used = palloc0_array(bool, nrids);
                                284                 :                : 
                                285                 :                :     all_targets_used =
                                286                 :            561 :         pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
                                287                 :                : 
                                288         [ +  + ]:           1338 :     for (int i = 0; i < nrids; ++i)
                                289                 :                :     {
                                290         [ +  + ]:            777 :         if (rids_used[i])
                                291                 :            435 :             any_rids_used = true;
                                292                 :                :         else
                                293                 :            342 :             all_rids_used = false;
                                294                 :                :     }
                                295                 :                : 
                                296         [ +  + ]:            561 :     if (all_rids_used)
                                297                 :                :     {
                                298         [ +  + ]:            245 :         if (all_targets_used)
                                299                 :            197 :             return PGPA_ITM_EQUAL;
                                300                 :                :         else
                                301                 :             48 :             return PGPA_ITM_KEYS_ARE_SUBSET;
                                302                 :                :     }
                                303                 :                :     else
                                304                 :                :     {
                                305         [ +  + ]:            316 :         if (all_targets_used)
                                306                 :             94 :             return PGPA_ITM_TARGETS_ARE_SUBSET;
                                307         [ +  + ]:            222 :         else if (any_rids_used)
                                308                 :             38 :             return PGPA_ITM_INTERSECTING;
                                309                 :                :         else
                                310                 :            184 :             return PGPA_ITM_DISJOINT;
                                311                 :                :     }
                                312                 :                : }
                                313                 :                : 
                                314                 :                : /*
                                315                 :                :  * Returns true if every target or sub-target is matched by at least one
                                316                 :                :  * identifier, and otherwise false.
                                317                 :                :  *
                                318                 :                :  * Also sets rids_used[i] = true for each idenifier that matches at least one
                                319                 :                :  * target.
                                320                 :                :  */
                                321                 :                : static bool
                                322                 :           1035 : pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                323                 :                :                               pgpa_advice_target *target, bool *rids_used)
                                324                 :                : {
                                325                 :           1035 :     bool        result = false;
                                326                 :                : 
                                327         [ +  + ]:           1035 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                328                 :                :     {
                                329                 :            315 :         result = true;
                                330                 :                : 
                                331   [ +  -  +  +  :           1104 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                332                 :                :         {
                                333         [ +  + ]:            474 :             if (!pgpa_identifiers_cover_target(nrids, rids, child_target,
                                334                 :                :                                                rids_used))
                                335                 :            215 :                 result = false;
                                336                 :                :         }
                                337                 :                :     }
                                338                 :                :     else
                                339                 :                :     {
                                340         [ +  + ]:           1777 :         for (int i = 0; i < nrids; ++i)
                                341                 :                :         {
                                342         [ +  + ]:           1057 :             if (pgpa_identifier_matches_target(&rids[i], target))
                                343                 :                :             {
                                344                 :            435 :                 rids_used[i] = true;
                                345                 :            435 :                 result = true;
                                346                 :                :             }
                                347                 :                :         }
                                348                 :                :     }
                                349                 :                : 
                                350                 :           1035 :     return result;
                                351                 :                : }
        

Generated by: LCOV version 2.4-beta