LCOV - code coverage report
Current view: top level - src/backend/utils/adt - misc.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 136 312 43.6 %
Date: 2017-09-29 13:40:31 Functions: 12 24 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * misc.c
       4             :  *
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, 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 <signal.h>
      19             : #include <dirent.h>
      20             : #include <math.h>
      21             : #include <unistd.h>
      22             : 
      23             : #include "access/sysattr.h"
      24             : #include "catalog/pg_authid.h"
      25             : #include "catalog/catalog.h"
      26             : #include "catalog/pg_tablespace.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "commands/dbcommands.h"
      29             : #include "common/keywords.h"
      30             : #include "funcapi.h"
      31             : #include "miscadmin.h"
      32             : #include "pgstat.h"
      33             : #include "parser/scansup.h"
      34             : #include "postmaster/syslogger.h"
      35             : #include "rewrite/rewriteHandler.h"
      36             : #include "storage/fd.h"
      37             : #include "storage/pmsignal.h"
      38             : #include "storage/proc.h"
      39             : #include "storage/procarray.h"
      40             : #include "utils/lsyscache.h"
      41             : #include "utils/ruleutils.h"
      42             : #include "tcop/tcopprot.h"
      43             : #include "utils/acl.h"
      44             : #include "utils/builtins.h"
      45             : #include "utils/timestamp.h"
      46             : 
      47             : 
      48             : /*
      49             :  * Common subroutine for num_nulls() and num_nonnulls().
      50             :  * Returns TRUE if successful, FALSE if function should return NULL.
      51             :  * If successful, total argument count and number of nulls are
      52             :  * returned into *nargs and *nulls.
      53             :  */
      54             : static bool
      55          20 : count_nulls(FunctionCallInfo fcinfo,
      56             :             int32 *nargs, int32 *nulls)
      57             : {
      58          20 :     int32       count = 0;
      59             :     int         i;
      60             : 
      61             :     /* Did we get a VARIADIC array argument, or separate arguments? */
      62          20 :     if (get_fn_expr_variadic(fcinfo->flinfo))
      63             :     {
      64             :         ArrayType  *arr;
      65             :         int         ndims,
      66             :                     nitems,
      67             :                    *dims;
      68             :         bits8      *bitmap;
      69             : 
      70          10 :         Assert(PG_NARGS() == 1);
      71             : 
      72             :         /*
      73             :          * If we get a null as VARIADIC array argument, we can't say anything
      74             :          * useful about the number of elements, so return NULL.  This behavior
      75             :          * is consistent with other variadic functions - see concat_internal.
      76             :          */
      77          10 :         if (PG_ARGISNULL(0))
      78           2 :             return false;
      79             : 
      80             :         /*
      81             :          * Non-null argument had better be an array.  We assume that any call
      82             :          * context that could let get_fn_expr_variadic return true will have
      83             :          * checked that a VARIADIC-labeled parameter actually is an array.  So
      84             :          * it should be okay to just Assert that it's an array rather than
      85             :          * doing a full-fledged error check.
      86             :          */
      87           8 :         Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, 0))));
      88             : 
      89             :         /* OK, safe to fetch the array value */
      90           8 :         arr = PG_GETARG_ARRAYTYPE_P(0);
      91             : 
      92             :         /* Count the array elements */
      93           8 :         ndims = ARR_NDIM(arr);
      94           8 :         dims = ARR_DIMS(arr);
      95           8 :         nitems = ArrayGetNItems(ndims, dims);
      96             : 
      97             :         /* Count those that are NULL */
      98           8 :         bitmap = ARR_NULLBITMAP(arr);
      99           8 :         if (bitmap)
     100             :         {
     101           4 :             int         bitmask = 1;
     102             : 
     103         212 :             for (i = 0; i < nitems; i++)
     104             :             {
     105         208 :                 if ((*bitmap & bitmask) == 0)
     106           4 :                     count++;
     107             : 
     108         208 :                 bitmask <<= 1;
     109         208 :                 if (bitmask == 0x100)
     110             :                 {
     111          24 :                     bitmap++;
     112          24 :                     bitmask = 1;
     113             :                 }
     114             :             }
     115             :         }
     116             : 
     117           8 :         *nargs = nitems;
     118           8 :         *nulls = count;
     119             :     }
     120             :     else
     121             :     {
     122             :         /* Separate arguments, so just count 'em */
     123          34 :         for (i = 0; i < PG_NARGS(); i++)
     124             :         {
     125          24 :             if (PG_ARGISNULL(i))
     126          14 :                 count++;
     127             :         }
     128             : 
     129          10 :         *nargs = PG_NARGS();
     130          10 :         *nulls = count;
     131             :     }
     132             : 
     133          18 :     return true;
     134             : }
     135             : 
     136             : /*
     137             :  * num_nulls()
     138             :  *  Count the number of NULL arguments
     139             :  */
     140             : Datum
     141          10 : pg_num_nulls(PG_FUNCTION_ARGS)
     142             : {
     143             :     int32       nargs,
     144             :                 nulls;
     145             : 
     146          10 :     if (!count_nulls(fcinfo, &nargs, &nulls))
     147           1 :         PG_RETURN_NULL();
     148             : 
     149           9 :     PG_RETURN_INT32(nulls);
     150             : }
     151             : 
     152             : /*
     153             :  * num_nonnulls()
     154             :  *  Count the number of non-NULL arguments
     155             :  */
     156             : Datum
     157          10 : pg_num_nonnulls(PG_FUNCTION_ARGS)
     158             : {
     159             :     int32       nargs,
     160             :                 nulls;
     161             : 
     162          10 :     if (!count_nulls(fcinfo, &nargs, &nulls))
     163           1 :         PG_RETURN_NULL();
     164             : 
     165           9 :     PG_RETURN_INT32(nargs - nulls);
     166             : }
     167             : 
     168             : 
     169             : /*
     170             :  * current_database()
     171             :  *  Expose the current database to the user
     172             :  */
     173             : Datum
     174         124 : current_database(PG_FUNCTION_ARGS)
     175             : {
     176             :     Name        db;
     177             : 
     178         124 :     db = (Name) palloc(NAMEDATALEN);
     179             : 
     180         124 :     namestrcpy(db, get_database_name(MyDatabaseId));
     181         124 :     PG_RETURN_NAME(db);
     182             : }
     183             : 
     184             : 
     185             : /*
     186             :  * current_query()
     187             :  *  Expose the current query to the user (useful in stored procedures)
     188             :  *  We might want to use ActivePortal->sourceText someday.
     189             :  */
     190             : Datum
     191           0 : current_query(PG_FUNCTION_ARGS)
     192             : {
     193             :     /* there is no easy way to access the more concise 'query_string' */
     194           0 :     if (debug_query_string)
     195           0 :         PG_RETURN_TEXT_P(cstring_to_text(debug_query_string));
     196             :     else
     197           0 :         PG_RETURN_NULL();
     198             : }
     199             : 
     200             : /*
     201             :  * Send a signal to another backend.
     202             :  *
     203             :  * The signal is delivered if the user is either a superuser or the same
     204             :  * role as the backend being signaled. For "dangerous" signals, an explicit
     205             :  * check for superuser needs to be done prior to calling this function.
     206             :  *
     207             :  * Returns 0 on success, 1 on general failure, 2 on normal permission error
     208             :  * and 3 if the caller needs to be a superuser.
     209             :  *
     210             :  * In the event of a general failure (return code 1), a warning message will
     211             :  * be emitted. For permission errors, doing that is the responsibility of
     212             :  * the caller.
     213             :  */
     214             : #define SIGNAL_BACKEND_SUCCESS 0
     215             : #define SIGNAL_BACKEND_ERROR 1
     216             : #define SIGNAL_BACKEND_NOPERMISSION 2
     217             : #define SIGNAL_BACKEND_NOSUPERUSER 3
     218             : static int
     219           0 : pg_signal_backend(int pid, int sig)
     220             : {
     221           0 :     PGPROC     *proc = BackendPidGetProc(pid);
     222             : 
     223             :     /*
     224             :      * BackendPidGetProc returns NULL if the pid isn't valid; but by the time
     225             :      * we reach kill(), a process for which we get a valid proc here might
     226             :      * have terminated on its own.  There's no way to acquire a lock on an
     227             :      * arbitrary process to prevent that. But since so far all the callers of
     228             :      * this mechanism involve some request for ending the process anyway, that
     229             :      * it might end on its own first is not a problem.
     230             :      */
     231           0 :     if (proc == NULL)
     232             :     {
     233             :         /*
     234             :          * This is just a warning so a loop-through-resultset will not abort
     235             :          * if one backend terminated on its own during the run.
     236             :          */
     237           0 :         ereport(WARNING,
     238             :                 (errmsg("PID %d is not a PostgreSQL server process", pid)));
     239           0 :         return SIGNAL_BACKEND_ERROR;
     240             :     }
     241             : 
     242             :     /* Only allow superusers to signal superuser-owned backends. */
     243           0 :     if (superuser_arg(proc->roleId) && !superuser())
     244           0 :         return SIGNAL_BACKEND_NOSUPERUSER;
     245             : 
     246             :     /* Users can signal backends they have role membership in. */
     247           0 :     if (!has_privs_of_role(GetUserId(), proc->roleId) &&
     248           0 :         !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID))
     249           0 :         return SIGNAL_BACKEND_NOPERMISSION;
     250             : 
     251             :     /*
     252             :      * Can the process we just validated above end, followed by the pid being
     253             :      * recycled for a new process, before reaching here?  Then we'd be trying
     254             :      * to kill the wrong thing.  Seems near impossible when sequential pid
     255             :      * assignment and wraparound is used.  Perhaps it could happen on a system
     256             :      * where pid re-use is randomized.  That race condition possibility seems
     257             :      * too unlikely to worry about.
     258             :      */
     259             : 
     260             :     /* If we have setsid(), signal the backend's whole process group */
     261             : #ifdef HAVE_SETSID
     262           0 :     if (kill(-pid, sig))
     263             : #else
     264             :     if (kill(pid, sig))
     265             : #endif
     266             :     {
     267             :         /* Again, just a warning to allow loops */
     268           0 :         ereport(WARNING,
     269             :                 (errmsg("could not send signal to process %d: %m", pid)));
     270           0 :         return SIGNAL_BACKEND_ERROR;
     271             :     }
     272           0 :     return SIGNAL_BACKEND_SUCCESS;
     273             : }
     274             : 
     275             : /*
     276             :  * Signal to cancel a backend process.  This is allowed if you are a member of
     277             :  * the role whose process is being canceled.
     278             :  *
     279             :  * Note that only superusers can signal superuser-owned processes.
     280             :  */
     281             : Datum
     282           0 : pg_cancel_backend(PG_FUNCTION_ARGS)
     283             : {
     284           0 :     int         r = pg_signal_backend(PG_GETARG_INT32(0), SIGINT);
     285             : 
     286           0 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     287           0 :         ereport(ERROR,
     288             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     289             :                  (errmsg("must be a superuser to cancel superuser query"))));
     290             : 
     291           0 :     if (r == SIGNAL_BACKEND_NOPERMISSION)
     292           0 :         ereport(ERROR,
     293             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     294             :                  (errmsg("must be a member of the role whose query is being canceled or member of pg_signal_backend"))));
     295             : 
     296           0 :     PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     297             : }
     298             : 
     299             : /*
     300             :  * Signal to terminate a backend process.  This is allowed if you are a member
     301             :  * of the role whose process is being terminated.
     302             :  *
     303             :  * Note that only superusers can signal superuser-owned processes.
     304             :  */
     305             : Datum
     306           0 : pg_terminate_backend(PG_FUNCTION_ARGS)
     307             : {
     308           0 :     int         r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
     309             : 
     310           0 :     if (r == SIGNAL_BACKEND_NOSUPERUSER)
     311           0 :         ereport(ERROR,
     312             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     313             :                  (errmsg("must be a superuser to terminate superuser process"))));
     314             : 
     315           0 :     if (r == SIGNAL_BACKEND_NOPERMISSION)
     316           0 :         ereport(ERROR,
     317             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     318             :                  (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend"))));
     319             : 
     320           0 :     PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
     321             : }
     322             : 
     323             : /*
     324             :  * Signal to reload the database configuration
     325             :  *
     326             :  * Permission checking for this function is managed through the normal
     327             :  * GRANT system.
     328             :  */
     329             : Datum
     330           0 : pg_reload_conf(PG_FUNCTION_ARGS)
     331             : {
     332           0 :     if (kill(PostmasterPid, SIGHUP))
     333             :     {
     334           0 :         ereport(WARNING,
     335             :                 (errmsg("failed to send signal to postmaster: %m")));
     336           0 :         PG_RETURN_BOOL(false);
     337             :     }
     338             : 
     339           0 :     PG_RETURN_BOOL(true);
     340             : }
     341             : 
     342             : 
     343             : /*
     344             :  * Rotate log file
     345             :  *
     346             :  * Permission checking for this function is managed through the normal
     347             :  * GRANT system.
     348             :  */
     349             : Datum
     350           0 : pg_rotate_logfile(PG_FUNCTION_ARGS)
     351             : {
     352           0 :     if (!Logging_collector)
     353             :     {
     354           0 :         ereport(WARNING,
     355             :                 (errmsg("rotation not possible because log collection not active")));
     356           0 :         PG_RETURN_BOOL(false);
     357             :     }
     358             : 
     359           0 :     SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
     360           0 :     PG_RETURN_BOOL(true);
     361             : }
     362             : 
     363             : /* Function to find out which databases make use of a tablespace */
     364             : 
     365             : typedef struct
     366             : {
     367             :     char       *location;
     368             :     DIR        *dirdesc;
     369             : } ts_db_fctx;
     370             : 
     371             : Datum
     372           0 : pg_tablespace_databases(PG_FUNCTION_ARGS)
     373             : {
     374             :     FuncCallContext *funcctx;
     375             :     struct dirent *de;
     376             :     ts_db_fctx *fctx;
     377             : 
     378           0 :     if (SRF_IS_FIRSTCALL())
     379             :     {
     380             :         MemoryContext oldcontext;
     381           0 :         Oid         tablespaceOid = PG_GETARG_OID(0);
     382             : 
     383           0 :         funcctx = SRF_FIRSTCALL_INIT();
     384           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     385             : 
     386           0 :         fctx = palloc(sizeof(ts_db_fctx));
     387             : 
     388           0 :         if (tablespaceOid == GLOBALTABLESPACE_OID)
     389             :         {
     390           0 :             fctx->dirdesc = NULL;
     391           0 :             ereport(WARNING,
     392             :                     (errmsg("global tablespace never has databases")));
     393             :         }
     394             :         else
     395             :         {
     396           0 :             if (tablespaceOid == DEFAULTTABLESPACE_OID)
     397           0 :                 fctx->location = psprintf("base");
     398             :             else
     399           0 :                 fctx->location = psprintf("pg_tblspc/%u/%s", tablespaceOid,
     400             :                                           TABLESPACE_VERSION_DIRECTORY);
     401             : 
     402           0 :             fctx->dirdesc = AllocateDir(fctx->location);
     403             : 
     404           0 :             if (!fctx->dirdesc)
     405             :             {
     406             :                 /* the only expected error is ENOENT */
     407           0 :                 if (errno != ENOENT)
     408           0 :                     ereport(ERROR,
     409             :                             (errcode_for_file_access(),
     410             :                              errmsg("could not open directory \"%s\": %m",
     411             :                                     fctx->location)));
     412           0 :                 ereport(WARNING,
     413             :                         (errmsg("%u is not a tablespace OID", tablespaceOid)));
     414             :             }
     415             :         }
     416           0 :         funcctx->user_fctx = fctx;
     417           0 :         MemoryContextSwitchTo(oldcontext);
     418             :     }
     419             : 
     420           0 :     funcctx = SRF_PERCALL_SETUP();
     421           0 :     fctx = (ts_db_fctx *) funcctx->user_fctx;
     422             : 
     423           0 :     if (!fctx->dirdesc)          /* not a tablespace */
     424           0 :         SRF_RETURN_DONE(funcctx);
     425             : 
     426           0 :     while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
     427             :     {
     428             :         char       *subdir;
     429             :         DIR        *dirdesc;
     430           0 :         Oid         datOid = atooid(de->d_name);
     431             : 
     432             :         /* this test skips . and .., but is awfully weak */
     433           0 :         if (!datOid)
     434           0 :             continue;
     435             : 
     436             :         /* if database subdir is empty, don't report tablespace as used */
     437             : 
     438           0 :         subdir = psprintf("%s/%s", fctx->location, de->d_name);
     439           0 :         dirdesc = AllocateDir(subdir);
     440           0 :         while ((de = ReadDir(dirdesc, subdir)) != NULL)
     441             :         {
     442           0 :             if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0)
     443           0 :                 break;
     444             :         }
     445           0 :         FreeDir(dirdesc);
     446           0 :         pfree(subdir);
     447             : 
     448           0 :         if (!de)
     449           0 :             continue;           /* indeed, nothing in it */
     450             : 
     451           0 :         SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(datOid));
     452             :     }
     453             : 
     454           0 :     FreeDir(fctx->dirdesc);
     455           0 :     SRF_RETURN_DONE(funcctx);
     456             : }
     457             : 
     458             : 
     459             : /*
     460             :  * pg_tablespace_location - get location for a tablespace
     461             :  */
     462             : Datum
     463           0 : pg_tablespace_location(PG_FUNCTION_ARGS)
     464             : {
     465           0 :     Oid         tablespaceOid = PG_GETARG_OID(0);
     466             :     char        sourcepath[MAXPGPATH];
     467             :     char        targetpath[MAXPGPATH];
     468             :     int         rllen;
     469             : 
     470             :     /*
     471             :      * It's useful to apply this function to pg_class.reltablespace, wherein
     472             :      * zero means "the database's default tablespace".  So, rather than
     473             :      * throwing an error for zero, we choose to assume that's what is meant.
     474             :      */
     475           0 :     if (tablespaceOid == InvalidOid)
     476           0 :         tablespaceOid = MyDatabaseTableSpace;
     477             : 
     478             :     /*
     479             :      * Return empty string for the cluster's default tablespaces
     480             :      */
     481           0 :     if (tablespaceOid == DEFAULTTABLESPACE_OID ||
     482             :         tablespaceOid == GLOBALTABLESPACE_OID)
     483           0 :         PG_RETURN_TEXT_P(cstring_to_text(""));
     484             : 
     485             : #if defined(HAVE_READLINK) || defined(WIN32)
     486             : 
     487             :     /*
     488             :      * Find the location of the tablespace by reading the symbolic link that
     489             :      * is in pg_tblspc/<oid>.
     490             :      */
     491           0 :     snprintf(sourcepath, sizeof(sourcepath), "pg_tblspc/%u", tablespaceOid);
     492             : 
     493           0 :     rllen = readlink(sourcepath, targetpath, sizeof(targetpath));
     494           0 :     if (rllen < 0)
     495           0 :         ereport(ERROR,
     496             :                 (errcode_for_file_access(),
     497             :                  errmsg("could not read symbolic link \"%s\": %m",
     498             :                         sourcepath)));
     499           0 :     if (rllen >= sizeof(targetpath))
     500           0 :         ereport(ERROR,
     501             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     502             :                  errmsg("symbolic link \"%s\" target is too long",
     503             :                         sourcepath)));
     504           0 :     targetpath[rllen] = '\0';
     505             : 
     506           0 :     PG_RETURN_TEXT_P(cstring_to_text(targetpath));
     507             : #else
     508             :     ereport(ERROR,
     509             :             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     510             :              errmsg("tablespaces are not supported on this platform")));
     511             :     PG_RETURN_NULL();
     512             : #endif
     513             : }
     514             : 
     515             : /*
     516             :  * pg_sleep - delay for N seconds
     517             :  */
     518             : Datum
     519           8 : pg_sleep(PG_FUNCTION_ARGS)
     520             : {
     521           8 :     float8      secs = PG_GETARG_FLOAT8(0);
     522             :     float8      endtime;
     523             : 
     524             :     /*
     525             :      * We sleep using WaitLatch, to ensure that we'll wake up promptly if an
     526             :      * important signal (such as SIGALRM or SIGINT) arrives.  Because
     527             :      * WaitLatch's upper limit of delay is INT_MAX milliseconds, and the user
     528             :      * might ask for more than that, we sleep for at most 10 minutes and then
     529             :      * loop.
     530             :      *
     531             :      * By computing the intended stop time initially, we avoid accumulation of
     532             :      * extra delay across multiple sleeps.  This also ensures we won't delay
     533             :      * less than the specified time when WaitLatch is terminated early by a
     534             :      * non-query-canceling signal such as SIGHUP.
     535             :      */
     536             : #define GetNowFloat()   ((float8) GetCurrentTimestamp() / 1000000.0)
     537             : 
     538           8 :     endtime = GetNowFloat() + secs;
     539             : 
     540             :     for (;;)
     541             :     {
     542             :         float8      delay;
     543             :         long        delay_ms;
     544             : 
     545          16 :         CHECK_FOR_INTERRUPTS();
     546             : 
     547          16 :         delay = endtime - GetNowFloat();
     548          16 :         if (delay >= 600.0)
     549           0 :             delay_ms = 600000;
     550          16 :         else if (delay > 0.0)
     551           8 :             delay_ms = (long) ceil(delay * 1000.0);
     552             :         else
     553           8 :             break;
     554             : 
     555           8 :         (void) WaitLatch(MyLatch,
     556             :                          WL_LATCH_SET | WL_TIMEOUT,
     557             :                          delay_ms,
     558             :                          WAIT_EVENT_PG_SLEEP);
     559           8 :         ResetLatch(MyLatch);
     560           8 :     }
     561             : 
     562           8 :     PG_RETURN_VOID();
     563             : }
     564             : 
     565             : /* Function to return the list of grammar keywords */
     566             : Datum
     567           0 : pg_get_keywords(PG_FUNCTION_ARGS)
     568             : {
     569             :     FuncCallContext *funcctx;
     570             : 
     571           0 :     if (SRF_IS_FIRSTCALL())
     572             :     {
     573             :         MemoryContext oldcontext;
     574             :         TupleDesc   tupdesc;
     575             : 
     576           0 :         funcctx = SRF_FIRSTCALL_INIT();
     577           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     578             : 
     579           0 :         tupdesc = CreateTemplateTupleDesc(3, false);
     580           0 :         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word",
     581             :                            TEXTOID, -1, 0);
     582           0 :         TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode",
     583             :                            CHAROID, -1, 0);
     584           0 :         TupleDescInitEntry(tupdesc, (AttrNumber) 3, "catdesc",
     585             :                            TEXTOID, -1, 0);
     586             : 
     587           0 :         funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
     588             : 
     589           0 :         MemoryContextSwitchTo(oldcontext);
     590             :     }
     591             : 
     592           0 :     funcctx = SRF_PERCALL_SETUP();
     593             : 
     594           0 :     if (funcctx->call_cntr < NumScanKeywords)
     595             :     {
     596             :         char       *values[3];
     597             :         HeapTuple   tuple;
     598             : 
     599             :         /* cast-away-const is ugly but alternatives aren't much better */
     600           0 :         values[0] = (char *) ScanKeywords[funcctx->call_cntr].name;
     601             : 
     602           0 :         switch (ScanKeywords[funcctx->call_cntr].category)
     603             :         {
     604             :             case UNRESERVED_KEYWORD:
     605           0 :                 values[1] = "U";
     606           0 :                 values[2] = _("unreserved");
     607           0 :                 break;
     608             :             case COL_NAME_KEYWORD:
     609           0 :                 values[1] = "C";
     610           0 :                 values[2] = _("unreserved (cannot be function or type name)");
     611           0 :                 break;
     612             :             case TYPE_FUNC_NAME_KEYWORD:
     613           0 :                 values[1] = "T";
     614           0 :                 values[2] = _("reserved (can be function or type name)");
     615           0 :                 break;
     616             :             case RESERVED_KEYWORD:
     617           0 :                 values[1] = "R";
     618           0 :                 values[2] = _("reserved");
     619           0 :                 break;
     620             :             default:            /* shouldn't be possible */
     621           0 :                 values[1] = NULL;
     622           0 :                 values[2] = NULL;
     623           0 :                 break;
     624             :         }
     625             : 
     626           0 :         tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
     627             : 
     628           0 :         SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
     629             :     }
     630             : 
     631           0 :     SRF_RETURN_DONE(funcctx);
     632             : }
     633             : 
     634             : 
     635             : /*
     636             :  * Return the type of the argument.
     637             :  */
     638             : Datum
     639          15 : pg_typeof(PG_FUNCTION_ARGS)
     640             : {
     641          15 :     PG_RETURN_OID(get_fn_expr_argtype(fcinfo->flinfo, 0));
     642             : }
     643             : 
     644             : 
     645             : /*
     646             :  * Implementation of the COLLATE FOR expression; returns the collation
     647             :  * of the argument.
     648             :  */
     649             : Datum
     650           5 : pg_collation_for(PG_FUNCTION_ARGS)
     651             : {
     652             :     Oid         typeid;
     653             :     Oid         collid;
     654             : 
     655           5 :     typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     656           5 :     if (!typeid)
     657           0 :         PG_RETURN_NULL();
     658           5 :     if (!type_is_collatable(typeid) && typeid != UNKNOWNOID)
     659           1 :         ereport(ERROR,
     660             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     661             :                  errmsg("collations are not supported by type %s",
     662             :                         format_type_be(typeid))));
     663             : 
     664           4 :     collid = PG_GET_COLLATION();
     665           4 :     if (!collid)
     666           1 :         PG_RETURN_NULL();
     667           3 :     PG_RETURN_TEXT_P(cstring_to_text(generate_collation_name(collid)));
     668             : }
     669             : 
     670             : 
     671             : /*
     672             :  * pg_relation_is_updatable - determine which update events the specified
     673             :  * relation supports.
     674             :  *
     675             :  * This relies on relation_is_updatable() in rewriteHandler.c, which see
     676             :  * for additional information.
     677             :  */
     678             : Datum
     679         147 : pg_relation_is_updatable(PG_FUNCTION_ARGS)
     680             : {
     681         147 :     Oid         reloid = PG_GETARG_OID(0);
     682         147 :     bool        include_triggers = PG_GETARG_BOOL(1);
     683             : 
     684         147 :     PG_RETURN_INT32(relation_is_updatable(reloid, include_triggers, NULL));
     685             : }
     686             : 
     687             : /*
     688             :  * pg_column_is_updatable - determine whether a column is updatable
     689             :  *
     690             :  * This function encapsulates the decision about just what
     691             :  * information_schema.columns.is_updatable actually means.  It's not clear
     692             :  * whether deletability of the column's relation should be required, so
     693             :  * we want that decision in C code where we could change it without initdb.
     694             :  */
     695             : Datum
     696          95 : pg_column_is_updatable(PG_FUNCTION_ARGS)
     697             : {
     698          95 :     Oid         reloid = PG_GETARG_OID(0);
     699          95 :     AttrNumber  attnum = PG_GETARG_INT16(1);
     700          95 :     AttrNumber  col = attnum - FirstLowInvalidHeapAttributeNumber;
     701          95 :     bool        include_triggers = PG_GETARG_BOOL(2);
     702             :     int         events;
     703             : 
     704             :     /* System columns are never updatable */
     705          95 :     if (attnum <= 0)
     706           0 :         PG_RETURN_BOOL(false);
     707             : 
     708          95 :     events = relation_is_updatable(reloid, include_triggers,
     709             :                                    bms_make_singleton(col));
     710             : 
     711             :     /* We require both updatability and deletability of the relation */
     712             : #define REQ_EVENTS ((1 << CMD_UPDATE) | (1 << CMD_DELETE))
     713             : 
     714          95 :     PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS);
     715             : }
     716             : 
     717             : 
     718             : /*
     719             :  * Is character a valid identifier start?
     720             :  * Must match scan.l's {ident_start} character class.
     721             :  */
     722             : static bool
     723         367 : is_ident_start(unsigned char c)
     724             : {
     725             :     /* Underscores and ASCII letters are OK */
     726         367 :     if (c == '_')
     727           0 :         return true;
     728         367 :     if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
     729         342 :         return true;
     730             :     /* Any high-bit-set character is OK (might be part of a multibyte char) */
     731          25 :     if (IS_HIGHBIT_SET(c))
     732           0 :         return true;
     733          25 :     return false;
     734             : }
     735             : 
     736             : /*
     737             :  * Is character a valid identifier continuation?
     738             :  * Must match scan.l's {ident_cont} character class.
     739             :  */
     740             : static bool
     741         342 : is_ident_cont(unsigned char c)
     742             : {
     743             :     /* Can be digit or dollar sign ... */
     744         342 :     if ((c >= '0' && c <= '9') || c == '$')
     745           0 :         return true;
     746             :     /* ... or an identifier start character */
     747         342 :     return is_ident_start(c);
     748             : }
     749             : 
     750             : /*
     751             :  * parse_ident - parse a SQL qualified identifier into separate identifiers.
     752             :  * When strict mode is active (second parameter), then any chars after
     753             :  * the last identifier are disallowed.
     754             :  */
     755             : Datum
     756          19 : parse_ident(PG_FUNCTION_ARGS)
     757             : {
     758          19 :     text       *qualname = PG_GETARG_TEXT_PP(0);
     759          19 :     bool        strict = PG_GETARG_BOOL(1);
     760          19 :     char       *qualname_str = text_to_cstring(qualname);
     761          19 :     ArrayBuildState *astate = NULL;
     762             :     char       *nextp;
     763          19 :     bool        after_dot = false;
     764             : 
     765             :     /*
     766             :      * The code below scribbles on qualname_str in some cases, so we should
     767             :      * reconvert qualname if we need to show the original string in error
     768             :      * messages.
     769             :      */
     770          19 :     nextp = qualname_str;
     771             : 
     772             :     /* skip leading whitespace */
     773          43 :     while (scanner_isspace(*nextp))
     774           5 :         nextp++;
     775             : 
     776             :     for (;;)
     777             :     {
     778             :         char       *curname;
     779          35 :         bool        missing_ident = true;
     780             : 
     781          35 :         if (*nextp == '"')
     782             :         {
     783             :             char       *endp;
     784             : 
     785          10 :             curname = nextp + 1;
     786             :             for (;;)
     787             :             {
     788          10 :                 endp = strchr(nextp + 1, '"');
     789          10 :                 if (endp == NULL)
     790           0 :                     ereport(ERROR,
     791             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     792             :                              errmsg("string is not a valid identifier: \"%s\"",
     793             :                                     text_to_cstring(qualname)),
     794             :                              errdetail("String has unclosed double quotes.")));
     795          10 :                 if (endp[1] != '"')
     796          10 :                     break;
     797           0 :                 memmove(endp, endp + 1, strlen(endp));
     798           0 :                 nextp = endp;
     799           0 :             }
     800          10 :             nextp = endp + 1;
     801          10 :             *endp = '\0';
     802             : 
     803          10 :             if (endp - curname == 0)
     804           0 :                 ereport(ERROR,
     805             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     806             :                          errmsg("string is not a valid identifier: \"%s\"",
     807             :                                 text_to_cstring(qualname)),
     808             :                          errdetail("Quoted identifier must not be empty.")));
     809             : 
     810          10 :             astate = accumArrayResult(astate, CStringGetTextDatum(curname),
     811             :                                       false, TEXTOID, CurrentMemoryContext);
     812          10 :             missing_ident = false;
     813             :         }
     814          25 :         else if (is_ident_start((unsigned char) *nextp))
     815             :         {
     816             :             char       *downname;
     817             :             int         len;
     818             :             text       *part;
     819             : 
     820          17 :             curname = nextp++;
     821         359 :             while (is_ident_cont((unsigned char) *nextp))
     822         325 :                 nextp++;
     823             : 
     824          17 :             len = nextp - curname;
     825             : 
     826             :             /*
     827             :              * We don't implicitly truncate identifiers. This is useful for
     828             :              * allowing the user to check for specific parts of the identifier
     829             :              * being too long. It's easy enough for the user to get the
     830             :              * truncated names by casting our output to name[].
     831             :              */
     832          17 :             downname = downcase_identifier(curname, len, false, false);
     833          17 :             part = cstring_to_text_with_len(downname, len);
     834          17 :             astate = accumArrayResult(astate, PointerGetDatum(part), false,
     835             :                                       TEXTOID, CurrentMemoryContext);
     836          17 :             missing_ident = false;
     837             :         }
     838             : 
     839          35 :         if (missing_ident)
     840             :         {
     841             :             /* Different error messages based on where we failed. */
     842           8 :             if (*nextp == '.')
     843           3 :                 ereport(ERROR,
     844             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     845             :                          errmsg("string is not a valid identifier: \"%s\"",
     846             :                                 text_to_cstring(qualname)),
     847             :                          errdetail("No valid identifier before \".\".")));
     848           5 :             else if (after_dot)
     849           2 :                 ereport(ERROR,
     850             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     851             :                          errmsg("string is not a valid identifier: \"%s\"",
     852             :                                 text_to_cstring(qualname)),
     853             :                          errdetail("No valid identifier after \".\".")));
     854             :             else
     855           3 :                 ereport(ERROR,
     856             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     857             :                          errmsg("string is not a valid identifier: \"%s\"",
     858             :                                 text_to_cstring(qualname))));
     859             :         }
     860             : 
     861          61 :         while (scanner_isspace(*nextp))
     862           7 :             nextp++;
     863             : 
     864          27 :         if (*nextp == '.')
     865             :         {
     866          16 :             after_dot = true;
     867          16 :             nextp++;
     868          37 :             while (scanner_isspace(*nextp))
     869           5 :                 nextp++;
     870             :         }
     871          11 :         else if (*nextp == '\0')
     872             :         {
     873           6 :             break;
     874             :         }
     875             :         else
     876             :         {
     877           5 :             if (strict)
     878           4 :                 ereport(ERROR,
     879             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     880             :                          errmsg("string is not a valid identifier: \"%s\"",
     881             :                                 text_to_cstring(qualname))));
     882           1 :             break;
     883             :         }
     884          16 :     }
     885             : 
     886           7 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
     887             : }
     888             : 
     889             : /*
     890             :  * pg_current_logfile
     891             :  *
     892             :  * Report current log file used by log collector by scanning current_logfiles.
     893             :  */
     894             : Datum
     895           0 : pg_current_logfile(PG_FUNCTION_ARGS)
     896             : {
     897             :     FILE       *fd;
     898             :     char        lbuffer[MAXPGPATH];
     899             :     char       *logfmt;
     900             :     char       *log_filepath;
     901           0 :     char       *log_format = lbuffer;
     902             :     char       *nlpos;
     903             : 
     904             :     /* The log format parameter is optional */
     905           0 :     if (PG_NARGS() == 0 || PG_ARGISNULL(0))
     906           0 :         logfmt = NULL;
     907             :     else
     908             :     {
     909           0 :         logfmt = text_to_cstring(PG_GETARG_TEXT_PP(0));
     910             : 
     911           0 :         if (strcmp(logfmt, "stderr") != 0 && strcmp(logfmt, "csvlog") != 0)
     912           0 :             ereport(ERROR,
     913             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     914             :                      errmsg("log format \"%s\" is not supported", logfmt),
     915             :                      errhint("The supported log formats are \"stderr\" and \"csvlog\".")));
     916             :     }
     917             : 
     918           0 :     fd = AllocateFile(LOG_METAINFO_DATAFILE, "r");
     919           0 :     if (fd == NULL)
     920             :     {
     921           0 :         if (errno != ENOENT)
     922           0 :             ereport(ERROR,
     923             :                     (errcode_for_file_access(),
     924             :                      errmsg("could not read file \"%s\": %m",
     925             :                             LOG_METAINFO_DATAFILE)));
     926           0 :         PG_RETURN_NULL();
     927             :     }
     928             : 
     929             :     /*
     930             :      * Read the file to gather current log filename(s) registered by the
     931             :      * syslogger.
     932             :      */
     933           0 :     while (fgets(lbuffer, sizeof(lbuffer), fd) != NULL)
     934             :     {
     935             :         /*
     936             :          * Extract log format and log file path from the line; lbuffer ==
     937             :          * log_format, they share storage.
     938             :          */
     939           0 :         log_filepath = strchr(lbuffer, ' ');
     940           0 :         if (log_filepath == NULL)
     941             :         {
     942             :             /* Uh oh.  No space found, so file content is corrupted. */
     943           0 :             elog(ERROR,
     944             :                  "missing space character in \"%s\"", LOG_METAINFO_DATAFILE);
     945             :             break;
     946             :         }
     947             : 
     948           0 :         *log_filepath = '\0';
     949           0 :         log_filepath++;
     950           0 :         nlpos = strchr(log_filepath, '\n');
     951           0 :         if (nlpos == NULL)
     952             :         {
     953             :             /* Uh oh.  No newline found, so file content is corrupted. */
     954           0 :             elog(ERROR,
     955             :                  "missing newline character in \"%s\"", LOG_METAINFO_DATAFILE);
     956             :             break;
     957             :         }
     958           0 :         *nlpos = '\0';
     959             : 
     960           0 :         if (logfmt == NULL || strcmp(logfmt, log_format) == 0)
     961             :         {
     962           0 :             FreeFile(fd);
     963           0 :             PG_RETURN_TEXT_P(cstring_to_text(log_filepath));
     964             :         }
     965             :     }
     966             : 
     967             :     /* Close the current log filename file. */
     968           0 :     FreeFile(fd);
     969             : 
     970           0 :     PG_RETURN_NULL();
     971             : }
     972             : 
     973             : /*
     974             :  * Report current log file used by log collector (1 argument version)
     975             :  *
     976             :  * note: this wrapper is necessary to pass the sanity check in opr_sanity,
     977             :  * which checks that all built-in functions that share the implementing C
     978             :  * function take the same number of arguments
     979             :  */
     980             : Datum
     981           0 : pg_current_logfile_1arg(PG_FUNCTION_ARGS)
     982             : {
     983           0 :     return pg_current_logfile(fcinfo);
     984             : }
     985             : 
     986             : /*
     987             :  * SQL wrapper around RelationGetReplicaIndex().
     988             :  */
     989             : Datum
     990           0 : pg_get_replica_identity_index(PG_FUNCTION_ARGS)
     991             : {
     992           0 :     Oid         reloid = PG_GETARG_OID(0);
     993             :     Oid         idxoid;
     994             :     Relation    rel;
     995             : 
     996           0 :     rel = heap_open(reloid, AccessShareLock);
     997           0 :     idxoid = RelationGetReplicaIndex(rel);
     998           0 :     heap_close(rel, AccessShareLock);
     999             : 
    1000           0 :     if (OidIsValid(idxoid))
    1001           0 :         PG_RETURN_OID(idxoid);
    1002             :     else
    1003           0 :         PG_RETURN_NULL();
    1004             : }

Generated by: LCOV version 1.11