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