mirror of
https://gitee.com/devlive-community/datacap.git
synced 2024-12-02 12:07:37 +08:00
[Core] [Parser] [MySql] Add limit parser
This commit is contained in:
parent
7a7e5881f0
commit
9f772ae4e4
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
@ -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)
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ import java.util.*
|
||||
|
||||
data class SelectItem(
|
||||
override val location: Optional<NodeLocation>,
|
||||
val text: Any
|
||||
val name: String
|
||||
) : Node(location)
|
||||
|
@ -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)
|
@ -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)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user