文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

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

一、数据结构

Form_pg_language
plpgsql语言定义结构体



CATALOG(pg_language,2612,LanguageRelationId)
{
    Oid         oid;            
    
    NameData    lanname;
    
    Oid         lanowner BKI_DEFAULT(PGUID);
    
    bool        lanispl BKI_DEFAULT(f);
    
    bool        lanpltrusted BKI_DEFAULT(f);
    
    Oid         lanplcallfoid BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    
    Oid         laninline BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
    
    Oid         lanvalidator BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
#ifdef CATALOG_VARLEN           
    
    aclitem     lanacl[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_language;

typedef FormData_pg_language *Form_pg_language;

ArrayType




typedef struct
{
    //可变的header
    int32       vl_len_;        
    //维度
    int         ndim;           
    //指向数据的偏移量,如为0则表示没有位图
    int32       dataoffset;     
    //元素类型的OID
    Oid         elemtype;       
} ArrayType;

DefElem



typedef struct DefElem
{
  NodeTag   type;
  char     *defnamespace; 
  char     *defname;
  Node     *arg;      
  DefElemAction defaction;  
  int     location;   
} DefElem;

二、源码解读




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))//普通属性
    {
      
      //识别可以同时传递给CREATE FUNCTION和ALTER FUNCTION的选项,并通过out参数返回
      continue;
    }
    else
      elog(ERROR, "option \"%s\" not recognized",
         defel->defname);
  }
  
  if (as_item)
    //必选项:函数体
    *as = (List *) as_item->arg;
  else
  {
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("no function body specified")));
    *as = NIL;        
  }
  if (language_item)
    //必选项:语言
    *language = strVal(language_item->arg);
  else
  {
    ereport(ERROR,
        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
         errmsg("no language specified")));
    *language = NULL;   
  }
  
  //可选项
  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);
}

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;
  
  return true;
duplicate_error:
  ereport(ERROR,
      (errcode(ERRCODE_SYNTAX_ERROR),
       errmsg("conflicting or redundant options"),
       parser_errposition(pstate, defel->location)));
  return false;       
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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 资料下载
  • 历年真题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯