golang解析sql从中获取表名

作者:
淡白
创建时间:
2022-04-24 20:34:12
Go MySQL sql antlr

摘要:ANTLR是一个用Java语言编写的识别器工具,可以自动生成解析器。它支持生成Java、Go、C等语言的解析器客户端。可以通过下载ANTLR的jar包和mysql的语法规则文件,使用ANTLR来生成解析器。通过覆盖`BaseMySqlParserListener`的`EnterTableName`接口,可以收集解析过程中的表名。

antlr

ANTLR是Another Tool for Language Recognition的简写,是一个用Java语言编写的识别器工具。它能够自动生成解析器,并将用户编写的ANTLR语法规则直接生成目标语言的解析器,它能够生成Java、Go、C等语言的解析器客户端。 作者还维护了很多语法规则其中就有mysql规则

使用

下载最新的jar包https://www.antlr.org/download/antlr-4.10.1-complete.jar 从github中下载MySqlLexer.g4MySqlParser.g4

执行命令生成go文件 (需要java环境) java -jar antlr-4.10.1-complete.jar -visitor -Dlanguage=Go -o parser MySqlLexer.g4

java -jar antlr-4.10.1-complete.jar -visitor -Dlanguage=Go -o parser MySqlParser.g4

目录结构如下:

├── MySqlLexer.g4
├── MySqlParser.g4
├── antlr-4.10.1-complete.jar
├── antlr.go
└── parser
    ├── MySqlLexer.interp
    ├── MySqlLexer.tokens
    ├── MySqlParser.interp
    ├── MySqlParser.tokens
    ├── mysql_lexer.go
    ├── mysql_parser.go
    ├── mysqlparser_base_listener.go
    └── mysqlparser_listener.go

antlr.go:

package antlr

import (
	"github.com/antlr/antlr4/runtime/Go/antlr"
	logs "github.com/danbai225/go-logs"
	"strings"
	"test/antlr/parser"
)

type Ml struct {
	*parser.BaseMySqlParserListener
	tableNames map[string]struct{}
}

func (m *Ml) EnterTableName(ctx *parser.TableNameContext) {
	if m.tableNames == nil {
		m.tableNames = make(map[string]struct{})
	}
	m.tableNames[ctx.GetText()] = struct{}{}
}
func (m *Ml) GetTableNames() []string {
	arr := make([]string, 0)
	if m.tableNames != nil {
		for k := range m.tableNames {
			arr = append(arr, k)
		}
	}
	return arr
}
func GetTableNames(sql string, sqlType string) []string {
	tokenStream := antlr.NewCommonTokenStream(parser.NewMySqlLexer(antlr.NewInputStream(strings.ToUpper(sql))), antlr.TokenDefaultChannel)
	sqlParser := parser.NewMySqlParser(tokenStream)
	ml := Ml{}
	switch sqlType {
	case "dml":
		antlr.ParseTreeWalkerDefault.Walk(&ml, sqlParser.DmlStatement())
	case "ddl":
		antlr.ParseTreeWalkerDefault.Walk(&ml, sqlParser.DdlStatement())
	}
	return ml.GetTableNames()
}

func Antlr() {
	sql := "SELECT without FROM fails"
	logs.Info(GetTableNames(sql, "dml"))
}

通过覆盖实现BaseMySqlParserListenerEnterTableName接口 再解析的过程中每个表名节点都将调用这个方法收集表名。