PostgreSQL——语义分析3——目标属性及Where子句
Posted weixin_47373497
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PostgreSQL——语义分析3——目标属性及Where子句相关的知识,希望对你有一定的参考价值。
2021SC@SDUSC
目录
概述
我负责的PostgreSQL代码部分:查询的编译与执行
此篇博客分析内容:目标属性的语义分析
上篇博客,我分析了from子句的语义分析,大体了解了PostgreSQL是如何处理范围表以及表与表之间的连接,此片博客接着向下分析,在确定了范围表后,又该如何取得目标属性呢?
目标属性的语义分析
处理目标属性的入口函数是TransformTargetList函数,TransformTargetList函数通过调用函数TransformTargetEntry来处理分析树的目标属性中的每一项。对于目标属性中任何一项都会调用函数makeTargetentry创建TargetEntry结构体用来存储和组织。TransformTargetList函数返回p_target,而再通过查询语义分析的函数transformSelectStmt将直接把transformTargetList函数的返回值作为目标属性赋值给targetList字段
TransformTargetList函数
ResTarget结构体
typedef struct ResTarget
NodeTag type; //节点类型
char *name; //列名或者null
List *indirection; //目标列存储下标,域名,*或者NIL
Node *val; //要计算的表达式
int location; //token的位置,-1表示未知
ResTarget;
transformTargetList函数是处理目标属性的入口函数,通过调用transformTargetEntry来处理目标属性。transformTargetList函数的参数由两个ParseState(在上上篇博客PostgreSQL–语义分析-ParseState分析过)和分析树的targetList字段指向的链表(在上上篇博客PostgreSQL–语义分析-Query分析过)
List *
transformTargetList(ParseState *pstate, List *targetlist,
ParseExprKind exprKind)
List *p_target = NIL;//准备空的链表p_target ,为了以后存储TargetEntry结构体
bool expand_star;//是否要展开查询表达式里包含的*(即是否把*对应的目标属性全部列出)
ListCell *o_target;//临时变量存储targetlist里面的每一个节点
//逐一取出targetlist里面的每一个节点
foreach(o_target, targetlist)
ResTarget *res = (ResTarget *) lfirst(o_target);//取出每个节点的RESTarget结构体
if (expand_star)//判断有无展开的*
//val字段:要计算的表达式
if (IsA(res->val, ColumnRef))//判断val字段中是否存储列名
ColumnRef *cref = (ColumnRef *) res->val;//从val字段中获取列名
if (IsA(llast(cref->fields), A_Star))//判断ResTarget的Val字段中是否是一个*
//如果是一个*,则调用 ExpandColumnRefStar函数,生成TargetEntry结构体,然后加入p_target中
p_target = list_concat(p_target,
ExpandColumnRefStar(pstate,
cref,
true));
continue;
ExpandColumnRefStar函数的作用:用来处理select中包含的情况,将展开成目标表的具体每一个列信息
A_Indirection结构体
typedef struct A_Indirection
NodeTag type;//节点类型
Node *arg; //存储被选择的目标列名
List *indirection;//存储的是例如A_Indices节点(具体A_Indices结构体见下), A_Star节点等。用于select语句包含数组的情况
A_Indirection;
PostgreSQL允许将字段定义成变长的多维数组。 数组类型可以是任何基本类型或用户定义类型,枚举类型或复合类型。 目前还不支持域的数组。
SELECT pay_by_quarter[3] FROM sal_emp; //select语句中包含数组的情况
SELECT schedule[1:2][2] FROM sal_emp WHERE name = ‘Bill’;//select语句中包含切片的情况
else if (IsA(res->val, A_Indirection))//判断val字段有无数组类型
A_Indirection *ind = (A_Indirection *) res->val;//如果有数组的话,则会把val字段的数组类型取出并暂存
if (IsA(llast(ind->indirection), A_Star))//因为A_Indirection结构体中存储有多个不同数据类型的节点,其中包含A_Star类型,判断 A_Indirection是否包含有A_Star数组类型
//ExpandIndirectionStar函数展开代指数组类型的*
p_target = list_concat(p_target,
ExpandIndirectionStar(pstate,
ind,
true,
exprKind));
continue;
A_Indices结构体
typedef struct A_Indices
NodeTag type;//节点类型
bool is_slice; //判断是否是切片
Node *lidx; //如果是切片,则存储切片的下边界
Node *uidx; //如果是切片,则存储切片的下标或者上边界
A_Indices;
//如果ResTarget的val字段不是一个*则调用transformTargetEntry函数为它生成Target Entry结构体并加入到p_target链表中。 transformTargetEntry函数会调用transformExpr函数为val字段中的数据类型生成表达式结构
p_target = lappend(p_target,
transformTargetEntry(pstate,
res->val,
NULL,
exprKind,
res->name,
false));
//TransformTargetList函数返回p_target,而再通过查询语义分析的函数transformSelectStmt将直接把transformTargetList函数的返回值作为目标属性赋值给targetList字段
return p_target;
TransformTargetEntry函数
对于每一个目标属性生成TargetEntry结构体
transformTargetEntry(ParseState *pstate,
Node *node,
Node *expr,
ParseExprKind exprKind,
char *colname,
bool resjunk)
if (expr == NULL)//判断存储的表达式是否为空
if (exprKind == EXPR_KIND_UPDATE_SOURCE && IsA(node, SetToDefault))//判断表达式是否为SetToDefault类型
expr = node;//如果是默认类型的话,则把表达式存储
else
expr = transformExpr(pstate, node, exprKind);//否则调用 transformExpr函数,进一步在transformExpr函数中报错
if (colname == NULL && !resjunk)//判断列名是否为空或者是否为垃圾属性
colname = FigureColname(node);//为它生成一个合适的列名
//调用makeTargetEntry为每一个目标属性生成Target Entry结构体,最后通过TransformTargetList函数加入到p_targetlist
return makeTargetEntry((Expr *) expr,
(AttrNumber) pstate->p_next_resno++,
colname,
resjunk);
makeTargetEntry函数
makeTargetEntry函数由transformTargetEntry调用用来创建结构体:TargetEntry。 makeTargetEntry函数的作用是将一个ResTarget结构体的链表转换成一个TargetEntry结构体的链表,而每一个TargetEntry表示查询树中的一个目标属性
TargetEntry *
makeTargetEntry(Expr *expr,
AttrNumber resno,
char *resname,
bool resjunk)
TargetEntry *tle = makeNode(TargetEntry);//创建并初始化节点
tle->expr = expr;//把传入makeTargetEntry函数的表达式赋值给节点
tle->resno = resno;//把传入makeTargetEntry函数的属性编号赋值给节点
tle->resname = resname;把传入makeTargetEntry函数的列名赋值给节点
//所有属性的初始值都默认为0,减少发生错误的概率
tle->ressortgroupref = 0;
tle->resorigtbl = InvalidOid;
tle->resorigcol = 0;
tle->resjunk = resjunk;//与传入的resjunk保持一致
return tle;//返回构造好的结构体
TargetEntry结构体
严格来说TargetEntry并不是一个表达式,因为它不能被表达式计算函数处理,但PostgreSQL仍将它视为一种表达式,因为把目标属性作为一个表达式树进行处理会很方便
typedef struct TargetEntry
Expr xpr; //表达式头部
Expr *expr; //目标属性中需要计算的表达式
AttrNumber resno; //属性编号,在select目标属性中resno对应了属性出现的位置
char *resname; //属性名
Index ressortgroupref; //被ORDER BY和GROUP BY子句引用时使用,0值表示没有被ORDER BY和GROUP BY子句引用,正值表示被引用
Oid resorigtbl; //目标属性所属于的源表的OID
AttrNumber resorigcol; //属性在源表中的属性号
bool resjunk; //表示该属性是否是一个junk属性,如果为真表示这个属性是一个工作属性,在输出结果时应该去除。
TargetEntry;
TransformExpr函数
当ResTarget val字段没有*则为ResTarget的val字段中的数据类型生成表达式结构
transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Node *result;
ParseExprKind sv_expr_kind;
Assert(exprKind != EXPR_KIND_NONE);
sv_expr_kind = pstate->p_expr_kind;//保存原来的表达式类型
pstate->p_expr_kind = exprKind;//把传入的解析后的表达式类型传入pstate,用于生成对应结构的表达式节点
//transformExprRecurse函数根据传入的不同类型的表达式,生成不同类型的表达式节点
result = transformExprRecurse(pstate, expr);
//还原原有的pstate表达式类型
pstate->p_expr_kind = sv_expr_kind;
//返回生成的表达式
return result;
Var结构体
typedef struct Var
Expr xpr;
Index varno; //变量属性所在的表在范围表中的编号
AttrNumber varattno; //属性编号
Oid vartype; //该属性数据类型在pg_type中的OID
int32 vartypmod; //该属性对应的pg_attribute元组的typmod值
Oid varcollid; //用于校对的OID
Index varlevelsup; //用于子查询中引用外层表的变量,表示子查询的层数
Index varnoold; //varno的原始值,用于调试信息
AttrNumber varoattno; //varoattno的原始值,用于调试
int location; //符号出现的位置
Var;
Where子句的语义分析
TransformWhereClause函数
在函数 transformWhereClause中,其会调用 transformExpr 来处理该where子句,并对该子句进行递归处理,由transformExprRecurse 函数完成此递归处理,并将其处理的结果作为 ParseState中 jointree的结果。
transformWhereClause(ParseState *pstate, Node *clause,
ParseExprKind exprKind, const char *constructName)
Node *qual;
if (clause == NULL)//判断where子句是否为空
return NULL;
//调用 transformExpr函数将分析树的whereClause字段表示的where子句转换为一棵表达式树,然后将ParseState的P_joinlist字段所指向的链表以及从where子句得到的表达式树包装成一个fromExpr结构体存入查询树的jointree中用以表示连接树
qual = transformExpr(pstate, clause, exprKind);
return qual;//返回表达式
语义分析部分总结
截止此篇博客,我们已经分析完了PostgreSQL中select语句中三个主要子句:select,from ,where的语义分析过程
select子句 | 解析分析树中的目标属性表达式并生成TargetEntry结构体存储目标属性 |
---|---|
from子句 | 解析分析树中的from表达式,根据from子句中出现的表,视图,子查询,函数或者连接表达式生成范围表 |
where子句 | 解析分析树中的where Clause字段,转换为一棵表达式树然后包装成FromExpr结构存入查询树的jointree |
以上是关于PostgreSQL——语义分析3——目标属性及Where子句的主要内容,如果未能解决你的问题,请参考以下文章