[Core] [Parser] [MySql] Add limit parser

This commit is contained in:
qianmoQ 2024-01-03 11:52:01 +08:00
parent 7a7e5881f0
commit 9f772ae4e4
16 changed files with 171 additions and 150 deletions

View File

@ -1,8 +1,15 @@
package io.edurt.datacap.parser.mysql
import io.edurt.datacap.parser.mysql.companion.ParserException
import io.edurt.datacap.parser.ParserResponse
import io.edurt.datacap.parser.model.Column
import io.edurt.datacap.parser.model.Table
import io.edurt.datacap.parser.mysql.companion.MySQLEndpoint
import io.edurt.datacap.parser.mysql.companion.ParserException
import io.edurt.datacap.parser.mysql.companion.ast.ParserOptions
import io.edurt.datacap.parser.mysql.companion.tree.QuerySpecification
import io.edurt.datacap.parser.mysql.companion.tree.Statement
import io.edurt.datacap.parser.type.EngineType
import io.edurt.datacap.parser.type.StatementType
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -15,7 +22,7 @@ object MySQLHelper {
response.engine = EngineType.MYSQL
try {
response.isParser = true
// check(SqlParser().createStatement(sql, ParserOptions(ParserOptions.DecimalLiteralTreatment.AS_DECIMAL)), response)
check(MySQLEndpoint().createStatement(sql, ParserOptions.DecimalLiteralTreatment.AS_DECIMAL), response)
}
catch (e: ParserException) {
response.isParser = false
@ -24,4 +31,32 @@ object MySQLHelper {
}
return response
}
@Throws(ParserException::class)
private fun check(statement: Statement, response: ParserResponse) {
when (statement) {
is QuerySpecification -> {
response.type = StatementType.SELECT
val table = Table()
statement.from.table.children.forEach {
table.name = it.name
table.alias = it.alias
}
statement.select.children.forEach {
val column = Column()
column.name = it.name
table.columns.add(column)
}
statement.limit.children.forEach {
table.limit = it.limit
}
response.table = table
}
else -> throw ParserException("Unsupported statement: " + statement.javaClass)
}
}
}

View File

@ -6,8 +6,7 @@ import io.edurt.datacap.parser.mysql.MySqlParserVisitor
import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import io.edurt.datacap.parser.mysql.companion.ast.ParserOptions
import io.edurt.datacap.parser.mysql.companion.tree.Relation
import io.edurt.datacap.parser.mysql.companion.tree.SelectItem
import io.edurt.datacap.parser.mysql.companion.tree.*
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.Token
import org.antlr.v4.runtime.tree.ErrorNode
@ -48,6 +47,7 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
override fun visitChildren(p0: RuleNode?): Node {
return when (p0) {
is SelectExpressionElementContext -> visitSelectExpressionElement(p0)
is SelectColumnElementContext -> visitSelectColumnElement(p0)
else -> throw UnsupportedOperationException("Unsupported RuleNode type: ${p0?.javaClass?.simpleName}")
}
}
@ -1070,12 +1070,14 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
TODO("Not yet implemented")
}
override fun visitTableSources(ctx: MySqlParser.TableSourcesContext?): Node {
TODO("Not yet implemented")
override fun visitTableSources(ctx: TableSourcesContext): Table {
val children: MutableSet<TableItem> = HashSet<TableItem>().toMutableSet()
ctx.tableSource().forEach { children += visitTableSourceBase(it as TableSourceBaseContext) }
return Table(getLocation(ctx), children)
}
override fun visitTableSourceBase(ctx: MySqlParser.TableSourceBaseContext?): Node {
TODO("Not yet implemented")
override fun visitTableSourceBase(ctx: TableSourceBaseContext): TableItem {
return visitAtomTableItem(ctx.tableSourceItem() as AtomTableItemContext)
}
override fun visitTableSourceNested(ctx: MySqlParser.TableSourceNestedContext?): Node {
@ -1086,15 +1088,19 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
TODO("Not yet implemented")
}
override fun visitAtomTableItem(ctx: MySqlParser.AtomTableItemContext?): Node {
TODO("Not yet implemented")
override fun visitAtomTableItem(ctx: AtomTableItemContext): TableItem {
var alias = ""
if (ctx.alias != null) {
alias = ctx.alias.text
}
return TableItem(getLocation(ctx), ctx.tableName().text, alias)
}
override fun visitSubqueryTableItem(ctx: MySqlParser.SubqueryTableItemContext?): Node {
TODO("Not yet implemented")
}
override fun visitTableSourcesItem(ctx: MySqlParser.TableSourcesItemContext?): Node {
override fun visitTableSourcesItem(ctx: MySqlParser.TableSourcesItemContext): Node {
TODO("Not yet implemented")
}
@ -1134,12 +1140,19 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
TODO("Not yet implemented")
}
override fun visitQuerySpecification(ctx: MySqlParser.QuerySpecificationContext): Node {
val from: Optional<Relation> = Optional.empty()
override fun visitQuerySpecification(ctx: QuerySpecificationContext): Node {
val selectItems: List<SelectItem> = visitSelectElements(ctx.selectElements(), SelectItem::class.java)
println(selectItems)
val fromClause: Relation = visitFromClause(ctx.fromClause())
TODO("Not yet implemented")
var limitClause: Limit = Limit(Optional.empty(), emptySet())
if (ctx.limitClause() != null) {
limitClause = visitLimitClause(ctx.limitClause())
}
return QuerySpecification(getLocation(ctx),
Select(getLocation(ctx.selectElements()), false, selectItems),
fromClause,
limitClause)
}
override fun visitQuerySpecificationNointo(ctx: MySqlParser.QuerySpecificationNointoContext?): Node {
@ -1190,15 +1203,15 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
TODO("Not yet implemented")
}
override fun visitSelectColumnElement(ctx: MySqlParser.SelectColumnElementContext?): Node {
TODO("Not yet implemented")
override fun visitSelectColumnElement(ctx: SelectColumnElementContext): Node {
return SelectItem(getLocation(ctx), ctx.fullColumnName().text)
}
override fun visitSelectFunctionElement(ctx: MySqlParser.SelectFunctionElementContext?): Node {
TODO("Not yet implemented")
}
override fun visitSelectExpressionElement(ctx: MySqlParser.SelectExpressionElementContext): Node {
override fun visitSelectExpressionElement(ctx: SelectExpressionElementContext): Node {
return SelectItem(getLocation(ctx.expression()), ctx.expression().text)
}
@ -1222,8 +1235,11 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
TODO("Not yet implemented")
}
override fun visitFromClause(ctx: MySqlParser.FromClauseContext?): Node {
TODO("Not yet implemented")
override fun visitFromClause(ctx: FromClauseContext): Relation {
if (ctx.tableSources() != null) {
return Relation(getLocation(ctx), visitTableSources(ctx.tableSources()))
}
return Relation(getLocation(ctx), Table(getLocation(ctx), emptySet()))
}
override fun visitGroupByClause(ctx: MySqlParser.GroupByClauseContext?): Node {
@ -1242,12 +1258,14 @@ class AstVisitorBuilder(options: ParserOptions.DecimalLiteralTreatment) : MySqlP
TODO("Not yet implemented")
}
override fun visitLimitClause(ctx: MySqlParser.LimitClauseContext?): Node {
TODO("Not yet implemented")
override fun visitLimitClause(ctx: LimitClauseContext): Limit {
val children: MutableSet<LimitItem> = HashSet<LimitItem>().toMutableSet()
ctx?.limitClauseAtom()?.forEach { children += visitLimitClauseAtom(it) }
return Limit(getLocation(ctx), children)
}
override fun visitLimitClauseAtom(ctx: MySqlParser.LimitClauseAtomContext?): Node {
TODO("Not yet implemented")
override fun visitLimitClauseAtom(ctx: LimitClauseAtomContext): LimitItem {
return LimitItem(getLocation(ctx), ctx.text.toLong())
}
override fun visitStartTransaction(ctx: MySqlParser.StartTransactionContext?): Node {

View File

@ -17,10 +17,6 @@ abstract class Node(location: Optional<NodeLocation>) {
return visitor.visitNode(this, context)
}
protected open fun getChildren(): List<out Node> {
throw UnsupportedOperationException("not yet implemented: " + javaClass.name)
}
protected open fun shallowEquals(other: Node): Boolean {
throw UnsupportedOperationException("not yet implemented: " + javaClass.name)
}

View File

@ -0,0 +1,8 @@
package io.edurt.datacap.parser.mysql.companion.tree
import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
class Limit(override val location: Optional<NodeLocation>,
val children: Set<LimitItem>) : Node(location)

View File

@ -0,0 +1,8 @@
package io.edurt.datacap.parser.mysql.companion.tree
import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
data class LimitItem(override val location: Optional<NodeLocation>,
val limit: Long) : Node(location)

View File

@ -1,4 +1,9 @@
package io.edurt.datacap.parser.mysql.companion.tree
class QuerySpecification {
}
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
data class QuerySpecification(override val location: Optional<NodeLocation>,
val select: Select,
val from: Relation,
val limit: Limit) : Statement(location)

View File

@ -5,7 +5,8 @@ import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
class Relation(location: Optional<NodeLocation>) : Node(location) {
data class Relation(override val location: Optional<NodeLocation>,
val table: Table) : Node(location) {
override fun <R, C> accept(visitor: AstVisitor<R, C>, context: C): R? {
return visitor.visitRelation(this, context)
}

View File

@ -1,70 +1,14 @@
package io.edurt.datacap.parser.mysql.companion.tree
import com.google.common.base.MoreObjects.toStringHelper
import com.google.common.collect.ImmutableList
import io.edurt.datacap.parser.mysql.companion.AstVisitor
import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
class Select : Node {
val distinct: Boolean
val selectItems: List<SelectItem>
constructor(distinct: Boolean, selectItems: List<SelectItem>) : this(Optional.empty(), distinct, selectItems)
constructor(location: NodeLocation, distinct: Boolean, selectItems: List<SelectItem>) : this(
Optional.of(location),
distinct,
selectItems
)
private constructor(location: Optional<NodeLocation>, distinct: Boolean, selectItems: List<SelectItem>) : super(location) {
this.distinct = distinct
this.selectItems = ImmutableList.copyOf(Objects.requireNonNull(selectItems, "selectItems"))
}
fun isDistinct(): Boolean {
return distinct
}
data class Select(override val location: Optional<NodeLocation>,
val distinct: Boolean,
val children: List<SelectItem>) : Node(location) {
override fun <R, C> accept(visitor: AstVisitor<R, C>, context: C): R? {
return visitor.visitSelect(this, context)
}
override fun getChildren(): List<Node> {
return selectItems
}
override fun toString(): String {
return toStringHelper(this)
.add("distinct", distinct)
.add("selectItems", selectItems)
.omitNullValues()
.toString()
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (other == null || javaClass != other.javaClass) {
return false
}
val select = other as Select
return distinct == select.distinct &&
Objects.equals(selectItems, select.selectItems)
}
override fun hashCode(): Int {
return Objects.hash(distinct, selectItems)
}
override fun shallowEquals(other: Node): Boolean {
if (! sameClass(this, other)) {
return false
}
return distinct == (other as Select).distinct
}
}

View File

@ -7,5 +7,5 @@ import java.util.*
data class SelectItem(
override val location: Optional<NodeLocation>,
val text: Any
val name: String
) : Node(location)

View File

@ -0,0 +1,8 @@
package io.edurt.datacap.parser.mysql.companion.tree
import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
data class Table(override val location: Optional<NodeLocation>,
val children: Set<TableItem>) : Node(location)

View File

@ -0,0 +1,9 @@
package io.edurt.datacap.parser.mysql.companion.tree
import io.edurt.datacap.parser.mysql.companion.ast.Node
import io.edurt.datacap.parser.mysql.companion.ast.NodeLocation
import java.util.*
data class TableItem(override val location: Optional<NodeLocation>,
val name: String,
val alias: String) : Node(location)

View File

@ -0,0 +1,45 @@
package io.edurt.datacap.parser.mysql
import io.edurt.datacap.parser.ParserResponse
import io.edurt.datacap.parser.type.StatementType
import org.junit.Assert.assertTrue
import org.junit.Test
import org.slf4j.LoggerFactory.getLogger
class MySQLHelperTest {
private val log = getLogger(MySQLHelperTest::class.java)
private fun printLog(response: ParserResponse) {
log.info("engine: [ {} ]", response.engine)
log.info("type: [ {} ]", response.type)
log.info("isParser: [ {} ]", response.isParser)
log.info("database name: [ {} ]", response.table.database)
log.info("table name: [ {} ]", response.table.name)
log.info("table limit: [ {} ]", response.table.limit)
log.info("message: [ {} ]", response.message)
response.table.columns.forEach {
log.info("column name: [ {} ], alias: [ {} ], type: [ {} ], expression: [ {} ], functions: [ {} ]", it.name, it.alias, it.type, it.expression, it.functions)
}
}
@Test
fun test() {
val response = MySQLHelper.parse("SELECT 1")
printLog(response)
assertTrue(response.type == StatementType.SELECT)
}
@Test
fun testFromTable() {
val response = MySQLHelper.parse("SELECT \"name\", \"age\" FROM \"a_table_name\"")
printLog(response)
assertTrue(response.isParser)
}
@Test
fun testLimit() {
val response = MySQLHelper.parse("SELECT * FROM \"a_table_name\" LIMIT 10")
printLog(response)
assertTrue(response.table.limit == 10L)
}
}

View File

@ -6,6 +6,7 @@ import org.junit.Test
class MySQLEndpointTest {
@Test
fun test() {
MySQLEndpoint().createStatement("SELECT 1", ParserOptions.DecimalLiteralTreatment.AS_DECIMAL)
val sql = "SELECT name AS name, age AS age, summer + 1 AS summer, SUM(summer) FROM a_table_name LIMIt 100"
MySQLEndpoint().createStatement(sql, ParserOptions.DecimalLiteralTreatment.AS_DECIMAL)
}
}

View File

@ -33,11 +33,6 @@
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>io.airlift</groupId>
<artifactId>slice</artifactId>

View File

@ -1,5 +1,6 @@
package io.edurt.datacap.parser.parser;
import io.edurt.datacap.parser.CaseInsensitiveStream;
import io.edurt.datacap.parser.ParsingException;
import io.edurt.datacap.parser.antlr.SqlBaseBaseListener;
import io.edurt.datacap.parser.antlr.SqlBaseLexer;
@ -10,7 +11,6 @@ import io.edurt.datacap.parser.tree.DataType;
import io.edurt.datacap.parser.tree.Expression;
import io.edurt.datacap.parser.tree.PathSpecification;
import io.edurt.datacap.parser.tree.Statement;
import io.edurt.datacap.parser.trino.CaseInsensitiveStream;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonToken;

View File

@ -1,52 +0,0 @@
package io.edurt.datacap.parser.trino
import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.IntStream
import org.antlr.v4.runtime.misc.Interval
class CaseInsensitiveStream : CharStream {
private val stream: CharStream
constructor(stream: CharStream) {
this.stream = stream
}
override fun consume() {
stream.consume()
}
override fun LA(p0: Int): Int {
return when (val result = stream.LA(p0)) {
0, IntStream.EOF -> result
else -> Character.toUpperCase(result)
}
}
override fun mark(): Int {
return stream.mark()
}
override fun release(p0: Int) {
stream.release(p0)
}
override fun index(): Int {
return stream.index()
}
override fun seek(p0: Int) {
stream.seek(p0)
}
override fun size(): Int {
return stream.size()
}
override fun getSourceName(): String {
return stream.getSourceName()
}
override fun getText(p0: Interval?): String {
return stream.getText(p0)
}
}