TiDB 源码阅读系列文章(五)TiDB SQL Parser 的实现
本文是 TiDB 源码阅读系列文章的第五篇,主要内容围绕 SQL Parser 功能实现进行讲解。内容源自社区伙伴马震(GitHub ID:mz)的投稿。系列文章的目的是与数据库研究者及爱好者深入交流,收到了社区的积极反馈。后续,成交量 源码期待更多伙伴加入 TiDB 的探讨与分享。
TiDB 的源码阅读系列文章,帮助读者系统性地学习 TiDB 内部实现。最近的《SQL 的一生》一文,全面阐述了 SQL 语句处理流程,从接收网络数据、MySQL 协议解析、SQL 语法解析、查询计划制定与优化、执行直至返回结果。
其中,SQL Parser 的功能是将 SQL 语句按照 SQL 语法规则进行解析,将文本转换为抽象语法树(AST)。此功能需要一定背景知识,下文将尝试介绍相关知识,以帮助理解这部分代码。
TiDB 使用 goyacc 根据预定义的 SQL 语法规则文件 parser.y 生成 SQL 语法解析器。这一过程可在 TiDB 的 Makefile 文件中看到,通过构建 goyacc 工具,使用 goyacc 依据 parser.y 生成解析器 parser.go。
goyacc 是 yacc 的 Golang 版本,因此理解语法规则定义文件 parser.y 及解析器工作原理之前,需要对 Lex & Yacc 有所了解。Lex & Yacc 是用于生成词法分析器和语法分析器的工具,它们简化了编译器的编写。
下文将详细介绍 Lex & Yacc 的工作流程,以及生成解析器的过程。我们将从 Lex 根据用户定义的 patterns 生成词法分析器,词法分析器读取源代码并转换为 tokens 输出,以及 Yacc 根据用户定义的语法规则生成语法分析器等角度进行阐述。
生成词法分析器和语法分析器的过程,用户需为 Lex 提供 patterns 的定义,为 Yacc 提供语法规则文件。这两种配置都是文本文件,结构相同,分为三个部分。我们将关注中间规则定义部分,并通过一个简单的例子来解释。
Lex 的输入文件中,规则定义部分使用正则表达式定义了变量、phpcms实现源码整数和操作符等 token 类型。例如整数 token 的定义,当输入字符串匹配正则表达式时,大括号内的动作会被执行,将整数值存储在变量yylval 中,并返回 token 类型 INTEGER 给 Yacc。
而 Yacc 的语法规则定义文件中,第一部分定义了 token 类型和运算符的结合性。四种运算符都是左结合,同一行的运算符优先级相同,不同行的运算符,后定义的行具有更高的优先级。语法规则使用 BNF 表达,大部分现代编程语言都可以使用 BNF 表示。
表达式解析是生成表达式的逆向操作,需要将语法树归约到一个非终结符。Yacc 生成的语法分析器使用自底向上的归约方式进行语法解析,同时使用堆栈保存中间状态。通过一个表达式 x + y * z 的解析过程,我们可以理解这一过程。
在这一过程中,读取的 token 压入堆栈,当发现堆栈中的内容匹配了某个产生式的右侧,则将匹配的项从堆栈中弹出,将该产生式左侧的非终结符压入堆栈。这个过程持续进行,直到读取完所有的 tokens,并且只有启始非终结符保留在堆栈中。
产生式右侧的大括号中定义了该规则关联的动作,例如将三项从堆栈中弹出,两个表达式相加,结果再压回堆栈顶。这里可以使用 $position 的形式访问堆栈中的项,$1 引用第一项,$2 引用第二项,以此类推。$$ 代表归约操作执行后的堆栈顶。本例的动作是将三项从堆栈中弹出,两个表达式相加,结果再压回堆栈顶。
在上述例子中,动作不仅完成了语法解析,还完成了表达式求值。一般希望语法解析的结果是一颗抽象语法树(AST),可以定义语法规则关联的phpweb漏洞源码动作。这样,解析完成时,我们就能得到由 nodeType 构成的抽象语法树,对这个语法树进行遍历访问,可以生成机器代码或解释执行。
至此,我们对 Lex & Yacc 的原理有了大致了解,虽然还有许多细节,如如何消除语法的歧义,但这些概念对于理解 TiDB 的代码已经足够。
下一部分,我们介绍 TiDB SQL Parser 的实现。有了前面的背景知识,对 TiDB 的 SQL Parser 模块的理解会更易上手。TiDB 使用手写的词法解析器(出于性能考虑),语法解析采用 goyacc。我们先来看 SQL 语法规则文件 parser.y,这是生成 SQL 语法解析器的基础。
parser.y 文件包含 多行代码,初看可能令人感到复杂,但该文件仍然遵循我们之前介绍的结构。我们只需要关注第一部分 definitions 和第二部分 rules。
第一部分定义了 token 类型、优先级、结合性等。注意 union 结构体,它定义了在语法解析过程中被压入堆栈的项的属性和类型。压入堆栈的项可能是终结符,也就是 token,它的类型可以是 item 或 ident;也可能是非终结符,即产生式的左侧,它的类型可以是 expr、statement、item 或 ident。
goyacc 根据这个 union 在解析器中生成对应的 struct。在语法解析过程中,非终结符会被构造成抽象语法树(AST)的节点 ast.ExprNode 或 ast.StmtNode。抽象语法树相关的数据结构定义在 ast 包中,它们大都实现了 ast.Node 接口。
ast.Node 接口有一个 Accept 方法,接受 Visitor 参数,后续对 AST 的处理主要依赖这个 Accept 方法,以 Visitor 模式遍历所有的节点以及对 AST 做结构转换。例如 plan.preprocess 是对 AST 做预处理,包括合法性检查以及名字绑定。ghost blog源码
union 后面是对 token 和非终结符按照类型分别定义。第一部分的最后是对优先级和结合性的定义。文件的第二部分是 SQL 语法的产生式和每个规则对应的 aciton。SQL 语法非常复杂,大部分内容都是产生式的定义。例如 SELECT 语法的定义,我们可以在 parser.y 中找到 SELECT 语句的产生式。
完成语法规则文件 parser.y 的定义后,使用 goyacc 生成语法解析器。TiDB 对 lexer 和 parser.go 进行封装,对外提供 parser.yy_parser 进行 SQL 语句的解析。
最后,我们通过一个简单的例子,使用 TiDB 的 SQL Parser 进行 SQL 语法解析,构建出抽象语法树,并通过 visitor 遍历 AST。我实现的 visitor 只输出节点的类型,运行结果依次输出遍历过程中遇到的节点类型。
了解 TiDB SQL Parser 的实现后,我们有可能实现当前不支持的语法,如添加内置函数。这为我们学习查询计划以及优化打下了基础。希望这篇文章对读者有所帮助。
作者介绍:马震,金蝶天燕架构师,负责中间件、大数据平台的研发,今年转向 NewSQL 领域,关注 OLTP/AP 融合,目前在推动金蝶下一代 ERP 引入 TiDB 作为数据库存储服务。
为ä»ä¹sparkSQL
SharkåsparkSQL ä½æ¯ï¼éçSparkçåå±ï¼å ¶ä¸sparkSQLä½ä¸ºSparkçæçä¸å继ç»åå±ï¼èä¸ååéäºhiveï¼åªæ¯å ¼å®¹hiveï¼èhive on sparkæ¯ä¸ä¸ªhiveçåå±è®¡åï¼è¯¥è®¡åå°sparkä½ä¸ºhiveçåºå±å¼æä¹ä¸ï¼ä¹å°±æ¯è¯´ï¼hiveå°ä¸ååéäºä¸ä¸ªå¼æï¼å¯ä»¥éç¨map-reduceãTezãsparkçå¼æã
ããShark为äºå®ç°Hiveå ¼å®¹ï¼å¨HQLæ¹é¢éç¨äºHiveä¸HQLç解æãé»è¾æ§è¡è®¡åç¿»è¯ãæ§è¡è®¡åä¼åçé»è¾ï¼å¯ä»¥è¿ä¼¼è®¤ä¸ºä» å°ç©çæ§è¡è®¡åä»MRä½ä¸æ¿æ¢æäºSparkä½ä¸ï¼è¾ 以å ååå¼åå¨çåç§åHiveå ³ç³»ä¸å¤§çä¼åï¼ï¼åæ¶è¿ä¾èµHive MetastoreåHive SerDeï¼ç¨äºå ¼å®¹ç°æçåç§Hiveåå¨æ ¼å¼ï¼ãè¿ä¸çç¥å¯¼è´äºä¸¤ä¸ªé®é¢ï¼ç¬¬ä¸æ¯æ§è¡è®¡åä¼åå®å ¨ä¾èµäºHiveï¼ä¸æ¹ä¾¿æ·»å æ°çä¼åçç¥ï¼äºæ¯å 为MRæ¯è¿ç¨çº§å¹¶è¡ï¼å代ç çæ¶åä¸æ¯å¾æ³¨æ线ç¨å®å ¨é®é¢ï¼å¯¼è´Sharkä¸å¾ä¸ä½¿ç¨å¦å¤ä¸å¥ç¬ç«ç»´æ¤çæäºè¡¥ä¸çHiveæºç åæ¯ï¼è³äºä¸ºä½ç¸å ³ä¿®æ¹æ²¡æå并å°Hive主线ï¼æä¹ä¸å¤ªæ¸ æ¥ï¼ã
ããæ¤å¤ï¼é¤äºå ¼å®¹HQLãå éç°æHiveæ°æ®çæ¥è¯¢åæ以å¤ï¼Spark SQLè¿æ¯æç´æ¥å¯¹åçRDD对象è¿è¡å ³ç³»æ¥è¯¢ãåæ¶ï¼é¤äºHQL以å¤ï¼Spark SQLè¿å 建äºä¸ä¸ªç²¾ç®çSQL parserï¼ä»¥åä¸å¥Scala DSLãä¹å°±æ¯è¯´ï¼å¦æåªæ¯ä½¿ç¨Spark SQLå 建çSQLæ¹è¨æScala DSL对åçRDD对象è¿è¡å ³ç³»æ¥è¯¢ï¼ç¨æ·å¨å¼åSparkåºç¨æ¶å®å ¨ä¸éè¦ä¾èµHiveçä»»ä½ä¸è¥¿ã
当前市面上的代码审计工具哪个比较好?
1. 第一类:Seay源代码审计系统
这是一款基于C#语言开发的PHP代码安全性审计系统,主要运行于Windows平台。它能发现SQL注入、代码执行、命令执行等多种漏洞,覆盖了常见的PHP安全问题。该软件提供了一键审计、代码调试、函数定位等功能,并支持插件扩展和自定义规则配置。此外,它还具备代码高亮、编码调试转换以及数据库执行监控等强大功能。
2. 第二类:Fortify SCA
Fortify SCA是惠普研发的商业软件产品,专注于源代码的java 加法源码白盒安全审计。作为一款收费软件,它提供了跨平台的Windows、Linux、Unix以及Mac版本。该工具通过内置的五大主要分析引擎对应用软件的源代码进行静态分析。
3. 第三类:RIPS
RIPS是一款基于PHP的开源代码安全审计工具,由国外安全研究员开发。尽管程序体积小巧(仅有KB),但它能调用PHP内置解析器接口token_get_all,并使用Parser进行语法分析,实现跨文件的变量及函数追踪。RIPS在扫描结果中直观地展示漏洞形成过程及变量传递,具有较低的误报率。它能发现SQL注入、XSS跨站、文件包含等多种漏洞,并提供多种样式的代码高亮显示。
sqlite怎样利用查询结果来创建新表?
当我们讨论sqlite时,如何利用查询结果创建新表是一个关键点。若要解答这一问题,我们需对sqlite的内部运作有深入理解。以《教父》中的名言作为引子,表明了慷慨与个人情感的联系,这里我们则将慷慨理解为对知识的热爱与分享。在忙碌的生活中,徒步成为了一种放松方式,尽管心中仍有未解之症结,但户外的风景与人情带来了一丝慰藉。工作与生活并重,自我成长与进步是追求的目标。
在没有担任团队领导(TL)后,内心可能有失落感,但这也促使了对sqlite源码的探索。学习规划与安排工作,使思路更加条理化,这是一种自我提升的过程。同时,享受生活与做好工作并行不悖。
近期的更新较为缓慢,原因在于深入研究sqlite源码,理解其核心部分,例如VBE与树结构的组织。我们采用情景分析的方法,从创建表开始,梳理创建表的流程,进而理解VBE的运行流程和数据存储方式。sqlite在解析SQL语句时使用了名为lemon的解析器与生成器,这一细节在知乎上有详细介绍。
无需深入解析词法分析的细节,重要的是理解如何从输入的SQL语句跳转到功能实现,通过查看解析器的工作方式。让我们一起探索,从创建表的实现开始。
创建表的关键SQL语句是:
sql
CREATE TABLE PERSON(ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL);
选择这一语句是因为它能展示不同数据类型的处理,并保持简单。在处理输入时,函数process_input负责逐行处理,直至完成语句。在函数执行过程中,我们关注到runOneSqlLine,这是将SQL语句翻译为sqlite3_stmt并执行的关键步骤。这一过程涉及sqlite3_prepare_v2、sqlite3LockAndPrepare、sqlite3Prepare、sqlite3RunParser、sqlite3Parser等函数。
解析SQL语句的核心在于一个名为yyParser的结构,它构建了一个符号表,用于标识SQL语句。搜索关键词“create table”导向了关键代码片段,揭示了创建表函数的调用过程。关注sqlite3StartTable与sqlite3EndTable,理解它们在创建新表记录中的作用。这些函数负责在内存中构建新的表表示,尤其在“CREATE”和“TABLE”出现时被调用。在创建表结束时,sqlite3EndTable()完成构建过程。
创建表的实现涉及函数调用和信息填充,确保新表记录正确初始化。理解这一过程是解答如何利用查询结果创建新表的关键。关注细节,如参数传递与函数逻辑,对于深入学习sqlite源码至关重要。
MySQL数据库脚本检查结果chkmysqlsh
MySQL数据库脚本检查结果
在进行数据库脚本编写、部署和升级时,往往容易出现错误或问题,这需要进行数据库脚本检查,以确保数据的准确性和稳定性。MySQL数据库脚本检查可以帮助我们检查每个脚本文件的语法和逻辑错误,以及确保脚本与目标数据库的兼容性。
对于MySQL数据库脚本检查,我们可以使用一些开源工具来帮助我们自动化检查和验证:
1. SQLCheck
SQLCheck是一种用于自动化SQL语法和风格检查的开源工具。它可以检查SQL是否符合SQL-标准,还可以在检查后自动修改SQL以符合标准。
使用SQLCheck进行MySQL数据库脚本检查:
– 安装SQLCheck:可以通过源代码或其他方式安装SQLCheck。
– 指定目标MySQL数据库:在检查之前,需要指定MySQL数据库的位置和连接信息。
– 执行检查:使用SQLCheck工具扫描所有脚本文件。它将输出每个文件的错误和警告信息,以及建议的更正措施。
2. SQL Parser
SQL Parser是另一种开源工具,用于解析SQL语句并生成SQL抽象语法树。除了解析SQL,它还提供了许多功能,例如词法分析、语法分析、语法树构造和转换。
使用SQL Parser进行MySQL数据库脚本检查:
– 安装SQL Parser:可以通过源代码或其他方式安装SQL Parser。
– 解析脚本文件:使用SQL Parser解析所有脚本文件,并将它们转换为SQL AST。
– 检查AST:检查SQL AST,以查找语法和逻辑错误。这可以通过使用自己的AST分析器来完成,也可以使用预构建的解析器/分析器。
3. MySQL Workbench
MySQL Workbench是由MySQL开发的用于设计、开发和管理MySQL数据库的工具。它包括一个SQL编辑器,支持语法高亮和自动完成,还具有自动修复和错误检测功能,可检查SQL语句的正确性。
使用MySQL Workbench进行MySQL数据库脚本检查:
– 安装MySQL Workbench:可以通过MySQL官网或其他方式安装MySQL Workbench。
– 打开SQL编辑器:进入MySQL Workbench,点击“File”菜单,选择“New Query Tab”打开SQL编辑器。
– 输入或导入脚本:将要检查的脚本文件输入或通过导入功能添加到SQL编辑器中。
– 检查脚本:在SQL编辑器中,MySQL Workbench会自动检查语法错误、逻辑错误和代码错误。在检查到错误时,MySQL Workbench会在脚本代码中显示错误行并显示有关错误的详细信息。
以上是三种常用的MySQL数据库脚本检查工具。在实际应用中,还可以根据自身需要进行扩展和定制,例如制定自己的语法规则、增加自定义检查项等。无论是什么方法,关键是检查每个脚本文件,以确保其与目标数据库的兼容性和准确性。
Mybatis:PageHelper分页插件源码及原理剖析
PageHelper是一款强大的Mybatis分页插件,以其开源和免费的特性受到赞誉。其功能复杂性远超初印象,实现了物理分页的强大与彻底。核心在于保持分页插件的基本功能,同时提供智能参数以适应复杂场景。
基本使用与配置方面,PageHelper依赖于jsqlparser包,Maven会自动引入。关键参数dialect = mysql,与oracle等其他数据库对应参数可根据需要选择使用或避免使用,保持分页插件的简洁性。
源码分析中,PageHelper包含SqlUtil、SqlUtilConfig等类,负责数据库类型专用SQL工具的管理与执行,如count查询、分页查询等。自动检测当前数据库方言的机制和缓存策略提高了插件的灵活性与效率。
PageSqlSource类展示了如何解析SQL并添加分页参数,确保分页查询的正确执行。Parser类则负责解析SQL并添加所需分页参数,实现分页功能的关键步骤。
执行分页查询的`SqlUtil.doProcessPage()`方法,展示了创建count查询、执行分页查询、修改参数列表等关键步骤,确保分页查询的高效执行。
PageHelper提供了两种使用方式:直接通过RowBounds参数进行分页查询和使用静态方法`PageHelper.startPage()`。其原理在于使用ThreadLocal传递和保存Page对象,每次查询时需单独设置,以保持分页功能的灵活性。
最佳使用建议包括明确指定方言以及编写SQL分页业务与对应的count查询,避免简化操作以提高性能。推荐进一步阅读相关聚合内容,如SpringBoot、设计模式、Mybatis、多线程等,以深入理解其应用与优化。
代码审计服务找哪家更省钱?
第一类:Seay源代码审计系统
这是基于C#语言开发的一款针对PHP代码安全性审计的系统,主要运行于Windows系统上。这款软件能够发现SQL注入、代码执行、命令执行、文件包含、文件上传、绕过转义防护、拒绝服务、XSS跨站、信息泄露、任意URL跳转等漏洞,基本上覆盖常见的PHP漏洞。在功能上,它支持一键审计、代码调试、函数定位、插件扩展、自定会规则配置、代码高亮、编码调试转换、数据库执行监控等数十项强大功能。
第二类:Fortify SCA
Fortify
SCA是由惠普研发的一款商业软件产品,针对源代码进行专业的白盒安全审计。当然,它是收费的,而且这种商业软件一般都价格不菲。它有Windows、Linux、Unix以及Mac版本,通过内置的五大主要分析引擎对应用软件的源代码进行静态分析。
第三类:RIPS
RIPS是一款基于PHP开发的针对PHP代码安全审计的软件。另外,它也是一款开源软件,由国外安全研究员开发,程序只有KB,目前能下载到的最新版本是0.,不过这款程序已经停止更新了。它最大的亮点在于调用了PHP内置解析器接口token_get_all,并且使用Parser做了语法分析,实现了跨文件的变量及函数追踪,扫描结果中非常直观地展示了漏洞形成及变量传递过程,误报率非常低。RIPS能够发现SQL注入、XSS跨站、文件包含、代码执行、文件读取等多种漏洞,文件多种样式的代码高亮。
shardingsphere源码阅读-SQL解析引擎
shardingsphere的核心功能之一是分片,其中SQL解析是关键步骤。它由SQL解析引擎执行,该过程涉及词法和语法解析,将SQL语句分解为不可再分的单词并理解其结构。解析结果包括表、条件、排序、分组等元素,最终形成抽象语法树。在shardingsphere中,SQL解析过程会生成SQLStatement对象,如InsertStatement,它封装了SQL片段及其相关信息,如插入字段的位置。
SQLParserEngine的初始化和使用始于AbstractRuntimeContext的构造函数。SQLParserEngineFactory负责创建和缓存不同数据库类型的SQL解析引擎,如ANTLR。在SQLParserEngine的parse方法中,会使用ParsingHook进行跟踪,解析前调用start,成功后调用finishSuccess,异常时调用finishFailure。实际解析工作由SQLParserExecutor完成,它将SQL解析为ParseTree,再由ParseTreeVisitor创建SQLStatement。
SQLParserEngine的入口与分库分表操作紧密相关,shardingsphere通过ShardingStatement类来执行SQL,类似于JDBC的Statement。在prepare方法中,通过SQLParserEngine创建SimpleQueryPrepareEngine,该引擎负责预处理SQL执行的必要信息,如路由和重写结果。具体细节将在后续的SQL路由和重写分析中深入探讨。
2024-12-23 23:46
2024-12-23 23:28
2024-12-23 23:19
2024-12-23 22:41
2024-12-23 21:38