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