LCOV - differential code coverage report
Current view: top level - src/test/modules/test_extensible - test_extensible.c (source / functions) Coverage Total Hit UNC GNC
Current: f76c13edadc2b319036e0d238703ed56bb23f934 vs 9e17d25e79d4756be08b4a5521b4b58450217137 Lines: 93.4 % 182 170 12 170
Current Date: 2026-09-20 14:13:17 +0900 Functions: 93.8 % 32 30 2 30
Baseline: lcov-20260920-baseline Branches: 75.8 % 66 50 16 50
Baseline Date: 2026-09-20 14:13:13 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 93.4 % 182 170 12 170
Function coverage date bins:
(7,30] days: 93.8 % 32 30 2 30
Branch coverage date bins:
(7,30] days: 75.8 % 66 50 16 50

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * test_extensible.c
                                  4                 :                :  *      Tests for extensible nodes and custom scans
                                  5                 :                :  *
                                  6                 :                :  * Copyright (c) 2026, PostgreSQL Global Development Group
                                  7                 :                :  *
                                  8                 :                :  * IDENTIFICATION
                                  9                 :                :  *      src/test/modules/test_extensible/test_extensible.c
                                 10                 :                :  *
                                 11                 :                :  *-------------------------------------------------------------------------
                                 12                 :                :  */
                                 13                 :                : 
                                 14                 :                : #include "postgres.h"
                                 15                 :                : 
                                 16                 :                : #include "access/table.h"
                                 17                 :                : #include "access/tableam.h"
                                 18                 :                : #include "catalog/namespace.h"
                                 19                 :                : #include "executor/executor.h"
                                 20                 :                : #include "fmgr.h"
                                 21                 :                : #include "miscadmin.h"
                                 22                 :                : #include "nodes/extensible.h"
                                 23                 :                : #include "nodes/nodes.h"
                                 24                 :                : #include "nodes/plannodes.h"
                                 25                 :                : #include "nodes/readfuncs.h"
                                 26                 :                : #include "optimizer/pathnode.h"
                                 27                 :                : #include "optimizer/paths.h"
                                 28                 :                : #include "optimizer/restrictinfo.h"
                                 29                 :                : #include "utils/builtins.h"
                                 30                 :                : #include "utils/guc.h"
                                 31                 :                : #include "utils/lsyscache.h"
                                 32                 :                : 
   20 michael@paquier.xyz        33                 :GNC           2 : PG_MODULE_MAGIC;
                                 34                 :                : 
                                 35                 :                : /* Name of the test table that triggers our CustomScan injection */
                                 36                 :                : #define TEST_TABLE_NAME     "test_extensible_tbl"
                                 37                 :                : 
                                 38                 :                : /*
                                 39                 :                :  * TestExtNode - an ExtensibleNode subtype carrying our planning data.
                                 40                 :                :  */
                                 41                 :                : typedef struct TestExtNode
                                 42                 :                : {
                                 43                 :                :     ExtensibleNode base;        /* must be first */
                                 44                 :                :     Oid         relid;          /* OID of the relation being scanned */
                                 45                 :                :     int         repeat_count;   /* how many times to return each scanned row */
                                 46                 :                : } TestExtNode;
                                 47                 :                : 
                                 48                 :                : #define TEST_EXT_NODE_NAME  "TestExtNode"
                                 49                 :                : #define TEST_CUSTOM_SCAN_NAME   "TestCustomScan"
                                 50                 :                : 
                                 51                 :                : /* GUC: how many times the custom scan returns each row */
                                 52                 :                : static int  test_repeat_count = 2;
                                 53                 :                : 
                                 54                 :                : static TestExtNode *text_to_test_ext_node(text *txt);
                                 55                 :                : 
                                 56                 :                : /* Encode a TestExtNode into its serialized representation */
                                 57                 :                : #define TEST_EXT_NODE_TO_TEXT(node) cstring_to_text(nodeToString(node))
                                 58                 :                : 
                                 59                 :                : /* Decode a function argument back into a TestExtNode */
                                 60                 :                : #define PG_GETARG_TEST_EXT_NODE(n)  text_to_test_ext_node(PG_GETARG_TEXT_PP(n))
                                 61                 :                : 
                                 62                 :              2 : PG_FUNCTION_INFO_V1(test_get_extensible_node_methods);
                                 63                 :              2 : PG_FUNCTION_INFO_V1(test_get_custom_scan_methods);
                                 64                 :              2 : PG_FUNCTION_INFO_V1(test_ext_node_make);
                                 65                 :              2 : PG_FUNCTION_INFO_V1(test_ext_node_copy);
                                 66                 :              2 : PG_FUNCTION_INFO_V1(test_ext_node_equal);
                                 67                 :              2 : PG_FUNCTION_INFO_V1(test_ext_node_get_relid);
                                 68                 :              2 : PG_FUNCTION_INFO_V1(test_ext_node_get_repeat_count);
                                 69                 :                : 
                                 70                 :                : /*
                                 71                 :                :  * ExtensibleNodeMethods callbacks.
                                 72                 :                :  *
                                 73                 :                :  * Note that nodeOut() and nodeRead() must agree on the set and order of
                                 74                 :                :  * serialized fields.
                                 75                 :                :  */
                                 76                 :                : static void
                                 77                 :              3 : test_ext_node_copy_cb(ExtensibleNode *newnode, const ExtensibleNode *oldnode)
                                 78                 :                : {
                                 79                 :              3 :     ((TestExtNode *) newnode)->relid = ((const TestExtNode *) oldnode)->relid;
                                 80                 :              3 :     ((TestExtNode *) newnode)->repeat_count =
                                 81                 :              3 :         ((const TestExtNode *) oldnode)->repeat_count;
                                 82                 :              3 : }
                                 83                 :                : 
                                 84                 :                : static bool
                                 85                 :              3 : test_ext_node_equal_cb(const ExtensibleNode *a, const ExtensibleNode *b)
                                 86                 :                : {
                                 87                 :              3 :     return ((const TestExtNode *) a)->relid ==
                                 88         [ +  + ]:              5 :         ((const TestExtNode *) b)->relid &&
                                 89                 :              2 :         ((const TestExtNode *) a)->repeat_count ==
                                 90         [ +  + ]:              2 :         ((const TestExtNode *) b)->repeat_count;
                                 91                 :                : }
                                 92                 :                : 
                                 93                 :                : static void
                                 94                 :             10 : test_ext_node_out_cb(StringInfo str, const ExtensibleNode *node)
                                 95                 :                : {
                                 96                 :             10 :     appendStringInfo(str, " :relid %u", ((const TestExtNode *) node)->relid);
                                 97                 :             10 :     appendStringInfo(str, " :repeat_count %d",
                                 98                 :             10 :                      ((const TestExtNode *) node)->repeat_count);
                                 99                 :             10 : }
                                100                 :                : 
                                101                 :                : /*
                                102                 :                :  * Fetch the next token of the string being read.
                                103                 :                :  *
                                104                 :                :  * Unlike anything in readfuncs.c, this is reachable with arbitrary strings
                                105                 :                :  * through SQL function calls, so the tokens need to be checked.
                                106                 :                :  *
                                107                 :                :  * If "expected" is specified, check that the token fetched matches with
                                108                 :                :  * the input expected.  Running out of tokens is a failure.
                                109                 :                :  */
                                110                 :                : static const char *
   10                           111                 :             97 : test_ext_node_next_token(ReadNodeContext *ctx, const char *expected)
                                112                 :                : {
                                113                 :                :     int         length;
   20                           114                 :             97 :     const char *token = pg_strtok(ctx, &length);
                                115                 :                : 
                                116         [ +  + ]:             97 :     if (token == NULL)
                                117         [ +  - ]:              1 :         ereport(ERROR,
                                118                 :                :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                                119                 :                :                  errmsg("unexpected end of \"%s\"", TEST_EXT_NODE_NAME)));
                                120                 :                : 
   10                           121         [ +  + ]:             96 :     if (expected != NULL)
                                122                 :                :     {
                                123         [ +  + ]:             51 :         if (length != (int) strlen(expected))
                                124                 :              3 :             return NULL;
                                125         [ -  + ]:             48 :         if (memcmp(token, expected, length) != 0)
   10 michael@paquier.xyz       126                 :UNC           0 :             return NULL;
                                127                 :                :     }
                                128                 :                : 
   20 michael@paquier.xyz       129                 :GNC          93 :     return token;
                                130                 :                : }
                                131                 :                : 
                                132                 :                : static void
                                133                 :             12 : test_ext_node_read_cb(ReadNodeContext *ctx, ExtensibleNode *node)
                                134                 :                : {
                                135                 :             12 :     TestExtNode *tnode = (TestExtNode *) node;
                                136                 :                : 
   10                           137                 :             12 :     (void) test_ext_node_next_token(ctx, NULL); /* skip :relid */
                                138                 :             12 :     tnode->relid = atooid(test_ext_node_next_token(ctx, NULL));
                                139                 :                : 
                                140                 :             11 :     (void) test_ext_node_next_token(ctx, NULL); /* skip :repeat_count */
                                141                 :             11 :     tnode->repeat_count = atoi(test_ext_node_next_token(ctx, NULL));
   20                           142                 :             11 : }
                                143                 :                : 
                                144                 :                : static const ExtensibleNodeMethods test_ext_node_methods =
                                145                 :                : {
                                146                 :                :     .extnodename = TEST_EXT_NODE_NAME,
                                147                 :                :     .node_size = sizeof(TestExtNode),
                                148                 :                :     .nodeCopy = test_ext_node_copy_cb,
                                149                 :                :     .nodeEqual = test_ext_node_equal_cb,
                                150                 :                :     .nodeOut = test_ext_node_out_cb,
                                151                 :                :     .nodeRead = test_ext_node_read_cb,
                                152                 :                : };
                                153                 :                : 
                                154                 :                : /*
                                155                 :                :  * TestCustomScanState - execution state for the custom scan
                                156                 :                :  */
                                157                 :                : typedef struct TestCustomScanState
                                158                 :                : {
                                159                 :                :     CustomScanState css;        /* must be first */
                                160                 :                :     TableScanDesc scandesc;
                                161                 :                :     int         repeat_count;   /* repeat_count from TestExtNode */
                                162                 :                :     int         repeats_left;   /* how many more times to return current row */
                                163                 :                : } TestCustomScanState;
                                164                 :                : 
                                165                 :                : /*
                                166                 :                :  * Executor callbacks
                                167                 :                :  */
                                168                 :                : 
                                169                 :                : /*
                                170                 :                :  * Retrieve our private planning data from a CustomScan node.
                                171                 :                :  */
                                172                 :                : static TestExtNode *
                                173                 :             22 : test_get_ext_node(CustomScan *cscan)
                                174                 :                : {
                                175                 :                :     TestExtNode *tnode;
                                176                 :                : 
                                177         [ -  + ]:             22 :     Assert(list_length(cscan->custom_private) == 1);
                                178                 :             22 :     tnode = (TestExtNode *) linitial(cscan->custom_private);
                                179                 :                : 
                                180         [ -  + ]:             22 :     Assert(IsA(tnode, ExtensibleNode));
                                181         [ -  + ]:             22 :     Assert(strcmp(tnode->base.extnodename, TEST_EXT_NODE_NAME) == 0);
                                182                 :                : 
                                183                 :             22 :     return tnode;
                                184                 :                : }
                                185                 :                : 
                                186                 :                : /*
                                187                 :                :  * BeginCustomScan.
                                188                 :                :  */
                                189                 :                : static void
                                190                 :             11 : test_begin_custom_scan(CustomScanState *node, EState *estate, int eflags)
                                191                 :                : {
                                192                 :             11 :     TestCustomScanState *tstate = (TestCustomScanState *) node;
                                193                 :             11 :     TestExtNode *tnode = test_get_ext_node((CustomScan *) node->ss.ps.plan);
                                194                 :             11 :     Relation    rel = node->ss.ss_currentRelation;
                                195                 :                : 
                                196         [ -  + ]:             11 :     Assert(tnode->repeat_count > 0);
                                197                 :             11 :     tstate->repeat_count = tnode->repeat_count;
                                198                 :             11 :     tstate->repeats_left = 0;
                                199                 :             11 :     tstate->scandesc = NULL;
                                200                 :                : 
                                201                 :                :     /* A plain EXPLAIN never executes the plan, so skip */
                                202         [ +  + ]:             11 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
                                203                 :              5 :         return;
                                204                 :                : 
                                205                 :              6 :     tstate->scandesc = table_beginscan(rel, estate->es_snapshot, 0, NULL,
                                206                 :                :                                        SO_NONE);
                                207                 :                : }
                                208                 :                : 
                                209                 :                : /*
                                210                 :                :  * Access method for ExecScan(): return the next tuple to be considered, or
                                211                 :                :  * NULL when the scan is done.
                                212                 :                :  */
                                213                 :                : static TupleTableSlot *
                                214                 :             38 : test_scan_next(ScanState *node)
                                215                 :                : {
                                216                 :             38 :     TestCustomScanState *tstate = (TestCustomScanState *) node;
                                217                 :             38 :     TupleTableSlot *slot = node->ss_ScanTupleSlot;
                                218                 :                : 
                                219                 :                :     /* Return the current tuple again if it still has repeats left */
                                220         [ +  + ]:             38 :     if (tstate->repeats_left > 0)
                                221                 :                :     {
                                222                 :             18 :         tstate->repeats_left--;
                                223                 :             18 :         return slot;
                                224                 :                :     }
                                225                 :                : 
                                226         [ +  + ]:             20 :     if (!table_scan_getnextslot(tstate->scandesc, ForwardScanDirection, slot))
                                227                 :              5 :         return NULL;
                                228                 :                : 
                                229                 :             15 :     tstate->repeats_left = tstate->repeat_count - 1;
                                230                 :             15 :     return slot;
                                231                 :                : }
                                232                 :                : 
                                233                 :                : /*
                                234                 :                :  * Recheck method for ExecScan(), used only during EvalPlanQual rechecks.
                                235                 :                :  * We evaluate no quals of our own, so there is nothing to recheck.
                                236                 :                :  */
                                237                 :                : static bool
   20 michael@paquier.xyz       238                 :UNC           0 : test_scan_recheck(ScanState *node, TupleTableSlot *slot)
                                239                 :                : {
                                240                 :              0 :     return true;
                                241                 :                : }
                                242                 :                : 
                                243                 :                : static TupleTableSlot *
   20 michael@paquier.xyz       244                 :GNC          36 : test_exec_custom_scan(CustomScanState *node)
                                245                 :                : {
                                246                 :             36 :     return ExecScan(&node->ss, test_scan_next, test_scan_recheck);
                                247                 :                : }
                                248                 :                : 
                                249                 :                : static void
                                250                 :             11 : test_end_custom_scan(CustomScanState *node)
                                251                 :                : {
                                252                 :             11 :     TestCustomScanState *tstate = (TestCustomScanState *) node;
                                253                 :                : 
                                254                 :                :     /* No scan started under EXEC_FLAG_EXPLAIN_ONLY */
                                255         [ +  + ]:             11 :     if (tstate->scandesc != NULL)
                                256                 :              6 :         table_endscan(tstate->scandesc);
                                257                 :             11 : }
                                258                 :                : 
                                259                 :                : /*
                                260                 :                :  * ReScanCustomScan: reset our own state as well as ExecScan()'s.
                                261                 :                :  */
                                262                 :                : static void
   20 michael@paquier.xyz       263                 :UNC           0 : test_rescan_custom_scan(CustomScanState *node)
                                264                 :                : {
                                265                 :              0 :     TestCustomScanState *tstate = (TestCustomScanState *) node;
                                266                 :                : 
                                267                 :              0 :     tstate->repeats_left = 0;
                                268                 :              0 :     table_rescan(tstate->scandesc, NULL);
                                269                 :              0 :     ExecScanReScan(&node->ss);
                                270                 :              0 : }
                                271                 :                : 
                                272                 :                : static const CustomExecMethods test_custom_exec_methods =
                                273                 :                : {
                                274                 :                :     .CustomName = TEST_CUSTOM_SCAN_NAME,
                                275                 :                :     .BeginCustomScan = test_begin_custom_scan,
                                276                 :                :     .ExecCustomScan = test_exec_custom_scan,
                                277                 :                :     .EndCustomScan = test_end_custom_scan,
                                278                 :                :     .ReScanCustomScan = test_rescan_custom_scan,
                                279                 :                : };
                                280                 :                : 
                                281                 :                : /*
                                282                 :                :  * CreateCustomScanState() allocates the CustomScanState and fills in its node
                                283                 :                :  * tag and its methods; everything else is left to ExecInitCustomScan().
                                284                 :                :  *
                                285                 :                :  * slotOps is set here because ExecInitCustomScan() reads it before creating
                                286                 :                :  * the scan tuple slot, and before it opens the scan relation itself.  So we
                                287                 :                :  * open the relation ourselves (its OID travels via our ExtensibleNode) just
                                288                 :                :  * to learn its slot type from table_slot_callbacks().
                                289                 :                :  *
                                290                 :                :  * A lock is taken rather than passing NoLock, since in a parallel worker
                                291                 :                :  * nothing has locked the relation yet; the worker takes its own lock later
                                292                 :                :  * in ExecGetRangeTableRelation().  AccessShareLock matches what a plain scan
                                293                 :                :  * uses.
                                294                 :                :  */
                                295                 :                : static Node *
   20 michael@paquier.xyz       296                 :GNC          11 : test_create_custom_scan_state(CustomScan *cscan)
                                297                 :                : {
                                298                 :                :     TestCustomScanState *tstate;
                                299                 :             11 :     TestExtNode *tnode = test_get_ext_node(cscan);
                                300                 :                :     Relation    rel;
                                301                 :                : 
                                302                 :                :     tstate = (TestCustomScanState *)
                                303                 :             11 :         newNode(sizeof(TestCustomScanState), T_CustomScanState);
                                304                 :             11 :     tstate->css.methods = &test_custom_exec_methods;
                                305                 :                : 
                                306                 :             11 :     rel = table_open(tnode->relid, AccessShareLock);
                                307                 :             11 :     tstate->css.slotOps = table_slot_callbacks(rel);
                                308                 :             11 :     table_close(rel, NoLock);
                                309                 :                : 
                                310                 :             11 :     return (Node *) tstate;
                                311                 :                : }
                                312                 :                : 
                                313                 :                : static const CustomScanMethods test_custom_scan_methods =
                                314                 :                : {
                                315                 :                :     .CustomName = TEST_CUSTOM_SCAN_NAME,
                                316                 :                :     .CreateCustomScanState = test_create_custom_scan_state,
                                317                 :                : };
                                318                 :                : 
                                319                 :                : /*
                                320                 :                :  * Planner callbacks
                                321                 :                :  */
                                322                 :                : 
                                323                 :                : /*
                                324                 :                :  * PlanCustomPath turns our CustomPath into the CustomScan plan node that the
                                325                 :                :  * executor runs.
                                326                 :                :  */
                                327                 :                : static Plan *
                                328                 :             10 : test_plan_custom_path(PlannerInfo *root,
                                329                 :                :                       RelOptInfo *rel,
                                330                 :                :                       struct CustomPath *best_path,
                                331                 :                :                       List *tlist,
                                332                 :                :                       List *clauses,
                                333                 :                :                       List *custom_plans)
                                334                 :                : {
                                335                 :             10 :     CustomScan *cscan = makeNode(CustomScan);
                                336                 :                : 
                                337                 :             10 :     cscan->scan.plan.targetlist = tlist;
                                338                 :                : 
                                339                 :                :     /*
                                340                 :                :      * Restriction clauses arrive as RestrictInfos; reduce them to bare
                                341                 :                :      * expressions for the plan's  qual. Pseudoconstants are dropped since the
                                342                 :                :      * core evaluates them in a gating Result node above us.
                                343                 :                :      */
                                344                 :             10 :     cscan->scan.plan.qual = extract_actual_clauses(clauses, false);
                                345                 :             10 :     cscan->scan.scanrelid = rel->relid;
                                346                 :             10 :     cscan->flags = best_path->flags;
                                347                 :                : 
                                348                 :                :     /* Our CustomPath has no child paths */
                                349                 :             10 :     cscan->custom_plans = custom_plans;
                                350                 :             10 :     cscan->custom_exprs = NIL;
                                351                 :                : 
                                352                 :                :     /* Pass the ExtensibleNode from the path to the plan */
                                353                 :             10 :     cscan->custom_private = best_path->custom_private;
                                354                 :             10 :     cscan->custom_scan_tlist = NIL;
                                355                 :             10 :     cscan->custom_relids = NULL;
                                356                 :             10 :     cscan->methods = &test_custom_scan_methods;
                                357                 :                : 
                                358                 :             10 :     return (Plan *) cscan;
                                359                 :                : }
                                360                 :                : 
                                361                 :                : static const CustomPathMethods test_custom_path_methods =
                                362                 :                : {
                                363                 :                :     .CustomName = TEST_CUSTOM_SCAN_NAME,
                                364                 :                :     .PlanCustomPath = test_plan_custom_path,
                                365                 :                : };
                                366                 :                : 
                                367                 :                : static set_rel_pathlist_hook_type prev_set_rel_pathlist_hook = NULL;
                                368                 :                : 
                                369                 :                : static void
                                370                 :             21 : test_set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                371                 :                :                       Index rti, RangeTblEntry *rte)
                                372                 :                : {
                                373                 :                :     CustomPath *cpath;
                                374                 :                :     TestExtNode *tnode;
                                375                 :                :     char       *relname;
                                376                 :                : 
                                377         [ -  + ]:             21 :     if (prev_set_rel_pathlist_hook)
   20 michael@paquier.xyz       378                 :UNC           0 :         prev_set_rel_pathlist_hook(root, rel, rti, rte);
                                379                 :                : 
                                380                 :                :     /*
                                381                 :                :      * Only consider plain base relations with a table AM, no inheritance and
                                382                 :                :      * no tablesample, keeping the logic simple.
                                383                 :                :      */
   20 michael@paquier.xyz       384   [ +  +  +  + ]:GNC          21 :     if (rel->reloptkind != RELOPT_BASEREL || rte->rtekind != RTE_RELATION)
                                385                 :              7 :         return;
                                386   [ -  +  -  - ]:             14 :     if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_MATVIEW)
   20 michael@paquier.xyz       387                 :UNC           0 :         return;
   20 michael@paquier.xyz       388   [ +  +  +  + ]:GNC          14 :     if (rte->tablesample != NULL || rte->inh)
                                389                 :              4 :         return;
                                390                 :                : 
                                391                 :                :     /*
                                392                 :                :      * Only inject our CustomPath for the specific marker table, free of
                                393                 :                :      * namespace.
                                394                 :                :      */
                                395                 :             10 :     relname = get_rel_name(rte->relid);
                                396   [ +  -  -  + ]:             10 :     if (relname == NULL || strcmp(relname, TEST_TABLE_NAME) != 0)
   20 michael@paquier.xyz       397                 :UNC           0 :         return;
                                398                 :                : 
                                399                 :                :     /* TestExtNode for the executor callbacks */
   20 michael@paquier.xyz       400                 :GNC          10 :     tnode = (TestExtNode *) newNode(sizeof(TestExtNode), T_ExtensibleNode);
                                401                 :             10 :     tnode->base.extnodename = TEST_EXT_NODE_NAME;
                                402                 :             10 :     tnode->relid = rte->relid;
                                403                 :                : 
                                404                 :                :     /*
                                405                 :                :      * Read once.  Changes to the GUC do not affect already-planned queries.
                                406                 :                :      * Each row is returned repeat_count times.
                                407                 :                :      */
                                408                 :             10 :     tnode->repeat_count = test_repeat_count;
                                409                 :                : 
                                410                 :                :     /*
                                411                 :                :      * Use a cost of zero to force our custom path; a real provider should
                                412                 :                :      * estimate the cost honestly, but it does not matter for this module.
                                413                 :                :      */
                                414                 :             10 :     cpath = makeNode(CustomPath);
                                415                 :             10 :     cpath->path.pathtype = T_CustomScan;
                                416                 :             10 :     cpath->path.parent = rel;
                                417                 :             10 :     cpath->path.pathtarget = rel->reltarget;
                                418                 :             10 :     cpath->path.rows = rel->rows * tnode->repeat_count;
                                419                 :             10 :     cpath->path.startup_cost = 0;
                                420                 :             10 :     cpath->path.total_cost = 0;
                                421                 :                : 
                                422                 :                :     /*
                                423                 :                :      * Consider it as parallel safe, since our scan touches nothing but its
                                424                 :                :      * own relation and keeps no state outside the CustomScanState.
                                425                 :                :      */
                                426                 :             10 :     cpath->path.parallel_safe = rel->consider_parallel;
                                427                 :                : 
                                428                 :             10 :     cpath->flags = 0;
                                429                 :             10 :     cpath->custom_paths = NIL;
                                430                 :             10 :     cpath->custom_private = list_make1(tnode);
                                431                 :             10 :     cpath->methods = &test_custom_path_methods;
                                432                 :                : 
                                433                 :             10 :     add_path(rel, (Path *) cpath);
                                434                 :                : }
                                435                 :                : 
                                436                 :                : /*
                                437                 :                :  * test_get_extensible_node_methods
                                438                 :                :  *
                                439                 :                :  * Thin wrapper around GetExtensibleNodeMethods().  Returns the registered
                                440                 :                :  * extnodename, or NULL when missing_ok = true and the name is not found.
                                441                 :                :  */
                                442                 :                : Datum
                                443                 :              3 : test_get_extensible_node_methods(PG_FUNCTION_ARGS)
                                444                 :                : {
                                445                 :              3 :     char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
                                446                 :              3 :     bool        missing_ok = PG_GETARG_BOOL(1);
                                447                 :                :     const ExtensibleNodeMethods *methods;
                                448                 :                : 
                                449                 :              3 :     methods = GetExtensibleNodeMethods(name, missing_ok);
                                450         [ +  + ]:              2 :     if (methods == NULL)
                                451                 :              1 :         PG_RETURN_NULL();
                                452                 :                : 
                                453                 :              1 :     PG_RETURN_TEXT_P(cstring_to_text(methods->extnodename));
                                454                 :                : }
                                455                 :                : 
                                456                 :                : /*
                                457                 :                :  * test_get_custom_scan_methods
                                458                 :                :  *
                                459                 :                :  * Thin wrapper around GetCustomScanMethods().  Returns the registered
                                460                 :                :  * CustomName, or NULL when missing_ok = true and the name is not found.
                                461                 :                :  */
                                462                 :                : Datum
                                463                 :              3 : test_get_custom_scan_methods(PG_FUNCTION_ARGS)
                                464                 :                : {
                                465                 :              3 :     char       *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
                                466                 :              3 :     bool        missing_ok = PG_GETARG_BOOL(1);
                                467                 :                :     const CustomScanMethods *methods;
                                468                 :                : 
                                469                 :              3 :     methods = GetCustomScanMethods(name, missing_ok);
                                470         [ +  + ]:              2 :     if (methods == NULL)
                                471                 :              1 :         PG_RETURN_NULL();
                                472                 :                : 
                                473                 :              1 :     PG_RETURN_TEXT_P(cstring_to_text(methods->CustomName));
                                474                 :                : }
                                475                 :                : 
                                476                 :                : /*
                                477                 :                :  * Decode a TestExtNode via stringToNode(), rejecting a string describing
                                478                 :                :  * some other kind of node instead of misinterpreting it as one of ours.
                                479                 :                :  */
                                480                 :                : static TestExtNode *
                                481                 :             14 : text_to_test_ext_node(text *txt)
                                482                 :                : {
   10                           483                 :             14 :     char       *str = text_to_cstring(txt);
                                484                 :                :     ReadNodeContext ctx;
                                485                 :                :     TestExtNode *tnode;
                                486                 :                : 
                                487                 :             14 :     ctx.str = str;
                                488                 :                : 
                                489   [ +  +  +  + ]:             27 :     if (test_ext_node_next_token(&ctx, "{") == NULL ||
                                490         [ +  - ]:             25 :         test_ext_node_next_token(&ctx, "EXTENSIBLENODE") == NULL ||
                                491         [ +  + ]:             24 :         test_ext_node_next_token(&ctx, ":extnodename") == NULL ||
                                492                 :             12 :         test_ext_node_next_token(&ctx, TEST_EXT_NODE_NAME) == NULL)
   20                           493         [ +  - ]:              3 :         ereport(ERROR,
                                494                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                495                 :                :                  errmsg("argument is not a serialized \"%s\"",
                                496                 :                :                         TEST_EXT_NODE_NAME)));
                                497                 :                : 
   10                           498                 :             11 :     tnode = (TestExtNode *) stringToNode(str);
                                499   [ +  -  -  + ]:             10 :     Assert(IsA(tnode, ExtensibleNode) &&
                                500                 :                :            strcmp(tnode->base.extnodename, TEST_EXT_NODE_NAME) == 0);
                                501                 :                : 
                                502                 :             10 :     return tnode;
                                503                 :                : }
                                504                 :                : 
                                505                 :                : /*
                                506                 :                :  * test_ext_node_make
                                507                 :                :  *
                                508                 :                :  * Builds a TestExtNode and returns it.
                                509                 :                :  */
                                510                 :                : Datum
   20                           511                 :              7 : test_ext_node_make(PG_FUNCTION_ARGS)
                                512                 :                : {
                                513                 :                :     TestExtNode *tnode;
                                514                 :                : 
                                515                 :              7 :     tnode = (TestExtNode *) newNode(sizeof(TestExtNode), T_ExtensibleNode);
                                516                 :              7 :     tnode->base.extnodename = TEST_EXT_NODE_NAME;
                                517                 :              7 :     tnode->relid = PG_GETARG_OID(0);
                                518                 :              7 :     tnode->repeat_count = PG_GETARG_INT32(1);
                                519                 :                : 
                                520                 :              7 :     PG_RETURN_TEXT_P(TEST_EXT_NODE_TO_TEXT(tnode));
                                521                 :                : }
                                522                 :                : 
                                523                 :                : /*
                                524                 :                :  * test_ext_node_copy
                                525                 :                :  */
                                526                 :                : Datum
                                527                 :              2 : test_ext_node_copy(PG_FUNCTION_ARGS)
                                528                 :                : {
                                529                 :              2 :     TestExtNode *tnode = PG_GETARG_TEST_EXT_NODE(0);
                                530                 :                : 
                                531                 :              2 :     PG_RETURN_TEXT_P(TEST_EXT_NODE_TO_TEXT(copyObject(tnode)));
                                532                 :                : }
                                533                 :                : 
                                534                 :                : /*
                                535                 :                :  * test_ext_node_equal
                                536                 :                :  */
                                537                 :                : Datum
                                538                 :              3 : test_ext_node_equal(PG_FUNCTION_ARGS)
                                539                 :                : {
                                540                 :              3 :     TestExtNode *a = PG_GETARG_TEST_EXT_NODE(0);
                                541                 :              3 :     TestExtNode *b = PG_GETARG_TEST_EXT_NODE(1);
                                542                 :                : 
                                543                 :              3 :     PG_RETURN_BOOL(equal(a, b));
                                544                 :                : }
                                545                 :                : 
                                546                 :                : /*
                                547                 :                :  * test_ext_node_get_relid
                                548                 :                :  * test_ext_node_get_repeat_count
                                549                 :                :  *
                                550                 :                :  * Field accessors for the custom node contents.
                                551                 :                :  */
                                552                 :                : Datum
                                553                 :              5 : test_ext_node_get_relid(PG_FUNCTION_ARGS)
                                554                 :                : {
                                555                 :              5 :     PG_RETURN_OID(PG_GETARG_TEST_EXT_NODE(0)->relid);
                                556                 :                : }
                                557                 :                : 
                                558                 :                : Datum
                                559                 :              1 : test_ext_node_get_repeat_count(PG_FUNCTION_ARGS)
                                560                 :                : {
                                561                 :              1 :     PG_RETURN_INT32(PG_GETARG_TEST_EXT_NODE(0)->repeat_count);
                                562                 :                : }
                                563                 :                : 
                                564                 :                : /*
                                565                 :                :  * Module initialization
                                566                 :                :  */
                                567                 :                : void
                                568                 :              2 : _PG_init(void)
                                569                 :                : {
                                570                 :              2 :     RegisterCustomScanMethods(&test_custom_scan_methods);
                                571                 :              2 :     RegisterExtensibleNodeMethods(&test_ext_node_methods);
                                572                 :                : 
                                573                 :              2 :     DefineCustomIntVariable("test_extensible.repeat_count",
                                574                 :                :                             "Number of times the custom scan returns each row.",
                                575                 :                :                             NULL,
                                576                 :                :                             &test_repeat_count,
                                577                 :                :                             2,
                                578                 :                :                             1,
                                579                 :                :                             100,
                                580                 :                :                             PGC_USERSET,
                                581                 :                :                             0,
                                582                 :                :                             NULL, NULL, NULL);
                                583                 :                : 
                                584                 :              2 :     MarkGUCPrefixReserved("test_extensible");
                                585                 :                : 
                                586                 :                :     /* Install the path-list hook to inject CustomPaths for the test table */
                                587                 :              2 :     prev_set_rel_pathlist_hook = set_rel_pathlist_hook;
                                588                 :              2 :     set_rel_pathlist_hook = test_set_rel_pathlist;
                                589                 :              2 : }
        

Generated by: LCOV version 2.0-1