PostgreSQL 源码解读(242)- plpgsql(CreateFunction-compute_function_attributes)
发表于:2025-11-11 作者:千家信息网编辑
千家信息网最后更新 2025年11月11日,本节简单介绍了PostgreSQL创建函数的过程,其实现函数是CreateFunction。一、数据结构Form_pg_languageplpgsql语言定义结构体/* ---------------
千家信息网最后更新 2025年11月11日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安全错误
数据库的锁怎样保障安全
数据库表结构有几种
安全狗服务器版4.2
河南智能语音服务器
政务类软件开发
数据库主键和外键的作用6
软件开发相关股票
山西互联网养老软件开发专业制作
计算机网络技术行情怎么样
公安机关网络安全协议
万方数据库database
计算机主机软件开发
北京云丁网络技术有限公司
中国软件开发行业
河北奕腾互联网科技有限公司
上海做软件开发30岁工资
网络安全创新引领未来动图
常见软件开发中的面谈问题
服务器的固态硬盘怎么使用
dh数据库百科
网络安全靠大家相关内容
数据库大致可分为
通过内网远程登服务器
酒泉市网络安全培训
android数据库创建两个表
平谷区推广软件开发热线
软件开发公司招聘条件
甲骨文免费云服务器矿创
java面试数据库问题
ip代理服务器如何搭建
java数据库数据字典