PostgreSQL的学习心得和知识总结(七)|分享工具以及select语句解析过程的分析树和语法树图示

文章目录

工具分享和使用

注:这是前面我给大家说的那个 《小宇带你学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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43949535/article/details/104023090