LCOV - differential code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_ast.c (source / functions) Coverage Total Hit LBC UBC CBC
Current: f76c13edadc2b319036e0d238703ed56bb23f934 vs 9e17d25e79d4756be08b4a5521b4b58450217137 Lines: 97.4 % 194 189 5 189
Current Date: 2026-09-20 14:13:17 +0900 Functions: 100.0 % 8 8 8
Baseline: lcov-20260920-baseline Branches: 90.0 % 150 135 1 14 135
Baseline Date: 2026-09-20 14:13:13 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 3 3 3
(7,30] days: 100.0 % 4 4 4
(30,360] days: 97.3 % 187 182 5 182
Function coverage date bins:
(30,360] days: 100.0 % 8 8 8
Branch coverage date bins:
(1,7] days: 75.0 % 4 3 1 3
(7,30] days: 50.0 % 2 1 1 1
(30,360] days: 91.0 % 144 131 13 131

 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 *
  192 rhaas@postgresql.org       30                 :CBC      143144 : pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
                                 31                 :                : {
                                 32   [ +  +  +  +  :         143144 :     switch (advice_tag)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                              +  - ]
                                 33                 :                :     {
                                 34                 :           2272 :         case PGPA_TAG_BITMAP_HEAP_SCAN:
                                 35                 :           2272 :             return "BITMAP_HEAP_SCAN";
  178                            36                 :            434 :         case PGPA_TAG_DO_NOT_SCAN:
                                 37                 :            434 :             return "DO_NOT_SCAN";
  192                            38                 :              1 :         case PGPA_TAG_FOREIGN_JOIN:
                                 39                 :              1 :             return "FOREIGN_JOIN";
                                 40                 :            171 :         case PGPA_TAG_GATHER:
                                 41                 :            171 :             return "GATHER";
                                 42                 :             58 :         case PGPA_TAG_GATHER_MERGE:
                                 43                 :             58 :             return "GATHER_MERGE";
                                 44                 :           4500 :         case PGPA_TAG_HASH_JOIN:
                                 45                 :           4500 :             return "HASH_JOIN";
                                 46                 :           1686 :         case PGPA_TAG_INDEX_ONLY_SCAN:
                                 47                 :           1686 :             return "INDEX_ONLY_SCAN";
                                 48                 :          12978 :         case PGPA_TAG_INDEX_SCAN:
                                 49                 :          12978 :             return "INDEX_SCAN";
                                 50                 :          10947 :         case PGPA_TAG_JOIN_ORDER:
                                 51                 :          10947 :             return "JOIN_ORDER";
                                 52                 :             29 :         case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
                                 53                 :             29 :             return "MERGE_JOIN_MATERIALIZE";
                                 54                 :            537 :         case PGPA_TAG_MERGE_JOIN_PLAIN:
                                 55                 :            537 :             return "MERGE_JOIN_PLAIN";
                                 56                 :            526 :         case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
                                 57                 :            526 :             return "NESTED_LOOP_MATERIALIZE";
                                 58                 :            231 :         case PGPA_TAG_NESTED_LOOP_MEMOIZE:
                                 59                 :            231 :             return "NESTED_LOOP_MEMOIZE";
                                 60                 :           9053 :         case PGPA_TAG_NESTED_LOOP_PLAIN:
                                 61                 :           9053 :             return "NESTED_LOOP_PLAIN";
                                 62                 :          70568 :         case PGPA_TAG_NO_GATHER:
                                 63                 :          70568 :             return "NO_GATHER";
                                 64                 :           2087 :         case PGPA_TAG_PARTITIONWISE:
                                 65                 :           2087 :             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                 :          25940 :         case PGPA_TAG_SEQ_SCAN:
                                 71                 :          25940 :             return "SEQ_SCAN";
                                 72                 :            421 :         case PGPA_TAG_TID_SCAN:
                                 73                 :            421 :             return "TID_SCAN";
                                 74                 :                :     }
                                 75                 :                : 
  192 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
  192 rhaas@postgresql.org       88                 :CBC      340066 : pgpa_parse_advice_tag(const char *tag, bool *fail)
                                 89                 :                : {
                                 90                 :         340066 :     *fail = false;
                                 91                 :                : 
                                 92   [ +  +  +  +  :         340066 :     switch (tag[0])
                                     +  +  +  +  +  
                                        +  +  +  + ]
                                 93                 :                :     {
                                 94                 :           8918 :         case 'b':
                                 95         [ +  + ]:           8918 :             if (strcmp(tag, "bitmap_heap_scan") == 0)
                                 96                 :           2173 :                 return PGPA_TAG_BITMAP_HEAP_SCAN;
                                 97                 :           6745 :             break;
  178                            98                 :           2826 :         case 'd':
                                 99         [ +  + ]:           2826 :             if (strcmp(tag, "do_not_scan") == 0)
                                100                 :            234 :                 return PGPA_TAG_DO_NOT_SCAN;
                                101                 :           2592 :             break;
  192                           102                 :           4946 :         case 'f':
                                103         [ +  + ]:           4946 :             if (strcmp(tag, "foreign_join") == 0)
                                104                 :             10 :                 return PGPA_TAG_FOREIGN_JOIN;
                                105                 :           4936 :             break;
                                106                 :           2271 :         case 'g':
                                107         [ +  + ]:           2271 :             if (strcmp(tag, "gather") == 0)
                                108                 :            337 :                 return PGPA_TAG_GATHER;
                                109         [ +  + ]:           1934 :             if (strcmp(tag, "gather_merge") == 0)
                                110                 :            112 :                 return PGPA_TAG_GATHER_MERGE;
                                111                 :           1822 :             break;
                                112                 :           7041 :         case 'h':
                                113         [ +  + ]:           7041 :             if (strcmp(tag, "hash_join") == 0)
                                114                 :           6401 :                 return PGPA_TAG_HASH_JOIN;
                                115                 :            640 :             break;
                                116                 :          17331 :         case 'i':
                                117         [ +  + ]:          17331 :             if (strcmp(tag, "index_scan") == 0)
                                118                 :           7783 :                 return PGPA_TAG_INDEX_SCAN;
                                119         [ +  + ]:           9548 :             if (strcmp(tag, "index_only_scan") == 0)
                                120                 :           1474 :                 return PGPA_TAG_INDEX_ONLY_SCAN;
                                121                 :           8074 :             break;
                                122                 :          11712 :         case 'j':
                                123         [ +  + ]:          11712 :             if (strcmp(tag, "join_order") == 0)
                                124                 :          10979 :                 return PGPA_TAG_JOIN_ORDER;
                                125                 :            733 :             break;
                                126                 :           4437 :         case 'm':
                                127         [ +  + ]:           4437 :             if (strcmp(tag, "merge_join_materialize") == 0)
                                128                 :             62 :                 return PGPA_TAG_MERGE_JOIN_MATERIALIZE;
                                129         [ +  + ]:           4375 :             if (strcmp(tag, "merge_join_plain") == 0)
                                130                 :            982 :                 return PGPA_TAG_MERGE_JOIN_PLAIN;
                                131                 :           3393 :             break;
                                132                 :          61095 :         case 'n':
                                133         [ +  + ]:          61095 :             if (strcmp(tag, "nested_loop_materialize") == 0)
                                134                 :            960 :                 return PGPA_TAG_NESTED_LOOP_MATERIALIZE;
                                135         [ +  + ]:          60135 :             if (strcmp(tag, "nested_loop_memoize") == 0)
                                136                 :            438 :                 return PGPA_TAG_NESTED_LOOP_MEMOIZE;
                                137         [ +  + ]:          59697 :             if (strcmp(tag, "nested_loop_plain") == 0)
                                138                 :          10972 :                 return PGPA_TAG_NESTED_LOOP_PLAIN;
                                139         [ +  + ]:          48725 :             if (strcmp(tag, "no_gather") == 0)
                                140                 :          42921 :                 return PGPA_TAG_NO_GATHER;
                                141                 :           5804 :             break;
                                142                 :          87492 :         case 'p':
                                143         [ +  + ]:          87492 :             if (strcmp(tag, "partitionwise") == 0)
                                144                 :           3200 :                 return PGPA_TAG_PARTITIONWISE;
                                145                 :          84292 :             break;
                                146                 :          41337 :         case 's':
                                147         [ +  + ]:          41337 :             if (strcmp(tag, "semijoin_non_unique") == 0)
                                148                 :           1176 :                 return PGPA_TAG_SEMIJOIN_NON_UNIQUE;
                                149         [ +  + ]:          40161 :             if (strcmp(tag, "semijoin_unique") == 0)
                                150                 :            156 :                 return PGPA_TAG_SEMIJOIN_UNIQUE;
                                151         [ +  + ]:          40005 :             if (strcmp(tag, "seq_scan") == 0)
                                152                 :          16470 :                 return PGPA_TAG_SEQ_SCAN;
                                153                 :          23535 :             break;
                                154                 :          24213 :         case 't':
                                155         [ +  + ]:          24213 :             if (strcmp(tag, "tid_scan") == 0)
                                156                 :            404 :                 return PGPA_TAG_TID_SCAN;
                                157                 :          23809 :             break;
                                158                 :                :     }
                                159                 :                : 
                                160                 :                :     /* didn't work out */
                                161                 :         232822 :     *fail = true;
                                162                 :                : 
                                163                 :                :     /* return an arbitrary value to unwind the call stack */
                                164                 :         232822 :     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                 :         171123 : pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
                                172                 :                : {
   11                           173                 :         171123 :     check_stack_depth();
                                174                 :                : 
  192                           175         [ +  + ]:         171123 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                176                 :                :     {
                                177                 :          12150 :         bool        first = true;
                                178                 :                :         char       *delims;
                                179                 :                : 
                                180         [ +  + ]:          12150 :         if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
                                181                 :             19 :             delims = "{}";
                                182                 :                :         else
                                183                 :          12131 :             delims = "()";
                                184                 :                : 
                                185                 :          12150 :         appendStringInfoChar(str, delims[0]);
                                186   [ +  -  +  +  :          52279 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                187                 :                :         {
                                188         [ +  + ]:          27979 :             if (first)
                                189                 :          12150 :                 first = false;
                                190                 :                :             else
                                191                 :          15829 :                 appendStringInfoChar(str, ' ');
                                192                 :          27979 :             pgpa_format_advice_target(str, child_target);
                                193                 :                :         }
                                194                 :          12150 :         appendStringInfoChar(str, delims[1]);
                                195                 :                :     }
                                196                 :                :     else
                                197                 :                :     {
                                198                 :                :         const char *rt_identifier;
                                199                 :                : 
                                200                 :         158973 :         rt_identifier = pgpa_identifier_string(&target->rid);
                                201                 :         158973 :         appendStringInfoString(str, rt_identifier);
                                202                 :                :     }
                                203                 :         171123 : }
                                204                 :                : 
                                205                 :                : /*
                                206                 :                :  * Format a pgpa_index_target as a string and append result to a StringInfo.
                                207                 :                :  */
                                208                 :                : void
                                209                 :          14664 : pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
                                210                 :                : {
                                211         [ +  + ]:          14664 :     if (itarget->indnamespace != NULL)
                                212                 :          14646 :         appendStringInfo(str, "%s.",
                                213                 :          14646 :                          quote_identifier(itarget->indnamespace));
                                214                 :          14664 :     appendStringInfoString(str, quote_identifier(itarget->indname));
                                215                 :          14664 : }
                                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 ||
  192 rhaas@postgresql.org      226         [ #  # ]:UBC           0 :          strcmp(i1->indnamespace, i2->indnamespace) != 0))
  192 rhaas@postgresql.org      227                 :CBC           1 :         return false;
                                228         [ -  + ]:              1 :     if (strcmp(i1->indname, i2->indname) != 0)
  192 rhaas@postgresql.org      229                 :UBC           0 :         return false;
                                230                 :                : 
  192 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                 :        1289578 : pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
                                239                 :                : {
   11                           240                 :        1289578 :     check_stack_depth();
                                241                 :                : 
                                242                 :                :     /* For non-identifiers, check all descendants. */
  192                           243         [ +  + ]:        1289578 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                244                 :                :     {
                                245   [ +  -  +  +  :         151371 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                246                 :                :         {
                                247         [ +  + ]:         146177 :             if (pgpa_identifier_matches_target(rid, child_target))
                                248                 :          70713 :                 return true;
                                249                 :                :         }
                                250                 :           2597 :         return false;
                                251                 :                :     }
                                252                 :                : 
                                253                 :                :     /* Straightforward comparisons of alias name and occurrence number. */
                                254         [ +  + ]:        1216268 :     if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
                                255                 :         657496 :         return false;
                                256         [ +  + ]:         558772 :     if (rid->occurrence != target->rid.occurrence)
                                257                 :          16776 :         return false;
                                258                 :                : 
                                259                 :                :     /*
                                260                 :                :      * A relation identifier should either include both of partition name and
                                261                 :                :      * partition schema, or neither one.
                                262                 :                :      */
    4                           263         [ -  + ]:         541996 :     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         [ +  + ]:         541996 :     if (!strings_equal_or_both_null(rid->partnsp, target->rid.partnsp))
                                270                 :             11 :         return false;
  192                           271         [ -  + ]:         541985 :     if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
  192 rhaas@postgresql.org      272                 :UBC           0 :         return false;
  192 rhaas@postgresql.org      273         [ -  + ]:CBC      541985 :     if (!strings_equal_or_both_null(rid->plan_name, target->rid.plan_name))
  192 rhaas@postgresql.org      274                 :UBC           0 :         return false;
                                275                 :                : 
  192 rhaas@postgresql.org      276                 :CBC      541985 :     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                 :         335847 : pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
                                287                 :                :                               pgpa_advice_target *target)
                                288                 :                : {
                                289                 :         335847 :     bool        all_rids_used = true;
                                290                 :         335847 :     bool        any_rids_used = false;
                                291                 :                :     bool        all_targets_used;
                                292                 :         335847 :     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                 :                :      */
   11                           299         [ -  + ]:         335847 :     CHECK_FOR_INTERRUPTS();
                                300                 :                : 
                                301                 :                :     all_targets_used =
  192                           302                 :         335847 :         pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
                                303                 :                : 
                                304         [ +  + ]:         921283 :     for (int i = 0; i < nrids; ++i)
                                305                 :                :     {
                                306         [ +  + ]:         585436 :         if (rids_used[i])
                                307                 :         265682 :             any_rids_used = true;
                                308                 :                :         else
                                309                 :         319754 :             all_rids_used = false;
                                310                 :                :     }
                                311                 :                : 
                                312         [ +  + ]:         335847 :     if (all_rids_used)
                                313                 :                :     {
                                314         [ +  + ]:         132511 :         if (all_targets_used)
                                315                 :         120758 :             return PGPA_ITM_EQUAL;
                                316                 :                :         else
                                317                 :          11753 :             return PGPA_ITM_KEYS_ARE_SUBSET;
                                318                 :                :     }
                                319                 :                :     else
                                320                 :                :     {
                                321         [ +  + ]:         203336 :         if (all_targets_used)
                                322                 :          79351 :             return PGPA_ITM_TARGETS_ARE_SUBSET;
                                323         [ +  + ]:         123985 :         else if (any_rids_used)
                                324                 :          11467 :             return PGPA_ITM_INTERSECTING;
                                325                 :                :         else
                                326                 :         112518 :             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                 :         556359 : pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                339                 :                :                               pgpa_advice_target *target, bool *rids_used)
                                340                 :                : {
                                341                 :         556359 :     bool        result = false;
                                342                 :                : 
   11                           343                 :         556359 :     check_stack_depth();
                                344                 :                : 
  192                           345         [ +  + ]:         556359 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                346                 :                :     {
                                347                 :         131841 :         result = true;
                                348                 :                : 
                                349   [ +  -  +  +  :         484194 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                350                 :                :         {
                                351         [ +  + ]:         220512 :             if (!pgpa_identifiers_cover_target(nrids, rids, child_target,
                                352                 :                :                                                rids_used))
                                353                 :          97549 :                 result = false;
                                354                 :                :         }
                                355                 :                :     }
                                356                 :                :     else
                                357                 :                :     {
                                358         [ +  + ]:        1283798 :         for (int i = 0; i < nrids; ++i)
                                359                 :                :         {
                                360         [ +  + ]:         859280 :             if (pgpa_identifier_matches_target(&rids[i], target))
                                361                 :                :             {
                                362                 :         265682 :                 rids_used[i] = true;
                                363                 :         265682 :                 result = true;
                                364                 :                :             }
                                365                 :                :         }
                                366                 :                :     }
                                367                 :                : 
                                368                 :         556359 :     return result;
                                369                 :                : }
        

Generated by: LCOV version 2.0-1