ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(242)- plpgsql(CreateFunction-compute_function_attributes)

PostgreSQL 源码解读(242)- plpgsql(CreateFunction-compute_function_attributes)

原创 PostgreSQL 作者:husthxd 时间:2020-02-21 09:40:27 0 删除 编辑

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

一、数据结构

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;

二、源码解读


/*
 * Dissect the list of options assembled in gram.y into function
 * attributes.
 * 解析集成在gram.y中的选项链表为函数属性
 */
static void
compute_function_attributes(ParseState *pstate,//解析状态结构体
              bool is_procedure,//是否过程?
              List *options,//选项链表(stmt->options)
              List **as,//as语句
              char **language,//语言
              Node **transform,//
              bool *windowfunc_p,//是否窗口函数
              char *volatility_p,//是否易变函数
              bool *strict_p,//是否严格
              bool *security_definer,//安全定义
              bool *leakproof_p,//是否leakproof
              ArrayType **proconfig,//过程配置信息
              float4 *procost,//过程成本
              float4 *prorows,//涉及的行数
              Oid *prosupport,//
              char *parallel_p)
{
  ListCell   *option;//临时变量
  DefElem    *as_item = NULL;
  DefElem    *language_item = NULL;
  DefElem    *transform_item = NULL;
  DefElem    *windowfunc_item = NULL;
  DefElem    *volatility_item = NULL;
  DefElem    *strict_item = NULL;
  DefElem    *security_item = NULL;
  DefElem    *leakproof_item = NULL;
  List     *set_items = NIL;
  DefElem    *cost_item = NULL;
  DefElem    *rows_item = NULL;
  DefElem    *support_item = NULL;
  DefElem    *parallel_item = NULL;
  foreach(option, options)//循环处理
  {
    //获取定义的元素信息
    DefElem    *defel = (DefElem *) lfirst(option);
    if (strcmp(defel->defname, "as") == 0)
    {
      //as
      if (as_item)
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
             errmsg("conflicting or redundant options"),
             parser_errposition(pstate, defel->location)));
      as_item = defel;
    }
    else if (strcmp(defel->defname, "language") == 0)
    {
      //language
      if (language_item)
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
             errmsg("conflicting or redundant options"),
             parser_errposition(pstate, defel->location)));
      language_item = defel;
    }
    else if (strcmp(defel->defname, "transform") == 0)
    {
      //transform
      if (transform_item)
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
             errmsg("conflicting or redundant options"),
             parser_errposition(pstate, defel->location)));
      transform_item = defel;
    }
    else if (strcmp(defel->defname, "window") == 0)
    {
      //窗口函数
      if (windowfunc_item)
        ereport(ERROR,
            (errcode(ERRCODE_SYNTAX_ERROR),
             errmsg("conflicting or redundant options"),
             parser_errposition(pstate, defel->location)));
      if (is_procedure)
        ereport(ERROR,
            (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
             errmsg("invalid attribute in procedure definition"),
             parser_errposition(pstate, defel->location)));
      windowfunc_item = defel;
    }
    else if (compute_common_attribute(pstate,
                      is_procedure,
                      defel,
                      &volatility_item,
                      &strict_item,
                      &security_item,
                      &leakproof_item,
                      &set_items,
                      &cost_item,
                      &rows_item,
                      &support_item,
                      &parallel_item))//普通属性
    {
      /* recognized common option */
      //识别可以同时传递给CREATE FUNCTION和ALTER FUNCTION的选项,并通过out参数返回
      continue;
    }
    else
      elog(ERROR, "option \"%s\" not recognized",
         defel->defname);
  }
  /* process required items */
  if (as_item)
    //必选项:函数体
    *as = (List *) as_item->arg;
  else
  {
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("no function body specified")));
    *as = NIL;        /* keep compiler quiet */
  }
  if (language_item)
    //必选项:语言
    *language = strVal(language_item->arg);
  else
  {
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("no language specified")));
    *language = NULL;   /* keep compiler quiet */
  }
  /* process optional items */
  //可选项
  if (transform_item)
    *transform = transform_item->arg;
  if (windowfunc_item)
    *windowfunc_p = intVal(windowfunc_item->arg);
  if (volatility_item)
    *volatility_p = interpret_func_volatility(volatility_item);
  if (strict_item)
    *strict_p = intVal(strict_item->arg);
  if (security_item)
    *security_definer = intVal(security_item->arg);
  if (leakproof_item)
    *leakproof_p = intVal(leakproof_item->arg);
  if (set_items)
    *proconfig = update_proconfig_value(NULL, set_items);
  if (cost_item)
  {
    *procost = defGetNumeric(cost_item);
    if (*procost <= 0)
      ereport(ERROR,
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
           errmsg("COST must be positive")));
  }
  if (rows_item)
  {
    *prorows = defGetNumeric(rows_item);
    if (*prorows <= 0)
      ereport(ERROR,
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
           errmsg("ROWS must be positive")));
  }
  if (support_item)
    *prosupport = interpret_func_support(support_item);
  if (parallel_item)
    *parallel_p = interpret_func_parallel(parallel_item);
}
/*
 * Recognize one of the options that can be passed to both CREATE
 * FUNCTION and ALTER FUNCTION and return it via one of the out
 * parameters. Returns true if the passed option was recognized. If
 * the out parameter we were going to assign to points to non-NULL,
 * raise a duplicate-clause error.  (We don't try to detect duplicate
 * SET parameters though --- if you're redundant, the last one wins.)
 * 识别可以同时传递给CREATE FUNCTION和ALTER FUNCTION的选项,并通过out参数返回
 */
static bool
compute_common_attribute(ParseState *pstate,
             bool is_procedure,
             DefElem *defel,
             DefElem **volatility_item,
             DefElem **strict_item,
             DefElem **security_item,
             DefElem **leakproof_item,
             List **set_items,
             DefElem **cost_item,
             DefElem **rows_item,
             DefElem **support_item,
             DefElem **parallel_item)
{
  //----------- 逐个判断赋值
  if (strcmp(defel->defname, "volatility") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*volatility_item)
      goto duplicate_error;
    *volatility_item = defel;
  }
  else if (strcmp(defel->defname, "strict") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*strict_item)
      goto duplicate_error;
    *strict_item = defel;
  }
  else if (strcmp(defel->defname, "security") == 0)
  {
    if (*security_item)
      goto duplicate_error;
    *security_item = defel;
  }
  else if (strcmp(defel->defname, "leakproof") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*leakproof_item)
      goto duplicate_error;
    *leakproof_item = defel;
  }
  else if (strcmp(defel->defname, "set") == 0)
  {
    *set_items = lappend(*set_items, defel->arg);
  }
  else if (strcmp(defel->defname, "cost") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*cost_item)
      goto duplicate_error;
    *cost_item = defel;
  }
  else if (strcmp(defel->defname, "rows") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*rows_item)
      goto duplicate_error;
    *rows_item = defel;
  }
  else if (strcmp(defel->defname, "support") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*support_item)
      goto duplicate_error;
    *support_item = defel;
  }
  else if (strcmp(defel->defname, "parallel") == 0)
  {
    if (is_procedure)
      goto procedure_error;
    if (*parallel_item)
      goto duplicate_error;
    *parallel_item = defel;
  }
  else
    return false;
  /* Recognized an option */
  return true;
duplicate_error:
  ereport(ERROR,
      (errcode(ERRCODE_SYNTAX_ERROR),
       errmsg("conflicting or redundant options"),
       parser_errposition(pstate, defel->location)));
  return false;       /* keep compiler quiet */
procedure_error:
  ereport(ERROR,
      (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
       errmsg("invalid attribute in procedure definition"),
       parser_errposition(pstate, defel->location)));
  return false;
}

三、跟踪分析

测试脚本


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 compute_function_attributes
Breakpoint 1 at 0x6702b7: file functioncmds.c, line 711.
(gdb) c
Continuing.
Breakpoint 1, compute_function_attributes (pstate=0x1dd4c88, is_procedure=false, 
    options=0x1daf7e8, as=0x7ffd231851d8, language=0x7ffd23185240, 
    transform=0x7ffd23185238, windowfunc_p=0x7ffd231851ff, volatility_p=0x7ffd231851fb "v", 
    strict_p=0x7ffd231851fe, security_definer=0x7ffd231851fd, leakproof_p=0x7ffd231851fc, 
    proconfig=0x7ffd231851f0, procost=0x7ffd231851ec, prorows=0x7ffd231851e8, 
    prosupport=0x7ffd231851e4, parallel_p=0x7ffd231851d7 "u") at functioncmds.c:711
711   DefElem    *as_item = NULL;

输入参数

(gdb) p *pstate
$1 = {parentParseState = 0x0, 
  p_sourcetext = 0x1daded8 "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) p is_procedure
$2 = false

SQL语句的选项#1(as语句)

(gdb) p *options
$3 = {type = T_List, length = 2, head = 0x1daf7c0, tail = 0x1daf8a0}
(gdb) p *(Node *)options->head->data.ptr_value
$4 = {type = T_DefElem}
(gdb) p *(DefElem *)options->head->data.ptr_value
$5 = {type = T_DefElem, defnamespace = 0x0, defname = 0xbbf727 "as", arg = 0x1daf730, 
  defaction = DEFELEM_UNSPEC, location = 134}
(gdb) set $defelem=(DefElem *)options->head->data.ptr_value
(gdb) p $defelem->arg
$6 = (Node *) 0x1daf730
(gdb) p *(Node *)$defelem->arg
$7 = {type = T_List}
(gdb) p *(List *)$defelem->arg
$8 = {type = T_List, length = 1, head = 0x1daf708, tail = 0x1daf708}
(gdb) set $arg=(List *)$defelem->arg
(gdb) p *(Node *)$arg->head->data.ptr_value
$9 = {type = T_String}
(gdb) p *(Value *)$arg->head->data.ptr_value
$11 = {type = T_String, val = {ival = 31126984, 
    str = 0x1daf5c8 "\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"}}

SQL语句的选项#2(语言)

(gdb) set $defelem2=(DefElem *)options->head->next->data.ptr_value
(gdb) p *$defelem2
$13 = {type = T_DefElem, defnamespace = 0x0, defname = 0xbbfcbe "language", 
  arg = 0x1daf820, defaction = DEFELEM_UNSPEC, location = 298}
(gdb) p *$defelem2->arg
$14 = {type = T_String}
(gdb) p *(Value *)$defelem2->arg
$15 = {type = T_String, val = {ival = 31126952, str = 0x1daf5a8 "plpgsql"}}
(gdb)

提取as_item和language_item

(gdb) n
712   DefElem    *language_item = NULL;
(gdb) n
713   DefElem    *transform_item = NULL;
(gdb) 
714   DefElem    *windowfunc_item = NULL;
(gdb) 
715   DefElem    *volatility_item = NULL;
(gdb) 
716   DefElem    *strict_item = NULL;
(gdb) 
717   DefElem    *security_item = NULL;
(gdb) 
718   DefElem    *leakproof_item = NULL;
(gdb) 
719   List     *set_items = NIL;
(gdb) 
720   DefElem    *cost_item = NULL;
(gdb) 
721   DefElem    *rows_item = NULL;
(gdb) 
722   DefElem    *support_item = NULL;
(gdb) 
723   DefElem    *parallel_item = NULL;
(gdb) 
725   foreach(option, options)
(gdb) 
727     DefElem    *defel = (DefElem *) lfirst(option);
(gdb) 
729     if (strcmp(defel->defname, "as") == 0)
(gdb) 
731       if (as_item)
(gdb) 
736       as_item = defel;
(gdb) 
725   foreach(option, options)
(gdb) p *as_item
$16 = {type = T_DefElem, defnamespace = 0x0, defname = 0xbbf727 "as", arg = 0x1daf730, 
  defaction = DEFELEM_UNSPEC, location = 134}
(gdb) n
727     DefElem    *defel = (DefElem *) lfirst(option);
(gdb) 
729     if (strcmp(defel->defname, "as") == 0)
(gdb) 
738     else if (strcmp(defel->defname, "language") == 0)
(gdb) 
740       if (language_item)
(gdb) 
745       language_item = defel;
(gdb) 
725   foreach(option, options)
(gdb) p *language_item
$17 = {type = T_DefElem, defnamespace = 0x0, defname = 0xbbfcbe "language", 
  arg = 0x1daf820, defaction = DEFELEM_UNSPEC, location = 298}
(gdb) n
792   if (as_item)

提取item中的arg

(gdb) n
793     *as = (List *) as_item->arg;
(gdb) 
802   if (language_item)
(gdb) p *as
$18 = (List *) 0x1daf730
(gdb) n
803     *language = strVal(language_item->arg);
(gdb) 
813   if (transform_item)
(gdb) p *language
$19 = 0x1daf5a8 "plpgsql"
(gdb) n
815   if (windowfunc_item)
(gdb) 
817   if (volatility_item)
(gdb) 
819   if (strict_item)
(gdb) 
821   if (security_item)
(gdb) 
823   if (leakproof_item)
(gdb) 
825   if (set_items)
(gdb) 
827   if (cost_item)
(gdb) 
835   if (rows_item)
(gdb) 
843   if (support_item)
(gdb) 
845   if (parallel_item)
(gdb) 
847 }
(gdb) 
CreateFunction (pstate=0x1dd4c88, stmt=0x1daf8c8) at functioncmds.c:989
989   languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));

DONE!

四、参考资料

N/A

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

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

注册时间:2007-12-28

  • 博文量
    1490
  • 访问量
    3927676