This commit is contained in:
by931
2022-09-06 22:30:37 +08:00
parent 66970f3e38
commit 3d6528675a
796 changed files with 3382 additions and 3382 deletions

View File

@@ -200,7 +200,7 @@ function hide_canvas() {
<p id="tip" align="center"></p>
<div><h1>16 解析引擎SQL 解析流程应该包括哪些核心阶段?(下)</h1>
<p>我们知道整个 SQL 解析引擎可以分成三个阶段(如下图所示),上一课时我们主要介绍了 ShardingSphere 中 SQL 解析引擎的第一个阶段,那么今天我将承接上一课时,继续讲解 ShardingSphere 中 SQL 解析流程中剩余的两个阶段。</p>
<p><img src="assets/Ciqc1F8ry7-AWFaOAACKmUmdLPs289.png" alt="Drawing 0.png" /></p>
<p><img src="assets/Ciqc1F8ry7-AWFaOAACKmUmdLPs289.png" alt="png" /></p>
<h3>SQL 解析引擎的三大阶段</h3>
<p>在 SQL 解析引擎的第一阶段中,我们详细介绍了 ShardingSphere 生成 SQL 抽象语法树的过程,并引出了 SQLStatementRule 规则类。今天我们将基于这个规则类来分析如何提取 SQLSegment 以及如何填充 SQL 语句的实现机制。</p>
<h4>1.第二阶段:提取 SQL 片段</h4>
@@ -222,7 +222,7 @@ function hide_canvas() {
&lt;/sql-statement-rule-definition&gt;
</code></pre>
<p>基于 ParseRuleRegistry 类进行规则获取和处理过程,涉及一大批实体对象以及用于解析 XML 配置文件的 JAXB 工具类的定义,内容虽多但并不复杂。核心类之间的关系如下图所示:</p>
<p><img src="assets/Ciqc1F8ry9CAPtDdAACEYYKrCTU070.png" alt="Drawing 2.png" /></p>
<p><img src="assets/Ciqc1F8ry9CAPtDdAACEYYKrCTU070.png" alt="png" /></p>
<p>ParseRuleRegistry 类层结构图</p>
<p>当获取规则之后,对于具体某种数据库类型的每条 SQL 而言,都会有一个 SQLStatementRule 对象。我们注意到每个 SQLStatementRule 都定义了一个“context”以及一个“sql-statement-class”。</p>
<p>这里的 context 实际上就是通过 SQL 解析所生成的抽象语法树 SQLAST 中的 ParserRuleContext包括 CreateTableContext、SelectContext 等各种 StatementContext。而针对每一种 context都有专门的一个 SQLStatement 对象与之对应,那么这个 SQLStatement 究竟长什么样呢?我们来看一下。</p>
@@ -254,7 +254,7 @@ function hide_canvas() {
<pre><code>SELECT task_id, task_name FROM health_task WHERE user_id = 'user1' AND record_id = 2
</code></pre>
<p>通过解析,我们获取了如下所示的抽象语法树:</p>
<p><img src="assets/Ciqc1F8ry_WAEwAzAACKQ3CnEFw961.png" alt="Drawing 4.png" /></p>
<p><img src="assets/Ciqc1F8ry_WAEwAzAACKQ3CnEFw961.png" alt="png" /></p>
<p>抽象语法树示意图</p>
<p>我们发现,对于上述抽象语法树中的某些节点(如 SELECT、FROM 和 WHERE没有子节点而对于如 FIELDS、TABLES 和 CONDITIONS 节点而言,本身也是一个树状结构。显然,这两种节点的提取规则应该是不一样的。</p>
<p>因此ShardingSphere 提供了两种 SQLSegmentExtractor一种是针对单节点的 OptionalSQLSegmentExtractor另一种是针对树状节点的 CollectionSQLSegmentExtractor。由于篇幅因素这里以 TableExtractor 为例,展示如何提取 TableSegment 的过程TableExtractor 的实现方法如下所示:</p>
@@ -363,11 +363,11 @@ function hide_canvas() {
}
</code></pre>
<p>这段代码在实现上采用了回调机制来完成对象的注入。在 ShardingSphere 中,基于回调的处理方式也非常普遍。本质上,回调解决了因为类与类之间的相互调用而造成的循环依赖问题,回调的实现策略通常采用了如下所示的类层结构:</p>
<p><img src="assets/Ciqc1F8rzBeAL-gtAAAtxVTlOkM440.png" alt="Drawing 6.png" /></p>
<p><img src="assets/Ciqc1F8rzBeAL-gtAAAtxVTlOkM440.png" alt="png" /></p>
<p>回调机制示意图</p>
<p>TableFiller 中所依赖的 TableSegmentAvailable 和 TableSegmentsAvailable 接口就类似于上图中的 Callback 接口,具体的 SQLStatement 就是 Callback 的实现类,而 TableFiller 则是 Callback 的调用者。以 TableFiller 为例,我们注意到,如果对应的 SQLStatement 实现了这两个接口中的任意一个,那么就可以通过 TableFiller 注入对应的 TableSegment从而完成 SQLSegment 的填充。</p>
<p>这里以 TableSegmentAvailable 接口为例,它有一组实现类,如下所示:</p>
<p><img src="assets/CgqCHl8rzC2ADPHvAAAxxRKUUYw921.png" alt="Drawing 8.png" /></p>
<p><img src="assets/CgqCHl8rzC2ADPHvAAAxxRKUUYw921.png" alt="png" /></p>
<p>TableSegmentAvailable实现类</p>
<p>以上图中的 CreateTableStatement 为例,该类同时实现了 TableSegmentAvailable 和 IndexSegmentsAvailable 这两个回调接口,所以就可以同时操作 TableSegment 和 IndexSegment 这两个 SQLSegment。CreateTableStatement 类的实现如下所示:</p>
<pre><code>public final class CreateTableStatement extends DDLStatement implements TableSegmentAvailable, IndexSegmentsAvailable {
@@ -380,7 +380,7 @@ function hide_canvas() {
}
</code></pre>
<p>至此,我们通过一个示例解释了与填充操作相关的各个类之间的协作关系,如下所示的类图展示了这种协作关系的整体结构。</p>
<p><img src="assets/CgqCHl8rzDqAVtDCAAB-8xyeFnI893.png" alt="Drawing 9.png" /></p>
<p><img src="assets/CgqCHl8rzDqAVtDCAAB-8xyeFnI893.png" alt="png" /></p>
<p>SQLStatement类层结构图</p>
<p>有了上图的基础,我们理解填充引擎 SQLStatementFillerEngine 就显得比较简单了SQLStatementFillerEngine 类的实现如下所示:</p>
<pre><code>public final class SQLStatementFillerEngine {