LCOV - differential code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_ast.c (source / functions) Coverage Total Hit UNC GNC
Current: bed3ffbf9d952be6c7d739d068cdce44c046dfb7 vs 574581b50ac9c63dd9e4abebb731a3b67e5b50f6 Lines: 97.4 % 191 186 5 186
Current Date: 2026-05-05 10:23:31 +0900 Functions: 100.0 % 8 8 8
Baseline: lcov-20260505-025707-baseline Branches: 90.9 % 154 140 14 140
Baseline Date: 2026-05-05 10:27:06 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(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:
(30,360] days: 90.9 % 154 140 14 140

 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 *
   54 rhaas@postgresql.org       29                 :GNC      144461 : pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
                                 30                 :                : {
                                 31   [ +  +  +  +  :         144461 :     switch (advice_tag)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                              +  - ]
                                 32                 :                :     {
                                 33                 :           2324 :         case PGPA_TAG_BITMAP_HEAP_SCAN:
                                 34                 :           2324 :             return "BITMAP_HEAP_SCAN";
   40                            35                 :            430 :         case PGPA_TAG_DO_NOT_SCAN:
                                 36                 :            430 :             return "DO_NOT_SCAN";
   54                            37                 :              1 :         case PGPA_TAG_FOREIGN_JOIN:
                                 38                 :              1 :             return "FOREIGN_JOIN";
                                 39                 :            174 :         case PGPA_TAG_GATHER:
                                 40                 :            174 :             return "GATHER";
                                 41                 :             60 :         case PGPA_TAG_GATHER_MERGE:
                                 42                 :             60 :             return "GATHER_MERGE";
                                 43                 :           4351 :         case PGPA_TAG_HASH_JOIN:
                                 44                 :           4351 :             return "HASH_JOIN";
                                 45                 :           1920 :         case PGPA_TAG_INDEX_ONLY_SCAN:
                                 46                 :           1920 :             return "INDEX_ONLY_SCAN";
                                 47                 :          12052 :         case PGPA_TAG_INDEX_SCAN:
                                 48                 :          12052 :             return "INDEX_SCAN";
                                 49                 :          11171 :         case PGPA_TAG_JOIN_ORDER:
                                 50                 :          11171 :             return "JOIN_ORDER";
                                 51                 :             27 :         case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
                                 52                 :             27 :             return "MERGE_JOIN_MATERIALIZE";
                                 53                 :            545 :         case PGPA_TAG_MERGE_JOIN_PLAIN:
                                 54                 :            545 :             return "MERGE_JOIN_PLAIN";
                                 55                 :            505 :         case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
                                 56                 :            505 :             return "NESTED_LOOP_MATERIALIZE";
                                 57                 :            237 :         case PGPA_TAG_NESTED_LOOP_MEMOIZE:
                                 58                 :            237 :             return "NESTED_LOOP_MEMOIZE";
                                 59                 :           9172 :         case PGPA_TAG_NESTED_LOOP_PLAIN:
                                 60                 :           9172 :             return "NESTED_LOOP_PLAIN";
                                 61                 :          71103 :         case PGPA_TAG_NO_GATHER:
                                 62                 :          71103 :             return "NO_GATHER";
                                 63                 :           2185 :         case PGPA_TAG_PARTITIONWISE:
                                 64                 :           2185 :             return "PARTITIONWISE";
                                 65                 :            628 :         case PGPA_TAG_SEMIJOIN_NON_UNIQUE:
                                 66                 :            628 :             return "SEMIJOIN_NON_UNIQUE";
                                 67                 :             79 :         case PGPA_TAG_SEMIJOIN_UNIQUE:
                                 68                 :             79 :             return "SEMIJOIN_UNIQUE";
                                 69                 :          27075 :         case PGPA_TAG_SEQ_SCAN:
                                 70                 :          27075 :             return "SEQ_SCAN";
                                 71                 :            422 :         case PGPA_TAG_TID_SCAN:
                                 72                 :            422 :             return "TID_SCAN";
                                 73                 :                :     }
                                 74                 :                : 
   54 rhaas@postgresql.org       75                 :UNC           0 :     pg_unreachable();
                                 76                 :                :     return NULL;
                                 77                 :                : }
                                 78                 :                : 
                                 79                 :                : /*
                                 80                 :                :  * Convert an advice tag, formatted as a string that has already been
                                 81                 :                :  * downcased as appropriate, to a pgpa_advice_tag_type.
                                 82                 :                :  *
                                 83                 :                :  * If we succeed, set *fail = false and return the result; if we fail,
                                 84                 :                :  * set *fail = true and return an arbitrary value.
                                 85                 :                :  */
                                 86                 :                : pgpa_advice_tag_type
   54 rhaas@postgresql.org       87                 :GNC      341727 : pgpa_parse_advice_tag(const char *tag, bool *fail)
                                 88                 :                : {
                                 89                 :         341727 :     *fail = false;
                                 90                 :                : 
                                 91   [ +  +  +  +  :         341727 :     switch (tag[0])
                                     +  +  +  +  +  
                                        +  +  +  + ]
                                 92                 :                :     {
                                 93                 :           8883 :         case 'b':
                                 94         [ +  + ]:           8883 :             if (strcmp(tag, "bitmap_heap_scan") == 0)
                                 95                 :           2220 :                 return PGPA_TAG_BITMAP_HEAP_SCAN;
                                 96                 :           6663 :             break;
   40                            97                 :           3316 :         case 'd':
                                 98         [ +  + ]:           3316 :             if (strcmp(tag, "do_not_scan") == 0)
                                 99                 :            462 :                 return PGPA_TAG_DO_NOT_SCAN;
                                100                 :           2854 :             break;
   54                           101                 :           5353 :         case 'f':
                                102         [ +  + ]:           5353 :             if (strcmp(tag, "foreign_join") == 0)
                                103                 :              8 :                 return PGPA_TAG_FOREIGN_JOIN;
                                104                 :           5345 :             break;
                                105                 :           2527 :         case 'g':
                                106         [ +  + ]:           2527 :             if (strcmp(tag, "gather") == 0)
                                107                 :            343 :                 return PGPA_TAG_GATHER;
                                108         [ +  + ]:           2184 :             if (strcmp(tag, "gather_merge") == 0)
                                109                 :            116 :                 return PGPA_TAG_GATHER_MERGE;
                                110                 :           2068 :             break;
                                111                 :           6427 :         case 'h':
                                112         [ +  + ]:           6427 :             if (strcmp(tag, "hash_join") == 0)
                                113                 :           5785 :                 return PGPA_TAG_HASH_JOIN;
                                114                 :            642 :             break;
                                115                 :          17072 :         case 'i':
                                116         [ +  + ]:          17072 :             if (strcmp(tag, "index_scan") == 0)
                                117                 :           7285 :                 return PGPA_TAG_INDEX_SCAN;
                                118         [ +  + ]:           9787 :             if (strcmp(tag, "index_only_scan") == 0)
                                119                 :           1710 :                 return PGPA_TAG_INDEX_ONLY_SCAN;
                                120                 :           8077 :             break;
                                121                 :          11866 :         case 'j':
                                122         [ +  + ]:          11866 :             if (strcmp(tag, "join_order") == 0)
                                123                 :          11194 :                 return PGPA_TAG_JOIN_ORDER;
                                124                 :            672 :             break;
                                125                 :           4262 :         case 'm':
                                126         [ +  + ]:           4262 :             if (strcmp(tag, "merge_join_materialize") == 0)
                                127                 :             58 :                 return PGPA_TAG_MERGE_JOIN_MATERIALIZE;
                                128         [ +  + ]:           4204 :             if (strcmp(tag, "merge_join_plain") == 0)
                                129                 :            912 :                 return PGPA_TAG_MERGE_JOIN_PLAIN;
                                130                 :           3292 :             break;
                                131                 :          63023 :         case 'n':
                                132         [ +  + ]:          63023 :             if (strcmp(tag, "nested_loop_materialize") == 0)
                                133                 :            920 :                 return PGPA_TAG_NESTED_LOOP_MATERIALIZE;
                                134         [ +  + ]:          62103 :             if (strcmp(tag, "nested_loop_memoize") == 0)
                                135                 :            444 :                 return PGPA_TAG_NESTED_LOOP_MEMOIZE;
                                136         [ +  + ]:          61659 :             if (strcmp(tag, "nested_loop_plain") == 0)
                                137                 :          11978 :                 return PGPA_TAG_NESTED_LOOP_PLAIN;
                                138         [ +  + ]:          49681 :             if (strcmp(tag, "no_gather") == 0)
                                139                 :          43270 :                 return PGPA_TAG_NO_GATHER;
                                140                 :           6411 :             break;
                                141                 :          84206 :         case 'p':
                                142         [ +  + ]:          84206 :             if (strcmp(tag, "partitionwise") == 0)
                                143                 :           3348 :                 return PGPA_TAG_PARTITIONWISE;
                                144                 :          80858 :             break;
                                145                 :          39854 :         case 's':
                                146         [ +  + ]:          39854 :             if (strcmp(tag, "semijoin_non_unique") == 0)
                                147                 :           1186 :                 return PGPA_TAG_SEMIJOIN_NON_UNIQUE;
                                148         [ +  + ]:          38668 :             if (strcmp(tag, "semijoin_unique") == 0)
                                149                 :            150 :                 return PGPA_TAG_SEMIJOIN_UNIQUE;
                                150         [ +  + ]:          38518 :             if (strcmp(tag, "seq_scan") == 0)
                                151                 :          16570 :                 return PGPA_TAG_SEQ_SCAN;
                                152                 :          21948 :             break;
                                153                 :          23855 :         case 't':
                                154         [ +  + ]:          23855 :             if (strcmp(tag, "tid_scan") == 0)
                                155                 :            405 :                 return PGPA_TAG_TID_SCAN;
                                156                 :          23450 :             break;
                                157                 :                :     }
                                158                 :                : 
                                159                 :                :     /* didn't work out */
                                160                 :         233363 :     *fail = true;
                                161                 :                : 
                                162                 :                :     /* return an arbitrary value to unwind the call stack */
                                163                 :         233363 :     return PGPA_TAG_SEQ_SCAN;
                                164                 :                : }
                                165                 :                : 
                                166                 :                : /*
                                167                 :                :  * Format a pgpa_advice_target as a string and append result to a StringInfo.
                                168                 :                :  */
                                169                 :                : void
                                170                 :         172895 : pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
                                171                 :                : {
                                172         [ +  + ]:         172895 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                173                 :                :     {
                                174                 :          12530 :         bool        first = true;
                                175                 :                :         char       *delims;
                                176                 :                : 
                                177         [ +  + ]:          12530 :         if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
                                178                 :             14 :             delims = "{}";
                                179                 :                :         else
                                180                 :          12516 :             delims = "()";
                                181                 :                : 
                                182                 :          12530 :         appendStringInfoChar(str, delims[0]);
                                183   [ +  -  +  +  :          53494 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                184                 :                :         {
                                185         [ +  + ]:          28434 :             if (first)
                                186                 :          12530 :                 first = false;
                                187                 :                :             else
                                188                 :          15904 :                 appendStringInfoChar(str, ' ');
                                189                 :          28434 :             pgpa_format_advice_target(str, child_target);
                                190                 :                :         }
                                191                 :          12530 :         appendStringInfoChar(str, delims[1]);
                                192                 :                :     }
                                193                 :                :     else
                                194                 :                :     {
                                195                 :                :         const char *rt_identifier;
                                196                 :                : 
                                197                 :         160365 :         rt_identifier = pgpa_identifier_string(&target->rid);
                                198                 :         160365 :         appendStringInfoString(str, rt_identifier);
                                199                 :                :     }
                                200                 :         172895 : }
                                201                 :                : 
                                202                 :                : /*
                                203                 :                :  * Format a pgpa_index_target as a string and append result to a StringInfo.
                                204                 :                :  */
                                205                 :                : void
                                206                 :          13972 : pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
                                207                 :                : {
                                208         [ +  + ]:          13972 :     if (itarget->indnamespace != NULL)
                                209                 :          13954 :         appendStringInfo(str, "%s.",
                                210                 :          13954 :                          quote_identifier(itarget->indnamespace));
                                211                 :          13972 :     appendStringInfoString(str, quote_identifier(itarget->indname));
                                212                 :          13972 : }
                                213                 :                : 
                                214                 :                : /*
                                215                 :                :  * Determine whether two pgpa_index_target objects are exactly identical.
                                216                 :                :  */
                                217                 :                : bool
                                218                 :              2 : pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
                                219                 :                : {
                                220                 :                :     /* indnamespace can be NULL, and two NULL values are equal */
                                221   [ +  -  +  + ]:              2 :     if ((i1->indnamespace != NULL || i2->indnamespace != NULL) &&
                                222   [ -  +  -  - ]:              1 :         (i1->indnamespace == NULL || i2->indnamespace == NULL ||
   54 rhaas@postgresql.org      223         [ #  # ]:UNC           0 :          strcmp(i1->indnamespace, i2->indnamespace) != 0))
   54 rhaas@postgresql.org      224                 :GNC           1 :         return false;
                                225         [ -  + ]:              1 :     if (strcmp(i1->indname, i2->indname) != 0)
   54 rhaas@postgresql.org      226                 :UNC           0 :         return false;
                                227                 :                : 
   54 rhaas@postgresql.org      228                 :GNC           1 :     return true;
                                229                 :                : }
                                230                 :                : 
                                231                 :                : /*
                                232                 :                :  * Check whether an identifier matches an any part of an advice target.
                                233                 :                :  */
                                234                 :                : bool
                                235                 :        1476545 : pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
                                236                 :                : {
                                237                 :                :     /* For non-identifiers, check all descendants. */
                                238         [ +  + ]:        1476545 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                239                 :                :     {
                                240   [ +  -  +  +  :         160202 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                241                 :                :         {
                                242         [ +  + ]:         153106 :             if (pgpa_identifier_matches_target(rid, child_target))
                                243                 :          74393 :                 return true;
                                244                 :                :         }
                                245                 :           3548 :         return false;
                                246                 :                :     }
                                247                 :                : 
                                248                 :                :     /* Straightforward comparisons of alias name and occurrence number. */
                                249         [ +  + ]:        1398604 :     if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
                                250                 :         796410 :         return false;
                                251         [ +  + ]:         602194 :     if (rid->occurrence != target->rid.occurrence)
                                252                 :          27158 :         return false;
                                253                 :                : 
                                254                 :                :     /*
                                255                 :                :      * If a relation identifier mentions a partition name, it should also
                                256                 :                :      * specify a partition schema. But the target may leave the schema NULL to
                                257                 :                :      * match anything.
                                258                 :                :      */
                                259   [ +  +  -  + ]:         575036 :     Assert(rid->partnsp != NULL || rid->partrel == NULL);
                                260   [ +  +  +  + ]:         575036 :     if (rid->partnsp != NULL && target->rid.partnsp != NULL &&
                                261         [ -  + ]:          48239 :         strcmp(rid->partnsp, target->rid.partnsp) != 0)
   54 rhaas@postgresql.org      262                 :UNC           0 :         return false;
                                263                 :                : 
                                264                 :                :     /*
                                265                 :                :      * These fields can be NULL on either side, but NULL only matches another
                                266                 :                :      * NULL.
                                267                 :                :      */
   54 rhaas@postgresql.org      268         [ +  + ]:GNC      575036 :     if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
                                269                 :             11 :         return false;
                                270         [ -  + ]:         575025 :     if (!strings_equal_or_both_null(rid->plan_name, target->rid.plan_name))
   54 rhaas@postgresql.org      271                 :UNC           0 :         return false;
                                272                 :                : 
   54 rhaas@postgresql.org      273                 :GNC      575025 :     return true;
                                274                 :                : }
                                275                 :                : 
                                276                 :                : /*
                                277                 :                :  * Match identifiers to advice targets and return an enum value indicating
                                278                 :                :  * the relationship between the set of keys and the set of targets.
                                279                 :                :  *
                                280                 :                :  * See the comments for pgpa_itm_type.
                                281                 :                :  */
                                282                 :                : pgpa_itm_type
                                283                 :         362305 : pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
                                284                 :                :                               pgpa_advice_target *target)
                                285                 :                : {
                                286                 :         362305 :     bool        all_rids_used = true;
                                287                 :         362305 :     bool        any_rids_used = false;
                                288                 :                :     bool        all_targets_used;
                                289                 :         362305 :     bool       *rids_used = palloc0_array(bool, nrids);
                                290                 :                : 
                                291                 :                :     all_targets_used =
                                292                 :         362305 :         pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
                                293                 :                : 
                                294         [ +  + ]:        1020026 :     for (int i = 0; i < nrids; ++i)
                                295                 :                :     {
                                296         [ +  + ]:         657721 :         if (rids_used[i])
                                297                 :         302669 :             any_rids_used = true;
                                298                 :                :         else
                                299                 :         355052 :             all_rids_used = false;
                                300                 :                :     }
                                301                 :                : 
                                302         [ +  + ]:         362305 :     if (all_rids_used)
                                303                 :                :     {
                                304         [ +  + ]:         138997 :         if (all_targets_used)
                                305                 :         121083 :             return PGPA_ITM_EQUAL;
                                306                 :                :         else
                                307                 :          17914 :             return PGPA_ITM_KEYS_ARE_SUBSET;
                                308                 :                :     }
                                309                 :                :     else
                                310                 :                :     {
                                311         [ +  + ]:         223308 :         if (all_targets_used)
                                312                 :          85203 :             return PGPA_ITM_TARGETS_ARE_SUBSET;
                                313         [ +  + ]:         138105 :         else if (any_rids_used)
                                314                 :          20956 :             return PGPA_ITM_INTERSECTING;
                                315                 :                :         else
                                316                 :         117149 :             return PGPA_ITM_DISJOINT;
                                317                 :                :     }
                                318                 :                : }
                                319                 :                : 
                                320                 :                : /*
                                321                 :                :  * Returns true if every target or sub-target is matched by at least one
                                322                 :                :  * identifier, and otherwise false.
                                323                 :                :  *
                                324                 :                :  * Also sets rids_used[i] = true for each identifier that matches at least one
                                325                 :                :  * target.
                                326                 :                :  */
                                327                 :                : static bool
                                328                 :         654688 : pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                329                 :                :                               pgpa_advice_target *target, bool *rids_used)
                                330                 :                : {
                                331                 :         654688 :     bool        result = false;
                                332                 :                : 
                                333         [ +  + ]:         654688 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                334                 :                :     {
                                335                 :         161320 :         result = true;
                                336                 :                : 
                                337   [ +  -  +  +  :         615023 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                338                 :                :         {
                                339         [ +  + ]:         292383 :             if (!pgpa_identifiers_cover_target(nrids, rids, child_target,
                                340                 :                :                                                rids_used))
                                341                 :         132056 :                 result = false;
                                342                 :                :         }
                                343                 :                :     }
                                344                 :                :     else
                                345                 :                :     {
                                346         [ +  + ]:        1534779 :         for (int i = 0; i < nrids; ++i)
                                347                 :                :         {
                                348         [ +  + ]:        1041411 :             if (pgpa_identifier_matches_target(&rids[i], target))
                                349                 :                :             {
                                350                 :         302669 :                 rids_used[i] = true;
                                351                 :         302669 :                 result = true;
                                352                 :                :             }
                                353                 :                :         }
                                354                 :                :     }
                                355                 :                : 
                                356                 :         654688 :     return result;
                                357                 :                : }
        

Generated by: LCOV version 2.5.0-beta