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