PostgreSQL 源码解读(242)- plpgsql(CreateFunction-compute_function_attributes)
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本节简单介绍了PostgreSQL创建函数的过程,其实现函数是CreateFunction。一、数据结构Form_pg_languageplpgsql语言定义结构体/* ---------------
千家信息网最后更新 2026年11月01日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 = falseSQL语句的选项#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
函数
结构
语句
语言
过程
对象
信息
元素
参数
属性
数据
项链
可变
跟踪
源码
普通
安全
位图
单位
参考资料
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
互联网科技
百单网互联网科技有限公司
互联网科技保险
科技类互联网
大白互联网科技
福州新华互联网科技学校 知乎
南山科技园互联网公司有哪些
互联网科技有哪些公司
荆州市昕露互联网络科技有限公司
花神(南京)互联网科技有限公司
华夏互联网科技业
坤平互联网科技
互联网与数字科技关系
神州科技互联网有限公司
云鲸互联网络科技有限公司
互联网科技新闻
众联互联网络科技
数字互联网科技有限公司
青岛易易互联网科技
数字科技和互联网金融哪个好
伟宏互联网科技
科技互联网政务服务
艾特科技互联网
凡科互联网科技
华脉科技工业互联网产品
现代科技和互联网有关系吗
广州恒生互联网科技有限
玉溪新华互联网科技好不好
徐州市互联网科技有限公司
美国互联网科技公司