LCOV - code coverage report
Current view: top level - src/backend/utils/adt - misc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 78.5 % 354 278
Test Date: 2026-07-22 12:15:41 Functions: 92.3 % 26 24
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 63.0 % 211 133

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

Generated by: LCOV version 2.0-1