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 : }
|