golang解析sql从中获取表名

作者:
淡白
创建时间:
Apr 24, 2022, 8:34:12 PM
学习 Go 开发 Go MySQL sql antlr 168

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

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接口
再解析的过程中每个表名节点都将调用这个方法收集表名。