Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * regproc.c
4 : * Functions for the built-in types regproc, regclass, regtype, etc.
5 : *
6 : * These types are all binary-compatible with type Oid, and rely on Oid
7 : * for comparison and so forth. Their only interesting behavior is in
8 : * special I/O conversion routines.
9 : *
10 : *
11 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
12 : * Portions Copyright (c) 1994, Regents of the University of California
13 : *
14 : *
15 : * IDENTIFICATION
16 : * src/backend/utils/adt/regproc.c
17 : *
18 : *-------------------------------------------------------------------------
19 : */
20 : #include "postgres.h"
21 :
22 : #include <ctype.h>
23 :
24 : #include "access/htup_details.h"
25 : #include "catalog/namespace.h"
26 : #include "catalog/pg_class.h"
27 : #include "catalog/pg_collation.h"
28 : #include "catalog/pg_operator.h"
29 : #include "catalog/pg_proc.h"
30 : #include "catalog/pg_ts_config.h"
31 : #include "catalog/pg_ts_dict.h"
32 : #include "catalog/pg_type.h"
33 : #include "commands/dbcommands.h"
34 : #include "lib/stringinfo.h"
35 : #include "mb/pg_wchar.h"
36 : #include "miscadmin.h"
37 : #include "nodes/miscnodes.h"
38 : #include "parser/parse_type.h"
39 : #include "parser/scansup.h"
40 : #include "utils/acl.h"
41 : #include "utils/builtins.h"
42 : #include "utils/lsyscache.h"
43 : #include "utils/regproc.h"
44 : #include "utils/syscache.h"
45 : #include "utils/varlena.h"
46 :
47 : static bool parseNumericOid(char *string, Oid *result, Node *escontext);
48 : static bool parseDashOrOid(char *string, Oid *result, Node *escontext);
49 : static bool parseNameAndArgTypes(const char *string, bool allowNone,
50 : List **names, int *nargs, Oid *argtypes,
51 : Node *escontext);
52 :
53 :
54 : /*****************************************************************************
55 : * USER I/O ROUTINES *
56 : *****************************************************************************/
57 :
58 : /*
59 : * regprocin - converts "proname" to proc OID
60 : *
61 : * We also accept a numeric OID, for symmetry with the output routine.
62 : *
63 : * '-' signifies unknown (OID 0). In all other cases, the input must
64 : * match an existing pg_proc entry.
65 : */
66 : Datum
67 969388 : regprocin(PG_FUNCTION_ARGS)
68 : {
69 969388 : char *pro_name_or_oid = PG_GETARG_CSTRING(0);
70 969388 : Node *escontext = fcinfo->context;
71 : RegProcedure result;
72 : List *names;
73 : FuncCandidateList clist;
74 :
75 : /* Handle "-" or numeric OID */
76 969388 : if (parseDashOrOid(pro_name_or_oid, &result, escontext))
77 968662 : PG_RETURN_OID(result);
78 :
79 : /* Else it's a name, possibly schema-qualified */
80 :
81 : /*
82 : * We should never get here in bootstrap mode, as all references should
83 : * have been resolved by genbki.pl.
84 : */
85 726 : if (IsBootstrapProcessingMode())
86 0 : elog(ERROR, "regproc values must be OIDs in bootstrap mode");
87 :
88 : /*
89 : * Normal case: parse the name into components and see if it matches any
90 : * pg_proc entries in the current search path.
91 : */
92 726 : names = stringToQualifiedNameList(pro_name_or_oid, escontext);
93 726 : if (names == NIL)
94 0 : PG_RETURN_NULL();
95 :
96 726 : clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, true);
97 :
98 726 : if (clist == NULL)
99 30 : ereturn(escontext, (Datum) 0,
100 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
101 : errmsg("function \"%s\" does not exist", pro_name_or_oid)));
102 696 : else if (clist->next != NULL)
103 0 : ereturn(escontext, (Datum) 0,
104 : (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
105 : errmsg("more than one function named \"%s\"",
106 : pro_name_or_oid)));
107 :
108 696 : result = clist->oid;
109 :
110 696 : PG_RETURN_OID(result);
111 : }
112 :
113 : /*
114 : * to_regproc - converts "proname" to proc OID
115 : *
116 : * If the name is not found, we return NULL.
117 : */
118 : Datum
119 30 : to_regproc(PG_FUNCTION_ARGS)
120 : {
121 30 : char *pro_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
122 : Datum result;
123 30 : ErrorSaveContext escontext = {T_ErrorSaveContext};
124 :
125 30 : if (!DirectInputFunctionCallSafe(regprocin, pro_name,
126 : InvalidOid, -1,
127 : (Node *) &escontext,
128 : &result))
129 12 : PG_RETURN_NULL();
130 18 : PG_RETURN_DATUM(result);
131 : }
132 :
133 : /*
134 : * regprocout - converts proc OID to "pro_name"
135 : */
136 : Datum
137 169828 : regprocout(PG_FUNCTION_ARGS)
138 : {
139 169828 : RegProcedure proid = PG_GETARG_OID(0);
140 : char *result;
141 : HeapTuple proctup;
142 :
143 169828 : if (proid == InvalidOid)
144 : {
145 77688 : result = pstrdup("-");
146 77688 : PG_RETURN_CSTRING(result);
147 : }
148 :
149 92140 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(proid));
150 :
151 92140 : if (HeapTupleIsValid(proctup))
152 : {
153 92140 : Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
154 92140 : char *proname = NameStr(procform->proname);
155 :
156 : /*
157 : * In bootstrap mode, skip the fancy namespace stuff and just return
158 : * the proc name. (This path is only needed for debugging output
159 : * anyway.)
160 : */
161 92140 : if (IsBootstrapProcessingMode())
162 0 : result = pstrdup(proname);
163 : else
164 : {
165 : char *nspname;
166 : FuncCandidateList clist;
167 :
168 : /*
169 : * Would this proc be found (uniquely!) by regprocin? If not,
170 : * qualify it.
171 : */
172 92140 : clist = FuncnameGetCandidates(list_make1(makeString(proname)),
173 : -1, NIL, false, false, false, false);
174 92140 : if (clist != NULL && clist->next == NULL &&
175 88500 : clist->oid == proid)
176 88500 : nspname = NULL;
177 : else
178 3640 : nspname = get_namespace_name(procform->pronamespace);
179 :
180 92140 : result = quote_qualified_identifier(nspname, proname);
181 : }
182 :
183 92140 : ReleaseSysCache(proctup);
184 : }
185 : else
186 : {
187 : /* If OID doesn't match any pg_proc entry, return it numerically */
188 0 : result = (char *) palloc(NAMEDATALEN);
189 0 : snprintf(result, NAMEDATALEN, "%u", proid);
190 : }
191 :
192 92140 : PG_RETURN_CSTRING(result);
193 : }
194 :
195 : /*
196 : * regprocrecv - converts external binary format to regproc
197 : */
198 : Datum
199 0 : regprocrecv(PG_FUNCTION_ARGS)
200 : {
201 : /* Exactly the same as oidrecv, so share code */
202 0 : return oidrecv(fcinfo);
203 : }
204 :
205 : /*
206 : * regprocsend - converts regproc to binary format
207 : */
208 : Datum
209 0 : regprocsend(PG_FUNCTION_ARGS)
210 : {
211 : /* Exactly the same as oidsend, so share code */
212 0 : return oidsend(fcinfo);
213 : }
214 :
215 :
216 : /*
217 : * regprocedurein - converts "proname(args)" to proc OID
218 : *
219 : * We also accept a numeric OID, for symmetry with the output routine.
220 : *
221 : * '-' signifies unknown (OID 0). In all other cases, the input must
222 : * match an existing pg_proc entry.
223 : */
224 : Datum
225 514 : regprocedurein(PG_FUNCTION_ARGS)
226 : {
227 514 : char *pro_name_or_oid = PG_GETARG_CSTRING(0);
228 514 : Node *escontext = fcinfo->context;
229 : RegProcedure result;
230 : List *names;
231 : int nargs;
232 : Oid argtypes[FUNC_MAX_ARGS];
233 : FuncCandidateList clist;
234 :
235 : /* Handle "-" or numeric OID */
236 514 : if (parseDashOrOid(pro_name_or_oid, &result, escontext))
237 14 : PG_RETURN_OID(result);
238 :
239 : /* The rest of this wouldn't work in bootstrap mode */
240 500 : if (IsBootstrapProcessingMode())
241 0 : elog(ERROR, "regprocedure values must be OIDs in bootstrap mode");
242 :
243 : /*
244 : * Else it's a name and arguments. Parse the name and arguments, look up
245 : * potential matches in the current namespace search list, and scan to see
246 : * which one exactly matches the given argument types. (There will not be
247 : * more than one match.)
248 : */
249 500 : if (!parseNameAndArgTypes(pro_name_or_oid, false,
250 : &names, &nargs, argtypes,
251 : escontext))
252 6 : PG_RETURN_NULL();
253 :
254 494 : clist = FuncnameGetCandidates(names, nargs, NIL, false, false,
255 : false, true);
256 :
257 494 : for (; clist; clist = clist->next)
258 : {
259 464 : if (memcmp(clist->args, argtypes, nargs * sizeof(Oid)) == 0)
260 464 : break;
261 : }
262 :
263 494 : if (clist == NULL)
264 30 : ereturn(escontext, (Datum) 0,
265 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
266 : errmsg("function \"%s\" does not exist", pro_name_or_oid)));
267 :
268 464 : result = clist->oid;
269 :
270 464 : PG_RETURN_OID(result);
271 : }
272 :
273 : /*
274 : * to_regprocedure - converts "proname(args)" to proc OID
275 : *
276 : * If the name is not found, we return NULL.
277 : */
278 : Datum
279 30 : to_regprocedure(PG_FUNCTION_ARGS)
280 : {
281 30 : char *pro_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
282 : Datum result;
283 30 : ErrorSaveContext escontext = {T_ErrorSaveContext};
284 :
285 30 : if (!DirectInputFunctionCallSafe(regprocedurein, pro_name,
286 : InvalidOid, -1,
287 : (Node *) &escontext,
288 : &result))
289 12 : PG_RETURN_NULL();
290 18 : PG_RETURN_DATUM(result);
291 : }
292 :
293 : /*
294 : * format_procedure - converts proc OID to "pro_name(args)"
295 : *
296 : * This exports the useful functionality of regprocedureout for use
297 : * in other backend modules. The result is a palloc'd string.
298 : */
299 : char *
300 22316 : format_procedure(Oid procedure_oid)
301 : {
302 22316 : return format_procedure_extended(procedure_oid, 0);
303 : }
304 :
305 : char *
306 0 : format_procedure_qualified(Oid procedure_oid)
307 : {
308 0 : return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY);
309 : }
310 :
311 : /*
312 : * format_procedure_extended - converts procedure OID to "pro_name(args)"
313 : *
314 : * This exports the useful functionality of regprocedureout for use
315 : * in other backend modules. The result is a palloc'd string, or NULL.
316 : *
317 : * Routine to produce regprocedure names; see format_procedure above.
318 : *
319 : * The following bits in 'flags' modify the behavior:
320 : * - FORMAT_PROC_INVALID_AS_NULL
321 : * if the procedure OID is invalid or unknown, return NULL instead
322 : * of the numeric OID.
323 : * - FORMAT_PROC_FORCE_QUALIFY
324 : * always schema-qualify procedure names, regardless of search_path
325 : */
326 : char *
327 26260 : format_procedure_extended(Oid procedure_oid, bits16 flags)
328 : {
329 : char *result;
330 : HeapTuple proctup;
331 :
332 26260 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
333 :
334 26260 : if (HeapTupleIsValid(proctup))
335 : {
336 26242 : Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
337 26242 : char *proname = NameStr(procform->proname);
338 26242 : int nargs = procform->pronargs;
339 : int i;
340 : char *nspname;
341 : StringInfoData buf;
342 :
343 : /* XXX no support here for bootstrap mode */
344 : Assert(!IsBootstrapProcessingMode());
345 :
346 26242 : initStringInfo(&buf);
347 :
348 : /*
349 : * Would this proc be found (given the right args) by regprocedurein?
350 : * If not, or if caller requests it, we need to qualify it.
351 : */
352 52204 : if ((flags & FORMAT_PROC_FORCE_QUALIFY) == 0 &&
353 25962 : FunctionIsVisible(procedure_oid))
354 25086 : nspname = NULL;
355 : else
356 1156 : nspname = get_namespace_name(procform->pronamespace);
357 :
358 26242 : appendStringInfo(&buf, "%s(",
359 : quote_qualified_identifier(nspname, proname));
360 79222 : for (i = 0; i < nargs; i++)
361 : {
362 52980 : Oid thisargtype = procform->proargtypes.values[i];
363 :
364 52980 : if (i > 0)
365 32950 : appendStringInfoChar(&buf, ',');
366 52980 : appendStringInfoString(&buf,
367 52980 : (flags & FORMAT_PROC_FORCE_QUALIFY) != 0 ?
368 344 : format_type_be_qualified(thisargtype) :
369 52636 : format_type_be(thisargtype));
370 : }
371 26242 : appendStringInfoChar(&buf, ')');
372 :
373 26242 : result = buf.data;
374 :
375 26242 : ReleaseSysCache(proctup);
376 : }
377 18 : else if ((flags & FORMAT_PROC_INVALID_AS_NULL) != 0)
378 : {
379 : /* If object is undefined, return NULL as wanted by caller */
380 18 : result = NULL;
381 : }
382 : else
383 : {
384 : /* If OID doesn't match any pg_proc entry, return it numerically */
385 0 : result = (char *) palloc(NAMEDATALEN);
386 0 : snprintf(result, NAMEDATALEN, "%u", procedure_oid);
387 : }
388 :
389 26260 : return result;
390 : }
391 :
392 : /*
393 : * Output an objname/objargs representation for the procedure with the
394 : * given OID. If it doesn't exist, an error is thrown.
395 : *
396 : * This can be used to feed get_object_address.
397 : */
398 : void
399 126 : format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs,
400 : bool missing_ok)
401 : {
402 : HeapTuple proctup;
403 : Form_pg_proc procform;
404 : int nargs;
405 : int i;
406 :
407 126 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
408 :
409 126 : if (!HeapTupleIsValid(proctup))
410 : {
411 0 : if (!missing_ok)
412 0 : elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
413 0 : return;
414 : }
415 :
416 126 : procform = (Form_pg_proc) GETSTRUCT(proctup);
417 126 : nargs = procform->pronargs;
418 :
419 126 : *objnames = list_make2(get_namespace_name_or_temp(procform->pronamespace),
420 : pstrdup(NameStr(procform->proname)));
421 126 : *objargs = NIL;
422 244 : for (i = 0; i < nargs; i++)
423 : {
424 118 : Oid thisargtype = procform->proargtypes.values[i];
425 :
426 118 : *objargs = lappend(*objargs, format_type_be_qualified(thisargtype));
427 : }
428 :
429 126 : ReleaseSysCache(proctup);
430 : }
431 :
432 : /*
433 : * regprocedureout - converts proc OID to "pro_name(args)"
434 : */
435 : Datum
436 17786 : regprocedureout(PG_FUNCTION_ARGS)
437 : {
438 17786 : RegProcedure proid = PG_GETARG_OID(0);
439 : char *result;
440 :
441 17786 : if (proid == InvalidOid)
442 3980 : result = pstrdup("-");
443 : else
444 13806 : result = format_procedure(proid);
445 :
446 17786 : PG_RETURN_CSTRING(result);
447 : }
448 :
449 : /*
450 : * regprocedurerecv - converts external binary format to regprocedure
451 : */
452 : Datum
453 0 : regprocedurerecv(PG_FUNCTION_ARGS)
454 : {
455 : /* Exactly the same as oidrecv, so share code */
456 0 : return oidrecv(fcinfo);
457 : }
458 :
459 : /*
460 : * regproceduresend - converts regprocedure to binary format
461 : */
462 : Datum
463 0 : regproceduresend(PG_FUNCTION_ARGS)
464 : {
465 : /* Exactly the same as oidsend, so share code */
466 0 : return oidsend(fcinfo);
467 : }
468 :
469 :
470 : /*
471 : * regoperin - converts "oprname" to operator OID
472 : *
473 : * We also accept a numeric OID, for symmetry with the output routine.
474 : *
475 : * '0' signifies unknown (OID 0). In all other cases, the input must
476 : * match an existing pg_operator entry.
477 : */
478 : Datum
479 60 : regoperin(PG_FUNCTION_ARGS)
480 : {
481 60 : char *opr_name_or_oid = PG_GETARG_CSTRING(0);
482 60 : Node *escontext = fcinfo->context;
483 : Oid result;
484 : List *names;
485 : FuncCandidateList clist;
486 :
487 : /* Handle "0" or numeric OID */
488 60 : if (parseNumericOid(opr_name_or_oid, &result, escontext))
489 0 : PG_RETURN_OID(result);
490 :
491 : /* Else it's a name, possibly schema-qualified */
492 :
493 : /* The rest of this wouldn't work in bootstrap mode */
494 60 : if (IsBootstrapProcessingMode())
495 0 : elog(ERROR, "regoper values must be OIDs in bootstrap mode");
496 :
497 : /*
498 : * Normal case: parse the name into components and see if it matches any
499 : * pg_operator entries in the current search path.
500 : */
501 60 : names = stringToQualifiedNameList(opr_name_or_oid, escontext);
502 60 : if (names == NIL)
503 0 : PG_RETURN_NULL();
504 :
505 60 : clist = OpernameGetCandidates(names, '\0', true);
506 :
507 60 : if (clist == NULL)
508 30 : ereturn(escontext, (Datum) 0,
509 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
510 : errmsg("operator does not exist: %s", opr_name_or_oid)));
511 30 : else if (clist->next != NULL)
512 6 : ereturn(escontext, (Datum) 0,
513 : (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
514 : errmsg("more than one operator named %s",
515 : opr_name_or_oid)));
516 :
517 24 : result = clist->oid;
518 :
519 24 : PG_RETURN_OID(result);
520 : }
521 :
522 : /*
523 : * to_regoper - converts "oprname" to operator OID
524 : *
525 : * If the name is not found, we return NULL.
526 : */
527 : Datum
528 24 : to_regoper(PG_FUNCTION_ARGS)
529 : {
530 24 : char *opr_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
531 : Datum result;
532 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
533 :
534 24 : if (!DirectInputFunctionCallSafe(regoperin, opr_name,
535 : InvalidOid, -1,
536 : (Node *) &escontext,
537 : &result))
538 12 : PG_RETURN_NULL();
539 12 : PG_RETURN_DATUM(result);
540 : }
541 :
542 : /*
543 : * regoperout - converts operator OID to "opr_name"
544 : */
545 : Datum
546 24 : regoperout(PG_FUNCTION_ARGS)
547 : {
548 24 : Oid oprid = PG_GETARG_OID(0);
549 : char *result;
550 : HeapTuple opertup;
551 :
552 24 : if (oprid == InvalidOid)
553 : {
554 0 : result = pstrdup("0");
555 0 : PG_RETURN_CSTRING(result);
556 : }
557 :
558 24 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
559 :
560 24 : if (HeapTupleIsValid(opertup))
561 : {
562 24 : Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
563 24 : char *oprname = NameStr(operform->oprname);
564 :
565 : /*
566 : * In bootstrap mode, skip the fancy namespace stuff and just return
567 : * the oper name. (This path is only needed for debugging output
568 : * anyway.)
569 : */
570 24 : if (IsBootstrapProcessingMode())
571 0 : result = pstrdup(oprname);
572 : else
573 : {
574 : FuncCandidateList clist;
575 :
576 : /*
577 : * Would this oper be found (uniquely!) by regoperin? If not,
578 : * qualify it.
579 : */
580 24 : clist = OpernameGetCandidates(list_make1(makeString(oprname)),
581 : '\0', false);
582 24 : if (clist != NULL && clist->next == NULL &&
583 24 : clist->oid == oprid)
584 24 : result = pstrdup(oprname);
585 : else
586 : {
587 : const char *nspname;
588 :
589 0 : nspname = get_namespace_name(operform->oprnamespace);
590 0 : nspname = quote_identifier(nspname);
591 0 : result = (char *) palloc(strlen(nspname) + strlen(oprname) + 2);
592 0 : sprintf(result, "%s.%s", nspname, oprname);
593 : }
594 : }
595 :
596 24 : ReleaseSysCache(opertup);
597 : }
598 : else
599 : {
600 : /*
601 : * If OID doesn't match any pg_operator entry, return it numerically
602 : */
603 0 : result = (char *) palloc(NAMEDATALEN);
604 0 : snprintf(result, NAMEDATALEN, "%u", oprid);
605 : }
606 :
607 24 : PG_RETURN_CSTRING(result);
608 : }
609 :
610 : /*
611 : * regoperrecv - converts external binary format to regoper
612 : */
613 : Datum
614 0 : regoperrecv(PG_FUNCTION_ARGS)
615 : {
616 : /* Exactly the same as oidrecv, so share code */
617 0 : return oidrecv(fcinfo);
618 : }
619 :
620 : /*
621 : * regopersend - converts regoper to binary format
622 : */
623 : Datum
624 0 : regopersend(PG_FUNCTION_ARGS)
625 : {
626 : /* Exactly the same as oidsend, so share code */
627 0 : return oidsend(fcinfo);
628 : }
629 :
630 :
631 : /*
632 : * regoperatorin - converts "oprname(args)" to operator OID
633 : *
634 : * We also accept a numeric OID, for symmetry with the output routine.
635 : *
636 : * '0' signifies unknown (OID 0). In all other cases, the input must
637 : * match an existing pg_operator entry.
638 : */
639 : Datum
640 84 : regoperatorin(PG_FUNCTION_ARGS)
641 : {
642 84 : char *opr_name_or_oid = PG_GETARG_CSTRING(0);
643 84 : Node *escontext = fcinfo->context;
644 : Oid result;
645 : List *names;
646 : int nargs;
647 : Oid argtypes[FUNC_MAX_ARGS];
648 :
649 : /* Handle "0" or numeric OID */
650 84 : if (parseNumericOid(opr_name_or_oid, &result, escontext))
651 0 : PG_RETURN_OID(result);
652 :
653 : /* The rest of this wouldn't work in bootstrap mode */
654 84 : if (IsBootstrapProcessingMode())
655 0 : elog(ERROR, "regoperator values must be OIDs in bootstrap mode");
656 :
657 : /*
658 : * Else it's a name and arguments. Parse the name and arguments, look up
659 : * potential matches in the current namespace search list, and scan to see
660 : * which one exactly matches the given argument types. (There will not be
661 : * more than one match.)
662 : */
663 84 : if (!parseNameAndArgTypes(opr_name_or_oid, true,
664 : &names, &nargs, argtypes,
665 : escontext))
666 6 : PG_RETURN_NULL();
667 :
668 78 : if (nargs == 1)
669 0 : ereturn(escontext, (Datum) 0,
670 : (errcode(ERRCODE_UNDEFINED_PARAMETER),
671 : errmsg("missing argument"),
672 : errhint("Use NONE to denote the missing argument of a unary operator.")));
673 78 : if (nargs != 2)
674 0 : ereturn(escontext, (Datum) 0,
675 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
676 : errmsg("too many arguments"),
677 : errhint("Provide two argument types for operator.")));
678 :
679 78 : result = OpernameGetOprid(names, argtypes[0], argtypes[1]);
680 :
681 78 : if (!OidIsValid(result))
682 30 : ereturn(escontext, (Datum) 0,
683 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
684 : errmsg("operator does not exist: %s", opr_name_or_oid)));
685 :
686 48 : PG_RETURN_OID(result);
687 : }
688 :
689 : /*
690 : * to_regoperator - converts "oprname(args)" to operator OID
691 : *
692 : * If the name is not found, we return NULL.
693 : */
694 : Datum
695 18 : to_regoperator(PG_FUNCTION_ARGS)
696 : {
697 18 : char *opr_name_or_oid = text_to_cstring(PG_GETARG_TEXT_PP(0));
698 : Datum result;
699 18 : ErrorSaveContext escontext = {T_ErrorSaveContext};
700 :
701 18 : if (!DirectInputFunctionCallSafe(regoperatorin, opr_name_or_oid,
702 : InvalidOid, -1,
703 : (Node *) &escontext,
704 : &result))
705 12 : PG_RETURN_NULL();
706 6 : PG_RETURN_DATUM(result);
707 : }
708 :
709 : /*
710 : * format_operator_extended - converts operator OID to "opr_name(args)"
711 : *
712 : * This exports the useful functionality of regoperatorout for use
713 : * in other backend modules. The result is a palloc'd string, or NULL.
714 : *
715 : * The following bits in 'flags' modify the behavior:
716 : * - FORMAT_OPERATOR_INVALID_AS_NULL
717 : * if the operator OID is invalid or unknown, return NULL instead
718 : * of the numeric OID.
719 : * - FORMAT_OPERATOR_FORCE_QUALIFY
720 : * always schema-qualify operator names, regardless of search_path
721 : */
722 : char *
723 2748 : format_operator_extended(Oid operator_oid, bits16 flags)
724 : {
725 : char *result;
726 : HeapTuple opertup;
727 :
728 2748 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
729 :
730 2748 : if (HeapTupleIsValid(opertup))
731 : {
732 2730 : Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
733 2730 : char *oprname = NameStr(operform->oprname);
734 : char *nspname;
735 : StringInfoData buf;
736 :
737 : /* XXX no support here for bootstrap mode */
738 : Assert(!IsBootstrapProcessingMode());
739 :
740 2730 : initStringInfo(&buf);
741 :
742 : /*
743 : * Would this oper be found (given the right args) by regoperatorin?
744 : * If not, or if caller explicitly requests it, we need to qualify it.
745 : */
746 2730 : if ((flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ||
747 2684 : !OperatorIsVisible(operator_oid))
748 : {
749 360 : nspname = get_namespace_name(operform->oprnamespace);
750 360 : appendStringInfo(&buf, "%s.",
751 : quote_identifier(nspname));
752 : }
753 :
754 2730 : appendStringInfo(&buf, "%s(", oprname);
755 :
756 2730 : if (operform->oprleft)
757 2718 : appendStringInfo(&buf, "%s,",
758 2718 : (flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
759 46 : format_type_be_qualified(operform->oprleft) :
760 2672 : format_type_be(operform->oprleft));
761 : else
762 12 : appendStringInfoString(&buf, "NONE,");
763 :
764 2730 : if (operform->oprright)
765 2730 : appendStringInfo(&buf, "%s)",
766 2730 : (flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
767 46 : format_type_be_qualified(operform->oprright) :
768 2684 : format_type_be(operform->oprright));
769 : else
770 0 : appendStringInfoString(&buf, "NONE)");
771 :
772 2730 : result = buf.data;
773 :
774 2730 : ReleaseSysCache(opertup);
775 : }
776 18 : else if ((flags & FORMAT_OPERATOR_INVALID_AS_NULL) != 0)
777 : {
778 : /* If object is undefined, return NULL as wanted by caller */
779 18 : result = NULL;
780 : }
781 : else
782 : {
783 : /*
784 : * If OID doesn't match any pg_operator entry, return it numerically
785 : */
786 0 : result = (char *) palloc(NAMEDATALEN);
787 0 : snprintf(result, NAMEDATALEN, "%u", operator_oid);
788 : }
789 :
790 2748 : return result;
791 : }
792 :
793 : char *
794 1966 : format_operator(Oid operator_oid)
795 : {
796 1966 : return format_operator_extended(operator_oid, 0);
797 : }
798 :
799 : char *
800 0 : format_operator_qualified(Oid operator_oid)
801 : {
802 0 : return format_operator_extended(operator_oid,
803 : FORMAT_OPERATOR_FORCE_QUALIFY);
804 : }
805 :
806 : void
807 6 : format_operator_parts(Oid operator_oid, List **objnames, List **objargs,
808 : bool missing_ok)
809 : {
810 : HeapTuple opertup;
811 : Form_pg_operator oprForm;
812 :
813 6 : opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
814 6 : if (!HeapTupleIsValid(opertup))
815 : {
816 0 : if (!missing_ok)
817 0 : elog(ERROR, "cache lookup failed for operator with OID %u",
818 : operator_oid);
819 0 : return;
820 : }
821 :
822 6 : oprForm = (Form_pg_operator) GETSTRUCT(opertup);
823 6 : *objnames = list_make2(get_namespace_name_or_temp(oprForm->oprnamespace),
824 : pstrdup(NameStr(oprForm->oprname)));
825 6 : *objargs = NIL;
826 6 : if (oprForm->oprleft)
827 6 : *objargs = lappend(*objargs,
828 6 : format_type_be_qualified(oprForm->oprleft));
829 6 : if (oprForm->oprright)
830 6 : *objargs = lappend(*objargs,
831 6 : format_type_be_qualified(oprForm->oprright));
832 :
833 6 : ReleaseSysCache(opertup);
834 : }
835 :
836 : /*
837 : * regoperatorout - converts operator OID to "opr_name(args)"
838 : */
839 : Datum
840 1010 : regoperatorout(PG_FUNCTION_ARGS)
841 : {
842 1010 : Oid oprid = PG_GETARG_OID(0);
843 : char *result;
844 :
845 1010 : if (oprid == InvalidOid)
846 0 : result = pstrdup("0");
847 : else
848 1010 : result = format_operator(oprid);
849 :
850 1010 : PG_RETURN_CSTRING(result);
851 : }
852 :
853 : /*
854 : * regoperatorrecv - converts external binary format to regoperator
855 : */
856 : Datum
857 0 : regoperatorrecv(PG_FUNCTION_ARGS)
858 : {
859 : /* Exactly the same as oidrecv, so share code */
860 0 : return oidrecv(fcinfo);
861 : }
862 :
863 : /*
864 : * regoperatorsend - converts regoperator to binary format
865 : */
866 : Datum
867 0 : regoperatorsend(PG_FUNCTION_ARGS)
868 : {
869 : /* Exactly the same as oidsend, so share code */
870 0 : return oidsend(fcinfo);
871 : }
872 :
873 :
874 : /*
875 : * regclassin - converts "classname" to class OID
876 : *
877 : * We also accept a numeric OID, for symmetry with the output routine.
878 : *
879 : * '-' signifies unknown (OID 0). In all other cases, the input must
880 : * match an existing pg_class entry.
881 : */
882 : Datum
883 49256 : regclassin(PG_FUNCTION_ARGS)
884 : {
885 49256 : char *class_name_or_oid = PG_GETARG_CSTRING(0);
886 49256 : Node *escontext = fcinfo->context;
887 : Oid result;
888 : List *names;
889 :
890 : /* Handle "-" or numeric OID */
891 49256 : if (parseDashOrOid(class_name_or_oid, &result, escontext))
892 14984 : PG_RETURN_OID(result);
893 :
894 : /* Else it's a name, possibly schema-qualified */
895 :
896 : /* The rest of this wouldn't work in bootstrap mode */
897 34272 : if (IsBootstrapProcessingMode())
898 0 : elog(ERROR, "regclass values must be OIDs in bootstrap mode");
899 :
900 : /*
901 : * Normal case: parse the name into components and see if it matches any
902 : * pg_class entries in the current search path.
903 : */
904 34272 : names = stringToQualifiedNameList(class_name_or_oid, escontext);
905 34272 : if (names == NIL)
906 0 : PG_RETURN_NULL();
907 :
908 : /* We might not even have permissions on this relation; don't lock it. */
909 34272 : result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, true);
910 :
911 34272 : if (!OidIsValid(result))
912 40 : ereturn(escontext, (Datum) 0,
913 : (errcode(ERRCODE_UNDEFINED_TABLE),
914 : errmsg("relation \"%s\" does not exist",
915 : NameListToString(names))));
916 :
917 34232 : PG_RETURN_OID(result);
918 : }
919 :
920 : /*
921 : * to_regclass - converts "classname" to class OID
922 : *
923 : * If the name is not found, we return NULL.
924 : */
925 : Datum
926 30 : to_regclass(PG_FUNCTION_ARGS)
927 : {
928 30 : char *class_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
929 : Datum result;
930 30 : ErrorSaveContext escontext = {T_ErrorSaveContext};
931 :
932 30 : if (!DirectInputFunctionCallSafe(regclassin, class_name,
933 : InvalidOid, -1,
934 : (Node *) &escontext,
935 : &result))
936 12 : PG_RETURN_NULL();
937 18 : PG_RETURN_DATUM(result);
938 : }
939 :
940 : /*
941 : * regclassout - converts class OID to "class_name"
942 : */
943 : Datum
944 209150 : regclassout(PG_FUNCTION_ARGS)
945 : {
946 209150 : Oid classid = PG_GETARG_OID(0);
947 : char *result;
948 : HeapTuple classtup;
949 :
950 209150 : if (classid == InvalidOid)
951 : {
952 498 : result = pstrdup("-");
953 498 : PG_RETURN_CSTRING(result);
954 : }
955 :
956 208652 : classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(classid));
957 :
958 208652 : if (HeapTupleIsValid(classtup))
959 : {
960 208180 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(classtup);
961 208180 : char *classname = NameStr(classform->relname);
962 :
963 : /*
964 : * In bootstrap mode, skip the fancy namespace stuff and just return
965 : * the class name. (This path is only needed for debugging output
966 : * anyway.)
967 : */
968 208180 : if (IsBootstrapProcessingMode())
969 0 : result = pstrdup(classname);
970 : else
971 : {
972 : char *nspname;
973 :
974 : /*
975 : * Would this class be found by regclassin? If not, qualify it.
976 : */
977 208180 : if (RelationIsVisible(classid))
978 136266 : nspname = NULL;
979 : else
980 71914 : nspname = get_namespace_name(classform->relnamespace);
981 :
982 208180 : result = quote_qualified_identifier(nspname, classname);
983 : }
984 :
985 208180 : ReleaseSysCache(classtup);
986 : }
987 : else
988 : {
989 : /* If OID doesn't match any pg_class entry, return it numerically */
990 472 : result = (char *) palloc(NAMEDATALEN);
991 472 : snprintf(result, NAMEDATALEN, "%u", classid);
992 : }
993 :
994 208652 : PG_RETURN_CSTRING(result);
995 : }
996 :
997 : /*
998 : * regclassrecv - converts external binary format to regclass
999 : */
1000 : Datum
1001 0 : regclassrecv(PG_FUNCTION_ARGS)
1002 : {
1003 : /* Exactly the same as oidrecv, so share code */
1004 0 : return oidrecv(fcinfo);
1005 : }
1006 :
1007 : /*
1008 : * regclasssend - converts regclass to binary format
1009 : */
1010 : Datum
1011 0 : regclasssend(PG_FUNCTION_ARGS)
1012 : {
1013 : /* Exactly the same as oidsend, so share code */
1014 0 : return oidsend(fcinfo);
1015 : }
1016 :
1017 :
1018 : /*
1019 : * regcollationin - converts "collationname" to collation OID
1020 : *
1021 : * We also accept a numeric OID, for symmetry with the output routine.
1022 : *
1023 : * '-' signifies unknown (OID 0). In all other cases, the input must
1024 : * match an existing pg_collation entry.
1025 : */
1026 : Datum
1027 60 : regcollationin(PG_FUNCTION_ARGS)
1028 : {
1029 60 : char *collation_name_or_oid = PG_GETARG_CSTRING(0);
1030 60 : Node *escontext = fcinfo->context;
1031 : Oid result;
1032 : List *names;
1033 :
1034 : /* Handle "-" or numeric OID */
1035 60 : if (parseDashOrOid(collation_name_or_oid, &result, escontext))
1036 12 : PG_RETURN_OID(result);
1037 :
1038 : /* Else it's a name, possibly schema-qualified */
1039 :
1040 : /* The rest of this wouldn't work in bootstrap mode */
1041 48 : if (IsBootstrapProcessingMode())
1042 0 : elog(ERROR, "regcollation values must be OIDs in bootstrap mode");
1043 :
1044 : /*
1045 : * Normal case: parse the name into components and see if it matches any
1046 : * pg_collation entries in the current search path.
1047 : */
1048 48 : names = stringToQualifiedNameList(collation_name_or_oid, escontext);
1049 48 : if (names == NIL)
1050 0 : PG_RETURN_NULL();
1051 :
1052 48 : result = get_collation_oid(names, true);
1053 :
1054 48 : if (!OidIsValid(result))
1055 24 : ereturn(escontext, (Datum) 0,
1056 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1057 : errmsg("collation \"%s\" for encoding \"%s\" does not exist",
1058 : NameListToString(names), GetDatabaseEncodingName())));
1059 :
1060 24 : PG_RETURN_OID(result);
1061 : }
1062 :
1063 : /*
1064 : * to_regcollation - converts "collationname" to collation OID
1065 : *
1066 : * If the name is not found, we return NULL.
1067 : */
1068 : Datum
1069 30 : to_regcollation(PG_FUNCTION_ARGS)
1070 : {
1071 30 : char *collation_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1072 : Datum result;
1073 30 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1074 :
1075 30 : if (!DirectInputFunctionCallSafe(regcollationin, collation_name,
1076 : InvalidOid, -1,
1077 : (Node *) &escontext,
1078 : &result))
1079 12 : PG_RETURN_NULL();
1080 18 : PG_RETURN_DATUM(result);
1081 : }
1082 :
1083 : /*
1084 : * regcollationout - converts collation OID to "collation_name"
1085 : */
1086 : Datum
1087 24 : regcollationout(PG_FUNCTION_ARGS)
1088 : {
1089 24 : Oid collationid = PG_GETARG_OID(0);
1090 : char *result;
1091 : HeapTuple collationtup;
1092 :
1093 24 : if (collationid == InvalidOid)
1094 : {
1095 0 : result = pstrdup("-");
1096 0 : PG_RETURN_CSTRING(result);
1097 : }
1098 :
1099 24 : collationtup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationid));
1100 :
1101 24 : if (HeapTupleIsValid(collationtup))
1102 : {
1103 24 : Form_pg_collation collationform = (Form_pg_collation) GETSTRUCT(collationtup);
1104 24 : char *collationname = NameStr(collationform->collname);
1105 :
1106 : /*
1107 : * In bootstrap mode, skip the fancy namespace stuff and just return
1108 : * the collation name. (This path is only needed for debugging output
1109 : * anyway.)
1110 : */
1111 24 : if (IsBootstrapProcessingMode())
1112 0 : result = pstrdup(collationname);
1113 : else
1114 : {
1115 : char *nspname;
1116 :
1117 : /*
1118 : * Would this collation be found by regcollationin? If not,
1119 : * qualify it.
1120 : */
1121 24 : if (CollationIsVisible(collationid))
1122 24 : nspname = NULL;
1123 : else
1124 0 : nspname = get_namespace_name(collationform->collnamespace);
1125 :
1126 24 : result = quote_qualified_identifier(nspname, collationname);
1127 : }
1128 :
1129 24 : ReleaseSysCache(collationtup);
1130 : }
1131 : else
1132 : {
1133 : /* If OID doesn't match any pg_collation entry, return it numerically */
1134 0 : result = (char *) palloc(NAMEDATALEN);
1135 0 : snprintf(result, NAMEDATALEN, "%u", collationid);
1136 : }
1137 :
1138 24 : PG_RETURN_CSTRING(result);
1139 : }
1140 :
1141 : /*
1142 : * regcollationrecv - converts external binary format to regcollation
1143 : */
1144 : Datum
1145 0 : regcollationrecv(PG_FUNCTION_ARGS)
1146 : {
1147 : /* Exactly the same as oidrecv, so share code */
1148 0 : return oidrecv(fcinfo);
1149 : }
1150 :
1151 : /*
1152 : * regcollationsend - converts regcollation to binary format
1153 : */
1154 : Datum
1155 0 : regcollationsend(PG_FUNCTION_ARGS)
1156 : {
1157 : /* Exactly the same as oidsend, so share code */
1158 0 : return oidsend(fcinfo);
1159 : }
1160 :
1161 :
1162 : /*
1163 : * regtypein - converts "typename" to type OID
1164 : *
1165 : * The type name can be specified using the full type syntax recognized by
1166 : * the parser; for example, DOUBLE PRECISION and INTEGER[] will work and be
1167 : * translated to the correct type names. (We ignore any typmod info
1168 : * generated by the parser, however.)
1169 : *
1170 : * We also accept a numeric OID, for symmetry with the output routine,
1171 : * and for possible use in bootstrap mode.
1172 : *
1173 : * '-' signifies unknown (OID 0). In all other cases, the input must
1174 : * match an existing pg_type entry.
1175 : */
1176 : Datum
1177 1120 : regtypein(PG_FUNCTION_ARGS)
1178 : {
1179 1120 : char *typ_name_or_oid = PG_GETARG_CSTRING(0);
1180 1120 : Node *escontext = fcinfo->context;
1181 : Oid result;
1182 : int32 typmod;
1183 :
1184 : /* Handle "-" or numeric OID */
1185 1120 : if (parseDashOrOid(typ_name_or_oid, &result, escontext))
1186 32 : PG_RETURN_OID(result);
1187 :
1188 : /* Else it's a type name, possibly schema-qualified or decorated */
1189 :
1190 : /* The rest of this wouldn't work in bootstrap mode */
1191 1088 : if (IsBootstrapProcessingMode())
1192 0 : elog(ERROR, "regtype values must be OIDs in bootstrap mode");
1193 :
1194 : /*
1195 : * Normal case: invoke the full parser to deal with special cases such as
1196 : * array syntax. We don't need to check for parseTypeString failure,
1197 : * since we'll just return anyway.
1198 : */
1199 1088 : (void) parseTypeString(typ_name_or_oid, &result, &typmod, escontext);
1200 :
1201 1052 : PG_RETURN_OID(result);
1202 : }
1203 :
1204 : /*
1205 : * to_regtype - converts "typename" to type OID
1206 : *
1207 : * If the name is not found, we return NULL.
1208 : */
1209 : Datum
1210 48 : to_regtype(PG_FUNCTION_ARGS)
1211 : {
1212 48 : char *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1213 : Datum result;
1214 48 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1215 :
1216 48 : if (!DirectInputFunctionCallSafe(regtypein, typ_name,
1217 : InvalidOid, -1,
1218 : (Node *) &escontext,
1219 : &result))
1220 12 : PG_RETURN_NULL();
1221 36 : PG_RETURN_DATUM(result);
1222 : }
1223 :
1224 : /*
1225 : * to_regtypemod - converts "typename" to type modifier
1226 : *
1227 : * If the name is not found, we return NULL.
1228 : */
1229 : Datum
1230 36 : to_regtypemod(PG_FUNCTION_ARGS)
1231 : {
1232 36 : char *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1233 : Oid typid;
1234 : int32 typmod;
1235 36 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1236 :
1237 : /* We rely on parseTypeString to parse the input. */
1238 36 : if (!parseTypeString(typ_name, &typid, &typmod, (Node *) &escontext))
1239 6 : PG_RETURN_NULL();
1240 :
1241 30 : PG_RETURN_INT32(typmod);
1242 : }
1243 :
1244 : /*
1245 : * regtypeout - converts type OID to "typ_name"
1246 : */
1247 : Datum
1248 18258 : regtypeout(PG_FUNCTION_ARGS)
1249 : {
1250 18258 : Oid typid = PG_GETARG_OID(0);
1251 : char *result;
1252 : HeapTuple typetup;
1253 :
1254 18258 : if (typid == InvalidOid)
1255 : {
1256 1896 : result = pstrdup("-");
1257 1896 : PG_RETURN_CSTRING(result);
1258 : }
1259 :
1260 16362 : typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
1261 :
1262 16362 : if (HeapTupleIsValid(typetup))
1263 : {
1264 16362 : Form_pg_type typeform = (Form_pg_type) GETSTRUCT(typetup);
1265 :
1266 : /*
1267 : * In bootstrap mode, skip the fancy namespace stuff and just return
1268 : * the type name. (This path is only needed for debugging output
1269 : * anyway.)
1270 : */
1271 16362 : if (IsBootstrapProcessingMode())
1272 : {
1273 0 : char *typname = NameStr(typeform->typname);
1274 :
1275 0 : result = pstrdup(typname);
1276 : }
1277 : else
1278 16362 : result = format_type_be(typid);
1279 :
1280 16362 : ReleaseSysCache(typetup);
1281 : }
1282 : else
1283 : {
1284 : /* If OID doesn't match any pg_type entry, return it numerically */
1285 0 : result = (char *) palloc(NAMEDATALEN);
1286 0 : snprintf(result, NAMEDATALEN, "%u", typid);
1287 : }
1288 :
1289 16362 : PG_RETURN_CSTRING(result);
1290 : }
1291 :
1292 : /*
1293 : * regtyperecv - converts external binary format to regtype
1294 : */
1295 : Datum
1296 0 : regtyperecv(PG_FUNCTION_ARGS)
1297 : {
1298 : /* Exactly the same as oidrecv, so share code */
1299 0 : return oidrecv(fcinfo);
1300 : }
1301 :
1302 : /*
1303 : * regtypesend - converts regtype to binary format
1304 : */
1305 : Datum
1306 0 : regtypesend(PG_FUNCTION_ARGS)
1307 : {
1308 : /* Exactly the same as oidsend, so share code */
1309 0 : return oidsend(fcinfo);
1310 : }
1311 :
1312 :
1313 : /*
1314 : * regconfigin - converts "tsconfigname" to tsconfig OID
1315 : *
1316 : * We also accept a numeric OID, for symmetry with the output routine.
1317 : *
1318 : * '-' signifies unknown (OID 0). In all other cases, the input must
1319 : * match an existing pg_ts_config entry.
1320 : */
1321 : Datum
1322 1940 : regconfigin(PG_FUNCTION_ARGS)
1323 : {
1324 1940 : char *cfg_name_or_oid = PG_GETARG_CSTRING(0);
1325 1940 : Node *escontext = fcinfo->context;
1326 : Oid result;
1327 : List *names;
1328 :
1329 : /* Handle "-" or numeric OID */
1330 1940 : if (parseDashOrOid(cfg_name_or_oid, &result, escontext))
1331 6 : PG_RETURN_OID(result);
1332 :
1333 : /* The rest of this wouldn't work in bootstrap mode */
1334 1934 : if (IsBootstrapProcessingMode())
1335 0 : elog(ERROR, "regconfig values must be OIDs in bootstrap mode");
1336 :
1337 : /*
1338 : * Normal case: parse the name into components and see if it matches any
1339 : * pg_ts_config entries in the current search path.
1340 : */
1341 1934 : names = stringToQualifiedNameList(cfg_name_or_oid, escontext);
1342 1934 : if (names == NIL)
1343 0 : PG_RETURN_NULL();
1344 :
1345 1934 : result = get_ts_config_oid(names, true);
1346 :
1347 1934 : if (!OidIsValid(result))
1348 6 : ereturn(escontext, (Datum) 0,
1349 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1350 : errmsg("text search configuration \"%s\" does not exist",
1351 : NameListToString(names))));
1352 :
1353 1928 : PG_RETURN_OID(result);
1354 : }
1355 :
1356 : /*
1357 : * regconfigout - converts tsconfig OID to "tsconfigname"
1358 : */
1359 : Datum
1360 10 : regconfigout(PG_FUNCTION_ARGS)
1361 : {
1362 10 : Oid cfgid = PG_GETARG_OID(0);
1363 : char *result;
1364 : HeapTuple cfgtup;
1365 :
1366 10 : if (cfgid == InvalidOid)
1367 : {
1368 0 : result = pstrdup("-");
1369 0 : PG_RETURN_CSTRING(result);
1370 : }
1371 :
1372 10 : cfgtup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
1373 :
1374 10 : if (HeapTupleIsValid(cfgtup))
1375 : {
1376 10 : Form_pg_ts_config cfgform = (Form_pg_ts_config) GETSTRUCT(cfgtup);
1377 10 : char *cfgname = NameStr(cfgform->cfgname);
1378 : char *nspname;
1379 :
1380 : /*
1381 : * Would this config be found by regconfigin? If not, qualify it.
1382 : */
1383 10 : if (TSConfigIsVisible(cfgid))
1384 6 : nspname = NULL;
1385 : else
1386 4 : nspname = get_namespace_name(cfgform->cfgnamespace);
1387 :
1388 10 : result = quote_qualified_identifier(nspname, cfgname);
1389 :
1390 10 : ReleaseSysCache(cfgtup);
1391 : }
1392 : else
1393 : {
1394 : /* If OID doesn't match any pg_ts_config row, return it numerically */
1395 0 : result = (char *) palloc(NAMEDATALEN);
1396 0 : snprintf(result, NAMEDATALEN, "%u", cfgid);
1397 : }
1398 :
1399 10 : PG_RETURN_CSTRING(result);
1400 : }
1401 :
1402 : /*
1403 : * regconfigrecv - converts external binary format to regconfig
1404 : */
1405 : Datum
1406 0 : regconfigrecv(PG_FUNCTION_ARGS)
1407 : {
1408 : /* Exactly the same as oidrecv, so share code */
1409 0 : return oidrecv(fcinfo);
1410 : }
1411 :
1412 : /*
1413 : * regconfigsend - converts regconfig to binary format
1414 : */
1415 : Datum
1416 0 : regconfigsend(PG_FUNCTION_ARGS)
1417 : {
1418 : /* Exactly the same as oidsend, so share code */
1419 0 : return oidsend(fcinfo);
1420 : }
1421 :
1422 :
1423 : /*
1424 : * regdictionaryin - converts "tsdictionaryname" to tsdictionary OID
1425 : *
1426 : * We also accept a numeric OID, for symmetry with the output routine.
1427 : *
1428 : * '-' signifies unknown (OID 0). In all other cases, the input must
1429 : * match an existing pg_ts_dict entry.
1430 : */
1431 : Datum
1432 642 : regdictionaryin(PG_FUNCTION_ARGS)
1433 : {
1434 642 : char *dict_name_or_oid = PG_GETARG_CSTRING(0);
1435 642 : Node *escontext = fcinfo->context;
1436 : Oid result;
1437 : List *names;
1438 :
1439 : /* Handle "-" or numeric OID */
1440 642 : if (parseDashOrOid(dict_name_or_oid, &result, escontext))
1441 6 : PG_RETURN_OID(result);
1442 :
1443 : /* The rest of this wouldn't work in bootstrap mode */
1444 636 : if (IsBootstrapProcessingMode())
1445 0 : elog(ERROR, "regdictionary values must be OIDs in bootstrap mode");
1446 :
1447 : /*
1448 : * Normal case: parse the name into components and see if it matches any
1449 : * pg_ts_dict entries in the current search path.
1450 : */
1451 636 : names = stringToQualifiedNameList(dict_name_or_oid, escontext);
1452 636 : if (names == NIL)
1453 0 : PG_RETURN_NULL();
1454 :
1455 636 : result = get_ts_dict_oid(names, true);
1456 :
1457 636 : if (!OidIsValid(result))
1458 6 : ereturn(escontext, (Datum) 0,
1459 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1460 : errmsg("text search dictionary \"%s\" does not exist",
1461 : NameListToString(names))));
1462 :
1463 630 : PG_RETURN_OID(result);
1464 : }
1465 :
1466 : /*
1467 : * regdictionaryout - converts tsdictionary OID to "tsdictionaryname"
1468 : */
1469 : Datum
1470 6640 : regdictionaryout(PG_FUNCTION_ARGS)
1471 : {
1472 6640 : Oid dictid = PG_GETARG_OID(0);
1473 : char *result;
1474 : HeapTuple dicttup;
1475 :
1476 6640 : if (dictid == InvalidOid)
1477 : {
1478 0 : result = pstrdup("-");
1479 0 : PG_RETURN_CSTRING(result);
1480 : }
1481 :
1482 6640 : dicttup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictid));
1483 :
1484 6640 : if (HeapTupleIsValid(dicttup))
1485 : {
1486 6640 : Form_pg_ts_dict dictform = (Form_pg_ts_dict) GETSTRUCT(dicttup);
1487 6640 : char *dictname = NameStr(dictform->dictname);
1488 : char *nspname;
1489 :
1490 : /*
1491 : * Would this dictionary be found by regdictionaryin? If not, qualify
1492 : * it.
1493 : */
1494 6640 : if (TSDictionaryIsVisible(dictid))
1495 6208 : nspname = NULL;
1496 : else
1497 432 : nspname = get_namespace_name(dictform->dictnamespace);
1498 :
1499 6640 : result = quote_qualified_identifier(nspname, dictname);
1500 :
1501 6640 : ReleaseSysCache(dicttup);
1502 : }
1503 : else
1504 : {
1505 : /* If OID doesn't match any pg_ts_dict row, return it numerically */
1506 0 : result = (char *) palloc(NAMEDATALEN);
1507 0 : snprintf(result, NAMEDATALEN, "%u", dictid);
1508 : }
1509 :
1510 6640 : PG_RETURN_CSTRING(result);
1511 : }
1512 :
1513 : /*
1514 : * regdictionaryrecv - converts external binary format to regdictionary
1515 : */
1516 : Datum
1517 0 : regdictionaryrecv(PG_FUNCTION_ARGS)
1518 : {
1519 : /* Exactly the same as oidrecv, so share code */
1520 0 : return oidrecv(fcinfo);
1521 : }
1522 :
1523 : /*
1524 : * regdictionarysend - converts regdictionary to binary format
1525 : */
1526 : Datum
1527 0 : regdictionarysend(PG_FUNCTION_ARGS)
1528 : {
1529 : /* Exactly the same as oidsend, so share code */
1530 0 : return oidsend(fcinfo);
1531 : }
1532 :
1533 : /*
1534 : * regrolein - converts "rolename" to role OID
1535 : *
1536 : * We also accept a numeric OID, for symmetry with the output routine.
1537 : *
1538 : * '-' signifies unknown (OID 0). In all other cases, the input must
1539 : * match an existing pg_authid entry.
1540 : */
1541 : Datum
1542 268 : regrolein(PG_FUNCTION_ARGS)
1543 : {
1544 268 : char *role_name_or_oid = PG_GETARG_CSTRING(0);
1545 268 : Node *escontext = fcinfo->context;
1546 : Oid result;
1547 : List *names;
1548 :
1549 : /* Handle "-" or numeric OID */
1550 268 : if (parseDashOrOid(role_name_or_oid, &result, escontext))
1551 12 : PG_RETURN_OID(result);
1552 :
1553 : /* The rest of this wouldn't work in bootstrap mode */
1554 256 : if (IsBootstrapProcessingMode())
1555 0 : elog(ERROR, "regrole values must be OIDs in bootstrap mode");
1556 :
1557 : /* Normal case: see if the name matches any pg_authid entry. */
1558 256 : names = stringToQualifiedNameList(role_name_or_oid, escontext);
1559 256 : if (names == NIL)
1560 0 : PG_RETURN_NULL();
1561 :
1562 256 : if (list_length(names) != 1)
1563 18 : ereturn(escontext, (Datum) 0,
1564 : (errcode(ERRCODE_INVALID_NAME),
1565 : errmsg("invalid name syntax")));
1566 :
1567 238 : result = get_role_oid(strVal(linitial(names)), true);
1568 :
1569 238 : if (!OidIsValid(result))
1570 54 : ereturn(escontext, (Datum) 0,
1571 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1572 : errmsg("role \"%s\" does not exist",
1573 : strVal(linitial(names)))));
1574 :
1575 184 : PG_RETURN_OID(result);
1576 : }
1577 :
1578 : /*
1579 : * to_regrole - converts "rolename" to role OID
1580 : *
1581 : * If the name is not found, we return NULL.
1582 : */
1583 : Datum
1584 54 : to_regrole(PG_FUNCTION_ARGS)
1585 : {
1586 54 : char *role_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1587 : Datum result;
1588 54 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1589 :
1590 54 : if (!DirectInputFunctionCallSafe(regrolein, role_name,
1591 : InvalidOid, -1,
1592 : (Node *) &escontext,
1593 : &result))
1594 36 : PG_RETURN_NULL();
1595 18 : PG_RETURN_DATUM(result);
1596 : }
1597 :
1598 : /*
1599 : * regroleout - converts role OID to "role_name"
1600 : */
1601 : Datum
1602 1622 : regroleout(PG_FUNCTION_ARGS)
1603 : {
1604 1622 : Oid roleoid = PG_GETARG_OID(0);
1605 : char *result;
1606 :
1607 1622 : if (roleoid == InvalidOid)
1608 : {
1609 140 : result = pstrdup("-");
1610 140 : PG_RETURN_CSTRING(result);
1611 : }
1612 :
1613 1482 : result = GetUserNameFromId(roleoid, true);
1614 :
1615 1482 : if (result)
1616 : {
1617 : /* pstrdup is not really necessary, but it avoids a compiler warning */
1618 1482 : result = pstrdup(quote_identifier(result));
1619 : }
1620 : else
1621 : {
1622 : /* If OID doesn't match any role, return it numerically */
1623 0 : result = (char *) palloc(NAMEDATALEN);
1624 0 : snprintf(result, NAMEDATALEN, "%u", roleoid);
1625 : }
1626 :
1627 1482 : PG_RETURN_CSTRING(result);
1628 : }
1629 :
1630 : /*
1631 : * regrolerecv - converts external binary format to regrole
1632 : */
1633 : Datum
1634 0 : regrolerecv(PG_FUNCTION_ARGS)
1635 : {
1636 : /* Exactly the same as oidrecv, so share code */
1637 0 : return oidrecv(fcinfo);
1638 : }
1639 :
1640 : /*
1641 : * regrolesend - converts regrole to binary format
1642 : */
1643 : Datum
1644 0 : regrolesend(PG_FUNCTION_ARGS)
1645 : {
1646 : /* Exactly the same as oidsend, so share code */
1647 0 : return oidsend(fcinfo);
1648 : }
1649 :
1650 : /*
1651 : * regnamespacein - converts "nspname" to namespace OID
1652 : *
1653 : * We also accept a numeric OID, for symmetry with the output routine.
1654 : *
1655 : * '-' signifies unknown (OID 0). In all other cases, the input must
1656 : * match an existing pg_namespace entry.
1657 : */
1658 : Datum
1659 798 : regnamespacein(PG_FUNCTION_ARGS)
1660 : {
1661 798 : char *nsp_name_or_oid = PG_GETARG_CSTRING(0);
1662 798 : Node *escontext = fcinfo->context;
1663 : Oid result;
1664 : List *names;
1665 :
1666 : /* Handle "-" or numeric OID */
1667 798 : if (parseDashOrOid(nsp_name_or_oid, &result, escontext))
1668 12 : PG_RETURN_OID(result);
1669 :
1670 : /* The rest of this wouldn't work in bootstrap mode */
1671 786 : if (IsBootstrapProcessingMode())
1672 0 : elog(ERROR, "regnamespace values must be OIDs in bootstrap mode");
1673 :
1674 : /* Normal case: see if the name matches any pg_namespace entry. */
1675 786 : names = stringToQualifiedNameList(nsp_name_or_oid, escontext);
1676 786 : if (names == NIL)
1677 0 : PG_RETURN_NULL();
1678 :
1679 786 : if (list_length(names) != 1)
1680 12 : ereturn(escontext, (Datum) 0,
1681 : (errcode(ERRCODE_INVALID_NAME),
1682 : errmsg("invalid name syntax")));
1683 :
1684 774 : result = get_namespace_oid(strVal(linitial(names)), true);
1685 :
1686 774 : if (!OidIsValid(result))
1687 30 : ereturn(escontext, (Datum) 0,
1688 : (errcode(ERRCODE_UNDEFINED_SCHEMA),
1689 : errmsg("schema \"%s\" does not exist",
1690 : strVal(linitial(names)))));
1691 :
1692 744 : PG_RETURN_OID(result);
1693 : }
1694 :
1695 : /*
1696 : * to_regnamespace - converts "nspname" to namespace OID
1697 : *
1698 : * If the name is not found, we return NULL.
1699 : */
1700 : Datum
1701 36 : to_regnamespace(PG_FUNCTION_ARGS)
1702 : {
1703 36 : char *nsp_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1704 : Datum result;
1705 36 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1706 :
1707 36 : if (!DirectInputFunctionCallSafe(regnamespacein, nsp_name,
1708 : InvalidOid, -1,
1709 : (Node *) &escontext,
1710 : &result))
1711 18 : PG_RETURN_NULL();
1712 18 : PG_RETURN_DATUM(result);
1713 : }
1714 :
1715 : /*
1716 : * regnamespaceout - converts namespace OID to "nsp_name"
1717 : */
1718 : Datum
1719 1124 : regnamespaceout(PG_FUNCTION_ARGS)
1720 : {
1721 1124 : Oid nspid = PG_GETARG_OID(0);
1722 : char *result;
1723 :
1724 1124 : if (nspid == InvalidOid)
1725 : {
1726 0 : result = pstrdup("-");
1727 0 : PG_RETURN_CSTRING(result);
1728 : }
1729 :
1730 1124 : result = get_namespace_name(nspid);
1731 :
1732 1124 : if (result)
1733 : {
1734 : /* pstrdup is not really necessary, but it avoids a compiler warning */
1735 1124 : result = pstrdup(quote_identifier(result));
1736 : }
1737 : else
1738 : {
1739 : /* If OID doesn't match any namespace, return it numerically */
1740 0 : result = (char *) palloc(NAMEDATALEN);
1741 0 : snprintf(result, NAMEDATALEN, "%u", nspid);
1742 : }
1743 :
1744 1124 : PG_RETURN_CSTRING(result);
1745 : }
1746 :
1747 : /*
1748 : * regnamespacerecv - converts external binary format to regnamespace
1749 : */
1750 : Datum
1751 0 : regnamespacerecv(PG_FUNCTION_ARGS)
1752 : {
1753 : /* Exactly the same as oidrecv, so share code */
1754 0 : return oidrecv(fcinfo);
1755 : }
1756 :
1757 : /*
1758 : * regnamespacesend - converts regnamespace to binary format
1759 : */
1760 : Datum
1761 0 : regnamespacesend(PG_FUNCTION_ARGS)
1762 : {
1763 : /* Exactly the same as oidsend, so share code */
1764 0 : return oidsend(fcinfo);
1765 : }
1766 :
1767 : /*
1768 : * regdatabasein - converts database name to database OID
1769 : *
1770 : * We also accept a numeric OID, for symmetry with the output routine.
1771 : *
1772 : * '-' signifies unknown (OID 0). In all other cases, the input must
1773 : * match an existing pg_database entry.
1774 : */
1775 : Datum
1776 92 : regdatabasein(PG_FUNCTION_ARGS)
1777 : {
1778 92 : char *db_name_or_oid = PG_GETARG_CSTRING(0);
1779 92 : Node *escontext = fcinfo->context;
1780 : Oid result;
1781 : List *names;
1782 :
1783 : /* Handle "-" or numeric OID */
1784 92 : if (parseDashOrOid(db_name_or_oid, &result, escontext))
1785 12 : PG_RETURN_OID(result);
1786 :
1787 : /* The rest of this wouldn't work in bootstrap mode */
1788 80 : if (IsBootstrapProcessingMode())
1789 0 : elog(ERROR, "regdatabase values must be OIDs in bootstrap mode");
1790 :
1791 : /* Normal case: see if the name matches any pg_database entry. */
1792 80 : names = stringToQualifiedNameList(db_name_or_oid, escontext);
1793 80 : if (names == NIL)
1794 0 : PG_RETURN_NULL();
1795 :
1796 80 : if (list_length(names) != 1)
1797 12 : ereturn(escontext, (Datum) 0,
1798 : (errcode(ERRCODE_INVALID_NAME),
1799 : errmsg("invalid name syntax")));
1800 :
1801 68 : result = get_database_oid(strVal(linitial(names)), true);
1802 :
1803 68 : if (!OidIsValid(result))
1804 30 : ereturn(escontext, (Datum) 0,
1805 : (errcode(ERRCODE_UNDEFINED_OBJECT),
1806 : errmsg("database \"%s\" does not exist",
1807 : strVal(linitial(names)))));
1808 :
1809 38 : PG_RETURN_OID(result);
1810 : }
1811 :
1812 : /*
1813 : * to_regdatabase - converts database name to database OID
1814 : *
1815 : * If the name is not found, we return NULL.
1816 : */
1817 : Datum
1818 36 : to_regdatabase(PG_FUNCTION_ARGS)
1819 : {
1820 36 : char *db_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1821 : Datum result;
1822 36 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1823 :
1824 36 : if (!DirectInputFunctionCallSafe(regdatabasein, db_name,
1825 : InvalidOid, -1,
1826 : (Node *) &escontext,
1827 : &result))
1828 18 : PG_RETURN_NULL();
1829 18 : PG_RETURN_DATUM(result);
1830 : }
1831 :
1832 : /*
1833 : * regdatabaseout - converts database OID to database name
1834 : */
1835 : Datum
1836 38 : regdatabaseout(PG_FUNCTION_ARGS)
1837 : {
1838 38 : Oid dboid = PG_GETARG_OID(0);
1839 : char *result;
1840 :
1841 38 : if (dboid == InvalidOid)
1842 : {
1843 0 : result = pstrdup("-");
1844 0 : PG_RETURN_CSTRING(result);
1845 : }
1846 :
1847 38 : result = get_database_name(dboid);
1848 :
1849 38 : if (result)
1850 : {
1851 : /* pstrdup is not really necessary, but it avoids a compiler warning */
1852 38 : result = pstrdup(quote_identifier(result));
1853 : }
1854 : else
1855 : {
1856 : /* If OID doesn't match any database, return it numerically */
1857 0 : result = (char *) palloc(NAMEDATALEN);
1858 0 : snprintf(result, NAMEDATALEN, "%u", dboid);
1859 : }
1860 :
1861 38 : PG_RETURN_CSTRING(result);
1862 : }
1863 :
1864 : /*
1865 : * regdatabaserecv - converts external binary format to regdatabase
1866 : */
1867 : Datum
1868 0 : regdatabaserecv(PG_FUNCTION_ARGS)
1869 : {
1870 : /* Exactly the same as oidrecv, so share code */
1871 0 : return oidrecv(fcinfo);
1872 : }
1873 :
1874 : /*
1875 : * regdatabasesend - converts regdatabase to binary format
1876 : */
1877 : Datum
1878 0 : regdatabasesend(PG_FUNCTION_ARGS)
1879 : {
1880 : /* Exactly the same as oidsend, so share code */
1881 0 : return oidsend(fcinfo);
1882 : }
1883 :
1884 : /*
1885 : * text_regclass: convert text to regclass
1886 : *
1887 : * This could be replaced by CoerceViaIO, except that we need to treat
1888 : * text-to-regclass as an implicit cast to support legacy forms of nextval()
1889 : * and related functions.
1890 : */
1891 : Datum
1892 4024 : text_regclass(PG_FUNCTION_ARGS)
1893 : {
1894 4024 : text *relname = PG_GETARG_TEXT_PP(0);
1895 : Oid result;
1896 : RangeVar *rv;
1897 :
1898 4024 : rv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
1899 :
1900 : /* We might not even have permissions on this relation; don't lock it. */
1901 4024 : result = RangeVarGetRelid(rv, NoLock, false);
1902 :
1903 4018 : PG_RETURN_OID(result);
1904 : }
1905 :
1906 :
1907 : /*
1908 : * Given a C string, parse it into a qualified-name list.
1909 : *
1910 : * If escontext is an ErrorSaveContext node, invalid input will be
1911 : * reported there instead of being thrown, and we return NIL.
1912 : * (NIL is not possible as a success return, since empty-input is an error.)
1913 : */
1914 : List *
1915 44964 : stringToQualifiedNameList(const char *string, Node *escontext)
1916 : {
1917 : char *rawname;
1918 44964 : List *result = NIL;
1919 : List *namelist;
1920 : ListCell *l;
1921 :
1922 : /* We need a modifiable copy of the input string. */
1923 44964 : rawname = pstrdup(string);
1924 :
1925 44964 : if (!SplitIdentifierString(rawname, '.', &namelist))
1926 0 : ereturn(escontext, NIL,
1927 : (errcode(ERRCODE_INVALID_NAME),
1928 : errmsg("invalid name syntax")));
1929 :
1930 44964 : if (namelist == NIL)
1931 0 : ereturn(escontext, NIL,
1932 : (errcode(ERRCODE_INVALID_NAME),
1933 : errmsg("invalid name syntax")));
1934 :
1935 113976 : foreach(l, namelist)
1936 : {
1937 69012 : char *curname = (char *) lfirst(l);
1938 :
1939 69012 : result = lappend(result, makeString(pstrdup(curname)));
1940 : }
1941 :
1942 44964 : pfree(rawname);
1943 44964 : list_free(namelist);
1944 :
1945 44964 : return result;
1946 : }
1947 :
1948 : /*****************************************************************************
1949 : * SUPPORT ROUTINES *
1950 : *****************************************************************************/
1951 :
1952 : /*
1953 : * Given a C string, see if it is all-digits (and not empty).
1954 : * If so, convert directly to OID and return true.
1955 : * If it is not all-digits, return false.
1956 : *
1957 : * If escontext is an ErrorSaveContext node, any error in oidin() will be
1958 : * reported there instead of being thrown (but we still return true).
1959 : */
1960 : static bool
1961 832214 : parseNumericOid(char *string, Oid *result, Node *escontext)
1962 : {
1963 832214 : if (string[0] >= '0' && string[0] <= '9' &&
1964 791744 : strspn(string, "0123456789") == strlen(string))
1965 : {
1966 : Datum oid_datum;
1967 :
1968 : /* We need not care here whether oidin() fails or not. */
1969 791744 : (void) DirectInputFunctionCallSafe(oidin, string,
1970 : InvalidOid, -1,
1971 : escontext,
1972 : &oid_datum);
1973 791744 : *result = DatumGetObjectId(oid_datum);
1974 791744 : return true;
1975 : }
1976 :
1977 : /* Prevent uninitialized-variable warnings from stupider compilers. */
1978 40470 : *result = InvalidOid;
1979 40470 : return false;
1980 : }
1981 :
1982 : /*
1983 : * As above, but also accept "-" as meaning 0 (InvalidOid).
1984 : */
1985 : static bool
1986 1024078 : parseDashOrOid(char *string, Oid *result, Node *escontext)
1987 : {
1988 : /* '-' ? */
1989 1024078 : if (strcmp(string, "-") == 0)
1990 : {
1991 192008 : *result = InvalidOid;
1992 192008 : return true;
1993 : }
1994 :
1995 : /* Numeric OID? */
1996 832070 : return parseNumericOid(string, result, escontext);
1997 : }
1998 :
1999 : /*
2000 : * Given a C string, parse it into a qualified function or operator name
2001 : * followed by a parenthesized list of type names. Reduce the
2002 : * type names to an array of OIDs (returned into *nargs and *argtypes;
2003 : * the argtypes array should be of size FUNC_MAX_ARGS). The function or
2004 : * operator name is returned to *names as a List of Strings.
2005 : *
2006 : * If allowNone is true, accept "NONE" and return it as InvalidOid (this is
2007 : * for unary operators).
2008 : *
2009 : * Returns true on success, false on failure (the latter only possible
2010 : * if escontext is an ErrorSaveContext node).
2011 : */
2012 : static bool
2013 584 : parseNameAndArgTypes(const char *string, bool allowNone, List **names,
2014 : int *nargs, Oid *argtypes,
2015 : Node *escontext)
2016 : {
2017 : char *rawname;
2018 : char *ptr;
2019 : char *ptr2;
2020 : char *typename;
2021 : bool in_quote;
2022 : bool had_comma;
2023 : int paren_count;
2024 : Oid typeid;
2025 : int32 typmod;
2026 :
2027 : /* We need a modifiable copy of the input string. */
2028 584 : rawname = pstrdup(string);
2029 :
2030 : /* Scan to find the expected left paren; mustn't be quoted */
2031 584 : in_quote = false;
2032 10280 : for (ptr = rawname; *ptr; ptr++)
2033 : {
2034 10274 : if (*ptr == '"')
2035 0 : in_quote = !in_quote;
2036 10274 : else if (*ptr == '(' && !in_quote)
2037 578 : break;
2038 : }
2039 584 : if (*ptr == '\0')
2040 6 : ereturn(escontext, false,
2041 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2042 : errmsg("expected a left parenthesis")));
2043 :
2044 : /* Separate the name and parse it into a list */
2045 578 : *ptr++ = '\0';
2046 578 : *names = stringToQualifiedNameList(rawname, escontext);
2047 578 : if (*names == NIL)
2048 0 : return false;
2049 :
2050 : /* Check for the trailing right parenthesis and remove it */
2051 578 : ptr2 = ptr + strlen(ptr);
2052 596 : while (--ptr2 > ptr)
2053 : {
2054 502 : if (!scanner_isspace(*ptr2))
2055 484 : break;
2056 : }
2057 578 : if (*ptr2 != ')')
2058 6 : ereturn(escontext, false,
2059 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2060 : errmsg("expected a right parenthesis")));
2061 :
2062 572 : *ptr2 = '\0';
2063 :
2064 : /* Separate the remaining string into comma-separated type names */
2065 572 : *nargs = 0;
2066 572 : had_comma = false;
2067 :
2068 : for (;;)
2069 : {
2070 : /* allow leading whitespace */
2071 1188 : while (scanner_isspace(*ptr))
2072 30 : ptr++;
2073 1158 : if (*ptr == '\0')
2074 : {
2075 : /* End of string. Okay unless we had a comma before. */
2076 572 : if (had_comma)
2077 0 : ereturn(escontext, false,
2078 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2079 : errmsg("expected a type name")));
2080 572 : break;
2081 : }
2082 586 : typename = ptr;
2083 : /* Find end of type name --- end of string or comma */
2084 : /* ... but not a quoted or parenthesized comma */
2085 586 : in_quote = false;
2086 586 : paren_count = 0;
2087 5280 : for (; *ptr; ptr++)
2088 : {
2089 4802 : if (*ptr == '"')
2090 0 : in_quote = !in_quote;
2091 4802 : else if (*ptr == ',' && !in_quote && paren_count == 0)
2092 : break;
2093 4694 : else if (!in_quote)
2094 : {
2095 4694 : switch (*ptr)
2096 : {
2097 0 : case '(':
2098 : case '[':
2099 0 : paren_count++;
2100 0 : break;
2101 0 : case ')':
2102 : case ']':
2103 0 : paren_count--;
2104 0 : break;
2105 : }
2106 : }
2107 : }
2108 586 : if (in_quote || paren_count != 0)
2109 0 : ereturn(escontext, false,
2110 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2111 : errmsg("improper type name")));
2112 :
2113 586 : ptr2 = ptr;
2114 586 : if (*ptr == ',')
2115 : {
2116 108 : had_comma = true;
2117 108 : *ptr++ = '\0';
2118 : }
2119 : else
2120 : {
2121 478 : had_comma = false;
2122 : Assert(*ptr == '\0');
2123 : }
2124 : /* Lop off trailing whitespace */
2125 586 : while (--ptr2 >= typename)
2126 : {
2127 586 : if (!scanner_isspace(*ptr2))
2128 586 : break;
2129 0 : *ptr2 = '\0';
2130 : }
2131 :
2132 586 : if (allowNone && pg_strcasecmp(typename, "none") == 0)
2133 : {
2134 : /* Special case for NONE */
2135 0 : typeid = InvalidOid;
2136 0 : typmod = -1;
2137 : }
2138 : else
2139 : {
2140 : /* Use full parser to resolve the type name */
2141 586 : if (!parseTypeString(typename, &typeid, &typmod, escontext))
2142 0 : return false;
2143 : }
2144 586 : if (*nargs >= FUNC_MAX_ARGS)
2145 0 : ereturn(escontext, false,
2146 : (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
2147 : errmsg("too many arguments")));
2148 :
2149 586 : argtypes[*nargs] = typeid;
2150 586 : (*nargs)++;
2151 : }
2152 :
2153 572 : pfree(rawname);
2154 :
2155 572 : return true;
2156 : }
|