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文档到电脑,方便收藏和打印~