一、node节点的定义

源代码路径postgresql-9.2.3/src/include/nodes/nodes.h

在查询解析SQL的查询部分,要用到大量的结构体,许多函数处理的逻辑类似,就是传入的结构体不同,为了处理这个问题,pg采用了一个基础结构体struct node,其他结构体的第一个字段与node的相同。通过这个字段来标识不同的结构体,而又同时能统一接口函数。

pg主要采用c实现,因此没有采用多态。(顺带说一句,之前一直以为MySQL的代码都是由c实现的,实际上,MySQL中也有部分是由C++实现的,比如它的查询解析部分,就使用了C++实现,而且大量采用了继承,模版,容器(MySQL自己实现的)等特性)。

Node的定义如下:

typedef
struct Node

{

    NodeTag        type;

} Node;

其他的节点也有类似定义,如常量的定义:

typedef
struct A_Const

{

    NodeTag        type;

    Value            val;        /* 值类型 */

    int            location;    /* 词的位置,未知时赋值为-1 */

} A_Const;

每个节点的第一个字段都是NodeTag.使用makeNode函数生成的每一个节点结构的第一个字段都会被赋值为枚举类型的NodeTag的一个值。NodeTag的定义如下:

typedef
enum NodeTag

{

    T_Invalid = 0,

 

    /*

     * TAGS FOR EXECUTOR NODES (execnodes.h)

     */

    T_IndexInfo = 10,

    T_ExprContext,

    T_ProjectionInfo,

    T_JunkFilter,

} NodeTag;

NodeTag是个枚举类型,包含约300个左右的枚举值,每个枚举值代表了一个结构体,篇幅限制,省略了其中的大部分。这些枚举值的数字是不连续,主要为方便以后添加新的结构体类型。每个节点的第一个字段都是NodeTag,它们可以在传递指针是都转为Node *结构,然后在根据NodeTag的值进行区别处理,这样做最大的好处就是能是函数接口统一。而且使用Node*定义变量比使用void *更好调试。

 

二、node节点的创建及释放

 

makeNode 是一个宏,用来创建一个节点并为该节点设置一个tag值。

#define makeNode(_type_)        ((_type_
*)
newNode(sizeof(_type_),T_##_type_))

 

实际调用的则是另一个宏newNode,而newNode则有两个版本,一个是针对gcc编译器,一个是针对g++编译器

#ifdef __GNUC__

 

/* 针对gcc版本的newNode */

#define newNode(size, tag) \

({    Node *_result; \

    AssertMacro((size) >= sizeof(Node));/* 检测申请的内存大小,>>=sizeof(Node) */ \

    _result = (Node *) palloc0fast(size); /* 申请内存 */ \

    _result->type = (tag); /*设置TypeTag */ \

    _result; /*返回值*/\

})

#else

 

/*

    针对g++编译器版本的newNode,区别在于,g++版本的返回的指针要用全局变量

*/

extern PGDLLIMPORT Node *newNodeMacroHolder;

 

#define newNode(size, tag) \

( \

    AssertMacro((size) >= sizeof(Node)),        /* need the tag, at least */ \

    newNodeMacroHolder = (Node *) palloc0fast(size), \

    newNodeMacroHolder->type = (tag), \

    newNodeMacroHolder \

)

#endif
/* __GNUC__ */

 

可以看出,创建一个新节点是通过两个宏makeNode,和newNode完成的。

注意:要避免直接使用newNode来创建节点,因为节点的大小在不同的环境下可能是不同的。使用makeNode即可,如:

Stmt *s = makeNode(Stmt);

 

释放节点很简单,创建时makeNode是使用palloc(相当于malloc)创建,直接使用pg中的pfree函数释放即可。

    pfree(s);

如果忘记释放也没关系,pg使用的内存上下文,能够自动的释放掉这些指针。

 

三、node节点的常用函数

 

    与node相关的函数包括

  1. nodeTag(nodeptr) 返回该节点对于的枚举值

    实际上是一个宏

        #define nodeTag(nodeptr)        (((const Node*)(nodeptr))->type)

  2. IsA(nodeptr,type) 判断某个节点指针指向的结构体是否是type类型,是就返回true,否则返回flase,实际上也是一个宏

        #define IsA(nodeptr,_type_)        (nodeTag(nodeptr) == T_##_type_)

  3. equal(const void *a,const void *b) 判断两个结构体是否相等,是就返回true,否则返回false.

    该函数的主要逻辑为:

    bool equal(const
    void *a, const
    void *b)

{

    bool        retval;

 

    if (a == b) /*指向相同的结构体*/

        return true;

 

    /*如果 a!=b, 则他们只有一个可以为NULL*/

    if (a == NULL || b == NULL)

        return false;

 

    /*是否是相同的结构类型 */

    if (nodeTag(a) != nodeTag(b))

        return false;

 

    switch (nodeTag(a))

    {

            /* 基础节点的类型比较 */

        case
T_Alias:

            retval = _equalAlias(a, b);/*两个Alias结构体的比较*/

            break;

        case
T_RangeVar:

            retval = _equalRangeVar(a, b);/*两个RangeVar结构体的比较*/

            break;

        …/*其他类型的结构体的比较*/

}

return retval;

}

个左右,每个结构体都要定义一个比较函数,因此这个equal函数的实现很长。最终由equal函数统一对外的接口。可见,代码开发不仅需要技巧,也需要很大的耐心。

 

四、再说Node节点的设计

 

    在pg的查询解析部分,包括查询解析,查询编译,查询重写,生成计划,制定执行路径等步骤。各个结构体都是一个特殊的节点,由NodeTag来标识。如果用面向对象的思维来理解的话,可以简单的看成是很多的特殊子节点继承自一个同一个父节点。这种设计并没有减少代码量,但是可以使函数拥有统一的对外接口,更容易书写成文档。相比void *,更能够提高调试能力。

    除了基本的node的节点设计,pg中还有可计算表达式树也采用了类似的方式实现,表示式树中的每个节点的第一个字段都是一个Expr类型的枚举值。

PostgreSQL源码解读 基础结构 node的更多相关文章

  1. PostgreSQL 源码解读 node的模拟实现

      node的实现是PostgreSQL的查询解析的基础,实现的关键是两个宏,makeNode和newNode.其他节点继承自Node节点,如果增加新的结构体,需要添加NodeTag中添加对应的枚举值 ...

  2. Spark jdbc postgresql数据库连接和写入操作源码解读

    概述:Spark postgresql jdbc 数据库连接和写入操作源码解读,详细记录了SparkSQL对数据库的操作,通过java程序,在本地开发和运行.整体为,Spark建立数据库连接,读取数据 ...

  3. nodeJS之eventproxy源码解读

    1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...

  4. 跟我一起读postgresql源码(八)——Executor(查询执行模块之——可优化语句的执行)

    2.可优化语句的执行 可优化语句的共同特点是它们被查询编译器处理后都会生成査询计划树,这一类语句由执行器(Executor)处理.该模块对外提供了三个接口: ExecutorStart.Executo ...

  5. 跟我一起读postgresql源码(十)——Executor(查询执行模块之——Scan节点(下))

    接前文跟我一起读postgresql源码(九)--Executor(查询执行模块之--Scan节点(上)) ,本篇把剩下的七个Scan节点结束掉. T_SubqueryScanState, T_Fun ...

  6. AbstractQueuedSynchronizer源码解读

    1. 背景 AQS(java.util.concurrent.locks.AbstractQueuedSynchronizer)是Doug Lea大师创作的用来构建锁或者其他同步组件(信号量.事件等) ...

  7. AbstractQueuedSynchronizer源码解读--续篇之Condition

    1. 背景 在之前的AbstractQueuedSynchronizer源码解读中,介绍了AQS的基本概念.互斥锁.共享锁.AQS对同步队列状态流转管理.线程阻塞与唤醒等内容.其中并不涉及Condit ...

  8. go语言nsq源码解读八 http.go、http_server.go

    这篇讲另两个文件http.go.http_server.go,这两个文件和第六讲go语言nsq源码解读六 tcp.go.tcp_server.go里的两个文件是相对应的.那两个文件用于处理tcp请求, ...

  9. ConcurrentLinkedQueue源码解读

    1.简介 ConcurrentLinkedQueue是JUC中的基于链表的无锁队列实现.本文将解读其源码实现. 2. 论文 ConcurrentLinkedQueue的实现是以Maged M. Mic ...

随机推荐

  1. SQL server将查询到的多行结果,拼接成字符串(列转行)

    select stuff(( ,,'') as UserNamestr 注释:查询出tabname表中的UserName列的所有内容,并将内容拼接成UserNamestr

  2. 九度oj 题目1340:小A的计算器

    题目描述: 以往的操作系统内部的数据表示都是二进制方式,小A新写了一个操作系统,系统内部的数据表示为26进制,其中0-25分别由a-z表示. 现在小A要在这个操作系统上实现一个计算器,这个计算器要能实 ...

  3. pmm metrics 数据采集来源

    handler状态参数 mysql> show global status like '%handler%'; +----------------------------+----------- ...

  4. [UOJ#221][BZOJ4652][Noi2016]循环之美

    [UOJ#221][BZOJ4652][Noi2016]循环之美 试题描述 牛牛是一个热爱算法设计的高中生.在他设计的算法中,常常会使用带小数的数进行计算.牛牛认为,如果在 k 进制下,一个数的小数部 ...

  5. BZOJ 2244 [SDOI2011]拦截导弹 ——CDQ分治

    三维偏序,直接CDQ硬上. 正反两次CDQ统计结尾的方案数,最后统计即可. #include <cstdio> #include <cstring> #include < ...

  6. BZOJ 3750: [POI2015]Pieczęć 【模拟】

    Description 一张n*m的方格纸,有些格子需要印成黑色,剩下的格子需要保留白色. 你有一个a*b的印章,有些格子是凸起(会沾上墨水)的.你需要判断能否用这个印章印出纸上的图案.印的过程中需要 ...

  7. 解决centos7中ens33中不显示IP等问题

    在虚拟机中安装centos7,输入ifconfig显示command not found.在sbin目录中发现没有ifconfig文件,这是因为centos7已经不使用 ifconfig命令了,已经用 ...

  8. 使用putty上传下载文件(pscp)

    putty作为ssh工具开源免费,简单易用.可是如何使用它来上传和下载文件呢?答案在于pscp. pscp下载地址:http://www.chiark.greenend.org.uk/~sgtatha ...

  9. linux 抓取访问量排行

    需求: 分析图片服务日志,把日志(每个图片访问次数*图片大小的总和)排行,取top10,也就是计算每个url的总访问大小 语句: awk '{a[$1]+=$10;}END{for(i in a){p ...

  10. 如何新建.htaccess文件

    如何新建.htaccess文件 (1)新建文本文档,即txt文件: (2)把重写代码写进去: (3)然后选择[另存为],命名为.htaccess即可.