SQL - 词法/语法分析各种方案总结

news/2024/5/19 17:00:17 标签: java, antlr, flex, yacc

文章目录

  • SQL 词法/语法分析
    • 一、技术实现方案
      • 1. Lex + Yacc
      • 2. ANTLR
      • 3. ANTLR 的 IDE ——ANTLRWorks2
      • 4. jsqlparser - 基于javacc实现的解析器JSqlParser
      • 4. Javacc/AST

SQL 词法/语法分析

一、技术实现方案

1. Lex + Yacc

  • Lex:词法分析 - flex
  • Yacc:语法分析 - bison
  • 搭载语言:C语言
  • 弊端:移植性不够,需要和以自动机为原理的YACC/LEX生成的一大堆整数表打交道。
    相当于从零开始造轮子,遂短时间不建议实验。
    不过也建议花十分钟简单看一下,在整个领域,他们实在太经典了,后面轻量级的 ANTLR 里面都有他们的思想。

2. ANTLR

  1. 参考资料:

    • atitit java解析sql语言解析器解释器的实现:https://blog.csdn.net/sdfhncfw/article/details/83767839#_Toc21097
    • ANTLR百度百科:https://baike.baidu.com/item/antlr/9368750?fr=aladdin
    • 开源语法分析器–ANTLR:https://blog.csdn.net/kabini/article/details/1602618

  2. ANTLR 总览:
    工作总流程:词法分析,而后进行语法分析,语义分析,构建sql的AST

    • 词法分析器(Lexer):
      词法分析器的工作是分析量化那些本来毫无意义的字符流,将他们翻译成离散的字符组(也就是一个一个的Token),包括关键字,标识符,符号(symbols)和操作符 供语法分析器使用。
    • 语法分析器(Parser):
      编译器又称为Syntactical analyser。在分析字符流的时候,Lexer不关心所生成的单个Token的语法意义及其与上下文之间的关系,而这就是Parser的工作。语法分析器将收到的Tokens组织起来,并转换成为目标语言语法定义所允许的序列。

      无论是Lexer还是Parser都是一种识别器,Lexer是字符序列识别器而Parser是Token序列识别器。他们在本质上是类似的东西,而只是在分工上有所不同而已。
    • 树分析器 (tree parser):
      树分析器可以用于对语法分析生成的抽象语法树进行遍历,并能执行一些相关的操作。
    • ANTLR:
      ANTLR将上述结合起来,它允许我们定义识别字符流的词法规则 和 用于解释Token流的语法分析规则。
      然后,ANTLR将根据用户提供的语法文件自动生成相应的词法/语法分析器。
      用户可以利用他们将输入的文本进行编译,并转换成其他形式(如AST—Abstract Syntax Tree,抽象的语法树)。

  3. ANTLR环境安装:

    • ANTLR安装:
      windows上安装配置 ANTLR:建议使用 ANTLR-4.8版本
      https://www.cnblogs.com/wynjauu/articles/9872822.html
      ANTLR的4.4和其他版本不一致,会有以下的问题,解决方案如下。
      找不到或无法加载主类 org.antlr.v4.gui.TestRig:https://blog.csdn.net/weixin_43171083/article/details/90510771

    • idea 中配置 ANTLR:
      https://blog.csdn.net/qq_21383435/article/details/80814618

  4. ANTLR 语法结构:

    • 参考资料:
      antlr4 实战 idea:https://blog.csdn.net/qq_39158142/article/details/86437919
      简洁如下:
    antlrv4">    //声明语法头,类似于java类的定义
        grammar Name; 
    
        //选项,如语言选项,输出选项,回溯选项,记忆选项等等
        options {...}
        import ... ;
    
        // 符号(Token)名大写开头
        // 解析规则(Parser rule)名小写开头,后面可以跟字母、数字、下划线    
        tokens {...}
        channels {...} // lexer only
        @actionName {...}
    
        // rule - 这是核心,表示规则,以 “:” 开始, “;” 结束, 多规则以 "|" 分隔。    
        rule1 // parser and lexer rules, possibly intermingled
        ...
        ruleN
    
  5. ANTLR 使用流程:
    ANTLR 官方demo:https://github.com/antlr/grammars-v4 。
    官方仓库中提供了 ANTLR 多个方向的 ANTLR,关于 SQL 的 词法/语法解析,该仓库中也有提供。
    ANTLR使用过程如下

    • 编写文法文件(g4后缀)
    • 使用g4文法文件通过antlr工具生成lexer词法解析器、parser语法解析器、visitor和listener的runtime target文件(支持多种语言)
    • 直接使用输出的runtime target文件模块进行开发工作
  6. ANTLR 实战:
    参考我的另一篇博客:ANTLR 实战 SQL 词法/语法分析
    https://blog.csdn.net/pentiumCM/article/details/106077486


3. ANTLR 的 IDE ——ANTLRWorks2

  • 参考资料:https://blog.csdn.net/u014454538/article/details/86351781


javaccJSqlParser_86">4. jsqlparser - 基于javacc实现的解析器JSqlParser

  1. 调用 Java 的 jar 包(相当于直接使用别人封装好的,自己拿过来使用即可)
  2. 搭载语言:java
  3. 参考资料:
    • JSqlParser——SQL语法解释器:SQL解析样例:https://blog.csdn.net/Gigaplant/article/details/80452892
      JSQLParser 来分析复杂SQL:https://www.jianshu.com/p/f57bc22b5b32
      JSqlParser系列之二代码结构:https://www.cnblogs.com/liuwt0911/p/4420472.html
      jsqlparser解析SQL工具类:https://blog.csdn.net/qq_26458903/article/details/89923522

  4. 使用方式:
    • maven 的 pom.xml 中添加如下的依赖:
      <!-- jsqlparser解析SQL工具类-->
      <dependency>
          <groupId>com.github.jsqlparser</groupId>
          <artifactId>jsqlparser</artifactId>
          <version>1.4</version>
      </dependency>
      
    • jsqlparser 语法demo:
      java">package indi.pentiumcm.utils;
      
      import net.sf.jsqlparser.JSQLParserException;
      import net.sf.jsqlparser.expression.Expression;
      import net.sf.jsqlparser.parser.CCJSqlParser;
      import net.sf.jsqlparser.parser.CCJSqlParserUtil;
      import net.sf.jsqlparser.parser.ParseException;
      import net.sf.jsqlparser.schema.Column;
      import net.sf.jsqlparser.statement.Statement;
      import net.sf.jsqlparser.statement.Statements;
      import net.sf.jsqlparser.statement.select.Select;
      import net.sf.jsqlparser.statement.select.SelectBody;
      import net.sf.jsqlparser.util.AddAliasesVisitor;
      import net.sf.jsqlparser.util.SelectUtils;
      import net.sf.jsqlparser.util.TablesNamesFinder;
      
      import java.util.List;
      
      /**
       * @projName: JavaSkillStack
       * @packgeName: indi.pentiumcm.utils
       * @className: jsqlparserUtils
       * @author: pentiumCM
       * @email: 842679178@qq.com
       * @date: 2020/5/11 22:22
       * @describe: jsqlparser工具类
       */
      public class jsqlparserUtils {     
      
          public static void main(String[] args) throws JSQLParserException, ParseException {
      
              //1、简单的语句解析
              // 1.1 单个语句
              String sql = "SELECT * FROM TABLE1";
              //方法1
              Statement statement = CCJSqlParserUtil.parse(sql);
              //方法2
              CCJSqlParser ccjSqlParser = new CCJSqlParser(sql);
              Statement statement2 = ccjSqlParser.Statement();
      
              // 1.2 多个语句
              String sqls = "SELECT * FROM TABLE1;SELECT * FROM TABLE2";
              //方法1
              Statements statements = CCJSqlParserUtil.parseStatements(sqls);
              //方法2
              CCJSqlParser ccjSqlParser2 = new CCJSqlParser(sqls);
      //        Statements statements3 = ccjSqlParser.Statements();
      
              List<Statement> statementList = statements.getStatements();
      
      
              // 2、简单的表达式解析
              //表达式
              Expression expression = CCJSqlParserUtil.parseExpression("a+b*c");
              //条件表达式
              Expression expression2 = CCJSqlParserUtil.parseCondExpression("A='123'");
      
      
              // 3、从SQL中提取表名
              String sql3 = "SELECT * FROM TABLE1";
              Statement statement3 = CCJSqlParserUtil.parse(sql);
              TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
              List<String> tableList = tablesNamesFinder.getTableList(statement);
      
      
              // 4、将别名应用于所有表达式
              Select select = (Select) CCJSqlParserUtil.parse("SELECT A,B,C FROM TABLE1");//此处的运行时类是Select
              SelectBody selectBody = select.getSelectBody();
              AddAliasesVisitor addAliasesVisitor = new AddAliasesVisitor();
              addAliasesVisitor.setPrefix("B");//设置前缀(如不进行设置默认为“A”)
              selectBody.accept(addAliasesVisitor);
              System.out.println(selectBody.toString());//SELECT A AS B1, B AS B2, C AS B3 FROM TABLE1
      
      
              // 5、向SELECT添加一列或表达式
              Select select5 = (Select) CCJSqlParserUtil.parse("SELECT A FROM TABLE1");
              SelectUtils.addExpression(select, new Column("B"));
              System.out.println(select);//SELECT A, B FROM TABLE1
          }
      }
      

4. Javacc/AST

  1. Javacc/AST简介:
    JavaCC 是一个代码生成器,可以根据输入的语言定义输出一个词法分析器和解析器,JavaCC 输出的代码是合法的可编译Java代码.

    解析器和词法分析器本身就是一个冗长而复杂的组件,手工编写一个这样的程序需要仔细考虑各条件的相互作用,总的来说,通过javacc完成一些字符串的分析,还是比较方便,现在普遍使用AST了。

http://www.niftyadmin.cn/n/1663810.html

相关文章

【MySQL学习笔记】3:使JDBC后端程序和MySQL数据库在局域网内分离

简述 上一篇中&#xff0c;MySQL数据库和后端程序都放在了我的32位CentOS上&#xff0c;这台240块的电脑很不好用&#xff0c;假期希望用另一台稍微好点的学习JDBC。数据库服务还是用那台CentOS上的MySQL&#xff0c;尝试在这台windows上连接它。 数据库驱动跟着后端程序跑&a…

【MySQL学习笔记】4:解决JDBC连接MySQL的query结果中文乱码问题

数据库编码 在之前创建testJDBC这个数据库的时候&#xff0c;使用的语句是&#xff1a; CREATE DATABASE IF NOT EXISTS testJDBC DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 指明了数据库编码是UTF-8。注意utf8和utf-8只是写法上的不同&#xff0c;遇到就直接看成utf-…

欢乐的票圈重构——九宫格控件(上)

项目重构的Git地址&#xff1a; github.com/razerdp/Fri… 本文控件Git地址: github.com/razerdp/Pho… 上集&#xff1a;欢乐的票圈重构之旅——RecyclerView的头尾布局增加 下集&#xff1a;欢乐的票圈重构——九宫格控件&#xff08;中上&#xff09; ps:票圈重构已经合并到…

【Java学习笔记】55:JDBC-MySQL基本使用,游标控制,CONCUR_UPDATABLE,更新/添加/删除

配置了这么久终于可以学习JDBC了&#xff0c;在这之前&#xff0c;给刚刚的表多插入一些表项&#xff1a; mysql> USE newDB; Database changed mysql> INSERT INTO NewUsr-> (id,name,age)-> VALUES-> (1,lzh,20),-> (3,sb,17),-> (2,奥特曼,9),-> (…

【Java学习笔记】56:多线程基础回顾

图示 多线程并发的情况&#xff0c;交替占用CPU资源。 实现多线程的方式 继承Thread类 public class Main {public static void main(String[] args) {MyThread mt new MyThread();// 子线程处于新建状态System.out.println("轮到主线程执行");mt.start();// …

ANTLR 实战 SQL 词法/语法分析

文章目录ANTLR 实战 SQL 词法/语法分析一、准备工具二、实战环节1. 方式1&#xff1a;不借助外部 IDE2. 方式2&#xff1a;借助 idea参考资料ANTLR 实战 SQL 词法/语法分析 关于 词法/语法分析 和 ANTLR 语法 的详细内容&#xff0c;可参考我的另一篇博客&#xff1a;https://…

【Java学习笔记】57:认识网络编程

URL对象 URL统一资源标识符&#xff0c;在Java中通常放在URL对象中使用。URL对象通常需要包含&#xff1a;①协议②地址③资源。 import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.S…

【Java学习笔记】58:多线程Socket通信的demo

多线程Socket通信 对于服务端来说&#xff0c;可能有多个客户端连接进来&#xff0c;从平衡上考虑就应当为每个客户端的连接单独开启一个线程&#xff0c;并且在main进程中继续用accept()等待其它用户连接。 对于客户端来说&#xff0c;虽然不涉及多个服务端问题&#xff0c;…