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