Coverage Report

Created: 2025-07-03 06:49

/src/postgres/src/backend/utils/adt/regproc.c
Line
Count
Source (jump to first uncovered line)
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
regprocin(PG_FUNCTION_ARGS)
68
0
{
69
0
  char     *pro_name_or_oid = PG_GETARG_CSTRING(0);
70
0
  Node     *escontext = fcinfo->context;
71
0
  RegProcedure result;
72
0
  List     *names;
73
0
  FuncCandidateList clist;
74
75
  /* Handle "-" or numeric OID */
76
0
  if (parseDashOrOid(pro_name_or_oid, &result, escontext))
77
0
    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
0
  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
0
  names = stringToQualifiedNameList(pro_name_or_oid, escontext);
93
0
  if (names == NIL)
94
0
    PG_RETURN_NULL();
95
96
0
  clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, true);
97
98
0
  if (clist == NULL)
99
0
    ereturn(escontext, (Datum) 0,
100
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
101
0
         errmsg("function \"%s\" does not exist", pro_name_or_oid)));
102
0
  else if (clist->next != NULL)
103
0
    ereturn(escontext, (Datum) 0,
104
0
        (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
105
0
         errmsg("more than one function named \"%s\"",
106
0
            pro_name_or_oid)));
107
108
0
  result = clist->oid;
109
110
0
  PG_RETURN_OID(result);
111
0
}
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
to_regproc(PG_FUNCTION_ARGS)
120
0
{
121
0
  char     *pro_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
122
0
  Datum   result;
123
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
124
125
0
  if (!DirectInputFunctionCallSafe(regprocin, pro_name,
126
0
                   InvalidOid, -1,
127
0
                   (Node *) &escontext,
128
0
                   &result))
129
0
    PG_RETURN_NULL();
130
0
  PG_RETURN_DATUM(result);
131
0
}
132
133
/*
134
 * regprocout   - converts proc OID to "pro_name"
135
 */
136
Datum
137
regprocout(PG_FUNCTION_ARGS)
138
0
{
139
0
  RegProcedure proid = PG_GETARG_OID(0);
140
0
  char     *result;
141
0
  HeapTuple proctup;
142
143
0
  if (proid == InvalidOid)
144
0
  {
145
0
    result = pstrdup("-");
146
0
    PG_RETURN_CSTRING(result);
147
0
  }
148
149
0
  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(proid));
150
151
0
  if (HeapTupleIsValid(proctup))
152
0
  {
153
0
    Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
154
0
    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
0
    if (IsBootstrapProcessingMode())
162
0
      result = pstrdup(proname);
163
0
    else
164
0
    {
165
0
      char     *nspname;
166
0
      FuncCandidateList clist;
167
168
      /*
169
       * Would this proc be found (uniquely!) by regprocin? If not,
170
       * qualify it.
171
       */
172
0
      clist = FuncnameGetCandidates(list_make1(makeString(proname)),
173
0
                      -1, NIL, false, false, false, false);
174
0
      if (clist != NULL && clist->next == NULL &&
175
0
        clist->oid == proid)
176
0
        nspname = NULL;
177
0
      else
178
0
        nspname = get_namespace_name(procform->pronamespace);
179
180
0
      result = quote_qualified_identifier(nspname, proname);
181
0
    }
182
183
0
    ReleaseSysCache(proctup);
184
0
  }
185
0
  else
186
0
  {
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
0
  }
191
192
0
  PG_RETURN_CSTRING(result);
193
0
}
194
195
/*
196
 *    regprocrecv     - converts external binary format to regproc
197
 */
198
Datum
199
regprocrecv(PG_FUNCTION_ARGS)
200
0
{
201
  /* Exactly the same as oidrecv, so share code */
202
0
  return oidrecv(fcinfo);
203
0
}
204
205
/*
206
 *    regprocsend     - converts regproc to binary format
207
 */
208
Datum
209
regprocsend(PG_FUNCTION_ARGS)
210
0
{
211
  /* Exactly the same as oidsend, so share code */
212
0
  return oidsend(fcinfo);
213
0
}
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
regprocedurein(PG_FUNCTION_ARGS)
226
0
{
227
0
  char     *pro_name_or_oid = PG_GETARG_CSTRING(0);
228
0
  Node     *escontext = fcinfo->context;
229
0
  RegProcedure result;
230
0
  List     *names;
231
0
  int     nargs;
232
0
  Oid     argtypes[FUNC_MAX_ARGS];
233
0
  FuncCandidateList clist;
234
235
  /* Handle "-" or numeric OID */
236
0
  if (parseDashOrOid(pro_name_or_oid, &result, escontext))
237
0
    PG_RETURN_OID(result);
238
239
  /* The rest of this wouldn't work in bootstrap mode */
240
0
  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
0
  if (!parseNameAndArgTypes(pro_name_or_oid, false,
250
0
                &names, &nargs, argtypes,
251
0
                escontext))
252
0
    PG_RETURN_NULL();
253
254
0
  clist = FuncnameGetCandidates(names, nargs, NIL, false, false,
255
0
                  false, true);
256
257
0
  for (; clist; clist = clist->next)
258
0
  {
259
0
    if (memcmp(clist->args, argtypes, nargs * sizeof(Oid)) == 0)
260
0
      break;
261
0
  }
262
263
0
  if (clist == NULL)
264
0
    ereturn(escontext, (Datum) 0,
265
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
266
0
         errmsg("function \"%s\" does not exist", pro_name_or_oid)));
267
268
0
  result = clist->oid;
269
270
0
  PG_RETURN_OID(result);
271
0
}
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
to_regprocedure(PG_FUNCTION_ARGS)
280
0
{
281
0
  char     *pro_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
282
0
  Datum   result;
283
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
284
285
0
  if (!DirectInputFunctionCallSafe(regprocedurein, pro_name,
286
0
                   InvalidOid, -1,
287
0
                   (Node *) &escontext,
288
0
                   &result))
289
0
    PG_RETURN_NULL();
290
0
  PG_RETURN_DATUM(result);
291
0
}
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
format_procedure(Oid procedure_oid)
301
0
{
302
0
  return format_procedure_extended(procedure_oid, 0);
303
0
}
304
305
char *
306
format_procedure_qualified(Oid procedure_oid)
307
0
{
308
0
  return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY);
309
0
}
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
format_procedure_extended(Oid procedure_oid, bits16 flags)
328
0
{
329
0
  char     *result;
330
0
  HeapTuple proctup;
331
332
0
  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
333
334
0
  if (HeapTupleIsValid(proctup))
335
0
  {
336
0
    Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
337
0
    char     *proname = NameStr(procform->proname);
338
0
    int     nargs = procform->pronargs;
339
0
    int     i;
340
0
    char     *nspname;
341
0
    StringInfoData buf;
342
343
    /* XXX no support here for bootstrap mode */
344
0
    Assert(!IsBootstrapProcessingMode());
345
346
0
    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
0
    if ((flags & FORMAT_PROC_FORCE_QUALIFY) == 0 &&
353
0
      FunctionIsVisible(procedure_oid))
354
0
      nspname = NULL;
355
0
    else
356
0
      nspname = get_namespace_name(procform->pronamespace);
357
358
0
    appendStringInfo(&buf, "%s(",
359
0
             quote_qualified_identifier(nspname, proname));
360
0
    for (i = 0; i < nargs; i++)
361
0
    {
362
0
      Oid     thisargtype = procform->proargtypes.values[i];
363
364
0
      if (i > 0)
365
0
        appendStringInfoChar(&buf, ',');
366
0
      appendStringInfoString(&buf,
367
0
                   (flags & FORMAT_PROC_FORCE_QUALIFY) != 0 ?
368
0
                   format_type_be_qualified(thisargtype) :
369
0
                   format_type_be(thisargtype));
370
0
    }
371
0
    appendStringInfoChar(&buf, ')');
372
373
0
    result = buf.data;
374
375
0
    ReleaseSysCache(proctup);
376
0
  }
377
0
  else if ((flags & FORMAT_PROC_INVALID_AS_NULL) != 0)
378
0
  {
379
    /* If object is undefined, return NULL as wanted by caller */
380
0
    result = NULL;
381
0
  }
382
0
  else
383
0
  {
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
0
  }
388
389
0
  return result;
390
0
}
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
format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs,
400
             bool missing_ok)
401
0
{
402
0
  HeapTuple proctup;
403
0
  Form_pg_proc procform;
404
0
  int     nargs;
405
0
  int     i;
406
407
0
  proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
408
409
0
  if (!HeapTupleIsValid(proctup))
410
0
  {
411
0
    if (!missing_ok)
412
0
      elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
413
0
    return;
414
0
  }
415
416
0
  procform = (Form_pg_proc) GETSTRUCT(proctup);
417
0
  nargs = procform->pronargs;
418
419
0
  *objnames = list_make2(get_namespace_name_or_temp(procform->pronamespace),
420
0
               pstrdup(NameStr(procform->proname)));
421
0
  *objargs = NIL;
422
0
  for (i = 0; i < nargs; i++)
423
0
  {
424
0
    Oid     thisargtype = procform->proargtypes.values[i];
425
426
0
    *objargs = lappend(*objargs, format_type_be_qualified(thisargtype));
427
0
  }
428
429
0
  ReleaseSysCache(proctup);
430
0
}
431
432
/*
433
 * regprocedureout    - converts proc OID to "pro_name(args)"
434
 */
435
Datum
436
regprocedureout(PG_FUNCTION_ARGS)
437
0
{
438
0
  RegProcedure proid = PG_GETARG_OID(0);
439
0
  char     *result;
440
441
0
  if (proid == InvalidOid)
442
0
    result = pstrdup("-");
443
0
  else
444
0
    result = format_procedure(proid);
445
446
0
  PG_RETURN_CSTRING(result);
447
0
}
448
449
/*
450
 *    regprocedurerecv      - converts external binary format to regprocedure
451
 */
452
Datum
453
regprocedurerecv(PG_FUNCTION_ARGS)
454
0
{
455
  /* Exactly the same as oidrecv, so share code */
456
0
  return oidrecv(fcinfo);
457
0
}
458
459
/*
460
 *    regproceduresend      - converts regprocedure to binary format
461
 */
462
Datum
463
regproceduresend(PG_FUNCTION_ARGS)
464
0
{
465
  /* Exactly the same as oidsend, so share code */
466
0
  return oidsend(fcinfo);
467
0
}
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
regoperin(PG_FUNCTION_ARGS)
480
0
{
481
0
  char     *opr_name_or_oid = PG_GETARG_CSTRING(0);
482
0
  Node     *escontext = fcinfo->context;
483
0
  Oid     result;
484
0
  List     *names;
485
0
  FuncCandidateList clist;
486
487
  /* Handle "0" or numeric OID */
488
0
  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
0
  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
0
  names = stringToQualifiedNameList(opr_name_or_oid, escontext);
502
0
  if (names == NIL)
503
0
    PG_RETURN_NULL();
504
505
0
  clist = OpernameGetCandidates(names, '\0', true);
506
507
0
  if (clist == NULL)
508
0
    ereturn(escontext, (Datum) 0,
509
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
510
0
         errmsg("operator does not exist: %s", opr_name_or_oid)));
511
0
  else if (clist->next != NULL)
512
0
    ereturn(escontext, (Datum) 0,
513
0
        (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
514
0
         errmsg("more than one operator named %s",
515
0
            opr_name_or_oid)));
516
517
0
  result = clist->oid;
518
519
0
  PG_RETURN_OID(result);
520
0
}
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
to_regoper(PG_FUNCTION_ARGS)
529
0
{
530
0
  char     *opr_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
531
0
  Datum   result;
532
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
533
534
0
  if (!DirectInputFunctionCallSafe(regoperin, opr_name,
535
0
                   InvalidOid, -1,
536
0
                   (Node *) &escontext,
537
0
                   &result))
538
0
    PG_RETURN_NULL();
539
0
  PG_RETURN_DATUM(result);
540
0
}
541
542
/*
543
 * regoperout   - converts operator OID to "opr_name"
544
 */
545
Datum
546
regoperout(PG_FUNCTION_ARGS)
547
0
{
548
0
  Oid     oprid = PG_GETARG_OID(0);
549
0
  char     *result;
550
0
  HeapTuple opertup;
551
552
0
  if (oprid == InvalidOid)
553
0
  {
554
0
    result = pstrdup("0");
555
0
    PG_RETURN_CSTRING(result);
556
0
  }
557
558
0
  opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
559
560
0
  if (HeapTupleIsValid(opertup))
561
0
  {
562
0
    Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
563
0
    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
0
    if (IsBootstrapProcessingMode())
571
0
      result = pstrdup(oprname);
572
0
    else
573
0
    {
574
0
      FuncCandidateList clist;
575
576
      /*
577
       * Would this oper be found (uniquely!) by regoperin? If not,
578
       * qualify it.
579
       */
580
0
      clist = OpernameGetCandidates(list_make1(makeString(oprname)),
581
0
                      '\0', false);
582
0
      if (clist != NULL && clist->next == NULL &&
583
0
        clist->oid == oprid)
584
0
        result = pstrdup(oprname);
585
0
      else
586
0
      {
587
0
        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
0
      }
594
0
    }
595
596
0
    ReleaseSysCache(opertup);
597
0
  }
598
0
  else
599
0
  {
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
0
  }
606
607
0
  PG_RETURN_CSTRING(result);
608
0
}
609
610
/*
611
 *    regoperrecv     - converts external binary format to regoper
612
 */
613
Datum
614
regoperrecv(PG_FUNCTION_ARGS)
615
0
{
616
  /* Exactly the same as oidrecv, so share code */
617
0
  return oidrecv(fcinfo);
618
0
}
619
620
/*
621
 *    regopersend     - converts regoper to binary format
622
 */
623
Datum
624
regopersend(PG_FUNCTION_ARGS)
625
0
{
626
  /* Exactly the same as oidsend, so share code */
627
0
  return oidsend(fcinfo);
628
0
}
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
regoperatorin(PG_FUNCTION_ARGS)
641
0
{
642
0
  char     *opr_name_or_oid = PG_GETARG_CSTRING(0);
643
0
  Node     *escontext = fcinfo->context;
644
0
  Oid     result;
645
0
  List     *names;
646
0
  int     nargs;
647
0
  Oid     argtypes[FUNC_MAX_ARGS];
648
649
  /* Handle "0" or numeric OID */
650
0
  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
0
  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
0
  if (!parseNameAndArgTypes(opr_name_or_oid, true,
664
0
                &names, &nargs, argtypes,
665
0
                escontext))
666
0
    PG_RETURN_NULL();
667
668
0
  if (nargs == 1)
669
0
    ereturn(escontext, (Datum) 0,
670
0
        (errcode(ERRCODE_UNDEFINED_PARAMETER),
671
0
         errmsg("missing argument"),
672
0
         errhint("Use NONE to denote the missing argument of a unary operator.")));
673
0
  if (nargs != 2)
674
0
    ereturn(escontext, (Datum) 0,
675
0
        (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
676
0
         errmsg("too many arguments"),
677
0
         errhint("Provide two argument types for operator.")));
678
679
0
  result = OpernameGetOprid(names, argtypes[0], argtypes[1]);
680
681
0
  if (!OidIsValid(result))
682
0
    ereturn(escontext, (Datum) 0,
683
0
        (errcode(ERRCODE_UNDEFINED_FUNCTION),
684
0
         errmsg("operator does not exist: %s", opr_name_or_oid)));
685
686
0
  PG_RETURN_OID(result);
687
0
}
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
to_regoperator(PG_FUNCTION_ARGS)
696
0
{
697
0
  char     *opr_name_or_oid = text_to_cstring(PG_GETARG_TEXT_PP(0));
698
0
  Datum   result;
699
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
700
701
0
  if (!DirectInputFunctionCallSafe(regoperatorin, opr_name_or_oid,
702
0
                   InvalidOid, -1,
703
0
                   (Node *) &escontext,
704
0
                   &result))
705
0
    PG_RETURN_NULL();
706
0
  PG_RETURN_DATUM(result);
707
0
}
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
format_operator_extended(Oid operator_oid, bits16 flags)
724
0
{
725
0
  char     *result;
726
0
  HeapTuple opertup;
727
728
0
  opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
729
730
0
  if (HeapTupleIsValid(opertup))
731
0
  {
732
0
    Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
733
0
    char     *oprname = NameStr(operform->oprname);
734
0
    char     *nspname;
735
0
    StringInfoData buf;
736
737
    /* XXX no support here for bootstrap mode */
738
0
    Assert(!IsBootstrapProcessingMode());
739
740
0
    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
0
    if ((flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ||
747
0
      !OperatorIsVisible(operator_oid))
748
0
    {
749
0
      nspname = get_namespace_name(operform->oprnamespace);
750
0
      appendStringInfo(&buf, "%s.",
751
0
               quote_identifier(nspname));
752
0
    }
753
754
0
    appendStringInfo(&buf, "%s(", oprname);
755
756
0
    if (operform->oprleft)
757
0
      appendStringInfo(&buf, "%s,",
758
0
               (flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
759
0
               format_type_be_qualified(operform->oprleft) :
760
0
               format_type_be(operform->oprleft));
761
0
    else
762
0
      appendStringInfoString(&buf, "NONE,");
763
764
0
    if (operform->oprright)
765
0
      appendStringInfo(&buf, "%s)",
766
0
               (flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
767
0
               format_type_be_qualified(operform->oprright) :
768
0
               format_type_be(operform->oprright));
769
0
    else
770
0
      appendStringInfoString(&buf, "NONE)");
771
772
0
    result = buf.data;
773
774
0
    ReleaseSysCache(opertup);
775
0
  }
776
0
  else if ((flags & FORMAT_OPERATOR_INVALID_AS_NULL) != 0)
777
0
  {
778
    /* If object is undefined, return NULL as wanted by caller */
779
0
    result = NULL;
780
0
  }
781
0
  else
782
0
  {
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
0
  }
789
790
0
  return result;
791
0
}
792
793
char *
794
format_operator(Oid operator_oid)
795
0
{
796
0
  return format_operator_extended(operator_oid, 0);
797
0
}
798
799
char *
800
format_operator_qualified(Oid operator_oid)
801
0
{
802
0
  return format_operator_extended(operator_oid,
803
0
                  FORMAT_OPERATOR_FORCE_QUALIFY);
804
0
}
805
806
void
807
format_operator_parts(Oid operator_oid, List **objnames, List **objargs,
808
            bool missing_ok)
809
0
{
810
0
  HeapTuple opertup;
811
0
  Form_pg_operator oprForm;
812
813
0
  opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
814
0
  if (!HeapTupleIsValid(opertup))
815
0
  {
816
0
    if (!missing_ok)
817
0
      elog(ERROR, "cache lookup failed for operator with OID %u",
818
0
         operator_oid);
819
0
    return;
820
0
  }
821
822
0
  oprForm = (Form_pg_operator) GETSTRUCT(opertup);
823
0
  *objnames = list_make2(get_namespace_name_or_temp(oprForm->oprnamespace),
824
0
               pstrdup(NameStr(oprForm->oprname)));
825
0
  *objargs = NIL;
826
0
  if (oprForm->oprleft)
827
0
    *objargs = lappend(*objargs,
828
0
               format_type_be_qualified(oprForm->oprleft));
829
0
  if (oprForm->oprright)
830
0
    *objargs = lappend(*objargs,
831
0
               format_type_be_qualified(oprForm->oprright));
832
833
0
  ReleaseSysCache(opertup);
834
0
}
835
836
/*
837
 * regoperatorout   - converts operator OID to "opr_name(args)"
838
 */
839
Datum
840
regoperatorout(PG_FUNCTION_ARGS)
841
0
{
842
0
  Oid     oprid = PG_GETARG_OID(0);
843
0
  char     *result;
844
845
0
  if (oprid == InvalidOid)
846
0
    result = pstrdup("0");
847
0
  else
848
0
    result = format_operator(oprid);
849
850
0
  PG_RETURN_CSTRING(result);
851
0
}
852
853
/*
854
 *    regoperatorrecv     - converts external binary format to regoperator
855
 */
856
Datum
857
regoperatorrecv(PG_FUNCTION_ARGS)
858
0
{
859
  /* Exactly the same as oidrecv, so share code */
860
0
  return oidrecv(fcinfo);
861
0
}
862
863
/*
864
 *    regoperatorsend     - converts regoperator to binary format
865
 */
866
Datum
867
regoperatorsend(PG_FUNCTION_ARGS)
868
0
{
869
  /* Exactly the same as oidsend, so share code */
870
0
  return oidsend(fcinfo);
871
0
}
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
regclassin(PG_FUNCTION_ARGS)
884
0
{
885
0
  char     *class_name_or_oid = PG_GETARG_CSTRING(0);
886
0
  Node     *escontext = fcinfo->context;
887
0
  Oid     result;
888
0
  List     *names;
889
890
  /* Handle "-" or numeric OID */
891
0
  if (parseDashOrOid(class_name_or_oid, &result, escontext))
892
0
    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
0
  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
0
  names = stringToQualifiedNameList(class_name_or_oid, escontext);
905
0
  if (names == NIL)
906
0
    PG_RETURN_NULL();
907
908
  /* We might not even have permissions on this relation; don't lock it. */
909
0
  result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, true);
910
911
0
  if (!OidIsValid(result))
912
0
    ereturn(escontext, (Datum) 0,
913
0
        (errcode(ERRCODE_UNDEFINED_TABLE),
914
0
         errmsg("relation \"%s\" does not exist",
915
0
            NameListToString(names))));
916
917
0
  PG_RETURN_OID(result);
918
0
}
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
to_regclass(PG_FUNCTION_ARGS)
927
0
{
928
0
  char     *class_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
929
0
  Datum   result;
930
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
931
932
0
  if (!DirectInputFunctionCallSafe(regclassin, class_name,
933
0
                   InvalidOid, -1,
934
0
                   (Node *) &escontext,
935
0
                   &result))
936
0
    PG_RETURN_NULL();
937
0
  PG_RETURN_DATUM(result);
938
0
}
939
940
/*
941
 * regclassout    - converts class OID to "class_name"
942
 */
943
Datum
944
regclassout(PG_FUNCTION_ARGS)
945
0
{
946
0
  Oid     classid = PG_GETARG_OID(0);
947
0
  char     *result;
948
0
  HeapTuple classtup;
949
950
0
  if (classid == InvalidOid)
951
0
  {
952
0
    result = pstrdup("-");
953
0
    PG_RETURN_CSTRING(result);
954
0
  }
955
956
0
  classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(classid));
957
958
0
  if (HeapTupleIsValid(classtup))
959
0
  {
960
0
    Form_pg_class classform = (Form_pg_class) GETSTRUCT(classtup);
961
0
    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
0
    if (IsBootstrapProcessingMode())
969
0
      result = pstrdup(classname);
970
0
    else
971
0
    {
972
0
      char     *nspname;
973
974
      /*
975
       * Would this class be found by regclassin? If not, qualify it.
976
       */
977
0
      if (RelationIsVisible(classid))
978
0
        nspname = NULL;
979
0
      else
980
0
        nspname = get_namespace_name(classform->relnamespace);
981
982
0
      result = quote_qualified_identifier(nspname, classname);
983
0
    }
984
985
0
    ReleaseSysCache(classtup);
986
0
  }
987
0
  else
988
0
  {
989
    /* If OID doesn't match any pg_class entry, return it numerically */
990
0
    result = (char *) palloc(NAMEDATALEN);
991
0
    snprintf(result, NAMEDATALEN, "%u", classid);
992
0
  }
993
994
0
  PG_RETURN_CSTRING(result);
995
0
}
996
997
/*
998
 *    regclassrecv      - converts external binary format to regclass
999
 */
1000
Datum
1001
regclassrecv(PG_FUNCTION_ARGS)
1002
0
{
1003
  /* Exactly the same as oidrecv, so share code */
1004
0
  return oidrecv(fcinfo);
1005
0
}
1006
1007
/*
1008
 *    regclasssend      - converts regclass to binary format
1009
 */
1010
Datum
1011
regclasssend(PG_FUNCTION_ARGS)
1012
0
{
1013
  /* Exactly the same as oidsend, so share code */
1014
0
  return oidsend(fcinfo);
1015
0
}
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
regcollationin(PG_FUNCTION_ARGS)
1028
0
{
1029
0
  char     *collation_name_or_oid = PG_GETARG_CSTRING(0);
1030
0
  Node     *escontext = fcinfo->context;
1031
0
  Oid     result;
1032
0
  List     *names;
1033
1034
  /* Handle "-" or numeric OID */
1035
0
  if (parseDashOrOid(collation_name_or_oid, &result, escontext))
1036
0
    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
0
  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
0
  names = stringToQualifiedNameList(collation_name_or_oid, escontext);
1049
0
  if (names == NIL)
1050
0
    PG_RETURN_NULL();
1051
1052
0
  result = get_collation_oid(names, true);
1053
1054
0
  if (!OidIsValid(result))
1055
0
    ereturn(escontext, (Datum) 0,
1056
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1057
0
         errmsg("collation \"%s\" for encoding \"%s\" does not exist",
1058
0
            NameListToString(names), GetDatabaseEncodingName())));
1059
1060
0
  PG_RETURN_OID(result);
1061
0
}
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
to_regcollation(PG_FUNCTION_ARGS)
1070
0
{
1071
0
  char     *collation_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1072
0
  Datum   result;
1073
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
1074
1075
0
  if (!DirectInputFunctionCallSafe(regcollationin, collation_name,
1076
0
                   InvalidOid, -1,
1077
0
                   (Node *) &escontext,
1078
0
                   &result))
1079
0
    PG_RETURN_NULL();
1080
0
  PG_RETURN_DATUM(result);
1081
0
}
1082
1083
/*
1084
 * regcollationout    - converts collation OID to "collation_name"
1085
 */
1086
Datum
1087
regcollationout(PG_FUNCTION_ARGS)
1088
0
{
1089
0
  Oid     collationid = PG_GETARG_OID(0);
1090
0
  char     *result;
1091
0
  HeapTuple collationtup;
1092
1093
0
  if (collationid == InvalidOid)
1094
0
  {
1095
0
    result = pstrdup("-");
1096
0
    PG_RETURN_CSTRING(result);
1097
0
  }
1098
1099
0
  collationtup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationid));
1100
1101
0
  if (HeapTupleIsValid(collationtup))
1102
0
  {
1103
0
    Form_pg_collation collationform = (Form_pg_collation) GETSTRUCT(collationtup);
1104
0
    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
0
    if (IsBootstrapProcessingMode())
1112
0
      result = pstrdup(collationname);
1113
0
    else
1114
0
    {
1115
0
      char     *nspname;
1116
1117
      /*
1118
       * Would this collation be found by regcollationin? If not,
1119
       * qualify it.
1120
       */
1121
0
      if (CollationIsVisible(collationid))
1122
0
        nspname = NULL;
1123
0
      else
1124
0
        nspname = get_namespace_name(collationform->collnamespace);
1125
1126
0
      result = quote_qualified_identifier(nspname, collationname);
1127
0
    }
1128
1129
0
    ReleaseSysCache(collationtup);
1130
0
  }
1131
0
  else
1132
0
  {
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
0
  }
1137
1138
0
  PG_RETURN_CSTRING(result);
1139
0
}
1140
1141
/*
1142
 *    regcollationrecv      - converts external binary format to regcollation
1143
 */
1144
Datum
1145
regcollationrecv(PG_FUNCTION_ARGS)
1146
0
{
1147
  /* Exactly the same as oidrecv, so share code */
1148
0
  return oidrecv(fcinfo);
1149
0
}
1150
1151
/*
1152
 *    regcollationsend      - converts regcollation to binary format
1153
 */
1154
Datum
1155
regcollationsend(PG_FUNCTION_ARGS)
1156
0
{
1157
  /* Exactly the same as oidsend, so share code */
1158
0
  return oidsend(fcinfo);
1159
0
}
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
regtypein(PG_FUNCTION_ARGS)
1178
0
{
1179
0
  char     *typ_name_or_oid = PG_GETARG_CSTRING(0);
1180
0
  Node     *escontext = fcinfo->context;
1181
0
  Oid     result;
1182
0
  int32   typmod;
1183
1184
  /* Handle "-" or numeric OID */
1185
0
  if (parseDashOrOid(typ_name_or_oid, &result, escontext))
1186
0
    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
0
  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
0
  (void) parseTypeString(typ_name_or_oid, &result, &typmod, escontext);
1200
1201
0
  PG_RETURN_OID(result);
1202
0
}
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
to_regtype(PG_FUNCTION_ARGS)
1211
0
{
1212
0
  char     *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1213
0
  Datum   result;
1214
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
1215
1216
0
  if (!DirectInputFunctionCallSafe(regtypein, typ_name,
1217
0
                   InvalidOid, -1,
1218
0
                   (Node *) &escontext,
1219
0
                   &result))
1220
0
    PG_RETURN_NULL();
1221
0
  PG_RETURN_DATUM(result);
1222
0
}
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
to_regtypemod(PG_FUNCTION_ARGS)
1231
0
{
1232
0
  char     *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1233
0
  Oid     typid;
1234
0
  int32   typmod;
1235
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
1236
1237
  /* We rely on parseTypeString to parse the input. */
1238
0
  if (!parseTypeString(typ_name, &typid, &typmod, (Node *) &escontext))
1239
0
    PG_RETURN_NULL();
1240
1241
0
  PG_RETURN_INT32(typmod);
1242
0
}
1243
1244
/*
1245
 * regtypeout   - converts type OID to "typ_name"
1246
 */
1247
Datum
1248
regtypeout(PG_FUNCTION_ARGS)
1249
0
{
1250
0
  Oid     typid = PG_GETARG_OID(0);
1251
0
  char     *result;
1252
0
  HeapTuple typetup;
1253
1254
0
  if (typid == InvalidOid)
1255
0
  {
1256
0
    result = pstrdup("-");
1257
0
    PG_RETURN_CSTRING(result);
1258
0
  }
1259
1260
0
  typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
1261
1262
0
  if (HeapTupleIsValid(typetup))
1263
0
  {
1264
0
    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
0
    if (IsBootstrapProcessingMode())
1272
0
    {
1273
0
      char     *typname = NameStr(typeform->typname);
1274
1275
0
      result = pstrdup(typname);
1276
0
    }
1277
0
    else
1278
0
      result = format_type_be(typid);
1279
1280
0
    ReleaseSysCache(typetup);
1281
0
  }
1282
0
  else
1283
0
  {
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
0
  }
1288
1289
0
  PG_RETURN_CSTRING(result);
1290
0
}
1291
1292
/*
1293
 *    regtyperecv     - converts external binary format to regtype
1294
 */
1295
Datum
1296
regtyperecv(PG_FUNCTION_ARGS)
1297
0
{
1298
  /* Exactly the same as oidrecv, so share code */
1299
0
  return oidrecv(fcinfo);
1300
0
}
1301
1302
/*
1303
 *    regtypesend     - converts regtype to binary format
1304
 */
1305
Datum
1306
regtypesend(PG_FUNCTION_ARGS)
1307
0
{
1308
  /* Exactly the same as oidsend, so share code */
1309
0
  return oidsend(fcinfo);
1310
0
}
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
regconfigin(PG_FUNCTION_ARGS)
1323
0
{
1324
0
  char     *cfg_name_or_oid = PG_GETARG_CSTRING(0);
1325
0
  Node     *escontext = fcinfo->context;
1326
0
  Oid     result;
1327
0
  List     *names;
1328
1329
  /* Handle "-" or numeric OID */
1330
0
  if (parseDashOrOid(cfg_name_or_oid, &result, escontext))
1331
0
    PG_RETURN_OID(result);
1332
1333
  /* The rest of this wouldn't work in bootstrap mode */
1334
0
  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
0
  names = stringToQualifiedNameList(cfg_name_or_oid, escontext);
1342
0
  if (names == NIL)
1343
0
    PG_RETURN_NULL();
1344
1345
0
  result = get_ts_config_oid(names, true);
1346
1347
0
  if (!OidIsValid(result))
1348
0
    ereturn(escontext, (Datum) 0,
1349
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1350
0
         errmsg("text search configuration \"%s\" does not exist",
1351
0
            NameListToString(names))));
1352
1353
0
  PG_RETURN_OID(result);
1354
0
}
1355
1356
/*
1357
 * regconfigout   - converts tsconfig OID to "tsconfigname"
1358
 */
1359
Datum
1360
regconfigout(PG_FUNCTION_ARGS)
1361
0
{
1362
0
  Oid     cfgid = PG_GETARG_OID(0);
1363
0
  char     *result;
1364
0
  HeapTuple cfgtup;
1365
1366
0
  if (cfgid == InvalidOid)
1367
0
  {
1368
0
    result = pstrdup("-");
1369
0
    PG_RETURN_CSTRING(result);
1370
0
  }
1371
1372
0
  cfgtup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
1373
1374
0
  if (HeapTupleIsValid(cfgtup))
1375
0
  {
1376
0
    Form_pg_ts_config cfgform = (Form_pg_ts_config) GETSTRUCT(cfgtup);
1377
0
    char     *cfgname = NameStr(cfgform->cfgname);
1378
0
    char     *nspname;
1379
1380
    /*
1381
     * Would this config be found by regconfigin? If not, qualify it.
1382
     */
1383
0
    if (TSConfigIsVisible(cfgid))
1384
0
      nspname = NULL;
1385
0
    else
1386
0
      nspname = get_namespace_name(cfgform->cfgnamespace);
1387
1388
0
    result = quote_qualified_identifier(nspname, cfgname);
1389
1390
0
    ReleaseSysCache(cfgtup);
1391
0
  }
1392
0
  else
1393
0
  {
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
0
  }
1398
1399
0
  PG_RETURN_CSTRING(result);
1400
0
}
1401
1402
/*
1403
 *    regconfigrecv     - converts external binary format to regconfig
1404
 */
1405
Datum
1406
regconfigrecv(PG_FUNCTION_ARGS)
1407
0
{
1408
  /* Exactly the same as oidrecv, so share code */
1409
0
  return oidrecv(fcinfo);
1410
0
}
1411
1412
/*
1413
 *    regconfigsend     - converts regconfig to binary format
1414
 */
1415
Datum
1416
regconfigsend(PG_FUNCTION_ARGS)
1417
0
{
1418
  /* Exactly the same as oidsend, so share code */
1419
0
  return oidsend(fcinfo);
1420
0
}
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
regdictionaryin(PG_FUNCTION_ARGS)
1433
0
{
1434
0
  char     *dict_name_or_oid = PG_GETARG_CSTRING(0);
1435
0
  Node     *escontext = fcinfo->context;
1436
0
  Oid     result;
1437
0
  List     *names;
1438
1439
  /* Handle "-" or numeric OID */
1440
0
  if (parseDashOrOid(dict_name_or_oid, &result, escontext))
1441
0
    PG_RETURN_OID(result);
1442
1443
  /* The rest of this wouldn't work in bootstrap mode */
1444
0
  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
0
  names = stringToQualifiedNameList(dict_name_or_oid, escontext);
1452
0
  if (names == NIL)
1453
0
    PG_RETURN_NULL();
1454
1455
0
  result = get_ts_dict_oid(names, true);
1456
1457
0
  if (!OidIsValid(result))
1458
0
    ereturn(escontext, (Datum) 0,
1459
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1460
0
         errmsg("text search dictionary \"%s\" does not exist",
1461
0
            NameListToString(names))));
1462
1463
0
  PG_RETURN_OID(result);
1464
0
}
1465
1466
/*
1467
 * regdictionaryout   - converts tsdictionary OID to "tsdictionaryname"
1468
 */
1469
Datum
1470
regdictionaryout(PG_FUNCTION_ARGS)
1471
0
{
1472
0
  Oid     dictid = PG_GETARG_OID(0);
1473
0
  char     *result;
1474
0
  HeapTuple dicttup;
1475
1476
0
  if (dictid == InvalidOid)
1477
0
  {
1478
0
    result = pstrdup("-");
1479
0
    PG_RETURN_CSTRING(result);
1480
0
  }
1481
1482
0
  dicttup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictid));
1483
1484
0
  if (HeapTupleIsValid(dicttup))
1485
0
  {
1486
0
    Form_pg_ts_dict dictform = (Form_pg_ts_dict) GETSTRUCT(dicttup);
1487
0
    char     *dictname = NameStr(dictform->dictname);
1488
0
    char     *nspname;
1489
1490
    /*
1491
     * Would this dictionary be found by regdictionaryin? If not, qualify
1492
     * it.
1493
     */
1494
0
    if (TSDictionaryIsVisible(dictid))
1495
0
      nspname = NULL;
1496
0
    else
1497
0
      nspname = get_namespace_name(dictform->dictnamespace);
1498
1499
0
    result = quote_qualified_identifier(nspname, dictname);
1500
1501
0
    ReleaseSysCache(dicttup);
1502
0
  }
1503
0
  else
1504
0
  {
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
0
  }
1509
1510
0
  PG_RETURN_CSTRING(result);
1511
0
}
1512
1513
/*
1514
 *    regdictionaryrecv - converts external binary format to regdictionary
1515
 */
1516
Datum
1517
regdictionaryrecv(PG_FUNCTION_ARGS)
1518
0
{
1519
  /* Exactly the same as oidrecv, so share code */
1520
0
  return oidrecv(fcinfo);
1521
0
}
1522
1523
/*
1524
 *    regdictionarysend - converts regdictionary to binary format
1525
 */
1526
Datum
1527
regdictionarysend(PG_FUNCTION_ARGS)
1528
0
{
1529
  /* Exactly the same as oidsend, so share code */
1530
0
  return oidsend(fcinfo);
1531
0
}
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
regrolein(PG_FUNCTION_ARGS)
1543
0
{
1544
0
  char     *role_name_or_oid = PG_GETARG_CSTRING(0);
1545
0
  Node     *escontext = fcinfo->context;
1546
0
  Oid     result;
1547
0
  List     *names;
1548
1549
  /* Handle "-" or numeric OID */
1550
0
  if (parseDashOrOid(role_name_or_oid, &result, escontext))
1551
0
    PG_RETURN_OID(result);
1552
1553
  /* The rest of this wouldn't work in bootstrap mode */
1554
0
  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
0
  names = stringToQualifiedNameList(role_name_or_oid, escontext);
1559
0
  if (names == NIL)
1560
0
    PG_RETURN_NULL();
1561
1562
0
  if (list_length(names) != 1)
1563
0
    ereturn(escontext, (Datum) 0,
1564
0
        (errcode(ERRCODE_INVALID_NAME),
1565
0
         errmsg("invalid name syntax")));
1566
1567
0
  result = get_role_oid(strVal(linitial(names)), true);
1568
1569
0
  if (!OidIsValid(result))
1570
0
    ereturn(escontext, (Datum) 0,
1571
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1572
0
         errmsg("role \"%s\" does not exist",
1573
0
            strVal(linitial(names)))));
1574
1575
0
  PG_RETURN_OID(result);
1576
0
}
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
to_regrole(PG_FUNCTION_ARGS)
1585
0
{
1586
0
  char     *role_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1587
0
  Datum   result;
1588
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
1589
1590
0
  if (!DirectInputFunctionCallSafe(regrolein, role_name,
1591
0
                   InvalidOid, -1,
1592
0
                   (Node *) &escontext,
1593
0
                   &result))
1594
0
    PG_RETURN_NULL();
1595
0
  PG_RETURN_DATUM(result);
1596
0
}
1597
1598
/*
1599
 * regroleout   - converts role OID to "role_name"
1600
 */
1601
Datum
1602
regroleout(PG_FUNCTION_ARGS)
1603
0
{
1604
0
  Oid     roleoid = PG_GETARG_OID(0);
1605
0
  char     *result;
1606
1607
0
  if (roleoid == InvalidOid)
1608
0
  {
1609
0
    result = pstrdup("-");
1610
0
    PG_RETURN_CSTRING(result);
1611
0
  }
1612
1613
0
  result = GetUserNameFromId(roleoid, true);
1614
1615
0
  if (result)
1616
0
  {
1617
    /* pstrdup is not really necessary, but it avoids a compiler warning */
1618
0
    result = pstrdup(quote_identifier(result));
1619
0
  }
1620
0
  else
1621
0
  {
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
0
  }
1626
1627
0
  PG_RETURN_CSTRING(result);
1628
0
}
1629
1630
/*
1631
 *    regrolerecv - converts external binary format to regrole
1632
 */
1633
Datum
1634
regrolerecv(PG_FUNCTION_ARGS)
1635
0
{
1636
  /* Exactly the same as oidrecv, so share code */
1637
0
  return oidrecv(fcinfo);
1638
0
}
1639
1640
/*
1641
 *    regrolesend - converts regrole to binary format
1642
 */
1643
Datum
1644
regrolesend(PG_FUNCTION_ARGS)
1645
0
{
1646
  /* Exactly the same as oidsend, so share code */
1647
0
  return oidsend(fcinfo);
1648
0
}
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
regnamespacein(PG_FUNCTION_ARGS)
1660
0
{
1661
0
  char     *nsp_name_or_oid = PG_GETARG_CSTRING(0);
1662
0
  Node     *escontext = fcinfo->context;
1663
0
  Oid     result;
1664
0
  List     *names;
1665
1666
  /* Handle "-" or numeric OID */
1667
0
  if (parseDashOrOid(nsp_name_or_oid, &result, escontext))
1668
0
    PG_RETURN_OID(result);
1669
1670
  /* The rest of this wouldn't work in bootstrap mode */
1671
0
  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
0
  names = stringToQualifiedNameList(nsp_name_or_oid, escontext);
1676
0
  if (names == NIL)
1677
0
    PG_RETURN_NULL();
1678
1679
0
  if (list_length(names) != 1)
1680
0
    ereturn(escontext, (Datum) 0,
1681
0
        (errcode(ERRCODE_INVALID_NAME),
1682
0
         errmsg("invalid name syntax")));
1683
1684
0
  result = get_namespace_oid(strVal(linitial(names)), true);
1685
1686
0
  if (!OidIsValid(result))
1687
0
    ereturn(escontext, (Datum) 0,
1688
0
        (errcode(ERRCODE_UNDEFINED_SCHEMA),
1689
0
         errmsg("schema \"%s\" does not exist",
1690
0
            strVal(linitial(names)))));
1691
1692
0
  PG_RETURN_OID(result);
1693
0
}
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
to_regnamespace(PG_FUNCTION_ARGS)
1702
0
{
1703
0
  char     *nsp_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1704
0
  Datum   result;
1705
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
1706
1707
0
  if (!DirectInputFunctionCallSafe(regnamespacein, nsp_name,
1708
0
                   InvalidOid, -1,
1709
0
                   (Node *) &escontext,
1710
0
                   &result))
1711
0
    PG_RETURN_NULL();
1712
0
  PG_RETURN_DATUM(result);
1713
0
}
1714
1715
/*
1716
 * regnamespaceout    - converts namespace OID to "nsp_name"
1717
 */
1718
Datum
1719
regnamespaceout(PG_FUNCTION_ARGS)
1720
0
{
1721
0
  Oid     nspid = PG_GETARG_OID(0);
1722
0
  char     *result;
1723
1724
0
  if (nspid == InvalidOid)
1725
0
  {
1726
0
    result = pstrdup("-");
1727
0
    PG_RETURN_CSTRING(result);
1728
0
  }
1729
1730
0
  result = get_namespace_name(nspid);
1731
1732
0
  if (result)
1733
0
  {
1734
    /* pstrdup is not really necessary, but it avoids a compiler warning */
1735
0
    result = pstrdup(quote_identifier(result));
1736
0
  }
1737
0
  else
1738
0
  {
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
0
  }
1743
1744
0
  PG_RETURN_CSTRING(result);
1745
0
}
1746
1747
/*
1748
 *    regnamespacerecv  - converts external binary format to regnamespace
1749
 */
1750
Datum
1751
regnamespacerecv(PG_FUNCTION_ARGS)
1752
0
{
1753
  /* Exactly the same as oidrecv, so share code */
1754
0
  return oidrecv(fcinfo);
1755
0
}
1756
1757
/*
1758
 *    regnamespacesend    - converts regnamespace to binary format
1759
 */
1760
Datum
1761
regnamespacesend(PG_FUNCTION_ARGS)
1762
0
{
1763
  /* Exactly the same as oidsend, so share code */
1764
0
  return oidsend(fcinfo);
1765
0
}
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
regdatabasein(PG_FUNCTION_ARGS)
1777
0
{
1778
0
  char     *db_name_or_oid = PG_GETARG_CSTRING(0);
1779
0
  Node     *escontext = fcinfo->context;
1780
0
  Oid     result;
1781
0
  List     *names;
1782
1783
  /* Handle "-" or numeric OID */
1784
0
  if (parseDashOrOid(db_name_or_oid, &result, escontext))
1785
0
    PG_RETURN_OID(result);
1786
1787
  /* The rest of this wouldn't work in bootstrap mode */
1788
0
  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
0
  names = stringToQualifiedNameList(db_name_or_oid, escontext);
1793
0
  if (names == NIL)
1794
0
    PG_RETURN_NULL();
1795
1796
0
  if (list_length(names) != 1)
1797
0
    ereturn(escontext, (Datum) 0,
1798
0
        (errcode(ERRCODE_INVALID_NAME),
1799
0
         errmsg("invalid name syntax")));
1800
1801
0
  result = get_database_oid(strVal(linitial(names)), true);
1802
1803
0
  if (!OidIsValid(result))
1804
0
    ereturn(escontext, (Datum) 0,
1805
0
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1806
0
         errmsg("database \"%s\" does not exist",
1807
0
            strVal(linitial(names)))));
1808
1809
0
  PG_RETURN_OID(result);
1810
0
}
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
to_regdatabase(PG_FUNCTION_ARGS)
1819
0
{
1820
0
  char     *db_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
1821
0
  Datum   result;
1822
0
  ErrorSaveContext escontext = {T_ErrorSaveContext};
1823
1824
0
  if (!DirectInputFunctionCallSafe(regdatabasein, db_name,
1825
0
                   InvalidOid, -1,
1826
0
                   (Node *) &escontext,
1827
0
                   &result))
1828
0
    PG_RETURN_NULL();
1829
0
  PG_RETURN_DATUM(result);
1830
0
}
1831
1832
/*
1833
 * regdatabaseout - converts database OID to database name
1834
 */
1835
Datum
1836
regdatabaseout(PG_FUNCTION_ARGS)
1837
0
{
1838
0
  Oid     dboid = PG_GETARG_OID(0);
1839
0
  char     *result;
1840
1841
0
  if (dboid == InvalidOid)
1842
0
  {
1843
0
    result = pstrdup("-");
1844
0
    PG_RETURN_CSTRING(result);
1845
0
  }
1846
1847
0
  result = get_database_name(dboid);
1848
1849
0
  if (result)
1850
0
  {
1851
    /* pstrdup is not really necessary, but it avoids a compiler warning */
1852
0
    result = pstrdup(quote_identifier(result));
1853
0
  }
1854
0
  else
1855
0
  {
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
0
  }
1860
1861
0
  PG_RETURN_CSTRING(result);
1862
0
}
1863
1864
/*
1865
 * regdatabaserecv - converts external binary format to regdatabase
1866
 */
1867
Datum
1868
regdatabaserecv(PG_FUNCTION_ARGS)
1869
0
{
1870
  /* Exactly the same as oidrecv, so share code */
1871
0
  return oidrecv(fcinfo);
1872
0
}
1873
1874
/*
1875
 * regdatabasesend - converts regdatabase to binary format
1876
 */
1877
Datum
1878
regdatabasesend(PG_FUNCTION_ARGS)
1879
0
{
1880
  /* Exactly the same as oidsend, so share code */
1881
0
  return oidsend(fcinfo);
1882
0
}
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
text_regclass(PG_FUNCTION_ARGS)
1893
0
{
1894
0
  text     *relname = PG_GETARG_TEXT_PP(0);
1895
0
  Oid     result;
1896
0
  RangeVar   *rv;
1897
1898
0
  rv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
1899
1900
  /* We might not even have permissions on this relation; don't lock it. */
1901
0
  result = RangeVarGetRelid(rv, NoLock, false);
1902
1903
0
  PG_RETURN_OID(result);
1904
0
}
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
stringToQualifiedNameList(const char *string, Node *escontext)
1916
0
{
1917
0
  char     *rawname;
1918
0
  List     *result = NIL;
1919
0
  List     *namelist;
1920
0
  ListCell   *l;
1921
1922
  /* We need a modifiable copy of the input string. */
1923
0
  rawname = pstrdup(string);
1924
1925
0
  if (!SplitIdentifierString(rawname, '.', &namelist))
1926
0
    ereturn(escontext, NIL,
1927
0
        (errcode(ERRCODE_INVALID_NAME),
1928
0
         errmsg("invalid name syntax")));
1929
1930
0
  if (namelist == NIL)
1931
0
    ereturn(escontext, NIL,
1932
0
        (errcode(ERRCODE_INVALID_NAME),
1933
0
         errmsg("invalid name syntax")));
1934
1935
0
  foreach(l, namelist)
1936
0
  {
1937
0
    char     *curname = (char *) lfirst(l);
1938
1939
0
    result = lappend(result, makeString(pstrdup(curname)));
1940
0
  }
1941
1942
0
  pfree(rawname);
1943
0
  list_free(namelist);
1944
1945
0
  return result;
1946
0
}
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
parseNumericOid(char *string, Oid *result, Node *escontext)
1962
0
{
1963
0
  if (string[0] >= '0' && string[0] <= '9' &&
1964
0
    strspn(string, "0123456789") == strlen(string))
1965
0
  {
1966
0
    Datum   oid_datum;
1967
1968
    /* We need not care here whether oidin() fails or not. */
1969
0
    (void) DirectInputFunctionCallSafe(oidin, string,
1970
0
                       InvalidOid, -1,
1971
0
                       escontext,
1972
0
                       &oid_datum);
1973
0
    *result = DatumGetObjectId(oid_datum);
1974
0
    return true;
1975
0
  }
1976
1977
  /* Prevent uninitialized-variable warnings from stupider compilers. */
1978
0
  *result = InvalidOid;
1979
0
  return false;
1980
0
}
1981
1982
/*
1983
 * As above, but also accept "-" as meaning 0 (InvalidOid).
1984
 */
1985
static bool
1986
parseDashOrOid(char *string, Oid *result, Node *escontext)
1987
0
{
1988
  /* '-' ? */
1989
0
  if (strcmp(string, "-") == 0)
1990
0
  {
1991
0
    *result = InvalidOid;
1992
0
    return true;
1993
0
  }
1994
1995
  /* Numeric OID? */
1996
0
  return parseNumericOid(string, result, escontext);
1997
0
}
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
parseNameAndArgTypes(const char *string, bool allowNone, List **names,
2014
           int *nargs, Oid *argtypes,
2015
           Node *escontext)
2016
0
{
2017
0
  char     *rawname;
2018
0
  char     *ptr;
2019
0
  char     *ptr2;
2020
0
  char     *typename;
2021
0
  bool    in_quote;
2022
0
  bool    had_comma;
2023
0
  int     paren_count;
2024
0
  Oid     typeid;
2025
0
  int32   typmod;
2026
2027
  /* We need a modifiable copy of the input string. */
2028
0
  rawname = pstrdup(string);
2029
2030
  /* Scan to find the expected left paren; mustn't be quoted */
2031
0
  in_quote = false;
2032
0
  for (ptr = rawname; *ptr; ptr++)
2033
0
  {
2034
0
    if (*ptr == '"')
2035
0
      in_quote = !in_quote;
2036
0
    else if (*ptr == '(' && !in_quote)
2037
0
      break;
2038
0
  }
2039
0
  if (*ptr == '\0')
2040
0
    ereturn(escontext, false,
2041
0
        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2042
0
         errmsg("expected a left parenthesis")));
2043
2044
  /* Separate the name and parse it into a list */
2045
0
  *ptr++ = '\0';
2046
0
  *names = stringToQualifiedNameList(rawname, escontext);
2047
0
  if (*names == NIL)
2048
0
    return false;
2049
2050
  /* Check for the trailing right parenthesis and remove it */
2051
0
  ptr2 = ptr + strlen(ptr);
2052
0
  while (--ptr2 > ptr)
2053
0
  {
2054
0
    if (!scanner_isspace(*ptr2))
2055
0
      break;
2056
0
  }
2057
0
  if (*ptr2 != ')')
2058
0
    ereturn(escontext, false,
2059
0
        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2060
0
         errmsg("expected a right parenthesis")));
2061
2062
0
  *ptr2 = '\0';
2063
2064
  /* Separate the remaining string into comma-separated type names */
2065
0
  *nargs = 0;
2066
0
  had_comma = false;
2067
2068
0
  for (;;)
2069
0
  {
2070
    /* allow leading whitespace */
2071
0
    while (scanner_isspace(*ptr))
2072
0
      ptr++;
2073
0
    if (*ptr == '\0')
2074
0
    {
2075
      /* End of string.  Okay unless we had a comma before. */
2076
0
      if (had_comma)
2077
0
        ereturn(escontext, false,
2078
0
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2079
0
             errmsg("expected a type name")));
2080
0
      break;
2081
0
    }
2082
0
    typename = ptr;
2083
    /* Find end of type name --- end of string or comma */
2084
    /* ... but not a quoted or parenthesized comma */
2085
0
    in_quote = false;
2086
0
    paren_count = 0;
2087
0
    for (; *ptr; ptr++)
2088
0
    {
2089
0
      if (*ptr == '"')
2090
0
        in_quote = !in_quote;
2091
0
      else if (*ptr == ',' && !in_quote && paren_count == 0)
2092
0
        break;
2093
0
      else if (!in_quote)
2094
0
      {
2095
0
        switch (*ptr)
2096
0
        {
2097
0
          case '(':
2098
0
          case '[':
2099
0
            paren_count++;
2100
0
            break;
2101
0
          case ')':
2102
0
          case ']':
2103
0
            paren_count--;
2104
0
            break;
2105
0
        }
2106
0
      }
2107
0
    }
2108
0
    if (in_quote || paren_count != 0)
2109
0
      ereturn(escontext, false,
2110
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2111
0
           errmsg("improper type name")));
2112
2113
0
    ptr2 = ptr;
2114
0
    if (*ptr == ',')
2115
0
    {
2116
0
      had_comma = true;
2117
0
      *ptr++ = '\0';
2118
0
    }
2119
0
    else
2120
0
    {
2121
0
      had_comma = false;
2122
0
      Assert(*ptr == '\0');
2123
0
    }
2124
    /* Lop off trailing whitespace */
2125
0
    while (--ptr2 >= typename)
2126
0
    {
2127
0
      if (!scanner_isspace(*ptr2))
2128
0
        break;
2129
0
      *ptr2 = '\0';
2130
0
    }
2131
2132
0
    if (allowNone && pg_strcasecmp(typename, "none") == 0)
2133
0
    {
2134
      /* Special case for NONE */
2135
0
      typeid = InvalidOid;
2136
0
      typmod = -1;
2137
0
    }
2138
0
    else
2139
0
    {
2140
      /* Use full parser to resolve the type name */
2141
0
      if (!parseTypeString(typename, &typeid, &typmod, escontext))
2142
0
        return false;
2143
0
    }
2144
0
    if (*nargs >= FUNC_MAX_ARGS)
2145
0
      ereturn(escontext, false,
2146
0
          (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
2147
0
           errmsg("too many arguments")));
2148
2149
0
    argtypes[*nargs] = typeid;
2150
0
    (*nargs)++;
2151
0
  }
2152
2153
0
  pfree(rawname);
2154
2155
0
  return true;
2156
0
}