千家信息网

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

发表于:2025-02-04 作者:千家信息网编辑
千家信息网最后更新 2025年02月04日,本节简单介绍了PostgreSQL创建函数的过程,其实现函数是CreateFunction。一、数据结构Form_pg_languageplpgsql语言定义结构体/* ---------------
千家信息网最后更新 2025年02月04日PostgreSQL 源码解读(242)- plpgsql(CreateFunction-compute_function_attributes)

本节简单介绍了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 voidcompute_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,                      ¶llel_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 boolcompute_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$$declarebegin  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_attributesBreakpoint 1 at 0x6702b7: file functioncmds.c, line 711.(gdb) cContinuing.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:711711   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) n712   DefElem    *language_item = NULL;(gdb) n713   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) n727     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) n792   if (as_item)

提取item中的arg

(gdb) n793     *as = (List *) as_item->arg;(gdb) 802   if (language_item)(gdb) p *as$18 = (List *) 0x1daf730(gdb) n803     *language = strVal(language_item->arg);(gdb) 813   if (transform_item)(gdb) p *language$19 = 0x1daf5a8 "plpgsql"(gdb) n815   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:989989   languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language));

DONE!

四、参考资料

N/A

0