我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PostgreSQL 源码解读(233)- 查询#126(NOT IN实现#4)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PostgreSQL 源码解读(233)- 查询#126(NOT IN实现#4)

本节简单解释了PostgreSQL NOT IN在执行时为何会出现时快时慢的现象。

测试数据如下:


[local]:5432 pg12@testdb=# select count(*) from tbl;
 count 
-------
     1
(1 row)
Time: 6.009 ms
[local]:5432 pg12@testdb=# select count(*) from t_big_null;
  count   
----------
 10000001
(1 row)
Time: 633.248 ms
[local]:5432 pg12@testdb=# \d tbl
                Table "public.tbl"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 id     | integer |           | not null | 
 value  | integer |           | not null | 
Indexes:
    "tbl_pkey" PRIMARY KEY, btree (id)
Rules:
    rule_tbl_update AS
    ON INSERT TO tbl
   WHERE (EXISTS ( SELECT tbl_1.id,
            tbl_1.value
           FROM tbl tbl_1
          WHERE tbl_1.id = new.id)) DO INSTEAD  UPDATE tbl SET value = tbl.value + 1
  WHERE tbl.id = new.id
[local]:5432 pg12@testdb=# \d t_big_null
             Table "public.t_big_null"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 id     | integer |           |          | 
[local]:5432 pg12@testdb=#

注意tbl表只有一行数据(id = 1),而t_big_null表在插入”id = 1”这一行时有意放在最后才插入


truncate table t_big_null;
insert into t_big_null select generate_series(2,10000000);
insert into t_big_null values(1);

一、数据结构

SubPlanState
子计划运行期状态




typedef struct SubPlanState
{
    NodeTag        type;
    SubPlan    *subplan;        
    struct PlanState *planstate;    
    struct PlanState *parent;    
    ExprState  *testexpr;        
    List       *args;            
    HeapTuple    curTuple;        
    Datum        curArray;        
    
    TupleDesc    descRight;        
    ProjectionInfo *projLeft;    
    ProjectionInfo *projRight;    
    TupleHashTable hashtable;    
    TupleHashTable hashnulls;    
    bool        havehashrows;    
    bool        havenullrows;    
    MemoryContext hashtablecxt; 
    MemoryContext hashtempcxt;    
    ExprContext *innerecontext; 
    AttrNumber *keyColIdx;        
    Oid           *tab_eq_funcoids;    
    Oid           *tab_collations; 
    FmgrInfo   *tab_hash_funcs; 
    FmgrInfo   *tab_eq_funcs;    
    FmgrInfo   *lhs_hash_funcs; 
    FmgrInfo   *cur_eq_funcs;    
    ExprState  *cur_eq_comp;    
} SubPlanState;

SubPlan
子查询计划




typedef struct SubPlan
{
    Expr        xpr;//表达式
    
    //从SubLink中拷贝而来
    SubLinkType subLinkType;    
    
    //组合操作符,转换为可执行的表达式
    Node       *testexpr;        
    List       *paramIds;        
    
    //Plan tree标识
    int            plan_id;        
    
    //EXPLAIN和debug目的的SubPlan标识
    char       *plan_name;        
    
    //用于确定subplan输出类型的额外信息
    Oid            firstColType;    
    int32        firstColTypmod; 
    Oid            firstColCollation;    
    
    //执行阶段的相关信息
    bool        useHashTable;    
    bool        unknownEqFalse; 
    bool        parallel_safe;    
    
    
    //用于给子查询传入和传出参数的信息
    
    //setParam和parParam是整型链表(param IDs)
    List       *setParam;        
    List       *parParam;        
    List       *args;            
    
    //估算执行成本
    Cost        startup_cost;    
    Cost        per_call_cost;    
} SubPlan;

SubLinkType
SubLink类型




typedef enum SubLinkType
{
    EXISTS_SUBLINK,
    ALL_SUBLINK,
    ANY_SUBLINK,
    ROWCOMPARE_SUBLINK,
    EXPR_SUBLINK,
    MULTIEXPR_SUBLINK,
    ARRAY_SUBLINK,
    CTE_SUBLINK                    
} SubLinkType;

SubLink
SubLink结构体



typedef struct SubLink
{
    Expr        xpr;
    SubLinkType subLinkType;    
    int            subLinkId;        
    Node       *testexpr;        
    List       *operName;        
    Node       *subselect;        
    int            location;        
} SubLink;

MaterialState
Material状态



typedef struct MaterialState
{
    ScanState    ss;                
    int            eflags;            
    bool        eof_underlying; 
    Tuplestorestate *tuplestorestate;
} MaterialState;

Tuplestorestate
Tuplestore相关操作的私有状态。




typedef enum
{
    TSS_INMEM,                    
    TSS_WRITEFILE,                
    TSS_READFILE                
} TupStoreStatus;

struct Tuplestorestate
{
    TupStoreStatus status;        
    int            eflags;            
    bool        backward;        
    bool        interXact;        
    bool        truncated;        
    int64        availMem;        
    int64        allowedMem;        
    int64        tuples;            
    BufFile    *myfile;            
    MemoryContext context;        
    ResourceOwner resowner;        
    
    void       *(*copytup) (Tuplestorestate *state, void *tup);
    
    void        (*writetup) (Tuplestorestate *state, void *tup);
    
    void       *(*readtup) (Tuplestorestate *state, unsigned int len);
    
    void      **memtuples;        
    int            memtupdeleted;    
    int            memtupcount;    
    int            memtupsize;        
    bool        growmemtuples;    
    
    TSReadPointer *readptrs;    
    int            activeptr;        
    int            readptrcount;    
    int            readptrsize;    
    int            writepos_file;    
    off_t        writepos_offset;    
};
#define COPYTUP(state,tup)    ((*(state)->copytup) (state, tup))
#define WRITETUP(state,tup) ((*(state)->writetup) (state, tup))
#define READTUP(state,len)    ((*(state)->readtup) (state, len))
#define LACKMEM(state)        ((state)->availMem < 0)
#define USEMEM(state,amt)    ((state)->availMem -= (amt))
#define FREEMEM(state,amt)    ((state)->availMem += (amt))

TSReadPointer
tuplestore读指针




typedef enum
{
    TSS_INMEM,                    
    TSS_WRITEFILE,                
    TSS_READFILE                
} TupStoreStatus;

typedef struct
{
    int            eflags;            
    bool        eof_reached;    
    int            current;        
    int            file;            
    off_t        offset;            
} TSReadPointer;

二、源码解读

NOT IN在实际执行时会转换为ALL_SUBLINK,执行的快慢取决于什么时候会碰到符合条件的记录,一旦遇到马上返回。因此,SQL的执行时间与数据表的扫描顺序有紧密的关系,符合条件的行越早出现,程序越早返回,需要的时间越短。
相关代码如下:


...
        //解析表达式
        rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
                                              &rownull);
        if (subLinkType == ANY_SUBLINK)
        {
            //ANY : 使用OR语义组合
            
            if (rownull)
                *isNull = true;
            else if (DatumGetBool(rowresult))
            {
                result = BoolGetDatum(true);
                *isNull = false;
                break;            
            }
        }

如上代码所示,在全表扫描物化的时候一旦textexpr表达式测试到其中一行满足条件,那么就会马上退出循环,而这一行如果非常幸运的出现在扫描的最开始的地方,那执行时间将会很快(扫描几个数据块 vs 全表扫描)。

新建一张表,插入2条记录,其中id = 2的行出现在block编号最小的地方,而id = 1出现在block编号最大的地方,这时候pg就会出现时快时慢的情况,两者相差3个数量级。


[local]:5432 pg12@testdb=# create table tbl3(id int);
CREATE TABLE
Time: 1.852 ms
[local]:5432 pg12@testdb=# insert into tbl3 values(1);
INSERT 0 1
Time: 1.276 ms
[local]:5432 pg12@testdb=# insert into tbl3 values(2);
INSERT 0 1
Time: 1.089 ms
[local]:5432 pg12@testdb=# select * from tbl3 where id not in (select b.id from t_big_null b);
 id 
----
(0 rows)
Time: 3.676 ms
[local]:5432 pg12@testdb=# select * from tbl3 where id not in (select b.id from t_big_null b);
 id 
----
(0 rows)
Time: 4925.893 ms (00:04.926)
[local]:5432 pg12@testdb=# select * from tbl3 where id not in (select b.id from t_big_null b);
 id 
----
(0 rows)
Time: 2.858 ms
[local]:5432 pg12@testdb=# select * from tbl3 where id not in (select b.id from t_big_null b);
 id 
----
(0 rows)
Time: 4588.436 ms (00:04.588)
[local]:5432 pg12@testdb=# select * from tbl3 where id not in (select b.id from t_big_null b);
 id 
----
(0 rows)
Time: 1.896 ms
[local]:5432 pg12@testdb=# select * from tbl3 where id not in (select b.id from t_big_null b);
 id 
----
(0 rows)
Time: 4653.525 ms (00:04.654)
[local]:5432 pg12@testdb=#

ExecScanSubPlan




static Datum
ExecScanSubPlan(SubPlanState *node,
                ExprContext *econtext,
                bool *isNull)
{
    SubPlan    *subplan = node->subplan;//子计划
    PlanState  *planstate = node->planstate;//计划运行期状态
    SubLinkType subLinkType = subplan->subLinkType;//子链接类型
    MemoryContext oldcontext;//原内存上下文
    TupleTableSlot *slot;//元组slot
    Datum        result;//结果指针
    bool        found = false;    
    ListCell   *pvar;//临时变量
    ListCell   *l;//临时变量
    ArrayBuildStateAny *astate = NULL;//
    
    if (subLinkType == MULTIEXPR_SUBLINK)
    {
        EState       *estate = node->parent->state;
        foreach(l, subplan->setParam)
        {
            int            paramid = lfirst_int(l);
            ParamExecData *prm = &(estate->es_param_exec_vals[paramid]);
            prm->execPlan = node;
        }
        *isNull = true;
        return (Datum) 0;
    }
    
    //数组
    if (subLinkType == ARRAY_SUBLINK)
        astate = initArrayResultAny(subplan->firstColType,
                                    CurrentMemoryContext, true);
    
    //切换上下文
    oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
    
    //通过父计划相关值中设置子计划参数
    Assert(list_length(subplan->parParam) == list_length(node->args));
    forboth(l, subplan->parParam, pvar, node->args)
    {
        int            paramid = lfirst_int(l);
        ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
        prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
                                               econtext,
                                               &(prm->isnull));
        planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
    }
    
    //执行ReScan
    //Reset a plan node so that its output can be re-scanned.
    ExecReScan(planstate);
    
    result = BoolGetDatum(subLinkType == ALL_SUBLINK);//ALL为T,否则为F
    *isNull = false;
    for (slot = ExecProcNode(planstate);
         !TupIsNull(slot);
         slot = ExecProcNode(planstate))//循环获取元组,直至没有元组为NULL(即已完成)
    {
        //元组描述符
        TupleDesc    tdesc = slot->tts_tupleDescriptor;
        Datum        rowresult;//结果
        bool        rownull;//是否为空?
        int            col;//列计数器
        ListCell   *plst;//临时变量
        if (subLinkType == EXISTS_SUBLINK)//EXISTS
        {
            found = true;
            result = BoolGetDatum(true);
            break;
        }
        if (subLinkType == EXPR_SUBLINK)//EXPR表达式
        {
            
            if (found)
                ereport(ERROR,
                        (errcode(ERRCODE_CARDINALITY_VIOLATION),
                         errmsg("more than one row returned by a subquery used as an expression")));
            found = true;
            
            if (node->curTuple)
                heap_freetuple(node->curTuple);
            node->curTuple = ExecCopySlotHeapTuple(slot);
            result = heap_getattr(node->curTuple, 1, tdesc, isNull);
            
            continue;
        }
        if (subLinkType == ARRAY_SUBLINK)//数组
        {
            Datum        dvalue;
            bool        disnull;
            found = true;
            
            Assert(subplan->firstColType == TupleDescAttr(tdesc, 0)->atttypid);
            dvalue = slot_getattr(slot, 1, &disnull);
            astate = accumArrayResultAny(astate, dvalue, disnull,
                                         subplan->firstColType, oldcontext);
            
            continue;
        }
        
        if (subLinkType == ROWCOMPARE_SUBLINK && found)//行比较
            ereport(ERROR,
                    (errcode(ERRCODE_CARDINALITY_VIOLATION),
                     errmsg("more than one row returned by a subquery used as an expression")));
        found = true;//初始为T
        
        col = 1;//列从1计数
        foreach(plst, subplan->paramIds)//循环遍历子查询参数
        {
            int            paramid = lfirst_int(plst);
            ParamExecData *prmdata;
            prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
            Assert(prmdata->execPlan == NULL);
            //获取参数值
            prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
            //下一个列
            col++;
        }
        //解析表达式
        rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
                                              &rownull);
        if (subLinkType == ANY_SUBLINK)
        {
            //ANY : 使用OR语义组合
            
            if (rownull)
                *isNull = true;
            else if (DatumGetBool(rowresult))
            {
                result = BoolGetDatum(true);
                *isNull = false;
                break;            
            }
        }
        else if (subLinkType == ALL_SUBLINK)
        {
            //ALL : 使用AND语义
            
            if (rownull)
                *isNull = true;
            else if (!DatumGetBool(rowresult))
            {
                result = BoolGetDatum(false);
                *isNull = false;
                break;            
            }
        }
        else
        {
            
            //这里一定是ROWCOMPARE
            result = rowresult;
            *isNull = rownull;
        }
    }
    MemoryContextSwitchTo(oldcontext);
    if (subLinkType == ARRAY_SUBLINK)
    {
        
        //在调用者上下文中返回结果
        result = makeArrayResultAny(astate, oldcontext, true);
    }
    else if (!found)
    {
        
        if (subLinkType == EXPR_SUBLINK ||
            subLinkType == ROWCOMPARE_SUBLINK)
        {
            result = (Datum) 0;
            *isNull = true;
        }
    }
    //返回结果
    return result;
}

三、跟踪分析

执行SQL:


[local]:5432 pg12@testdb=# select * from tbl a where a.id not in (select b.id from t_big_null b);

启动gdb跟踪,设置断点,观察到断点hit 1760次后就会退出,因此设置为忽略前1758次,只跟踪最后2次。


(gdb) info b
Num     Type           Disp Enb Address            What
13      breakpoint     keep y   0x0000000000721126 in ExecMaterial at nodeMaterial.c:150
    breakpoint already hit 1760 times
    ignore next 3360 hits
...
(gdb) b nodeSubplan.c:328
Breakpoint 17 at 0x7303b9: file nodeSubplan.c, line 328.
(gdb) del 16
(gdb) info b
Num     Type           Disp Enb Address            What
17      breakpoint     keep y   0x00000000007303b9 in ExecScanSubPlan at nodeSubplan.c:328
(gdb) ignore 17 1758
Will ignore next 1758 crossings of breakpoint 17.
(gdb) c
Continuing.

开始跟踪,这是第1759次,这时候从SubPlan获取的数据是id = 10000000


Breakpoint 17, ExecScanSubPlan (node=0x3069268, econtext=0x3068aa0, isNull=0x3068dbd)
    at nodeSubplan.c:328
328            TupleDesc    tdesc = slot->tts_tupleDescriptor;
(gdb) n
334            if (subLinkType == EXISTS_SUBLINK)
(gdb) 
341            if (subLinkType == EXPR_SUBLINK)
(gdb) 
367            if (subLinkType == ARRAY_SUBLINK)
(gdb) 
383            if (subLinkType == ROWCOMPARE_SUBLINK && found)
(gdb) 
388            found = true;
(gdb) 
395            col = 1;
(gdb) 
396            foreach(plst, subplan->paramIds)
(gdb) 
398                int            paramid = lfirst_int(plst);
(gdb) 
401                prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
(gdb) 
402                Assert(prmdata->execPlan == NULL);
(gdb) 
403                prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
(gdb) p *prmdata
$109 = {execPlan = 0x0, value = 9999999, isnull = false}
(gdb) n
404                col++;
(gdb) p *prmdata
$110 = {execPlan = 0x0, value = 10000000, isnull = false}
(gdb) n
396            foreach(plst, subplan->paramIds)
(gdb)

解析表达式


407            rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
(gdb) step
ExecEvalExprSwitchContext (state=0x3069380, econtext=0x3068aa0, isNull=0x7ffd184750ef)
    at ../../../class="lazy" data-src/include/executor/executor.h:306
306        oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
(gdb) n
307        retDatum = state->evalfunc(state, econtext, isNull);
(gdb) step
ExecInterpExpr (state=0x3069380, econtext=0x3068aa0, isnull=0x7ffd184750ef)
    at execExprInterp.c:404
404        if (unlikely(state == NULL))
(gdb) n
411        op = state->steps;
(gdb) p *state
$111 = {tag = {type = T_ExprState}, flags = 6 '\006', resnull = false, resvalue = 0, 
  resultslot = 0x0, steps = 0x3069418, evalfunc = 0x6e2d4d <ExecInterpExpr>, 
  expr = 0x30917a8, evalfunc_private = 0x6e2d4d <ExecInterpExpr>, steps_len = 5, 
  steps_alloc = 16, parent = 0x3068988, ext_params = 0x0, innermost_caseval = 0x0, 
  innermost_casenull = 0x0, innermost_domainval = 0x0, innermost_domainnull = 0x0}
(gdb) n
412        resultslot = state->resultslot;
(gdb) 
413        innerslot = econtext->ecxt_innertuple;
(gdb) 
414        outerslot = econtext->ecxt_outertuple;
(gdb) 
415        scanslot = econtext->ecxt_scantuple;
(gdb) p *innerslot
Cannot access memory at address 0x0
(gdb) p *outerslot
Cannot access memory at address 0x0
(gdb) n
418        EEO_DISPATCH();
(gdb) p *scanslot
$112 = {type = T_TupleTableSlot, tts_flags = 16, tts_nvalid = 1, 
  tts_ops = 0xc3e780 <TTSOpsBufferHeapTuple>, tts_tupleDescriptor = 0x7fab449c99f0, 
  tts_values = 0x3068bd0, tts_isnull = 0x3068be0, tts_mcxt = 0x3067da0, tts_tid = {
    ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 2}, tts_tableOid = 40960}
(gdb) p *scanslot->tts_values
$113 = 1
(gdb) n
448                CheckOpSlotCompatibility(op, scanslot);
(gdb) n
450                slot_getsomeattrs(scanslot, op->d.fetch.last_var);
(gdb) 
452                EEO_NEXT();
(gdb) 
487                int            attnum = op->d.var.attnum;
(gdb) 
491                Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
(gdb) 
492                *op->resvalue = scanslot->tts_values[attnum];
(gdb) 
493                *op->resnull = scanslot->tts_isnull[attnum];
(gdb) 
495                EEO_NEXT();
(gdb) p *op->resvalue
$114 = 1
(gdb) n
962                ExecEvalParamExec(state, op, econtext);
(gdb) 
964                EEO_NEXT();
(gdb) p *op
$115 = {opcode = 7224136, resvalue = 0x30698b8, resnull = 0x30698c0, d = {fetch = {
      last_var = 0, fixed = 23, known_desc = 0x0, kind = 0x0}, var = {attnum = 0, 
      vartype = 23}, wholerow = {var = 0x1700000000, first = false, slow = false, 
      tupdesc = 0x0, junkFilter = 0x0}, assign_var = {resultnum = 0, attnum = 23}, 
    assign_tmp = {resultnum = 0}, constval = {value = 98784247808, isnull = false}, 
    func = {finfo = 0x1700000000, fcinfo_data = 0x0, fn_addr = 0x0, nargs = 0}, 
    boolexpr = {anynull = 0x1700000000, jumpdone = 0}, qualexpr = {jumpdone = 0}, jump = {
      jumpdone = 0}, nulltest_row = {argdesc = 0x1700000000}, param = {paramid = 0, 
      paramtype = 23}, cparam = {paramfunc = 0x1700000000, paramarg = 0x0, paramid = 0, 
      paramtype = 0}, casetest = {value = 0x1700000000, isnull = 0x0}, make_readonly = {
      value = 0x1700000000, isnull = 0x0}, iocoerce = {finfo_out = 0x1700000000, 
      fcinfo_data_out = 0x0, finfo_in = 0x0, fcinfo_data_in = 0x0}, sqlvaluefunction = {
      svf = 0x1700000000}, nextvalueexpr = {seqid = 0, seqtypid = 23}, arrayexpr = {
      elemvalues = 0x1700000000, elemnulls = 0x0, nelems = 0, elemtype = 0, 
      elemlength = 0, elembyval = false, elemalign = 0 '\000', multidims = false}, 
    arraycoerce = {elemexprstate = 0x1700000000, resultelemtype = 0, amstate = 0x0}, 
    row = {tupdesc = 0x1700000000, elemvalues = 0x0, elemnulls = 0x0}, rowcompare_step = {
      finfo = 0x1700000000, fcinfo_data = 0x0, fn_addr = 0x0, jumpnull = 0, 
      jumpdone = 0}, rowcompare_final = {rctype = 0}, minmax = {values = 0x1700000000, 
      nulls = 0x0, nelems = 0, op = IS_GREATEST, finfo = 0x0, fcinfo_data = 0x0}, 
    fieldselect = {fieldnum = 0, resulttype = 23, argdesc = 0x0}, fieldstore = {
      fstore = 0x1700000000, argdesc = 0x0, values = 0x0, nulls = 0x0, ncolumns = 0}, 
    sbsref_subscript = {state = 0x1700000000, off = 0, isupper = false, jumpdone = 0}, 
    sbsref = {state = 0x1700000000}, domaincheck = {
      constraintname = 0x1700000000 <Address 0x1700000000 out of bounds>, 
      checkvalue = 0x0, checknull = 0x0, resulttype = 0}, convert_rowtype = {
      convert = 0x1700000000, indesc = 0x0, outdesc = 0x0, map = 0x0, 
      initialized = false}, scalararrayop = {element_type = 0, useOr = 23, typlen = 0, 
      typbyval = false, typalign = 0 '\000', finfo = 0x0, fcinfo_data = 0x0, 
      fn_addr = 0x0}, xmlexpr = {xexpr = 0x1700000000, named_argvalue = 0x0, 
      named_argnull = 0x0, argvalue = 0x0, argnull = 0x0}, aggref = {
      astate = 0x1700000000}, grouping_func = {parent = 0x1700000000, clauses = 0x0}, 
    window_func = {wfstate = 0x1700000000}, subplan = {sstate = 0x1700000000}, 
    alternative_subplan = {asstate = 0x1700000000}, agg_deserialize = {
      aggstate = 0x1700000000, fcinfo_data = 0x0, jumpnull = 0}, 
    agg_strict_input_check = {args = 0x1700000000, nulls = 0x0, nargs = 0, jumpnull = 0}, 
    agg_init_trans = {aggstate = 0x1700000000, pertrans = 0x0, aggcontext = 0x0, 
      setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_strict_trans_check = {
      aggstate = 0x1700000000, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, 
    agg_trans = {aggstate = 0x1700000000, pertrans = 0x0, aggcontext = 0x0, setno = 0, 
      transno = 0, setoff = 0}}}
(gdb) p *state
$116 = {tag = {type = T_ExprState}, flags = 6 '\006', resnull = false, resvalue = 0, 
  resultslot = 0x0, steps = 0x3069418, evalfunc = 0x6e2d4d <ExecInterpExpr>, 
  expr = 0x30917a8, evalfunc_private = 0x6e2d4d <ExecInterpExpr>, steps_len = 5, 
  steps_alloc = 16, parent = 0x3068988, ext_params = 0x0, innermost_caseval = 0x0, 
  innermost_casenull = 0x0, innermost_domainval = 0x0, innermost_domainnull = 0x0}
(gdb) n
634                FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
(gdb) 
635                NullableDatum *args = fcinfo->args;
(gdb) p *fcinfo
$117 = {flinfo = 0x3069830, context = 0x0, resultinfo = 0x0, fncollation = 0, 
  isnull = false, nargs = 2, args = 0x30698a8}
(gdb) p *fcinfo->args
$118 = {value = 1, isnull = false}
(gdb) n
640                for (argno = 0; argno < op->d.func.nargs; argno++)
(gdb) p op->d.func.nargs
$119 = 2
(gdb) p *op
$120 = {opcode = 7222440, resvalue = 0x3069388, resnull = 0x3069385, d = {fetch = {
      last_var = 50763824, fixed = false, known_desc = 0x3069888, 
      kind = 0x96c2b2 <int4eq>}, var = {attnum = 50763824, vartype = 0}, wholerow = {
      var = 0x3069830, first = 136, slow = 152, tupdesc = 0x96c2b2 <int4eq>, 
      junkFilter = 0x2}, assign_var = {resultnum = 50763824, attnum = 0}, assign_tmp = {
      resultnum = 50763824}, constval = {value = 50763824, isnull = 136}, func = {
      finfo = 0x3069830, fcinfo_data = 0x3069888, fn_addr = 0x96c2b2 <int4eq>, 
      nargs = 2}, boolexpr = {anynull = 0x3069830, jumpdone = 50763912}, qualexpr = {
      jumpdone = 50763824}, jump = {jumpdone = 50763824}, nulltest_row = {
      argdesc = 0x3069830}, param = {paramid = 50763824, paramtype = 0}, cparam = {
      paramfunc = 0x3069830, paramarg = 0x3069888, paramid = 9880242, paramtype = 0}, 
    casetest = {value = 0x3069830, isnull = 0x3069888}, make_readonly = {
      value = 0x3069830, isnull = 0x3069888}, iocoerce = {finfo_out = 0x3069830, 
      fcinfo_data_out = 0x3069888, finfo_in = 0x96c2b2 <int4eq>, fcinfo_data_in = 0x2}, 
    sqlvaluefunction = {svf = 0x3069830}, nextvalueexpr = {seqid = 50763824, 
      seqtypid = 0}, arrayexpr = {elemvalues = 0x3069830, elemnulls = 0x3069888, 
      nelems = 9880242, elemtype = 0, elemlength = 2, elembyval = false, 
      elemalign = 0 '\000', multidims = false}, arraycoerce = {elemexprstate = 0x3069830, 
      resultelemtype = 50763912, amstate = 0x96c2b2 <int4eq>}, row = {
      tupdesc = 0x3069830, elemvalues = 0x3069888, elemnulls = 0x96c2b2 <int4eq>}, 
    rowcompare_step = {finfo = 0x3069830, fcinfo_data = 0x3069888, 
      fn_addr = 0x96c2b2 <int4eq>, jumpnull = 2, jumpdone = 0}, rowcompare_final = {
      rctype = 50763824}, minmax = {values = 0x3069830, nulls = 0x3069888, 
      nelems = 9880242, op = IS_GREATEST, finfo = 0x2, fcinfo_data = 0x0}, fieldselect = {
      fieldnum = -26576, resulttype = 0, argdesc = 0x3069888}, fieldstore = {
      fstore = 0x3069830, argdesc = 0x3069888, values = 0x96c2b2 <int4eq>, nulls = 0x2, 
      ncolumns = 0}, sbsref_subscript = {state = 0x3069830, off = 50763912, 
      isupper = false, jumpdone = 9880242}, sbsref = {state = 0x3069830}, domaincheck = {
      constraintname = 0x3069830 "\262\302\226", checkvalue = 0x3069888, 
      checknull = 0x96c2b2 <int4eq>, resulttype = 2}, convert_rowtype = {
      convert = 0x3069830, indesc = 0x3069888, outdesc = 0x96c2b2 <int4eq>, map = 0x2, 
      initialized = false}, scalararrayop = {element_type = 50763824, useOr = false, 
      typlen = 0, typbyval = 136, typalign = -104 '\230', finfo = 0x96c2b2 <int4eq>, 
      fcinfo_data = 0x2, fn_addr = 0x0}, xmlexpr = {xexpr = 0x3069830, 
      named_argvalue = 0x3069888, named_argnull = 0x96c2b2 <int4eq>, argvalue = 0x2, 
      argnull = 0x0}, aggref = {astate = 0x3069830}, grouping_func = {parent = 0x3069830, 
      clauses = 0x3069888}, window_func = {wfstate = 0x3069830}, subplan = {
      sstate = 0x3069830}, alternative_subplan = {asstate = 0x3069830}, 
    agg_deserialize = {aggstate = 0x3069830, fcinfo_data = 0x3069888, 
      jumpnull = 9880242}, agg_strict_input_check = {args = 0x3069830, nulls = 0x3069888, 
      nargs = 9880242, jumpnull = 0}, agg_init_trans = {aggstate = 0x3069830, 
      pertrans = 0x3069888, aggcontext = 0x96c2b2 <int4eq>, setno = 2, transno = 0, 
      setoff = 0, jumpnull = 0}, agg_strict_trans_check = {aggstate = 0x3069830, 
      setno = 50763912, transno = 0, setoff = 9880242, jumpnull = 0}, agg_trans = {
      aggstate = 0x3069830, pertrans = 0x3069888, aggcontext = 0x96c2b2 <int4eq>, 
      setno = 2, transno = 0, setoff = 0}}}
(gdb) p op->d->func
$121 = {finfo = 0x3069830, fcinfo_data = 0x3069888, fn_addr = 0x96c2b2 <int4eq>, 
  nargs = 2}
(gdb) p op->d->func->finfo
$122 = (FmgrInfo *) 0x3069830
(gdb) p *op->d->func->finfo
$123 = {fn_addr = 0x96c2b2 <int4eq>, fn_oid = 65, fn_nargs = 2, fn_strict = true, 
  fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x3067da0, 
  fn_expr = 0x30917a8}
(gdb) p *op->d->func->fcinfo_data
$124 = {flinfo = 0x3069830, context = 0x0, resultinfo = 0x0, fncollation = 0, 
  isnull = false, nargs = 2, args = 0x30698a8}
(gdb) p *op->d->func->fcinfo_data->flinfo
$125 = {fn_addr = 0x96c2b2 <int4eq>, fn_oid = 65, fn_nargs = 2, fn_strict = true, 
  fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x3067da0, 
  fn_expr = 0x30917a8}
(gdb) p *op->d->func->fcinfo_data->args
$126 = {value = 1, isnull = false}
(gdb) n
642                    if (args[argno].isnull)
(gdb) 
640                for (argno = 0; argno < op->d.func.nargs; argno++)
(gdb) 
642                    if (args[argno].isnull)
(gdb) 
640                for (argno = 0; argno < op->d.func.nargs; argno++)
(gdb) 
648                fcinfo->isnull = false;
(gdb) p *args
$127 = {value = 1, isnull = false}
(gdb) n
649                d = op->d.func.fn_addr(fcinfo);
(gdb) 
650                *op->resvalue = d;
(gdb) p d
$128 = 0
(gdb) n
651                *op->resnull = fcinfo->isnull;
(gdb) 
654                EEO_NEXT();
(gdb) 
425                goto out;
(gdb) n
1747        *isnull = state->resnull;
(gdb) 
1748        return state->resvalue;
(gdb) p *state
$129 = {tag = {type = T_ExprState}, flags = 6 '\006', resnull = false, resvalue = 0, 
  resultslot = 0x0, steps = 0x3069418, evalfunc = 0x6e2d4d <ExecInterpExpr>, 
  expr = 0x30917a8, evalfunc_private = 0x6e2d4d <ExecInterpExpr>, steps_len = 5, 
  steps_alloc = 16, parent = 0x3068988, ext_params = 0x0, innermost_caseval = 0x0, 
  innermost_casenull = 0x0, innermost_domainval = 0x0, innermost_domainnull = 0x0}
(gdb) n
1749    }
(gdb) 
ExecEvalExprSwitchContext (state=0x3069380, econtext=0x3068aa0, isNull=0x7ffd184750ef)
    at ../../../class="lazy" data-src/include/executor/executor.h:308
308        MemoryContextSwitchTo(oldContext);
(gdb) 
309        return retDatum;
(gdb) p retDatum
$130 = 0
(gdb) n
310    }
(gdb)

这是第1760次调用


ExecScanSubPlan (node=0x3069268, econtext=0x3068aa0, isNull=0x3068dbd)
    at nodeSubplan.c:410
410            if (subLinkType == ANY_SUBLINK)
(gdb) 
413                if (rownull)
(gdb) 
415                else if (DatumGetBool(rowresult))
(gdb) p rowresult
$131 = 0
(gdb) n
326             slot = ExecProcNode(planstate))
(gdb) 
324        for (slot = ExecProcNode(planstate);
(gdb) 
325             !TupIsNull(slot);
(gdb) 
Breakpoint 17, ExecScanSubPlan (node=0x3069268, econtext=0x3068aa0, isNull=0x3068dbd)
    at nodeSubplan.c:328
328            TupleDesc    tdesc = slot->tts_tupleDescriptor;
(gdb) 
334            if (subLinkType == EXISTS_SUBLINK)
(gdb) 
341            if (subLinkType == EXPR_SUBLINK)
(gdb) 
367            if (subLinkType == ARRAY_SUBLINK)
(gdb) 
383            if (subLinkType == ROWCOMPARE_SUBLINK && found)
(gdb) 
388            found = true;
(gdb) 
395            col = 1;
(gdb) p *slot->tts_values
$132 = 10000000 --> 上一次的数据
(gdb) n
396            foreach(plst, subplan->paramIds)
(gdb) 
398                int            paramid = lfirst_int(plst);
(gdb) 
401                prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
(gdb) 
402                Assert(prmdata->execPlan == NULL);
(gdb) p *prmdata
$133 = {execPlan = 0x0, value = 10000000, isnull = false}
(gdb) n
403                prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
(gdb) 
404                col++;
(gdb) p *prmdata
$134 = {execPlan = 0x0, value = 1, isnull = false} --> 本次数据,值为1
(gdb) info b
Num     Type           Disp Enb Address            What
17      breakpoint     keep y   0x00000000007303b9 in ExecScanSubPlan at nodeSubplan.c:328
    breakpoint already hit 1760 times
(gdb) n
396            foreach(plst, subplan->paramIds)
(gdb) 
407            rowresult = ExecEvalExprSwitchContext(node->testexpr, econtext,
(gdb) 
410            if (subLinkType == ANY_SUBLINK)
(gdb) 
413                if (rownull)
(gdb) 
415                else if (DatumGetBool(rowresult))
(gdb) 
417                    result = BoolGetDatum(true);
(gdb) 
418                    *isNull = false;
(gdb) 
419                    break;            
(gdb) 
442        MemoryContextSwitchTo(oldcontext);
(gdb) 
444        if (subLinkType == ARRAY_SUBLINK)
(gdb) 
449        else if (!found)
(gdb) 
464        return result;
(gdb) 
(gdb) p result
$135 = 1 --> 满足条件

DONE

四、参考资料

N/A

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PostgreSQL 源码解读(233)- 查询#126(NOT IN实现#4)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

目录