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