LCOV - differential code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_ast.c (source / functions) Coverage Total Hit UBC CBC
Current: 77aeca80249c9e640c811e80633a2e334a9320de vs 38afc3dcb25c45b744d4025029ce0a6c90b7059f Lines: 97.4 % 191 186 5 186
Current Date: 2026-07-25 19:08:27 +0900 Functions: 100.0 % 8 8 8
Baseline: lcov-20260725-baseline Branches: 90.9 % 154 140 14 140
Baseline Date: 2026-07-25 19:09:19 +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 *
  135 rhaas@postgresql.org       29                 :CBC      147369 : pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
                                 30                 :                : {
                                 31   [ +  +  +  +  :         147369 :     switch (advice_tag)
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                     +  +  +  +  +  
                                              +  - ]
                                 32                 :                :     {
                                 33                 :           2344 :         case PGPA_TAG_BITMAP_HEAP_SCAN:
                                 34                 :           2344 :             return "BITMAP_HEAP_SCAN";
  121                            35                 :            432 :         case PGPA_TAG_DO_NOT_SCAN:
                                 36                 :            432 :             return "DO_NOT_SCAN";
  135                            37                 :              1 :         case PGPA_TAG_FOREIGN_JOIN:
                                 38                 :              1 :             return "FOREIGN_JOIN";
                                 39                 :            172 :         case PGPA_TAG_GATHER:
                                 40                 :            172 :             return "GATHER";
                                 41                 :             61 :         case PGPA_TAG_GATHER_MERGE:
                                 42                 :             61 :             return "GATHER_MERGE";
                                 43                 :           4728 :         case PGPA_TAG_HASH_JOIN:
                                 44                 :           4728 :             return "HASH_JOIN";
                                 45                 :           1664 :         case PGPA_TAG_INDEX_ONLY_SCAN:
                                 46                 :           1664 :             return "INDEX_ONLY_SCAN";
                                 47                 :          13359 :         case PGPA_TAG_INDEX_SCAN:
                                 48                 :          13359 :             return "INDEX_SCAN";
                                 49                 :          11239 :         case PGPA_TAG_JOIN_ORDER:
                                 50                 :          11239 :             return "JOIN_ORDER";
                                 51                 :             28 :         case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
                                 52                 :             28 :             return "MERGE_JOIN_MATERIALIZE";
                                 53                 :            587 :         case PGPA_TAG_MERGE_JOIN_PLAIN:
                                 54                 :            587 :             return "MERGE_JOIN_PLAIN";
                                 55                 :            525 :         case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
                                 56                 :            525 :             return "NESTED_LOOP_MATERIALIZE";
                                 57                 :            238 :         case PGPA_TAG_NESTED_LOOP_MEMOIZE:
                                 58                 :            238 :             return "NESTED_LOOP_MEMOIZE";
                                 59                 :           9388 :         case PGPA_TAG_NESTED_LOOP_PLAIN:
                                 60                 :           9388 :             return "NESTED_LOOP_PLAIN";
                                 61                 :          72367 :         case PGPA_TAG_NO_GATHER:
                                 62                 :          72367 :             return "NO_GATHER";
                                 63                 :           2200 :         case PGPA_TAG_PARTITIONWISE:
                                 64                 :           2200 :             return "PARTITIONWISE";
                                 65                 :            632 :         case PGPA_TAG_SEMIJOIN_NON_UNIQUE:
                                 66                 :            632 :             return "SEMIJOIN_NON_UNIQUE";
                                 67                 :             79 :         case PGPA_TAG_SEMIJOIN_UNIQUE:
                                 68                 :             79 :             return "SEMIJOIN_UNIQUE";
                                 69                 :          26903 :         case PGPA_TAG_SEQ_SCAN:
                                 70                 :          26903 :             return "SEQ_SCAN";
                                 71                 :            422 :         case PGPA_TAG_TID_SCAN:
                                 72                 :            422 :             return "TID_SCAN";
                                 73                 :                :     }
                                 74                 :                : 
  135 rhaas@postgresql.org       75                 :UBC           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
  135 rhaas@postgresql.org       87                 :CBC      351008 : pgpa_parse_advice_tag(const char *tag, bool *fail)
                                 88                 :                : {
                                 89                 :         351008 :     *fail = false;
                                 90                 :                : 
                                 91   [ +  +  +  +  :         351008 :     switch (tag[0])
                                     +  +  +  +  +  
                                        +  +  +  + ]
                                 92                 :                :     {
                                 93                 :           8899 :         case 'b':
                                 94         [ +  + ]:           8899 :             if (strcmp(tag, "bitmap_heap_scan") == 0)
                                 95                 :           2238 :                 return PGPA_TAG_BITMAP_HEAP_SCAN;
                                 96                 :           6661 :             break;
  121                            97                 :           3126 :         case 'd':
                                 98         [ +  + ]:           3126 :             if (strcmp(tag, "do_not_scan") == 0)
                                 99                 :            233 :                 return PGPA_TAG_DO_NOT_SCAN;
                                100                 :           2893 :             break;
  135                           101                 :           5458 :         case 'f':
                                102         [ +  + ]:           5458 :             if (strcmp(tag, "foreign_join") == 0)
                                103                 :              8 :                 return PGPA_TAG_FOREIGN_JOIN;
                                104                 :           5450 :             break;
                                105                 :           2611 :         case 'g':
                                106         [ +  + ]:           2611 :             if (strcmp(tag, "gather") == 0)
                                107                 :            339 :                 return PGPA_TAG_GATHER;
                                108         [ +  + ]:           2272 :             if (strcmp(tag, "gather_merge") == 0)
                                109                 :            118 :                 return PGPA_TAG_GATHER_MERGE;
                                110                 :           2154 :             break;
                                111                 :           7343 :         case 'h':
                                112         [ +  + ]:           7343 :             if (strcmp(tag, "hash_join") == 0)
                                113                 :           6703 :                 return PGPA_TAG_HASH_JOIN;
                                114                 :            640 :             break;
                                115                 :          17614 :         case 'i':
                                116         [ +  + ]:          17614 :             if (strcmp(tag, "index_scan") == 0)
                                117                 :           8067 :                 return PGPA_TAG_INDEX_SCAN;
                                118         [ +  + ]:           9547 :             if (strcmp(tag, "index_only_scan") == 0)
                                119                 :           1450 :                 return PGPA_TAG_INDEX_ONLY_SCAN;
                                120                 :           8097 :             break;
                                121                 :          11994 :         case 'j':
                                122         [ +  + ]:          11994 :             if (strcmp(tag, "join_order") == 0)
                                123                 :          11262 :                 return PGPA_TAG_JOIN_ORDER;
                                124                 :            732 :             break;
                                125                 :           4356 :         case 'm':
                                126         [ +  + ]:           4356 :             if (strcmp(tag, "merge_join_materialize") == 0)
                                127                 :             60 :                 return PGPA_TAG_MERGE_JOIN_MATERIALIZE;
                                128         [ +  + ]:           4296 :             if (strcmp(tag, "merge_join_plain") == 0)
                                129                 :            990 :                 return PGPA_TAG_MERGE_JOIN_PLAIN;
                                130                 :           3306 :             break;
                                131                 :          62198 :         case 'n':
                                132         [ +  + ]:          62198 :             if (strcmp(tag, "nested_loop_materialize") == 0)
                                133                 :            960 :                 return PGPA_TAG_NESTED_LOOP_MATERIALIZE;
                                134         [ +  + ]:          61238 :             if (strcmp(tag, "nested_loop_memoize") == 0)
                                135                 :            456 :                 return PGPA_TAG_NESTED_LOOP_MEMOIZE;
                                136         [ +  + ]:          60782 :             if (strcmp(tag, "nested_loop_plain") == 0)
                                137                 :          11096 :                 return PGPA_TAG_NESTED_LOOP_PLAIN;
                                138         [ +  + ]:          49686 :             if (strcmp(tag, "no_gather") == 0)
                                139                 :          43679 :                 return PGPA_TAG_NO_GATHER;
                                140                 :           6007 :             break;
                                141                 :          88995 :         case 'p':
                                142         [ +  + ]:          88995 :             if (strcmp(tag, "partitionwise") == 0)
                                143                 :           3376 :                 return PGPA_TAG_PARTITIONWISE;
                                144                 :          85619 :             break;
                                145                 :          42458 :         case 's':
                                146         [ +  + ]:          42458 :             if (strcmp(tag, "semijoin_non_unique") == 0)
                                147                 :           1194 :                 return PGPA_TAG_SEMIJOIN_NON_UNIQUE;
                                148         [ +  + ]:          41264 :             if (strcmp(tag, "semijoin_unique") == 0)
                                149                 :            150 :                 return PGPA_TAG_SEMIJOIN_UNIQUE;
                                150         [ +  + ]:          41114 :             if (strcmp(tag, "seq_scan") == 0)
                                151                 :          16781 :                 return PGPA_TAG_SEQ_SCAN;
                                152                 :          24333 :             break;
                                153                 :          24023 :         case 't':
                                154         [ +  + ]:          24023 :             if (strcmp(tag, "tid_scan") == 0)
                                155                 :            405 :                 return PGPA_TAG_TID_SCAN;
                                156                 :          23618 :             break;
                                157                 :                :     }
                                158                 :                : 
                                159                 :                :     /* didn't work out */
                                160                 :         241443 :     *fail = true;
                                161                 :                : 
                                162                 :                :     /* return an arbitrary value to unwind the call stack */
                                163                 :         241443 :     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                 :         176528 : pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
                                171                 :                : {
                                172         [ +  + ]:         176528 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                173                 :                :     {
                                174                 :          12604 :         bool        first = true;
                                175                 :                :         char       *delims;
                                176                 :                : 
                                177         [ +  + ]:          12604 :         if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
                                178                 :             14 :             delims = "{}";
                                179                 :                :         else
                                180                 :          12590 :             delims = "()";
                                181                 :                : 
                                182                 :          12604 :         appendStringInfoChar(str, delims[0]);
                                183   [ +  -  +  +  :          54367 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                184                 :                :         {
                                185         [ +  + ]:          29159 :             if (first)
                                186                 :          12604 :                 first = false;
                                187                 :                :             else
                                188                 :          16555 :                 appendStringInfoChar(str, ' ');
                                189                 :          29159 :             pgpa_format_advice_target(str, child_target);
                                190                 :                :         }
                                191                 :          12604 :         appendStringInfoChar(str, delims[1]);
                                192                 :                :     }
                                193                 :                :     else
                                194                 :                :     {
                                195                 :                :         const char *rt_identifier;
                                196                 :                : 
                                197                 :         163924 :         rt_identifier = pgpa_identifier_string(&target->rid);
                                198                 :         163924 :         appendStringInfoString(str, rt_identifier);
                                199                 :                :     }
                                200                 :         176528 : }
                                201                 :                : 
                                202                 :                : /*
                                203                 :                :  * Format a pgpa_index_target as a string and append result to a StringInfo.
                                204                 :                :  */
                                205                 :                : void
                                206                 :          15023 : pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
                                207                 :                : {
                                208         [ +  + ]:          15023 :     if (itarget->indnamespace != NULL)
                                209                 :          15005 :         appendStringInfo(str, "%s.",
                                210                 :          15005 :                          quote_identifier(itarget->indnamespace));
                                211                 :          15023 :     appendStringInfoString(str, quote_identifier(itarget->indname));
                                212                 :          15023 : }
                                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 ||
  135 rhaas@postgresql.org      223         [ #  # ]:UBC           0 :          strcmp(i1->indnamespace, i2->indnamespace) != 0))
  135 rhaas@postgresql.org      224                 :CBC           1 :         return false;
                                225         [ -  + ]:              1 :     if (strcmp(i1->indname, i2->indname) != 0)
  135 rhaas@postgresql.org      226                 :UBC           0 :         return false;
                                227                 :                : 
  135 rhaas@postgresql.org      228                 :CBC           1 :     return true;
                                229                 :                : }
                                230                 :                : 
                                231                 :                : /*
                                232                 :                :  * Check whether an identifier matches an any part of an advice target.
                                233                 :                :  */
                                234                 :                : bool
                                235                 :        1538657 : pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
                                236                 :                : {
                                237                 :                :     /* For non-identifiers, check all descendants. */
                                238         [ +  + ]:        1538657 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                239                 :                :     {
                                240   [ +  -  +  +  :         171706 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                241                 :                :         {
                                242         [ +  + ]:         164790 :             if (pgpa_identifier_matches_target(rid, child_target))
                                243                 :          79285 :                 return true;
                                244                 :                :         }
                                245                 :           3458 :         return false;
                                246                 :                :     }
                                247                 :                : 
                                248                 :                :     /* Straightforward comparisons of alias name and occurrence number. */
                                249         [ +  + ]:        1455914 :     if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
                                250                 :         819570 :         return false;
                                251         [ +  + ]:         636344 :     if (rid->occurrence != target->rid.occurrence)
                                252                 :          26864 :         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   [ +  +  -  + ]:         609480 :     Assert(rid->partnsp != NULL || rid->partrel == NULL);
                                260   [ +  +  +  + ]:         609480 :     if (rid->partnsp != NULL && target->rid.partnsp != NULL &&
                                261         [ -  + ]:          48309 :         strcmp(rid->partnsp, target->rid.partnsp) != 0)
  135 rhaas@postgresql.org      262                 :UBC           0 :         return false;
                                263                 :                : 
                                264                 :                :     /*
                                265                 :                :      * These fields can be NULL on either side, but NULL only matches another
                                266                 :                :      * NULL.
                                267                 :                :      */
  135 rhaas@postgresql.org      268         [ +  + ]:CBC      609480 :     if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
                                269                 :             11 :         return false;
                                270         [ -  + ]:         609469 :     if (!strings_equal_or_both_null(rid->plan_name, target->rid.plan_name))
  135 rhaas@postgresql.org      271                 :UBC           0 :         return false;
                                272                 :                : 
  135 rhaas@postgresql.org      273                 :CBC      609469 :     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                 :         392750 : pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids,
                                284                 :                :                               pgpa_advice_target *target)
                                285                 :                : {
                                286                 :         392750 :     bool        all_rids_used = true;
                                287                 :         392750 :     bool        any_rids_used = false;
                                288                 :                :     bool        all_targets_used;
                                289                 :         392750 :     bool       *rids_used = palloc0_array(bool, nrids);
                                290                 :                : 
                                291                 :                :     all_targets_used =
                                292                 :         392750 :         pgpa_identifiers_cover_target(nrids, rids, target, rids_used);
                                293                 :                : 
                                294         [ +  + ]:        1105557 :     for (int i = 0; i < nrids; ++i)
                                295                 :                :     {
                                296         [ +  + ]:         712807 :         if (rids_used[i])
                                297                 :         320559 :             any_rids_used = true;
                                298                 :                :         else
                                299                 :         392248 :             all_rids_used = false;
                                300                 :                :     }
                                301                 :                : 
                                302         [ +  + ]:         392750 :     if (all_rids_used)
                                303                 :                :     {
                                304         [ +  + ]:         145723 :         if (all_targets_used)
                                305                 :         128723 :             return PGPA_ITM_EQUAL;
                                306                 :                :         else
                                307                 :          17000 :             return PGPA_ITM_KEYS_ARE_SUBSET;
                                308                 :                :     }
                                309                 :                :     else
                                310                 :                :     {
                                311         [ +  + ]:         247027 :         if (all_targets_used)
                                312                 :          96684 :             return PGPA_ITM_TARGETS_ARE_SUBSET;
                                313         [ +  + ]:         150343 :         else if (any_rids_used)
                                314                 :          21871 :             return PGPA_ITM_INTERSECTING;
                                315                 :                :         else
                                316                 :         128472 :             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                 :         687352 : pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids,
                                329                 :                :                               pgpa_advice_target *target, bool *rids_used)
                                330                 :                : {
                                331                 :         687352 :     bool        result = false;
                                332                 :                : 
                                333         [ +  + ]:         687352 :     if (target->ttype != PGPA_TARGET_IDENTIFIER)
                                334                 :                :     {
                                335                 :         168375 :         result = true;
                                336                 :                : 
                                337   [ +  -  +  +  :         631352 :         foreach_ptr(pgpa_advice_target, child_target, target->children)
                                              +  + ]
                                338                 :                :         {
                                339         [ +  + ]:         294602 :             if (!pgpa_identifiers_cover_target(nrids, rids, child_target,
                                340                 :                :                                                rids_used))
                                341                 :         131871 :                 result = false;
                                342                 :                :         }
                                343                 :                :     }
                                344                 :                :     else
                                345                 :                :     {
                                346         [ +  + ]:        1594180 :         for (int i = 0; i < nrids; ++i)
                                347                 :                :         {
                                348         [ +  + ]:        1075203 :             if (pgpa_identifier_matches_target(&rids[i], target))
                                349                 :                :             {
                                350                 :         320559 :                 rids_used[i] = true;
                                351                 :         320559 :                 result = true;
                                352                 :                :             }
                                353                 :                :         }
                                354                 :                :     }
                                355                 :                : 
                                356                 :         687352 :     return result;
                                357                 :                : }
        

Generated by: LCOV version 2.0-1