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