LCOV - code coverage report
Current view: top level - src/backend/utils/adt - misc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 78.5 % 354 278
Test Date: 2026-04-07 14:16:30 Functions: 92.3 % 26 24
Legend: Lines:     hit not hit

            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         7370 : current_database(PG_FUNCTION_ARGS)
     212              : {
     213              :     Name        db;
     214              : 
     215         7370 :     db = (Name) palloc(NAMEDATALEN);
     216              : 
     217         7370 :     namestrcpy(db, get_database_name(MyDatabaseId));
     218         7370 :     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          165 : pg_tablespace_location(PG_FUNCTION_ARGS)
     318              : {
     319          165 :     Oid         tablespaceOid = PG_GETARG_OID(0);
     320              :     char       *tablespaceLoc;
     321              : 
     322              :     /* Get LOCATION string from its OID */
     323          165 :     tablespaceLoc = get_tablespace_location(tablespaceOid);
     324              : 
     325          165 :     PG_RETURN_TEXT_P(cstring_to_text(tablespaceLoc));
     326              : }
     327              : 
     328              : /*
     329              :  * pg_sleep - delay for N seconds
     330              :  */
     331              : Datum
     332          129 : pg_sleep(PG_FUNCTION_ARGS)
     333              : {
     334          129 :     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          129 :     if (isnan(secs) || secs <= 0.0)
     345            0 :         PG_RETURN_VOID();
     346          129 :     secs *= USECS_PER_SEC;      /* we assume overflow will produce +Inf */
     347          129 :     secs = ceil(secs);          /* round up any fractional microsecond */
     348          129 :     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          129 :     endtime = GetCurrentTimestamp() + usecs;
     363              : 
     364              :     for (;;)
     365          138 :     {
     366              :         TimestampTz delay;
     367              :         long        delay_ms;
     368              : 
     369          267 :         CHECK_FOR_INTERRUPTS();
     370              : 
     371          254 :         delay = endtime - GetCurrentTimestamp();
     372          254 :         if (delay >= 600 * USECS_PER_SEC)
     373            0 :             delay_ms = 600000;
     374          254 :         else if (delay > 0)
     375          138 :             delay_ms = (long) ((delay + 999) / 1000);
     376              :         else
     377          116 :             break;
     378              : 
     379          138 :         (void) WaitLatch(MyLatch,
     380              :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
     381              :                          delay_ms,
     382              :                          WAIT_EVENT_PG_SLEEP);
     383          138 :         ResetLatch(MyLatch);
     384              :     }
     385              : 
     386          116 :     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          934 : pg_typeof(PG_FUNCTION_ARGS)
     538              : {
     539          934 :     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          425 : pg_get_replica_identity_index(PG_FUNCTION_ARGS)
    1075              : {
    1076          425 :     Oid         reloid = PG_GETARG_OID(0);
    1077              :     Oid         idxoid;
    1078              :     Relation    rel;
    1079              : 
    1080          425 :     rel = table_open(reloid, AccessShareLock);
    1081          425 :     idxoid = RelationGetReplicaIndex(rel);
    1082          425 :     table_close(rel, AccessShareLock);
    1083              : 
    1084          425 :     if (OidIsValid(idxoid))
    1085          214 :         PG_RETURN_OID(idxoid);
    1086              :     else
    1087          211 :         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