ANTLR4解析MySQL语句

ANTLR

一款强大的生成”语法解析器”的工具,可以用作读取、处理、执行或翻译结构化文本或二进制文件。广泛的用来构建新的语言、工具和框架。这个”语法解析器”创建和遍历语法树。

eg:Hive和Spark使用antlr生成词法语法解析器、Twitter使用antlr来解析用户输入的查询内容,Oracle把antlr的功能内嵌在SQL 开发IDE中,NetBeans IDE使用antlr解析C++语言,也有公司使用antlr来从文件中抽取信息

ANTLR官网

大致流程:

  • 首先需要编写文法文件(g4后缀);
  • 然后使用g4文法文件通过antlr工具生成lexer词法解析器、parser语法解析器、visitor和listener的runtime target文件(支持多种语言)
  • 然后可以直接使用输出的runtime target文件模块进行开发工作

生成MySQL语法解析器

官方提供的各种语言的g4文件
Python target说明

安装ANTLR工具:

cd /usr/local/lib
wget https://www.antlr.org/download/antlr-4.7.2-complete.jar
export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
alias antlr4='java -jar /usr/local/lib/antlr-4.7.2-complete.jar'
alias grun='java org.antlr.v4.gui.TestRig'

根据mysql文法文件生成Python词法/语法解析器:

antlr4 -Dlanguage=Python2 MySqlLexer.g4
antlr4 -Dlanguage=Python2 MySqlParser.g4

安装Python runtime模块

pip install antlr4-python2-runtime

测试用例:

import sys
from antlr4 import *
from antlr4.InputStream import InputStream
from MySqlLexer import MySqlLexer
from MySqlParser import MySqlParser


class CaseChangingCharInputStream(InputStream):
    def __init__(self, data, upper=True):
        super(CaseChangingCharInputStream, self).__init__(data)
        self.upper = upper

    def LA(self, pos):
        value = super(CaseChangingCharInputStream, self).LA(pos)
        if 0 <= value < 256:
            if pos <= 0: return value
            str_value = chr(value)
            return ord(str_value.upper()) if self.upper else ord(str_value.lower())
        else:
            return value


class CaseChangingCharFileStream(FileStream, CaseChangingCharInputStream):
    def __init__(self, file, upper=True):
        super(CaseChangingCharFileStream, self).__init__(file)
        self.upper = upper


if __name__ == '__main__':
    if len(sys.argv) > 1:
        input_stream = CaseChangingCharFileStream(sys.argv[1])
    else:
        input_stream = CaseChangingCharInputStream('SeLeCT id FROM student')

    lexer = MySqlLexer(input_stream)
    token_stream = CommonTokenStream(lexer)
    parser = MySqlParser(token_stream)
    tree = parser.root()

问题

  • mysql语句不区分大小写,但是目前官方提供的g4文件是区分大小写的,所以使用lexer/parser时会报错。
    • 两种方案:一种是修改g4文件;一种是在喂给lexer之前,将所有字符转成大写。
    • 官方解释
# 重写LA
class CaseChangingCharInputStream(InputStream):
    def __init__(self, data, upper=True):
        super(CaseChangingCharInputStream, self).__init__(data)
        self.upper = upper

    def LA(self, pos):
        value = super(CaseChangingCharInputStream, self).LA(pos)
        if 0 <= value < 256:
            if pos <= 0: return value
            str_value = chr(value)
            return ord(str_value.upper()) if self.upper else ord(str_value.lower())
        else:
            return value


class CaseChangingCharFileStream(FileStream, CaseChangingCharInputStream):
    def __init__(self, file, upper=True):
        super(CaseChangingCharFileStream, self).__init__(file)
        self.upper = upper

欢迎关注微信公众号(coder0x00)或扫描下方二维码关注,我们将持续搜寻程序员必备基础技能包提供给大家。

《ANTLR4解析MySQL语句》

    原文作者:lndyzwdxhs
    原文地址: https://www.jianshu.com/p/f8adc051880d
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞