LCOV - differential code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_output.c (source / functions) Coverage Total Hit UNC GNC
Current: 0e5ff9b9b45a657aea12440478dc002e9b01f138 vs 0123ce131fca454009439dfa3b2266d1d40737d7 Lines: 95.2 % 209 199 10 199
Current Date: 2026-03-14 14:10:32 -0400 Functions: 100.0 % 13 13 13
Baseline: lcov-20260315-024220-baseline Branches: 84.0 % 119 100 19 100
Baseline Date: 2026-03-14 15:27:56 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 95.2 % 209 199 10 199
Function coverage date bins:
(1,7] days: 100.0 % 13 13 13
Branch coverage date bins:
(1,7] days: 84.0 % 119 100 19 100

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * pgpa_output.c
                                  4                 :                :  *    produce textual output from the results of a plan tree walk
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  *    contrib/pg_plan_advice/pgpa_output.c
                                  9                 :                :  *
                                 10                 :                :  *-------------------------------------------------------------------------
                                 11                 :                :  */
                                 12                 :                : 
                                 13                 :                : #include "postgres.h"
                                 14                 :                : 
                                 15                 :                : #include "pgpa_output.h"
                                 16                 :                : #include "pgpa_scan.h"
                                 17                 :                : 
                                 18                 :                : #include "nodes/parsenodes.h"
                                 19                 :                : #include "parser/parsetree.h"
                                 20                 :                : #include "utils/builtins.h"
                                 21                 :                : #include "utils/lsyscache.h"
                                 22                 :                : 
                                 23                 :                : /*
                                 24                 :                :  * Context object for textual advice generation.
                                 25                 :                :  *
                                 26                 :                :  * rt_identifiers is the caller-provided array of range table identifiers.
                                 27                 :                :  * See the comments at the top of pgpa_identifier.c for more details.
                                 28                 :                :  *
                                 29                 :                :  * buf is the caller-provided output buffer.
                                 30                 :                :  *
                                 31                 :                :  * wrap_column is the wrap column, so that we don't create output that is
                                 32                 :                :  * too wide. See pgpa_maybe_linebreak() and comments in pgpa_output_advice.
                                 33                 :                :  */
                                 34                 :                : typedef struct pgpa_output_context
                                 35                 :                : {
                                 36                 :                :     const char **rid_strings;
                                 37                 :                :     StringInfo  buf;
                                 38                 :                :     int         wrap_column;
                                 39                 :                : } pgpa_output_context;
                                 40                 :                : 
                                 41                 :                : static void pgpa_output_unrolled_join(pgpa_output_context *context,
                                 42                 :                :                                       pgpa_unrolled_join *join);
                                 43                 :                : static void pgpa_output_join_member(pgpa_output_context *context,
                                 44                 :                :                                     pgpa_join_member *member);
                                 45                 :                : static void pgpa_output_scan_strategy(pgpa_output_context *context,
                                 46                 :                :                                       pgpa_scan_strategy strategy,
                                 47                 :                :                                       List *scans);
                                 48                 :                : static void pgpa_output_relation_name(pgpa_output_context *context, Oid relid);
                                 49                 :                : static void pgpa_output_query_feature(pgpa_output_context *context,
                                 50                 :                :                                       pgpa_qf_type type,
                                 51                 :                :                                       List *query_features);
                                 52                 :                : static void pgpa_output_simple_strategy(pgpa_output_context *context,
                                 53                 :                :                                         char *strategy,
                                 54                 :                :                                         List *relid_sets);
                                 55                 :                : static void pgpa_output_no_gather(pgpa_output_context *context,
                                 56                 :                :                                   Bitmapset *relids);
                                 57                 :                : static void pgpa_output_relations(pgpa_output_context *context, StringInfo buf,
                                 58                 :                :                                   Bitmapset *relids);
                                 59                 :                : 
                                 60                 :                : static char *pgpa_cstring_join_strategy(pgpa_join_strategy strategy);
                                 61                 :                : static char *pgpa_cstring_scan_strategy(pgpa_scan_strategy strategy);
                                 62                 :                : static char *pgpa_cstring_query_feature_type(pgpa_qf_type type);
                                 63                 :                : 
                                 64                 :                : static void pgpa_maybe_linebreak(StringInfo buf, int wrap_column);
                                 65                 :                : 
                                 66                 :                : /*
                                 67                 :                :  * Append query advice to the provided buffer.
                                 68                 :                :  *
                                 69                 :                :  * Before calling this function, 'walker' must be used to iterate over the
                                 70                 :                :  * main plan tree and all subplans from the PlannedStmt.
                                 71                 :                :  *
                                 72                 :                :  * 'rt_identifiers' is a table of unique identifiers, one for each RTI.
                                 73                 :                :  * See pgpa_create_identifiers_for_planned_stmt().
                                 74                 :                :  *
                                 75                 :                :  * Results will be appended to 'buf'.
                                 76                 :                :  */
                                 77                 :                : void
    3 rhaas@postgresql.org       78                 :GNC         120 : pgpa_output_advice(StringInfo buf, pgpa_plan_walker_context *walker,
                                 79                 :                :                    pgpa_identifier *rt_identifiers)
                                 80                 :                : {
                                 81                 :            120 :     Index       rtable_length = list_length(walker->pstmt->rtable);
                                 82                 :                :     ListCell   *lc;
                                 83                 :                :     pgpa_output_context context;
                                 84                 :                : 
                                 85                 :                :     /* Basic initialization. */
                                 86                 :            120 :     memset(&context, 0, sizeof(pgpa_output_context));
                                 87                 :            120 :     context.buf = buf;
                                 88                 :                : 
                                 89                 :                :     /*
                                 90                 :                :      * Convert identifiers to string form. Note that the loop variable here is
                                 91                 :                :      * not an RTI, because RTIs are 1-based. Some RTIs will have no
                                 92                 :                :      * identifier, either because the reloptkind is RTE_JOIN or because that
                                 93                 :                :      * portion of the query didn't make it into the final plan.
                                 94                 :                :      */
                                 95                 :            120 :     context.rid_strings = palloc0_array(const char *, rtable_length);
                                 96         [ +  + ]:            489 :     for (int i = 0; i < rtable_length; ++i)
                                 97         [ +  + ]:            369 :         if (rt_identifiers[i].alias_name != NULL)
                                 98                 :            299 :             context.rid_strings[i] = pgpa_identifier_string(&rt_identifiers[i]);
                                 99                 :                : 
                                100                 :                :     /*
                                101                 :                :      * If the user chooses to use EXPLAIN (PLAN_ADVICE) in an 80-column window
                                102                 :                :      * from a psql client with default settings, psql will add one space to
                                103                 :                :      * the left of the output and EXPLAIN will add two more to the left of the
                                104                 :                :      * advice. Thus, lines of more than 77 characters will wrap. We set the
                                105                 :                :      * wrap limit to 76 here so that the output won't reach all the way to the
                                106                 :                :      * very last column of the terminal.
                                107                 :                :      *
                                108                 :                :      * Of course, this is fairly arbitrary set of assumptions, and one could
                                109                 :                :      * well make an argument for a different wrap limit, or for a configurable
                                110                 :                :      * one.
                                111                 :                :      */
                                112                 :            120 :     context.wrap_column = 76;
                                113                 :                : 
                                114                 :                :     /*
                                115                 :                :      * Each piece of JOIN_ORDER() advice fully describes the join order for a
                                116                 :                :      * a single unrolled join. Merging is not permitted, because that would
                                117                 :                :      * change the meaning, e.g. SEQ_SCAN(a b c d) means simply that sequential
                                118                 :                :      * scans should be used for all of those relations, and is thus equivalent
                                119                 :                :      * to SEQ_SCAN(a b) SEQ_SCAN(c d), but JOIN_ORDER(a b c d) means that "a"
                                120                 :                :      * is the driving table which is then joined to "b" then "c" then "d",
                                121                 :                :      * which is totally different from JOIN_ORDER(a b) and JOIN_ORDER(c d).
                                122                 :                :      */
                                123   [ +  +  +  +  :            203 :     foreach(lc, walker->toplevel_unrolled_joins)
                                              +  + ]
                                124                 :                :     {
                                125                 :             83 :         pgpa_unrolled_join *ujoin = lfirst(lc);
                                126                 :                : 
                                127         [ +  + ]:             83 :         if (buf->len > 0)
                                128                 :             11 :             appendStringInfoChar(buf, '\n');
                                129                 :             83 :         appendStringInfo(context.buf, "JOIN_ORDER(");
                                130                 :             83 :         pgpa_output_unrolled_join(&context, ujoin);
                                131                 :             83 :         appendStringInfoChar(context.buf, ')');
                                132                 :             83 :         pgpa_maybe_linebreak(context.buf, context.wrap_column);
                                133                 :                :     }
                                134                 :                : 
                                135                 :                :     /* Emit join strategy advice. */
                                136         [ +  + ]:            840 :     for (int s = 0; s < NUM_PGPA_JOIN_STRATEGY; ++s)
                                137                 :                :     {
                                138                 :            720 :         char       *strategy = pgpa_cstring_join_strategy(s);
                                139                 :                : 
                                140                 :            720 :         pgpa_output_simple_strategy(&context,
                                141                 :                :                                     strategy,
                                142                 :                :                                     walker->join_strategies[s]);
                                143                 :                :     }
                                144                 :                : 
                                145                 :                :     /*
                                146                 :                :      * Emit scan strategy advice (but not for ordinary scans, which are
                                147                 :                :      * definitionally uninteresting).
                                148                 :                :      */
                                149         [ +  + ]:           1080 :     for (int c = 0; c < NUM_PGPA_SCAN_STRATEGY; ++c)
                                150         [ +  + ]:            960 :         if (c != PGPA_SCAN_ORDINARY)
                                151                 :            840 :             pgpa_output_scan_strategy(&context, c, walker->scans[c]);
                                152                 :                : 
                                153                 :                :     /* Emit query feature advice. */
                                154         [ +  + ]:            600 :     for (int t = 0; t < NUM_PGPA_QF_TYPES; ++t)
                                155                 :            480 :         pgpa_output_query_feature(&context, t, walker->query_features[t]);
                                156                 :                : 
                                157                 :                :     /* Emit NO_GATHER advice. */
                                158                 :            120 :     pgpa_output_no_gather(&context, walker->no_gather_scans);
                                159                 :            120 : }
                                160                 :                : 
                                161                 :                : /*
                                162                 :                :  * Output the members of an unrolled join, first the outermost member, and
                                163                 :                :  * then the inner members one by one, as part of JOIN_ORDER() advice.
                                164                 :                :  */
                                165                 :                : static void
                                166                 :             87 : pgpa_output_unrolled_join(pgpa_output_context *context,
                                167                 :                :                           pgpa_unrolled_join *join)
                                168                 :                : {
                                169                 :             87 :     pgpa_output_join_member(context, &join->outer);
                                170                 :                : 
                                171         [ +  + ]:            203 :     for (int k = 0; k < join->ninner; ++k)
                                172                 :                :     {
                                173                 :            116 :         pgpa_join_member *member = &join->inner[k];
                                174                 :                : 
                                175                 :            116 :         pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                176                 :            116 :         appendStringInfoChar(context->buf, ' ');
                                177                 :            116 :         pgpa_output_join_member(context, member);
                                178                 :                :     }
                                179                 :             87 : }
                                180                 :                : 
                                181                 :                : /*
                                182                 :                :  * Output a single member of an unrolled join as part of JOIN_ORDER() advice.
                                183                 :                :  */
                                184                 :                : static void
                                185                 :            203 : pgpa_output_join_member(pgpa_output_context *context,
                                186                 :                :                         pgpa_join_member *member)
                                187                 :                : {
                                188         [ +  + ]:            203 :     if (member->unrolled_join != NULL)
                                189                 :                :     {
                                190                 :              4 :         appendStringInfoChar(context->buf, '(');
                                191                 :              4 :         pgpa_output_unrolled_join(context, member->unrolled_join);
                                192                 :              4 :         appendStringInfoChar(context->buf, ')');
                                193                 :                :     }
                                194                 :                :     else
                                195                 :                :     {
                                196                 :            199 :         pgpa_scan  *scan = member->scan;
                                197                 :                : 
                                198         [ -  + ]:            199 :         Assert(scan != NULL);
                                199         [ +  + ]:            199 :         if (bms_membership(scan->relids) == BMS_SINGLETON)
                                200                 :            198 :             pgpa_output_relations(context, context->buf, scan->relids);
                                201                 :                :         else
                                202                 :                :         {
                                203                 :              1 :             appendStringInfoChar(context->buf, '{');
                                204                 :              1 :             pgpa_output_relations(context, context->buf, scan->relids);
                                205                 :              1 :             appendStringInfoChar(context->buf, '}');
                                206                 :                :         }
                                207                 :                :     }
                                208                 :            203 : }
                                209                 :                : 
                                210                 :                : /*
                                211                 :                :  * Output advice for a List of pgpa_scan objects.
                                212                 :                :  *
                                213                 :                :  * All the scans must use the strategy specified by the "strategy" argument.
                                214                 :                :  */
                                215                 :                : static void
                                216                 :            840 : pgpa_output_scan_strategy(pgpa_output_context *context,
                                217                 :                :                           pgpa_scan_strategy strategy,
                                218                 :                :                           List *scans)
                                219                 :                : {
                                220                 :            840 :     bool        first = true;
                                221                 :                : 
                                222         [ +  + ]:            840 :     if (scans == NIL)
                                223                 :            694 :         return;
                                224                 :                : 
                                225         [ +  + ]:            146 :     if (context->buf->len > 0)
                                226                 :             98 :         appendStringInfoChar(context->buf, '\n');
                                227                 :            146 :     appendStringInfo(context->buf, "%s(",
                                228                 :                :                      pgpa_cstring_scan_strategy(strategy));
                                229                 :                : 
                                230   [ +  -  +  +  :            541 :     foreach_ptr(pgpa_scan, scan, scans)
                                              +  + ]
                                231                 :                :     {
                                232                 :            249 :         Plan       *plan = scan->plan;
                                233                 :                : 
                                234         [ +  + ]:            249 :         if (first)
                                235                 :            146 :             first = false;
                                236                 :                :         else
                                237                 :                :         {
                                238                 :            103 :             pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                239                 :            103 :             appendStringInfoChar(context->buf, ' ');
                                240                 :                :         }
                                241                 :                : 
                                242                 :                :         /* Output the relation identifiers. */
                                243         [ +  + ]:            249 :         if (bms_membership(scan->relids) == BMS_SINGLETON)
                                244                 :            244 :             pgpa_output_relations(context, context->buf, scan->relids);
                                245                 :                :         else
                                246                 :                :         {
                                247                 :              5 :             appendStringInfoChar(context->buf, '(');
                                248                 :              5 :             pgpa_output_relations(context, context->buf, scan->relids);
                                249                 :              5 :             appendStringInfoChar(context->buf, ')');
                                250                 :                :         }
                                251                 :                : 
                                252                 :                :         /* For index or index-only scans, output index information. */
                                253         [ +  + ]:            249 :         if (strategy == PGPA_SCAN_INDEX)
                                254                 :                :         {
                                255         [ -  + ]:             65 :             Assert(IsA(plan, IndexScan));
                                256                 :             65 :             pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                257                 :             65 :             appendStringInfoChar(context->buf, ' ');
                                258                 :             65 :             pgpa_output_relation_name(context, ((IndexScan *) plan)->indexid);
                                259                 :                :         }
                                260         [ +  + ]:            184 :         else if (strategy == PGPA_SCAN_INDEX_ONLY)
                                261                 :                :         {
                                262         [ -  + ]:              7 :             Assert(IsA(plan, IndexOnlyScan));
                                263                 :              7 :             pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                264                 :              7 :             appendStringInfoChar(context->buf, ' ');
                                265                 :              7 :             pgpa_output_relation_name(context,
                                266                 :                :                                       ((IndexOnlyScan *) plan)->indexid);
                                267                 :                :         }
                                268                 :                :     }
                                269                 :                : 
                                270                 :            146 :     appendStringInfoChar(context->buf, ')');
                                271                 :            146 :     pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                272                 :                : }
                                273                 :                : 
                                274                 :                : /*
                                275                 :                :  * Output a schema-qualified relation name.
                                276                 :                :  */
                                277                 :                : static void
                                278                 :             72 : pgpa_output_relation_name(pgpa_output_context *context, Oid relid)
                                279                 :                : {
                                280                 :             72 :     Oid         nspoid = get_rel_namespace(relid);
                                281                 :             72 :     char       *relnamespace = get_namespace_name_or_temp(nspoid);
                                282                 :             72 :     char       *relname = get_rel_name(relid);
                                283                 :                : 
                                284                 :             72 :     appendStringInfoString(context->buf, quote_identifier(relnamespace));
                                285                 :             72 :     appendStringInfoChar(context->buf, '.');
                                286                 :             72 :     appendStringInfoString(context->buf, quote_identifier(relname));
                                287                 :             72 : }
                                288                 :                : 
                                289                 :                : /*
                                290                 :                :  * Output advice for a List of pgpa_query_feature objects.
                                291                 :                :  *
                                292                 :                :  * All features must be of the type specified by the "type" argument.
                                293                 :                :  */
                                294                 :                : static void
                                295                 :            480 : pgpa_output_query_feature(pgpa_output_context *context, pgpa_qf_type type,
                                296                 :                :                           List *query_features)
                                297                 :                : {
                                298                 :            480 :     bool        first = true;
                                299                 :                : 
                                300         [ +  + ]:            480 :     if (query_features == NIL)
                                301                 :            453 :         return;
                                302                 :                : 
                                303         [ +  - ]:             27 :     if (context->buf->len > 0)
                                304                 :             27 :         appendStringInfoChar(context->buf, '\n');
                                305                 :             27 :     appendStringInfo(context->buf, "%s(",
                                306                 :                :                      pgpa_cstring_query_feature_type(type));
                                307                 :                : 
                                308   [ +  -  +  +  :             83 :     foreach_ptr(pgpa_query_feature, qf, query_features)
                                              +  + ]
                                309                 :                :     {
                                310         [ +  + ]:             29 :         if (first)
                                311                 :             27 :             first = false;
                                312                 :                :         else
                                313                 :                :         {
                                314                 :              2 :             pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                315                 :              2 :             appendStringInfoChar(context->buf, ' ');
                                316                 :                :         }
                                317                 :                : 
                                318         [ +  + ]:             29 :         if (bms_membership(qf->relids) == BMS_SINGLETON)
                                319                 :             23 :             pgpa_output_relations(context, context->buf, qf->relids);
                                320                 :                :         else
                                321                 :                :         {
                                322                 :              6 :             appendStringInfoChar(context->buf, '(');
                                323                 :              6 :             pgpa_output_relations(context, context->buf, qf->relids);
                                324                 :              6 :             appendStringInfoChar(context->buf, ')');
                                325                 :                :         }
                                326                 :                :     }
                                327                 :                : 
                                328                 :             27 :     appendStringInfoChar(context->buf, ')');
                                329                 :             27 :     pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                330                 :                : }
                                331                 :                : 
                                332                 :                : /*
                                333                 :                :  * Output "simple" advice for a List of Bitmapset objects each of which
                                334                 :                :  * contains one or more RTIs.
                                335                 :                :  *
                                336                 :                :  * By simple, we just mean that the advice emitted follows the most
                                337                 :                :  * straightforward pattern: the strategy name, followed by a list of items
                                338                 :                :  * separated by spaces and surrounded by parentheses. Individual items in
                                339                 :                :  * the list are a single relation identifier for a Bitmapset that contains
                                340                 :                :  * just one member, or a sub-list again separated by spaces and surrounded
                                341                 :                :  * by parentheses for a Bitmapset with multiple members. Bitmapsets with
                                342                 :                :  * no members probably shouldn't occur here, but if they do they'll be
                                343                 :                :  * rendered as an empty sub-list.
                                344                 :                :  */
                                345                 :                : static void
                                346                 :            720 : pgpa_output_simple_strategy(pgpa_output_context *context, char *strategy,
                                347                 :                :                             List *relid_sets)
                                348                 :                : {
                                349                 :            720 :     bool        first = true;
                                350                 :                : 
                                351         [ +  + ]:            720 :     if (relid_sets == NIL)
                                352                 :            631 :         return;
                                353                 :                : 
                                354         [ +  - ]:             89 :     if (context->buf->len > 0)
                                355                 :             89 :         appendStringInfoChar(context->buf, '\n');
                                356                 :             89 :     appendStringInfo(context->buf, "%s(", strategy);
                                357                 :                : 
                                358   [ +  -  +  +  :            294 :     foreach_node(Bitmapset, relids, relid_sets)
                                              +  + ]
                                359                 :                :     {
                                360         [ +  + ]:            116 :         if (first)
                                361                 :             89 :             first = false;
                                362                 :                :         else
                                363                 :                :         {
                                364                 :             27 :             pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                365                 :             27 :             appendStringInfoChar(context->buf, ' ');
                                366                 :                :         }
                                367                 :                : 
                                368         [ +  + ]:            116 :         if (bms_membership(relids) == BMS_SINGLETON)
                                369                 :            112 :             pgpa_output_relations(context, context->buf, relids);
                                370                 :                :         else
                                371                 :                :         {
                                372                 :              4 :             appendStringInfoChar(context->buf, '(');
                                373                 :              4 :             pgpa_output_relations(context, context->buf, relids);
                                374                 :              4 :             appendStringInfoChar(context->buf, ')');
                                375                 :                :         }
                                376                 :                :     }
                                377                 :                : 
                                378                 :             89 :     appendStringInfoChar(context->buf, ')');
                                379                 :             89 :     pgpa_maybe_linebreak(context->buf, context->wrap_column);
                                380                 :                : }
                                381                 :                : 
                                382                 :                : /*
                                383                 :                :  * Output NO_GATHER advice for all relations not appearing beneath any
                                384                 :                :  * Gather or Gather Merge node.
                                385                 :                :  */
                                386                 :                : static void
                                387                 :            120 : pgpa_output_no_gather(pgpa_output_context *context, Bitmapset *relids)
                                388                 :                : {
                                389         [ +  + ]:            120 :     if (relids == NULL)
                                390                 :              8 :         return;
                                391         [ +  - ]:            112 :     if (context->buf->len > 0)
                                392                 :            112 :         appendStringInfoChar(context->buf, '\n');
                                393                 :            112 :     appendStringInfoString(context->buf, "NO_GATHER(");
                                394                 :            112 :     pgpa_output_relations(context, context->buf, relids);
                                395                 :            112 :     appendStringInfoChar(context->buf, ')');
                                396                 :                : }
                                397                 :                : 
                                398                 :                : /*
                                399                 :                :  * Output the identifiers for each RTI in the provided set.
                                400                 :                :  *
                                401                 :                :  * Identifiers are separated by spaces, and a line break is possible after
                                402                 :                :  * each one.
                                403                 :                :  */
                                404                 :                : static void
                                405                 :            705 : pgpa_output_relations(pgpa_output_context *context, StringInfo buf,
                                406                 :                :                       Bitmapset *relids)
                                407                 :                : {
                                408                 :            705 :     int         rti = -1;
                                409                 :            705 :     bool        first = true;
                                410                 :                : 
                                411         [ +  + ]:           1562 :     while ((rti = bms_next_member(relids, rti)) >= 0)
                                412                 :                :     {
                                413                 :            857 :         const char *rid_string = context->rid_strings[rti - 1];
                                414                 :                : 
                                415         [ -  + ]:            857 :         if (rid_string == NULL)
    3 rhaas@postgresql.org      416         [ #  # ]:UNC           0 :             elog(ERROR, "no identifier for RTI %d", rti);
                                417                 :                : 
    3 rhaas@postgresql.org      418         [ +  + ]:GNC         857 :         if (first)
                                419                 :                :         {
                                420                 :            705 :             first = false;
                                421                 :            705 :             appendStringInfoString(buf, rid_string);
                                422                 :                :         }
                                423                 :                :         else
                                424                 :                :         {
                                425                 :            152 :             pgpa_maybe_linebreak(buf, context->wrap_column);
                                426                 :            152 :             appendStringInfo(buf, " %s", rid_string);
                                427                 :                :         }
                                428                 :                :     }
                                429                 :            705 : }
                                430                 :                : 
                                431                 :                : /*
                                432                 :                :  * Get a C string that corresponds to the specified join strategy.
                                433                 :                :  */
                                434                 :                : static char *
                                435                 :            720 : pgpa_cstring_join_strategy(pgpa_join_strategy strategy)
                                436                 :                : {
                                437   [ +  +  +  +  :            720 :     switch (strategy)
                                           +  +  - ]
                                438                 :                :     {
                                439                 :            120 :         case JSTRAT_MERGE_JOIN_PLAIN:
                                440                 :            120 :             return "MERGE_JOIN_PLAIN";
                                441                 :            120 :         case JSTRAT_MERGE_JOIN_MATERIALIZE:
                                442                 :            120 :             return "MERGE_JOIN_MATERIALIZE";
                                443                 :            120 :         case JSTRAT_NESTED_LOOP_PLAIN:
                                444                 :            120 :             return "NESTED_LOOP_PLAIN";
                                445                 :            120 :         case JSTRAT_NESTED_LOOP_MATERIALIZE:
                                446                 :            120 :             return "NESTED_LOOP_MATERIALIZE";
                                447                 :            120 :         case JSTRAT_NESTED_LOOP_MEMOIZE:
                                448                 :            120 :             return "NESTED_LOOP_MEMOIZE";
                                449                 :            120 :         case JSTRAT_HASH_JOIN:
                                450                 :            120 :             return "HASH_JOIN";
                                451                 :                :     }
                                452                 :                : 
    3 rhaas@postgresql.org      453                 :UNC           0 :     pg_unreachable();
                                454                 :                :     return NULL;
                                455                 :                : }
                                456                 :                : 
                                457                 :                : /*
                                458                 :                :  * Get a C string that corresponds to the specified scan strategy.
                                459                 :                :  */
                                460                 :                : static char *
    3 rhaas@postgresql.org      461                 :GNC         146 : pgpa_cstring_scan_strategy(pgpa_scan_strategy strategy)
                                462                 :                : {
                                463   [ -  +  +  -  :            146 :     switch (strategy)
                                        +  +  +  +  
                                                 - ]
                                464                 :                :     {
    3 rhaas@postgresql.org      465                 :UNC           0 :         case PGPA_SCAN_ORDINARY:
                                466                 :              0 :             return "ORDINARY_SCAN";
    3 rhaas@postgresql.org      467                 :GNC          78 :         case PGPA_SCAN_SEQ:
                                468                 :             78 :             return "SEQ_SCAN";
                                469                 :              3 :         case PGPA_SCAN_BITMAP_HEAP:
                                470                 :              3 :             return "BITMAP_HEAP_SCAN";
    3 rhaas@postgresql.org      471                 :UNC           0 :         case PGPA_SCAN_FOREIGN:
                                472                 :              0 :             return "FOREIGN_JOIN";
    3 rhaas@postgresql.org      473                 :GNC          47 :         case PGPA_SCAN_INDEX:
                                474                 :             47 :             return "INDEX_SCAN";
                                475                 :              7 :         case PGPA_SCAN_INDEX_ONLY:
                                476                 :              7 :             return "INDEX_ONLY_SCAN";
                                477                 :              7 :         case PGPA_SCAN_PARTITIONWISE:
                                478                 :              7 :             return "PARTITIONWISE";
                                479                 :              4 :         case PGPA_SCAN_TID:
                                480                 :              4 :             return "TID_SCAN";
                                481                 :                :     }
                                482                 :                : 
    3 rhaas@postgresql.org      483                 :UNC           0 :     pg_unreachable();
                                484                 :                :     return NULL;
                                485                 :                : }
                                486                 :                : 
                                487                 :                : /*
                                488                 :                :  * Get a C string that corresponds to the query feature type.
                                489                 :                :  */
                                490                 :                : static char *
    3 rhaas@postgresql.org      491                 :GNC          27 : pgpa_cstring_query_feature_type(pgpa_qf_type type)
                                492                 :                : {
                                493   [ +  +  +  +  :             27 :     switch (type)
                                                 - ]
                                494                 :                :     {
                                495                 :              6 :         case PGPAQF_GATHER:
                                496                 :              6 :             return "GATHER";
                                497                 :              7 :         case PGPAQF_GATHER_MERGE:
                                498                 :              7 :             return "GATHER_MERGE";
                                499                 :              5 :         case PGPAQF_SEMIJOIN_NON_UNIQUE:
                                500                 :              5 :             return "SEMIJOIN_NON_UNIQUE";
                                501                 :              9 :         case PGPAQF_SEMIJOIN_UNIQUE:
                                502                 :              9 :             return "SEMIJOIN_UNIQUE";
                                503                 :                :     }
                                504                 :                : 
                                505                 :                : 
    3 rhaas@postgresql.org      506                 :UNC           0 :     pg_unreachable();
                                507                 :                :     return NULL;
                                508                 :                : }
                                509                 :                : 
                                510                 :                : /*
                                511                 :                :  * Insert a line break into the StringInfoData, if needed.
                                512                 :                :  *
                                513                 :                :  * If wrap_column is zero or negative, this does nothing. Otherwise, we
                                514                 :                :  * consider inserting a newline. We only insert a newline if the length of
                                515                 :                :  * the last line in the buffer exceeds wrap_column, and not if we'd be
                                516                 :                :  * inserting a newline at or before the beginning of the current line.
                                517                 :                :  *
                                518                 :                :  * The position at which the newline is inserted is simply wherever the
                                519                 :                :  * buffer ended the last time this function was called. In other words,
                                520                 :                :  * the caller is expected to call this function every time we reach a good
                                521                 :                :  * place for a line break.
                                522                 :                :  */
                                523                 :                : static void
    3 rhaas@postgresql.org      524                 :GNC         817 : pgpa_maybe_linebreak(StringInfo buf, int wrap_column)
                                525                 :                : {
                                526                 :                :     char       *trailing_nl;
                                527                 :                :     int         line_start;
                                528                 :                :     int         save_cursor;
                                529                 :                : 
                                530                 :                :     /* If line wrapping is disabled, exit quickly. */
                                531         [ -  + ]:            817 :     if (wrap_column <= 0)
    3 rhaas@postgresql.org      532                 :UNC           0 :         return;
                                533                 :                : 
                                534                 :                :     /*
                                535                 :                :      * Set line_start to the byte offset within buf->data of the first
                                536                 :                :      * character of the current line, where the current line means the last
                                537                 :                :      * one in the buffer. Note that line_start could be the offset of the
                                538                 :                :      * trailing '\0' if the last character in the buffer is a line break.
                                539                 :                :      */
    3 rhaas@postgresql.org      540                 :GNC         817 :     trailing_nl = strrchr(buf->data, '\n');
                                541         [ +  + ]:            817 :     if (trailing_nl == NULL)
                                542                 :            243 :         line_start = 0;
                                543                 :                :     else
                                544                 :            574 :         line_start = (trailing_nl - buf->data) + 1;
                                545                 :                : 
                                546                 :                :     /*
                                547                 :                :      * Remember that the current end of the buffer is a potential location to
                                548                 :                :      * insert a line break on a future call to this function.
                                549                 :                :      */
                                550                 :            817 :     save_cursor = buf->cursor;
                                551                 :            817 :     buf->cursor = buf->len;
                                552                 :                : 
                                553                 :                :     /* If we haven't passed the wrap column, we don't need a newline. */
                                554         [ +  + ]:            817 :     if (buf->len - line_start <= wrap_column)
                                555                 :            797 :         return;
                                556                 :                : 
                                557                 :                :     /*
                                558                 :                :      * It only makes sense to insert a newline at a position later than the
                                559                 :                :      * beginning of the current line.
                                560                 :                :      */
                                561         [ -  + ]:             20 :     if (save_cursor <= line_start)
    3 rhaas@postgresql.org      562                 :UNC           0 :         return;
                                563                 :                : 
                                564                 :                :     /* Insert a newline at the previous cursor location. */
    3 rhaas@postgresql.org      565                 :GNC          20 :     enlargeStringInfo(buf, 1);
                                566                 :             20 :     memmove(&buf->data[save_cursor] + 1, &buf->data[save_cursor],
                                567                 :             20 :             buf->len - save_cursor);
                                568                 :             20 :     ++buf->cursor;
                                569                 :             20 :     buf->data[++buf->len] = '\0';
                                570                 :             20 :     buf->data[save_cursor] = '\n';
                                571                 :                : }
        

Generated by: LCOV version 2.4-beta