一般PG数据类型不会带括号,varchar和numeric是比较特殊的基本类型,后面可以带括号指定长度或精度。
本篇回答下面两个问题:
varchar和varchar(2)区别:
TypeName->typmods
链表上挂一个A_Const记录长度 限制。DDL执行后会记录到表结构中。Const->consttypmod
会记录具体限制的值(例如consttypmod=6表示长度限制为2,因为mod要减去varheadsize=4后使用)。consttypmod!=-1
会构造表达式计算,调用varchar函数对Const进行验证,如果长度超出限制直接报错。(如何调入varchar函数?表达式执行框架,参考《Postgresql源码(85)》)create table v1(c1 varchar, c2 varchar(2), c3 varchar(4));
varchar和varchar(2)的区别主要是在:
TypeName->typmods
链表会挂一个A_Const记录括号内给的长度。transform函数不会处理建表时的varchar类型,语义解析生成的Query树结构:
优化器无处理。
insert into v1 values ('12345', '12', '1234');
结果
InsertStmt
relation
selectStmt
type = T_SelectStmt
valuesLists = 0x2a26170
{ptr_value = 0x2a25f80}
{ptr_value = 0x2a25f50}
{type = T_A_Const, fval = {type = T_String, fval = 0x2a25f38 "12345"}}
{ptr_value = 0x2a25fe8}
{type = T_A_Const, fval = {type = T_String, fval = 0x2a25f38 "12"}}
{ptr_value = 0x2a26030}
{type = T_A_Const, fval = {type = T_String, fval = 0x2a25f38 "1234"}}
语义分析结果,数据在targetList中包装成三个Const
Query
rtable = 0x2a26d60
{ptr_value = 0x2a26820}
{type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16489, relkind = 114 'r', ...}
jointree = 0x2b11870
{type = T_FromExpr, fromlist = 0x0, quals = 0x0}
targetList = 0x2b11780
{ptr_value = 0x2b11730}
{xpr = {type = T_TargetEntry}, expr = 0x2a27118, resno = 1, resname = 0x2a26e50 "c1"}
expr =
Const =
{xpr = {type = T_Const},
consttype = 1043,
consttypmod = -1,
constcollid = 100,
constlen = -1,
constvalue = 44198808,
constisnull = false,
constbyval = false,
location = 23}
{ptr_value = 0x2b117d0}
{xpr = {type = T_TargetEntry}, expr = 0x2a27330, resno = 2, resname = 0x2a26f58 "c2"}
expr =
Const =
{xpr = {type = T_Const},
consttype = 1043,
consttypmod = 6, // 6 - 4(varheadersize) = 2 就是长度限制
constcollid = 100,
constlen = -1,
constvalue = 45161664,
constisnull = false,
constbyval = false,
location = -1}
{ptr_value = 0x2b11820}
{xpr = {type = T_TargetEntry}, expr = 0x2b116e0, resno = 3, resname = 0x2a26fc0 "c3"}
expr =
Const =
{xpr = {type = T_Const},
consttype = 1043,
consttypmod = 8,
constcollid = 100,
constlen = -1,
constvalue = 45162248,
constisnull = false,
constbyval = false,
location = -1}
优化器会把T_FuncExpr类型的Const执行掉
pg_plan_queries
pg_plan_query
planner
standard_planner
subquery_planner
preprocess_expression
eval_const_expressions
eval_const_expressions_mutator
expression_tree_mutator_impl
eval_const_expressions_mutator
expression_tree_mutator_impl
eval_const_expressions_mutator
simplify_function
evaluate_function
evaluate_expr
ExecEvalExprSwitchContext
ExecInterpExprStillValid
ExecInterpExpr
函数ExecInterpExpr开始执行
EEO_CASE(EEOP_FUNCEXPR_STRICT)
执行需要数据结构p op->d
p op->d.func
finfo = 0x2b1cd98,
FmgrInfo = {fn_addr = 0xb100d8 <varchar>, fn_oid = 669, fn_nargs = 3, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x2b1c9e0, fn_expr = 0x2b121a8}
fcinfo_data = 0x2b1cde8,
FunctionCallInfo = {flinfo = 0x2b1cd98, context = 0x0, resultinfo = 0x0, fncollation = 100, isnull = false, nargs = 3, args = 0x2b1ce08}
args[0] = {value = 44200488, isnull = false}
args[1] = {value = 6, isnull = false}
args[2] = {value = 0, isnull = false}
fn_addr = 0xb100d8 <varchar>,
nargs = 3}
执行过程
ExecInterpExpr
...
...
EEO_CASE(EEOP_FUNCEXPR_STRICT)
{
FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
NullableDatum *args = fcinfo->args;
int nargs = op->d.func.nargs;
Datum d;
// nargs = 3
/* strict function, so check for NULL args */
for (int argno = 0; argno < nargs; argno++)
{
if (args[argno].isnull)
{
*op->resnull = true;
goto strictfail;
}
}
fcinfo->isnull = false;
d = op->d.func.fn_addr(fcinfo);
*op->resvalue = d;
*op->resnull = fcinfo->isnull;
strictfail:
EEO_NEXT();
}
进入varchar函数,3个入参:
VarChar *source = {vl_len_ = "\030\000\000", vl_dat = 0x2a2722c "12"}
int32 typmod = 6
bool isExplicit = false
当前长度:
len = VARSIZE_ANY_EXHDR(source);
len = 2
最大长度:
maxlen = typmod - VARHDRSZ;
2 = 6 - 4
长度合适可以直接返回,如果长度不满足,在varchar函数中报错。
最终优化器输出:可以看到TargetEntry只有常量Const了,函数Const被优化器处理掉了。
PlannedStmt
planTree = 0x2a26930
ModifyTable
lefttree = 0x2b12c48
Result
targetlist = 0x2b12d28
{ptr_value = 0x2b12cd8}
TargetEntry
expr = Const{consttype = 1043, consttypmod = -1, constcollid = 100, constlen = -1, constvalue = 44200296}
resno = 1,
resname = 0x2a26e50 "c1"
{ptr_value = 0x2b12d78}
TargetEntry
expr = Const{consttype = 1043, consttypmod = 6, constcollid = 100, constlen = -1, constvalue = 45163000}
resno = 2,
resname = 0x2a26f58 "c2"
{ptr_value = 0x2b12dc8}
TargetEntry
expr = Const{consttype = 1043, consttypmod = 8, constcollid = 100, constlen = -1, constvalue = 45163584}
resno = 3,
resname = 0x2a26fc0 "c3"
lefttree = 0x0,
righttree = 0x0
righttree = 0x0
resultRelations = 0x2b12b08
rtable = 0x2b12ea8
{ptr_value = 0x2a26ba0}
{type = T_RangeTblEntry, rtekind = RTE_RELATION, relid = 16489, relkind = 114 'r'}
{ptr_value = 0x2b12f48}
{type = T_RangeTblEntry, rtekind = RTE_RESULT, relid = 0, relkind = 0 '\000'}
resultRelations = 0x2b13058
relationOids = 0x2b12ef8
paramExecTypes = 0x2b12b58