YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/postgres/src/backend/commands/functioncmds.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * functioncmds.c
4
 *
5
 *    Routines for CREATE and DROP FUNCTION commands and CREATE and DROP
6
 *    CAST commands.
7
 *
8
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
9
 * Portions Copyright (c) 1994, Regents of the University of California
10
 *
11
 *
12
 * IDENTIFICATION
13
 *    src/backend/commands/functioncmds.c
14
 *
15
 * DESCRIPTION
16
 *    These routines take the parse tree and pick out the
17
 *    appropriate arguments/flags, and pass the results to the
18
 *    corresponding "FooDefine" routines (in src/catalog) that do
19
 *    the actual catalog-munging.  These routines also verify permission
20
 *    of the user to execute the command.
21
 *
22
 * NOTES
23
 *    These things must be defined and committed in the following order:
24
 *    "create function":
25
 *        input/output, recv/send procedures
26
 *    "create type":
27
 *        type
28
 *    "create operator":
29
 *        operators
30
 *
31
 *-------------------------------------------------------------------------
32
 */
33
#include "postgres.h"
34
35
#include "access/genam.h"
36
#include "access/heapam.h"
37
#include "access/htup_details.h"
38
#include "access/sysattr.h"
39
#include "catalog/dependency.h"
40
#include "catalog/indexing.h"
41
#include "catalog/objectaccess.h"
42
#include "catalog/pg_aggregate.h"
43
#include "catalog/pg_cast.h"
44
#include "catalog/pg_language.h"
45
#include "catalog/pg_namespace.h"
46
#include "catalog/pg_proc.h"
47
#include "catalog/pg_transform.h"
48
#include "catalog/pg_type.h"
49
#include "commands/alter.h"
50
#include "commands/defrem.h"
51
#include "commands/extension.h"
52
#include "commands/proclang.h"
53
#include "executor/execdesc.h"
54
#include "executor/executor.h"
55
#include "funcapi.h"
56
#include "miscadmin.h"
57
#include "optimizer/clauses.h"
58
#include "optimizer/var.h"
59
#include "parser/parse_coerce.h"
60
#include "parser/parse_collate.h"
61
#include "parser/parse_expr.h"
62
#include "parser/parse_func.h"
63
#include "parser/parse_type.h"
64
#include "pgstat.h"
65
#include "utils/acl.h"
66
#include "utils/builtins.h"
67
#include "utils/fmgroids.h"
68
#include "utils/guc.h"
69
#include "utils/lsyscache.h"
70
#include "utils/memutils.h"
71
#include "utils/rel.h"
72
#include "utils/syscache.h"
73
#include "utils/typcache.h"
74
#include "utils/tqual.h"
75
76
/*
77
 *   Examine the RETURNS clause of the CREATE FUNCTION statement
78
 *   and return information about it as *prorettype_p and *returnsSet.
79
 *
80
 * This is more complex than the average typename lookup because we want to
81
 * allow a shell type to be used, or even created if the specified return type
82
 * doesn't exist yet.  (Without this, there's no way to define the I/O procs
83
 * for a new type.)  But SQL function creation won't cope, so error out if
84
 * the target language is SQL.  (We do this here, not in the SQL-function
85
 * validator, so as not to produce a NOTICE and then an ERROR for the same
86
 * condition.)
87
 */
88
static void
89
compute_return_type(TypeName *returnType, Oid languageOid,
90
          Oid *prorettype_p, bool *returnsSet_p)
91
2.88k
{
92
2.88k
  Oid     rettype;
93
2.88k
  Type    typtup;
94
2.88k
  AclResult aclresult;
95
96
2.88k
  typtup = LookupTypeName(NULL, returnType, NULL, false);
97
98
2.88k
  if (typtup)
99
2.83k
  {
100
2.83k
    if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
101
62
    {
102
62
      if (languageOid == SQLlanguageId)
103
62
        ereport(ERROR,
104
62
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
105
62
             errmsg("SQL function cannot return shell type %s",
106
62
                TypeNameToString(returnType))));
107
62
      else
108
62
        ereport(NOTICE,
109
62
            (errcode(ERRCODE_WRONG_OBJECT_TYPE),
110
62
             errmsg("return type %s is only a shell",
111
62
                TypeNameToString(returnType))));
112
62
    }
113
2.83k
    rettype = typeTypeId(typtup);
114
2.83k
    ReleaseSysCache(typtup);
115
2.83k
  }
116
46
  else
117
46
  {
118
46
    char     *typnam = TypeNameToString(returnType);
119
46
    Oid     namespaceId;
120
46
    AclResult aclresult;
121
46
    char     *typname;
122
46
    ObjectAddress address;
123
124
    /*
125
     * Only C-coded functions can be I/O functions.  We enforce this
126
     * restriction here mainly to prevent littering the catalogs with
127
     * shell types due to simple typos in user-defined function
128
     * definitions.
129
     */
130
46
    if (languageOid != INTERNALlanguageId &&
131
46
      languageOid != ClanguageId)
132
46
      ereport(ERROR,
133
46
          (errcode(ERRCODE_UNDEFINED_OBJECT),
134
46
           errmsg("type \"%s\" does not exist", typnam)));
135
136
    /* Reject if there's typmod decoration, too */
137
46
    if (returnType->typmods != NIL)
138
46
      ereport(ERROR,
139
46
          (errcode(ERRCODE_SYNTAX_ERROR),
140
46
           errmsg("type modifier cannot be specified for shell type \"%s\"",
141
46
              typnam)));
142
143
    /* Otherwise, go ahead and make a shell type */
144
46
    ereport(NOTICE,
145
46
        (errcode(ERRCODE_UNDEFINED_OBJECT),
146
46
         errmsg("type \"%s\" is not yet defined", typnam),
147
46
         errdetail("Creating a shell type definition.")));
148
46
    namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
149
46
                            &typname);
150
46
    aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
151
46
                      ACL_CREATE);
152
46
    if (aclresult != ACLCHECK_OK)
153
0
      aclcheck_error(aclresult, OBJECT_SCHEMA,
154
0
               get_namespace_name(namespaceId));
155
46
    address = TypeShellMake(typname, namespaceId, GetUserId());
156
46
    rettype = address.objectId;
157
46
    Assert(OidIsValid(rettype));
158
46
  }
159
160
2.88k
  aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE);
161
2.88k
  if (aclresult != ACLCHECK_OK)
162
1
    aclcheck_error_type(aclresult, rettype);
163
164
2.88k
  *prorettype_p = rettype;
165
2.88k
  *returnsSet_p = returnType->setof;
166
2.88k
}
167
168
/*
169
 * Interpret the function parameter list of a CREATE FUNCTION or
170
 * CREATE AGGREGATE statement.
171
 *
172
 * Input parameters:
173
 * parameters: list of FunctionParameter structs
174
 * languageOid: OID of function language (InvalidOid if it's CREATE AGGREGATE)
175
 * is_aggregate: needed only to determine error handling
176
 *
177
 * Results are stored into output parameters.  parameterTypes must always
178
 * be created, but the other arrays are set to NULL if not needed.
179
 * variadicArgType is set to the variadic array type if there's a VARIADIC
180
 * parameter (there can be only one); or to InvalidOid if not.
181
 * requiredResultType is set to InvalidOid if there are no OUT parameters,
182
 * else it is set to the OID of the implied result type.
183
 */
184
void
185
interpret_function_parameter_list(ParseState *pstate,
186
                  List *parameters,
187
                  Oid languageOid,
188
                  ObjectType objtype,
189
                  oidvector **parameterTypes,
190
                  ArrayType **allParameterTypes,
191
                  ArrayType **parameterModes,
192
                  ArrayType **parameterNames,
193
                  List **parameterDefaults,
194
                  Oid *variadicArgType,
195
                  Oid *requiredResultType)
196
2.95k
{
197
2.95k
  int     parameterCount = list_length(parameters);
198
2.95k
  Oid      *inTypes;
199
2.95k
  int     inCount = 0;
200
2.95k
  Datum    *allTypes;
201
2.95k
  Datum    *paramModes;
202
2.95k
  Datum    *paramNames;
203
2.95k
  int     outCount = 0;
204
2.95k
  int     varCount = 0;
205
2.95k
  bool    have_names = false;
206
2.95k
  bool    have_defaults = false;
207
2.95k
  ListCell   *x;
208
2.95k
  int     i;
209
210
2.95k
  *variadicArgType = InvalidOid; /* default result */
211
2.95k
  *requiredResultType = InvalidOid; /* default result */
212
213
2.95k
  inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
214
2.95k
  allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
215
2.95k
  paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
216
2.95k
  paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
217
2.95k
  *parameterDefaults = NIL;
218
219
  /* Scan the list and extract data into work arrays */
220
2.95k
  i = 0;
221
2.95k
  foreach(x, parameters)
222
5.98k
  {
223
5.98k
    FunctionParameter *fp = (FunctionParameter *) lfirst(x);
224
5.98k
    TypeName   *t = fp->argType;
225
5.98k
    bool    isinput = false;
226
5.98k
    Oid     toid;
227
5.98k
    Type    typtup;
228
5.98k
    AclResult aclresult;
229
230
5.98k
    typtup = LookupTypeName(NULL, t, NULL, false);
231
5.98k
    if (typtup)
232
5.98k
    {
233
5.98k
      if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
234
99
      {
235
        /* As above, hard error if language is SQL */
236
99
        if (languageOid == SQLlanguageId)
237
99
          ereport(ERROR,
238
99
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
239
99
               errmsg("SQL function cannot accept shell type %s",
240
99
                  TypeNameToString(t))));
241
        /* We don't allow creating aggregates on shell types either */
242
99
        else if (objtype == OBJECT_AGGREGATE)
243
99
          ereport(ERROR,
244
99
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
245
99
               errmsg("aggregate cannot accept shell type %s",
246
99
                  TypeNameToString(t))));
247
99
        else
248
99
          ereport(NOTICE,
249
99
              (errcode(ERRCODE_WRONG_OBJECT_TYPE),
250
99
               errmsg("argument type %s is only a shell",
251
99
                  TypeNameToString(t))));
252
99
      }
253
5.98k
      toid = typeTypeId(typtup);
254
5.98k
      ReleaseSysCache(typtup);
255
5.98k
    }
256
1
    else
257
1
    {
258
1
      ereport(ERROR,
259
1
          (errcode(ERRCODE_UNDEFINED_OBJECT),
260
1
           errmsg("type %s does not exist",
261
1
              TypeNameToString(t))));
262
1
      toid = InvalidOid; /* keep compiler quiet */
263
1
    }
264
265
5.98k
    aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
266
5.98k
    if (aclresult != ACLCHECK_OK)
267
2
      aclcheck_error_type(aclresult, toid);
268
269
5.98k
    if (t->setof)
270
0
    {
271
0
      if (objtype == OBJECT_AGGREGATE)
272
0
        ereport(ERROR,
273
0
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
274
0
             errmsg("aggregates cannot accept set arguments")));
275
0
      else if (objtype == OBJECT_PROCEDURE)
276
0
        ereport(ERROR,
277
0
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
278
0
             errmsg("procedures cannot accept set arguments")));
279
0
      else
280
0
        ereport(ERROR,
281
0
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
282
0
             errmsg("functions cannot accept set arguments")));
283
0
    }
284
285
5.98k
    if (objtype == OBJECT_PROCEDURE)
286
10
    {
287
10
      if (fp->mode == FUNC_PARAM_OUT)
288
10
        ereport(ERROR,
289
10
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
290
10
             (errmsg("procedures cannot have OUT arguments"),
291
10
              errhint("INOUT arguments are permitted."))));
292
10
    }
293
294
    /* handle input parameters */
295
5.98k
    if (fp->mode != FUNC_PARAM_OUT && 
fp->mode != FUNC_PARAM_TABLE4.54k
)
296
4.52k
    {
297
      /* other input parameters can't follow a VARIADIC parameter */
298
4.52k
      if (varCount > 0)
299
4.52k
        ereport(ERROR,
300
4.52k
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
301
4.52k
             errmsg("VARIADIC parameter must be the last input parameter")));
302
4.52k
      inTypes[inCount++] = toid;
303
4.52k
      isinput = true;
304
4.52k
    }
305
306
    /* handle output parameters */
307
5.98k
    if (fp->mode != FUNC_PARAM_IN && 
fp->mode != FUNC_PARAM_VARIADIC1.46k
)
308
1.45k
    {
309
1.45k
      if (objtype == OBJECT_PROCEDURE)
310
1
        *requiredResultType = RECORDOID;
311
1.45k
      else if (outCount == 0) /* save first output param's type */
312
119
        *requiredResultType = toid;
313
1.45k
      outCount++;
314
1.45k
    }
315
316
5.98k
    if (fp->mode == FUNC_PARAM_VARIADIC)
317
12
    {
318
12
      *variadicArgType = toid;
319
12
      varCount++;
320
      /* validate variadic parameter type */
321
12
      switch (toid)
322
12
      {
323
2
        case ANYARRAYOID:
324
2
        case ANYOID:
325
          /* okay */
326
2
          break;
327
10
        default:
328
10
          if (!OidIsValid(get_element_type(toid)))
329
10
            ereport(ERROR,
330
10
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
331
10
                 errmsg("VARIADIC parameter must be an array")));
332
10
          break;
333
12
      }
334
12
    }
335
336
5.98k
    allTypes[i] = ObjectIdGetDatum(toid);
337
338
5.98k
    paramModes[i] = CharGetDatum(fp->mode);
339
340
5.98k
    if (fp->name && 
fp->name[0]2.61k
)
341
2.61k
    {
342
2.61k
      ListCell   *px;
343
344
      /*
345
       * As of Postgres 9.0 we disallow using the same name for two
346
       * input or two output function parameters.  Depending on the
347
       * function's language, conflicting input and output names might
348
       * be bad too, but we leave it to the PL to complain if so.
349
       */
350
2.61k
      foreach(px, parameters)
351
28.6k
      {
352
28.6k
        FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
353
354
28.6k
        if (prevfp == fp)
355
2.61k
          break;
356
        /* pure in doesn't conflict with pure out */
357
26.0k
        if ((fp->mode == FUNC_PARAM_IN ||
358
26.0k
           
fp->mode == FUNC_PARAM_VARIADIC24.9k
) &&
359
26.0k
          
(1.05k
prevfp->mode == FUNC_PARAM_OUT1.05k
||
360
1.05k
           
prevfp->mode == FUNC_PARAM_TABLE1.04k
))
361
13
          continue;
362
26.0k
        if ((prevfp->mode == FUNC_PARAM_IN ||
363
26.0k
           
prevfp->mode == FUNC_PARAM_VARIADIC23.5k
) &&
364
26.0k
          
(2.48k
fp->mode == FUNC_PARAM_OUT2.48k
||
365
2.48k
           
fp->mode == FUNC_PARAM_TABLE1.05k
))
366
1.44k
          continue;
367
24.5k
        if (prevfp->name && prevfp->name[0] &&
368
24.5k
          strcmp(prevfp->name, fp->name) == 0)
369
24.5k
          ereport(ERROR,
370
24.5k
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
371
24.5k
               errmsg("parameter name \"%s\" used more than once",
372
24.5k
                  fp->name)));
373
24.5k
      }
374
375
2.61k
      paramNames[i] = CStringGetTextDatum(fp->name);
376
2.61k
      have_names = true;
377
2.61k
    }
378
379
5.98k
    if (fp->defexpr)
380
93
    {
381
93
      Node     *def;
382
383
93
      if (!isinput)
384
93
        ereport(ERROR,
385
93
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
386
93
             errmsg("only input parameters can have default values")));
387
388
93
      def = transformExpr(pstate, fp->defexpr,
389
93
                EXPR_KIND_FUNCTION_DEFAULT);
390
93
      def = coerce_to_specific_type(pstate, def, toid, "DEFAULT");
391
93
      assign_expr_collations(pstate, def);
392
393
      /*
394
       * Make sure no variables are referred to (this is probably dead
395
       * code now that add_missing_from is history).
396
       */
397
93
      if (list_length(pstate->p_rtable) != 0 ||
398
93
        contain_var_clause(def))
399
93
        ereport(ERROR,
400
93
            (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
401
93
             errmsg("cannot use table references in parameter default value")));
402
403
      /*
404
       * transformExpr() should have already rejected subqueries,
405
       * aggregates, and window functions, based on the EXPR_KIND_ for a
406
       * default expression.
407
       *
408
       * It can't return a set either --- but coerce_to_specific_type
409
       * already checked that for us.
410
       *
411
       * Note: the point of these restrictions is to ensure that an
412
       * expression that, on its face, hasn't got subplans, aggregates,
413
       * etc cannot suddenly have them after function default arguments
414
       * are inserted.
415
       */
416
417
93
      *parameterDefaults = lappend(*parameterDefaults, def);
418
93
      have_defaults = true;
419
93
    }
420
5.88k
    else
421
5.88k
    {
422
5.88k
      if (isinput && 
have_defaults4.43k
)
423
5.88k
        ereport(ERROR,
424
5.88k
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
425
5.88k
             errmsg("input parameters after one with a default value must also have defaults")));
426
5.88k
    }
427
428
5.98k
    i++;
429
5.98k
  }
430
431
  /* Now construct the proper outputs as needed */
432
2.95k
  *parameterTypes = buildoidvector(inTypes, inCount);
433
434
2.95k
  if (outCount > 0 || 
varCount > 02.83k
)
435
124
  {
436
124
    *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
437
124
                       sizeof(Oid), true, 'i');
438
124
    *parameterModes = construct_array(paramModes, parameterCount, CHAROID,
439
124
                      1, true, 'c');
440
124
    if (outCount > 1)
441
104
      *requiredResultType = RECORDOID;
442
    /* otherwise we set requiredResultType correctly above */
443
124
  }
444
2.83k
  else
445
2.83k
  {
446
2.83k
    *allParameterTypes = NULL;
447
2.83k
    *parameterModes = NULL;
448
2.83k
  }
449
450
2.95k
  if (have_names)
451
623
  {
452
3.25k
    for (i = 0; i < parameterCount; 
i++2.62k
)
453
2.62k
    {
454
2.62k
      if (paramNames[i] == PointerGetDatum(NULL))
455
9
        paramNames[i] = CStringGetTextDatum("");
456
2.62k
    }
457
623
    *parameterNames = construct_array(paramNames, parameterCount, TEXTOID,
458
623
                      -1, false, 'i');
459
623
  }
460
2.33k
  else
461
2.33k
    *parameterNames = NULL;
462
2.95k
}
463
464
465
/*
466
 * Recognize one of the options that can be passed to both CREATE
467
 * FUNCTION and ALTER FUNCTION and return it via one of the out
468
 * parameters. Returns true if the passed option was recognized. If
469
 * the out parameter we were going to assign to points to non-NULL,
470
 * raise a duplicate-clause error.  (We don't try to detect duplicate
471
 * SET parameters though --- if you're redundant, the last one wins.)
472
 */
473
static bool
474
compute_common_attribute(ParseState *pstate,
475
             bool is_procedure,
476
             DefElem *defel,
477
             DefElem **volatility_item,
478
             DefElem **strict_item,
479
             DefElem **security_item,
480
             DefElem **leakproof_item,
481
             List **set_items,
482
             DefElem **cost_item,
483
             DefElem **rows_item,
484
             DefElem **parallel_item)
485
4.58k
{
486
4.58k
  if (strcmp(defel->defname, "volatility") == 0)
487
1.97k
  {
488
1.97k
    if (is_procedure)
489
0
      goto procedure_error;
490
1.97k
    if (*volatility_item)
491
0
      goto duplicate_error;
492
493
1.97k
    *volatility_item = defel;
494
1.97k
  }
495
2.61k
  else if (strcmp(defel->defname, "strict") == 0)
496
1.63k
  {
497
1.63k
    if (is_procedure)
498
0
      goto procedure_error;
499
1.63k
    if (*strict_item)
500
0
      goto duplicate_error;
501
502
1.63k
    *strict_item = defel;
503
1.63k
  }
504
979
  else if (strcmp(defel->defname, "security") == 0)
505
13
  {
506
13
    if (*security_item)
507
0
      goto duplicate_error;
508
509
13
    *security_item = defel;
510
13
  }
511
966
  else if (strcmp(defel->defname, "leakproof") == 0)
512
6
  {
513
6
    if (is_procedure)
514
0
      goto procedure_error;
515
6
    if (*leakproof_item)
516
0
      goto duplicate_error;
517
518
6
    *leakproof_item = defel;
519
6
  }
520
960
  else if (strcmp(defel->defname, "set") == 0)
521
1
  {
522
1
    *set_items = lappend(*set_items, defel->arg);
523
1
  }
524
959
  else if (strcmp(defel->defname, "cost") == 0)
525
10
  {
526
10
    if (is_procedure)
527
0
      goto procedure_error;
528
10
    if (*cost_item)
529
0
      goto duplicate_error;
530
531
10
    *cost_item = defel;
532
10
  }
533
949
  else if (strcmp(defel->defname, "rows") == 0)
534
10
  {
535
10
    if (is_procedure)
536
0
      goto procedure_error;
537
10
    if (*rows_item)
538
0
      goto duplicate_error;
539
540
10
    *rows_item = defel;
541
10
  }
542
939
  else if (strcmp(defel->defname, "parallel") == 0)
543
939
  {
544
939
    if (is_procedure)
545
0
      goto procedure_error;
546
939
    if (*parallel_item)
547
0
      goto duplicate_error;
548
549
939
    *parallel_item = defel;
550
939
  }
551
0
  else
552
0
    return false;
553
554
  /* Recognized an option */
555
4.58k
  return true;
556
557
0
duplicate_error:
558
0
  ereport(ERROR,
559
0
      (errcode(ERRCODE_SYNTAX_ERROR),
560
0
       errmsg("conflicting or redundant options"),
561
0
       parser_errposition(pstate, defel->location)));
562
0
  return false;       /* keep compiler quiet */
563
564
0
procedure_error:
565
0
  ereport(ERROR,
566
0
      (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
567
0
       errmsg("invalid attribute in procedure definition"),
568
0
       parser_errposition(pstate, defel->location)));
569
0
  return false;
570
0
}
571
572
static char
573
interpret_func_volatility(DefElem *defel)
574
1.97k
{
575
1.97k
  char     *str = strVal(defel->arg);
576
577
1.97k
  if (strcmp(str, "immutable") == 0)
578
1.57k
    return PROVOLATILE_IMMUTABLE;
579
402
  else if (strcmp(str, "stable") == 0)
580
80
    return PROVOLATILE_STABLE;
581
322
  else if (strcmp(str, "volatile") == 0)
582
322
    return PROVOLATILE_VOLATILE;
583
0
  else
584
0
  {
585
0
    elog(ERROR, "invalid volatility \"%s\"", str);
586
0
    return 0;       /* keep compiler quiet */
587
0
  }
588
1.97k
}
589
590
static char
591
interpret_func_parallel(DefElem *defel)
592
939
{
593
939
  char     *str = strVal(defel->arg);
594
595
939
  if (strcmp(str, "safe") == 0)
596
932
    return PROPARALLEL_SAFE;
597
7
  else if (strcmp(str, "unsafe") == 0)
598
1
    return PROPARALLEL_UNSAFE;
599
6
  else if (strcmp(str, "restricted") == 0)
600
6
    return PROPARALLEL_RESTRICTED;
601
0
  else
602
0
  {
603
0
    ereport(ERROR,
604
0
        (errcode(ERRCODE_SYNTAX_ERROR),
605
0
         errmsg("parameter \"parallel\" must be SAFE, RESTRICTED, or UNSAFE")));
606
0
    return PROPARALLEL_UNSAFE; /* keep compiler quiet */
607
0
  }
608
939
}
609
610
/*
611
 * Update a proconfig value according to a list of VariableSetStmt items.
612
 *
613
 * The input and result may be NULL to signify a null entry.
614
 */
615
static ArrayType *
616
update_proconfig_value(ArrayType *a, List *set_items)
617
1
{
618
1
  ListCell   *l;
619
620
1
  foreach(l, set_items)
621
1
  {
622
1
    VariableSetStmt *sstmt = lfirst_node(VariableSetStmt, l);
623
624
1
    if (sstmt->kind == VAR_RESET_ALL)
625
0
      a = NULL;
626
1
    else
627
1
    {
628
1
      char     *valuestr = ExtractSetVariableArgs(sstmt);
629
630
1
      if (valuestr)
631
1
        a = GUCArrayAdd(a, sstmt->name, valuestr);
632
0
      else        /* RESET */
633
0
        a = GUCArrayDelete(a, sstmt->name);
634
1
    }
635
1
  }
636
637
1
  return a;
638
1
}
639
640
641
/*
642
 * Dissect the list of options assembled in gram.y into function
643
 * attributes.
644
 */
645
static void
646
compute_function_attributes(ParseState *pstate,
647
              bool is_procedure,
648
              List *options,
649
              List **as,
650
              char **language,
651
              Node **transform,
652
              bool *windowfunc_p,
653
              char *volatility_p,
654
              bool *strict_p,
655
              bool *security_definer,
656
              bool *leakproof_p,
657
              ArrayType **proconfig,
658
              float4 *procost,
659
              float4 *prorows,
660
              char *parallel_p)
661
2.93k
{
662
2.93k
  ListCell   *option;
663
2.93k
  DefElem    *as_item = NULL;
664
2.93k
  DefElem    *language_item = NULL;
665
2.93k
  DefElem    *transform_item = NULL;
666
2.93k
  DefElem    *windowfunc_item = NULL;
667
2.93k
  DefElem    *volatility_item = NULL;
668
2.93k
  DefElem    *strict_item = NULL;
669
2.93k
  DefElem    *security_item = NULL;
670
2.93k
  DefElem    *leakproof_item = NULL;
671
2.93k
  List     *set_items = NIL;
672
2.93k
  DefElem    *cost_item = NULL;
673
2.93k
  DefElem    *rows_item = NULL;
674
2.93k
  DefElem    *parallel_item = NULL;
675
676
2.93k
  foreach(option, options)
677
10.4k
  {
678
10.4k
    DefElem    *defel = (DefElem *) lfirst(option);
679
680
10.4k
    if (strcmp(defel->defname, "as") == 0)
681
2.93k
    {
682
2.93k
      if (as_item)
683
2.93k
        ereport(ERROR,
684
2.93k
            (errcode(ERRCODE_SYNTAX_ERROR),
685
2.93k
             errmsg("conflicting or redundant options"),
686
2.93k
             parser_errposition(pstate, defel->location)));
687
2.93k
      as_item = defel;
688
2.93k
    }
689
7.50k
    else if (strcmp(defel->defname, "language") == 0)
690
2.93k
    {
691
2.93k
      if (language_item)
692
2.93k
        ereport(ERROR,
693
2.93k
            (errcode(ERRCODE_SYNTAX_ERROR),
694
2.93k
             errmsg("conflicting or redundant options"),
695
2.93k
             parser_errposition(pstate, defel->location)));
696
2.93k
      language_item = defel;
697
2.93k
    }
698
4.56k
    else if (strcmp(defel->defname, "transform") == 0)
699
0
    {
700
0
      if (transform_item)
701
0
        ereport(ERROR,
702
0
            (errcode(ERRCODE_SYNTAX_ERROR),
703
0
             errmsg("conflicting or redundant options"),
704
0
             parser_errposition(pstate, defel->location)));
705
0
      transform_item = defel;
706
0
    }
707
4.56k
    else if (strcmp(defel->defname, "window") == 0)
708
2
    {
709
2
      if (windowfunc_item)
710
2
        ereport(ERROR,
711
2
            (errcode(ERRCODE_SYNTAX_ERROR),
712
2
             errmsg("conflicting or redundant options"),
713
2
             parser_errposition(pstate, defel->location)));
714
2
      if (is_procedure)
715
2
        ereport(ERROR,
716
2
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
717
2
             errmsg("invalid attribute in procedure definition"),
718
2
             parser_errposition(pstate, defel->location)));
719
2
      windowfunc_item = defel;
720
2
    }
721
4.56k
    else if (compute_common_attribute(pstate,
722
4.56k
                      is_procedure,
723
4.56k
                      defel,
724
4.56k
                      &volatility_item,
725
4.56k
                      &strict_item,
726
4.56k
                      &security_item,
727
4.56k
                      &leakproof_item,
728
4.56k
                      &set_items,
729
4.56k
                      &cost_item,
730
4.56k
                      &rows_item,
731
4.56k
                      &parallel_item))
732
4.56k
    {
733
      /* recognized common option */
734
4.56k
      continue;
735
4.56k
    }
736
0
    else
737
0
      elog(ERROR, "option \"%s\" not recognized",
738
10.4k
         defel->defname);
739
10.4k
  }
740
741
  /* process required items */
742
2.93k
  if (as_item)
743
2.93k
    *as = (List *) as_item->arg;
744
0
  else
745
0
  {
746
0
    ereport(ERROR,
747
0
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
748
0
         errmsg("no function body specified")));
749
0
    *as = NIL;       /* keep compiler quiet */
750
0
  }
751
752
2.93k
  if (language_item)
753
2.93k
    *language = strVal(language_item->arg);
754
0
  else
755
0
  {
756
0
    ereport(ERROR,
757
0
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
758
0
         errmsg("no language specified")));
759
0
    *language = NULL;   /* keep compiler quiet */
760
0
  }
761
762
  /* process optional items */
763
2.93k
  if (transform_item)
764
0
    *transform = transform_item->arg;
765
2.93k
  if (windowfunc_item)
766
2
    *windowfunc_p = intVal(windowfunc_item->arg);
767
2.93k
  if (volatility_item)
768
1.96k
    *volatility_p = interpret_func_volatility(volatility_item);
769
2.93k
  if (strict_item)
770
1.62k
    *strict_p = intVal(strict_item->arg);
771
2.93k
  if (security_item)
772
11
    *security_definer = intVal(security_item->arg);
773
2.93k
  if (leakproof_item)
774
2
    *leakproof_p = intVal(leakproof_item->arg);
775
2.93k
  if (set_items)
776
1
    *proconfig = update_proconfig_value(NULL, set_items);
777
2.93k
  if (cost_item)
778
9
  {
779
9
    *procost = defGetNumeric(cost_item);
780
9
    if (*procost <= 0)
781
9
      ereport(ERROR,
782
9
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
783
9
           errmsg("COST must be positive")));
784
9
  }
785
2.93k
  if (rows_item)
786
10
  {
787
10
    *prorows = defGetNumeric(rows_item);
788
10
    if (*prorows <= 0)
789
10
      ereport(ERROR,
790
10
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
791
10
           errmsg("ROWS must be positive")));
792
10
  }
793
2.93k
  if (parallel_item)
794
939
    *parallel_p = interpret_func_parallel(parallel_item);
795
2.93k
}
796
797
798
/*
799
 * For a dynamically linked C language object, the form of the clause is
800
 *
801
 *     AS <object file name> [, <link symbol name> ]
802
 *
803
 * In all other cases
804
 *
805
 *     AS <object reference, or sql code>
806
 */
807
static void
808
interpret_AS_clause(Oid languageOid, const char *languageName,
809
          char *funcname, List *as,
810
          char **prosrc_str_p, char **probin_str_p)
811
2.92k
{
812
2.92k
  Assert(as != NIL);
813
814
2.92k
  if (languageOid == ClanguageId)
815
1.39k
  {
816
    /*
817
     * For "C" language, store the file name in probin and, when given,
818
     * the link symbol name in prosrc.  If link symbol is omitted,
819
     * substitute procedure name.  We also allow link symbol to be
820
     * specified as "-", since that was the habit in PG versions before
821
     * 8.4, and there might be dump files out there that don't translate
822
     * that back to "omitted".
823
     */
824
1.39k
    *probin_str_p = strVal(linitial(as));
825
1.39k
    if (list_length(as) == 1)
826
471
      *prosrc_str_p = funcname;
827
928
    else
828
928
    {
829
928
      *prosrc_str_p = strVal(lsecond(as));
830
928
      if (strcmp(*prosrc_str_p, "-") == 0)
831
0
        *prosrc_str_p = funcname;
832
928
    }
833
1.39k
  }
834
1.52k
  else
835
1.52k
  {
836
    /* Everything else wants the given string in prosrc. */
837
1.52k
    *prosrc_str_p = strVal(linitial(as));
838
1.52k
    *probin_str_p = NULL;
839
840
1.52k
    if (list_length(as) != 1)
841
1.52k
      ereport(ERROR,
842
1.52k
          (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
843
1.52k
           errmsg("only one AS item needed for language \"%s\"",
844
1.52k
              languageName)));
845
846
1.52k
    if (languageOid == INTERNALlanguageId)
847
496
    {
848
      /*
849
       * In PostgreSQL versions before 6.5, the SQL name of the created
850
       * function could not be different from the internal name, and
851
       * "prosrc" wasn't used.  So there is code out there that does
852
       * CREATE FUNCTION xyz AS '' LANGUAGE internal. To preserve some
853
       * modicum of backwards compatibility, accept an empty "prosrc"
854
       * value as meaning the supplied SQL function name.
855
       */
856
496
      if (strlen(*prosrc_str_p) == 0)
857
0
        *prosrc_str_p = funcname;
858
496
    }
859
1.52k
  }
860
2.92k
}
861
862
863
/*
864
 * CreateFunction
865
 *   Execute a CREATE FUNCTION (or CREATE PROCEDURE) utility statement.
866
 */
867
ObjectAddress
868
CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
869
2.93k
{
870
2.93k
  char     *probin_str;
871
2.93k
  char     *prosrc_str;
872
2.93k
  Oid     prorettype;
873
2.93k
  bool    returnsSet;
874
2.93k
  char     *language;
875
2.93k
  Oid     languageOid;
876
2.93k
  Oid     languageValidator;
877
2.93k
  Node     *transformDefElem = NULL;
878
2.93k
  char     *funcname;
879
2.93k
  Oid     namespaceId;
880
2.93k
  AclResult aclresult;
881
2.93k
  oidvector  *parameterTypes;
882
2.93k
  ArrayType  *allParameterTypes;
883
2.93k
  ArrayType  *parameterModes;
884
2.93k
  ArrayType  *parameterNames;
885
2.93k
  List     *parameterDefaults;
886
2.93k
  Oid     variadicArgType;
887
2.93k
  List     *trftypes_list = NIL;
888
2.93k
  ArrayType  *trftypes;
889
2.93k
  Oid     requiredResultType;
890
2.93k
  bool    isWindowFunc,
891
2.93k
        isStrict,
892
2.93k
        security,
893
2.93k
        isLeakProof;
894
2.93k
  char    volatility;
895
2.93k
  ArrayType  *proconfig;
896
2.93k
  float4    procost;
897
2.93k
  float4    prorows;
898
2.93k
  HeapTuple languageTuple;
899
2.93k
  Form_pg_language languageStruct;
900
2.93k
  List     *as_clause;
901
2.93k
  char    parallel;
902
903
  /* Convert list of names to a name and namespace */
904
2.93k
  namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
905
2.93k
                          &funcname);
906
907
  /* Check we have creation rights in target namespace */
908
2.93k
  aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
909
2.93k
  if (aclresult != ACLCHECK_OK)
910
0
    aclcheck_error(aclresult, OBJECT_SCHEMA,
911
0
             get_namespace_name(namespaceId));
912
913
  /* Set default attributes */
914
2.93k
  isWindowFunc = false;
915
2.93k
  isStrict = false;
916
2.93k
  security = false;
917
2.93k
  isLeakProof = false;
918
2.93k
  volatility = PROVOLATILE_VOLATILE;
919
2.93k
  proconfig = NULL;
920
2.93k
  procost = -1;       /* indicates not set */
921
2.93k
  prorows = -1;       /* indicates not set */
922
2.93k
  parallel = PROPARALLEL_UNSAFE;
923
924
  /* Extract non-default attributes from stmt->options list */
925
2.93k
  compute_function_attributes(pstate,
926
2.93k
                stmt->is_procedure,
927
2.93k
                stmt->options,
928
2.93k
                &as_clause, &language, &transformDefElem,
929
2.93k
                &isWindowFunc, &volatility,
930
2.93k
                &isStrict, &security, &isLeakProof,
931
2.93k
                &proconfig, &procost, &prorows, &parallel);
932
933
  /* Look up the language and validate permissions */
934
2.93k
  languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
935
2.93k
  if (!HeapTupleIsValid(languageTuple))
936
2.93k
    ereport(ERROR,
937
2.93k
        (errcode(ERRCODE_UNDEFINED_OBJECT),
938
2.93k
         errmsg("language \"%s\" does not exist", language),
939
2.93k
         (PLTemplateExists(language) ?
940
2.93k
          errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
941
942
2.93k
  languageOid = HeapTupleGetOid(languageTuple);
943
2.93k
  languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
944
945
2.93k
  if (languageStruct->lanpltrusted)
946
1.03k
  {
947
    /* if trusted language, need USAGE privilege */
948
1.03k
    AclResult aclresult;
949
950
1.03k
    aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE);
951
1.03k
    if (aclresult != ACLCHECK_OK)
952
1
      aclcheck_error(aclresult, OBJECT_LANGUAGE,
953
1
               NameStr(languageStruct->lanname));
954
1.03k
  }
955
1.89k
  else
956
1.89k
  {
957
    /* 
958
     * If untrusted language, must be superuser, or someone with the
959
     * yb_extension role in the midst of creating an extension.
960
     */
961
1.89k
    if (!(IsYbExtensionUser(GetUserId()) && creating_extension) && 
!superuser()367
)
962
0
      aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_LANGUAGE,
963
0
               NameStr(languageStruct->lanname));
964
1.89k
  }
965
966
2.93k
  languageValidator = languageStruct->lanvalidator;
967
968
2.93k
  ReleaseSysCache(languageTuple);
969
970
  /*
971
   * Only superuser is allowed to create leakproof functions because
972
   * leakproof functions can see tuples which have not yet been filtered out
973
   * by security barrier views or row level security policies.
974
   */
975
2.93k
  if (isLeakProof && 
!superuser()2
)
976
2.93k
    ereport(ERROR,
977
2.93k
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
978
2.93k
         errmsg("only superuser can define a leakproof function")));
979
980
2.93k
  if (transformDefElem)
981
0
  {
982
0
    ListCell   *lc;
983
984
0
    foreach(lc, castNode(List, transformDefElem))
985
0
    {
986
0
      Oid     typeid = typenameTypeId(NULL,
987
0
                        lfirst_node(TypeName, lc));
988
0
      Oid     elt = get_base_element_type(typeid);
989
990
0
      typeid = elt ? elt : typeid;
991
992
0
      get_transform_oid(typeid, languageOid, false);
993
0
      trftypes_list = lappend_oid(trftypes_list, typeid);
994
0
    }
995
0
  }
996
997
  /*
998
   * Convert remaining parameters of CREATE to form wanted by
999
   * ProcedureCreate.
1000
   */
1001
2.93k
  interpret_function_parameter_list(pstate,
1002
2.93k
                    stmt->parameters,
1003
2.93k
                    languageOid,
1004
2.93k
                    stmt->is_procedure ? 
OBJECT_PROCEDURE25
:
OBJECT_FUNCTION2.90k
,
1005
2.93k
                    &parameterTypes,
1006
2.93k
                    &allParameterTypes,
1007
2.93k
                    &parameterModes,
1008
2.93k
                    &parameterNames,
1009
2.93k
                    &parameterDefaults,
1010
2.93k
                    &variadicArgType,
1011
2.93k
                    &requiredResultType);
1012
1013
2.93k
  if (stmt->is_procedure)
1014
25
  {
1015
25
    Assert(!stmt->returnType);
1016
25
    prorettype = requiredResultType ? 
requiredResultType1
:
VOIDOID24
;
1017
25
    returnsSet = false;
1018
25
  }
1019
2.90k
  else if (stmt->returnType)
1020
2.88k
  {
1021
    /* explicit RETURNS clause */
1022
2.88k
    compute_return_type(stmt->returnType, languageOid,
1023
2.88k
              &prorettype, &returnsSet);
1024
2.88k
    if (OidIsValid(requiredResultType) && 
prorettype != requiredResultType98
)
1025
2.88k
      ereport(ERROR,
1026
2.88k
          (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1027
2.88k
           errmsg("function result type must be %s because of OUT parameters",
1028
2.88k
              format_type_be(requiredResultType))));
1029
2.88k
  }
1030
25
  else if (OidIsValid(requiredResultType))
1031
21
  {
1032
    /* default RETURNS clause from OUT parameters */
1033
21
    prorettype = requiredResultType;
1034
21
    returnsSet = false;
1035
21
  }
1036
4
  else
1037
4
  {
1038
4
    ereport(ERROR,
1039
4
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
1040
4
         errmsg("function result type must be specified")));
1041
    /* Alternative possibility: default to RETURNS VOID */
1042
4
    prorettype = VOIDOID;
1043
4
    returnsSet = false;
1044
4
  }
1045
1046
2.93k
  if (list_length(trftypes_list) > 0)
1047
0
  {
1048
0
    ListCell   *lc;
1049
0
    Datum    *arr;
1050
0
    int     i;
1051
1052
0
    arr = palloc(list_length(trftypes_list) * sizeof(Datum));
1053
0
    i = 0;
1054
0
    foreach(lc, trftypes_list)
1055
0
      arr[i++] = ObjectIdGetDatum(lfirst_oid(lc));
1056
0
    trftypes = construct_array(arr, list_length(trftypes_list),
1057
0
                   OIDOID, sizeof(Oid), true, 'i');
1058
0
  }
1059
2.93k
  else
1060
2.93k
  {
1061
    /* store SQL NULL instead of empty array */
1062
2.93k
    trftypes = NULL;
1063
2.93k
  }
1064
1065
2.93k
  interpret_AS_clause(languageOid, language, funcname, as_clause,
1066
2.93k
            &prosrc_str, &probin_str);
1067
1068
  /*
1069
   * Set default values for COST and ROWS depending on other parameters;
1070
   * reject ROWS if it's not returnsSet.  NB: pg_dump knows these default
1071
   * values, keep it in sync if you change them.
1072
   */
1073
2.93k
  if (procost < 0)
1074
2.90k
  {
1075
    /* SQL and PL-language functions are assumed more expensive */
1076
2.90k
    if (languageOid == INTERNALlanguageId ||
1077
2.90k
      
languageOid == 2.41k
ClanguageId2.41k
)
1078
1.88k
      procost = 1;
1079
1.01k
    else
1080
1.01k
      procost = 100;
1081
2.90k
  }
1082
2.93k
  if (prorows < 0)
1083
2.90k
  {
1084
2.90k
    if (returnsSet)
1085
159
      prorows = 1000;
1086
2.74k
    else
1087
2.74k
      prorows = 0;   /* dummy value if not returnsSet */
1088
2.90k
  }
1089
30
  else if (!returnsSet)
1090
0
    ereport(ERROR,
1091
2.93k
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1092
2.93k
         errmsg("ROWS is not applicable when function does not return a set")));
1093
1094
  /*
1095
   * And now that we have all the parameters, and know we're permitted to do
1096
   * so, go ahead and create the function.
1097
   */
1098
2.93k
  return ProcedureCreate(funcname,
1099
2.93k
               namespaceId,
1100
2.93k
               stmt->replace,
1101
2.93k
               returnsSet,
1102
2.93k
               prorettype,
1103
2.93k
               GetUserId(),
1104
2.93k
               languageOid,
1105
2.93k
               languageValidator,
1106
2.93k
               prosrc_str,  /* converted to text later */
1107
2.93k
               probin_str,  /* converted to text later */
1108
2.93k
               stmt->is_procedure ? 
PROKIND_PROCEDURE25
:
(2.90k
isWindowFunc2.90k
?
PROKIND_WINDOW2
:
PROKIND_FUNCTION2.90k
),
1109
2.93k
               security,
1110
2.93k
               isLeakProof,
1111
2.93k
               isStrict,
1112
2.93k
               volatility,
1113
2.93k
               parallel,
1114
2.93k
               parameterTypes,
1115
2.93k
               PointerGetDatum(allParameterTypes),
1116
2.93k
               PointerGetDatum(parameterModes),
1117
2.93k
               PointerGetDatum(parameterNames),
1118
2.93k
               parameterDefaults,
1119
2.93k
               PointerGetDatum(trftypes),
1120
2.93k
               PointerGetDatum(proconfig),
1121
2.93k
               procost,
1122
2.93k
               prorows);
1123
2.93k
}
1124
1125
/*
1126
 * Guts of function deletion.
1127
 *
1128
 * Note: this is also used for aggregate deletion, since the OIDs of
1129
 * both functions and aggregates point to pg_proc.
1130
 */
1131
void
1132
RemoveFunctionById(Oid funcOid)
1133
2.13k
{
1134
2.13k
  Relation  relation;
1135
2.13k
  HeapTuple tup;
1136
2.13k
  char    prokind;
1137
1138
  /*
1139
   * Delete the pg_proc tuple.
1140
   */
1141
2.13k
  relation = heap_open(ProcedureRelationId, RowExclusiveLock);
1142
1143
2.13k
  tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
1144
2.13k
  if (!HeapTupleIsValid(tup)) /* should not happen */
1145
0
    elog(ERROR, "cache lookup failed for function %u", funcOid);
1146
1147
2.13k
  prokind = ((Form_pg_proc) GETSTRUCT(tup))->prokind;
1148
1149
2.13k
  CatalogTupleDelete(relation, tup);
1150
1151
2.13k
  ReleaseSysCache(tup);
1152
1153
2.13k
  heap_close(relation, RowExclusiveLock);
1154
1155
  /*
1156
   * If there's a pg_aggregate tuple, delete that too.
1157
   */
1158
2.13k
  if (prokind == PROKIND_AGGREGATE)
1159
20
  {
1160
20
    relation = heap_open(AggregateRelationId, RowExclusiveLock);
1161
1162
20
    tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcOid));
1163
20
    if (!HeapTupleIsValid(tup)) /* should not happen */
1164
0
      elog(ERROR, "cache lookup failed for pg_aggregate tuple for function %u", funcOid);
1165
1166
20
    CatalogTupleDelete(relation, tup);
1167
1168
20
    ReleaseSysCache(tup);
1169
1170
20
    heap_close(relation, RowExclusiveLock);
1171
20
  }
1172
2.13k
}
1173
1174
/*
1175
 * Implements the ALTER FUNCTION utility command (except for the
1176
 * RENAME and OWNER clauses, which are handled as part of the generic
1177
 * ALTER framework).
1178
 */
1179
ObjectAddress
1180
AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
1181
19
{
1182
19
  HeapTuple tup;
1183
19
  Oid     funcOid;
1184
19
  Form_pg_proc procForm;
1185
19
  bool    is_procedure;
1186
19
  Relation  rel;
1187
19
  ListCell   *l;
1188
19
  DefElem    *volatility_item = NULL;
1189
19
  DefElem    *strict_item = NULL;
1190
19
  DefElem    *security_def_item = NULL;
1191
19
  DefElem    *leakproof_item = NULL;
1192
19
  List     *set_items = NIL;
1193
19
  DefElem    *cost_item = NULL;
1194
19
  DefElem    *rows_item = NULL;
1195
19
  DefElem    *parallel_item = NULL;
1196
19
  ObjectAddress address;
1197
1198
19
  rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1199
1200
19
  funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
1201
1202
19
  tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
1203
19
  if (!HeapTupleIsValid(tup)) /* should not happen */
1204
0
    elog(ERROR, "cache lookup failed for function %u", funcOid);
1205
1206
19
  procForm = (Form_pg_proc) GETSTRUCT(tup);
1207
1208
  /* Permission check: must own function */
1209
19
  if (!pg_proc_ownercheck(funcOid, GetUserId()))
1210
0
    aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype,
1211
0
             NameListToString(stmt->func->objname));
1212
1213
19
  if (procForm->prokind == PROKIND_AGGREGATE)
1214
19
    ereport(ERROR,
1215
19
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1216
19
         errmsg("\"%s\" is an aggregate function",
1217
19
            NameListToString(stmt->func->objname))));
1218
1219
19
  is_procedure = (procForm->prokind == PROKIND_PROCEDURE);
1220
1221
  /* Examine requested actions. */
1222
19
  foreach(l, stmt->actions)
1223
19
  {
1224
19
    DefElem    *defel = (DefElem *) lfirst(l);
1225
1226
19
    if (compute_common_attribute(pstate,
1227
19
                   is_procedure,
1228
19
                   defel,
1229
19
                   &volatility_item,
1230
19
                   &strict_item,
1231
19
                   &security_def_item,
1232
19
                   &leakproof_item,
1233
19
                   &set_items,
1234
19
                   &cost_item,
1235
19
                   &rows_item,
1236
19
                   &parallel_item) == false)
1237
0
      elog(ERROR, "option \"%s\" not recognized", defel->defname);
1238
19
  }
1239
1240
19
  if (volatility_item)
1241
4
    procForm->provolatile = interpret_func_volatility(volatility_item);
1242
19
  if (strict_item)
1243
8
    procForm->proisstrict = intVal(strict_item->arg);
1244
19
  if (security_def_item)
1245
2
    procForm->prosecdef = intVal(security_def_item->arg);
1246
19
  if (leakproof_item)
1247
4
  {
1248
4
    procForm->proleakproof = intVal(leakproof_item->arg);
1249
4
    if (procForm->proleakproof && 
!superuser()2
)
1250
4
      ereport(ERROR,
1251
4
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1252
4
           errmsg("only superuser can define a leakproof function")));
1253
4
  }
1254
19
  if (cost_item)
1255
1
  {
1256
1
    procForm->procost = defGetNumeric(cost_item);
1257
1
    if (procForm->procost <= 0)
1258
1
      ereport(ERROR,
1259
1
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1260
1
           errmsg("COST must be positive")));
1261
1
  }
1262
19
  if (rows_item)
1263
0
  {
1264
0
    procForm->prorows = defGetNumeric(rows_item);
1265
0
    if (procForm->prorows <= 0)
1266
0
      ereport(ERROR,
1267
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1268
0
           errmsg("ROWS must be positive")));
1269
0
    if (!procForm->proretset)
1270
0
      ereport(ERROR,
1271
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1272
0
           errmsg("ROWS is not applicable when function does not return a set")));
1273
0
  }
1274
19
  if (set_items)
1275
0
  {
1276
0
    Datum   datum;
1277
0
    bool    isnull;
1278
0
    ArrayType  *a;
1279
0
    Datum   repl_val[Natts_pg_proc];
1280
0
    bool    repl_null[Natts_pg_proc];
1281
0
    bool    repl_repl[Natts_pg_proc];
1282
1283
    /* extract existing proconfig setting */
1284
0
    datum = SysCacheGetAttr(PROCOID, tup, Anum_pg_proc_proconfig, &isnull);
1285
0
    a = isnull ? NULL : DatumGetArrayTypeP(datum);
1286
1287
    /* update according to each SET or RESET item, left to right */
1288
0
    a = update_proconfig_value(a, set_items);
1289
1290
    /* update the tuple */
1291
0
    memset(repl_repl, false, sizeof(repl_repl));
1292
0
    repl_repl[Anum_pg_proc_proconfig - 1] = true;
1293
1294
0
    if (a == NULL)
1295
0
    {
1296
0
      repl_val[Anum_pg_proc_proconfig - 1] = (Datum) 0;
1297
0
      repl_null[Anum_pg_proc_proconfig - 1] = true;
1298
0
    }
1299
0
    else
1300
0
    {
1301
0
      repl_val[Anum_pg_proc_proconfig - 1] = PointerGetDatum(a);
1302
0
      repl_null[Anum_pg_proc_proconfig - 1] = false;
1303
0
    }
1304
1305
0
    tup = heap_modify_tuple(tup, RelationGetDescr(rel),
1306
0
                repl_val, repl_null, repl_repl);
1307
0
  }
1308
19
  if (parallel_item)
1309
0
    procForm->proparallel = interpret_func_parallel(parallel_item);
1310
1311
  /* Do the update */
1312
19
  CatalogTupleUpdate(rel, &tup->t_self, tup);
1313
1314
19
  InvokeObjectPostAlterHook(ProcedureRelationId, funcOid, 0);
1315
1316
19
  ObjectAddressSet(address, ProcedureRelationId, funcOid);
1317
1318
19
  heap_close(rel, NoLock);
1319
19
  heap_freetuple(tup);
1320
1321
19
  return address;
1322
19
}
1323
1324
/*
1325
 * SetFunctionReturnType - change declared return type of a function
1326
 *
1327
 * This is presently only used for adjusting legacy functions that return
1328
 * OPAQUE to return whatever we find their correct definition should be.
1329
 * The caller should emit a suitable warning explaining what we did.
1330
 */
1331
void
1332
SetFunctionReturnType(Oid funcOid, Oid newRetType)
1333
20
{
1334
20
  Relation  pg_proc_rel;
1335
20
  HeapTuple tup;
1336
20
  Form_pg_proc procForm;
1337
20
  ObjectAddress func_address;
1338
20
  ObjectAddress type_address;
1339
1340
20
  pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1341
1342
20
  tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
1343
20
  if (!HeapTupleIsValid(tup)) /* should not happen */
1344
0
    elog(ERROR, "cache lookup failed for function %u", funcOid);
1345
20
  procForm = (Form_pg_proc) GETSTRUCT(tup);
1346
1347
20
  if (procForm->prorettype != OPAQUEOID)  /* caller messed up */
1348
0
    elog(ERROR, "function %u doesn't return OPAQUE", funcOid);
1349
1350
  /* okay to overwrite copied tuple */
1351
20
  procForm->prorettype = newRetType;
1352
1353
  /* update the catalog and its indexes */
1354
20
  CatalogTupleUpdate(pg_proc_rel, &tup->t_self, tup);
1355
1356
20
  heap_close(pg_proc_rel, RowExclusiveLock);
1357
1358
  /*
1359
   * Also update the dependency to the new type. Opaque is a pinned type, so
1360
   * there is no old dependency record for it that we would need to remove.
1361
   */
1362
20
  ObjectAddressSet(type_address, TypeRelationId, newRetType);
1363
20
  ObjectAddressSet(func_address, ProcedureRelationId, funcOid);
1364
20
  recordDependencyOn(&func_address, &type_address, DEPENDENCY_NORMAL);
1365
20
}
1366
1367
1368
/*
1369
 * SetFunctionArgType - change declared argument type of a function
1370
 *
1371
 * As above, but change an argument's type.
1372
 */
1373
void
1374
SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType)
1375
10
{
1376
10
  Relation  pg_proc_rel;
1377
10
  HeapTuple tup;
1378
10
  Form_pg_proc procForm;
1379
10
  ObjectAddress func_address;
1380
10
  ObjectAddress type_address;
1381
1382
10
  pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock);
1383
1384
10
  tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
1385
10
  if (!HeapTupleIsValid(tup)) /* should not happen */
1386
0
    elog(ERROR, "cache lookup failed for function %u", funcOid);
1387
10
  procForm = (Form_pg_proc) GETSTRUCT(tup);
1388
1389
10
  if (argIndex < 0 || argIndex >= procForm->pronargs ||
1390
10
    procForm->proargtypes.values[argIndex] != OPAQUEOID)
1391
0
    elog(ERROR, "function %u doesn't take OPAQUE", funcOid);
1392
1393
  /* okay to overwrite copied tuple */
1394
10
  procForm->proargtypes.values[argIndex] = newArgType;
1395
1396
  /* update the catalog and its indexes */
1397
10
  CatalogTupleUpdate(pg_proc_rel, &tup->t_self, tup);
1398
1399
10
  heap_close(pg_proc_rel, RowExclusiveLock);
1400
1401
  /*
1402
   * Also update the dependency to the new type. Opaque is a pinned type, so
1403
   * there is no old dependency record for it that we would need to remove.
1404
   */
1405
10
  ObjectAddressSet(type_address, TypeRelationId, newArgType);
1406
10
  ObjectAddressSet(func_address, ProcedureRelationId, funcOid);
1407
10
  recordDependencyOn(&func_address, &type_address, DEPENDENCY_NORMAL);
1408
10
}
1409
1410
1411
1412
/*
1413
 * CREATE CAST
1414
 */
1415
ObjectAddress
1416
CreateCast(CreateCastStmt *stmt)
1417
130
{
1418
130
  Oid     sourcetypeid;
1419
130
  Oid     targettypeid;
1420
130
  char    sourcetyptype;
1421
130
  char    targettyptype;
1422
130
  Oid     funcid;
1423
130
  Oid     castid;
1424
130
  int     nargs;
1425
130
  char    castcontext;
1426
130
  char    castmethod;
1427
130
  Relation  relation;
1428
130
  HeapTuple tuple;
1429
130
  Datum   values[Natts_pg_cast];
1430
130
  bool    nulls[Natts_pg_cast];
1431
130
  ObjectAddress myself,
1432
130
        referenced;
1433
130
  AclResult aclresult;
1434
1435
130
  sourcetypeid = typenameTypeId(NULL, stmt->sourcetype);
1436
130
  targettypeid = typenameTypeId(NULL, stmt->targettype);
1437
130
  sourcetyptype = get_typtype(sourcetypeid);
1438
130
  targettyptype = get_typtype(targettypeid);
1439
1440
  /* No pseudo-types allowed */
1441
130
  if (sourcetyptype == TYPTYPE_PSEUDO)
1442
130
    ereport(ERROR,
1443
130
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1444
130
         errmsg("source data type %s is a pseudo-type",
1445
130
            TypeNameToString(stmt->sourcetype))));
1446
1447
130
  if (targettyptype == TYPTYPE_PSEUDO)
1448
130
    ereport(ERROR,
1449
130
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1450
130
         errmsg("target data type %s is a pseudo-type",
1451
130
            TypeNameToString(stmt->targettype))));
1452
1453
  /* Permission check */
1454
130
  if (!pg_type_ownercheck(sourcetypeid, GetUserId())
1455
130
    && 
!pg_type_ownercheck(targettypeid, GetUserId())2
)
1456
130
    ereport(ERROR,
1457
130
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1458
130
         errmsg("must be owner of type %s or type %s",
1459
130
            format_type_be(sourcetypeid),
1460
130
            format_type_be(targettypeid))));
1461
1462
130
  aclresult = pg_type_aclcheck(sourcetypeid, GetUserId(), ACL_USAGE);
1463
130
  if (aclresult != ACLCHECK_OK)
1464
1
    aclcheck_error_type(aclresult, sourcetypeid);
1465
1466
130
  aclresult = pg_type_aclcheck(targettypeid, GetUserId(), ACL_USAGE);
1467
130
  if (aclresult != ACLCHECK_OK)
1468
0
    aclcheck_error_type(aclresult, targettypeid);
1469
1470
  /* Domains are allowed for historical reasons, but we warn */
1471
130
  if (sourcetyptype == TYPTYPE_DOMAIN)
1472
130
    ereport(WARNING,
1473
129
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1474
129
         errmsg("cast will be ignored because the source data type is a domain")));
1475
1476
129
  else if (targettyptype == TYPTYPE_DOMAIN)
1477
0
    ereport(WARNING,
1478
130
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1479
130
         errmsg("cast will be ignored because the target data type is a domain")));
1480
1481
  /* Determine the cast method */
1482
130
  if (stmt->func != NULL)
1483
17
    castmethod = COERCION_METHOD_FUNCTION;
1484
113
  else if (stmt->inout)
1485
73
    castmethod = COERCION_METHOD_INOUT;
1486
40
  else
1487
40
    castmethod = COERCION_METHOD_BINARY;
1488
1489
130
  if (castmethod == COERCION_METHOD_FUNCTION)
1490
17
  {
1491
17
    Form_pg_proc procstruct;
1492
1493
17
    funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func, false);
1494
1495
17
    tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
1496
17
    if (!HeapTupleIsValid(tuple))
1497
0
      elog(ERROR, "cache lookup failed for function %u", funcid);
1498
1499
17
    procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1500
17
    nargs = procstruct->pronargs;
1501
17
    if (nargs < 1 || nargs > 3)
1502
17
      ereport(ERROR,
1503
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1504
17
           errmsg("cast function must take one to three arguments")));
1505
17
    if (!IsBinaryCoercible(sourcetypeid, procstruct->proargtypes.values[0]))
1506
17
      ereport(ERROR,
1507
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1508
17
           errmsg("argument of cast function must match or be binary-coercible from source data type")));
1509
17
    if (nargs > 1 && 
procstruct->proargtypes.values[1] != 4
INT4OID4
)
1510
17
      ereport(ERROR,
1511
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1512
17
           errmsg("second argument of cast function must be type %s",
1513
17
              "integer")));
1514
17
    if (nargs > 2 && 
procstruct->proargtypes.values[2] != 4
BOOLOID4
)
1515
17
      ereport(ERROR,
1516
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1517
17
           errmsg("third argument of cast function must be type %s",
1518
17
              "boolean")));
1519
17
    if (!IsBinaryCoercible(procstruct->prorettype, targettypeid))
1520
17
      ereport(ERROR,
1521
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1522
17
           errmsg("return data type of cast function must match or be binary-coercible to target data type")));
1523
1524
    /*
1525
     * Restricting the volatility of a cast function may or may not be a
1526
     * good idea in the abstract, but it definitely breaks many old
1527
     * user-defined types.  Disable this check --- tgl 2/1/03
1528
     */
1529
#ifdef NOT_USED
1530
    if (procstruct->provolatile == PROVOLATILE_VOLATILE)
1531
      ereport(ERROR,
1532
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1533
           errmsg("cast function must not be volatile")));
1534
#endif
1535
17
    if (procstruct->prokind != PROKIND_FUNCTION)
1536
17
      ereport(ERROR,
1537
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1538
17
           errmsg("cast function must be a normal function")));
1539
17
    if (procstruct->proretset)
1540
17
      ereport(ERROR,
1541
17
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1542
17
           errmsg("cast function must not return a set")));
1543
1544
17
    ReleaseSysCache(tuple);
1545
17
  }
1546
113
  else
1547
113
  {
1548
113
    funcid = InvalidOid;
1549
113
    nargs = 0;
1550
113
  }
1551
1552
130
  if (castmethod == COERCION_METHOD_BINARY)
1553
39
  {
1554
39
    int16   typ1len;
1555
39
    int16   typ2len;
1556
39
    bool    typ1byval;
1557
39
    bool    typ2byval;
1558
39
    char    typ1align;
1559
39
    char    typ2align;
1560
1561
    /*
1562
     * Must be superuser to create binary-compatible casts, since
1563
     * erroneous casts can easily crash the backend.
1564
     */
1565
39
    if (!superuser())
1566
39
      ereport(ERROR,
1567
39
          (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1568
39
           errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
1569
1570
    /*
1571
     * Also, insist that the types match as to size, alignment, and
1572
     * pass-by-value attributes; this provides at least a crude check that
1573
     * they have similar representations.  A pair of types that fail this
1574
     * test should certainly not be equated.
1575
     */
1576
39
    get_typlenbyvalalign(sourcetypeid, &typ1len, &typ1byval, &typ1align);
1577
39
    get_typlenbyvalalign(targettypeid, &typ2len, &typ2byval, &typ2align);
1578
39
    if (typ1len != typ2len ||
1579
39
      typ1byval != typ2byval ||
1580
39
      typ1align != typ2align)
1581
39
      ereport(ERROR,
1582
39
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1583
39
           errmsg("source and target data types are not physically compatible")));
1584
1585
    /*
1586
     * We know that composite, enum and array types are never binary-
1587
     * compatible with each other.  They all have OIDs embedded in them.
1588
     *
1589
     * Theoretically you could build a user-defined base type that is
1590
     * binary-compatible with a composite, enum, or array type.  But we
1591
     * disallow that too, as in practice such a cast is surely a mistake.
1592
     * You can always work around that by writing a cast function.
1593
     */
1594
39
    if (sourcetyptype == TYPTYPE_COMPOSITE ||
1595
39
      targettyptype == TYPTYPE_COMPOSITE)
1596
39
      ereport(ERROR,
1597
39
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1598
39
           errmsg("composite data types are not binary-compatible")));
1599
1600
39
    if (sourcetyptype == TYPTYPE_ENUM ||
1601
39
      targettyptype == TYPTYPE_ENUM)
1602
39
      ereport(ERROR,
1603
39
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1604
39
           errmsg("enum data types are not binary-compatible")));
1605
1606
39
    if (OidIsValid(get_element_type(sourcetypeid)) ||
1607
39
      OidIsValid(get_element_type(targettypeid)))
1608
39
      ereport(ERROR,
1609
39
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1610
39
           errmsg("array data types are not binary-compatible")));
1611
1612
    /*
1613
     * We also disallow creating binary-compatibility casts involving
1614
     * domains.  Casting from a domain to its base type is already
1615
     * allowed, and casting the other way ought to go through domain
1616
     * coercion to permit constraint checking.  Again, if you're intent on
1617
     * having your own semantics for that, create a no-op cast function.
1618
     *
1619
     * NOTE: if we were to relax this, the above checks for composites
1620
     * etc. would have to be modified to look through domains to their
1621
     * base types.
1622
     */
1623
39
    if (sourcetyptype == TYPTYPE_DOMAIN ||
1624
39
      targettyptype == TYPTYPE_DOMAIN)
1625
39
      ereport(ERROR,
1626
39
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1627
39
           errmsg("domain data types must not be marked binary-compatible")));
1628
39
  }
1629
1630
  /*
1631
   * Allow source and target types to be same only for length coercion
1632
   * functions.  We assume a multi-arg function does length coercion.
1633
   */
1634
130
  if (sourcetypeid == targettypeid && 
nargs < 24
)
1635
130
    ereport(ERROR,
1636
130
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1637
130
         errmsg("source data type and target data type are the same")));
1638
1639
  /* convert CoercionContext enum to char value for castcontext */
1640
130
  switch (stmt->context)
1641
130
  {
1642
102
    case COERCION_IMPLICIT:
1643
102
      castcontext = COERCION_CODE_IMPLICIT;
1644
102
      break;
1645
14
    case COERCION_ASSIGNMENT:
1646
14
      castcontext = COERCION_CODE_ASSIGNMENT;
1647
14
      break;
1648
13
    case COERCION_EXPLICIT:
1649
13
      castcontext = COERCION_CODE_EXPLICIT;
1650
13
      break;
1651
0
    default:
1652
0
      elog(ERROR, "unrecognized CoercionContext: %d", stmt->context);
1653
0
      castcontext = 0;  /* keep compiler quiet */
1654
0
      break;
1655
130
  }
1656
1657
129
  relation = heap_open(CastRelationId, RowExclusiveLock);
1658
1659
  /*
1660
   * Check for duplicate.  This is just to give a friendly error message,
1661
   * the unique index would catch it anyway (so no need to sweat about race
1662
   * conditions).
1663
   */
1664
129
  tuple = SearchSysCache2(CASTSOURCETARGET,
1665
129
              ObjectIdGetDatum(sourcetypeid),
1666
129
              ObjectIdGetDatum(targettypeid));
1667
129
  if (HeapTupleIsValid(tuple))
1668
129
    ereport(ERROR,
1669
129
        (errcode(ERRCODE_DUPLICATE_OBJECT),
1670
129
         errmsg("cast from type %s to type %s already exists",
1671
129
            format_type_be(sourcetypeid),
1672
129
            format_type_be(targettypeid))));
1673
1674
  /* ready to go */
1675
129
  values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
1676
129
  values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
1677
129
  values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
1678
129
  values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
1679
129
  values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
1680
1681
129
  MemSet(nulls, false, sizeof(nulls));
1682
1683
129
  tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
1684
1685
129
  castid = CatalogTupleInsert(relation, tuple);
1686
1687
  /* make dependency entries */
1688
129
  myself.classId = CastRelationId;
1689
129
  myself.objectId = castid;
1690
129
  myself.objectSubId = 0;
1691
1692
  /* dependency on source type */
1693
129
  referenced.classId = TypeRelationId;
1694
129
  referenced.objectId = sourcetypeid;
1695
129
  referenced.objectSubId = 0;
1696
129
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1697
1698
  /* dependency on target type */
1699
129
  referenced.classId = TypeRelationId;
1700
129
  referenced.objectId = targettypeid;
1701
129
  referenced.objectSubId = 0;
1702
129
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1703
1704
  /* dependency on function */
1705
129
  if (OidIsValid(funcid))
1706
17
  {
1707
17
    referenced.classId = ProcedureRelationId;
1708
17
    referenced.objectId = funcid;
1709
17
    referenced.objectSubId = 0;
1710
17
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1711
17
  }
1712
1713
  /* dependency on extension */
1714
129
  recordDependencyOnCurrentExtension(&myself, false);
1715
1716
  /* Post creation hook for new cast */
1717
129
  InvokeObjectPostCreateHook(CastRelationId, castid, 0);
1718
1719
129
  heap_freetuple(tuple);
1720
1721
129
  heap_close(relation, RowExclusiveLock);
1722
1723
129
  return myself;
1724
129
}
1725
1726
/*
1727
 * get_cast_oid - given two type OIDs, look up a cast OID
1728
 *
1729
 * If missing_ok is false, throw an error if the cast is not found.  If
1730
 * true, just return InvalidOid.
1731
 */
1732
Oid
1733
get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok)
1734
13
{
1735
13
  Oid     oid;
1736
1737
13
  oid = GetSysCacheOid2(CASTSOURCETARGET,
1738
13
              ObjectIdGetDatum(sourcetypeid),
1739
13
              ObjectIdGetDatum(targettypeid));
1740
13
  if (!OidIsValid(oid) && 
!missing_ok6
)
1741
13
    ereport(ERROR,
1742
13
        (errcode(ERRCODE_UNDEFINED_OBJECT),
1743
13
         errmsg("cast from type %s to type %s does not exist",
1744
13
            format_type_be(sourcetypeid),
1745
13
            format_type_be(targettypeid))));
1746
13
  return oid;
1747
13
}
1748
1749
void
1750
DropCastById(Oid castOid)
1751
79
{
1752
79
  Relation  relation;
1753
79
  ScanKeyData scankey;
1754
79
  SysScanDesc scan;
1755
79
  HeapTuple tuple;
1756
1757
79
  relation = heap_open(CastRelationId, RowExclusiveLock);
1758
1759
79
  ScanKeyInit(&scankey,
1760
79
        ObjectIdAttributeNumber,
1761
79
        BTEqualStrategyNumber, F_OIDEQ,
1762
79
        ObjectIdGetDatum(castOid));
1763
79
  scan = systable_beginscan(relation, CastOidIndexId, true,
1764
79
                NULL, 1, &scankey);
1765
1766
79
  tuple = systable_getnext(scan);
1767
79
  if (!HeapTupleIsValid(tuple))
1768
0
    elog(ERROR, "could not find tuple for cast %u", castOid);
1769
79
  CatalogTupleDelete(relation, tuple);
1770
1771
79
  systable_endscan(scan);
1772
79
  heap_close(relation, RowExclusiveLock);
1773
79
}
1774
1775
1776
static void
1777
check_transform_function(Form_pg_proc procstruct)
1778
0
{
1779
0
  if (procstruct->provolatile == PROVOLATILE_VOLATILE)
1780
0
    ereport(ERROR,
1781
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1782
0
         errmsg("transform function must not be volatile")));
1783
0
  if (procstruct->prokind != PROKIND_FUNCTION)
1784
0
    ereport(ERROR,
1785
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1786
0
         errmsg("transform function must be a normal function")));
1787
0
  if (procstruct->proretset)
1788
0
    ereport(ERROR,
1789
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1790
0
         errmsg("transform function must not return a set")));
1791
0
  if (procstruct->pronargs != 1)
1792
0
    ereport(ERROR,
1793
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1794
0
         errmsg("transform function must take one argument")));
1795
0
  if (procstruct->proargtypes.values[0] != INTERNALOID)
1796
0
    ereport(ERROR,
1797
0
        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1798
0
         errmsg("first argument of transform function must be type %s",
1799
0
            "internal")));
1800
0
}
1801
1802
1803
/*
1804
 * CREATE TRANSFORM
1805
 */
1806
ObjectAddress
1807
CreateTransform(CreateTransformStmt *stmt)
1808
0
{
1809
0
  Oid     typeid;
1810
0
  char    typtype;
1811
0
  Oid     langid;
1812
0
  Oid     fromsqlfuncid;
1813
0
  Oid     tosqlfuncid;
1814
0
  AclResult aclresult;
1815
0
  Form_pg_proc procstruct;
1816
0
  Datum   values[Natts_pg_transform];
1817
0
  bool    nulls[Natts_pg_transform];
1818
0
  bool    replaces[Natts_pg_transform];
1819
0
  Oid     transformid;
1820
0
  HeapTuple tuple;
1821
0
  HeapTuple newtuple;
1822
0
  Relation  relation;
1823
0
  ObjectAddress myself,
1824
0
        referenced;
1825
0
  bool    is_replace;
1826
1827
  /*
1828
   * Get the type
1829
   */
1830
0
  typeid = typenameTypeId(NULL, stmt->type_name);
1831
0
  typtype = get_typtype(typeid);
1832
1833
0
  if (typtype == TYPTYPE_PSEUDO)
1834
0
    ereport(ERROR,
1835
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1836
0
         errmsg("data type %s is a pseudo-type",
1837
0
            TypeNameToString(stmt->type_name))));
1838
1839
0
  if (typtype == TYPTYPE_DOMAIN)
1840
0
    ereport(ERROR,
1841
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1842
0
         errmsg("data type %s is a domain",
1843
0
            TypeNameToString(stmt->type_name))));
1844
1845
0
  if (!pg_type_ownercheck(typeid, GetUserId()))
1846
0
    aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid);
1847
1848
0
  aclresult = pg_type_aclcheck(typeid, GetUserId(), ACL_USAGE);
1849
0
  if (aclresult != ACLCHECK_OK)
1850
0
    aclcheck_error_type(aclresult, typeid);
1851
1852
  /*
1853
   * Get the language
1854
   */
1855
0
  langid = get_language_oid(stmt->lang, false);
1856
1857
0
  aclresult = pg_language_aclcheck(langid, GetUserId(), ACL_USAGE);
1858
0
  if (aclresult != ACLCHECK_OK)
1859
0
    aclcheck_error(aclresult, OBJECT_LANGUAGE, stmt->lang);
1860
1861
  /*
1862
   * Get the functions
1863
   */
1864
0
  if (stmt->fromsql)
1865
0
  {
1866
0
    fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql, false);
1867
1868
0
    if (!pg_proc_ownercheck(fromsqlfuncid, GetUserId()))
1869
0
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
1870
1871
0
    aclresult = pg_proc_aclcheck(fromsqlfuncid, GetUserId(), ACL_EXECUTE);
1872
0
    if (aclresult != ACLCHECK_OK)
1873
0
      aclcheck_error(aclresult, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
1874
1875
0
    tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fromsqlfuncid));
1876
0
    if (!HeapTupleIsValid(tuple))
1877
0
      elog(ERROR, "cache lookup failed for function %u", fromsqlfuncid);
1878
0
    procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1879
0
    if (procstruct->prorettype != INTERNALOID)
1880
0
      ereport(ERROR,
1881
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1882
0
           errmsg("return data type of FROM SQL function must be %s",
1883
0
              "internal")));
1884
0
    check_transform_function(procstruct);
1885
0
    ReleaseSysCache(tuple);
1886
0
  }
1887
0
  else
1888
0
    fromsqlfuncid = InvalidOid;
1889
1890
0
  if (stmt->tosql)
1891
0
  {
1892
0
    tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql, false);
1893
1894
0
    if (!pg_proc_ownercheck(tosqlfuncid, GetUserId()))
1895
0
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
1896
1897
0
    aclresult = pg_proc_aclcheck(tosqlfuncid, GetUserId(), ACL_EXECUTE);
1898
0
    if (aclresult != ACLCHECK_OK)
1899
0
      aclcheck_error(aclresult, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
1900
1901
0
    tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(tosqlfuncid));
1902
0
    if (!HeapTupleIsValid(tuple))
1903
0
      elog(ERROR, "cache lookup failed for function %u", tosqlfuncid);
1904
0
    procstruct = (Form_pg_proc) GETSTRUCT(tuple);
1905
0
    if (procstruct->prorettype != typeid)
1906
0
      ereport(ERROR,
1907
0
          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1908
0
           errmsg("return data type of TO SQL function must be the transform data type")));
1909
0
    check_transform_function(procstruct);
1910
0
    ReleaseSysCache(tuple);
1911
0
  }
1912
0
  else
1913
0
    tosqlfuncid = InvalidOid;
1914
1915
  /*
1916
   * Ready to go
1917
   */
1918
0
  values[Anum_pg_transform_trftype - 1] = ObjectIdGetDatum(typeid);
1919
0
  values[Anum_pg_transform_trflang - 1] = ObjectIdGetDatum(langid);
1920
0
  values[Anum_pg_transform_trffromsql - 1] = ObjectIdGetDatum(fromsqlfuncid);
1921
0
  values[Anum_pg_transform_trftosql - 1] = ObjectIdGetDatum(tosqlfuncid);
1922
1923
0
  MemSet(nulls, false, sizeof(nulls));
1924
1925
0
  relation = heap_open(TransformRelationId, RowExclusiveLock);
1926
1927
0
  tuple = SearchSysCache2(TRFTYPELANG,
1928
0
              ObjectIdGetDatum(typeid),
1929
0
              ObjectIdGetDatum(langid));
1930
0
  if (HeapTupleIsValid(tuple))
1931
0
  {
1932
0
    if (!stmt->replace)
1933
0
      ereport(ERROR,
1934
0
          (errcode(ERRCODE_DUPLICATE_OBJECT),
1935
0
           errmsg("transform for type %s language \"%s\" already exists",
1936
0
              format_type_be(typeid),
1937
0
              stmt->lang)));
1938
1939
0
    MemSet(replaces, false, sizeof(replaces));
1940
0
    replaces[Anum_pg_transform_trffromsql - 1] = true;
1941
0
    replaces[Anum_pg_transform_trftosql - 1] = true;
1942
1943
0
    newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), values, nulls, replaces);
1944
0
    CatalogTupleUpdate(relation, &newtuple->t_self, newtuple);
1945
1946
0
    transformid = HeapTupleGetOid(tuple);
1947
0
    ReleaseSysCache(tuple);
1948
0
    is_replace = true;
1949
0
  }
1950
0
  else
1951
0
  {
1952
0
    newtuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
1953
0
    transformid = CatalogTupleInsert(relation, newtuple);
1954
0
    is_replace = false;
1955
0
  }
1956
1957
0
  if (is_replace)
1958
0
    deleteDependencyRecordsFor(TransformRelationId, transformid, true);
1959
1960
  /* make dependency entries */
1961
0
  myself.classId = TransformRelationId;
1962
0
  myself.objectId = transformid;
1963
0
  myself.objectSubId = 0;
1964
1965
  /* dependency on language */
1966
0
  referenced.classId = LanguageRelationId;
1967
0
  referenced.objectId = langid;
1968
0
  referenced.objectSubId = 0;
1969
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1970
1971
  /* dependency on type */
1972
0
  referenced.classId = TypeRelationId;
1973
0
  referenced.objectId = typeid;
1974
0
  referenced.objectSubId = 0;
1975
0
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1976
1977
  /* dependencies on functions */
1978
0
  if (OidIsValid(fromsqlfuncid))
1979
0
  {
1980
0
    referenced.classId = ProcedureRelationId;
1981
0
    referenced.objectId = fromsqlfuncid;
1982
0
    referenced.objectSubId = 0;
1983
0
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1984
0
  }
1985
0
  if (OidIsValid(tosqlfuncid))
1986
0
  {
1987
0
    referenced.classId = ProcedureRelationId;
1988
0
    referenced.objectId = tosqlfuncid;
1989
0
    referenced.objectSubId = 0;
1990
0
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
1991
0
  }
1992
1993
  /* dependency on extension */
1994
0
  recordDependencyOnCurrentExtension(&myself, is_replace);
1995
1996
  /* Post creation hook for new transform */
1997
0
  InvokeObjectPostCreateHook(TransformRelationId, transformid, 0);
1998
1999
0
  heap_freetuple(newtuple);
2000
2001
0
  heap_close(relation, RowExclusiveLock);
2002
2003
0
  return myself;
2004
0
}
2005
2006
2007
/*
2008
 * get_transform_oid - given type OID and language OID, look up a transform OID
2009
 *
2010
 * If missing_ok is false, throw an error if the transform is not found.  If
2011
 * true, just return InvalidOid.
2012
 */
2013
Oid
2014
get_transform_oid(Oid type_id, Oid lang_id, bool missing_ok)
2015
8.99k
{
2016
8.99k
  Oid     oid;
2017
2018
8.99k
  oid = GetSysCacheOid2(TRFTYPELANG,
2019
8.99k
              ObjectIdGetDatum(type_id),
2020
8.99k
              ObjectIdGetDatum(lang_id));
2021
8.99k
  if (!OidIsValid(oid) && !missing_ok)
2022
8.99k
    ereport(ERROR,
2023
8.99k
        (errcode(ERRCODE_UNDEFINED_OBJECT),
2024
8.99k
         errmsg("transform for type %s language \"%s\" does not exist",
2025
8.99k
            format_type_be(type_id),
2026
8.99k
            get_language_name(lang_id, false))));
2027
8.99k
  return oid;
2028
8.99k
}
2029
2030
2031
void
2032
DropTransformById(Oid transformOid)
2033
0
{
2034
0
  Relation  relation;
2035
0
  ScanKeyData scankey;
2036
0
  SysScanDesc scan;
2037
0
  HeapTuple tuple;
2038
2039
0
  relation = heap_open(TransformRelationId, RowExclusiveLock);
2040
2041
0
  ScanKeyInit(&scankey,
2042
0
        ObjectIdAttributeNumber,
2043
0
        BTEqualStrategyNumber, F_OIDEQ,
2044
0
        ObjectIdGetDatum(transformOid));
2045
0
  scan = systable_beginscan(relation, TransformOidIndexId, true,
2046
0
                NULL, 1, &scankey);
2047
2048
0
  tuple = systable_getnext(scan);
2049
0
  if (!HeapTupleIsValid(tuple))
2050
0
    elog(ERROR, "could not find tuple for transform %u", transformOid);
2051
0
  CatalogTupleDelete(relation, tuple);
2052
2053
0
  systable_endscan(scan);
2054
0
  heap_close(relation, RowExclusiveLock);
2055
0
}
2056
2057
2058
/*
2059
 * Subroutine for ALTER FUNCTION/AGGREGATE SET SCHEMA/RENAME
2060
 *
2061
 * Is there a function with the given name and signature already in the given
2062
 * namespace?  If so, raise an appropriate error message.
2063
 */
2064
void
2065
IsThereFunctionInNamespace(const char *proname, int pronargs,
2066
               oidvector *proargtypes, Oid nspOid)
2067
5
{
2068
  /* check for duplicate name (more friendly than unique-index failure) */
2069
5
  if (SearchSysCacheExists3(PROCNAMEARGSNSP,
2070
5
                CStringGetDatum(proname),
2071
5
                PointerGetDatum(proargtypes),
2072
5
                ObjectIdGetDatum(nspOid)))
2073
5
    ereport(ERROR,
2074
5
        (errcode(ERRCODE_DUPLICATE_FUNCTION),
2075
5
         errmsg("function %s already exists in schema \"%s\"",
2076
5
            funcname_signature_string(proname, pronargs,
2077
5
                          NIL, proargtypes->values),
2078
5
            get_namespace_name(nspOid))));
2079
5
}
2080
2081
/*
2082
 * ExecuteDoStmt
2083
 *    Execute inline procedural-language code
2084
 *
2085
 * See at ExecuteCallStmt() about the atomic argument.
2086
 */
2087
void
2088
ExecuteDoStmt(DoStmt *stmt, bool atomic)
2089
441
{
2090
441
  InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
2091
0
  ListCell   *arg;
2092
441
  DefElem    *as_item = NULL;
2093
441
  DefElem    *language_item = NULL;
2094
441
  char     *language;
2095
441
  Oid     laninline;
2096
441
  HeapTuple languageTuple;
2097
441
  Form_pg_language languageStruct;
2098
2099
  /* Process options we got from gram.y */
2100
441
  foreach(arg, stmt->args)
2101
446
  {
2102
446
    DefElem    *defel = (DefElem *) lfirst(arg);
2103
2104
446
    if (strcmp(defel->defname, "as") == 0)
2105
441
    {
2106
441
      if (as_item)
2107
441
        ereport(ERROR,
2108
441
            (errcode(ERRCODE_SYNTAX_ERROR),
2109
441
             errmsg("conflicting or redundant options")));
2110
441
      as_item = defel;
2111
441
    }
2112
5
    else if (strcmp(defel->defname, "language") == 0)
2113
5
    {
2114
5
      if (language_item)
2115
5
        ereport(ERROR,
2116
5
            (errcode(ERRCODE_SYNTAX_ERROR),
2117
5
             errmsg("conflicting or redundant options")));
2118
5
      language_item = defel;
2119
5
    }
2120
0
    else
2121
0
      elog(ERROR, "option \"%s\" not recognized",
2122
446
         defel->defname);
2123
446
  }
2124
2125
441
  if (as_item)
2126
441
    codeblock->source_text = strVal(as_item->arg);
2127
0
  else
2128
441
    ereport(ERROR,
2129
441
        (errcode(ERRCODE_SYNTAX_ERROR),
2130
441
         errmsg("no inline code specified")));
2131
2132
  /* if LANGUAGE option wasn't specified, use the default */
2133
441
  if (language_item)
2134
5
    language = strVal(language_item->arg);
2135
436
  else
2136
436
    language = "plpgsql";
2137
2138
  /* Look up the language and validate permissions */
2139
441
  languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));
2140
441
  if (!HeapTupleIsValid(languageTuple))
2141
441
    ereport(ERROR,
2142
441
        (errcode(ERRCODE_UNDEFINED_OBJECT),
2143
441
         errmsg("language \"%s\" does not exist", language),
2144
441
         (PLTemplateExists(language) ?
2145
441
          errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
2146
2147
441
  codeblock->langOid = HeapTupleGetOid(languageTuple);
2148
441
  languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
2149
441
  codeblock->langIsTrusted = languageStruct->lanpltrusted;
2150
441
  codeblock->atomic = atomic;
2151
2152
441
  if (languageStruct->lanpltrusted)
2153
441
  {
2154
    /* if trusted language, need USAGE privilege */
2155
441
    AclResult aclresult;
2156
2157
441
    aclresult = pg_language_aclcheck(codeblock->langOid, GetUserId(),
2158
441
                     ACL_USAGE);
2159
441
    if (aclresult != ACLCHECK_OK)
2160
0
      aclcheck_error(aclresult, OBJECT_LANGUAGE,
2161
0
               NameStr(languageStruct->lanname));
2162
441
  }
2163
0
  else
2164
0
  {
2165
    /* if untrusted language, must be superuser */
2166
0
    if (!superuser())
2167
0
      aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_LANGUAGE,
2168
0
               NameStr(languageStruct->lanname));
2169
0
  }
2170
2171
  /* get the handler function's OID */
2172
441
  laninline = languageStruct->laninline;
2173
441
  if (!OidIsValid(laninline))
2174
441
    ereport(ERROR,
2175
441
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2176
441
         errmsg("language \"%s\" does not support inline code execution",
2177
441
            NameStr(languageStruct->lanname))));
2178
2179
441
  ReleaseSysCache(languageTuple);
2180
2181
  /* execute the inline handler */
2182
441
  OidFunctionCall1(laninline, PointerGetDatum(codeblock));
2183
441
}
2184
2185
/*
2186
 * Execute CALL statement
2187
 *
2188
 * Inside a top-level CALL statement, transaction-terminating commands such as
2189
 * COMMIT or a PL-specific equivalent are allowed.  The terminology in the SQL
2190
 * standard is that CALL establishes a non-atomic execution context.  Most
2191
 * other commands establish an atomic execution context, in which transaction
2192
 * control actions are not allowed.  If there are nested executions of CALL,
2193
 * we want to track the execution context recursively, so that the nested
2194
 * CALLs can also do transaction control.  Note, however, that for example in
2195
 * CALL -> SELECT -> CALL, the second call cannot do transaction control,
2196
 * because the SELECT in between establishes an atomic execution context.
2197
 *
2198
 * So when ExecuteCallStmt() is called from the top level, we pass in atomic =
2199
 * false (recall that that means transactions = yes).  We then create a
2200
 * CallContext node with content atomic = false, which is passed in the
2201
 * fcinfo->context field to the procedure invocation.  The language
2202
 * implementation should then take appropriate measures to allow or prevent
2203
 * transaction commands based on that information, e.g., call
2204
 * SPI_connect_ext(SPI_OPT_NONATOMIC).  The language should also pass on the
2205
 * atomic flag to any nested invocations to CALL.
2206
 *
2207
 * The expression data structures and execution context that we create
2208
 * within this function are children of the portalContext of the Portal
2209
 * that the CALL utility statement runs in.  Therefore, any pass-by-ref
2210
 * values that we're passing to the procedure will survive transaction
2211
 * commits that might occur inside the procedure.
2212
 */
2213
void
2214
ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver *dest)
2215
426
{
2216
426
  ListCell   *lc;
2217
426
  FuncExpr   *fexpr;
2218
426
  int     nargs;
2219
426
  int     i;
2220
426
  AclResult aclresult;
2221
426
  FmgrInfo  flinfo;
2222
426
  FunctionCallInfoData fcinfo;
2223
426
  CallContext *callcontext;
2224
426
  EState     *estate;
2225
426
  ExprContext *econtext;
2226
426
  HeapTuple tp;
2227
426
  PgStat_FunctionCallUsage fcusage;
2228
426
  Datum   retval;
2229
2230
426
  fexpr = stmt->funcexpr;
2231
426
  Assert(fexpr);
2232
426
  Assert(IsA(fexpr, FuncExpr));
2233
2234
426
  aclresult = pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE);
2235
426
  if (aclresult != ACLCHECK_OK)
2236
1
    aclcheck_error(aclresult, OBJECT_PROCEDURE, get_func_name(fexpr->funcid));
2237
2238
  /* Prep the context object we'll pass to the procedure */
2239
426
  callcontext = makeNode(CallContext);
2240
0
  callcontext->atomic = atomic;
2241
2242
426
  tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
2243
426
  if (!HeapTupleIsValid(tp))
2244
0
    elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
2245
2246
  /*
2247
   * If proconfig is set we can't allow transaction commands because of the
2248
   * way the GUC stacking works: The transaction boundary would have to pop
2249
   * the proconfig setting off the stack.  That restriction could be lifted
2250
   * by redesigning the GUC nesting mechanism a bit.
2251
   */
2252
426
  if (!heap_attisnull(tp, Anum_pg_proc_proconfig, NULL))
2253
0
    callcontext->atomic = true;
2254
2255
  /*
2256
   * In security definer procedures, we can't allow transaction commands.
2257
   * StartTransaction() insists that the security context stack is empty,
2258
   * and AbortTransaction() resets the security context.  This could be
2259
   * reorganized, but right now it doesn't work.
2260
   */
2261
426
  if (((Form_pg_proc) GETSTRUCT(tp))->prosecdef)
2262
0
    callcontext->atomic = true;
2263
2264
  /*
2265
   * Expand named arguments, defaults, etc.  We do not want to scribble on
2266
   * the passed-in CallStmt parse tree, so first flat-copy fexpr, allowing
2267
   * us to replace its args field.  (Note that expand_function_arguments
2268
   * will not modify any of the passed-in data structure.)
2269
   */
2270
426
  {
2271
426
    FuncExpr   *nexpr = makeNode(FuncExpr);
2272
2273
0
    memcpy(nexpr, fexpr, sizeof(FuncExpr));
2274
426
    fexpr = nexpr;
2275
426
  }
2276
2277
0
  fexpr->args = expand_function_arguments(fexpr->args,
2278
426
                      fexpr->funcresulttype,
2279
426
                      tp);
2280
426
  nargs = list_length(fexpr->args);
2281
2282
426
  ReleaseSysCache(tp);
2283
2284
  /* safety check; see ExecInitFunc() */
2285
426
  if (nargs > FUNC_MAX_ARGS)
2286
426
    ereport(ERROR,
2287
426
        (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
2288
426
         errmsg_plural("cannot pass more than %d argument to a procedure",
2289
426
                 "cannot pass more than %d arguments to a procedure",
2290
426
                 FUNC_MAX_ARGS,
2291
426
                 FUNC_MAX_ARGS)));
2292
2293
  /* Initialize function call structure */
2294
426
  InvokeFunctionExecuteHook(fexpr->funcid);
2295
426
  fmgr_info(fexpr->funcid, &flinfo);
2296
426
  fmgr_info_set_expr((Node *) fexpr, &flinfo);
2297
426
  InitFunctionCallInfoData(fcinfo, &flinfo, nargs, fexpr->inputcollid, (Node *) callcontext, NULL);
2298
2299
  /*
2300
   * Evaluate procedure arguments inside a suitable execution context.  Note
2301
   * we can't free this context till the procedure returns.
2302
   */
2303
426
  estate = CreateExecutorState();
2304
426
  estate->es_param_list_info = params;
2305
426
  econtext = CreateExprContext(estate);
2306
2307
426
  i = 0;
2308
426
  foreach(lc, fexpr->args)
2309
421
  {
2310
421
    ExprState  *exprstate;
2311
421
    Datum   val;
2312
421
    bool    isnull;
2313
2314
421
    exprstate = ExecPrepareExpr(lfirst(lc), estate);
2315
2316
421
    val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
2317
2318
421
    fcinfo.arg[i] = val;
2319
421
    fcinfo.argnull[i] = isnull;
2320
2321
421
    i++;
2322
421
  }
2323
2324
426
  pgstat_init_function_usage(&fcinfo, &fcusage);
2325
426
  retval = FunctionCallInvoke(&fcinfo);
2326
426
  pgstat_end_function_usage(&fcusage, true);
2327
2328
426
  if (fexpr->funcresulttype == VOIDOID)
2329
422
  {
2330
    /* do nothing */
2331
422
  }
2332
4
  else if (fexpr->funcresulttype == RECORDOID)
2333
1
  {
2334
    /*
2335
     * send tuple to client
2336
     */
2337
2338
1
    HeapTupleHeader td;
2339
1
    Oid     tupType;
2340
1
    int32   tupTypmod;
2341
1
    TupleDesc retdesc;
2342
1
    HeapTupleData rettupdata;
2343
1
    TupOutputState *tstate;
2344
1
    TupleTableSlot *slot;
2345
2346
1
    if (fcinfo.isnull)
2347
0
      elog(ERROR, "procedure returned null record");
2348
2349
1
    td = DatumGetHeapTupleHeader(retval);
2350
1
    tupType = HeapTupleHeaderGetTypeId(td);
2351
1
    tupTypmod = HeapTupleHeaderGetTypMod(td);
2352
1
    retdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
2353
2354
1
    tstate = begin_tup_output_tupdesc(dest, retdesc);
2355
2356
1
    rettupdata.t_len = HeapTupleHeaderGetDatumLength(td);
2357
1
    ItemPointerSetInvalid(&(rettupdata.t_self));
2358
1
    rettupdata.t_tableOid = InvalidOid;
2359
1
    rettupdata.t_data = td;
2360
1
    rettupdata.t_ybctid = PointerGetDatum(NULL);
2361
2362
1
    slot = ExecStoreHeapTuple(&rettupdata, tstate->slot, false);
2363
1
    tstate->dest->receiveSlot(slot, tstate->dest);
2364
2365
1
    end_tup_output(tstate);
2366
2367
1
    ReleaseTupleDesc(retdesc);
2368
1
  }
2369
3
  else
2370
3
    elog(ERROR, "unexpected result type for procedure: %u",
2371
426
       fexpr->funcresulttype);
2372
2373
426
  FreeExecutorState(estate);
2374
426
}
2375
2376
/*
2377
 * Construct the tuple descriptor for a CALL statement return
2378
 */
2379
TupleDesc
2380
CallStmtResultDesc(CallStmt *stmt)
2381
1
{
2382
1
  FuncExpr   *fexpr;
2383
1
  HeapTuple tuple;
2384
1
  TupleDesc tupdesc;
2385
2386
1
  fexpr = stmt->funcexpr;
2387
2388
1
  tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
2389
1
  if (!HeapTupleIsValid(tuple))
2390
0
    elog(ERROR, "cache lookup failed for procedure %u", fexpr->funcid);
2391
2392
1
  tupdesc = build_function_result_tupdesc_t(tuple);
2393
2394
1
  ReleaseSysCache(tuple);
2395
2396
1
  return tupdesc;
2397
1
}