LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - misc.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 76.1 % 351 267 47 11 37 30 9 133 48 77 26 139 10 8
Current Date: 2023-04-08 15:15:32 Functions: 91.7 % 24 22 2 18 4 2 22
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * misc.c
       4                 :  *
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *    src/backend/utils/adt/misc.c
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 :  */
      15                 : #include "postgres.h"
      16                 : 
      17                 : #include <sys/file.h>
      18                 : #include <sys/stat.h>
      19                 : #include <dirent.h>
      20                 : #include <fcntl.h>
      21                 : #include <math.h>
      22                 : #include <unistd.h>
      23                 : 
      24                 : #include "access/sysattr.h"
      25                 : #include "access/table.h"
      26                 : #include "catalog/catalog.h"
      27                 : #include "catalog/pg_tablespace.h"
      28                 : #include "catalog/pg_type.h"
      29                 : #include "catalog/system_fk_info.h"
      30                 : #include "commands/dbcommands.h"
      31                 : #include "commands/tablespace.h"
      32                 : #include "common/keywords.h"
      33                 : #include "funcapi.h"
      34                 : #include "miscadmin.h"
      35                 : #include "nodes/miscnodes.h"
      36                 : #include "parser/parse_type.h"
      37                 : #include "parser/scansup.h"
      38                 : #include "pgstat.h"
      39                 : #include "postmaster/syslogger.h"
      40                 : #include "rewrite/rewriteHandler.h"
      41                 : #include "storage/fd.h"
      42                 : #include "storage/latch.h"
      43                 : #include "tcop/tcopprot.h"
      44                 : #include "utils/builtins.h"
      45                 : #include "utils/fmgroids.h"
      46                 : #include "utils/lsyscache.h"
      47                 : #include "utils/ruleutils.h"
      48                 : #include "utils/timestamp.h"
      49                 : 
      50                 : 
      51                 : /*
      52                 :  * structure to cache metadata needed in pg_input_is_valid_common
      53                 :  */
      54                 : typedef struct ValidIOData
      55                 : {
      56                 :     Oid         typoid;
      57                 :     int32       typmod;
      58                 :     bool        typname_constant;
      59                 :     Oid         typiofunc;
      60                 :     Oid         typioparam;
      61                 :     FmgrInfo    inputproc;
      62                 : } ValidIOData;
      63                 : 
      64                 : static bool pg_input_is_valid_common(FunctionCallInfo fcinfo,
      65                 :                                      text *txt, text *typname,
      66                 :                                      ErrorSaveContext *escontext);
      67                 : 
      68                 : 
      69                 : /*
      70                 :  * Common subroutine for num_nulls() and num_nonnulls().
      71                 :  * Returns true if successful, false if function should return NULL.
      72                 :  * If successful, total argument count and number of nulls are
      73                 :  * returned into *nargs and *nulls.
      74                 :  */
      75                 : static bool
      76 GIC          60 : count_nulls(FunctionCallInfo fcinfo,
      77                 :             int32 *nargs, int32 *nulls)
      78                 : {
      79              60 :     int32       count = 0;
      80                 :     int         i;
      81                 : 
      82                 :     /* Did we get a VARIADIC array argument, or separate arguments? */
      83              60 :     if (get_fn_expr_variadic(fcinfo->flinfo))
      84                 :     {
      85                 :         ArrayType  *arr;
      86                 :         int         ndims,
      87                 :                     nitems,
      88                 :                    *dims;
      89                 :         bits8      *bitmap;
      90                 : 
      91              30 :         Assert(PG_NARGS() == 1);
      92                 : 
      93                 :         /*
      94                 :          * If we get a null as VARIADIC array argument, we can't say anything
      95                 :          * useful about the number of elements, so return NULL.  This behavior
      96                 :          * is consistent with other variadic functions - see concat_internal.
      97 ECB             :          */
      98 GIC          30 :         if (PG_ARGISNULL(0))
      99               6 :             return false;
     100 ECB             : 
     101                 :         /*
     102                 :          * Non-null argument had better be an array.  We assume that any call
     103                 :          * context that could let get_fn_expr_variadic return true will have
     104                 :          * checked that a VARIADIC-labeled parameter actually is an array.  So
     105                 :          * it should be okay to just Assert that it's an array rather than
     106                 :          * doing a full-fledged error check.
     107                 :          */
     108 GIC          24 :         Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, 0))));
     109                 : 
     110                 :         /* OK, safe to fetch the array value */
     111              24 :         arr = PG_GETARG_ARRAYTYPE_P(0);
     112 ECB             : 
     113                 :         /* Count the array elements */
     114 GIC          24 :         ndims = ARR_NDIM(arr);
     115              24 :         dims = ARR_DIMS(arr);
     116              24 :         nitems = ArrayGetNItems(ndims, dims);
     117                 : 
     118                 :         /* Count those that are NULL */
     119 CBC          24 :         bitmap = ARR_NULLBITMAP(arr);
     120              24 :         if (bitmap)
     121                 :         {
     122 GIC          12 :             int         bitmask = 1;
     123                 : 
     124             636 :             for (i = 0; i < nitems; i++)
     125                 :             {
     126             624 :                 if ((*bitmap & bitmask) == 0)
     127              12 :                     count++;
     128                 : 
     129 CBC         624 :                 bitmask <<= 1;
     130 GIC         624 :                 if (bitmask == 0x100)
     131                 :                 {
     132 CBC          72 :                     bitmap++;
     133 GIC          72 :                     bitmask = 1;
     134                 :                 }
     135 ECB             :             }
     136                 :         }
     137                 : 
     138 GIC          24 :         *nargs = nitems;
     139              24 :         *nulls = count;
     140 ECB             :     }
     141                 :     else
     142                 :     {
     143                 :         /* Separate arguments, so just count 'em */
     144 GIC         102 :         for (i = 0; i < PG_NARGS(); i++)
     145 ECB             :         {
     146 GIC          72 :             if (PG_ARGISNULL(i))
     147 CBC          42 :                 count++;
     148 ECB             :         }
     149                 : 
     150 CBC          30 :         *nargs = PG_NARGS();
     151              30 :         *nulls = count;
     152                 :     }
     153 ECB             : 
     154 CBC          54 :     return true;
     155                 : }
     156                 : 
     157                 : /*
     158                 :  * num_nulls()
     159 ECB             :  *  Count the number of NULL arguments
     160                 :  */
     161                 : Datum
     162 GIC          30 : pg_num_nulls(PG_FUNCTION_ARGS)
     163                 : {
     164                 :     int32       nargs,
     165 ECB             :                 nulls;
     166                 : 
     167 CBC          30 :     if (!count_nulls(fcinfo, &nargs, &nulls))
     168               3 :         PG_RETURN_NULL();
     169                 : 
     170 GIC          27 :     PG_RETURN_INT32(nulls);
     171 ECB             : }
     172                 : 
     173                 : /*
     174                 :  * num_nonnulls()
     175                 :  *  Count the number of non-NULL arguments
     176                 :  */
     177                 : Datum
     178 GIC          30 : pg_num_nonnulls(PG_FUNCTION_ARGS)
     179                 : {
     180                 :     int32       nargs,
     181                 :                 nulls;
     182                 : 
     183 CBC          30 :     if (!count_nulls(fcinfo, &nargs, &nulls))
     184 GIC           3 :         PG_RETURN_NULL();
     185                 : 
     186              27 :     PG_RETURN_INT32(nargs - nulls);
     187                 : }
     188 ECB             : 
     189                 : 
     190                 : /*
     191                 :  * current_database()
     192                 :  *  Expose the current database to the user
     193                 :  */
     194                 : Datum
     195 GIC        2371 : current_database(PG_FUNCTION_ARGS)
     196                 : {
     197                 :     Name        db;
     198                 : 
     199 CBC        2371 :     db = (Name) palloc(NAMEDATALEN);
     200                 : 
     201 GIC        2371 :     namestrcpy(db, get_database_name(MyDatabaseId));
     202            2371 :     PG_RETURN_NAME(db);
     203                 : }
     204 ECB             : 
     205                 : 
     206                 : /*
     207                 :  * current_query()
     208                 :  *  Expose the current query to the user (useful in stored procedures)
     209                 :  *  We might want to use ActivePortal->sourceText someday.
     210                 :  */
     211                 : Datum
     212 UIC           0 : current_query(PG_FUNCTION_ARGS)
     213                 : {
     214                 :     /* there is no easy way to access the more concise 'query_string' */
     215               0 :     if (debug_query_string)
     216 LBC           0 :         PG_RETURN_TEXT_P(cstring_to_text(debug_query_string));
     217                 :     else
     218 UIC           0 :         PG_RETURN_NULL();
     219                 : }
     220 ECB             : 
     221                 : /* Function to find out which databases make use of a tablespace */
     222                 : 
     223                 : Datum
     224 GIC           3 : pg_tablespace_databases(PG_FUNCTION_ARGS)
     225                 : {
     226               3 :     Oid         tablespaceOid = PG_GETARG_OID(0);
     227               3 :     ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
     228                 :     char       *location;
     229                 :     DIR        *dirdesc;
     230                 :     struct dirent *de;
     231                 : 
     232               3 :     InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
     233 EUB             : 
     234 GIC           3 :     if (tablespaceOid == GLOBALTABLESPACE_OID)
     235                 :     {
     236 UBC           0 :         ereport(WARNING,
     237 EUB             :                 (errmsg("global tablespace never has databases")));
     238                 :         /* return empty tuplestore */
     239 UBC           0 :         return (Datum) 0;
     240                 :     }
     241                 : 
     242 GIC           3 :     if (tablespaceOid == DEFAULTTABLESPACE_OID)
     243 GNC           3 :         location = "base";
     244                 :     else
     245 LBC           0 :         location = psprintf("pg_tblspc/%u/%s", tablespaceOid,
     246                 :                             TABLESPACE_VERSION_DIRECTORY);
     247 ECB             : 
     248 CBC           3 :     dirdesc = AllocateDir(location);
     249                 : 
     250 GIC           3 :     if (!dirdesc)
     251                 :     {
     252                 :         /* the only expected error is ENOENT */
     253 LBC           0 :         if (errno != ENOENT)
     254 UIC           0 :             ereport(ERROR,
     255 ECB             :                     (errcode_for_file_access(),
     256                 :                      errmsg("could not open directory \"%s\": %m",
     257 EUB             :                             location)));
     258 UIC           0 :         ereport(WARNING,
     259                 :                 (errmsg("%u is not a tablespace OID", tablespaceOid)));
     260 EUB             :         /* return empty tuplestore */
     261 UIC           0 :         return (Datum) 0;
     262                 :     }
     263 ECB             : 
     264 CBC          27 :     while ((de = ReadDir(dirdesc, location)) != NULL)
     265                 :     {
     266 GBC          24 :         Oid         datOid = atooid(de->d_name);
     267                 :         char       *subdir;
     268                 :         bool        isempty;
     269 ECB             :         Datum       values[1];
     270                 :         bool        nulls[1];
     271                 : 
     272                 :         /* this test skips . and .., but is awfully weak */
     273 GIC          24 :         if (!datOid)
     274 GBC           9 :             continue;
     275 EUB             : 
     276                 :         /* if database subdir is empty, don't report tablespace as used */
     277                 : 
     278 GIC          15 :         subdir = psprintf("%s/%s", location, de->d_name);
     279 GBC          15 :         isempty = directory_is_empty(subdir);
     280 GIC          15 :         pfree(subdir);
     281                 : 
     282 GBC          15 :         if (isempty)
     283 UIC           0 :             continue;           /* indeed, nothing in it */
     284                 : 
     285 CBC          15 :         values[0] = ObjectIdGetDatum(datOid);
     286 GIC          15 :         nulls[0] = false;
     287 ECB             : 
     288 GIC          15 :         tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
     289                 :                              values, nulls);
     290                 :     }
     291                 : 
     292               3 :     FreeDir(dirdesc);
     293               3 :     return (Datum) 0;
     294 ECB             : }
     295                 : 
     296                 : 
     297                 : /*
     298                 :  * pg_tablespace_location - get location for a tablespace
     299                 :  */
     300                 : Datum
     301 CBC          25 : pg_tablespace_location(PG_FUNCTION_ARGS)
     302                 : {
     303              25 :     Oid         tablespaceOid = PG_GETARG_OID(0);
     304 EUB             :     char        sourcepath[MAXPGPATH];
     305                 :     char        targetpath[MAXPGPATH];
     306 ECB             :     int         rllen;
     307                 :     struct stat st;
     308                 : 
     309                 :     /*
     310                 :      * It's useful to apply this function to pg_class.reltablespace, wherein
     311                 :      * zero means "the database's default tablespace".  So, rather than
     312                 :      * throwing an error for zero, we choose to assume that's what is meant.
     313                 :      */
     314 GIC          25 :     if (tablespaceOid == InvalidOid)
     315 UIC           0 :         tablespaceOid = MyDatabaseTableSpace;
     316                 : 
     317                 :     /*
     318                 :      * Return empty string for the cluster's default tablespaces
     319                 :      */
     320 CBC          25 :     if (tablespaceOid == DEFAULTTABLESPACE_OID ||
     321                 :         tablespaceOid == GLOBALTABLESPACE_OID)
     322              22 :         PG_RETURN_TEXT_P(cstring_to_text(""));
     323                 : 
     324                 :     /*
     325                 :      * Find the location of the tablespace by reading the symbolic link that
     326                 :      * is in pg_tblspc/<oid>.
     327                 :      */
     328 GIC           3 :     snprintf(sourcepath, sizeof(sourcepath), "pg_tblspc/%u", tablespaceOid);
     329                 : 
     330                 :     /*
     331 ECB             :      * Before reading the link, check if the source path is a link or a
     332 EUB             :      * junction point.  Note that a directory is possible for a tablespace
     333                 :      * created with allow_in_place_tablespaces enabled.  If a directory is
     334                 :      * found, a relative path to the data directory is returned.
     335                 :      */
     336 GIC           3 :     if (lstat(sourcepath, &st) < 0)
     337                 :     {
     338 UIC           0 :         ereport(ERROR,
     339                 :                 (errcode_for_file_access(),
     340                 :                  errmsg("could not stat file \"%s\": %m",
     341 ECB             :                         sourcepath)));
     342                 :     }
     343                 : 
     344 GIC           3 :     if (!S_ISLNK(st.st_mode))
     345               3 :         PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
     346                 : 
     347                 :     /*
     348 ECB             :      * In presence of a link or a junction point, return the path pointing to.
     349                 :      */
     350 UBC           0 :     rllen = readlink(sourcepath, targetpath, sizeof(targetpath));
     351 UIC           0 :     if (rllen < 0)
     352               0 :         ereport(ERROR,
     353                 :                 (errcode_for_file_access(),
     354                 :                  errmsg("could not read symbolic link \"%s\": %m",
     355                 :                         sourcepath)));
     356 LBC           0 :     if (rllen >= sizeof(targetpath))
     357               0 :         ereport(ERROR,
     358                 :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     359                 :                  errmsg("symbolic link \"%s\" target is too long",
     360                 :                         sourcepath)));
     361 UIC           0 :     targetpath[rllen] = '\0';
     362 EUB             : 
     363 UBC           0 :     PG_RETURN_TEXT_P(cstring_to_text(targetpath));
     364                 : }
     365                 : 
     366                 : /*
     367 EUB             :  * pg_sleep - delay for N seconds
     368                 :  */
     369                 : Datum
     370 GIC          36 : pg_sleep(PG_FUNCTION_ARGS)
     371                 : {
     372              36 :     float8      secs = PG_GETARG_FLOAT8(0);
     373                 :     float8      endtime;
     374                 : 
     375                 :     /*
     376 ECB             :      * We sleep using WaitLatch, to ensure that we'll wake up promptly if an
     377                 :      * important signal (such as SIGALRM or SIGINT) arrives.  Because
     378                 :      * WaitLatch's upper limit of delay is INT_MAX milliseconds, and the user
     379                 :      * might ask for more than that, we sleep for at most 10 minutes and then
     380                 :      * loop.
     381                 :      *
     382                 :      * By computing the intended stop time initially, we avoid accumulation of
     383                 :      * extra delay across multiple sleeps.  This also ensures we won't delay
     384                 :      * less than the specified time when WaitLatch is terminated early by a
     385                 :      * non-query-canceling signal such as SIGHUP.
     386                 :      */
     387                 : #define GetNowFloat()   ((float8) GetCurrentTimestamp() / 1000000.0)
     388                 : 
     389 GIC          36 :     endtime = GetNowFloat() + secs;
     390                 : 
     391                 :     for (;;)
     392              47 :     {
     393                 :         float8      delay;
     394                 :         long        delay_ms;
     395 ECB             : 
     396 GIC          83 :         CHECK_FOR_INTERRUPTS();
     397                 : 
     398 CBC          82 :         delay = endtime - GetNowFloat();
     399 GIC          82 :         if (delay >= 600.0)
     400 UIC           0 :             delay_ms = 600000;
     401 GIC          82 :         else if (delay > 0.0)
     402 CBC          47 :             delay_ms = (long) ceil(delay * 1000.0);
     403                 :         else
     404              35 :             break;
     405 ECB             : 
     406 GBC          47 :         (void) WaitLatch(MyLatch,
     407 ECB             :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
     408                 :                          delay_ms,
     409                 :                          WAIT_EVENT_PG_SLEEP);
     410 CBC          47 :         ResetLatch(MyLatch);
     411                 :     }
     412 ECB             : 
     413 GIC          35 :     PG_RETURN_VOID();
     414                 : }
     415                 : 
     416 ECB             : /* Function to return the list of grammar keywords */
     417                 : Datum
     418 UIC           0 : pg_get_keywords(PG_FUNCTION_ARGS)
     419 ECB             : {
     420                 :     FuncCallContext *funcctx;
     421                 : 
     422 UIC           0 :     if (SRF_IS_FIRSTCALL())
     423                 :     {
     424 EUB             :         MemoryContext oldcontext;
     425                 :         TupleDesc   tupdesc;
     426                 : 
     427 UIC           0 :         funcctx = SRF_FIRSTCALL_INIT();
     428 UBC           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     429                 : 
     430 UNC           0 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     431               0 :             elog(ERROR, "return type must be a row type");
     432               0 :         funcctx->tuple_desc = tupdesc;
     433 UIC           0 :         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
     434                 : 
     435 UBC           0 :         MemoryContextSwitchTo(oldcontext);
     436                 :     }
     437 EUB             : 
     438 UIC           0 :     funcctx = SRF_PERCALL_SETUP();
     439                 : 
     440               0 :     if (funcctx->call_cntr < ScanKeywords.num_keywords)
     441                 :     {
     442                 :         char       *values[5];
     443 EUB             :         HeapTuple   tuple;
     444                 : 
     445                 :         /* cast-away-const is ugly but alternatives aren't much better */
     446 UIC           0 :         values[0] = unconstify(char *,
     447 EUB             :                                GetScanKeyword(funcctx->call_cntr,
     448                 :                                               &ScanKeywords));
     449                 : 
     450 UBC           0 :         switch (ScanKeywordCategories[funcctx->call_cntr])
     451 EUB             :         {
     452 UBC           0 :             case UNRESERVED_KEYWORD:
     453               0 :                 values[1] = "U";
     454               0 :                 values[3] = _("unreserved");
     455               0 :                 break;
     456               0 :             case COL_NAME_KEYWORD:
     457               0 :                 values[1] = "C";
     458               0 :                 values[3] = _("unreserved (cannot be function or type name)");
     459               0 :                 break;
     460               0 :             case TYPE_FUNC_NAME_KEYWORD:
     461               0 :                 values[1] = "T";
     462               0 :                 values[3] = _("reserved (can be function or type name)");
     463               0 :                 break;
     464               0 :             case RESERVED_KEYWORD:
     465               0 :                 values[1] = "R";
     466               0 :                 values[3] = _("reserved");
     467               0 :                 break;
     468               0 :             default:            /* shouldn't be possible */
     469 UIC           0 :                 values[1] = NULL;
     470               0 :                 values[3] = NULL;
     471 UBC           0 :                 break;
     472                 :         }
     473 EUB             : 
     474 UBC           0 :         if (ScanKeywordBareLabel[funcctx->call_cntr])
     475                 :         {
     476 UIC           0 :             values[2] = "true";
     477               0 :             values[4] = _("can be bare label");
     478 EUB             :         }
     479                 :         else
     480                 :         {
     481 UIC           0 :             values[2] = "false";
     482 UBC           0 :             values[4] = _("requires AS");
     483                 :         }
     484 EUB             : 
     485 UIC           0 :         tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
     486                 : 
     487 UBC           0 :         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
     488                 :     }
     489                 : 
     490 UIC           0 :     SRF_RETURN_DONE(funcctx);
     491                 : }
     492                 : 
     493 ECB             : 
     494                 : /* Function to return the list of catalog foreign key relationships */
     495                 : Datum
     496 GIC         660 : pg_get_catalog_foreign_keys(PG_FUNCTION_ARGS)
     497                 : {
     498 ECB             :     FuncCallContext *funcctx;
     499                 :     FmgrInfo   *arrayinp;
     500                 : 
     501 GIC         660 :     if (SRF_IS_FIRSTCALL())
     502                 :     {
     503 ECB             :         MemoryContext oldcontext;
     504                 :         TupleDesc   tupdesc;
     505                 : 
     506 CBC           3 :         funcctx = SRF_FIRSTCALL_INIT();
     507 GBC           3 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     508 ECB             : 
     509 GNC           3 :         if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     510 UNC           0 :             elog(ERROR, "return type must be a row type");
     511 CBC           3 :         funcctx->tuple_desc = BlessTupleDesc(tupdesc);
     512 ECB             : 
     513                 :         /*
     514                 :          * We use array_in to convert the C strings in sys_fk_relationships[]
     515                 :          * to text arrays.  But we cannot use DirectFunctionCallN to call
     516                 :          * array_in, and it wouldn't be very efficient if we could.  Fill an
     517                 :          * FmgrInfo to use for the call.
     518                 :          */
     519 GIC           3 :         arrayinp = (FmgrInfo *) palloc(sizeof(FmgrInfo));
     520               3 :         fmgr_info(F_ARRAY_IN, arrayinp);
     521 CBC           3 :         funcctx->user_fctx = arrayinp;
     522                 : 
     523               3 :         MemoryContextSwitchTo(oldcontext);
     524 ECB             :     }
     525                 : 
     526 GIC         660 :     funcctx = SRF_PERCALL_SETUP();
     527             660 :     arrayinp = (FmgrInfo *) funcctx->user_fctx;
     528 ECB             : 
     529 CBC         660 :     if (funcctx->call_cntr < lengthof(sys_fk_relationships))
     530                 :     {
     531 GIC         657 :         const SysFKRelationship *fkrel = &sys_fk_relationships[funcctx->call_cntr];
     532                 :         Datum       values[6];
     533 ECB             :         bool        nulls[6];
     534                 :         HeapTuple   tuple;
     535                 : 
     536 CBC         657 :         memset(nulls, false, sizeof(nulls));
     537                 : 
     538             657 :         values[0] = ObjectIdGetDatum(fkrel->fk_table);
     539 GIC         657 :         values[1] = FunctionCall3(arrayinp,
     540                 :                                   CStringGetDatum(fkrel->fk_columns),
     541 ECB             :                                   ObjectIdGetDatum(TEXTOID),
     542                 :                                   Int32GetDatum(-1));
     543 GIC         657 :         values[2] = ObjectIdGetDatum(fkrel->pk_table);
     544             657 :         values[3] = FunctionCall3(arrayinp,
     545                 :                                   CStringGetDatum(fkrel->pk_columns),
     546                 :                                   ObjectIdGetDatum(TEXTOID),
     547                 :                                   Int32GetDatum(-1));
     548             657 :         values[4] = BoolGetDatum(fkrel->is_array);
     549 CBC         657 :         values[5] = BoolGetDatum(fkrel->is_opt);
     550                 : 
     551             657 :         tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
     552                 : 
     553 GIC         657 :         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
     554                 :     }
     555                 : 
     556               3 :     SRF_RETURN_DONE(funcctx);
     557                 : }
     558                 : 
     559                 : 
     560 ECB             : /*
     561                 :  * Return the type of the argument.
     562                 :  */
     563                 : Datum
     564 GIC         758 : pg_typeof(PG_FUNCTION_ARGS)
     565 ECB             : {
     566 CBC         758 :     PG_RETURN_OID(get_fn_expr_argtype(fcinfo->flinfo, 0));
     567 EUB             : }
     568 ECB             : 
     569                 : 
     570                 : /*
     571                 :  * Implementation of the COLLATE FOR expression; returns the collation
     572                 :  * of the argument.
     573                 :  */
     574                 : Datum
     575 CBC          15 : pg_collation_for(PG_FUNCTION_ARGS)
     576 ECB             : {
     577                 :     Oid         typeid;
     578                 :     Oid         collid;
     579                 : 
     580 GIC          15 :     typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     581              15 :     if (!typeid)
     582 UIC           0 :         PG_RETURN_NULL();
     583 GIC          15 :     if (!type_is_collatable(typeid) && typeid != UNKNOWNOID)
     584               3 :         ereport(ERROR,
     585                 :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     586                 :                  errmsg("collations are not supported by type %s",
     587                 :                         format_type_be(typeid))));
     588                 : 
     589 CBC          12 :     collid = PG_GET_COLLATION();
     590 GIC          12 :     if (!collid)
     591 CBC           3 :         PG_RETURN_NULL();
     592               9 :     PG_RETURN_TEXT_P(cstring_to_text(generate_collation_name(collid)));
     593                 : }
     594 ECB             : 
     595                 : 
     596                 : /*
     597                 :  * pg_relation_is_updatable - determine which update events the specified
     598                 :  * relation supports.
     599                 :  *
     600                 :  * This relies on relation_is_updatable() in rewriteHandler.c, which see
     601                 :  * for additional information.
     602                 :  */
     603                 : Datum
     604 GIC         447 : pg_relation_is_updatable(PG_FUNCTION_ARGS)
     605                 : {
     606 CBC         447 :     Oid         reloid = PG_GETARG_OID(0);
     607 GIC         447 :     bool        include_triggers = PG_GETARG_BOOL(1);
     608 ECB             : 
     609 CBC         447 :     PG_RETURN_INT32(relation_is_updatable(reloid, NIL, include_triggers, NULL));
     610 ECB             : }
     611                 : 
     612                 : /*
     613                 :  * pg_column_is_updatable - determine whether a column is updatable
     614                 :  *
     615                 :  * This function encapsulates the decision about just what
     616 EUB             :  * information_schema.columns.is_updatable actually means.  It's not clear
     617                 :  * whether deletability of the column's relation should be required, so
     618 ECB             :  * we want that decision in C code where we could change it without initdb.
     619                 :  */
     620                 : Datum
     621 GIC         285 : pg_column_is_updatable(PG_FUNCTION_ARGS)
     622                 : {
     623             285 :     Oid         reloid = PG_GETARG_OID(0);
     624 CBC         285 :     AttrNumber  attnum = PG_GETARG_INT16(1);
     625 GIC         285 :     AttrNumber  col = attnum - FirstLowInvalidHeapAttributeNumber;
     626             285 :     bool        include_triggers = PG_GETARG_BOOL(2);
     627                 :     int         events;
     628                 : 
     629                 :     /* System columns are never updatable */
     630             285 :     if (attnum <= 0)
     631 UIC           0 :         PG_RETURN_BOOL(false);
     632                 : 
     633 GIC         285 :     events = relation_is_updatable(reloid, NIL, include_triggers,
     634                 :                                    bms_make_singleton(col));
     635                 : 
     636                 :     /* We require both updatability and deletability of the relation */
     637 ECB             : #define REQ_EVENTS ((1 << CMD_UPDATE) | (1 << CMD_DELETE))
     638                 : 
     639 CBC         285 :     PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS);
     640 ECB             : }
     641                 : 
     642                 : 
     643                 : /*
     644                 :  * pg_input_is_valid - test whether string is valid input for datatype.
     645                 :  *
     646                 :  * Returns true if OK, false if not.
     647                 :  *
     648                 :  * This will only work usefully if the datatype's input function has been
     649                 :  * updated to return "soft" errors via errsave/ereturn.
     650                 :  */
     651                 : Datum
     652 GNC         442 : pg_input_is_valid(PG_FUNCTION_ARGS)
     653                 : {
     654             442 :     text       *txt = PG_GETARG_TEXT_PP(0);
     655             442 :     text       *typname = PG_GETARG_TEXT_PP(1);
     656             442 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     657                 : 
     658             442 :     PG_RETURN_BOOL(pg_input_is_valid_common(fcinfo, txt, typname,
     659                 :                                             &escontext));
     660                 : }
     661                 : 
     662                 : /*
     663                 :  * pg_input_error_info - test whether string is valid input for datatype.
     664                 :  *
     665                 :  * Returns NULL if OK, else the primary message, detail message, hint message
     666                 :  * and sql error code from the error.
     667                 :  *
     668                 :  * This will only work usefully if the datatype's input function has been
     669                 :  * updated to return "soft" errors via errsave/ereturn.
     670                 :  */
     671                 : Datum
     672             383 : pg_input_error_info(PG_FUNCTION_ARGS)
     673                 : {
     674             383 :     text       *txt = PG_GETARG_TEXT_PP(0);
     675             383 :     text       *typname = PG_GETARG_TEXT_PP(1);
     676             383 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
     677                 :     TupleDesc   tupdesc;
     678                 :     Datum       values[4];
     679                 :     bool        isnull[4];
     680                 : 
     681             383 :     if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
     682 UNC           0 :         elog(ERROR, "return type must be a row type");
     683                 : 
     684                 :     /* Enable details_wanted */
     685 GNC         383 :     escontext.details_wanted = true;
     686                 : 
     687             383 :     if (pg_input_is_valid_common(fcinfo, txt, typname,
     688                 :                                  &escontext))
     689              13 :         memset(isnull, true, sizeof(isnull));
     690                 :     else
     691                 :     {
     692                 :         char       *sqlstate;
     693                 : 
     694             355 :         Assert(escontext.error_occurred);
     695             355 :         Assert(escontext.error_data != NULL);
     696             355 :         Assert(escontext.error_data->message != NULL);
     697                 : 
     698             355 :         memset(isnull, false, sizeof(isnull));
     699                 : 
     700             355 :         values[0] = CStringGetTextDatum(escontext.error_data->message);
     701                 : 
     702             355 :         if (escontext.error_data->detail != NULL)
     703              31 :             values[1] = CStringGetTextDatum(escontext.error_data->detail);
     704                 :         else
     705             324 :             isnull[1] = true;
     706                 : 
     707             355 :         if (escontext.error_data->hint != NULL)
     708 UNC           0 :             values[2] = CStringGetTextDatum(escontext.error_data->hint);
     709                 :         else
     710 GNC         355 :             isnull[2] = true;
     711                 : 
     712             355 :         sqlstate = unpack_sql_state(escontext.error_data->sqlerrcode);
     713             355 :         values[3] = CStringGetTextDatum(sqlstate);
     714                 :     }
     715                 : 
     716             368 :     return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
     717                 : }
     718                 : 
     719                 : /* Common subroutine for the above */
     720                 : static bool
     721             825 : pg_input_is_valid_common(FunctionCallInfo fcinfo,
     722                 :                          text *txt, text *typname,
     723                 :                          ErrorSaveContext *escontext)
     724                 : {
     725             825 :     char       *str = text_to_cstring(txt);
     726                 :     ValidIOData *my_extra;
     727                 :     Datum       converted;
     728                 : 
     729                 :     /*
     730                 :      * We arrange to look up the needed I/O info just once per series of
     731                 :      * calls, assuming the data type doesn't change underneath us.
     732                 :      */
     733             825 :     my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra;
     734             825 :     if (my_extra == NULL)
     735                 :     {
     736            1538 :         fcinfo->flinfo->fn_extra =
     737             769 :             MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
     738                 :                                sizeof(ValidIOData));
     739             769 :         my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra;
     740             769 :         my_extra->typoid = InvalidOid;
     741                 :         /* Detect whether typname argument is constant. */
     742             769 :         my_extra->typname_constant = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
     743                 :     }
     744                 : 
     745                 :     /*
     746                 :      * If the typname argument is constant, we only need to parse it the first
     747                 :      * time through.
     748                 :      */
     749             825 :     if (my_extra->typoid == InvalidOid || !my_extra->typname_constant)
     750                 :     {
     751             787 :         char       *typnamestr = text_to_cstring(typname);
     752                 :         Oid         typoid;
     753                 : 
     754                 :         /* Parse type-name argument to obtain type OID and encoded typmod. */
     755             787 :         (void) parseTypeString(typnamestr, &typoid, &my_extra->typmod, NULL);
     756                 : 
     757                 :         /* Update type-specific info if typoid changed. */
     758             787 :         if (my_extra->typoid != typoid)
     759                 :         {
     760             777 :             getTypeInputInfo(typoid,
     761                 :                              &my_extra->typiofunc,
     762                 :                              &my_extra->typioparam);
     763             777 :             fmgr_info_cxt(my_extra->typiofunc, &my_extra->inputproc,
     764             777 :                           fcinfo->flinfo->fn_mcxt);
     765             777 :             my_extra->typoid = typoid;
     766                 :         }
     767                 :     }
     768                 : 
     769                 :     /* Now we can try to perform the conversion. */
     770             825 :     return InputFunctionCallSafe(&my_extra->inputproc,
     771                 :                                  str,
     772                 :                                  my_extra->typioparam,
     773                 :                                  my_extra->typmod,
     774                 :                                  (Node *) escontext,
     775                 :                                  &converted);
     776                 : }
     777                 : 
     778                 : 
     779 ECB             : /*
     780                 :  * Is character a valid identifier start?
     781                 :  * Must match scan.l's {ident_start} character class.
     782                 :  */
     783                 : static bool
     784 GIC        1101 : is_ident_start(unsigned char c)
     785                 : {
     786                 :     /* Underscores and ASCII letters are OK */
     787            1101 :     if (c == '_')
     788 UIC           0 :         return true;
     789 GIC        1101 :     if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
     790            1026 :         return true;
     791                 :     /* Any high-bit-set character is OK (might be part of a multibyte char) */
     792              75 :     if (IS_HIGHBIT_SET(c))
     793 LBC           0 :         return true;
     794 GIC          75 :     return false;
     795 ECB             : }
     796                 : 
     797                 : /*
     798                 :  * Is character a valid identifier continuation?
     799                 :  * Must match scan.l's {ident_cont} character class.
     800                 :  */
     801                 : static bool
     802 CBC        1026 : is_ident_cont(unsigned char c)
     803 EUB             : {
     804                 :     /* Can be digit or dollar sign ... */
     805 GIC        1026 :     if ((c >= '0' && c <= '9') || c == '$')
     806 LBC           0 :         return true;
     807                 :     /* ... or an identifier start character */
     808 CBC        1026 :     return is_ident_start(c);
     809                 : }
     810 ECB             : 
     811                 : /*
     812                 :  * parse_ident - parse a SQL qualified identifier into separate identifiers.
     813                 :  * When strict mode is active (second parameter), then any chars after
     814                 :  * the last identifier are disallowed.
     815                 :  */
     816                 : Datum
     817 CBC          57 : parse_ident(PG_FUNCTION_ARGS)
     818                 : {
     819              57 :     text       *qualname = PG_GETARG_TEXT_PP(0);
     820 GIC          57 :     bool        strict = PG_GETARG_BOOL(1);
     821 CBC          57 :     char       *qualname_str = text_to_cstring(qualname);
     822 GIC          57 :     ArrayBuildState *astate = NULL;
     823 ECB             :     char       *nextp;
     824 CBC          57 :     bool        after_dot = false;
     825                 : 
     826 ECB             :     /*
     827                 :      * The code below scribbles on qualname_str in some cases, so we should
     828                 :      * reconvert qualname if we need to show the original string in error
     829 EUB             :      * messages.
     830                 :      */
     831 CBC          57 :     nextp = qualname_str;
     832                 : 
     833 ECB             :     /* skip leading whitespace */
     834 CBC          72 :     while (scanner_isspace(*nextp))
     835 GIC          15 :         nextp++;
     836                 : 
     837 ECB             :     for (;;)
     838 GIC          48 :     {
     839                 :         char       *curname;
     840             105 :         bool        missing_ident = true;
     841                 : 
     842 CBC         105 :         if (*nextp == '"')
     843                 :         {
     844                 :             char       *endp;
     845                 : 
     846              30 :             curname = nextp + 1;
     847                 :             for (;;)
     848                 :             {
     849 GIC          30 :                 endp = strchr(nextp + 1, '"');
     850              30 :                 if (endp == NULL)
     851 UIC           0 :                     ereport(ERROR,
     852                 :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     853                 :                              errmsg("string is not a valid identifier: \"%s\"",
     854 ECB             :                                     text_to_cstring(qualname)),
     855                 :                              errdetail("String has unclosed double quotes.")));
     856 GIC          30 :                 if (endp[1] != '"')
     857 CBC          30 :                     break;
     858 LBC           0 :                 memmove(endp, endp + 1, strlen(endp));
     859 UIC           0 :                 nextp = endp;
     860 ECB             :             }
     861 CBC          30 :             nextp = endp + 1;
     862 GIC          30 :             *endp = '\0';
     863 ECB             : 
     864 GIC          30 :             if (endp - curname == 0)
     865 UIC           0 :                 ereport(ERROR,
     866                 :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     867                 :                          errmsg("string is not a valid identifier: \"%s\"",
     868                 :                                 text_to_cstring(qualname)),
     869                 :                          errdetail("Quoted identifier must not be empty.")));
     870 ECB             : 
     871 GIC          30 :             astate = accumArrayResult(astate, CStringGetTextDatum(curname),
     872 ECB             :                                       false, TEXTOID, CurrentMemoryContext);
     873 GIC          30 :             missing_ident = false;
     874                 :         }
     875              75 :         else if (is_ident_start((unsigned char) *nextp))
     876 ECB             :         {
     877                 :             char       *downname;
     878                 :             int         len;
     879                 :             text       *part;
     880                 : 
     881 CBC          51 :             curname = nextp++;
     882 GIC        1026 :             while (is_ident_cont((unsigned char) *nextp))
     883             975 :                 nextp++;
     884 ECB             : 
     885 CBC          51 :             len = nextp - curname;
     886 ECB             : 
     887                 :             /*
     888                 :              * We don't implicitly truncate identifiers. This is useful for
     889                 :              * allowing the user to check for specific parts of the identifier
     890                 :              * being too long. It's easy enough for the user to get the
     891                 :              * truncated names by casting our output to name[].
     892                 :              */
     893 GIC          51 :             downname = downcase_identifier(curname, len, false, false);
     894              51 :             part = cstring_to_text_with_len(downname, len);
     895              51 :             astate = accumArrayResult(astate, PointerGetDatum(part), false,
     896                 :                                       TEXTOID, CurrentMemoryContext);
     897              51 :             missing_ident = false;
     898                 :         }
     899                 : 
     900             105 :         if (missing_ident)
     901                 :         {
     902                 :             /* Different error messages based on where we failed. */
     903              24 :             if (*nextp == '.')
     904               9 :                 ereport(ERROR,
     905 ECB             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     906                 :                          errmsg("string is not a valid identifier: \"%s\"",
     907                 :                                 text_to_cstring(qualname)),
     908                 :                          errdetail("No valid identifier before \".\".")));
     909 GBC          15 :             else if (after_dot)
     910 CBC           6 :                 ereport(ERROR,
     911 ECB             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     912                 :                          errmsg("string is not a valid identifier: \"%s\"",
     913                 :                                 text_to_cstring(qualname)),
     914 EUB             :                          errdetail("No valid identifier after \".\".")));
     915 ECB             :             else
     916 GIC           9 :                 ereport(ERROR,
     917                 :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     918                 :                          errmsg("string is not a valid identifier: \"%s\"",
     919                 :                                 text_to_cstring(qualname))));
     920                 :         }
     921                 : 
     922             102 :         while (scanner_isspace(*nextp))
     923 CBC          21 :             nextp++;
     924                 : 
     925 GIC          81 :         if (*nextp == '.')
     926 ECB             :         {
     927 GBC          48 :             after_dot = true;
     928 GIC          48 :             nextp++;
     929 CBC          63 :             while (scanner_isspace(*nextp))
     930 GIC          15 :                 nextp++;
     931                 :         }
     932              33 :         else if (*nextp == '\0')
     933                 :         {
     934              18 :             break;
     935                 :         }
     936                 :         else
     937                 :         {
     938 CBC          15 :             if (strict)
     939 GIC          12 :                 ereport(ERROR,
     940 ECB             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     941                 :                          errmsg("string is not a valid identifier: \"%s\"",
     942                 :                                 text_to_cstring(qualname))));
     943 CBC           3 :             break;
     944                 :         }
     945 ECB             :     }
     946                 : 
     947 GIC          21 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
     948                 : }
     949                 : 
     950                 : /*
     951                 :  * pg_current_logfile
     952 ECB             :  *
     953                 :  * Report current log file used by log collector by scanning current_logfiles.
     954                 :  */
     955                 : Datum
     956 CBC           6 : pg_current_logfile(PG_FUNCTION_ARGS)
     957                 : {
     958                 :     FILE       *fd;
     959 ECB             :     char        lbuffer[MAXPGPATH];
     960                 :     char       *logfmt;
     961                 : 
     962                 :     /* The log format parameter is optional */
     963 CBC           6 :     if (PG_NARGS() == 0 || PG_ARGISNULL(0))
     964 UIC           0 :         logfmt = NULL;
     965                 :     else
     966                 :     {
     967 CBC           6 :         logfmt = text_to_cstring(PG_GETARG_TEXT_PP(0));
     968                 : 
     969 GIC           6 :         if (strcmp(logfmt, "stderr") != 0 &&
     970 CBC           4 :             strcmp(logfmt, "csvlog") != 0 &&
     971               2 :             strcmp(logfmt, "jsonlog") != 0)
     972 UBC           0 :             ereport(ERROR,
     973                 :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     974                 :                      errmsg("log format \"%s\" is not supported", logfmt),
     975                 :                      errhint("The supported log formats are \"stderr\", \"csvlog\", and \"jsonlog\".")));
     976                 :     }
     977 ECB             : 
     978 CBC           6 :     fd = AllocateFile(LOG_METAINFO_DATAFILE, "r");
     979 GBC           6 :     if (fd == NULL)
     980 EUB             :     {
     981 UIC           0 :         if (errno != ENOENT)
     982 LBC           0 :             ereport(ERROR,
     983 ECB             :                     (errcode_for_file_access(),
     984                 :                      errmsg("could not read file \"%s\": %m",
     985                 :                             LOG_METAINFO_DATAFILE)));
     986 UBC           0 :         PG_RETURN_NULL();
     987                 :     }
     988                 : 
     989                 : #ifdef WIN32
     990                 :     /* syslogger.c writes CRLF line endings on Windows */
     991                 :     _setmode(_fileno(fd), _O_TEXT);
     992 ECB             : #endif
     993                 : 
     994                 :     /*
     995                 :      * Read the file to gather current log filename(s) registered by the
     996                 :      * syslogger.
     997                 :      */
     998 GIC          12 :     while (fgets(lbuffer, sizeof(lbuffer), fd) != NULL)
     999                 :     {
    1000                 :         char       *log_format;
    1001                 :         char       *log_filepath;
    1002 ECB             :         char       *nlpos;
    1003                 : 
    1004                 :         /* Extract log format and log file path from the line. */
    1005 GIC          12 :         log_format = lbuffer;
    1006 CBC          12 :         log_filepath = strchr(lbuffer, ' ');
    1007 GIC          12 :         if (log_filepath == NULL)
    1008                 :         {
    1009                 :             /* Uh oh.  No space found, so file content is corrupted. */
    1010 UIC           0 :             elog(ERROR,
    1011                 :                  "missing space character in \"%s\"", LOG_METAINFO_DATAFILE);
    1012                 :             break;
    1013                 :         }
    1014 ECB             : 
    1015 CBC          12 :         *log_filepath = '\0';
    1016              12 :         log_filepath++;
    1017 GIC          12 :         nlpos = strchr(log_filepath, '\n');
    1018 CBC          12 :         if (nlpos == NULL)
    1019                 :         {
    1020                 :             /* Uh oh.  No newline found, so file content is corrupted. */
    1021 LBC           0 :             elog(ERROR,
    1022                 :                  "missing newline character in \"%s\"", LOG_METAINFO_DATAFILE);
    1023                 :             break;
    1024 ECB             :         }
    1025 CBC          12 :         *nlpos = '\0';
    1026                 : 
    1027 GIC          12 :         if (logfmt == NULL || strcmp(logfmt, log_format) == 0)
    1028                 :         {
    1029               6 :             FreeFile(fd);
    1030 CBC           6 :             PG_RETURN_TEXT_P(cstring_to_text(log_filepath));
    1031 ECB             :         }
    1032                 :     }
    1033                 : 
    1034                 :     /* Close the current log filename file. */
    1035 UIC           0 :     FreeFile(fd);
    1036                 : 
    1037 LBC           0 :     PG_RETURN_NULL();
    1038                 : }
    1039                 : 
    1040                 : /*
    1041                 :  * Report current log file used by log collector (1 argument version)
    1042                 :  *
    1043 ECB             :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
    1044                 :  * which checks that all built-in functions that share the implementing C
    1045                 :  * function take the same number of arguments
    1046                 :  */
    1047                 : Datum
    1048 CBC           6 : pg_current_logfile_1arg(PG_FUNCTION_ARGS)
    1049 ECB             : {
    1050 CBC           6 :     return pg_current_logfile(fcinfo);
    1051 ECB             : }
    1052                 : 
    1053                 : /*
    1054                 :  * SQL wrapper around RelationGetReplicaIndex().
    1055                 :  */
    1056                 : Datum
    1057 GIC         310 : pg_get_replica_identity_index(PG_FUNCTION_ARGS)
    1058                 : {
    1059 CBC         310 :     Oid         reloid = PG_GETARG_OID(0);
    1060 ECB             :     Oid         idxoid;
    1061                 :     Relation    rel;
    1062                 : 
    1063 GIC         310 :     rel = table_open(reloid, AccessShareLock);
    1064 CBC         310 :     idxoid = RelationGetReplicaIndex(rel);
    1065 GIC         310 :     table_close(rel, AccessShareLock);
    1066                 : 
    1067             310 :     if (OidIsValid(idxoid))
    1068 CBC         184 :         PG_RETURN_OID(idxoid);
    1069                 :     else
    1070 GIC         126 :         PG_RETURN_NULL();
    1071                 : }
    1072                 : 
    1073                 : /*
    1074                 :  * Transition function for the ANY_VALUE aggregate
    1075                 :  */
    1076                 : Datum
    1077 GNC           9 : any_value_transfn(PG_FUNCTION_ARGS)
    1078                 : {
    1079               9 :     PG_RETURN_DATUM(PG_GETARG_DATUM(0));
    1080                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a