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
函数
结构
语句
语言
过程
对象
信息
元素
参数
属性
数据
项链
可变
跟踪
源码
普通
安全
位图
单位
参考资料
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
区块链p2p网络技术
海量数据包括哪些数据库
电话卡的服务器地址是什么意思
知道软件开发商可以找到软件吗
网络安全法第12款
软件开发费用人工
苏州应用软件开发怎么样
重庆大学数据库实验六
百果园软件开发有限公司
惠州数据库培训机构
存储保护情况 网络安全
软件开发公司保密协议
手机怎么打开access数据库
网络安全主题演讲稿300字
安卓软件开发工具和技术
开源ssl vpn服务器
网络安全大赛的名人
莫斯科服务器到中国
网络安全情报接入标准
为什么服务器无法下载pdf
移动信息网络安全汇报
网络安全单位做好网络安全宣传
湖南三维人口管理系统软件开发
小孩子学软件开发
青浦区数据库安防系统厂家制度
126邮箱服务器端口
鼎兴网络技术广州有限公司
软件开发如何跑销路
浪潮信息通用服务器
程序设计网络安全系列