Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * misc.c
4 : *
5 : *
6 : * Portions Copyright (c) 1996-2022, 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/catalog.h"
27 : #include "catalog/pg_tablespace.h"
28 : #include "catalog/pg_type.h"
29 : #include "catalog/system_fk_info.h"
30 : #include "commands/dbcommands.h"
31 : #include "commands/tablespace.h"
32 : #include "common/keywords.h"
33 : #include "funcapi.h"
34 : #include "miscadmin.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/timestamp.h"
47 :
48 : /*
49 : * Common subroutine for num_nulls() and num_nonnulls().
50 : * Returns true if successful, false if function should return NULL.
51 : * If successful, total argument count and number of nulls are
52 : * returned into *nargs and *nulls.
53 : */
54 : static bool
55 120 : count_nulls(FunctionCallInfo fcinfo,
56 : int32 *nargs, int32 *nulls)
57 : {
58 120 : int32 count = 0;
59 : int i;
60 :
61 : /* Did we get a VARIADIC array argument, or separate arguments? */
62 120 : if (get_fn_expr_variadic(fcinfo->flinfo))
63 : {
64 : ArrayType *arr;
65 : int ndims,
66 : nitems,
67 : *dims;
68 : bits8 *bitmap;
69 :
70 : Assert(PG_NARGS() == 1);
71 :
72 : /*
73 : * If we get a null as VARIADIC array argument, we can't say anything
74 : * useful about the number of elements, so return NULL. This behavior
75 : * is consistent with other variadic functions - see concat_internal.
76 : */
77 60 : if (PG_ARGISNULL(0))
78 12 : return false;
79 :
80 : /*
81 : * Non-null argument had better be an array. We assume that any call
82 : * context that could let get_fn_expr_variadic return true will have
83 : * checked that a VARIADIC-labeled parameter actually is an array. So
84 : * it should be okay to just Assert that it's an array rather than
85 : * doing a full-fledged error check.
86 : */
87 : Assert(OidIsValid(get_base_element_type(get_fn_expr_argtype(fcinfo->flinfo, 0))));
88 :
89 : /* OK, safe to fetch the array value */
90 48 : arr = PG_GETARG_ARRAYTYPE_P(0);
91 :
92 : /* Count the array elements */
93 48 : ndims = ARR_NDIM(arr);
94 48 : dims = ARR_DIMS(arr);
95 48 : nitems = ArrayGetNItems(ndims, dims);
96 :
97 : /* Count those that are NULL */
98 48 : bitmap = ARR_NULLBITMAP(arr);
99 48 : if (bitmap)
100 : {
101 24 : int bitmask = 1;
102 :
103 1272 : for (i = 0; i < nitems; i++)
104 : {
105 1248 : if ((*bitmap & bitmask) == 0)
106 24 : count++;
107 :
108 1248 : bitmask <<= 1;
109 1248 : if (bitmask == 0x100)
110 : {
111 144 : bitmap++;
112 144 : bitmask = 1;
113 : }
114 : }
115 : }
116 :
117 48 : *nargs = nitems;
118 48 : *nulls = count;
119 : }
120 : else
121 : {
122 : /* Separate arguments, so just count 'em */
123 204 : for (i = 0; i < PG_NARGS(); i++)
124 : {
125 144 : if (PG_ARGISNULL(i))
126 84 : count++;
127 : }
128 :
129 60 : *nargs = PG_NARGS();
130 60 : *nulls = count;
131 : }
132 :
133 108 : return true;
134 : }
135 :
136 : /*
137 : * num_nulls()
138 : * Count the number of NULL arguments
139 : */
140 : Datum
141 60 : pg_num_nulls(PG_FUNCTION_ARGS)
142 : {
143 : int32 nargs,
144 : nulls;
145 :
146 60 : if (!count_nulls(fcinfo, &nargs, &nulls))
147 6 : PG_RETURN_NULL();
148 :
149 54 : PG_RETURN_INT32(nulls);
150 : }
151 :
152 : /*
153 : * num_nonnulls()
154 : * Count the number of non-NULL arguments
155 : */
156 : Datum
157 60 : pg_num_nonnulls(PG_FUNCTION_ARGS)
158 : {
159 : int32 nargs,
160 : nulls;
161 :
162 60 : if (!count_nulls(fcinfo, &nargs, &nulls))
163 6 : PG_RETURN_NULL();
164 :
165 54 : PG_RETURN_INT32(nargs - nulls);
166 : }
167 :
168 :
169 : /*
170 : * current_database()
171 : * Expose the current database to the user
172 : */
173 : Datum
174 4274 : current_database(PG_FUNCTION_ARGS)
175 : {
176 : Name db;
177 :
178 4274 : db = (Name) palloc(NAMEDATALEN);
179 :
180 4274 : namestrcpy(db, get_database_name(MyDatabaseId));
181 4274 : PG_RETURN_NAME(db);
182 : }
183 :
184 :
185 : /*
186 : * current_query()
187 : * Expose the current query to the user (useful in stored procedures)
188 : * We might want to use ActivePortal->sourceText someday.
189 : */
190 : Datum
191 0 : current_query(PG_FUNCTION_ARGS)
192 : {
193 : /* there is no easy way to access the more concise 'query_string' */
194 0 : if (debug_query_string)
195 0 : PG_RETURN_TEXT_P(cstring_to_text(debug_query_string));
196 : else
197 0 : PG_RETURN_NULL();
198 : }
199 :
200 : /* Function to find out which databases make use of a tablespace */
201 :
202 : Datum
203 6 : pg_tablespace_databases(PG_FUNCTION_ARGS)
204 : {
205 6 : Oid tablespaceOid = PG_GETARG_OID(0);
206 6 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
207 : char *location;
208 : DIR *dirdesc;
209 : struct dirent *de;
210 :
211 6 : SetSingleFuncCall(fcinfo, SRF_SINGLE_USE_EXPECTED);
212 :
213 6 : if (tablespaceOid == GLOBALTABLESPACE_OID)
214 : {
215 0 : ereport(WARNING,
216 : (errmsg("global tablespace never has databases")));
217 : /* return empty tuplestore */
218 0 : return (Datum) 0;
219 : }
220 :
221 6 : if (tablespaceOid == DEFAULTTABLESPACE_OID)
222 6 : location = psprintf("base");
223 : else
224 0 : location = psprintf("pg_tblspc/%u/%s", tablespaceOid,
225 : TABLESPACE_VERSION_DIRECTORY);
226 :
227 6 : dirdesc = AllocateDir(location);
228 :
229 6 : if (!dirdesc)
230 : {
231 : /* the only expected error is ENOENT */
232 0 : if (errno != ENOENT)
233 0 : ereport(ERROR,
234 : (errcode_for_file_access(),
235 : errmsg("could not open directory \"%s\": %m",
236 : location)));
237 0 : ereport(WARNING,
238 : (errmsg("%u is not a tablespace OID", tablespaceOid)));
239 : /* return empty tuplestore */
240 0 : return (Datum) 0;
241 : }
242 :
243 54 : while ((de = ReadDir(dirdesc, location)) != NULL)
244 : {
245 48 : Oid datOid = atooid(de->d_name);
246 : char *subdir;
247 : bool isempty;
248 : Datum values[1];
249 : bool nulls[1];
250 :
251 : /* this test skips . and .., but is awfully weak */
252 48 : if (!datOid)
253 18 : continue;
254 :
255 : /* if database subdir is empty, don't report tablespace as used */
256 :
257 30 : subdir = psprintf("%s/%s", location, de->d_name);
258 30 : isempty = directory_is_empty(subdir);
259 30 : pfree(subdir);
260 :
261 30 : if (isempty)
262 0 : continue; /* indeed, nothing in it */
263 :
264 30 : values[0] = ObjectIdGetDatum(datOid);
265 30 : nulls[0] = false;
266 :
267 30 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
268 : values, nulls);
269 : }
270 :
271 6 : FreeDir(dirdesc);
272 6 : return (Datum) 0;
273 : }
274 :
275 :
276 : /*
277 : * pg_tablespace_location - get location for a tablespace
278 : */
279 : Datum
280 34 : pg_tablespace_location(PG_FUNCTION_ARGS)
281 : {
282 34 : Oid tablespaceOid = PG_GETARG_OID(0);
283 : char sourcepath[MAXPGPATH];
284 : char targetpath[MAXPGPATH];
285 : int rllen;
286 : #ifndef WIN32
287 : struct stat st;
288 : #endif
289 :
290 : /*
291 : * It's useful to apply this function to pg_class.reltablespace, wherein
292 : * zero means "the database's default tablespace". So, rather than
293 : * throwing an error for zero, we choose to assume that's what is meant.
294 : */
295 34 : if (tablespaceOid == InvalidOid)
296 0 : tablespaceOid = MyDatabaseTableSpace;
297 :
298 : /*
299 : * Return empty string for the cluster's default tablespaces
300 : */
301 34 : if (tablespaceOid == DEFAULTTABLESPACE_OID ||
302 : tablespaceOid == GLOBALTABLESPACE_OID)
303 28 : PG_RETURN_TEXT_P(cstring_to_text(""));
304 :
305 : #if defined(HAVE_READLINK) || defined(WIN32)
306 :
307 : /*
308 : * Find the location of the tablespace by reading the symbolic link that
309 : * is in pg_tblspc/<oid>.
310 : */
311 6 : snprintf(sourcepath, sizeof(sourcepath), "pg_tblspc/%u", tablespaceOid);
312 :
313 : /*
314 : * Before reading the link, check if the source path is a link or a
315 : * junction point. Note that a directory is possible for a tablespace
316 : * created with allow_in_place_tablespaces enabled. If a directory is
317 : * found, a relative path to the data directory is returned.
318 : */
319 : #ifdef WIN32
320 : if (!pgwin32_is_junction(sourcepath))
321 : PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
322 : #else
323 6 : if (lstat(sourcepath, &st) < 0)
324 : {
325 0 : ereport(ERROR,
326 : (errcode_for_file_access(),
327 : errmsg("could not stat file \"%s\": %m",
328 : sourcepath)));
329 : }
330 :
331 6 : if (!S_ISLNK(st.st_mode))
332 6 : PG_RETURN_TEXT_P(cstring_to_text(sourcepath));
333 : #endif
334 :
335 : /*
336 : * In presence of a link or a junction point, return the path pointing to.
337 : */
338 0 : rllen = readlink(sourcepath, targetpath, sizeof(targetpath));
339 0 : if (rllen < 0)
340 0 : ereport(ERROR,
341 : (errcode_for_file_access(),
342 : errmsg("could not read symbolic link \"%s\": %m",
343 : sourcepath)));
344 0 : if (rllen >= sizeof(targetpath))
345 0 : ereport(ERROR,
346 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
347 : errmsg("symbolic link \"%s\" target is too long",
348 : sourcepath)));
349 0 : targetpath[rllen] = '\0';
350 :
351 0 : PG_RETURN_TEXT_P(cstring_to_text(targetpath));
352 : #else
353 : ereport(ERROR,
354 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
355 : errmsg("tablespaces are not supported on this platform")));
356 : PG_RETURN_NULL();
357 : #endif
358 : }
359 :
360 : /*
361 : * pg_sleep - delay for N seconds
362 : */
363 : Datum
364 70 : pg_sleep(PG_FUNCTION_ARGS)
365 : {
366 70 : float8 secs = PG_GETARG_FLOAT8(0);
367 : float8 endtime;
368 :
369 : /*
370 : * We sleep using WaitLatch, to ensure that we'll wake up promptly if an
371 : * important signal (such as SIGALRM or SIGINT) arrives. Because
372 : * WaitLatch's upper limit of delay is INT_MAX milliseconds, and the user
373 : * might ask for more than that, we sleep for at most 10 minutes and then
374 : * loop.
375 : *
376 : * By computing the intended stop time initially, we avoid accumulation of
377 : * extra delay across multiple sleeps. This also ensures we won't delay
378 : * less than the specified time when WaitLatch is terminated early by a
379 : * non-query-canceling signal such as SIGHUP.
380 : */
381 : #define GetNowFloat() ((float8) GetCurrentTimestamp() / 1000000.0)
382 :
383 70 : endtime = GetNowFloat() + secs;
384 :
385 : for (;;)
386 86 : {
387 : float8 delay;
388 : long delay_ms;
389 :
390 156 : CHECK_FOR_INTERRUPTS();
391 :
392 154 : delay = endtime - GetNowFloat();
393 154 : if (delay >= 600.0)
394 0 : delay_ms = 600000;
395 154 : else if (delay > 0.0)
396 86 : delay_ms = (long) ceil(delay * 1000.0);
397 : else
398 68 : break;
399 :
400 86 : (void) WaitLatch(MyLatch,
401 : WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
402 : delay_ms,
403 : WAIT_EVENT_PG_SLEEP);
404 86 : ResetLatch(MyLatch);
405 : }
406 :
407 68 : PG_RETURN_VOID();
408 : }
409 :
410 : /* Function to return the list of grammar keywords */
411 : Datum
412 0 : pg_get_keywords(PG_FUNCTION_ARGS)
413 : {
414 : FuncCallContext *funcctx;
415 :
416 0 : if (SRF_IS_FIRSTCALL())
417 : {
418 : MemoryContext oldcontext;
419 : TupleDesc tupdesc;
420 :
421 0 : funcctx = SRF_FIRSTCALL_INIT();
422 0 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
423 :
424 0 : tupdesc = CreateTemplateTupleDesc(5);
425 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word",
426 : TEXTOID, -1, 0);
427 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode",
428 : CHAROID, -1, 0);
429 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "barelabel",
430 : BOOLOID, -1, 0);
431 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "catdesc",
432 : TEXTOID, -1, 0);
433 0 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "baredesc",
434 : TEXTOID, -1, 0);
435 :
436 0 : funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
437 :
438 0 : MemoryContextSwitchTo(oldcontext);
439 : }
440 :
441 0 : funcctx = SRF_PERCALL_SETUP();
442 :
443 0 : if (funcctx->call_cntr < ScanKeywords.num_keywords)
444 : {
445 : char *values[5];
446 : HeapTuple tuple;
447 :
448 : /* cast-away-const is ugly but alternatives aren't much better */
449 0 : values[0] = unconstify(char *,
450 : GetScanKeyword(funcctx->call_cntr,
451 : &ScanKeywords));
452 :
453 0 : switch (ScanKeywordCategories[funcctx->call_cntr])
454 : {
455 0 : case UNRESERVED_KEYWORD:
456 0 : values[1] = "U";
457 0 : values[3] = _("unreserved");
458 0 : break;
459 0 : case COL_NAME_KEYWORD:
460 0 : values[1] = "C";
461 0 : values[3] = _("unreserved (cannot be function or type name)");
462 0 : break;
463 0 : case TYPE_FUNC_NAME_KEYWORD:
464 0 : values[1] = "T";
465 0 : values[3] = _("reserved (can be function or type name)");
466 0 : break;
467 0 : case RESERVED_KEYWORD:
468 0 : values[1] = "R";
469 0 : values[3] = _("reserved");
470 0 : break;
471 0 : default: /* shouldn't be possible */
472 0 : values[1] = NULL;
473 0 : values[3] = NULL;
474 0 : break;
475 : }
476 :
477 0 : if (ScanKeywordBareLabel[funcctx->call_cntr])
478 : {
479 0 : values[2] = "true";
480 0 : values[4] = _("can be bare label");
481 : }
482 : else
483 : {
484 0 : values[2] = "false";
485 0 : values[4] = _("requires AS");
486 : }
487 :
488 0 : tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
489 :
490 0 : SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
491 : }
492 :
493 0 : SRF_RETURN_DONE(funcctx);
494 : }
495 :
496 :
497 : /* Function to return the list of catalog foreign key relationships */
498 : Datum
499 1320 : pg_get_catalog_foreign_keys(PG_FUNCTION_ARGS)
500 : {
501 : FuncCallContext *funcctx;
502 : FmgrInfo *arrayinp;
503 :
504 1320 : if (SRF_IS_FIRSTCALL())
505 : {
506 : MemoryContext oldcontext;
507 : TupleDesc tupdesc;
508 :
509 6 : funcctx = SRF_FIRSTCALL_INIT();
510 6 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
511 :
512 6 : tupdesc = CreateTemplateTupleDesc(6);
513 6 : TupleDescInitEntry(tupdesc, (AttrNumber) 1, "fktable",
514 : REGCLASSOID, -1, 0);
515 6 : TupleDescInitEntry(tupdesc, (AttrNumber) 2, "fkcols",
516 : TEXTARRAYOID, -1, 0);
517 6 : TupleDescInitEntry(tupdesc, (AttrNumber) 3, "pktable",
518 : REGCLASSOID, -1, 0);
519 6 : TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pkcols",
520 : TEXTARRAYOID, -1, 0);
521 6 : TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_array",
522 : BOOLOID, -1, 0);
523 6 : TupleDescInitEntry(tupdesc, (AttrNumber) 6, "is_opt",
524 : BOOLOID, -1, 0);
525 :
526 6 : funcctx->tuple_desc = BlessTupleDesc(tupdesc);
527 :
528 : /*
529 : * We use array_in to convert the C strings in sys_fk_relationships[]
530 : * to text arrays. But we cannot use DirectFunctionCallN to call
531 : * array_in, and it wouldn't be very efficient if we could. Fill an
532 : * FmgrInfo to use for the call.
533 : */
534 6 : arrayinp = (FmgrInfo *) palloc(sizeof(FmgrInfo));
535 6 : fmgr_info(F_ARRAY_IN, arrayinp);
536 6 : funcctx->user_fctx = arrayinp;
537 :
538 6 : MemoryContextSwitchTo(oldcontext);
539 : }
540 :
541 1320 : funcctx = SRF_PERCALL_SETUP();
542 1320 : arrayinp = (FmgrInfo *) funcctx->user_fctx;
543 :
544 1320 : if (funcctx->call_cntr < lengthof(sys_fk_relationships))
545 : {
546 1314 : const SysFKRelationship *fkrel = &sys_fk_relationships[funcctx->call_cntr];
547 : Datum values[6];
548 : bool nulls[6];
549 : HeapTuple tuple;
550 :
551 1314 : memset(nulls, false, sizeof(nulls));
552 :
553 1314 : values[0] = ObjectIdGetDatum(fkrel->fk_table);
554 1314 : values[1] = FunctionCall3(arrayinp,
555 : CStringGetDatum(fkrel->fk_columns),
556 : ObjectIdGetDatum(TEXTOID),
557 : Int32GetDatum(-1));
558 1314 : values[2] = ObjectIdGetDatum(fkrel->pk_table);
559 1314 : values[3] = FunctionCall3(arrayinp,
560 : CStringGetDatum(fkrel->pk_columns),
561 : ObjectIdGetDatum(TEXTOID),
562 : Int32GetDatum(-1));
563 1314 : values[4] = BoolGetDatum(fkrel->is_array);
564 1314 : values[5] = BoolGetDatum(fkrel->is_opt);
565 :
566 1314 : tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
567 :
568 1314 : SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
569 : }
570 :
571 6 : SRF_RETURN_DONE(funcctx);
572 : }
573 :
574 :
575 : /*
576 : * Return the type of the argument.
577 : */
578 : Datum
579 1546 : pg_typeof(PG_FUNCTION_ARGS)
580 : {
581 1546 : PG_RETURN_OID(get_fn_expr_argtype(fcinfo->flinfo, 0));
582 : }
583 :
584 :
585 : /*
586 : * Implementation of the COLLATE FOR expression; returns the collation
587 : * of the argument.
588 : */
589 : Datum
590 30 : pg_collation_for(PG_FUNCTION_ARGS)
591 : {
592 : Oid typeid;
593 : Oid collid;
594 :
595 30 : typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
596 30 : if (!typeid)
597 0 : PG_RETURN_NULL();
598 30 : if (!type_is_collatable(typeid) && typeid != UNKNOWNOID)
599 6 : ereport(ERROR,
600 : (errcode(ERRCODE_DATATYPE_MISMATCH),
601 : errmsg("collations are not supported by type %s",
602 : format_type_be(typeid))));
603 :
604 24 : collid = PG_GET_COLLATION();
605 24 : if (!collid)
606 6 : PG_RETURN_NULL();
607 18 : PG_RETURN_TEXT_P(cstring_to_text(generate_collation_name(collid)));
608 : }
609 :
610 :
611 : /*
612 : * pg_relation_is_updatable - determine which update events the specified
613 : * relation supports.
614 : *
615 : * This relies on relation_is_updatable() in rewriteHandler.c, which see
616 : * for additional information.
617 : */
618 : Datum
619 882 : pg_relation_is_updatable(PG_FUNCTION_ARGS)
620 : {
621 882 : Oid reloid = PG_GETARG_OID(0);
622 882 : bool include_triggers = PG_GETARG_BOOL(1);
623 :
624 882 : PG_RETURN_INT32(relation_is_updatable(reloid, NIL, include_triggers, NULL));
625 : }
626 :
627 : /*
628 : * pg_column_is_updatable - determine whether a column is updatable
629 : *
630 : * This function encapsulates the decision about just what
631 : * information_schema.columns.is_updatable actually means. It's not clear
632 : * whether deletability of the column's relation should be required, so
633 : * we want that decision in C code where we could change it without initdb.
634 : */
635 : Datum
636 570 : pg_column_is_updatable(PG_FUNCTION_ARGS)
637 : {
638 570 : Oid reloid = PG_GETARG_OID(0);
639 570 : AttrNumber attnum = PG_GETARG_INT16(1);
640 570 : AttrNumber col = attnum - FirstLowInvalidHeapAttributeNumber;
641 570 : bool include_triggers = PG_GETARG_BOOL(2);
642 : int events;
643 :
644 : /* System columns are never updatable */
645 570 : if (attnum <= 0)
646 0 : PG_RETURN_BOOL(false);
647 :
648 570 : events = relation_is_updatable(reloid, NIL, include_triggers,
649 : bms_make_singleton(col));
650 :
651 : /* We require both updatability and deletability of the relation */
652 : #define REQ_EVENTS ((1 << CMD_UPDATE) | (1 << CMD_DELETE))
653 :
654 570 : PG_RETURN_BOOL((events & REQ_EVENTS) == REQ_EVENTS);
655 : }
656 :
657 :
658 : /*
659 : * Is character a valid identifier start?
660 : * Must match scan.l's {ident_start} character class.
661 : */
662 : static bool
663 2202 : is_ident_start(unsigned char c)
664 : {
665 : /* Underscores and ASCII letters are OK */
666 2202 : if (c == '_')
667 0 : return true;
668 2202 : if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
669 2052 : return true;
670 : /* Any high-bit-set character is OK (might be part of a multibyte char) */
671 150 : if (IS_HIGHBIT_SET(c))
672 0 : return true;
673 150 : return false;
674 : }
675 :
676 : /*
677 : * Is character a valid identifier continuation?
678 : * Must match scan.l's {ident_cont} character class.
679 : */
680 : static bool
681 2052 : is_ident_cont(unsigned char c)
682 : {
683 : /* Can be digit or dollar sign ... */
684 2052 : if ((c >= '0' && c <= '9') || c == '$')
685 0 : return true;
686 : /* ... or an identifier start character */
687 2052 : return is_ident_start(c);
688 : }
689 :
690 : /*
691 : * parse_ident - parse a SQL qualified identifier into separate identifiers.
692 : * When strict mode is active (second parameter), then any chars after
693 : * the last identifier are disallowed.
694 : */
695 : Datum
696 114 : parse_ident(PG_FUNCTION_ARGS)
697 : {
698 114 : text *qualname = PG_GETARG_TEXT_PP(0);
699 114 : bool strict = PG_GETARG_BOOL(1);
700 114 : char *qualname_str = text_to_cstring(qualname);
701 114 : ArrayBuildState *astate = NULL;
702 : char *nextp;
703 114 : bool after_dot = false;
704 :
705 : /*
706 : * The code below scribbles on qualname_str in some cases, so we should
707 : * reconvert qualname if we need to show the original string in error
708 : * messages.
709 : */
710 114 : nextp = qualname_str;
711 :
712 : /* skip leading whitespace */
713 144 : while (scanner_isspace(*nextp))
714 30 : nextp++;
715 :
716 : for (;;)
717 96 : {
718 : char *curname;
719 210 : bool missing_ident = true;
720 :
721 210 : if (*nextp == '"')
722 : {
723 : char *endp;
724 :
725 60 : curname = nextp + 1;
726 : for (;;)
727 : {
728 60 : endp = strchr(nextp + 1, '"');
729 60 : if (endp == NULL)
730 0 : ereport(ERROR,
731 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
732 : errmsg("string is not a valid identifier: \"%s\"",
733 : text_to_cstring(qualname)),
734 : errdetail("String has unclosed double quotes.")));
735 60 : if (endp[1] != '"')
736 60 : break;
737 0 : memmove(endp, endp + 1, strlen(endp));
738 0 : nextp = endp;
739 : }
740 60 : nextp = endp + 1;
741 60 : *endp = '\0';
742 :
743 60 : if (endp - curname == 0)
744 0 : ereport(ERROR,
745 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
746 : errmsg("string is not a valid identifier: \"%s\"",
747 : text_to_cstring(qualname)),
748 : errdetail("Quoted identifier must not be empty.")));
749 :
750 60 : astate = accumArrayResult(astate, CStringGetTextDatum(curname),
751 : false, TEXTOID, CurrentMemoryContext);
752 60 : missing_ident = false;
753 : }
754 150 : else if (is_ident_start((unsigned char) *nextp))
755 : {
756 : char *downname;
757 : int len;
758 : text *part;
759 :
760 102 : curname = nextp++;
761 2052 : while (is_ident_cont((unsigned char) *nextp))
762 1950 : nextp++;
763 :
764 102 : len = nextp - curname;
765 :
766 : /*
767 : * We don't implicitly truncate identifiers. This is useful for
768 : * allowing the user to check for specific parts of the identifier
769 : * being too long. It's easy enough for the user to get the
770 : * truncated names by casting our output to name[].
771 : */
772 102 : downname = downcase_identifier(curname, len, false, false);
773 102 : part = cstring_to_text_with_len(downname, len);
774 102 : astate = accumArrayResult(astate, PointerGetDatum(part), false,
775 : TEXTOID, CurrentMemoryContext);
776 102 : missing_ident = false;
777 : }
778 :
779 210 : if (missing_ident)
780 : {
781 : /* Different error messages based on where we failed. */
782 48 : if (*nextp == '.')
783 18 : ereport(ERROR,
784 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
785 : errmsg("string is not a valid identifier: \"%s\"",
786 : text_to_cstring(qualname)),
787 : errdetail("No valid identifier before \".\".")));
788 30 : else if (after_dot)
789 12 : ereport(ERROR,
790 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
791 : errmsg("string is not a valid identifier: \"%s\"",
792 : text_to_cstring(qualname)),
793 : errdetail("No valid identifier after \".\".")));
794 : else
795 18 : ereport(ERROR,
796 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
797 : errmsg("string is not a valid identifier: \"%s\"",
798 : text_to_cstring(qualname))));
799 : }
800 :
801 204 : while (scanner_isspace(*nextp))
802 42 : nextp++;
803 :
804 162 : if (*nextp == '.')
805 : {
806 96 : after_dot = true;
807 96 : nextp++;
808 126 : while (scanner_isspace(*nextp))
809 30 : nextp++;
810 : }
811 66 : else if (*nextp == '\0')
812 : {
813 36 : break;
814 : }
815 : else
816 : {
817 30 : if (strict)
818 24 : ereport(ERROR,
819 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
820 : errmsg("string is not a valid identifier: \"%s\"",
821 : text_to_cstring(qualname))));
822 6 : break;
823 : }
824 : }
825 :
826 42 : PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
827 : }
828 :
829 : /*
830 : * pg_current_logfile
831 : *
832 : * Report current log file used by log collector by scanning current_logfiles.
833 : */
834 : Datum
835 12 : pg_current_logfile(PG_FUNCTION_ARGS)
836 : {
837 : FILE *fd;
838 : char lbuffer[MAXPGPATH];
839 : char *logfmt;
840 :
841 : /* The log format parameter is optional */
842 12 : if (PG_NARGS() == 0 || PG_ARGISNULL(0))
843 0 : logfmt = NULL;
844 : else
845 : {
846 12 : logfmt = text_to_cstring(PG_GETARG_TEXT_PP(0));
847 :
848 12 : if (strcmp(logfmt, "stderr") != 0 &&
849 8 : strcmp(logfmt, "csvlog") != 0 &&
850 4 : strcmp(logfmt, "jsonlog") != 0)
851 0 : ereport(ERROR,
852 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
853 : errmsg("log format \"%s\" is not supported", logfmt),
854 : errhint("The supported log formats are \"stderr\", \"csvlog\", and \"jsonlog\".")));
855 : }
856 :
857 12 : fd = AllocateFile(LOG_METAINFO_DATAFILE, "r");
858 12 : if (fd == NULL)
859 : {
860 0 : if (errno != ENOENT)
861 0 : ereport(ERROR,
862 : (errcode_for_file_access(),
863 : errmsg("could not read file \"%s\": %m",
864 : LOG_METAINFO_DATAFILE)));
865 0 : PG_RETURN_NULL();
866 : }
867 :
868 : #ifdef WIN32
869 : /* syslogger.c writes CRLF line endings on Windows */
870 : _setmode(_fileno(fd), _O_TEXT);
871 : #endif
872 :
873 : /*
874 : * Read the file to gather current log filename(s) registered by the
875 : * syslogger.
876 : */
877 24 : while (fgets(lbuffer, sizeof(lbuffer), fd) != NULL)
878 : {
879 : char *log_format;
880 : char *log_filepath;
881 : char *nlpos;
882 :
883 : /* Extract log format and log file path from the line. */
884 24 : log_format = lbuffer;
885 24 : log_filepath = strchr(lbuffer, ' ');
886 24 : if (log_filepath == NULL)
887 : {
888 : /* Uh oh. No space found, so file content is corrupted. */
889 0 : elog(ERROR,
890 : "missing space character in \"%s\"", LOG_METAINFO_DATAFILE);
891 : break;
892 : }
893 :
894 24 : *log_filepath = '\0';
895 24 : log_filepath++;
896 24 : nlpos = strchr(log_filepath, '\n');
897 24 : if (nlpos == NULL)
898 : {
899 : /* Uh oh. No newline found, so file content is corrupted. */
900 0 : elog(ERROR,
901 : "missing newline character in \"%s\"", LOG_METAINFO_DATAFILE);
902 : break;
903 : }
904 24 : *nlpos = '\0';
905 :
906 24 : if (logfmt == NULL || strcmp(logfmt, log_format) == 0)
907 : {
908 12 : FreeFile(fd);
909 12 : PG_RETURN_TEXT_P(cstring_to_text(log_filepath));
910 : }
911 : }
912 :
913 : /* Close the current log filename file. */
914 0 : FreeFile(fd);
915 :
916 0 : PG_RETURN_NULL();
917 : }
918 :
919 : /*
920 : * Report current log file used by log collector (1 argument version)
921 : *
922 : * note: this wrapper is necessary to pass the sanity check in opr_sanity,
923 : * which checks that all built-in functions that share the implementing C
924 : * function take the same number of arguments
925 : */
926 : Datum
927 12 : pg_current_logfile_1arg(PG_FUNCTION_ARGS)
928 : {
929 12 : return pg_current_logfile(fcinfo);
930 : }
931 :
932 : /*
933 : * SQL wrapper around RelationGetReplicaIndex().
934 : */
935 : Datum
936 528 : pg_get_replica_identity_index(PG_FUNCTION_ARGS)
937 : {
938 528 : Oid reloid = PG_GETARG_OID(0);
939 : Oid idxoid;
940 : Relation rel;
941 :
942 528 : rel = table_open(reloid, AccessShareLock);
943 528 : idxoid = RelationGetReplicaIndex(rel);
944 528 : table_close(rel, AccessShareLock);
945 :
946 528 : if (OidIsValid(idxoid))
947 344 : PG_RETURN_OID(idxoid);
948 : else
949 184 : PG_RETURN_NULL();
950 : }
|