LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_proc.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 343 411 83.5 %
Date: 2017-09-29 13:40:31 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_proc.c
       4             :  *    routines to support manipulation of the pg_proc relation
       5             :  *
       6             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/catalog/pg_proc.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/htup_details.h"
      18             : #include "access/xact.h"
      19             : #include "catalog/dependency.h"
      20             : #include "catalog/indexing.h"
      21             : #include "catalog/objectaccess.h"
      22             : #include "catalog/pg_language.h"
      23             : #include "catalog/pg_namespace.h"
      24             : #include "catalog/pg_proc.h"
      25             : #include "catalog/pg_proc_fn.h"
      26             : #include "catalog/pg_transform.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "commands/defrem.h"
      29             : #include "executor/functions.h"
      30             : #include "funcapi.h"
      31             : #include "mb/pg_wchar.h"
      32             : #include "miscadmin.h"
      33             : #include "nodes/nodeFuncs.h"
      34             : #include "parser/parse_type.h"
      35             : #include "tcop/pquery.h"
      36             : #include "tcop/tcopprot.h"
      37             : #include "utils/acl.h"
      38             : #include "utils/builtins.h"
      39             : #include "utils/lsyscache.h"
      40             : #include "utils/regproc.h"
      41             : #include "utils/rel.h"
      42             : #include "utils/syscache.h"
      43             : 
      44             : 
      45             : typedef struct
      46             : {
      47             :     char       *proname;
      48             :     char       *prosrc;
      49             : } parse_error_callback_arg;
      50             : 
      51             : static void sql_function_parse_error_callback(void *arg);
      52             : static int match_prosrc_to_query(const char *prosrc, const char *queryText,
      53             :                       int cursorpos);
      54             : static bool match_prosrc_to_literal(const char *prosrc, const char *literal,
      55             :                         int cursorpos, int *newcursorpos);
      56             : 
      57             : 
      58             : /* ----------------------------------------------------------------
      59             :  *      ProcedureCreate
      60             :  *
      61             :  * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig
      62             :  * are either arrays of the proper types or NULL.  We declare them Datum,
      63             :  * not "ArrayType *", to avoid importing array.h into pg_proc_fn.h.
      64             :  * ----------------------------------------------------------------
      65             :  */
      66             : ObjectAddress
      67         846 : ProcedureCreate(const char *procedureName,
      68             :                 Oid procNamespace,
      69             :                 bool replace,
      70             :                 bool returnsSet,
      71             :                 Oid returnType,
      72             :                 Oid proowner,
      73             :                 Oid languageObjectId,
      74             :                 Oid languageValidator,
      75             :                 const char *prosrc,
      76             :                 const char *probin,
      77             :                 bool isAgg,
      78             :                 bool isWindowFunc,
      79             :                 bool security_definer,
      80             :                 bool isLeakProof,
      81             :                 bool isStrict,
      82             :                 char volatility,
      83             :                 char parallel,
      84             :                 oidvector *parameterTypes,
      85             :                 Datum allParameterTypes,
      86             :                 Datum parameterModes,
      87             :                 Datum parameterNames,
      88             :                 List *parameterDefaults,
      89             :                 Datum trftypes,
      90             :                 Datum proconfig,
      91             :                 float4 procost,
      92             :                 float4 prorows)
      93             : {
      94             :     Oid         retval;
      95             :     int         parameterCount;
      96             :     int         allParamCount;
      97             :     Oid        *allParams;
      98         846 :     char       *paramModes = NULL;
      99         846 :     bool        genericInParam = false;
     100         846 :     bool        genericOutParam = false;
     101         846 :     bool        anyrangeInParam = false;
     102         846 :     bool        anyrangeOutParam = false;
     103         846 :     bool        internalInParam = false;
     104         846 :     bool        internalOutParam = false;
     105         846 :     Oid         variadicType = InvalidOid;
     106         846 :     Acl        *proacl = NULL;
     107             :     Relation    rel;
     108             :     HeapTuple   tup;
     109             :     HeapTuple   oldtup;
     110             :     bool        nulls[Natts_pg_proc];
     111             :     Datum       values[Natts_pg_proc];
     112             :     bool        replaces[Natts_pg_proc];
     113             :     Oid         relid;
     114             :     NameData    procname;
     115             :     TupleDesc   tupDesc;
     116             :     bool        is_update;
     117             :     ObjectAddress myself,
     118             :                 referenced;
     119             :     int         i;
     120             :     Oid         trfid;
     121             : 
     122             :     /*
     123             :      * sanity checks
     124             :      */
     125         846 :     Assert(PointerIsValid(prosrc));
     126             : 
     127         846 :     parameterCount = parameterTypes->dim1;
     128         846 :     if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
     129           0 :         ereport(ERROR,
     130             :                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
     131             :                  errmsg_plural("functions cannot have more than %d argument",
     132             :                                "functions cannot have more than %d arguments",
     133             :                                FUNC_MAX_ARGS,
     134             :                                FUNC_MAX_ARGS)));
     135             :     /* note: the above is correct, we do NOT count output arguments */
     136             : 
     137             :     /* Deconstruct array inputs */
     138         846 :     if (allParameterTypes != PointerGetDatum(NULL))
     139             :     {
     140             :         /*
     141             :          * We expect the array to be a 1-D OID array; verify that. We don't
     142             :          * need to use deconstruct_array() since the array data is just going
     143             :          * to look like a C array of OID values.
     144             :          */
     145          73 :         ArrayType  *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes);
     146             : 
     147          73 :         allParamCount = ARR_DIMS(allParamArray)[0];
     148          73 :         if (ARR_NDIM(allParamArray) != 1 ||
     149          73 :             allParamCount <= 0 ||
     150         146 :             ARR_HASNULL(allParamArray) ||
     151          73 :             ARR_ELEMTYPE(allParamArray) != OIDOID)
     152           0 :             elog(ERROR, "allParameterTypes is not a 1-D Oid array");
     153          73 :         allParams = (Oid *) ARR_DATA_PTR(allParamArray);
     154          73 :         Assert(allParamCount >= parameterCount);
     155             :         /* we assume caller got the contents right */
     156             :     }
     157             :     else
     158             :     {
     159         773 :         allParamCount = parameterCount;
     160         773 :         allParams = parameterTypes->values;
     161             :     }
     162             : 
     163         846 :     if (parameterModes != PointerGetDatum(NULL))
     164             :     {
     165             :         /*
     166             :          * We expect the array to be a 1-D CHAR array; verify that. We don't
     167             :          * need to use deconstruct_array() since the array data is just going
     168             :          * to look like a C array of char values.
     169             :          */
     170          73 :         ArrayType  *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
     171             : 
     172         146 :         if (ARR_NDIM(modesArray) != 1 ||
     173         146 :             ARR_DIMS(modesArray)[0] != allParamCount ||
     174         146 :             ARR_HASNULL(modesArray) ||
     175          73 :             ARR_ELEMTYPE(modesArray) != CHAROID)
     176           0 :             elog(ERROR, "parameterModes is not a 1-D char array");
     177          73 :         paramModes = (char *) ARR_DATA_PTR(modesArray);
     178             :     }
     179             : 
     180             :     /*
     181             :      * Detect whether we have polymorphic or INTERNAL arguments.  The first
     182             :      * loop checks input arguments, the second output arguments.
     183             :      */
     184        2148 :     for (i = 0; i < parameterCount; i++)
     185             :     {
     186        1302 :         switch (parameterTypes->values[i])
     187             :         {
     188             :             case ANYARRAYOID:
     189             :             case ANYELEMENTOID:
     190             :             case ANYNONARRAYOID:
     191             :             case ANYENUMOID:
     192          75 :                 genericInParam = true;
     193          75 :                 break;
     194             :             case ANYRANGEOID:
     195           6 :                 genericInParam = true;
     196           6 :                 anyrangeInParam = true;
     197           6 :                 break;
     198             :             case INTERNALOID:
     199         140 :                 internalInParam = true;
     200         140 :                 break;
     201             :         }
     202             :     }
     203             : 
     204         846 :     if (allParameterTypes != PointerGetDatum(NULL))
     205             :     {
     206         291 :         for (i = 0; i < allParamCount; i++)
     207             :         {
     208         436 :             if (paramModes == NULL ||
     209         360 :                 paramModes[i] == PROARGMODE_IN ||
     210         142 :                 paramModes[i] == PROARGMODE_VARIADIC)
     211          91 :                 continue;       /* ignore input-only params */
     212             : 
     213         127 :             switch (allParams[i])
     214             :             {
     215             :                 case ANYARRAYOID:
     216             :                 case ANYELEMENTOID:
     217             :                 case ANYNONARRAYOID:
     218             :                 case ANYENUMOID:
     219          16 :                     genericOutParam = true;
     220          16 :                     break;
     221             :                 case ANYRANGEOID:
     222           6 :                     genericOutParam = true;
     223           6 :                     anyrangeOutParam = true;
     224           6 :                     break;
     225             :                 case INTERNALOID:
     226           0 :                     internalOutParam = true;
     227           0 :                     break;
     228             :             }
     229             :         }
     230             :     }
     231             : 
     232             :     /*
     233             :      * Do not allow polymorphic return type unless at least one input argument
     234             :      * is polymorphic.  ANYRANGE return type is even stricter: must have an
     235             :      * ANYRANGE input (since we can't deduce the specific range type from
     236             :      * ANYELEMENT).  Also, do not allow return type INTERNAL unless at least
     237             :      * one input argument is INTERNAL.
     238             :      */
     239         846 :     if ((IsPolymorphicType(returnType) || genericOutParam)
     240          43 :         && !genericInParam)
     241           2 :         ereport(ERROR,
     242             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     243             :                  errmsg("cannot determine result data type"),
     244             :                  errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
     245             : 
     246         844 :     if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
     247             :         !anyrangeInParam)
     248           4 :         ereport(ERROR,
     249             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     250             :                  errmsg("cannot determine result data type"),
     251             :                  errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
     252             : 
     253         840 :     if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
     254           0 :         ereport(ERROR,
     255             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     256             :                  errmsg("unsafe use of pseudo-type \"internal\""),
     257             :                  errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
     258             : 
     259             :     /*
     260             :      * don't allow functions of complex types that have the same name as
     261             :      * existing attributes of the type
     262             :      */
     263        1137 :     if (parameterCount == 1 &&
     264         594 :         OidIsValid(parameterTypes->values[0]) &&
     265         319 :         (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid &&
     266          22 :         get_attnum(relid, procedureName) != InvalidAttrNumber)
     267           0 :         ereport(ERROR,
     268             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
     269             :                  errmsg("\"%s\" is already an attribute of type %s",
     270             :                         procedureName,
     271             :                         format_type_be(parameterTypes->values[0]))));
     272             : 
     273         840 :     if (paramModes != NULL)
     274             :     {
     275             :         /*
     276             :          * Only the last input parameter can be variadic; if it is, save its
     277             :          * element type.  Errors here are just elog since caller should have
     278             :          * checked this already.
     279             :          */
     280         276 :         for (i = 0; i < allParamCount; i++)
     281             :         {
     282         207 :             switch (paramModes[i])
     283             :             {
     284             :                 case PROARGMODE_IN:
     285             :                 case PROARGMODE_INOUT:
     286          78 :                     if (OidIsValid(variadicType))
     287           0 :                         elog(ERROR, "variadic parameter must be last");
     288          78 :                     break;
     289             :                 case PROARGMODE_OUT:
     290             :                 case PROARGMODE_TABLE:
     291             :                     /* okay */
     292         114 :                     break;
     293             :                 case PROARGMODE_VARIADIC:
     294          15 :                     if (OidIsValid(variadicType))
     295           0 :                         elog(ERROR, "variadic parameter must be last");
     296          15 :                     switch (allParams[i])
     297             :                     {
     298             :                         case ANYOID:
     299           1 :                             variadicType = ANYOID;
     300           1 :                             break;
     301             :                         case ANYARRAYOID:
     302           5 :                             variadicType = ANYELEMENTOID;
     303           5 :                             break;
     304             :                         default:
     305           9 :                             variadicType = get_element_type(allParams[i]);
     306           9 :                             if (!OidIsValid(variadicType))
     307           0 :                                 elog(ERROR, "variadic parameter is not an array");
     308           9 :                             break;
     309             :                     }
     310          15 :                     break;
     311             :                 default:
     312           0 :                     elog(ERROR, "invalid parameter mode '%c'", paramModes[i]);
     313             :                     break;
     314             :             }
     315             :         }
     316             :     }
     317             : 
     318             :     /*
     319             :      * All seems OK; prepare the data to be inserted into pg_proc.
     320             :      */
     321             : 
     322       25200 :     for (i = 0; i < Natts_pg_proc; ++i)
     323             :     {
     324       24360 :         nulls[i] = false;
     325       24360 :         values[i] = (Datum) 0;
     326       24360 :         replaces[i] = true;
     327             :     }
     328             : 
     329         840 :     namestrcpy(&procname, procedureName);
     330         840 :     values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);
     331         840 :     values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
     332         840 :     values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);
     333         840 :     values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
     334         840 :     values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
     335         840 :     values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
     336         840 :     values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
     337         840 :     values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid);
     338         840 :     values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
     339         840 :     values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
     340         840 :     values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
     341         840 :     values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
     342         840 :     values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
     343         840 :     values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
     344         840 :     values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
     345         840 :     values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel);
     346         840 :     values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);
     347         840 :     values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));
     348         840 :     values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
     349         840 :     values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
     350         840 :     if (allParameterTypes != PointerGetDatum(NULL))
     351          69 :         values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
     352             :     else
     353         771 :         nulls[Anum_pg_proc_proallargtypes - 1] = true;
     354         840 :     if (parameterModes != PointerGetDatum(NULL))
     355          69 :         values[Anum_pg_proc_proargmodes - 1] = parameterModes;
     356             :     else
     357         771 :         nulls[Anum_pg_proc_proargmodes - 1] = true;
     358         840 :     if (parameterNames != PointerGetDatum(NULL))
     359         128 :         values[Anum_pg_proc_proargnames - 1] = parameterNames;
     360             :     else
     361         712 :         nulls[Anum_pg_proc_proargnames - 1] = true;
     362         840 :     if (parameterDefaults != NIL)
     363          34 :         values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults));
     364             :     else
     365         806 :         nulls[Anum_pg_proc_proargdefaults - 1] = true;
     366         840 :     if (trftypes != PointerGetDatum(NULL))
     367           0 :         values[Anum_pg_proc_protrftypes - 1] = trftypes;
     368             :     else
     369         840 :         nulls[Anum_pg_proc_protrftypes - 1] = true;
     370         840 :     values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);
     371         840 :     if (probin)
     372         156 :         values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin);
     373             :     else
     374         684 :         nulls[Anum_pg_proc_probin - 1] = true;
     375         840 :     if (proconfig != PointerGetDatum(NULL))
     376           6 :         values[Anum_pg_proc_proconfig - 1] = proconfig;
     377             :     else
     378         834 :         nulls[Anum_pg_proc_proconfig - 1] = true;
     379             :     /* proacl will be determined later */
     380             : 
     381         840 :     rel = heap_open(ProcedureRelationId, RowExclusiveLock);
     382         840 :     tupDesc = RelationGetDescr(rel);
     383             : 
     384             :     /* Check for pre-existing definition */
     385         840 :     oldtup = SearchSysCache3(PROCNAMEARGSNSP,
     386             :                              PointerGetDatum(procedureName),
     387             :                              PointerGetDatum(parameterTypes),
     388             :                              ObjectIdGetDatum(procNamespace));
     389             : 
     390         840 :     if (HeapTupleIsValid(oldtup))
     391             :     {
     392             :         /* There is one; okay to replace it? */
     393         146 :         Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
     394             :         Datum       proargnames;
     395             :         bool        isnull;
     396             : 
     397         146 :         if (!replace)
     398           1 :             ereport(ERROR,
     399             :                     (errcode(ERRCODE_DUPLICATE_FUNCTION),
     400             :                      errmsg("function \"%s\" already exists with same argument types",
     401             :                             procedureName)));
     402         145 :         if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner))
     403           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
     404             :                            procedureName);
     405             : 
     406             :         /*
     407             :          * Not okay to change the return type of the existing proc, since
     408             :          * existing rules, views, etc may depend on the return type.
     409             :          */
     410         289 :         if (returnType != oldproc->prorettype ||
     411         144 :             returnsSet != oldproc->proretset)
     412           1 :             ereport(ERROR,
     413             :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     414             :                      errmsg("cannot change return type of existing function"),
     415             :                      errhint("Use DROP FUNCTION %s first.",
     416             :                              format_procedure(HeapTupleGetOid(oldtup)))));
     417             : 
     418             :         /*
     419             :          * If it returns RECORD, check for possible change of record type
     420             :          * implied by OUT parameters
     421             :          */
     422         144 :         if (returnType == RECORDOID)
     423             :         {
     424             :             TupleDesc   olddesc;
     425             :             TupleDesc   newdesc;
     426             : 
     427          14 :             olddesc = build_function_result_tupdesc_t(oldtup);
     428          14 :             newdesc = build_function_result_tupdesc_d(allParameterTypes,
     429             :                                                       parameterModes,
     430             :                                                       parameterNames);
     431          14 :             if (olddesc == NULL && newdesc == NULL)
     432             :                  /* ok, both are runtime-defined RECORDs */ ;
     433          26 :             else if (olddesc == NULL || newdesc == NULL ||
     434          13 :                      !equalTupleDescs(olddesc, newdesc))
     435           0 :                 ereport(ERROR,
     436             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     437             :                          errmsg("cannot change return type of existing function"),
     438             :                          errdetail("Row type defined by OUT parameters is different."),
     439             :                          errhint("Use DROP FUNCTION %s first.",
     440             :                                  format_procedure(HeapTupleGetOid(oldtup)))));
     441             :         }
     442             : 
     443             :         /*
     444             :          * If there were any named input parameters, check to make sure the
     445             :          * names have not been changed, as this could break existing calls. We
     446             :          * allow adding names to formerly unnamed parameters, though.
     447             :          */
     448         144 :         proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
     449             :                                       Anum_pg_proc_proargnames,
     450             :                                       &isnull);
     451         144 :         if (!isnull)
     452             :         {
     453             :             Datum       proargmodes;
     454             :             char      **old_arg_names;
     455             :             char      **new_arg_names;
     456             :             int         n_old_arg_names;
     457             :             int         n_new_arg_names;
     458             :             int         j;
     459             : 
     460          20 :             proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
     461             :                                           Anum_pg_proc_proargmodes,
     462             :                                           &isnull);
     463          20 :             if (isnull)
     464           4 :                 proargmodes = PointerGetDatum(NULL);    /* just to be sure */
     465             : 
     466          20 :             n_old_arg_names = get_func_input_arg_names(proargnames,
     467             :                                                        proargmodes,
     468             :                                                        &old_arg_names);
     469          20 :             n_new_arg_names = get_func_input_arg_names(parameterNames,
     470             :                                                        parameterModes,
     471             :                                                        &new_arg_names);
     472          63 :             for (j = 0; j < n_old_arg_names; j++)
     473             :             {
     474          46 :                 if (old_arg_names[j] == NULL)
     475           1 :                     continue;
     476          89 :                 if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
     477          44 :                     strcmp(old_arg_names[j], new_arg_names[j]) != 0)
     478           3 :                     ereport(ERROR,
     479             :                             (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     480             :                              errmsg("cannot change name of input parameter \"%s\"",
     481             :                                     old_arg_names[j]),
     482             :                              errhint("Use DROP FUNCTION %s first.",
     483             :                                      format_procedure(HeapTupleGetOid(oldtup)))));
     484             :             }
     485             :         }
     486             : 
     487             :         /*
     488             :          * If there are existing defaults, check compatibility: redefinition
     489             :          * must not remove any defaults nor change their types.  (Removing a
     490             :          * default might cause a function to fail to satisfy an existing call.
     491             :          * Changing type would only be possible if the associated parameter is
     492             :          * polymorphic, and in such cases a change of default type might alter
     493             :          * the resolved output type of existing calls.)
     494             :          */
     495         141 :         if (oldproc->pronargdefaults != 0)
     496             :         {
     497             :             Datum       proargdefaults;
     498             :             List       *oldDefaults;
     499             :             ListCell   *oldlc;
     500             :             ListCell   *newlc;
     501             : 
     502           1 :             if (list_length(parameterDefaults) < oldproc->pronargdefaults)
     503           1 :                 ereport(ERROR,
     504             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     505             :                          errmsg("cannot remove parameter defaults from existing function"),
     506             :                          errhint("Use DROP FUNCTION %s first.",
     507             :                                  format_procedure(HeapTupleGetOid(oldtup)))));
     508             : 
     509           0 :             proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
     510             :                                              Anum_pg_proc_proargdefaults,
     511             :                                              &isnull);
     512           0 :             Assert(!isnull);
     513           0 :             oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults)));
     514           0 :             Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
     515             : 
     516             :             /* new list can have more defaults than old, advance over 'em */
     517           0 :             newlc = list_head(parameterDefaults);
     518           0 :             for (i = list_length(parameterDefaults) - oldproc->pronargdefaults;
     519             :                  i > 0;
     520           0 :                  i--)
     521           0 :                 newlc = lnext(newlc);
     522             : 
     523           0 :             foreach(oldlc, oldDefaults)
     524             :             {
     525           0 :                 Node       *oldDef = (Node *) lfirst(oldlc);
     526           0 :                 Node       *newDef = (Node *) lfirst(newlc);
     527             : 
     528           0 :                 if (exprType(oldDef) != exprType(newDef))
     529           0 :                     ereport(ERROR,
     530             :                             (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     531             :                              errmsg("cannot change data type of existing parameter default value"),
     532             :                              errhint("Use DROP FUNCTION %s first.",
     533             :                                      format_procedure(HeapTupleGetOid(oldtup)))));
     534           0 :                 newlc = lnext(newlc);
     535             :             }
     536             :         }
     537             : 
     538             :         /* Can't change aggregate or window-function status, either */
     539         140 :         if (oldproc->proisagg != isAgg)
     540             :         {
     541           0 :             if (oldproc->proisagg)
     542           0 :                 ereport(ERROR,
     543             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     544             :                          errmsg("function \"%s\" is an aggregate function",
     545             :                                 procedureName)));
     546             :             else
     547           0 :                 ereport(ERROR,
     548             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     549             :                          errmsg("function \"%s\" is not an aggregate function",
     550             :                                 procedureName)));
     551             :         }
     552         140 :         if (oldproc->proiswindow != isWindowFunc)
     553             :         {
     554           0 :             if (oldproc->proiswindow)
     555           0 :                 ereport(ERROR,
     556             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     557             :                          errmsg("function \"%s\" is a window function",
     558             :                                 procedureName)));
     559             :             else
     560           0 :                 ereport(ERROR,
     561             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     562             :                          errmsg("function \"%s\" is not a window function",
     563             :                                 procedureName)));
     564             :         }
     565             : 
     566             :         /*
     567             :          * Do not change existing ownership or permissions, either.  Note
     568             :          * dependency-update code below has to agree with this decision.
     569             :          */
     570         140 :         replaces[Anum_pg_proc_proowner - 1] = false;
     571         140 :         replaces[Anum_pg_proc_proacl - 1] = false;
     572             : 
     573             :         /* Okay, do it... */
     574         140 :         tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
     575         140 :         CatalogTupleUpdate(rel, &tup->t_self, tup);
     576             : 
     577         140 :         ReleaseSysCache(oldtup);
     578         140 :         is_update = true;
     579             :     }
     580             :     else
     581             :     {
     582             :         /* Creating a new procedure */
     583             : 
     584             :         /* First, get default permissions and set up proacl */
     585         694 :         proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner,
     586             :                                       procNamespace);
     587         694 :         if (proacl != NULL)
     588           1 :             values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
     589             :         else
     590         693 :             nulls[Anum_pg_proc_proacl - 1] = true;
     591             : 
     592         694 :         tup = heap_form_tuple(tupDesc, values, nulls);
     593         694 :         CatalogTupleInsert(rel, tup);
     594         694 :         is_update = false;
     595             :     }
     596             : 
     597             : 
     598         834 :     retval = HeapTupleGetOid(tup);
     599             : 
     600             :     /*
     601             :      * Create dependencies for the new function.  If we are updating an
     602             :      * existing function, first delete any existing pg_depend entries.
     603             :      * (However, since we are not changing ownership or permissions, the
     604             :      * shared dependencies do *not* need to change, and we leave them alone.)
     605             :      */
     606         834 :     if (is_update)
     607         140 :         deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
     608             : 
     609         834 :     myself.classId = ProcedureRelationId;
     610         834 :     myself.objectId = retval;
     611         834 :     myself.objectSubId = 0;
     612             : 
     613             :     /* dependency on namespace */
     614         834 :     referenced.classId = NamespaceRelationId;
     615         834 :     referenced.objectId = procNamespace;
     616         834 :     referenced.objectSubId = 0;
     617         834 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     618             : 
     619             :     /* dependency on implementation language */
     620         834 :     referenced.classId = LanguageRelationId;
     621         834 :     referenced.objectId = languageObjectId;
     622         834 :     referenced.objectSubId = 0;
     623         834 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     624             : 
     625             :     /* dependency on return type */
     626         834 :     referenced.classId = TypeRelationId;
     627         834 :     referenced.objectId = returnType;
     628         834 :     referenced.objectSubId = 0;
     629         834 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     630             : 
     631             :     /* dependency on transform used by return type, if any */
     632         834 :     if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
     633             :     {
     634          34 :         referenced.classId = TransformRelationId;
     635          34 :         referenced.objectId = trfid;
     636          34 :         referenced.objectSubId = 0;
     637          34 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     638             :     }
     639             : 
     640             :     /* dependency on parameter types */
     641        2229 :     for (i = 0; i < allParamCount; i++)
     642             :     {
     643        1395 :         referenced.classId = TypeRelationId;
     644        1395 :         referenced.objectId = allParams[i];
     645        1395 :         referenced.objectSubId = 0;
     646        1395 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     647             : 
     648             :         /* dependency on transform used by parameter type, if any */
     649        1395 :         if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
     650             :         {
     651          77 :             referenced.classId = TransformRelationId;
     652          77 :             referenced.objectId = trfid;
     653          77 :             referenced.objectSubId = 0;
     654          77 :             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     655             :         }
     656             :     }
     657             : 
     658             :     /* dependency on parameter default expressions */
     659         834 :     if (parameterDefaults)
     660          32 :         recordDependencyOnExpr(&myself, (Node *) parameterDefaults,
     661             :                                NIL, DEPENDENCY_NORMAL);
     662             : 
     663             :     /* dependency on owner */
     664         834 :     if (!is_update)
     665         694 :         recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
     666             : 
     667             :     /* dependency on any roles mentioned in ACL */
     668         834 :     if (!is_update && proacl != NULL)
     669             :     {
     670             :         int         nnewmembers;
     671             :         Oid        *newmembers;
     672             : 
     673           1 :         nnewmembers = aclmembers(proacl, &newmembers);
     674           1 :         updateAclDependencies(ProcedureRelationId, retval, 0,
     675             :                               proowner,
     676             :                               0, NULL,
     677             :                               nnewmembers, newmembers);
     678             :     }
     679             : 
     680             :     /* dependency on extension */
     681         834 :     recordDependencyOnCurrentExtension(&myself, is_update);
     682             : 
     683         834 :     heap_freetuple(tup);
     684             : 
     685             :     /* Post creation hook for new function */
     686         834 :     InvokeObjectPostCreateHook(ProcedureRelationId, retval, 0);
     687             : 
     688         834 :     heap_close(rel, RowExclusiveLock);
     689             : 
     690             :     /* Verify function body */
     691         834 :     if (OidIsValid(languageValidator))
     692             :     {
     693         764 :         ArrayType  *set_items = NULL;
     694         764 :         int         save_nestlevel = 0;
     695             : 
     696             :         /* Advance command counter so new tuple can be seen by validator */
     697         764 :         CommandCounterIncrement();
     698             : 
     699             :         /*
     700             :          * Set per-function configuration parameters so that the validation is
     701             :          * done with the environment the function expects.  However, if
     702             :          * check_function_bodies is off, we don't do this, because that would
     703             :          * create dump ordering hazards that pg_dump doesn't know how to deal
     704             :          * with.  (For example, a SET clause might refer to a not-yet-created
     705             :          * text search configuration.)  This means that the validator
     706             :          * shouldn't complain about anything that might depend on a GUC
     707             :          * parameter when check_function_bodies is off.
     708             :          */
     709         764 :         if (check_function_bodies)
     710             :         {
     711         763 :             set_items = (ArrayType *) DatumGetPointer(proconfig);
     712         763 :             if (set_items)      /* Need a new GUC nesting level */
     713             :             {
     714           5 :                 save_nestlevel = NewGUCNestLevel();
     715           5 :                 ProcessGUCArray(set_items,
     716           5 :                                 (superuser() ? PGC_SUSET : PGC_USERSET),
     717             :                                 PGC_S_SESSION,
     718             :                                 GUC_ACTION_SAVE);
     719             :             }
     720             :         }
     721             : 
     722         763 :         OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
     723             : 
     724         732 :         if (set_items)
     725           4 :             AtEOXact_GUC(true, save_nestlevel);
     726             :     }
     727             : 
     728         802 :     return myself;
     729             : }
     730             : 
     731             : 
     732             : 
     733             : /*
     734             :  * Validator for internal functions
     735             :  *
     736             :  * Check that the given internal function name (the "prosrc" value) is
     737             :  * a known builtin function.
     738             :  */
     739             : Datum
     740          53 : fmgr_internal_validator(PG_FUNCTION_ARGS)
     741             : {
     742          53 :     Oid         funcoid = PG_GETARG_OID(0);
     743             :     HeapTuple   tuple;
     744             :     bool        isnull;
     745             :     Datum       tmp;
     746             :     char       *prosrc;
     747             : 
     748          53 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     749           0 :         PG_RETURN_VOID();
     750             : 
     751             :     /*
     752             :      * We do not honor check_function_bodies since it's unlikely the function
     753             :      * name will be found later if it isn't there now.
     754             :      */
     755             : 
     756          53 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     757          53 :     if (!HeapTupleIsValid(tuple))
     758           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     759             : 
     760          53 :     tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
     761          53 :     if (isnull)
     762           0 :         elog(ERROR, "null prosrc");
     763          53 :     prosrc = TextDatumGetCString(tmp);
     764             : 
     765          53 :     if (fmgr_internal_function(prosrc) == InvalidOid)
     766           1 :         ereport(ERROR,
     767             :                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
     768             :                  errmsg("there is no built-in function named \"%s\"",
     769             :                         prosrc)));
     770             : 
     771          52 :     ReleaseSysCache(tuple);
     772             : 
     773          52 :     PG_RETURN_VOID();
     774             : }
     775             : 
     776             : 
     777             : 
     778             : /*
     779             :  * Validator for C language functions
     780             :  *
     781             :  * Make sure that the library file exists, is loadable, and contains
     782             :  * the specified link symbol. Also check for a valid function
     783             :  * information record.
     784             :  */
     785             : Datum
     786         156 : fmgr_c_validator(PG_FUNCTION_ARGS)
     787             : {
     788         156 :     Oid         funcoid = PG_GETARG_OID(0);
     789             :     void       *libraryhandle;
     790             :     HeapTuple   tuple;
     791             :     bool        isnull;
     792             :     Datum       tmp;
     793             :     char       *prosrc;
     794             :     char       *probin;
     795             : 
     796         156 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     797           0 :         PG_RETURN_VOID();
     798             : 
     799             :     /*
     800             :      * It'd be most consistent to skip the check if !check_function_bodies,
     801             :      * but the purpose of that switch is to be helpful for pg_dump loading,
     802             :      * and for pg_dump loading it's much better if we *do* check.
     803             :      */
     804             : 
     805         156 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     806         156 :     if (!HeapTupleIsValid(tuple))
     807           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     808             : 
     809         156 :     tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
     810         156 :     if (isnull)
     811           0 :         elog(ERROR, "null prosrc for C function %u", funcoid);
     812         156 :     prosrc = TextDatumGetCString(tmp);
     813             : 
     814         156 :     tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_probin, &isnull);
     815         156 :     if (isnull)
     816           0 :         elog(ERROR, "null probin for C function %u", funcoid);
     817         156 :     probin = TextDatumGetCString(tmp);
     818             : 
     819         156 :     (void) load_external_function(probin, prosrc, true, &libraryhandle);
     820         154 :     (void) fetch_finfo_record(libraryhandle, prosrc);
     821             : 
     822         154 :     ReleaseSysCache(tuple);
     823             : 
     824         154 :     PG_RETURN_VOID();
     825             : }
     826             : 
     827             : 
     828             : /*
     829             :  * Validator for SQL language functions
     830             :  *
     831             :  * Parse it here in order to be sure that it contains no syntax errors.
     832             :  */
     833             : Datum
     834         210 : fmgr_sql_validator(PG_FUNCTION_ARGS)
     835             : {
     836         210 :     Oid         funcoid = PG_GETARG_OID(0);
     837             :     HeapTuple   tuple;
     838             :     Form_pg_proc proc;
     839             :     List       *raw_parsetree_list;
     840             :     List       *querytree_list;
     841             :     ListCell   *lc;
     842             :     bool        isnull;
     843             :     Datum       tmp;
     844             :     char       *prosrc;
     845             :     parse_error_callback_arg callback_arg;
     846             :     ErrorContextCallback sqlerrcontext;
     847             :     bool        haspolyarg;
     848             :     int         i;
     849             : 
     850         210 :     if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
     851           0 :         PG_RETURN_VOID();
     852             : 
     853         210 :     tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid));
     854         210 :     if (!HeapTupleIsValid(tuple))
     855           0 :         elog(ERROR, "cache lookup failed for function %u", funcoid);
     856         210 :     proc = (Form_pg_proc) GETSTRUCT(tuple);
     857             : 
     858             :     /* Disallow pseudotype result */
     859             :     /* except for RECORD, VOID, or polymorphic */
     860         263 :     if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
     861          80 :         proc->prorettype != RECORDOID &&
     862          45 :         proc->prorettype != VOIDOID &&
     863          24 :         !IsPolymorphicType(proc->prorettype))
     864           1 :         ereport(ERROR,
     865             :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     866             :                  errmsg("SQL functions cannot return type %s",
     867             :                         format_type_be(proc->prorettype))));
     868             : 
     869             :     /* Disallow pseudotypes in arguments */
     870             :     /* except for polymorphic */
     871         209 :     haspolyarg = false;
     872         467 :     for (i = 0; i < proc->pronargs; i++)
     873             :     {
     874         258 :         if (get_typtype(proc->proargtypes.values[i]) == TYPTYPE_PSEUDO)
     875             :         {
     876          45 :             if (IsPolymorphicType(proc->proargtypes.values[i]))
     877          45 :                 haspolyarg = true;
     878             :             else
     879           0 :                 ereport(ERROR,
     880             :                         (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
     881             :                          errmsg("SQL functions cannot have arguments of type %s",
     882             :                                 format_type_be(proc->proargtypes.values[i]))));
     883             :         }
     884             :     }
     885             : 
     886             :     /* Postpone body checks if !check_function_bodies */
     887         209 :     if (check_function_bodies)
     888             :     {
     889         208 :         tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
     890         208 :         if (isnull)
     891           0 :             elog(ERROR, "null prosrc");
     892             : 
     893         208 :         prosrc = TextDatumGetCString(tmp);
     894             : 
     895             :         /*
     896             :          * Setup error traceback support for ereport().
     897             :          */
     898         208 :         callback_arg.proname = NameStr(proc->proname);
     899         208 :         callback_arg.prosrc = prosrc;
     900             : 
     901         208 :         sqlerrcontext.callback = sql_function_parse_error_callback;
     902         208 :         sqlerrcontext.arg = (void *) &callback_arg;
     903         208 :         sqlerrcontext.previous = error_context_stack;
     904         208 :         error_context_stack = &sqlerrcontext;
     905             : 
     906             :         /*
     907             :          * We can't do full prechecking of the function definition if there
     908             :          * are any polymorphic input types, because actual datatypes of
     909             :          * expression results will be unresolvable.  The check will be done at
     910             :          * runtime instead.
     911             :          *
     912             :          * We can run the text through the raw parser though; this will at
     913             :          * least catch silly syntactic errors.
     914             :          */
     915         208 :         raw_parsetree_list = pg_parse_query(prosrc);
     916             : 
     917         207 :         if (!haspolyarg)
     918             :         {
     919             :             /*
     920             :              * OK to do full precheck: analyze and rewrite the queries, then
     921             :              * verify the result type.
     922             :              */
     923             :             SQLFunctionParseInfoPtr pinfo;
     924             : 
     925             :             /* But first, set up parameter information */
     926         173 :             pinfo = prepare_sql_fn_parse_info(tuple, NULL, InvalidOid);
     927             : 
     928         173 :             querytree_list = NIL;
     929         344 :             foreach(lc, raw_parsetree_list)
     930             :             {
     931         173 :                 RawStmt    *parsetree = lfirst_node(RawStmt, lc);
     932             :                 List       *querytree_sublist;
     933             : 
     934         173 :                 querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
     935             :                                                                   prosrc,
     936             :                                                                   (ParserSetupHook) sql_fn_parser_setup,
     937             :                                                                   pinfo,
     938             :                                                                   NULL);
     939         171 :                 querytree_list = list_concat(querytree_list,
     940             :                                              querytree_sublist);
     941             :             }
     942             : 
     943         171 :             (void) check_sql_fn_retval(funcoid, proc->prorettype,
     944             :                                        querytree_list,
     945             :                                        NULL, NULL);
     946             :         }
     947             : 
     948         203 :         error_context_stack = sqlerrcontext.previous;
     949             :     }
     950             : 
     951         204 :     ReleaseSysCache(tuple);
     952             : 
     953         204 :     PG_RETURN_VOID();
     954             : }
     955             : 
     956             : /*
     957             :  * Error context callback for handling errors in SQL function definitions
     958             :  */
     959             : static void
     960           5 : sql_function_parse_error_callback(void *arg)
     961             : {
     962           5 :     parse_error_callback_arg *callback_arg = (parse_error_callback_arg *) arg;
     963             : 
     964             :     /* See if it's a syntax error; if so, transpose to CREATE FUNCTION */
     965           5 :     if (!function_parse_error_transpose(callback_arg->prosrc))
     966             :     {
     967             :         /* If it's not a syntax error, push info onto context stack */
     968           2 :         errcontext("SQL function \"%s\"", callback_arg->proname);
     969             :     }
     970           5 : }
     971             : 
     972             : /*
     973             :  * Adjust a syntax error occurring inside the function body of a CREATE
     974             :  * FUNCTION or DO command.  This can be used by any function validator or
     975             :  * anonymous-block handler, not only for SQL-language functions.
     976             :  * It is assumed that the syntax error position is initially relative to the
     977             :  * function body string (as passed in).  If possible, we adjust the position
     978             :  * to reference the original command text; if we can't manage that, we set
     979             :  * up an "internal query" syntax error instead.
     980             :  *
     981             :  * Returns true if a syntax error was processed, false if not.
     982             :  */
     983             : bool
     984          39 : function_parse_error_transpose(const char *prosrc)
     985             : {
     986             :     int         origerrposition;
     987             :     int         newerrposition;
     988             :     const char *queryText;
     989             : 
     990             :     /*
     991             :      * Nothing to do unless we are dealing with a syntax error that has a
     992             :      * cursor position.
     993             :      *
     994             :      * Some PLs may prefer to report the error position as an internal error
     995             :      * to begin with, so check that too.
     996             :      */
     997          39 :     origerrposition = geterrposition();
     998          39 :     if (origerrposition <= 0)
     999             :     {
    1000          34 :         origerrposition = getinternalerrposition();
    1001          34 :         if (origerrposition <= 0)
    1002           5 :             return false;
    1003             :     }
    1004             : 
    1005             :     /* We can get the original query text from the active portal (hack...) */
    1006          34 :     Assert(ActivePortal && ActivePortal->status == PORTAL_ACTIVE);
    1007          34 :     queryText = ActivePortal->sourceText;
    1008             : 
    1009             :     /* Try to locate the prosrc in the original text */
    1010          34 :     newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition);
    1011             : 
    1012          34 :     if (newerrposition > 0)
    1013             :     {
    1014             :         /* Successful, so fix error position to reference original query */
    1015          34 :         errposition(newerrposition);
    1016             :         /* Get rid of any report of the error as an "internal query" */
    1017          34 :         internalerrposition(0);
    1018          34 :         internalerrquery(NULL);
    1019             :     }
    1020             :     else
    1021             :     {
    1022             :         /*
    1023             :          * If unsuccessful, convert the position to an internal position
    1024             :          * marker and give the function text as the internal query.
    1025             :          */
    1026           0 :         errposition(0);
    1027           0 :         internalerrposition(origerrposition);
    1028           0 :         internalerrquery(prosrc);
    1029             :     }
    1030             : 
    1031          34 :     return true;
    1032             : }
    1033             : 
    1034             : /*
    1035             :  * Try to locate the string literal containing the function body in the
    1036             :  * given text of the CREATE FUNCTION or DO command.  If successful, return
    1037             :  * the character (not byte) index within the command corresponding to the
    1038             :  * given character index within the literal.  If not successful, return 0.
    1039             :  */
    1040             : static int
    1041          34 : match_prosrc_to_query(const char *prosrc, const char *queryText,
    1042             :                       int cursorpos)
    1043             : {
    1044             :     /*
    1045             :      * Rather than fully parsing the original command, we just scan the
    1046             :      * command looking for $prosrc$ or 'prosrc'.  This could be fooled (though
    1047             :      * not in any very probable scenarios), so fail if we find more than one
    1048             :      * match.
    1049             :      */
    1050          34 :     int         prosrclen = strlen(prosrc);
    1051          34 :     int         querylen = strlen(queryText);
    1052          34 :     int         matchpos = 0;
    1053             :     int         curpos;
    1054             :     int         newcursorpos;
    1055             : 
    1056        2583 :     for (curpos = 0; curpos < querylen - prosrclen; curpos++)
    1057             :     {
    1058        2613 :         if (queryText[curpos] == '$' &&
    1059          96 :             strncmp(prosrc, &queryText[curpos + 1], prosrclen) == 0 &&
    1060          32 :             queryText[curpos + 1 + prosrclen] == '$')
    1061             :         {
    1062             :             /*
    1063             :              * Found a $foo$ match.  Since there are no embedded quoting
    1064             :              * characters in a dollar-quoted literal, we don't have to do any
    1065             :              * fancy arithmetic; just offset by the starting position.
    1066             :              */
    1067          32 :             if (matchpos)
    1068           0 :                 return 0;       /* multiple matches, fail */
    1069          32 :             matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
    1070             :                 + cursorpos;
    1071             :         }
    1072        2519 :         else if (queryText[curpos] == '\'' &&
    1073           2 :                  match_prosrc_to_literal(prosrc, &queryText[curpos + 1],
    1074             :                                          cursorpos, &newcursorpos))
    1075             :         {
    1076             :             /*
    1077             :              * Found a 'foo' match.  match_prosrc_to_literal() has adjusted
    1078             :              * for any quotes or backslashes embedded in the literal.
    1079             :              */
    1080           2 :             if (matchpos)
    1081           0 :                 return 0;       /* multiple matches, fail */
    1082           4 :             matchpos = pg_mbstrlen_with_len(queryText, curpos + 1)
    1083           2 :                 + newcursorpos;
    1084             :         }
    1085             :     }
    1086             : 
    1087          34 :     return matchpos;
    1088             : }
    1089             : 
    1090             : /*
    1091             :  * Try to match the given source text to a single-quoted literal.
    1092             :  * If successful, adjust newcursorpos to correspond to the character
    1093             :  * (not byte) index corresponding to cursorpos in the source text.
    1094             :  *
    1095             :  * At entry, literal points just past a ' character.  We must check for the
    1096             :  * trailing quote.
    1097             :  */
    1098             : static bool
    1099           2 : match_prosrc_to_literal(const char *prosrc, const char *literal,
    1100             :                         int cursorpos, int *newcursorpos)
    1101             : {
    1102           2 :     int         newcp = cursorpos;
    1103             :     int         chlen;
    1104             : 
    1105             :     /*
    1106             :      * This implementation handles backslashes and doubled quotes in the
    1107             :      * string literal.  It does not handle the SQL syntax for literals
    1108             :      * continued across line boundaries.
    1109             :      *
    1110             :      * We do the comparison a character at a time, not a byte at a time, so
    1111             :      * that we can do the correct cursorpos math.
    1112             :      */
    1113          26 :     while (*prosrc)
    1114             :     {
    1115          22 :         cursorpos--;            /* characters left before cursor */
    1116             : 
    1117             :         /*
    1118             :          * Check for backslashes and doubled quotes in the literal; adjust
    1119             :          * newcp when one is found before the cursor.
    1120             :          */
    1121          22 :         if (*literal == '\\')
    1122             :         {
    1123           0 :             literal++;
    1124           0 :             if (cursorpos > 0)
    1125           0 :                 newcp++;
    1126             :         }
    1127          22 :         else if (*literal == '\'')
    1128             :         {
    1129           0 :             if (literal[1] != '\'')
    1130           0 :                 goto fail;
    1131           0 :             literal++;
    1132           0 :             if (cursorpos > 0)
    1133           0 :                 newcp++;
    1134             :         }
    1135          22 :         chlen = pg_mblen(prosrc);
    1136          22 :         if (strncmp(prosrc, literal, chlen) != 0)
    1137           0 :             goto fail;
    1138          22 :         prosrc += chlen;
    1139          22 :         literal += chlen;
    1140             :     }
    1141             : 
    1142           2 :     if (*literal == '\'' && literal[1] != '\'')
    1143             :     {
    1144             :         /* success */
    1145           2 :         *newcursorpos = newcp;
    1146           2 :         return true;
    1147             :     }
    1148             : 
    1149             : fail:
    1150             :     /* Must set *newcursorpos to suppress compiler warning */
    1151           0 :     *newcursorpos = newcp;
    1152           0 :     return false;
    1153             : }
    1154             : 
    1155             : List *
    1156           0 : oid_array_to_list(Datum datum)
    1157             : {
    1158           0 :     ArrayType  *array = DatumGetArrayTypeP(datum);
    1159             :     Datum      *values;
    1160             :     int         nelems;
    1161             :     int         i;
    1162           0 :     List       *result = NIL;
    1163             : 
    1164           0 :     deconstruct_array(array,
    1165             :                       OIDOID,
    1166             :                       sizeof(Oid), true, 'i',
    1167             :                       &values, NULL, &nelems);
    1168           0 :     for (i = 0; i < nelems; i++)
    1169           0 :         result = lappend_oid(result, values[i]);
    1170           0 :     return result;
    1171             : }

Generated by: LCOV version 1.11