How Pg knows what I want to do?

For more inf at : www.leehao.org

Before we discussion, you must know some basic knowledge about lexical and grammatical knowledge, which can give you some basic description of that. Taking "select * from my_first_table where id=2;" as an instance to illustrate how the PostgreSQL do transform.
    从gramm.y 中可以看出 selectstmt 中的targlist参数的 执行操作:
target_el: a_expr AS ColLabel
        $$ = makeNode(ResTarget);
        $$->name = $3;
        $$->indirection = NIL;
        $$->val = (Node *)$1;
        $$->location = @1;
中可以看出, 其返回的是一个ResTarget节点。 如果是select * xxx 此类形式,则$$->val 为:ColumnRef node对象。
    而对于 select * 的形式,对于* (asterisk) 的处理由下面定义给出:
| '*'
    {
        ColumnRef *n = makeNode(ColumnRef);
        n->fields = list_make1(makeNode(A_Star));
        n->location = @1;

        $$ = makeNode(ResTarget);
        $$->name = NULL;
        $$->indirection = NIL;
        $$->val = (Node *)n;
        $$->location = @1;
    }
;
而where clause所定义的action 如下:
where_clause:
    WHERE a_expr { $$ = $2; }
    | /*EMPTY*/ { $$ = NULL; }
;
其后的参数定义为:a_expr, 即对于简单的where 条件[不涉及到cursor此类所生成的where条件]。 而对于 where id =1 此类语句的 a_expr 被定义为:
a_expr:
| a_expr '=' a_expr
{
    $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2);
} //表示是一个普通的表达式。


0: 在函数pg_analyze_and_rewrite中,

1:在parse_analyze 函数中, 首先调用make_parsestate(NULL)函数来生成一个新的ParseState对象 *pstate,然后将pstate中的p_sourcetext设置为查询字符串,而该字符串在后续中被用来进行错误检查,以告诉用户,其所写查询语句错误之处,如果该查询语句错误的情况下。

2:当存在着参数时候,系统将会首先调用parse_fixed_parameters来处理,带有参数的情况。然后 调用transformTopLevelStmt来进行原始查询语法树的处理,最后是调用用户自定义的处理函数来完成原始查询语法树的处理,该部分由用户注册一个回调函数 post_parse_analyze_hook来实现该项功能。

3:在 transformTopLevelStmt 函数中,首先其会判定该语句是否是selectstmt,如果是selectstmt,将其转换为selectstmt,该转换是通过 SelectStmt *stmt = (SelectStmt *) parseTree 语句来完成。当其存在着into子句时候,其会创建一个CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); 节点,并将参数parseTree 赋值给 ctas的query域,并将设置ctas的相应各个域,然后将该函数输入参数stmt中的intoClause域置空。然后使用parseTree = (Node *) ctas;将该函数的输入参数parseTree设置为ctas。

4:调用函数 transformStmt ,来进行查询语句的转换工作。

5: 在函数 transformStmt 中, 首先根据其NodeTag来判定该语句是何种语句,因为根据Node的数据结构可得知,其第一个字节存放的是该节点的类型,即NodeTag. 同样根据其NodeTag的类型不同,其中分类处理的有如下情况:
transformInsertStmt
transformDeleteStmt
transformUpdateStmt
transformValuesClause
transformSelectStmt
transformSetOperationStmt
transformDeclareCursorStmt
transformExplainStmt
transformCreateTableAsStmt

系统中的一个trick是对于其中的selectstmt, insertstmt, wherestmt等数据结构的第一个域都为NodeTag,这样当函数中的参数为Node*类型时候,可以更加第一个参数获得类型后,然后时候(SelectStmt*)等类型转换,可以将其转换为其实际类型。

6:在函数 transformSelectStmt 进行真正的转换,首先其会生成一个 Query* 节点,用来保存其在处理语法树的结果。 在生成Query* 节点后,将其commandType 设置为CMD_SELECT用以表明该语句为select语句, 然后处理with子句,如果存在with子句,由 transformWithClause 函数来处理with子句。 在该函数中如果出现into子句则会抛出异常,因为该into子句已经在 transformTopLevelStmt 中处理(被置为NULL)。在该函数中处理:
transformWithClause (如果存在)
transformFromClause
transformTargetList
markTargetListOrigins
transformWhereClause (with WHERE)
transformWhereClause (with HAVING)
transformSortClause
transformGroupClause
transformDistinctClause
transformDistinctOnClause
transformLimitClause (with OFFSET)
transformLimitClause (with LIMIT)
transformWindowDefinitions
makeFromExpr
parseCheckAggregates
transformLockingClause
assign_query_collations

7: 在函数 transformFromClause 中, 处理每一个from子句 ( foreach(fl, frmList) ), 然后调用 transformFromClauseItem 处理每一个fromClause子句中的每一个元素, 然后将每个元素的namespace作为返回结果传出。 并作为下一步的namespace冲突检查。

8: 在函数 transformFromClauseItem 中, 其处理每一个 FromClause中的每一个 table或者 rangeTable. 因为from 中不仅仅存在这个实体table, view,也可以使其他 select 查询子句。 因此在该函数中会分不同的情况进行处理。 当时一个普通的 table, view等 属于rangeTable时候 [IsA(n, RangeVar)] ,首先将函数的输入参数转换为 RangeVar*类型,然后处理from tables。在进行 range Table处理的过程中需要来看看是否其是一个CTE (公共表达式),如果是一个公共表达 则会调用 transformCTEReference 来处理公共表达式。 如果其不是一个CTE,那么其必然是一个表的情况。 然后调用 transformTableEntry 来进行对表处理。 在这里需要大家注意一个新的数据结构 RangeVar,具体的描述参见源码(src/include/nodes/primnodes.h)。 transformTableEntry 函数返回一个 RangeTblEntry *rte 数据,该数据结构中保存了from语句中的tables各种情况。

9: 在函数 transformTableEntry 中,我们处理from Table的情况, 其由 addRangeTableEntry 函数来完成。 addRangeTableEntry函数将构建一个RangeTblEntry*。

10: 在函数 addRangeTableEntry中, 首先创建一个新的 RangeTblEntry 对象 RangeTblEntry *rte, 该对象用来保存所处理的tables。 然后使用parserOpenTable函数来获得该table的 relation id, 并设置 rte中的相应域,并设置rte中的其他各个域信息, 最后设置 pstat中的p_rtable 域,该域保存了from 语句中的各个表信息。

11:在处理完from 子句后,系统将会处理targetList, 由函数 transformTargetList 来完成对target list的处理。 其首先顺序处理stmt中的targetList的每一个 target :
  foreach(o_target, targetlist)
  {
    ResTarget *res = (ResTarget *) lfirst(o_target);
    ...
    ...
  }    
如果该target 是一个普通的列形式,则其会继续判定是否为 *,如果该target 为 *形式,则会调用ExpandColumnRefStar函数将该*展开,展开成目标表的具体每一个列信息。 若非上述的情况则调用 transformTargetEntry 来将stmt->targetList 转换为 TargetEntry, 并将其返回结果作为 ParseState中的 targetList 结果。 其在 makeTargetEntry 函数中会调用 makeNode(TargetEntry); 来生成具体的 TargetEntry对象。

12: 处理完from子句后,系统将会处理查询语句中存在着where的情况。 系统会调用 transformWhereClause 函数来处理where子句。而对于where子句的处理又分为普通where和带有having子句的情况。

13: 在函数 transformWhereClause中,其会调用 transformExpr 来处理该where子句,并对该子句进行递归处理,由transformExprRecurse 函数完成此递归处理,并将其处理的结果作为 ParseState中 jointree的结果。

14:在函数 transformExprRecurse 中, 其首先获得所要处理对象的类型,然后根据不同的类型进行分段处理。
T_ColumnRef: transformColumnRef
T_ParamRef:transformParamRef
T_A_Const :
T_A_Indirection: transformExprRecurse, transformIndirection
T_A_ArrayExpr: transformArrayExpr
T_TypeCast: transformTypeCast, 而在此类情况下又需要处理其是 A_ArrayExpr 这种情况。
T_CollateClause: transformCollateClause
T_A_Expr : 而对于此情况,又根据其类型做了不同的处理,即该表达式是 operation [普通操作,例如:a = b;], or, and ,not, any ,all, distinct, nullif, of, in等情况。 而这些类型可以被用来修饰了任何一个表达式。
T_FuncCall: transformFuncCall
T_NamedArgExpr:
T_SubLink: transformSubLink
T_CaseExpr: transformCaseExpr
T_RowExpr: transformRowExpr
T_CoalesceExpr: transformCoalesceExpr
T_MinMaxExpr: transformMinMaxExpr
T_XmlExpr: transformXmlExpr
T_XmlSerialize: transformXmlSerialize
T_NullTest:
T_BooleanTest: transformBooleanTest
T_CurrentOfExpr: transformCurrentOfExpr
其他情况;
本例中其为普通的操作,故而由 transformAExprOp 函数来完成相应的操作, 在该函数中 分别处理操作符两边的表达式。 由于本例是 id=2 ,而id为为表的一列,因此在进行递归处理表达时,最后由 transformColumnRef(pstate, (ColumnRef *) expr);来处理为列时的情况。

14: 在函数transformColumnRef中,处理了查询语句中的出现的表中列的情况。在此函数中又处理了不同情况下的情况,对于查询语句中的列,允许如下情况:A, A.B, A.B.C, A.B.C.D, A.* , A.B.*, A.B.C.*这几类情况。因此会根据其输入参数ColumnRef *cref中的cref->fields长度来对不同情况进行处理,当其长度为1,2,3,4 这四种情况进行分类处理。

15: 在处理Where子句后,继续处理其他情况,例如:having, sort, group , offset, limit, window等情况。在处理完所有的子句情况下,根据上述处理的情况设置,Query *qry 中的各个域值。至此,系统处理完整个查询语句,并将 Query *qry最为结果返回。

16: 在完成对原始查询语法树的处理后,系统将调用 pg_rewrite_query(query),来完成对该查询语句的改写工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值