文章目录
工具分享和使用
注:这是前面我给大家说的那个 《小宇带你学pg内核分析》里面工具,可以得到一个查询语句的抽象语法树(图片),非常的好用 因此,这里推荐给大家以及感谢宇神为Postgresql的贡献。
工具的用途:通过这个工具 我们可以十分方便的把log文件的输出(文字形式)转化为图片
第一步:宇神的git地址,上去拉取代码
第二步:准备我们的所需要的原料:抽象语法树的日志文件
我们这里将要调试的SQL语句是:
select a from tb1 where a=1;
开始调试,打上断点
我是在\src\backend\tcop\uxdb.c里面exec_simple_query函数的
/*
* Do basic parsing of the query or queries (this should be safe even if
* we are in aborted transaction state!)
*/
parsetree_list = ux_parse_query(query_string);//从此进入
得到了分析树(语法树)
看一下执行call elog_node_display前的函数调用堆栈:
(gdb) bt
#0 transformStmt (pstate=0x1ff58e8, parseTree=0x1ff4f38) at analyze.c:292
#1 0x00000000008a137a in transformOptionalSelectInto (pstate=0x1ff58e8,
parseTree=0x1ff4f38) at analyze.c:242
#2 0x00000000008a1275 in transformTopLevelStmt (pstate=0x1ff58e8,
parseTree=0x1ff5048) at analyze.c:192
#3 0x00000000008a10eb in parse_analyze (parseTree=0x1ff5048,
sourceText=0x1ff4178 "select a from tb1 where a=1;", paramTypes=0x0,
numParams=0, queryEnv=0x0) at analyze.c:112
#4 0x0000000000b5e7ea in ux_analyze_and_rewrite (parsetree=0x1ff5048,
query_string=0x1ff4178 "select a from tb1 where a=1;", paramTypes=0x0,
numParams=0, queryEnv=0x0) at uxdb.c:657
#5 0x0000000000b5edba in exec_simple_query (
query_string=0x1ff4178 "select a from tb1 where a=1;") at uxdb.c:1028
#6 0x0000000000b634a7 in UxdbMain (argc=1, argv=0x1f9e840,
dbname=0x1f9e728 "uxdb", username=0x1f75368 "uxdb") at uxdb.c:4236
#7 0x0000000000ad077d in BackendRun (port=0x1f75d70) at uxmaster.c:4822
#8 0x0000000000acfefa in BackendStartup (port=0x1f75d70) at uxmaster.c:4494
#9 0x0000000000acc1bf in ServerLoop () at uxmaster.c:2039
#10 0x0000000000acb587 in PostmasterMain (argc=3, argv=0x1f708f0)
at uxmaster.c:1584
#11 0x0000000000a0f11e in main (argc=3, argv=0x1f708f0) at main.c:286
(gdb) call elog_node_display(15,"a_newname",parseTree,1)
然后在GDB调试里面执行,如下操作:
call elog_node_display(15,”随便起个名字”,要显示的结果的地址,1);
我这里就是 call elog_node_display(15,"a_newname",parseTree,1)
好了,我们这个时候就得到语法树的日志文件
第三步:将日志文件(在你的数据库集群里面,有个log目录。然后选择最新时间的日志文件)
将生成的日志中解析的结果复制一份放在node文件夹(就是宇神的工具目录下面的node目录)下 *.node文件中。如下:
解释一下上面简单的抽象语法树:
1. SELECT 类型的节点的intoClause为空;
2. targetList目标列为RESTARGET来代表的(因为我们没有指定名字,名字也为空。其值val为COLUMNREF 所引用的为a这一列);
3. fromClause子句里面有一个范围量RANGEVAR 代表了我们要查询的表
4. whereClause子句 里面是有一个常量表达式:a=1
第四步:生成图片(执行./pgNodeGraph)
他就会自动生成这个图片,我们用浏览器打开如下:
firefox ./a_newname.node.jpg
OK
第五步:我们下面试一下 搞个查询树(Query结构)
继续刚才的调试,因为这个result是Query * 类型的,其代码原型如下:
/*
* transformStmt -
* recursively transform a Parse tree into a Query tree.
*/
Query *
transformStmt(ParseState *pstate, Node *parseTree)
{
Query *result;
/*
* We apply RAW_EXPRESSION_COVERAGE_TEST testing to basic DML statements;
* we can't just run it on everything because raw_expression_tree_walker()
* doesn't claim to handle utility statements.
*/
#ifdef RAW_EXPRESSION_COVERAGE_TEST
switch (nodeTag(parseTree))
{
case T_SelectStmt:
case T_InsertStmt:
case T_UpdateStmt:
case T_DeleteStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
break;
}
#endif /* RAW_EXPRESSION_COVERAGE_TEST */
switch (nodeTag(parseTree))
{
/*
* Optimizable statements
*/
case T_InsertStmt:
result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
break;
case T_DeleteStmt:
result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
break;
case T_UpdateStmt:
result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
break;
case T_SelectStmt:
{
SelectStmt *n = (SelectStmt *) parseTree;
if (n->valuesLists)
result = transformValuesClause(pstate, n);
else if (n->op == SETOP_NONE)
result = transformSelectStmt(pstate, n);
else
result = transformSetOperationStmt(pstate, n);
}
break;
/*
* Special cases
*/
case T_DeclareCursorStmt:
result =
transformDeclareCursorStmt(pstate,(DeclareCursorStmt *) parseTree);
break;
case T_ExplainStmt:
result =
transformExplainStmt(pstate,(ExplainStmt *) parseTree);
break;
case T_CreateTableAsStmt:
result =
transformCreateTableAsStmt(pstate,(CreateTableAsStmt *) parseTree);
break;
default:
/*
* other statements don't require any transformation; just return
* the original parsetree with a Query node plastered on top.
*/
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) parseTree;
break;
}
/* Mark as original query until we learn differently */
result->querySource = QSRC_ORIGINAL;
result->canSetTag = true;
return result;
}
然后和上面的步骤一致,从找到log目录下的最新文件。拷贝内容到node目录下面,新建一个.node的文件。然后执行./pgNodeGraph)他就会自动生成这个图片,我们用浏览器打开即可。
我们的Query结果如下:
{QUERY
:commandType 1
:querySource 0
:canSetTag true
:utilityStmt <>
:resultRelation 0
:hasAggs false
:hasWindowFuncs false
:hasTargetSRFs false
:hasSubLinks false
:hasDistinctOn false
:hasRecursive false
:hasModifyingCTE false
:hasForUpdate false
:hasRowSecurity false
:cteList <>
:rtable (
{RTE
:alias <>
:eref
{ALIAS
:aliasname tb1
:colnames ("a")
}
:rtekind 0
:relid 16384
:relkind r
:tablesample <>
:lateral false
:inh true
:inFromCl true
:requiredPerms 2
:checkAsUser 0
:selectedCols (b 9)
:insertedCols (b)
:updatedCols (b)
:securityQuals <>
}
)
:jointree
{FROMEXPR
:fromlist (
{RANGETBLREF
:rtindex 1
}
)
:quals
{OPEXPR
:opno 96
:opfuncid 65
:opresulttype 16
:opretset false
:opcollid 0
:inputcollid 0
:args (
{VAR
:varno 1
:varattno 1
:vartype 23
:vartypmod -1
:varcollid 0
:varlevelsup 0
:varnoold 1
:varoattno 1
:location 24
}
{CONST
:consttype 23
:consttypmod -1
:constcollid 0
:constlen 4
:constbyval true
:constisnull false
:location 26
:constvalue 4 [ 1 0 0 0 0 0 0 0 ]
}
)
:location 25
}
}
:targetList (
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 1
:vartype 23
:vartypmod -1
:varcollid 0
:varlevelsup 0
:varnoold 1
:varoattno 1
:location 7
}
:resno 1
:resname a
:ressortgroupref 0
:resorigtbl 16384
:resorigcol 1
:resjunk false
}
)
:override 0
:onConflict <>
:returningList <>
:groupClause <>
:groupingSets <>
:havingQual <>
:windowClause <>
:distinctClause <>
:sortClause <>
:limitOffset <>
:limitCount <>
:rowMarks <>
:setOperations <>
:constraintDeps <>
:stmt_location 0
:stmt_len 0
}
生成的图片如下:
OK,以上就是没有经过查询重写的查询树图片。
下面看一下,经过查询重写模块之后的查询树图片(因为这里我进行的只是一个简单的SQL语句,最后的结果可能与上图没有什么太大的区别)
此时经过查询重写的查询树如下:
{QUERY
:commandType 1
:querySource 0
:canSetTag true
:utilityStmt <>
:resultRelation 0
:hasAggs false
:hasWindowFuncs false
:hasTargetSRFs false
:hasSubLinks false
:hasDistinctOn false
:hasRecursive false
:hasModifyingCTE false
:hasForUpdate false
:hasRowSecurity false
:cteList <>
:rtable (
{RTE
:alias <>
:eref
{ALIAS
:aliasname tb1
:colnames ("a")
}
:rtekind 0
:relid 16384
:relkind r
:tablesample <>
:lateral false
:inh true
:inFromCl true
:requiredPerms 2
:checkAsUser 0
:selectedCols (b 9)
:insertedCols (b)
:updatedCols (b)
:securityQuals <>
}
)
:jointree
{FROMEXPR
:fromlist (
{RANGETBLREF
:rtindex 1
}
)
:quals
{OPEXPR
:opno 96
:opfuncid 65
:opresulttype 16
:opretset false
:opcollid 0
:inputcollid 0
:args (
{VAR
:varno 1
:varattno 1
:vartype 23
:vartypmod -1
:varcollid 0
:varlevelsup 0
:varnoold 1
:varoattno 1
:location 24
}
{CONST
:consttype 23
:consttypmod -1
:constcollid 0
:constlen 4
:constbyval true
:constisnull false
:location 26
:constvalue 4 [ 1 0 0 0 0 0 0 0 ]
}
)
:location 25
}
}
:targetList (
{TARGETENTRY
:expr
{VAR
:varno 1
:varattno 1
:vartype 23
:vartypmod -1
:varcollid 0
:varlevelsup 0
:varnoold 1
:varoattno 1
:location 7
}
:resno 1
:resname a
:ressortgroupref 0
:resorigtbl 16384
:resorigcol 1
:resjunk false
}
)
:override 0
:onConflict <>
:returningList <>
:groupClause <>
:groupingSets <>
:havingQual <>
:windowClause <>
:distinctClause <>
:sortClause <>
:limitOffset <>
:limitCount <>
:rowMarks <>
:setOperations <>
:constraintDeps <>
:stmt_location 0
:stmt_len 27
}
)
经过上面两图片的对比,也就仅仅多了个stmt_len
原因在于:我是在下面的transformOptionalSelectInto执行之后 调用的call。所以两图基本上一样
/*
* transformTopLevelStmt -
* transform a Parse tree into a Query tree.
*
* This function is just responsible for transferring statement location data
* from the RawStmt into the finished Query.
*/
Query *
transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree)
{
Query *result;
/* We're at top level, so allow SELECT INTO */
result = transformOptionalSelectInto(pstate, parseTree->stmt);
result->stmt_location = parseTree->stmt_location;
result->stmt_len = parseTree->stmt_len;
return result;
}