对postgres.cpp的解析

源码链接

https://www.gitlink.org.cn/Eao3piq4e/openGauss-server/tree/master/src%2Fgausskernel%2Fprocess%2Ftcop%2Fpostgres.cpp

概述

         在我之前的这篇对查询分析模块的总结性博客:查询分析模块总结,我提到的pg_parse_query()、pg_analyze_and_rewrite() 和 exec_simple_query() 函数均在 postgres.cpp 文件中,关于这几个函数的作用,我想之前这篇博客的一张图就能很好地说明了:

可以看到,exec_simple_query() 是主调函数,pg_parse_query() 利用查询语句得到分析树链表,pg_analyze_and_rewrite() 则处理分析树链表并返回重写后的查询树链表。实际上,当pg_analyze_and_rewrite() 接收到要处理分析树链表的任务时,它麾下的 parse_analyze() 就负责将分析树链表转化为查询树链表,之后的重写工作自然就落到了 pg_rewrite_query() 肩上。在这篇博客,我要介绍的的正是 pg_rewrite_query() 所做的工作。

解析

pg_rewrite_query()

//代码清单1
//src/gausskernel/process/tcop/postgres.cpp
static List* pg_rewrite_query(Query* query)
{
    List* querytree_list = NIL;
······
    if (query->commandType == CMD_UTILITY) {
#ifdef ENABLE_MULTIPLE_NODES
        if (IsA(query->utilityStmt, CreateTableAsStmt)) 
        {
            /*
             * CREATE TABLE AS SELECT and SELECT INTO are rewritten so that the
             * target table is created first. The SELECT query is then transformed
             * into an INSERT INTO statement
             */
             /* handle materilized view. */
            CreateTableAsStmt *stmt = (CreateTableAsStmt *)query->utilityStmt;
            if (IS_PGXC_COORDINATOR && stmt->relkind == OBJECT_MATVIEW) 
            {
                /* Check if OK to create matview */
                check_basetable((Query *)stmt->query, true, stmt->into->ivm);
                /* Check for select privilege */
                (void)ExecCheckRTPerms(query->rtable, true);
······
            }
             
            if (stmt->relkind == OBJECT_MATVIEW && IS_PGXC_DATANODE)
            {
                querytree_list = list_make1(query);
            }
            else
            {
                /*
                 * CREATE TABLE AS SELECT and SELECT INTO are rewritten so that the
                 * target table is created first. The SELECT query is then transformed
                 * into an INSERT INTO statement
                 */
                querytree_list = QueryRewriteCTAS(query);
            }
        }
        else if (IsA(query->utilityStmt, CopyStmt))
        {
            querytree_list = query_rewrite_copy(query);
        }
        else if(IsA(query->utilityStmt, RefreshMatViewStmt))
        {
            RefreshMatViewStmt *stmt = (RefreshMatViewStmt *)query->utilityStmt;
            if (!stmt->incremental && !isIncMatView(stmt->relation)) 
            {
                querytree_list = QueryRewriteRefresh(query);
            }
            else
            {
                querytree_list = list_make1(query);
            }
        }
        else if (IsA(query->utilityStmt, AlterTableStmt)) 
        {
            querytree_list = query_rewrite_alter_table(query);
        }
        else
        {
            /* don't rewrite utilities, just dump them into result list */
            querytree_list = list_make1(query);
        }
#else
        if (IsA(query->utilityStmt, CreateTableAsStmt))
        {
            CreateTableAsStmt *stmt = (CreateTableAsStmt *)query->utilityStmt;
            if (stmt->relkind == OBJECT_MATVIEW) 
            {
 
                /* Check if OK to create matview */
                check_basetable((Query *)stmt->query, true, stmt->into->ivm);
                /* Check for select privilege */
                (void)ExecCheckRTPerms(query->rtable, true);
 
                if (stmt->into && stmt->into->ivm) 
                {
                    check_matview_op_supported(stmt);
                }
 
                querytree_list = list_make1(query);
            }
            else
            {
                querytree_list = QueryRewriteCTAS(query);
            }
        }
        else
        {
            querytree_list = list_make1(query);
        }
#endif
    }
    else 
    {
        /* rewrite regular queries */
        querytree_list = QueryRewrite(query);
    }
······
    return querytree_list;
}

        这个函数分支挺多的,总的分支有两条,一条用于由功能性语句构造的查询树(有些分支是可以重写的),另一条则用于所有分支均可重写的查询树,比如由 SELECT 语句构造的查询树。对于由非功能性语句构造出来的查询树,在代码清单1第99行将调用 QueryRewrite() 来对查询树进行重写操作,而对于由功能性语句构造出来的查询树,即由建表、创建用户、备份等功能性语句构造出来的查询树,就会出现很多分支了,这些分支在代码清单1第8~94行十分清楚。

        代码清单1中很多语句块的判断条件用到了 IsA() 函数,在先前的博客中我也讲解了它的作用,它被用来判断语句的类型,源码如下:

//代码清单2
//src/include/nodes/nodes.h
#define IsA(nodeptr, _type_) (nodeTag(nodeptr) == T_##_type_)

这个宏定义后半部分的 "##" 是起连接作用的,是将 _type_ 连接到 T_ 后面的,至于 nodeTag() 函数的用处我在这篇博客已经讲过了:对analyze.cpp的解析(一),在此不做赘述。

        那么接下来讲回代码清单1对于由功能性语句构造出来的查询树的操作分支,在 ENABLE_MULTIPLE_NODES 模式下,又分为建表语句( CreateTableAsStmt )、备份语句( CopyStmt )、物化视图刷新语句( RefreshMatViewStmt )、更改表语句( AlterTableStmt ),在其它模式下,则只有建表语句,接下来继续分析。

        由代码清单1第27~39和第70~88行,无论是不是在 ENABLE_MULTIPLE_NODES 模式下,如果查询树由建表语句构造而来,那么只有两种选择,一是利用 list_make1() 不做修改地将最终返回的链表设置为原先的查询树链表,而是利用 QueryRewriteCTAS() 对由 CREATE TABLE AS SELECT 和 SELECT INTO 语句构造的查询树重写为 INSERT INTO 语句,这三种语句的功能都是从旧表中拷贝一些数据到新表中,推荐这个网站进行浏览:SQL SELECT INTO 语句 | 菜鸟教程

        在 ENABLE_MULTIPLE_NODES 模式下,如果等待处理的查询树由备份语句构造而来,那么就调用 query_rewrite_copy() 进行重写;如果由物化视图刷新语句构造而来,就调用 QueryRewriteRefresh() 或者 list_make1() ;如果由更改表语句构造而来,那么就调用query_rewrite_alter_table() 进行重写,最后的一种情况就是不对原先的查询树重写,而是将它放入结果链表中去。在其它模式下,流程没有区别。

总结

        这个函数内部的分支其实看代码就可以看出来,其结构是十分的清晰明了的。这个函数的代码结构确实简单,但是它起到的作用仍然是十分大的,比如它可以支持更多的语句,我们可以将CREATE TABLE AS SELECT 和 SELECT INTO 语句重写为 INSERT INTO 语句,这样就能够支持更多的语句类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔走的月光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值