一、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. 关于hadoop学习的思考(一) —— 小的知识点的总结

    一.对于CDH的小总结: CDH:是Cloudera公司在Apache开源项目hadoop的基础上发型的,共有五个版本前两个已不再更新,最经的两个分别是CDH4(基于hadoop2.0.0版本演化而来 ...

  2. appium+python自动化-adb shell模拟点击事件(input tap)

    前言 appium有时候定位一个元素很难定位到,或者说明明定位到这个元素了,却无法点击,这个时候该怎么办呢? 求助大神是没用的,点击不了就是点击不了,appium不是万能的,这个时候应该转换思路,换其 ...

  3. c++ string char* 获取输入值的区别

    #include <iostream> #include <string> using namespace std; void reverseStr(string &s ...

  4. 学习笔记6——插件 API,“过滤器”(Filters)和“动作”(Actions)

    WordPress 中有一种叫执行挂勾的机制,允许插件把一些功能“挂载”到 WordPress 当中.也就是说,在系统运行至某一个环节时,去调用插件内的一些函数.执行挂勾分为两种: 动作 (Actio ...

  5. 九度oj 题目1025:最大报销额

    题目描述:     现有一笔经费可以报销一定额度的发票.允许报销的发票类型包括买图书(A类).文具(B类).差旅(C类),要求每张发票的总额不得超过1000元,每张发票上,单项物品的价值不得超过600 ...

  6. "sort open failed +1 no such file or directory"解决方

    GNU的sort也认老式字段规格: +n.m. 但是字段和字符都从0开始计, 例如-k3 -k2可以等效为+2 -3 +1 -2. 目前使用的sort+和-必须成对使用, 只用+就会报错说”sort: ...

  7. 如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2'

    如何解决 错误code signing is required for product type 'xxxxx' in SDK 'iOS 8.2' 大家在做真机调试的时候,或许会遇到这样的问题,那如何 ...

  8. RDDs基本操作、RDDs特性、KeyValue对RDDs、RDD依赖

    摘要:RDD是Spark中极为重要的数据抽象,这里总结RDD的概念,基本操作Transformation(转换)与Action,RDDs的特性,KeyValue对RDDs的Transformation ...

  9. 【Luogu】P2324骑士精神(IDA*)

    题目链接 当guess>limit-deep的时候return就好了. guess是估价函数,值为不在自己地盘上的骑士个数.limit是本次迭代阈值.deep是已经走了多少步. 这个优化是显然的 ...

  10. SPOJ QTREE Query on a tree ——树链剖分 线段树

    [题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #incl ...