ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(243)- plpgsql(interpret_function_parameter_list)

PostgreSQL 源码解读(243)- plpgsql(interpret_function_parameter_list)

原创 PostgreSQL 作者:husthxd 时间:2020-02-21 18:22:26 0 删除 编辑

本节简单介绍了PostgreSQL创建函数的过程,其实现函数是CreateFunction。这里介绍了CreateFunction中的interpret_function_parameter_list函数。

一、数据结构

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;

二、源码解读


/*
 * Interpret the function parameter list of a CREATE FUNCTION or
 * CREATE AGGREGATE statement.
 * 解析CREATE FUNCTON/CREATE AGGREGATE语句中的参数链表
 *
 * Input parameters:
 * parameters: list of FunctionParameter structs
 * languageOid: OID of function language (InvalidOid if it's CREATE AGGREGATE)
 * objtype: needed only to determine error handling and required result type
 * 输入参数:
 * parameters:FunctionParameter结构体链表
 * languageOid:语言ID
 * objtype:确定错误处理时需要,并且需要结果类型
 *
 * Results are stored into output parameters.  parameterTypes must always
 * be created, but the other arrays are set to NULL if not needed.
 * variadicArgType is set to the variadic array type if there's a VARIADIC
 * parameter (there can be only one); or to InvalidOid if not.
 * requiredResultType is set to InvalidOid if there are no OUT parameters,
 * else it is set to the OID of the implied result type.
 * 结果存储在输出参数中.必须创建parameterTypes,如不需要其他数组将设置为NULL.
 * 如存在VARIADIC参数(有且仅有一个),variadicArgType将设置为variadic数组类型.
 * 如无OUT参数,则requiredResultType设置为InvalidOid,否则设置为结果类型.
 */
void
interpret_function_parameter_list(ParseState *pstate,//解析状态
                  List *parameters,//参数链表
                  Oid languageOid,//语言OID
                  ObjectType objtype,//对象类型
                  oidvector **parameterTypes,//参数类型oid vector
                  ArrayType **allParameterTypes,//所有的参数类型
                  ArrayType **parameterModes,//参数模式,i/o/b/v/t
                  ArrayType **parameterNames,//参数名称
                  List **parameterDefaults,//参数默认值链表
                  Oid *variadicArgType,//variadic参数类型OID
                  Oid *requiredResultType)//结果类型
{
  int     parameterCount = list_length(parameters);//参数个数
  Oid      *inTypes;
  int     inCount = 0;
  Datum    *allTypes;
  Datum    *paramModes;
  Datum    *paramNames;
  int     outCount = 0;
  int     varCount = 0;
  bool    have_names = false;
  bool    have_defaults = false;
  ListCell   *x;
  int     i;
  *variadicArgType = InvalidOid;  /* default result */
  *requiredResultType = InvalidOid; /* default result */
  //输入参数类型
  inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
  //所有参数的类型
  allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
  //
  paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
  //参数名称
  paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
  *parameterDefaults = NIL;
  /* Scan the list and extract data into work arrays */
  //扫描链表,提前数据到结果数组中
  i = 0;
  foreach(x, parameters)
  {
    //函数参数
    FunctionParameter *fp = (FunctionParameter *) lfirst(x);
    //类型名称
    TypeName   *t = fp->argType;
    //是否输入参数
    bool    isinput = false;
    Oid     toid;
    //pg_proc元组
    Type    typtup;
    //权限检查结果
    AclResult aclresult;
    //检索type tuple
    typtup = LookupTypeName(NULL, t, NULL, false);
    if (typtup)
    {
      //
      if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
      {
        /* As above, hard error if language is SQL */
        if (languageOid == SQLlanguageId)
          ereport(ERROR,
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
               errmsg("SQL function cannot accept shell type %s",
                  TypeNameToString(t))));
        /* We don't allow creating aggregates on shell types either */
        else if (objtype == OBJECT_AGGREGATE)
          ereport(ERROR,
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
               errmsg("aggregate cannot accept shell type %s",
                  TypeNameToString(t))));
        else
          ereport(NOTICE,
              (errcode(ERRCODE_WRONG_OBJECT_TYPE),
               errmsg("argument type %s is only a shell",
                  TypeNameToString(t))));
      }
      //type的OID
      toid = typeTypeId(typtup);
      //释放缓存
      ReleaseSysCache(typtup);
    }
    else
    {
      //该类型不存在
      ereport(ERROR,
          (errcode(ERRCODE_UNDEFINED_OBJECT),
           errmsg("type %s does not exist",
              TypeNameToString(t))));
      toid = InvalidOid;  /* keep compiler quiet */
    }
    //权限检查
    aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
    if (aclresult != ACLCHECK_OK)
      aclcheck_error_type(aclresult, toid);
    if (t->setof)
    {
      //存在setof
      if (objtype == OBJECT_AGGREGATE)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("aggregates cannot accept set arguments")));
      else if (objtype == OBJECT_PROCEDURE)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("procedures cannot accept set arguments")));
      else
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("functions cannot accept set arguments")));
    }
    if (objtype == OBJECT_PROCEDURE)
    {
      //过程对象,不需要OUT参数,只允许inout参数
      if (fp->mode == FUNC_PARAM_OUT)
        ereport(ERROR,
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
             (errmsg("procedures cannot have OUT arguments"),
              errhint("INOUT arguments are permitted."))));
    }
    /* handle input parameters */
    //处理输入参数
    if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
    {
      //非OUT参数并且不是TABLE参数
      /* other input parameters can't follow a VARIADIC parameter */
      if (varCount > 0)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("VARIADIC parameter must be the last input parameter")));
      //写入到输入类型数组中
      inTypes[inCount++] = toid;
      isinput = true;
    }
    /* handle output parameters */
    //处理输出参数
    if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
    {
      if (objtype == OBJECT_PROCEDURE)
        //存储过程:要求输出结果类型为RECORD
        *requiredResultType = RECORDOID;
      else if (outCount == 0) /* save first output param's type */
        //第一个OID
        *requiredResultType = toid;
      outCount++;
    }
    if (fp->mode == FUNC_PARAM_VARIADIC)
    {
      //variadic参数
      *variadicArgType = toid;
      varCount++;
      /* validate variadic parameter type */
      //验证variadic参数类型
      switch (toid)
      {
        case ANYARRAYOID:
        case ANYOID:
          /* okay */
          break;
        default:
          if (!OidIsValid(get_element_type(toid)))
            ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("VARIADIC parameter must be an array")));
          break;
      }
    }
    //转换为Datum
    allTypes[i] = ObjectIdGetDatum(toid);
    //参数类型
    paramModes[i] = CharGetDatum(fp->mode);
    if (fp->name && fp->name[0])
    {
      //检查参数名称,参数名称不能重复
      ListCell   *px;
      /*
       * As of Postgres 9.0 we disallow using the same name for two
       * input or two output function parameters.  Depending on the
       * function's language, conflicting input and output names might
       * be bad too, but we leave it to the PL to complain if so.
       */
      foreach(px, parameters)
      {
        //循环判断参数是否重复
        FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
        if (prevfp == fp)
          break;
        /* pure in doesn't conflict with pure out */
        //输入和输出不冲突
        if ((fp->mode == FUNC_PARAM_IN ||
           fp->mode == FUNC_PARAM_VARIADIC) &&
          (prevfp->mode == FUNC_PARAM_OUT ||
           prevfp->mode == FUNC_PARAM_TABLE))
          continue;
        if ((prevfp->mode == FUNC_PARAM_IN ||
           prevfp->mode == FUNC_PARAM_VARIADIC) &&
          (fp->mode == FUNC_PARAM_OUT ||
           fp->mode == FUNC_PARAM_TABLE))
          continue;
        if (prevfp->name && prevfp->name[0] &&
          strcmp(prevfp->name, fp->name) == 0)
          ereport(ERROR,
              (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
               errmsg("parameter name \"%s\" used more than once",
                  fp->name)));
      }
      //获取Datum
      paramNames[i] = CStringGetTextDatum(fp->name);
      have_names = true;
    }
    if (fp->defexpr)
    {
      Node     *def;
      if (!isinput)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("only input parameters can have default values")));
      def = transformExpr(pstate, fp->defexpr,
                EXPR_KIND_FUNCTION_DEFAULT);
      def = coerce_to_specific_type(pstate, def, toid, "DEFAULT");
      assign_expr_collations(pstate, def);
      /*
       * Make sure no variables are referred to (this is probably dead
       * code now that add_missing_from is history).
       */
      if (list_length(pstate->p_rtable) != 0 ||
        contain_var_clause(def))
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
             errmsg("cannot use table references in parameter default value")));
      /*
       * transformExpr() should have already rejected subqueries,
       * aggregates, and window functions, based on the EXPR_KIND_ for a
       * default expression.
       *
       * It can't return a set either --- but coerce_to_specific_type
       * already checked that for us.
       *
       * Note: the point of these restrictions is to ensure that an
       * expression that, on its face, hasn't got subplans, aggregates,
       * etc cannot suddenly have them after function default arguments
       * are inserted.
       */
      *parameterDefaults = lappend(*parameterDefaults, def);
      have_defaults = true;
    }
    else
    {
      if (isinput && have_defaults)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("input parameters after one with a default value must also have defaults")));
    }
    i++;
  }
  /* Now construct the proper outputs as needed */
  //如需要,构建合适的输出
  *parameterTypes = buildoidvector(inTypes, inCount);
  if (outCount > 0 || varCount > 0)
  {
    //输出参数
    *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
                       sizeof(Oid), true, 'i');
    *parameterModes = construct_array(paramModes, parameterCount, CHAROID,
                      1, true, 'c');
    if (outCount > 1)
      *requiredResultType = RECORDOID;
    /* otherwise we set requiredResultType correctly above */
  }
  else
  {
    *allParameterTypes = NULL;
    *parameterModes = NULL;
  }
  if (have_names)
  {
    //指定了参数名称
    for (i = 0; i < parameterCount; i++)
    {
      if (paramNames[i] == PointerGetDatum(NULL))
        paramNames[i] = CStringGetTextDatum("");
    }
    *parameterNames = construct_array(paramNames, parameterCount, TEXTOID,
                      -1, false, 'i');
  }
  else
    *parameterNames = NULL;
}

三、跟踪分析

测试脚本


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) c
Continuing.
Breakpoint 1, interpret_function_parameter_list (pstate=0x10edc88, parameters=0x10c7d30, 
    languageOid=13581, objtype=OBJECT_FUNCTION, parameterTypes=0x7ffec5c6ea88, 
    allParameterTypes=0x7ffec5c6ea80, parameterModes=0x7ffec5c6ea78, 
    parameterNames=0x7ffec5c6ea70, parameterDefaults=0x7ffec5c6ea68, 
    variadicArgType=0x7ffec5c6ea64, requiredResultType=0x7ffec5c6ea60) at functioncmds.c:195
195   int     parameterCount = list_length(parameters);
(gdb)

输入参数,语言为pl/pgsql,对象类型是function,存在有5个参数

(gdb) p *pstate
$1 = {parentParseState = 0x0, 
  p_sourcetext = 0x10c6ed8 "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)\nreturns record \nas\n$$\ndeclare\nbegin\n  raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 :="..., p_rtable = 0x0, p_joinexprs = 0x0, 
  p_joinlist = 0x0, p_namespace = 0x0, p_lateral_active = false, p_ctenamespace = 0x0, 
  p_future_ctes = 0x0, p_parent_cte = 0x0, p_target_relation = 0x0, 
  p_target_rangetblentry = 0x0, p_is_insert = false, p_windowdefs = 0x0, 
  p_expr_kind = EXPR_KIND_NONE, p_next_resno = 1, p_multiassign_exprs = 0x0, 
  p_locking_clause = 0x0, p_locked_from_parent = false, p_resolve_unknowns = true, 
  p_queryEnv = 0x0, p_hasAggs = false, p_hasWindowFuncs = false, p_hasTargetSRFs = false, 
  p_hasSubLinks = false, p_hasModifyingCTE = false, p_last_srf = 0x0, 
  p_pre_columnref_hook = 0x0, p_post_columnref_hook = 0x0, p_paramref_hook = 0x0, 
  p_coerce_param_hook = 0x0, p_ref_hook_state = 0x0}
(gdb) 
(gdb) p *parameters
$2 = {type = T_List, length = 5, head = 0x10c7d08, tail = 0x10c8480}
(gdb)

初始化相关变量

(gdb) n
197   int     inCount = 0;
(gdb) 
201   int     outCount = 0;
(gdb) 
202   int     varCount = 0;
(gdb) 
203   bool    have_names = false;
(gdb) 
204   bool    have_defaults = false;
(gdb) 
208   *variadicArgType = InvalidOid;  /* default result */
(gdb) 
209   *requiredResultType = InvalidOid; /* default result */
(gdb) 
211   inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
(gdb) 
212   allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
(gdb) 
213   paramModes = (Datum *) palloc(parameterCount * sizeof(Datum));
(gdb) 
214   paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum));
(gdb) 
215   *parameterDefaults = NIL;
(gdb) 
218   i = 0;
(gdb) 
219   foreach(x, parameters)
(gdb)

开始循环,第一个参数,名称为pi_v1,参数类型为pg_catalog.int4

(gdb) 
221     FunctionParameter *fp = (FunctionParameter *) lfirst(x);
(gdb) 
222     TypeName   *t = fp->argType;
(gdb) 
223     bool    isinput = false;
(gdb) p *fp
$4 = {type = T_FunctionParameter, name = 0x10c7b60 "pi_v1", argType = 0x10c7c58, 
  mode = FUNC_PARAM_IN, defexpr = 0x0}
(gdb) p *fp->argType
$5 = {type = T_TypeName, names = 0x10c7bd0, typeOid = 0, setof = false, pct_type = false, 
  typmods = 0x0, typemod = -1, arrayBounds = 0x0, location = 46}
(gdb) p *fp->argType->names
$6 = {type = T_List, length = 2, head = 0x10c7c30, tail = 0x10c7ba8}
(gdb) p *(Node *)fp->argType->names->head->data.ptr_value
$7 = {type = T_String}
(gdb) p *(Value *)fp->argType->names->head->data.ptr_value
$8 = {type = T_String, val = {ival = 12340746, str = 0xbc4e0a "pg_catalog"}}
(gdb) p *(Value *)fp->argType->names->head->next->data.ptr_value
$9 = {type = T_String, val = {ival = 12320664, str = 0xbbff98 "int4"}}
(gdb) p *t
$10 = {type = T_TypeName, names = 0x10c7bd0, typeOid = 0, setof = false, pct_type = false, 
  typmods = 0x0, typemod = -1, arrayBounds = 0x0, location = 46}
(gdb)

获取pg_type中对应type的tuple

(gdb) n
228     typtup = LookupTypeName(NULL, t, NULL, false);
(gdb) 
229     if (typtup)
(gdb) p *typtup
$11 = {t_len = 176, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 8}, 
  t_tableOid = 1247, t_data = 0x7ff12a2c3c40}
(gdb) p *typtup->t_data
$12 = {t_choice = {t_heap = {t_xmin = 1, t_xmax = 0, t_field3 = {t_cid = 0, t_xvac = 0}}, 
    t_datum = {datum_len_ = 1, datum_typmod = 0, datum_typeid = 0}}, t_ctid = {ip_blkid = {
      bi_hi = 0, bi_lo = 0}, ip_posid = 8}, t_infomask2 = 31, t_infomask = 2305, 
  t_hoff = 32 ' ', t_bits = 0x7ff12a2c3c57 "\377\377\377\017"}
(gdb) x/16c typtup->t_data->t_bits
0x7ff12a2c3c57: -1 '\377' -1 '\377' -1 '\377' 15 '\017' 0 '\000'    0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c5f: 0 '\000'  23 '\027' 0 '\000'  0 '\000'  0 '\000'    105 'i' 110 'n' 116 't'
(gdb) x/64c typtup->t_data->t_bits
0x7ff12a2c3c57: -1 '\377' -1 '\377' -1 '\377' 15 '\017' 0 '\000'    0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c5f: 0 '\000'  23 '\027' 0 '\000'  0 '\000'  0 '\000'    105 'i' 110 'n' 116 't'
0x7ff12a2c3c67: 52 '4'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c6f: 0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'    0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c77: 0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'    0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c7f: 0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'    0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c87: 0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'    0 '\000'  0 '\000'  0 '\000'
0x7ff12a2c3c8f: 0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'    0 '\000'  0 '\000'  0 '\000'
(gdb) 
(gdb) p *((Form_pg_type) GETSTRUCT(typtup))
$14 = {oid = 23, typname = {data = "int4", '\000' <repeats 59 times>}, typnamespace = 11, 
  typowner = 10, typlen = 4, typbyval = true, typtype = 98 'b', typcategory = 78 'N', 
  typispreferred = false, typisdefined = true, typdelim = 44 ',', typrelid = 0, 
  typelem = 0, typarray = 1007, typinput = 42, typoutput = 43, typreceive = 2406, 
  typsend = 2407, typmodin = 0, typmodout = 0, typanalyze = 0, typalign = 105 'i', 
  typstorage = 112 'p', typnotnull = false, typbasetype = 0, typtypmod = -1, typndims = 0, 
  typcollation = 0}
(gdb)

获取type的oid

(gdb) n
251       toid = typeTypeId(typtup);
(gdb) 
252       ReleaseSysCache(typtup);
(gdb) 
263     aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
(gdb) p toid
$15 = 23
(gdb)

检查权限

263     aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
(gdb) p toid
$15 = 23
(gdb) n
264     if (aclresult != ACLCHECK_OK)
(gdb) 
267     if (t->setof)
(gdb)

处理输入参数

(gdb) p t->setof
$16 = false
(gdb) n
283     if (objtype == OBJECT_PROCEDURE)
(gdb) 
293     if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
(gdb) 
296       if (varCount > 0)
(gdb) 
300       inTypes[inCount++] = toid;
(gdb) 
301       isinput = true;
(gdb) 
305     if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
(gdb) 
314     if (fp->mode == FUNC_PARAM_VARIADIC)
(gdb) 
(gdb) n
334     allTypes[i] = ObjectIdGetDatum(toid);
(gdb) 
336     paramModes[i] = CharGetDatum(fp->mode);
(gdb) 
338     if (fp->name && fp->name[0])
(gdb) p fp->mode
$17 = FUNC_PARAM_IN
(gdb) n
348       foreach(px, parameters)
(gdb)

判断是否重名

(gdb) n
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
353           break;
(gdb)

如无重名,设置相关变量,把fp->name的地址写入paramNames指针数组中

373       paramNames[i] = CStringGetTextDatum(fp->name);
(gdb) 
374       have_names = true;
(gdb) 
377     if (fp->defexpr)
(gdb) p paramNames[0]
$18 = 17751776

下一个参数

(gdb) n
420       if (isinput && have_defaults)
(gdb) 
426     i++;
(gdb) p *fp->defexpr
Cannot access memory at address 0x0
(gdb) n
219   foreach(x, parameters)
(gdb) 
221     FunctionParameter *fp = (FunctionParameter *) lfirst(x);
(gdb) 
222     TypeName   *t = fp->argType;
(gdb) 
223     bool    isinput = false;
(gdb) 
228     typtup = LookupTypeName(NULL, t, NULL, false);
(gdb) 
(gdb) n
229     if (typtup)
(gdb) 
231       if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
(gdb) 
251       toid = typeTypeId(typtup);
(gdb) 
252       ReleaseSysCache(typtup);
(gdb) p *((Form_pg_type) GETSTRUCT(typtup))
$22 = {oid = 1043, typname = {data = "varchar", '\000' <repeats 56 times>}, 
  typnamespace = 11, typowner = 10, typlen = -1, typbyval = false, typtype = 98 'b', 
  typcategory = 83 'S', typispreferred = false, typisdefined = true, typdelim = 44 ',', 
  typrelid = 0, typelem = 0, typarray = 1015, typinput = 1046, typoutput = 1047, 
  typreceive = 2432, typsend = 2433, typmodin = 2915, typmodout = 2916, typanalyze = 0, 
  typalign = 105 'i', typstorage = 120 'x', typnotnull = false, typbasetype = 0, 
  typtypmod = -1, typndims = 0, typcollation = 100}
(gdb) n
263     aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
(gdb) 
264     if (aclresult != ACLCHECK_OK)
(gdb) 
267     if (t->setof)
(gdb) 
283     if (objtype == OBJECT_PROCEDURE)
(gdb) 
293     if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
(gdb) 
296       if (varCount > 0)
(gdb) 
300       inTypes[inCount++] = toid;
(gdb) 
301       isinput = true;
(gdb) 
305     if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
(gdb) 
314     if (fp->mode == FUNC_PARAM_VARIADIC)
(gdb) 
334     allTypes[i] = ObjectIdGetDatum(toid);
(gdb) 
336     paramModes[i] = CharGetDatum(fp->mode);
(gdb)

判断参数是否重复

338     if (fp->name && fp->name[0])
(gdb) 
348       foreach(px, parameters)
(gdb) p fp->name
$23 = 0x10c7d68 "pi_v2"
(gdb) p fp->name[0]
$24 = 112 'p'
(gdb) n
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) p *prevfp
$25 = {type = T_FunctionParameter, name = 0x10c7b60 "pi_v1", argType = 0x10c7c58, 
  mode = FUNC_PARAM_IN, defexpr = 0x0}
(gdb) n
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
357           (prevfp->mode == FUNC_PARAM_OUT ||
(gdb) 
356            fp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
358            prevfp->mode == FUNC_PARAM_TABLE))
(gdb) 
357           (prevfp->mode == FUNC_PARAM_OUT ||
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
361            prevfp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
363            fp->mode == FUNC_PARAM_TABLE))
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
366           strcmp(prevfp->name, fp->name) == 0)
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) p *prevfp
$26 = {type = T_FunctionParameter, name = 0x10c7d68 "pi_v2", argType = 0x10c7e60, 
  mode = FUNC_PARAM_IN, defexpr = 0x0}
(gdb) p *fp
$27 = {type = T_FunctionParameter, name = 0x10c7d68 "pi_v2", argType = 0x10c7e60, 
  mode = FUNC_PARAM_IN, defexpr = 0x0}
(gdb) n
353           break;
(gdb) 
373       paramNames[i] = CStringGetTextDatum(fp->name);
(gdb) 
374       have_names = true;
(gdb) 
377     if (fp->defexpr)
(gdb) 
420       if (isinput && have_defaults)
(gdb) 
426     i++;
(gdb) 
219   foreach(x, parameters)

下一参数,这时候是一个INOUT参数(在IN和OUT数组中均记录)

(gdb) n
221     FunctionParameter *fp = (FunctionParameter *) lfirst(x);
(gdb) 
222     TypeName   *t = fp->argType;
(gdb) 
223     bool    isinput = false;
(gdb) 
228     typtup = LookupTypeName(NULL, t, NULL, false);
(gdb) 
229     if (typtup)
(gdb) 
231       if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
(gdb) 
251       toid = typeTypeId(typtup);
(gdb) p *((Form_pg_type) GETSTRUCT(typtup))
$28 = {oid = 1043, typname = {data = "varchar", '\000' <repeats 56 times>}, 
  typnamespace = 11, typowner = 10, typlen = -1, typbyval = false, typtype = 98 'b', 
  typcategory = 83 'S', typispreferred = false, typisdefined = true, typdelim = 44 ',', 
  typrelid = 0, typelem = 0, typarray = 1015, typinput = 1046, typoutput = 1047, 
  typreceive = 2432, typsend = 2433, typmodin = 2915, typmodout = 2916, typanalyze = 0, 
  typalign = 105 'i', typstorage = 120 'x', typnotnull = false, typbasetype = 0, 
  typtypmod = -1, typndims = 0, typcollation = 100}
(gdb) n
252       ReleaseSysCache(typtup);
(gdb) 
263     aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
(gdb) 
264     if (aclresult != ACLCHECK_OK)
(gdb) 
267     if (t->setof)
(gdb) 
283     if (objtype == OBJECT_PROCEDURE)
(gdb) 
293     if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
(gdb) 
296       if (varCount > 0)
(gdb) 
300       inTypes[inCount++] = toid;
(gdb) 
301       isinput = true;
(gdb) 
305     if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
(gdb) 
307       if (objtype == OBJECT_PROCEDURE)
(gdb) 
309       else if (outCount == 0) /* save first output param's type */
(gdb) 
310         *requiredResultType = toid;
(gdb) 
311       outCount++;
(gdb) 
314     if (fp->mode == FUNC_PARAM_VARIADIC)
(gdb) 
334     allTypes[i] = ObjectIdGetDatum(toid);
(gdb) 
336     paramModes[i] = CharGetDatum(fp->mode);
(gdb) 
338     if (fp->name && fp->name[0])
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) p *prevfp
$29 = {type = T_FunctionParameter, name = 0x10c7b60 "pi_v1", argType = 0x10c7c58, 
  mode = FUNC_PARAM_IN, defexpr = 0x0}
(gdb) n
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
356            fp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) p *fp
$30 = {type = T_FunctionParameter, name = 0x10c7f38 "pio_v3", argType = 0x10c8030, 
  mode = FUNC_PARAM_INOUT, defexpr = 0x0}
(gdb) n
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
361            prevfp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
363            fp->mode == FUNC_PARAM_TABLE))
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
366           strcmp(prevfp->name, fp->name) == 0)
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
356            fp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
361            prevfp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
363            fp->mode == FUNC_PARAM_TABLE))
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
366           strcmp(prevfp->name, fp->name) == 0)
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
353           break;
(gdb) 
373       paramNames[i] = CStringGetTextDatum(fp->name);
(gdb) 
374       have_names = true;
(gdb) 
377     if (fp->defexpr)
(gdb) 
420       if (isinput && have_defaults)
(gdb) 
426     i++;
(gdb) 
219   foreach(x, parameters)

下一个参数,OUT参数

(gdb) n
221     FunctionParameter *fp = (FunctionParameter *) lfirst(x);
(gdb) n
222     TypeName   *t = fp->argType;
(gdb) 
223     bool    isinput = false;
(gdb) 
228     typtup = LookupTypeName(NULL, t, NULL, false);
(gdb) 
229     if (typtup)
(gdb) 
231       if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
(gdb) 
251       toid = typeTypeId(typtup);
(gdb) p *((Form_pg_type) GETSTRUCT(typtup))
$31 = {oid = 23, typname = {data = "int4", '\000' <repeats 59 times>}, typnamespace = 11, 
  typowner = 10, typlen = 4, typbyval = true, typtype = 98 'b', typcategory = 78 'N', 
  typispreferred = false, typisdefined = true, typdelim = 44 ',', typrelid = 0, 
  typelem = 0, typarray = 1007, typinput = 42, typoutput = 43, typreceive = 2406, 
  typsend = 2407, typmodin = 0, typmodout = 0, typanalyze = 0, typalign = 105 'i', 
  typstorage = 112 'p', typnotnull = false, typbasetype = 0, typtypmod = -1, typndims = 0, 
  typcollation = 0}
(gdb) n
252       ReleaseSysCache(typtup);
(gdb) 
263     aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE);
(gdb) 
264     if (aclresult != ACLCHECK_OK)
(gdb) 
267     if (t->setof)
(gdb) 
283     if (objtype == OBJECT_PROCEDURE)
(gdb) 
293     if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
(gdb) 
305     if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
(gdb) 
307       if (objtype == OBJECT_PROCEDURE)
(gdb) 
309       else if (outCount == 0) /* save first output param's type */
(gdb) 
311       outCount++;
(gdb) 
314     if (fp->mode == FUNC_PARAM_VARIADIC)
(gdb) 
334     allTypes[i] = ObjectIdGetDatum(toid);
(gdb) 
336     paramModes[i] = CharGetDatum(fp->mode);
(gdb) 
338     if (fp->name && fp->name[0])
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) p *prevfp
$32 = {type = T_FunctionParameter, name = 0x10c7b60 "pi_v1", argType = 0x10c7c58, 
  mode = FUNC_PARAM_IN, defexpr = 0x0}
(gdb) p *fp
$33 = {type = T_FunctionParameter, name = 0x10c8108 "po_v4", argType = 0x10c8200, 
  mode = FUNC_PARAM_OUT, defexpr = 0x0}
(gdb) n
356            fp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
361            prevfp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
364           continue;
(gdb) n
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
356            fp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
362           (fp->mode == FUNC_PARAM_OUT ||
(gdb) 
361            prevfp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
364           continue;
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
356            fp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
355         if ((fp->mode == FUNC_PARAM_IN ||
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
361            prevfp->mode == FUNC_PARAM_VARIADIC) &&
(gdb) 
360         if ((prevfp->mode == FUNC_PARAM_IN ||
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
366           strcmp(prevfp->name, fp->name) == 0)
(gdb) 
365         if (prevfp->name && prevfp->name[0] &&
(gdb) 
348       foreach(px, parameters)
(gdb) 
350         FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
(gdb) 
352         if (prevfp == fp)
(gdb) 
353           break;
(gdb) 
373       paramNames[i] = CStringGetTextDatum(fp->name);
(gdb) 
374       have_names = true;
(gdb) 
377     if (fp->defexpr)
(gdb) 
420       if (isinput && have_defaults)
(gdb) 
426     i++;
(gdb) 
219   foreach(x, parameters)

下一个参数

......

完成所有参数的解析

219   foreach(x, parameters)
(gdb) 
430   *parameterTypes = buildoidvector(inTypes, inCount);
(gdb) 
432   if (outCount > 0 || varCount > 0)
(gdb) 
434     *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
(gdb) n
436     *parameterModes = construct_array(paramModes, parameterCount, CHAROID,
(gdb) 
438     if (outCount > 1)
(gdb) 
439       *requiredResultType = RECORDOID;
(gdb) p *allTypes
$34 = 23
(gdb) p allTypes[4]
$35 = 1043
(gdb) n
438     if (outCount > 1)
(gdb) 
448   if (have_names)
(gdb) 
450     for (i = 0; i < parameterCount; i++)
(gdb) 
452       if (paramNames[i] == PointerGetDatum(NULL))
(gdb) 
450     for (i = 0; i < parameterCount; i++)
(gdb) 
452       if (paramNames[i] == PointerGetDatum(NULL))
(gdb) 
450     for (i = 0; i < parameterCount; i++)
(gdb) 
452       if (paramNames[i] == PointerGetDatum(NULL))
(gdb) 
450     for (i = 0; i < parameterCount; i++)
(gdb) 
452       if (paramNames[i] == PointerGetDatum(NULL))
(gdb) 
450     for (i = 0; i < parameterCount; i++)
(gdb) 
452       if (paramNames[i] == PointerGetDatum(NULL))
(gdb) 
450     for (i = 0; i < parameterCount; i++)
(gdb) 
455     *parameterNames = construct_array(paramNames, parameterCount, TEXTOID,
(gdb) 
460 }
(gdb) 
CreateFunction (pstate=0x10edc88, stmt=0x10c88c8) at functioncmds.c:1065
1065    if (stmt->is_procedure)
(gdb)

DONE!

四、参考资料

N/A

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2676678/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
ITPUB数据库版块资深版主,对Oracle、PostgreSQL有深入研究。

注册时间:2007-12-28

  • 博文量
    1488
  • 访问量
    3925784