本节简单介绍了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 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,
¶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 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;
/* 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
$$
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