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