温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

分析PostgreSQL CreateFunction中的ProcedureCreate函数

发布时间:2021-11-05 15:06:23 来源:亿速云 阅读:206 作者:iii 栏目:关系型数据库

本篇内容介绍了“分析PostgreSQL CreateFunction中的ProcedureCreate函数”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、数据结构

Form_pg_language
plpgsql语言定义结构体

/* ----------------
 *      pg_language definition.  cpp turns this into
 *      typedef struct FormData_pg_language
 * ----------------
 */
CATALOG(pg_language,2612,LanguageRelationId)
{
    Oid         oid;            /* oid */
    /* Language name */
    NameData    lanname;
    /* Language's owner */
    Oid         lanowner BKI_DEFAULT(PGUID);
    /* Is a procedural language */
    bool        lanispl BKI_DEFAULT(f);
    /* PL is trusted */
    bool        lanpltrusted BKI_DEFAULT(f);
    /* Call handler, if it's a PL */
    Oid         lanplcallfoid BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    /* Optional anonymous-block handler function */
    Oid         laninline BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    /* Optional validation function */
    Oid         lanvalidator BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
#ifdef CATALOG_VARLEN           /* variable-length fields start here */
    /* Access privileges */
    aclitem     lanacl[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_language;
/* ----------------
 *      Form_pg_language corresponds to a pointer to a tuple with
 *      the format of pg_language relation.
 * ----------------
 */
typedef FormData_pg_language *Form_pg_language;

ArrayType

/*
 * Arrays are varlena objects, so must meet the varlena convention that
 * the first int32 of the object contains the total object size in bytes.
 * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
 * Arrays是可变对象集,必须符合varlena约定,即对象的第一个int32包含对象的总大小(以字节为单位)。
 * 但是,一定要确保使用VARSIZE和SET_VARSIZE函数范围该结构体
 *
 * CAUTION: if you change the header for ordinary arrays you will also
 * need to change the headers for oidvector and int2vector!
 */
typedef struct
{
    //可变的header
    int32       vl_len_;        /* varlena header (do not touch directly!) */
    //维度
    int         ndim;           /* # of dimensions */
    //指向数据的偏移量,如为0则表示没有位图
    int32       dataoffset;     /* offset to data, or 0 if no bitmap */
    //元素类型的OID
    Oid         elemtype;       /* element type OID */
} ArrayType;

DefElem

typedef struct DefElem
{
  NodeTag   type;
  char     *defnamespace; /* NULL if unqualified name */
  char     *defname;
  Node     *arg;      /* a (Value *) or a (TypeName *) */
  DefElemAction defaction;  /* unspecified action, or SET/ADD/DROP */
  int     location;   /* token location, or -1 if unknown */
} DefElem;

FunctionParameter

typedef enum FunctionParameterMode
{
  /* the assigned enum values appear in pg_proc, don't change 'em! */
  FUNC_PARAM_IN = 'i',    /* input only */
  FUNC_PARAM_OUT = 'o',   /* output only */
  FUNC_PARAM_INOUT = 'b',   /* both */
  FUNC_PARAM_VARIADIC = 'v',  /* variadic (always input) */
  FUNC_PARAM_TABLE = 't'    /* table function output column */
} FunctionParameterMode;
typedef struct FunctionParameter
{
  NodeTag   type;
  char     *name;     /* parameter name, or NULL if not given */
  TypeName   *argType;    /* TypeName for parameter type */
  FunctionParameterMode mode; /* IN/OUT/etc */
  Node     *defexpr;    /* raw default expr, or NULL if not given */
} FunctionParameter;

二、源码解读

/* ----------------------------------------------------------------
 *    ProcedureCreate
 *
 * Note: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig
 * are either arrays of the proper types or NULL.  We declare them Datum,
 * not "ArrayType *", to avoid importing array.h into pg_proc.h.
 * 注意: allParameterTypes, parameterModes, parameterNames, trftypes, and proconfig
 *      要么为相应类型的数组,要么为NULL.这些变量的类型为Datum而不是ArrayType *,
 *      目的是为了避免引入array.h到pg_proc.h中
 * ----------------------------------------------------------------
 */
ObjectAddress
ProcedureCreate(const char *procedureName,
        Oid procNamespace,
        bool replace,
        bool returnsSet,
        Oid returnType,
        Oid proowner,
        Oid languageObjectId,
        Oid languageValidator,
        const char *prosrc,
        const char *probin,
        char prokind,
        bool security_definer,
        bool isLeakProof,
        bool isStrict,
        char volatility,
        char parallel,
        oidvector *parameterTypes,
        Datum allParameterTypes,
        Datum parameterModes,
        Datum parameterNames,
        List *parameterDefaults,
        Datum trftypes,
        Datum proconfig,
        Oid prosupport,
        float4 procost,
        float4 prorows)
{
  Oid     retval;//返回值类型
  int     parameterCount;//输入参数
  int     allParamCount;//所有参数,如无输出参数,则为0
  Oid      *allParams;//所有参数类型,如无输出参数,则为NULL
  char     *paramModes = NULL;//参数类型
  bool    genericInParam = false;
  bool    genericOutParam = false;
  bool    anyrangeInParam = false;
  bool    anyrangeOutParam = false;
  bool    internalInParam = false;
  bool    internalOutParam = false;
  Oid     variadicType = InvalidOid;
  Acl      *proacl = NULL;//ACL结构体
  Relation  rel;//关系
  HeapTuple tup;//tuple
  HeapTuple oldtup;//原pg_proc tuple
  bool    nulls[Natts_pg_proc];//是否为null
  Datum   values[Natts_pg_proc];//值
  bool    replaces[Natts_pg_proc];//是否替换
  NameData  procname;//名称
  TupleDesc tupDesc;//tuple描述符
  bool    is_update;//是否替换?
  ObjectAddress myself,
        referenced;
  int     i;//临时变量
  Oid     trfid;
  /*
   * sanity checks
   */
  Assert(PointerIsValid(prosrc));
  //参数个数
  parameterCount = parameterTypes->dim1;
  //#define FUNC_MAX_ARGS   100
  if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
    ereport(ERROR,
        (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
         errmsg_plural("functions cannot have more than %d argument",
                 "functions cannot have more than %d arguments",
                 FUNC_MAX_ARGS,
                 FUNC_MAX_ARGS)));
  /* note: the above is correct, we do NOT count output arguments(不计算输出参数) */
  /* Deconstruct array inputs */
  //重构数组输入:所有参数类型
  if (allParameterTypes != PointerGetDatum(NULL))
  {
    /*
     * We expect the array to be a 1-D OID array; verify that. We don't
     * need to use deconstruct_array() since the array data is just going
     * to look like a C array of OID values.
     * 我们期望数组是一维OID数组,需要验证这一点.
     * 因为数组中的数据看起来像是C语言中的OID数组,隐藏不需要使用deconstruct_array()函数
     */
    ArrayType  *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes);
    //获取数组的维数
    //#define ARR_DIMS(a) \
    ((int *) (((char *) (a)) + sizeof(ArrayType)))
    //#define ARR_NDIM(a)       ((a)->ndim)
    //#define ARR_HASNULL(a)      ((a)->dataoffset != 0)
    allParamCount = ARR_DIMS(allParamArray)[0];
    if (ARR_NDIM(allParamArray) != 1 ||
      allParamCount <= 0 ||
      ARR_HASNULL(allParamArray) ||
      ARR_ELEMTYPE(allParamArray) != OIDOID)
      elog(ERROR, "allParameterTypes is not a 1-D Oid array");
    //所有参数
    allParams = (Oid *) ARR_DATA_PTR(allParamArray);
    Assert(allParamCount >= parameterCount);
    /* we assume caller got the contents right */
  }
  else
  {
    //均为输入参数,无输出参数
    allParamCount = parameterCount;
    allParams = parameterTypes->values;
  }
  if (parameterModes != PointerGetDatum(NULL))
  {
    //参数模式(输入/输出等)
    /*
     * We expect the array to be a 1-D CHAR array; verify that. We don't
     * need to use deconstruct_array() since the array data is just going
     * to look like a C array of char values.
     */
    ArrayType  *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
    if (ARR_NDIM(modesArray) != 1 ||
      ARR_DIMS(modesArray)[0] != allParamCount ||
      ARR_HASNULL(modesArray) ||
      ARR_ELEMTYPE(modesArray) != CHAROID)
      elog(ERROR, "parameterModes is not a 1-D char array");
    paramModes = (char *) ARR_DATA_PTR(modesArray);
  }
  /*
   * Detect whether we have polymorphic or INTERNAL arguments.  The first
   * loop checks input arguments, the second output arguments.
   * 检查是否存在多态或者INTERNAL参数.
   * 两趟循环:第一趟检查输入参数,第二趟检查输出参数
   */
  for (i = 0; i < parameterCount; i++)
  {
    switch (parameterTypes->values[i])
    {
      case ANYARRAYOID:
      case ANYELEMENTOID:
      case ANYNONARRAYOID:
      case ANYENUMOID:
        genericInParam = true;
        break;
      case ANYRANGEOID:
        genericInParam = true;
        anyrangeInParam = true;
        break;
      case INTERNALOID:
        internalInParam = true;
        break;
    }
  }
  if (allParameterTypes != PointerGetDatum(NULL))
  {
    for (i = 0; i < allParamCount; i++)
    {
      if (paramModes == NULL ||
        paramModes[i] == PROARGMODE_IN ||
        paramModes[i] == PROARGMODE_VARIADIC)
        continue;   /* ignore input-only params */
      switch (allParams[i])
      {
        case ANYARRAYOID:
        case ANYELEMENTOID:
        case ANYNONARRAYOID:
        case ANYENUMOID:
          genericOutParam = true;
          break;
        case ANYRANGEOID:
          genericOutParam = true;
          anyrangeOutParam = true;
          break;
        case INTERNALOID:
          internalOutParam = true;
          break;
      }
    }
  }
  /*
   * Do not allow polymorphic return type unless at least one input argument
   * is polymorphic.  ANYRANGE return type is even stricter: must have an
   * ANYRANGE input (since we can't deduce the specific range type from
   * ANYELEMENT).  Also, do not allow return type INTERNAL unless at least
   * one input argument is INTERNAL.
   * 至少存在一个多态输入参数的情况下才允许返回多态类型.
   * ANYRANGE返回类型更为严格:必须含有一个ANYRANGE输入(因为无法从ANYELEMENT中规约特殊的范围类型.)
   * 同时,除非至少有一个INTERNAL输入参数类型,否则不允许返回INTERNAL类型.
   */
  if ((IsPolymorphicType(returnType) || genericOutParam)
    && !genericInParam)
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("cannot determine result data type"),
         errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
  if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
    !anyrangeInParam)
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("cannot determine result data type"),
         errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
  if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("unsafe use of pseudo-type \"internal\""),
         errdetail("A function returning \"internal\" must have at least one \"internal\" argument.")));
  if (paramModes != NULL)
  {
    /*
     * Only the last input parameter can be variadic; if it is, save its
     * element type.  Errors here are just elog since caller should have
     * checked this already.
     * 只有最后一个输入参数可以是variadic.如是,则存储元素类型.
     */
    for (i = 0; i < allParamCount; i++)
    {
      switch (paramModes[i])
      {
        case PROARGMODE_IN:
        case PROARGMODE_INOUT:
          if (OidIsValid(variadicType))
            elog(ERROR, "variadic parameter must be last");
          break;
        case PROARGMODE_OUT:
        case PROARGMODE_TABLE:
          /* okay */
          break;
        case PROARGMODE_VARIADIC:
          if (OidIsValid(variadicType))
            elog(ERROR, "variadic parameter must be last");
          switch (allParams[i])
          {
            case ANYOID:
              variadicType = ANYOID;
              break;
            case ANYARRAYOID:
              variadicType = ANYELEMENTOID;
              break;
            default:
              variadicType = get_element_type(allParams[i]);
              if (!OidIsValid(variadicType))
                elog(ERROR, "variadic parameter is not an array");
              break;
          }
          break;
        default:
          elog(ERROR, "invalid parameter mode '%c'", paramModes[i]);
          break;
      }
    }
  }
  /*
   * All seems OK; prepare the data to be inserted into pg_proc.
   * 检查完毕,写入到pg_proc中
   */
  //#define Natts_pg_proc 29
  for (i = 0; i < Natts_pg_proc; ++i)
  {
    nulls[i] = false;
    values[i] = (Datum) 0;
    replaces[i] = true;
  }
  //#define Anum_pg_proc_oid 1
  //#define Anum_pg_proc_proname 2
  //...
  //#define Anum_pg_proc_proacl 29
  namestrcpy(&procname, procedureName);
  values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);
  values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
  values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);
  values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
  values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
  values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
  values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
  values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(prosupport);
  values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);
  values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
  values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
  values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
  values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
  values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
  values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel);
  values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);
  values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));
  values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
  values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
  if (allParameterTypes != PointerGetDatum(NULL))
    values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
  else
    nulls[Anum_pg_proc_proallargtypes - 1] = true;
  if (parameterModes != PointerGetDatum(NULL))
    values[Anum_pg_proc_proargmodes - 1] = parameterModes;
  else
    nulls[Anum_pg_proc_proargmodes - 1] = true;
  if (parameterNames != PointerGetDatum(NULL))
    values[Anum_pg_proc_proargnames - 1] = parameterNames;
  else
    nulls[Anum_pg_proc_proargnames - 1] = true;
  if (parameterDefaults != NIL)
    values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults));
  else
    nulls[Anum_pg_proc_proargdefaults - 1] = true;
  if (trftypes != PointerGetDatum(NULL))
    values[Anum_pg_proc_protrftypes - 1] = trftypes;
  else
    nulls[Anum_pg_proc_protrftypes - 1] = true;
  values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);
  if (probin)
    values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin);
  else
    nulls[Anum_pg_proc_probin - 1] = true;
  if (proconfig != PointerGetDatum(NULL))
    values[Anum_pg_proc_proconfig - 1] = proconfig;
  else
    nulls[Anum_pg_proc_proconfig - 1] = true;
  /* proacl will be determined later */
  rel = table_open(ProcedureRelationId, RowExclusiveLock);
  tupDesc = RelationGetDescr(rel);
  /* Check for pre-existing definition */
  //检查是否已存在
  oldtup = SearchSysCache3(PROCNAMEARGSNSP,
               PointerGetDatum(procedureName),
               PointerGetDatum(parameterTypes),
               ObjectIdGetDatum(procNamespace));
  if (HeapTupleIsValid(oldtup))
  {
    //-------- 已存在
    /* There is one; okay to replace it? */
    //获取原记录(pg_proc记录)
    Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
    Datum   proargnames;
    bool    isnull;
    const char *dropcmd;
    if (!replace)
      ereport(ERROR,
          (errcode(ERRCODE_DUPLICATE_FUNCTION),
           errmsg("function \"%s\" already exists with same argument types",
              procedureName)));
    if (!pg_proc_ownercheck(oldproc->oid, proowner))
      aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
               procedureName);
    /* Not okay to change routine kind */
    //不能改变类型(如原来是proc,那么创建同名func是不行的)
    if (oldproc->prokind != prokind)
      ereport(ERROR,
          (errcode(ERRCODE_WRONG_OBJECT_TYPE),
           errmsg("cannot change routine kind"),
           (oldproc->prokind == PROKIND_AGGREGATE ?
            errdetail("\"%s\" is an aggregate function.", procedureName) :
            oldproc->prokind == PROKIND_FUNCTION ?
            errdetail("\"%s\" is a function.", procedureName) :
            oldproc->prokind == PROKIND_PROCEDURE ?
            errdetail("\"%s\" is a procedure.", procedureName) :
            oldproc->prokind == PROKIND_WINDOW ?
            errdetail("\"%s\" is a window function.", procedureName) :
            0)));
    dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" :
           prokind == PROKIND_AGGREGATE ? "DROP AGGREGATE" :
           "DROP FUNCTION");
    /*
     * Not okay to change the return type of the existing proc, since
     * existing rules, views, etc may depend on the return type.
     * 改变返回类型也是不行的,因为现存的规则\视图等等可能依赖返回类型.
     *
     * In case of a procedure, a changing return type means that whether
     * the procedure has output parameters was changed.  Since there is no
     * user visible return type, we produce a more specific error message.
     * 如为存储过程,改变返回类型意味着过程已有的输出参数已改变.
     * 由于存在非用户可见返回类型,隐藏产生更为详尽的错误信息.
     */
    if (returnType != oldproc->prorettype ||
      returnsSet != oldproc->proretset)
      ereport(ERROR,
          (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
           prokind == PROKIND_PROCEDURE
           ? errmsg("cannot change whether a procedure has output parameters")
           : errmsg("cannot change return type of existing function"),
      /*
       * translator: first %s is DROP FUNCTION, DROP PROCEDURE, or DROP
       * AGGREGATE
       */
           errhint("Use %s %s first.",
               dropcmd,
               format_procedure(oldproc->oid))));
    /*
     * If it returns RECORD, check for possible change of record type
     * implied by OUT parameters
     * 如果返回RECORD类型,检查使用OUT参数指明的可能的record类型变化.
     */
    if (returnType == RECORDOID)
    {
      //返回RECORD类型
      TupleDesc olddesc;
      TupleDesc newdesc;
      olddesc = build_function_result_tupdesc_t(oldtup);
      newdesc = build_function_result_tupdesc_d(prokind,
                            allParameterTypes,
                            parameterModes,
                            parameterNames);
      if (olddesc == NULL && newdesc == NULL)
         /* ok, both are runtime-defined RECORDs */ ;
      else if (olddesc == NULL || newdesc == NULL ||
           !equalTupleDescs(olddesc, newdesc))
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("cannot change return type of existing function"),
             errdetail("Row type defined by OUT parameters is different."),
        /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
             errhint("Use %s %s first.",
                 dropcmd,
                 format_procedure(oldproc->oid))));
    }
    /*
     * If there were any named input parameters, check to make sure the
     * names have not been changed, as this could break existing calls. We
     * allow adding names to formerly unnamed parameters, though.
     * 如存在已命名的输入参数,确保名称没有变更,否则会破坏现存的调用.
     * 但允许添加名称到未命名的参数中.
     */
    proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
                    Anum_pg_proc_proargnames,
                    &isnull);
    if (!isnull)
    {
      Datum   proargmodes;
      char    **old_arg_names;
      char    **new_arg_names;
      int     n_old_arg_names;
      int     n_new_arg_names;
      int     j;
      proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
                      Anum_pg_proc_proargmodes,
                      &isnull);
      if (isnull)
        proargmodes = PointerGetDatum(NULL);  /* just to be sure */
      n_old_arg_names = get_func_input_arg_names(proargnames,
                             proargmodes,
                             &old_arg_names);
      n_new_arg_names = get_func_input_arg_names(parameterNames,
                             parameterModes,
                             &new_arg_names);
      for (j = 0; j < n_old_arg_names; j++)
      {
        if (old_arg_names[j] == NULL)
          continue;
        if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
          strcmp(old_arg_names[j], new_arg_names[j]) != 0)
          ereport(ERROR,
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
               errmsg("cannot change name of input parameter \"%s\"",
                  old_arg_names[j]),
          /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
               errhint("Use %s %s first.",
                   dropcmd,
                   format_procedure(oldproc->oid))));
      }
    }
    /*
     * If there are existing defaults, check compatibility: redefinition
     * must not remove any defaults nor change their types.  (Removing a
     * default might cause a function to fail to satisfy an existing call.
     * Changing type would only be possible if the associated parameter is
     * polymorphic, and in such cases a change of default type might alter
     * the resolved output type of existing calls.)
     * 存在defaults(默认参数),检查兼容性:
     * 重新定义不应删去已有的默认定义或者改变类型,否则会破坏现存的调用.
     */
    if (oldproc->pronargdefaults != 0)
    {
      //默认值判断
      Datum   proargdefaults;
      List     *oldDefaults;
      ListCell   *oldlc;
      ListCell   *newlc;
      if (list_length(parameterDefaults) < oldproc->pronargdefaults)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("cannot remove parameter defaults from existing function"),
        /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
             errhint("Use %s %s first.",
                 dropcmd,
                 format_procedure(oldproc->oid))));
      proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
                       Anum_pg_proc_proargdefaults,
                       &isnull);
      Assert(!isnull);
      oldDefaults = castNode(List, stringToNode(TextDatumGetCString(proargdefaults)));
      Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
      /* new list can have more defaults than old, advance over 'em */
      newlc = list_head(parameterDefaults);
      for (i = list_length(parameterDefaults) - oldproc->pronargdefaults;
         i > 0;
         i--)
        newlc = lnext(newlc);
      foreach(oldlc, oldDefaults)
      {
        Node     *oldDef = (Node *) lfirst(oldlc);
        Node     *newDef = (Node *) lfirst(newlc);
        if (exprType(oldDef) != exprType(newDef))
          ereport(ERROR,
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
               errmsg("cannot change data type of existing parameter default value"),
          /* translator: first %s is DROP FUNCTION or DROP PROCEDURE */
               errhint("Use %s %s first.",
                   dropcmd,
                   format_procedure(oldproc->oid))));
        newlc = lnext(newlc);
      }
    }
    /*
     * Do not change existing oid, ownership or permissions, either.  Note
     * dependency-update code below has to agree with this decision.
     * 不改变现有的oid,宿主或者权限等.
     * 注意下面的依赖更新代码必须遵循这一约定.
     */
    replaces[Anum_pg_proc_oid - 1] = false;
    replaces[Anum_pg_proc_proowner - 1] = false;
    replaces[Anum_pg_proc_proacl - 1] = false;
    /* Okay, do it... */
    tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
    CatalogTupleUpdate(rel, &tup->t_self, tup);
    ReleaseSysCache(oldtup);
    is_update = true;
  }
  else
  {
    //-------------- 不存在
    /* Creating a new procedure */
    //创建新的过程
    Oid     newOid;
    /* First, get default permissions and set up proacl */
    //首先:获取默认的权限并设置proacl
    proacl = get_user_default_acl(OBJECT_FUNCTION, proowner,
                    procNamespace);
    if (proacl != NULL)
      values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
    else
      nulls[Anum_pg_proc_proacl - 1] = true;
    //获取新的OID
    newOid = GetNewOidWithIndex(rel, ProcedureOidIndexId,
                  Anum_pg_proc_oid);
    //设置pg_proc中的OID
    values[Anum_pg_proc_oid - 1] = ObjectIdGetDatum(newOid);
    //构造tuple
    tup = heap_form_tuple(tupDesc, values, nulls);
    //执行插入操作
    CatalogTupleInsert(rel, tup);
    is_update = false;
  }
  //获取pg_proc结构体
  retval = ((Form_pg_proc) GETSTRUCT(tup))->oid;
  /*
   * Create dependencies for the new function.  If we are updating an
   * existing function, first delete any existing pg_depend entries.
   * (However, since we are not changing ownership or permissions, the
   * shared dependencies do *not* need to change, and we leave them alone.)
   * 创建新函数的依赖.
   * 如正在更新现存的函数,首先删除现存的pg_depend条目.
   */
  if (is_update)
    //删除依赖
    deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
  myself.classId = ProcedureRelationId;
  myself.objectId = retval;
  myself.objectSubId = 0;
  /* dependency on namespace */
  //依赖:namespace
  referenced.classId = NamespaceRelationId;
  referenced.objectId = procNamespace;
  referenced.objectSubId = 0;
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  /* dependency on implementation language */
  //依赖:语言
  referenced.classId = LanguageRelationId;
  referenced.objectId = languageObjectId;
  referenced.objectSubId = 0;
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  /* dependency on return type */
  //依赖:返回类型
  referenced.classId = TypeRelationId;
  referenced.objectId = returnType;
  referenced.objectSubId = 0;
  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  /* dependency on transform used by return type, if any */
  //依赖:返回类型的转换规则
  if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
  {
    referenced.classId = TransformRelationId;
    referenced.objectId = trfid;
    referenced.objectSubId = 0;
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  }
  /* dependency on parameter types */
  //依赖:参数类型
  for (i = 0; i < allParamCount; i++)
  {
    referenced.classId = TypeRelationId;
    referenced.objectId = allParams[i];
    referenced.objectSubId = 0;
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    /* dependency on transform used by parameter type, if any */
    if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
    {
      referenced.classId = TransformRelationId;
      referenced.objectId = trfid;
      referenced.objectSubId = 0;
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    }
  }
  /* dependency on parameter default expressions */
  //依赖:默认表达式
  if (parameterDefaults)
    recordDependencyOnExpr(&myself, (Node *) parameterDefaults,
                 NIL, DEPENDENCY_NORMAL);
  /* dependency on support function, if any */
  //依赖:支持的函数
  if (OidIsValid(prosupport))
  {
    referenced.classId = ProcedureRelationId;
    referenced.objectId = prosupport;
    referenced.objectSubId = 0;
    recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  }
  /* dependency on owner */
  //依赖:owner
  if (!is_update)
    recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
  /* dependency on any roles mentioned in ACL */
  //依赖:ACL中标明的角色
  if (!is_update)
    recordDependencyOnNewAcl(ProcedureRelationId, retval, 0,
                 proowner, proacl);
  /* dependency on extension */
  //依赖:扩展
  recordDependencyOnCurrentExtension(&myself, is_update);
  heap_freetuple(tup);
  /* Post creation hook for new function */
  //调用对象创建后的钩子函数
  InvokeObjectPostCreateHook(ProcedureRelationId, retval, 0);
  //关闭pg_proc
  table_close(rel, RowExclusiveLock);
  /* Verify function body */
  //验证函数body
  if (OidIsValid(languageValidator))
  {
    ArrayType  *set_items = NULL;
    int     save_nestlevel = 0;
    /* Advance command counter so new tuple can be seen by validator */
    //增加命令计数
    CommandCounterIncrement();
    /*
     * Set per-function configuration parameters so that the validation is
     * done with the environment the function expects.  However, if
     * check_function_bodies is off, we don't do this, because that would
     * create dump ordering hazards that pg_dump doesn't know how to deal
     * with.  (For example, a SET clause might refer to a not-yet-created
     * text search configuration.)  This means that the validator
     * shouldn't complain about anything that might depend on a GUC
     * parameter when check_function_bodies is off.
     */
    if (check_function_bodies)
    {
      //检查函数体
      //获取函数设定的参数
      set_items = (ArrayType *) DatumGetPointer(proconfig);
      if (set_items)    /* Need a new GUC nesting level */
      {
        save_nestlevel = NewGUCNestLevel();
        ProcessGUCArray(set_items,
                (superuser() ? PGC_SUSET : PGC_USERSET),
                PGC_S_SESSION,
                GUC_ACTION_SAVE);
      }
    }
    //调用语言校验器
    OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
    if (set_items)
      AtEOXact_GUC(true, save_nestlevel);
  }
  return myself;
}

三、跟踪分析

测试脚本

create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)
returns record 
as
$$
declare
begin
  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;
  pio_v3 := 'pio_v3 i/o';
  po_v4 := 100;
  po_v5 := 'po_v5 out';
end;
$$ LANGUAGE plpgsql;

启动GDB跟踪

(gdb) b ProcedureCreate
Breakpoint 1 at 0x5bd665: file pg_proc.c, line 99.
(gdb) c
Continuing.
Breakpoint 1, ProcedureCreate (procedureName=0x1173ab0 "func_test", procNamespace=2200, 
    replace=true, returnsSet=false, returnType=2249, proowner=10, languageObjectId=13581, 
    languageValidator=13580, 
    prosrc=0x11745c8 "\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;\n  pio_v3 := 'pio_v3 i/o';\n  po_v4 := 100;\n  po_v5 := 'po_v5 out';\nend;\n", 
    probin=0x0, prokind=102 'f', security_definer=false, isLeakProof=false, isStrict=false, 
    volatility=118 'v', parallel=117 'u', parameterTypes=0x119a3d0, 
    allParameterTypes=18458616, parameterModes=18457432, parameterNames=18456792, 
    parameterDefaults=0x0, trftypes=0, proconfig=0, prosupport=0, procost=100, prorows=0)
    at pg_proc.c:99
99    char     *paramModes = NULL;
(gdb)

输入参数

[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_type
                   Table "pg_catalog.pg_type"
     Column     |     Type     | Collation | Nullable | Default 
----------------+--------------+-----------+----------+---------
 oid            | oid          |           | not null | 
 typname        | name         |           | not null | 
 typnamespace   | oid          |           | not null | 
 typowner       | oid          |           | not null | 
 typlen         | smallint     |           | not null | 
 typbyval       | boolean      |           | not null | 
 typtype        | "char"       |           | not null | 
 typcategory    | "char"       |           | not null | 
 typispreferred | boolean      |           | not null | 
 typisdefined   | boolean      |           | not null | 
 typdelim       | "char"       |           | not null | 
 typrelid       | oid          |           | not null | 
 typelem        | oid          |           | not null | 
 typarray       | oid          |           | not null | 
 typinput       | regproc      |           | not null | 
 typoutput      | regproc      |           | not null | 
 typreceive     | regproc      |           | not null | 
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,typname from pg_type where oid=2249;
 oid  | typname 
------+---------
 2249 | record
(1 row)
[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_namespace
            Table "pg_catalog.pg_namespace"
  Column  |   Type    | Collation | Nullable | Default 
----------+-----------+-----------+----------+---------
 oid      | oid       |           | not null | 
 nspname  | name      |           | not null | 
 nspowner | oid       |           | not null | 
 nspacl   | aclitem[] |           |          | 
Indexes:
    "pg_namespace_nspname_index" UNIQUE, btree (nspname)
    "pg_namespace_oid_index" UNIQUE, btree (oid)
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,nspname from pg_namespace where oid=2200;
 oid  | nspname 
------+---------
 2200 | public
(1 row)
[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_user
                        View "pg_catalog.pg_user"
    Column    |           Type           | Collation | Nullable | Default 
--------------+--------------------------+-----------+----------+---------
 usename      | name                     |           |          | 
 usesysid     | oid                      |           |          | 
 usecreatedb  | boolean                  |           |          | 
 usesuper     | boolean                  |           |          | 
 userepl      | boolean                  |           |          | 
 usebypassrls | boolean                  |           |          | 
 passwd       | text                     |           |          | 
 valuntil     | timestamp with time zone |           |          | 
 useconfig    | text[]                   | C         |          | 
[local:/data/run/pg12]:5120 pg12@testdb=# select usename,usesysid from pg_user where usesysid=10;
 usename | usesysid 
---------+----------
 pg12    |       10
(1 row)
[local:/data/run/pg12]:5120 pg12@testdb=# \d pg_language
               Table "pg_catalog.pg_language"
    Column     |   Type    | Collation | Nullable | Default 
---------------+-----------+-----------+----------+---------
 oid           | oid       |           | not null | 
 lanname       | name      |           | not null | 
 lanowner      | oid       |           | not null | 
 lanispl       | boolean   |           | not null | 
 lanpltrusted  | boolean   |           | not null | 
 lanplcallfoid | oid       |           | not null | 
 laninline     | oid       |           | not null | 
 lanvalidator  | oid       |           | not null | 
 lanacl        | aclitem[] |           |          | 
Indexes:
    "pg_language_name_index" UNIQUE, btree (lanname)
    "pg_language_oid_index" UNIQUE, btree (oid)
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,lanname,lanowner from pg_language where oid=13581;
  oid  | lanname | lanowner 
-------+---------+----------
 13581 | plpgsql |       10
(1 row)

初始化本地临时变量

99    char     *paramModes = NULL;
(gdb) n
100   bool    genericInParam = false;
(gdb) 
101   bool    genericOutParam = false;
(gdb) 
102   bool    anyrangeInParam = false;
(gdb) 
103   bool    anyrangeOutParam = false;
(gdb) 
104   bool    internalInParam = false;
(gdb) 
105   bool    internalOutParam = false;
(gdb) 
106   Oid     variadicType = InvalidOid;
(gdb) 
107   Acl      *proacl = NULL;
(gdb) 
125   Assert(PointerIsValid(prosrc));
(gdb) 
127   parameterCount = parameterTypes->dim1;
(gdb)

获取参数个数(3个输入参数,类型为26-oid,数据类型为int4,varchar,varchar)

(gdb) n
128   if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS)
(gdb) p parameterCount
$1 = 3
(gdb) p *parameterTypes
$2 = {vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0, 
  values = 0x119a3e8}
(gdb) 
(gdb) p *parameterTypes->values
$3 = 23
(gdb) p parameterTypes->values[1]
$4 = 1043
(gdb) p parameterTypes->values[2]
$5 = 1043
(gdb) 
###
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,typname from pg_type where oid=26;
 oid | typname 
-----+---------
  26 | oid
(1 row)
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,typname from pg_type where oid in (23,1043);
 oid  | typname 
------+---------
   23 | int4
 1043 | varchar
(2 rows)

重构数组输入:所有参数类型

(gdb) n
138   if (allParameterTypes != PointerGetDatum(NULL))
(gdb) 
145     ArrayType  *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes);
(gdb) 
147     allParamCount = ARR_DIMS(allParamArray)[0];
(gdb) 
148     if (ARR_NDIM(allParamArray) != 1 ||
(gdb) 
150       ARR_HASNULL(allParamArray) ||
(gdb) 
149       allParamCount <= 0 ||
(gdb) 
151       ARR_ELEMTYPE(allParamArray) != OIDOID)
(gdb) 
150       ARR_HASNULL(allParamArray) ||
(gdb) 
153     allParams = (Oid *) ARR_DATA_PTR(allParamArray);
(gdb) 
154     Assert(allParamCount >= parameterCount);
(gdb) p *allParamArray
$6 = {vl_len_ = 176, ndim = 1, dataoffset = 0, elemtype = 26}
(gdb) p allParamArray[1]
$7 = {vl_len_ = 5, ndim = 1, dataoffset = 23, elemtype = 1043}
(gdb) p allParamCount
$8 = 5
(gdb) p *ARR_DIMS(allParamArray)
$9 = 5
(gdb) p allParamArray[2]
$10 = {vl_len_ = 1043, ndim = 23, dataoffset = 1043, elemtype = 2139062142}
(gdb) p allParamArray[3]
$11 = {vl_len_ = 2139062143, ndim = 2139062143, dataoffset = 2139062143, 
  elemtype = 2139062143}
(gdb) n
163   if (parameterModes != PointerGetDatum(NULL))
(gdb)

处理参数模式:参数模式(输入/输出等),分别是i,i,b,o,o

(gdb) n
170     ArrayType  *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
(gdb) 
172     if (ARR_NDIM(modesArray) != 1 ||
(gdb) 
173       ARR_DIMS(modesArray)[0] != allParamCount ||
(gdb) 
172     if (ARR_NDIM(modesArray) != 1 ||
(gdb) 
174       ARR_HASNULL(modesArray) ||
(gdb) 
173       ARR_DIMS(modesArray)[0] != allParamCount ||
(gdb) 
175       ARR_ELEMTYPE(modesArray) != CHAROID)
(gdb) 
174       ARR_HASNULL(modesArray) ||
(gdb) 
177     paramModes = (char *) ARR_DATA_PTR(modesArray);
(gdb) 
184   for (i = 0; i < parameterCount; i++)
(gdb) p paramModes[0]
$12 = 105 'i'
(gdb) p paramModes[1]
$13 = 105 'i'
(gdb) p paramModes[2]
$14 = 98 'b'
(gdb) p paramModes[3]
$15 = 111 'o'
(gdb) p paramModes[4]
$16 = 111 'o'
(gdb)

检查是否存在多态或者INTERNAL参数.
两趟循环:第一趟检查输入参数,第二趟检查输出参数

(gdb) n
186     switch (parameterTypes->values[i])
(gdb) 
184   for (i = 0; i < parameterCount; i++)
(gdb) 
186     switch (parameterTypes->values[i])
(gdb) 
184   for (i = 0; i < parameterCount; i++)
(gdb) 
186     switch (parameterTypes->values[i])
(gdb) 
184   for (i = 0; i < parameterCount; i++)
(gdb) 
204   if (allParameterTypes != PointerGetDatum(NULL))
(gdb) 
206     for (i = 0; i < allParamCount; i++)
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
211         continue;   /* ignore input-only params */
(gdb) 
206     for (i = 0; i < allParamCount; i++)
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
211         continue;   /* ignore input-only params */
(gdb) 
206     for (i = 0; i < allParamCount; i++)
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
210         paramModes[i] == PROARGMODE_VARIADIC)
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
213       switch (allParams[i])
(gdb) 
206     for (i = 0; i < allParamCount; i++)
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
210         paramModes[i] == PROARGMODE_VARIADIC)
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
213       switch (allParams[i])
(gdb) 
206     for (i = 0; i < allParamCount; i++)
(gdb) 
208       if (paramModes == NULL ||
(gdb) p allParamCount
$17 = 5
(gdb) n
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
208       if (paramModes == NULL ||
(gdb) 
210         paramModes[i] == PROARGMODE_VARIADIC)
(gdb) 
209         paramModes[i] == PROARGMODE_IN ||
(gdb) 
213       switch (allParams[i])
(gdb) 
206     for (i = 0; i < allParamCount; i++)
(gdb) 
239   if ((IsPolymorphicType(returnType) || genericOutParam)
(gdb)

至少存在一个多态输入参数的情况下才允许返回多态类型.
ANYRANGE返回类型更为严格:必须含有一个ANYRANGE输入(因为无法从ANYELEMENT中规约特殊的范围类型.)
同时,除非至少有一个INTERNAL输入参数类型,否则不允许返回INTERNAL类型.

(gdb) n
246   if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
(gdb) 
253   if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
(gdb) 
259   if (paramModes != NULL)
(gdb)

只有最后一个输入参数可以是variadic.如是,则存储元素类型.

266     for (i = 0; i < allParamCount; i++)
(gdb) n
268       switch (paramModes[i])
(gdb) 
272           if (OidIsValid(variadicType))
(gdb) 
274           break;
(gdb) 
266     for (i = 0; i < allParamCount; i++)
(gdb) 
268       switch (paramModes[i])
(gdb) 
272           if (OidIsValid(variadicType))
(gdb) 
274           break;
(gdb) 
266     for (i = 0; i < allParamCount; i++)
(gdb) 
268       switch (paramModes[i])
(gdb) 
272           if (OidIsValid(variadicType))
(gdb) 
274           break;
(gdb) 
266     for (i = 0; i < allParamCount; i++)
(gdb) 
268       switch (paramModes[i])
(gdb) 
278           break;
(gdb) 
266     for (i = 0; i < allParamCount; i++)
(gdb) 
268       switch (paramModes[i])
(gdb) 
278           break;
(gdb) 
266     for (i = 0; i < allParamCount; i++)
(gdb) 
308   for (i = 0; i < Natts_pg_proc; ++i)
(gdb)

检查完毕,写入到pg_proc中,初始化values

308   for (i = 0; i < Natts_pg_proc; ++i)
(gdb) 
310     nulls[i] = false;
(gdb) 
311     values[i] = (Datum) 0;
(gdb) 
312     replaces[i] = true;
(gdb) 
...

赋值

308   for (i = 0; i < Natts_pg_proc; ++i)
(gdb) 
315   namestrcpy(&procname, procedureName);
(gdb) 
316   values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname);
(gdb) p procedureName
$20 = 0x1173ab0 "func_test"
(gdb) n
317   values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace);
(gdb) 
318   values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner);
(gdb) 
319   values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId);
(gdb) 
320   values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
(gdb) 
321   values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
(gdb) 
322   values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
(gdb) 
323   values[Anum_pg_proc_prosupport - 1] = ObjectIdGetDatum(prosupport);
(gdb) 
324   values[Anum_pg_proc_prokind - 1] = CharGetDatum(prokind);
(gdb) 
325   values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
(gdb) 
326   values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof);
(gdb) 
327   values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict);
(gdb) 
328   values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet);
(gdb) 
329   values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility);
(gdb) 
330   values[Anum_pg_proc_proparallel - 1] = CharGetDatum(parallel);
(gdb) 
331   values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount);
(gdb) 
332   values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults));
(gdb) 
333   values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType);
(gdb) 
334   values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes);
(gdb) 
335   if (allParameterTypes != PointerGetDatum(NULL))
(gdb) 
336     values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes;
(gdb) 
339   if (parameterModes != PointerGetDatum(NULL))
(gdb) 
340     values[Anum_pg_proc_proargmodes - 1] = parameterModes;
(gdb) 
343   if (parameterNames != PointerGetDatum(NULL))
(gdb) 
344     values[Anum_pg_proc_proargnames - 1] = parameterNames;
(gdb) 
347   if (parameterDefaults != NIL)
(gdb) 
350     nulls[Anum_pg_proc_proargdefaults - 1] = true;
(gdb) 
351   if (trftypes != PointerGetDatum(NULL))
(gdb) 
354     nulls[Anum_pg_proc_protrftypes - 1] = true;
(gdb) 
355   values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc);
(gdb) 
356   if (probin)
(gdb) 
359     nulls[Anum_pg_proc_probin - 1] = true;
(gdb) 
360   if (proconfig != PointerGetDatum(NULL))
(gdb) 
363     nulls[Anum_pg_proc_proconfig - 1] = true;
(gdb) 
366   rel = table_open(ProcedureRelationId, RowExclusiveLock);
(gdb) 
367   tupDesc = RelationGetDescr(rel);
(gdb)

判断是否已存在

(gdb) p values[28]
$21 = 0
(gdb) p values[0]
$22 = 0
(gdb) p values[2]
$23 = 2200
(gdb) n
370   oldtup = SearchSysCache3(PROCNAMEARGSNSP,
(gdb) p *tupDesc
$24 = {natts = 29, tdtypeid = 81, tdtypmod = -1, tdrefcount = 1, constr = 0x7fbeb44493b8, 
  attrs = 0x7fbeb44483b8}
(gdb) n
375   if (HeapTupleIsValid(oldtup))
(gdb) 
378     Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
(gdb) 
383     if (!replace)
(gdb) p oldproc
$25 = (Form_pg_proc) 0x7fbeb4388bf8
(gdb) p *oldproc
$26 = {oid = 16387, proname = {data = "func_test", '\000' <repeats 54 times>}, 
  pronamespace = 2200, proowner = 10, prolang = 13581, procost = 100, prorows = 0, 
  provariadic = 0, prosupport = 0, prokind = 102 'f', prosecdef = false, 
  proleakproof = false, proisstrict = false, proretset = false, provolatile = 118 'v', 
  proparallel = 117 'u', pronargs = 3, pronargdefaults = 0, prorettype = 2249, proargtypes = {
    vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0, 
    values = 0x7fbeb4388c80}}
(gdb)

执行相关判断:如返回类型,参数类型,参数个数等

(gdb) n
388     if (!pg_proc_ownercheck(oldproc->oid, proowner))
(gdb) 
393     if (oldproc->prokind != prokind)
(gdb) 
407     dropcmd = (prokind == PROKIND_PROCEDURE ? "DROP PROCEDURE" :
(gdb) 
419     if (returnType != oldproc->prorettype ||
(gdb) 
420       returnsSet != oldproc->proretset)
(gdb) 
419     if (returnType != oldproc->prorettype ||
(gdb) 
439     if (returnType == RECORDOID)
(gdb) 
444       olddesc = build_function_result_tupdesc_t(oldtup);
(gdb) 
445       newdesc = build_function_result_tupdesc_d(prokind,
(gdb) 
449       if (olddesc == NULL && newdesc == NULL)
(gdb) 
451       else if (olddesc == NULL || newdesc == NULL ||
(gdb) 
452            !equalTupleDescs(olddesc, newdesc))
(gdb) 
451       else if (olddesc == NULL || newdesc == NULL ||
(gdb) 
468     proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
(gdb) 
471     if (!isnull)
(gdb) 
480       proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
(gdb) 
483       if (isnull)
(gdb) 
486       n_old_arg_names = get_func_input_arg_names(proargnames,
(gdb) 
489       n_new_arg_names = get_func_input_arg_names(parameterNames,
(gdb) 
492       for (j = 0; j < n_old_arg_names; j++)
(gdb) 
494         if (old_arg_names[j] == NULL)
(gdb) 
496         if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
(gdb) 
497           strcmp(old_arg_names[j], new_arg_names[j]) != 0)
(gdb) 
496         if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
(gdb) 
492       for (j = 0; j < n_old_arg_names; j++)
(gdb) 
494         if (old_arg_names[j] == NULL)
(gdb) 
496         if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
(gdb) 
497           strcmp(old_arg_names[j], new_arg_names[j]) != 0)
(gdb) 
496         if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
(gdb) 
492       for (j = 0; j < n_old_arg_names; j++)
(gdb) 
494         if (old_arg_names[j] == NULL)
(gdb) 
496         if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
(gdb) 
497           strcmp(old_arg_names[j], new_arg_names[j]) != 0)
(gdb) 
496         if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
(gdb) 
492       for (j = 0; j < n_old_arg_names; j++)
(gdb) 
517     if (oldproc->pronargdefaults != 0)
(gdb) 
568     replaces[Anum_pg_proc_oid - 1] = false;
(gdb) 
569     replaces[Anum_pg_proc_proowner - 1] = false;
(gdb) 
570     replaces[Anum_pg_proc_proacl - 1] = false;
(gdb) 
573     tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
(gdb) 
574     CatalogTupleUpdate(rel, &tup->t_self, tup);
(gdb

更新tuple

(gdb) n
576     ReleaseSysCache(oldtup);
(gdb) 
577     is_update = true;
(gdb) 
601   retval = ((Form_pg_proc) GETSTRUCT(tup))->oid;
(gdb)

处理函数依赖

(gdb) p retval
$27 = 16387
(gdb) n
610     deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
(gdb) 
612   myself.classId = ProcedureRelationId;
(gdb) 
613   myself.objectId = retval;
(gdb) 
614   myself.objectSubId = 0;
(gdb) 
617   referenced.classId = NamespaceRelationId;
(gdb) 
618   referenced.objectId = procNamespace;
(gdb) 
619   referenced.objectSubId = 0;
(gdb) 
620   recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
623   referenced.classId = LanguageRelationId;
(gdb) 
624   referenced.objectId = languageObjectId;
(gdb) 
625   referenced.objectSubId = 0;
(gdb) 
626   recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
629   referenced.classId = TypeRelationId;
(gdb) 
630   referenced.objectId = returnType;
(gdb) 
631   referenced.objectSubId = 0;
(gdb) 
632   recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
635   if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
(gdb) 
644   for (i = 0; i < allParamCount; i++)
(gdb) 
646     referenced.classId = TypeRelationId;
(gdb) 
647     referenced.objectId = allParams[i];
(gdb) 
648     referenced.objectSubId = 0;
(gdb) 
649     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
652     if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
(gdb) 
644   for (i = 0; i < allParamCount; i++)
(gdb) 
646     referenced.classId = TypeRelationId;
(gdb) 
647     referenced.objectId = allParams[i];
(gdb) 
648     referenced.objectSubId = 0;
(gdb) 
649     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
652     if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
(gdb) 
644   for (i = 0; i < allParamCount; i++)
(gdb) 
646     referenced.classId = TypeRelationId;
(gdb) 
647     referenced.objectId = allParams[i];
(gdb) 
648     referenced.objectSubId = 0;
(gdb) 
649     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
652     if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
(gdb) 
644   for (i = 0; i < allParamCount; i++)
(gdb) 
646     referenced.classId = TypeRelationId;
(gdb) 
647     referenced.objectId = allParams[i];
(gdb) 
648     referenced.objectSubId = 0;
(gdb) 
649     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
652     if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
(gdb) 
644   for (i = 0; i < allParamCount; i++)
(gdb) 
646     referenced.classId = TypeRelationId;
(gdb) 
647     referenced.objectId = allParams[i];
(gdb) 
648     referenced.objectSubId = 0;
(gdb) 
649     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
(gdb) 
652     if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
(gdb) 
644   for (i = 0; i < allParamCount; i++)
(gdb) 
662   if (parameterDefaults)
(gdb) 
667   if (OidIsValid(prosupport))
(gdb) 
676   if (!is_update)
(gdb) 
680   if (!is_update)
(gdb) 
685   recordDependencyOnCurrentExtension(&myself, is_update);
(gdb) 
687   heap_freetuple(tup);
(gdb) 
690   InvokeObjectPostCreateHook(ProcedureRelationId, retval, 0);
(gdb) 
692   table_close(rel, RowExclusiveLock);
(gdb)

执行函数body验证

695   if (OidIsValid(languageValidator))
(gdb) 
697     ArrayType  *set_items = NULL;
(gdb) 
698     int     save_nestlevel = 0;
(gdb) 
701     CommandCounterIncrement();
(gdb) 
713     if (check_function_bodies)
(gdb) 
(gdb) n
715       set_items = (ArrayType *) DatumGetPointer(proconfig);
(gdb) 
716       if (set_items)    /* Need a new GUC nesting level */
(gdb) 
726     OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval));
(gdb) 
728     if (set_items)
(gdb) 
732   return myself;
(gdb) 
733 }
(gdb)

完成调用

(gdb) 
733 }
(gdb) 
CreateFunction (pstate=0x1199c88, stmt=0x11748c8) at functioncmds.c:1176
1176  }
(gdb)

“分析PostgreSQL CreateFunction中的ProcedureCreate函数”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI