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