Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/misc.c
Line
Count
Source
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
count_nulls(FunctionCallInfo fcinfo,
79
      int32 *nargs, int32 *nulls)
80
0
{
81
0
  int32   count = 0;
82
0
  int     i;
83
84
  /* Did we get a VARIADIC array argument, or separate arguments? */
85
0
  if (get_fn_expr_variadic(fcinfo->flinfo))
86
0
  {
87
0
    ArrayType  *arr;
88
0
    int     ndims,
89
0
          nitems,
90
0
           *dims;
91
0
    uint8    *bitmap;
92
93
0
    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
0
    if (PG_ARGISNULL(0))
101
0
      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
0
    Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, 0))));
111
112
    /* OK, safe to fetch the array value */
113
0
    arr = PG_GETARG_ARRAYTYPE_P(0);
114
115
    /* Count the array elements */
116
0
    ndims = ARR_NDIM(arr);
117
0
    dims = ARR_DIMS(arr);
118
0
    nitems = ArrayGetNItems(ndims, dims);
119
120
    /* Count those that are NULL */
121
0
    bitmap = ARR_NULLBITMAP(arr);
122
0
    if (bitmap)
123
0
    {
124
0
      int     bitmask = 1;
125
126
0
      for (i = 0; i < nitems; i++)
127
0
      {
128
0
        if ((*bitmap & bitmask) == 0)
129
0
          count++;
130
131
0
        bitmask <<= 1;
132
0
        if (bitmask == 0x100)
133
0
        {
134
0
          bitmap++;
135
0
          bitmask = 1;
136
0
        }
137
0
      }
138
0
    }
139
140
0
    *nargs = nitems;
141
0
    *nulls = count;
142
0
  }
143
0
  else
144
0
  {
145
    /* Separate arguments, so just count 'em */
146
0
    for (i = 0; i < PG_NARGS(); i++)
147
0
    {
148
0
      if (PG_ARGISNULL(i))
149
0
        count++;
150
0
    }
151
152
0
    *nargs = PG_NARGS();
153
0
    *nulls = count;
154
0
  }
155
156
0
  return true;
157
0
}
158
159
/*
160
 * num_nulls()
161
 *  Count the number of NULL arguments
162
 */
163
Datum
164
pg_num_nulls(PG_FUNCTION_ARGS)
165
0
{
166
0
  int32   nargs,
167
0
        nulls;
168
169
0
  if (!count_nulls(fcinfo, &nargs, &nulls))
170
0
    PG_RETURN_NULL();
171
172
0
  PG_RETURN_INT32(nulls);
173
0
}
174
175
/*
176
 * num_nonnulls()
177
 *  Count the number of non-NULL arguments
178
 */
179
Datum
180
pg_num_nonnulls(PG_FUNCTION_ARGS)
181
0
{
182
0
  int32   nargs,
183
0
        nulls;
184
185
0
  if (!count_nulls(fcinfo, &nargs, &nulls))
186
0
    PG_RETURN_NULL();
187
188
0
  PG_RETURN_INT32(nargs - nulls);
189
0
}
190
191
/*
192
 * error_on_null()
193
 *  Check if the input is the NULL value
194
 */
195
Datum
196
pg_error_on_null(PG_FUNCTION_ARGS)
197
0
{
198
0
  if (PG_ARGISNULL(0))
199
0
    ereport(ERROR,
200
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
201
0
         errmsg("null value not allowed")));
202
203
0
  PG_RETURN_DATUM(PG_GETARG_DATUM(0));
204
0
}
205
206
/*
207
 * current_database()
208
 *  Expose the current database to the user
209
 */
210
Datum
211
current_database(PG_FUNCTION_ARGS)
212
0
{
213
0
  Name    db;
214
215
0
  db = (Name) palloc(NAMEDATALEN);
216
217
0
  namestrcpy(db, get_database_name(MyDatabaseId));
218
0
  PG_RETURN_NAME(db);
219
0
}
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
current_query(PG_FUNCTION_ARGS)
229
0
{
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
0
  else
234
0
    PG_RETURN_NULL();
235
0
}
236
237
/* Function to find out which databases make use of a tablespace */
238
239
Datum
240
pg_tablespace_databases(PG_FUNCTION_ARGS)
241
0
{
242
0
  Oid     tablespaceOid = PG_GETARG_OID(0);
243
0
  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
244
0
  char     *location;
245
0
  DIR      *dirdesc;
246
0
  struct dirent *de;
247
248
0
  InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
249
250
0
  if (tablespaceOid == GLOBALTABLESPACE_OID)
251
0
  {
252
0
    ereport(WARNING,
253
0
        (errmsg("global tablespace never has databases")));
254
    /* return empty tuplestore */
255
0
    return (Datum) 0;
256
0
  }
257
258
0
  if (tablespaceOid == DEFAULTTABLESPACE_OID)
259
0
    location = "base";
260
0
  else
261
0
    location = psprintf("%s/%u/%s", PG_TBLSPC_DIR, tablespaceOid,
262
0
              TABLESPACE_VERSION_DIRECTORY);
263
264
0
  dirdesc = AllocateDir(location);
265
266
0
  if (!dirdesc)
267
0
  {
268
    /* the only expected error is ENOENT */
269
0
    if (errno != ENOENT)
270
0
      ereport(ERROR,
271
0
          (errcode_for_file_access(),
272
0
           errmsg("could not open directory \"%s\": %m",
273
0
              location)));
274
0
    ereport(WARNING,
275
0
        (errmsg("%u is not a tablespace OID", tablespaceOid)));
276
    /* return empty tuplestore */
277
0
    return (Datum) 0;
278
0
  }
279
280
0
  while ((de = ReadDir(dirdesc, location)) != NULL)
281
0
  {
282
0
    Oid     datOid = atooid(de->d_name);
283
0
    char     *subdir;
284
0
    bool    isempty;
285
0
    Datum   values[1];
286
0
    bool    nulls[1];
287
288
    /* this test skips . and .., but is awfully weak */
289
0
    if (!datOid)
290
0
      continue;
291
292
    /* if database subdir is empty, don't report tablespace as used */
293
294
0
    subdir = psprintf("%s/%s", location, de->d_name);
295
0
    isempty = directory_is_empty(subdir);
296
0
    pfree(subdir);
297
298
0
    if (isempty)
299
0
      continue;     /* indeed, nothing in it */
300
301
0
    values[0] = ObjectIdGetDatum(datOid);
302
0
    nulls[0] = false;
303
304
0
    tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
305
0
               values, nulls);
306
0
  }
307
308
0
  FreeDir(dirdesc);
309
0
  return (Datum) 0;
310
0
}
311
312
313
/*
314
 * pg_tablespace_location - get location for a tablespace
315
 */
316
Datum
317
pg_tablespace_location(PG_FUNCTION_ARGS)
318
0
{
319
0
  Oid     tablespaceOid = PG_GETARG_OID(0);
320
0
  char     *tablespaceLoc;
321
322
  /* Get LOCATION string from its OID */
323
0
  tablespaceLoc = get_tablespace_location(tablespaceOid);
324
325
0
  PG_RETURN_TEXT_P(cstring_to_text(tablespaceLoc));
326
0
}
327
328
/*
329
 * pg_sleep - delay for N seconds
330
 */
331
Datum
332
pg_sleep(PG_FUNCTION_ARGS)
333
0
{
334
0
  float8    secs = PG_GETARG_FLOAT8(0);
335
0
  int64   usecs;
336
0
  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
0
  if (isnan(secs) || secs <= 0.0)
345
0
    PG_RETURN_VOID();
346
0
  secs *= USECS_PER_SEC;   /* we assume overflow will produce +Inf */
347
0
  secs = ceil(secs);      /* round up any fractional microsecond */
348
0
  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
0
  endtime = GetCurrentTimestamp() + usecs;
363
364
0
  for (;;)
365
0
  {
366
0
    TimestampTz delay;
367
0
    long    delay_ms;
368
369
0
    CHECK_FOR_INTERRUPTS();
370
371
0
    delay = endtime - GetCurrentTimestamp();
372
0
    if (delay >= 600 * USECS_PER_SEC)
373
0
      delay_ms = 600000;
374
0
    else if (delay > 0)
375
0
      delay_ms = (long) ((delay + 999) / 1000);
376
0
    else
377
0
      break;
378
379
0
    (void) WaitLatch(MyLatch,
380
0
             WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
381
0
             delay_ms,
382
0
             WAIT_EVENT_PG_SLEEP);
383
0
    ResetLatch(MyLatch);
384
0
  }
385
386
0
  PG_RETURN_VOID();
387
0
}
388
389
/* Function to return the list of grammar keywords */
390
Datum
391
pg_get_keywords(PG_FUNCTION_ARGS)
392
0
{
393
0
  FuncCallContext *funcctx;
394
395
0
  if (SRF_IS_FIRSTCALL())
396
0
  {
397
0
    MemoryContext oldcontext;
398
0
    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
0
  }
410
411
0
  funcctx = SRF_PERCALL_SETUP();
412
413
0
  if (funcctx->call_cntr < ScanKeywords.num_keywords)
414
0
  {
415
0
    char     *values[5];
416
0
    HeapTuple tuple;
417
418
    /* cast-away-const is ugly but alternatives aren't much better */
419
0
    values[0] = unconstify(char *,
420
0
                 GetScanKeyword(funcctx->call_cntr,
421
0
                        &ScanKeywords));
422
423
0
    switch (ScanKeywordCategories[funcctx->call_cntr])
424
0
    {
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
0
    }
446
447
0
    if (ScanKeywordBareLabel[funcctx->call_cntr])
448
0
    {
449
0
      values[2] = "true";
450
0
      values[4] = _("can be bare label");
451
0
    }
452
0
    else
453
0
    {
454
0
      values[2] = "false";
455
0
      values[4] = _("requires AS");
456
0
    }
457
458
0
    tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
459
460
0
    SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
461
0
  }
462
463
0
  SRF_RETURN_DONE(funcctx);
464
0
}
465
466
467
/* Function to return the list of catalog foreign key relationships */
468
Datum
469
pg_get_catalog_foreign_keys(PG_FUNCTION_ARGS)
470
0
{
471
0
  FuncCallContext *funcctx;
472
0
  FmgrInfo   *arrayinp;
473
474
0
  if (SRF_IS_FIRSTCALL())
475
0
  {
476
0
    MemoryContext oldcontext;
477
0
    TupleDesc tupdesc;
478
479
0
    funcctx = SRF_FIRSTCALL_INIT();
480
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
481
482
0
    if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
483
0
      elog(ERROR, "return type must be a row type");
484
0
    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
0
    arrayinp = palloc_object(FmgrInfo);
493
0
    fmgr_info(F_ARRAY_IN, arrayinp);
494
0
    funcctx->user_fctx = arrayinp;
495
496
0
    MemoryContextSwitchTo(oldcontext);
497
0
  }
498
499
0
  funcctx = SRF_PERCALL_SETUP();
500
0
  arrayinp = (FmgrInfo *) funcctx->user_fctx;
501
502
0
  if (funcctx->call_cntr < lengthof(sys_fk_relationships))
503
0
  {
504
0
    const SysFKRelationship *fkrel = &sys_fk_relationships[funcctx->call_cntr];
505
0
    Datum   values[6];
506
0
    bool    nulls[6];
507
0
    HeapTuple tuple;
508
509
0
    memset(nulls, false, sizeof(nulls));
510
511
0
    values[0] = ObjectIdGetDatum(fkrel->fk_table);
512
0
    values[1] = FunctionCall3(arrayinp,
513
0
                  CStringGetDatum(fkrel->fk_columns),
514
0
                  ObjectIdGetDatum(TEXTOID),
515
0
                  Int32GetDatum(-1));
516
0
    values[2] = ObjectIdGetDatum(fkrel->pk_table);
517
0
    values[3] = FunctionCall3(arrayinp,
518
0
                  CStringGetDatum(fkrel->pk_columns),
519
0
                  ObjectIdGetDatum(TEXTOID),
520
0
                  Int32GetDatum(-1));
521
0
    values[4] = BoolGetDatum(fkrel->is_array);
522
0
    values[5] = BoolGetDatum(fkrel->is_opt);
523
524
0
    tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
525
526
0
    SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
527
0
  }
528
529
0
  SRF_RETURN_DONE(funcctx);
530
0
}
531
532
533
/*
534
 * Return the type of the argument.
535
 */
536
Datum
537
pg_typeof(PG_FUNCTION_ARGS)
538
0
{
539
0
  PG_RETURN_OID(get_fn_expr_argtype(fcinfo->flinfo, 0));
540
0
}
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
pg_basetype(PG_FUNCTION_ARGS)
557
0
{
558
0
  Oid     typid = PG_GETARG_OID(0);
559
560
  /*
561
   * We loop to find the bottom base type in a stack of domains.
562
   */
563
0
  for (;;)
564
0
  {
565
0
    HeapTuple tup;
566
0
    Form_pg_type typTup;
567
568
0
    tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
569
0
    if (!HeapTupleIsValid(tup))
570
0
      PG_RETURN_NULL(); /* return NULL for bogus OID */
571
0
    typTup = (Form_pg_type) GETSTRUCT(tup);
572
0
    if (typTup->typtype != TYPTYPE_DOMAIN)
573
0
    {
574
      /* Not a domain, so done */
575
0
      ReleaseSysCache(tup);
576
0
      break;
577
0
    }
578
579
0
    typid = typTup->typbasetype;
580
0
    ReleaseSysCache(tup);
581
0
  }
582
583
0
  PG_RETURN_OID(typid);
584
0
}
585
586
587
/*
588
 * Implementation of the COLLATE FOR expression; returns the collation
589
 * of the argument.
590
 */
591
Datum
592
pg_collation_for(PG_FUNCTION_ARGS)
593
0
{
594
0
  Oid     typeid;
595
0
  Oid     collid;
596
597
0
  typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
598
0
  if (!typeid)
599
0
    PG_RETURN_NULL();
600
0
  if (!type_is_collatable(typeid) && typeid != UNKNOWNOID)
601
0
    ereport(ERROR,
602
0
        (errcode(ERRCODE_DATATYPE_MISMATCH),
603
0
         errmsg("collations are not supported by type %s",
604
0
            format_type_be(typeid))));
605
606
0
  collid = PG_GET_COLLATION();
607
0
  if (!collid)
608
0
    PG_RETURN_NULL();
609
0
  PG_RETURN_TEXT_P(cstring_to_text(generate_collation_name(collid)));
610
0
}
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
pg_relation_is_updatable(PG_FUNCTION_ARGS)
622
0
{
623
0
  Oid     reloid = PG_GETARG_OID(0);
624
0
  bool    include_triggers = PG_GETARG_BOOL(1);
625
626
0
  PG_RETURN_INT32(relation_is_updatable(reloid, NIL, include_triggers, NULL));
627
0
}
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
pg_column_is_updatable(PG_FUNCTION_ARGS)
639
0
{
640
0
  Oid     reloid = PG_GETARG_OID(0);
641
0
  AttrNumber  attnum = PG_GETARG_INT16(1);
642
0
  AttrNumber  col = attnum - FirstLowInvalidHeapAttributeNumber;
643
0
  bool    include_triggers = PG_GETARG_BOOL(2);
644
0
  int     events;
645
646
  /* System columns are never updatable */
647
0
  if (attnum <= 0)
648
0
    PG_RETURN_BOOL(false);
649
650
0
  events = relation_is_updatable(reloid, NIL, include_triggers,
651
0
                   bms_make_singleton(col));
652
653
  /* We require both updatability and deletability of the relation */
654
0
#define REQ_EVENTS ((1 << CMD_UPDATE) | (1 << CMD_DELETE))
655
656
0
  PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS);
657
0
}
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
pg_input_is_valid(PG_FUNCTION_ARGS)
670
0
{
671
0
  text     *txt = PG_GETARG_TEXT_PP(0);
672
0
  text     *typname = PG_GETARG_TEXT_PP(1);
673
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
674
675
0
  PG_RETURN_BOOL(pg_input_is_valid_common(fcinfo, txt, typname,
676
0
                      &escontext));
677
0
}
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
pg_input_error_info(PG_FUNCTION_ARGS)
690
0
{
691
0
  text     *txt = PG_GETARG_TEXT_PP(0);
692
0
  text     *typname = PG_GETARG_TEXT_PP(1);
693
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
694
0
  TupleDesc tupdesc;
695
0
  Datum   values[4];
696
0
  bool    isnull[4];
697
698
0
  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
0
  escontext.details_wanted = true;
703
704
0
  if (pg_input_is_valid_common(fcinfo, txt, typname,
705
0
                 &escontext))
706
0
    memset(isnull, true, sizeof(isnull));
707
0
  else
708
0
  {
709
0
    char     *sqlstate;
710
711
0
    Assert(escontext.error_occurred);
712
0
    Assert(escontext.error_data != NULL);
713
0
    Assert(escontext.error_data->message != NULL);
714
715
0
    memset(isnull, false, sizeof(isnull));
716
717
0
    values[0] = CStringGetTextDatum(escontext.error_data->message);
718
719
0
    if (escontext.error_data->detail != NULL)
720
0
      values[1] = CStringGetTextDatum(escontext.error_data->detail);
721
0
    else
722
0
      isnull[1] = true;
723
724
0
    if (escontext.error_data->hint != NULL)
725
0
      values[2] = CStringGetTextDatum(escontext.error_data->hint);
726
0
    else
727
0
      isnull[2] = true;
728
729
0
    sqlstate = unpack_sql_state(escontext.error_data->sqlerrcode);
730
0
    values[3] = CStringGetTextDatum(sqlstate);
731
0
  }
732
733
0
  return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
734
0
}
735
736
/* Common subroutine for the above */
737
static bool
738
pg_input_is_valid_common(FunctionCallInfo fcinfo,
739
             text *txt, text *typname,
740
             ErrorSaveContext *escontext)
741
0
{
742
0
  char     *str = text_to_cstring(txt);
743
0
  ValidIOData *my_extra;
744
0
  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
0
  my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra;
751
0
  if (my_extra == NULL)
752
0
  {
753
0
    fcinfo->flinfo->fn_extra =
754
0
      MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
755
0
                 sizeof(ValidIOData));
756
0
    my_extra = (ValidIOData *) fcinfo->flinfo->fn_extra;
757
0
    my_extra->typoid = InvalidOid;
758
    /* Detect whether typname argument is constant. */
759
0
    my_extra->typname_constant = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
760
0
  }
761
762
  /*
763
   * If the typname argument is constant, we only need to parse it the first
764
   * time through.
765
   */
766
0
  if (my_extra->typoid == InvalidOid || !my_extra->typname_constant)
767
0
  {
768
0
    char     *typnamestr = text_to_cstring(typname);
769
0
    Oid     typoid;
770
771
    /* Parse type-name argument to obtain type OID and encoded typmod. */
772
0
    (void) parseTypeString(typnamestr, &typoid, &my_extra->typmod, NULL);
773
774
    /* Update type-specific info if typoid changed. */
775
0
    if (my_extra->typoid != typoid)
776
0
    {
777
0
      getTypeInputInfo(typoid,
778
0
               &my_extra->typiofunc,
779
0
               &my_extra->typioparam);
780
0
      fmgr_info_cxt(my_extra->typiofunc, &my_extra->inputproc,
781
0
              fcinfo->flinfo->fn_mcxt);
782
0
      my_extra->typoid = typoid;
783
0
    }
784
0
  }
785
786
  /* Now we can try to perform the conversion. */
787
0
  return InputFunctionCallSafe(&my_extra->inputproc,
788
0
                 str,
789
0
                 my_extra->typioparam,
790
0
                 my_extra->typmod,
791
0
                 (Node *) escontext,
792
0
                 &converted);
793
0
}
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
is_ident_start(unsigned char c)
802
0
{
803
  /* Underscores and ASCII letters are OK */
804
0
  if (c == '_')
805
0
    return true;
806
0
  if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
807
0
    return true;
808
  /* Any high-bit-set character is OK (might be part of a multibyte char) */
809
0
  if (IS_HIGHBIT_SET(c))
810
0
    return true;
811
0
  return false;
812
0
}
813
814
/*
815
 * Is character a valid identifier continuation?
816
 * Must match scan.l's {ident_cont} character class.
817
 */
818
static bool
819
is_ident_cont(unsigned char c)
820
0
{
821
  /* Can be digit or dollar sign ... */
822
0
  if ((c >= '0' && c <= '9') || c == '$')
823
0
    return true;
824
  /* ... or an identifier start character */
825
0
  return is_ident_start(c);
826
0
}
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
parse_ident(PG_FUNCTION_ARGS)
835
0
{
836
0
  text     *qualname = PG_GETARG_TEXT_PP(0);
837
0
  bool    strict = PG_GETARG_BOOL(1);
838
0
  char     *qualname_str = text_to_cstring(qualname);
839
0
  ArrayBuildState *astate = NULL;
840
0
  char     *nextp;
841
0
  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
0
  nextp = qualname_str;
849
850
  /* skip leading whitespace */
851
0
  while (scanner_isspace(*nextp))
852
0
    nextp++;
853
854
0
  for (;;)
855
0
  {
856
0
    char     *curname;
857
0
    bool    missing_ident = true;
858
859
0
    if (*nextp == '"')
860
0
    {
861
0
      char     *endp;
862
863
0
      curname = nextp + 1;
864
0
      for (;;)
865
0
      {
866
0
        endp = strchr(nextp + 1, '"');
867
0
        if (endp == NULL)
868
0
          ereport(ERROR,
869
0
              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
870
0
               errmsg("string is not a valid identifier: \"%s\"",
871
0
                  text_to_cstring(qualname)),
872
0
               errdetail("String has unclosed double quotes.")));
873
0
        if (endp[1] != '"')
874
0
          break;
875
0
        memmove(endp, endp + 1, strlen(endp));
876
0
        nextp = endp;
877
0
      }
878
0
      nextp = endp + 1;
879
0
      *endp = '\0';
880
881
0
      if (endp - curname == 0)
882
0
        ereport(ERROR,
883
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
884
0
             errmsg("string is not a valid identifier: \"%s\"",
885
0
                text_to_cstring(qualname)),
886
0
             errdetail("Quoted identifier must not be empty.")));
887
888
0
      astate = accumArrayResult(astate, CStringGetTextDatum(curname),
889
0
                    false, TEXTOID, CurrentMemoryContext);
890
0
      missing_ident = false;
891
0
    }
892
0
    else if (is_ident_start((unsigned char) *nextp))
893
0
    {
894
0
      char     *downname;
895
0
      int     len;
896
0
      text     *part;
897
898
0
      curname = nextp++;
899
0
      while (is_ident_cont((unsigned char) *nextp))
900
0
        nextp++;
901
902
0
      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
0
      downname = downcase_identifier(curname, len, false, false);
911
0
      part = cstring_to_text_with_len(downname, len);
912
0
      astate = accumArrayResult(astate, PointerGetDatum(part), false,
913
0
                    TEXTOID, CurrentMemoryContext);
914
0
      missing_ident = false;
915
0
    }
916
917
0
    if (missing_ident)
918
0
    {
919
      /* Different error messages based on where we failed. */
920
0
      if (*nextp == '.')
921
0
        ereport(ERROR,
922
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
923
0
             errmsg("string is not a valid identifier: \"%s\"",
924
0
                text_to_cstring(qualname)),
925
0
             errdetail("No valid identifier before \".\".")));
926
0
      else if (after_dot)
927
0
        ereport(ERROR,
928
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
929
0
             errmsg("string is not a valid identifier: \"%s\"",
930
0
                text_to_cstring(qualname)),
931
0
             errdetail("No valid identifier after \".\".")));
932
0
      else
933
0
        ereport(ERROR,
934
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
935
0
             errmsg("string is not a valid identifier: \"%s\"",
936
0
                text_to_cstring(qualname))));
937
0
    }
938
939
0
    while (scanner_isspace(*nextp))
940
0
      nextp++;
941
942
0
    if (*nextp == '.')
943
0
    {
944
0
      after_dot = true;
945
0
      nextp++;
946
0
      while (scanner_isspace(*nextp))
947
0
        nextp++;
948
0
    }
949
0
    else if (*nextp == '\0')
950
0
    {
951
0
      break;
952
0
    }
953
0
    else
954
0
    {
955
0
      if (strict)
956
0
        ereport(ERROR,
957
0
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
958
0
             errmsg("string is not a valid identifier: \"%s\"",
959
0
                text_to_cstring(qualname))));
960
0
      break;
961
0
    }
962
0
  }
963
964
0
  PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
965
0
}
966
967
/*
968
 * pg_current_logfile
969
 *
970
 * Report current log file used by log collector by scanning current_logfiles.
971
 */
972
Datum
973
pg_current_logfile(PG_FUNCTION_ARGS)
974
0
{
975
0
  FILE     *fd;
976
0
  char    lbuffer[MAXPGPATH];
977
0
  char     *logfmt;
978
979
  /* The log format parameter is optional */
980
0
  if (PG_NARGS() == 0 || PG_ARGISNULL(0))
981
0
    logfmt = NULL;
982
0
  else
983
0
  {
984
0
    logfmt = text_to_cstring(PG_GETARG_TEXT_PP(0));
985
986
0
    if (strcmp(logfmt, "stderr") != 0 &&
987
0
      strcmp(logfmt, "csvlog") != 0 &&
988
0
      strcmp(logfmt, "jsonlog") != 0)
989
0
      ereport(ERROR,
990
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
991
0
           errmsg("log format \"%s\" is not supported", logfmt),
992
0
           errhint("The supported log formats are \"stderr\", \"csvlog\", and \"jsonlog\".")));
993
0
  }
994
995
0
  fd = AllocateFile(LOG_METAINFO_DATAFILE, "r");
996
0
  if (fd == NULL)
997
0
  {
998
0
    if (errno != ENOENT)
999
0
      ereport(ERROR,
1000
0
          (errcode_for_file_access(),
1001
0
           errmsg("could not read file \"%s\": %m",
1002
0
              LOG_METAINFO_DATAFILE)));
1003
0
    PG_RETURN_NULL();
1004
0
  }
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
0
  while (fgets(lbuffer, sizeof(lbuffer), fd) != NULL)
1016
0
  {
1017
0
    char     *log_format;
1018
0
    char     *log_filepath;
1019
0
    char     *nlpos;
1020
1021
    /* Extract log format and log file path from the line. */
1022
0
    log_format = lbuffer;
1023
0
    log_filepath = strchr(lbuffer, ' ');
1024
0
    if (log_filepath == NULL)
1025
0
    {
1026
      /* Uh oh.  No space found, so file content is corrupted. */
1027
0
      elog(ERROR,
1028
0
         "missing space character in \"%s\"", LOG_METAINFO_DATAFILE);
1029
0
      break;
1030
0
    }
1031
1032
0
    *log_filepath = '\0';
1033
0
    log_filepath++;
1034
0
    nlpos = strchr(log_filepath, '\n');
1035
0
    if (nlpos == NULL)
1036
0
    {
1037
      /* Uh oh.  No newline found, so file content is corrupted. */
1038
0
      elog(ERROR,
1039
0
         "missing newline character in \"%s\"", LOG_METAINFO_DATAFILE);
1040
0
      break;
1041
0
    }
1042
0
    *nlpos = '\0';
1043
1044
0
    if (logfmt == NULL || strcmp(logfmt, log_format) == 0)
1045
0
    {
1046
0
      FreeFile(fd);
1047
0
      PG_RETURN_TEXT_P(cstring_to_text(log_filepath));
1048
0
    }
1049
0
  }
1050
1051
  /* Close the current log filename file. */
1052
0
  FreeFile(fd);
1053
1054
0
  PG_RETURN_NULL();
1055
0
}
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
pg_current_logfile_1arg(PG_FUNCTION_ARGS)
1066
0
{
1067
0
  return pg_current_logfile(fcinfo);
1068
0
}
1069
1070
/*
1071
 * SQL wrapper around RelationGetReplicaIndex().
1072
 */
1073
Datum
1074
pg_get_replica_identity_index(PG_FUNCTION_ARGS)
1075
0
{
1076
0
  Oid     reloid = PG_GETARG_OID(0);
1077
0
  Oid     idxoid;
1078
0
  Relation  rel;
1079
1080
0
  rel = table_open(reloid, AccessShareLock);
1081
0
  idxoid = RelationGetReplicaIndex(rel);
1082
0
  table_close(rel, AccessShareLock);
1083
1084
0
  if (OidIsValid(idxoid))
1085
0
    PG_RETURN_OID(idxoid);
1086
0
  else
1087
0
    PG_RETURN_NULL();
1088
0
}
1089
1090
/*
1091
 * Transition function for the ANY_VALUE aggregate
1092
 */
1093
Datum
1094
any_value_transfn(PG_FUNCTION_ARGS)
1095
0
{
1096
0
  PG_RETURN_DATUM(PG_GETARG_DATUM(0));
1097
0
}