You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ul...@apache.org on 2022/12/29 05:00:07 UTC
[kyuubi] branch master updated: [KYUUBI #4029] Support trino get schemas
This is an automated email from the ASF dual-hosted git repository.
ulyssesyou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 985330f01 [KYUUBI #4029] Support trino get schemas
985330f01 is described below
commit 985330f01c4c7f2169bcdacbe9f37f7f36b7d474
Author: ulysses-you <ul...@gmail.com>
AuthorDate: Thu Dec 29 12:59:51 2022 +0800
[KYUUBI #4029] Support trino get schemas
### _Why are the changes needed?_
Support Trino get schemas statement. We convert statement to operation at Trino frontend.
Note, this pr does not support the whole code path from Trino frontend. This pr aims to add an example how to make Trino frontend work with existed Kyuubi server framework.
### _How was this patch tested?_
- [x] Add some test cases that check the changes thoroughly including negative and positive cases if possible
- [ ] Add screenshots for manual tests if appropriate
- [ ] [Run test](https://kyuubi.apache.org/docs/latest/develop_tools/testing.html#running-tests) locally before make a pull request
Closes #4030 from ulysses-you/trino-get-schemas.
Closes #4029
f43408c2 [ulysses-you] address comments
a29bcdb8 [ulysses-you] fix
6fddbe6e [ulysses-you] support trino get schemas
Authored-by: ulysses-you <ul...@gmail.com>
Signed-off-by: ulysses-you <ul...@apache.org>
---
.rat-excludes | 2 +
.../org/apache/kyuubi/sql/KyuubiSqlBaseLexer.g4 | 27 +++++++-
.../org/apache/kyuubi/sql/KyuubiTrinoBaseParser.g4 | 31 +++++++++
.../trino/api/KyuubiTrinoOperationTranslator.scala | 50 +++++++++++++++
.../server/trino/api/v1/StatementResource.scala | 4 +-
.../apache/kyuubi/sql/parser/KyuubiParser.scala | 75 +++++++++++++++++++++-
.../KyuubiTrinoParser.scala} | 38 +++--------
.../apache/kyuubi/sql/trino/TrinoOperations.scala | 28 ++++++++
.../sql/trino/TrinoStatementAstBuilder.scala | 49 ++++++++++++++
.../parser/trino/KyuubiTrinoStatementSuite.scala | 67 +++++++++++++++++++
10 files changed, 337 insertions(+), 34 deletions(-)
diff --git a/.rat-excludes b/.rat-excludes
index 9ef03313f..29679f7db 100644
--- a/.rat-excludes
+++ b/.rat-excludes
@@ -50,3 +50,5 @@ build/scala-*/**
**/metadata-store-schema*.sql
**/pnpm-lock.yaml
**/node_modules/**
+**/gen/*
+**/*.tokens
diff --git a/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiSqlBaseLexer.g4 b/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiSqlBaseLexer.g4
index 4617493e1..9391a424e 100644
--- a/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiSqlBaseLexer.g4
+++ b/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiSqlBaseLexer.g4
@@ -43,20 +43,41 @@ lexer grammar KyuubiSqlBaseLexer;
SEMICOLON: ';';
+AND: 'AND';
BQ: '`';
+BY: 'BY';
COMMA: ',';
DOT: '.';
EQ : '=' | '==';
+NSEQ: '<=>';
+NEQ : '<>';
+NEQJ: '!=';
+LT : '<';
+LTE : '<=' | '!>';
+GT : '>';
+GTE : '>=' | '!<';
LEFT_PAREN: '(';
RIGHT_PAREN: ')';
+OR: 'OR';
DESC: 'DESC';
DESCRIBE: 'DESCRIBE';
-SESSION: 'SESSION';
+FROM: 'FROM';
+
+LIKE: 'LIKE';
KYUUBI: 'KYUUBI';
KYUUBIADMIN: 'KYUUBIADMIN';
+ORDER: 'ORDER';
+SELECT: 'SELECT';
+SESSION: 'SESSION';
+SYSTEM_JDBC_SCHEMAS: 'SYSTEM.JDBC.SCHEMAS';
+TABLE_CATALOG: 'TABLE_CATALOG';
+TABLE_SCHEM: 'TABLE_SCHEM';
+
+WHERE: 'WHERE';
+
BACKQUOTED_IDENTIFIER
: '`' ( ~'`' | '``' )* '`'
;
@@ -74,7 +95,9 @@ IDENTIFIER
;
STRING
- : '"' ( '\\'. | '""' | ~('"'| '\\') )* '"'
+ : '\'' ( ~('\''|'\\') | ('\\' .) )* '\''
+ | 'R\'' (~'\'')* '\''
+ | 'R"'(~'"')* '"'
;
fragment DIGIT
diff --git a/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiTrinoBaseParser.g4 b/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiTrinoBaseParser.g4
new file mode 100644
index 000000000..7d33241b7
--- /dev/null
+++ b/kyuubi-server/src/main/antlr4/org/apache/kyuubi/sql/KyuubiTrinoBaseParser.g4
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parser grammar KyuubiTrinoBaseParser;
+
+options { tokenVocab = KyuubiSqlBaseLexer; }
+
+singleStatement
+ : statement SEMICOLON* EOF
+ ;
+
+statement
+ : SELECT TABLE_SCHEM COMMA TABLE_CATALOG FROM SYSTEM_JDBC_SCHEMAS
+ (WHERE (TABLE_CATALOG EQ catalog=STRING+)? AND? (TABLE_SCHEM LIKE schema=STRING+)?)?
+ ORDER BY TABLE_CATALOG COMMA TABLE_SCHEM #getSchemas
+ | .*? #passThrough
+ ;
diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/KyuubiTrinoOperationTranslator.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/KyuubiTrinoOperationTranslator.scala
new file mode 100644
index 000000000..ee72998e5
--- /dev/null
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/KyuubiTrinoOperationTranslator.scala
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.server.trino.api
+
+import org.apache.hive.service.rpc.thrift.TProtocolVersion
+
+import org.apache.kyuubi.operation.OperationHandle
+import org.apache.kyuubi.service.BackendService
+import org.apache.kyuubi.sql.plan.PassThroughNode
+import org.apache.kyuubi.sql.trino.{KyuubiTrinoParser, TrinoGetSchemas}
+
+class KyuubiTrinoOperationTranslator(backendService: BackendService) {
+ lazy val parser = new KyuubiTrinoParser()
+
+ def transform(
+ statement: String,
+ user: String,
+ ipAddress: String,
+ configs: Map[String, String],
+ runAsync: Boolean,
+ queryTimeout: Long): OperationHandle = {
+ val sessionHandle = backendService.openSession(
+ TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V11,
+ user,
+ "",
+ ipAddress,
+ configs)
+ parser.parsePlan(statement) match {
+ case TrinoGetSchemas(catalogName, schemaPattern) =>
+ backendService.getSchemas(sessionHandle, catalogName, schemaPattern)
+ case PassThroughNode() =>
+ backendService.executeStatement(sessionHandle, statement, configs, runAsync, queryTimeout)
+ }
+ }
+}
diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/v1/StatementResource.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/v1/StatementResource.scala
index 11cd8eaf5..04e3408a3 100644
--- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/v1/StatementResource.scala
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/trino/api/v1/StatementResource.scala
@@ -26,13 +26,15 @@ import io.swagger.v3.oas.annotations.tags.Tag
import io.trino.client.QueryResults
import org.apache.kyuubi.Logging
-import org.apache.kyuubi.server.trino.api.ApiRequestContext
+import org.apache.kyuubi.server.trino.api.{ApiRequestContext, KyuubiTrinoOperationTranslator}
import org.apache.kyuubi.server.trino.api.v1.dto.Ok
@Tag(name = "Statement")
@Produces(Array(MediaType.APPLICATION_JSON))
private[v1] class StatementResource extends ApiRequestContext with Logging {
+ lazy val translator = new KyuubiTrinoOperationTranslator(fe.be)
+
@ApiResponse(
responseCode = "200",
content = Array(new Content(
diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala
index d87ec766d..c9529f83c 100644
--- a/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala
@@ -17,6 +17,9 @@
package org.apache.kyuubi.sql.parser
+import java.lang.{Long => JLong}
+import java.nio.CharBuffer
+
import org.antlr.v4.runtime._
import org.antlr.v4.runtime.atn.PredictionMode
import org.antlr.v4.runtime.misc.{Interval, ParseCancellationException}
@@ -26,7 +29,7 @@ import org.apache.kyuubi.sql.plan.KyuubiTreeNode
class KyuubiParser {
- val astBuilder = new KyuubiAstBuilder
+ lazy val astBuilder = new KyuubiAstBuilder
def parsePlan(sqlText: String): KyuubiTreeNode = parse(sqlText) { parser =>
astBuilder.visit(parser.singleStatement) match {
@@ -60,6 +63,76 @@ class KyuubiParser {
}
}
}
+object KyuubiParser {
+ val U16_CHAR_PATTERN = """\\u([a-fA-F0-9]{4})(?s).*""".r
+ val U32_CHAR_PATTERN = """\\U([a-fA-F0-9]{8})(?s).*""".r
+ val OCTAL_CHAR_PATTERN = """\\([01][0-7]{2})(?s).*""".r
+ val ESCAPED_CHAR_PATTERN = """\\((?s).)(?s).*""".r
+
+ /** Unescape backslash-escaped string enclosed by quotes. */
+ def unescapeSQLString(b: String): String = {
+ val sb = new StringBuilder(b.length())
+
+ def appendEscapedChar(n: Char): Unit = {
+ n match {
+ case '0' => sb.append('\u0000')
+ case '\'' => sb.append('\'')
+ case '"' => sb.append('\"')
+ case 'b' => sb.append('\b')
+ case 'n' => sb.append('\n')
+ case 'r' => sb.append('\r')
+ case 't' => sb.append('\t')
+ case 'Z' => sb.append('\u001A')
+ case '\\' => sb.append('\\')
+ // The following 2 lines are exactly what MySQL does TODO: why do we do this?
+ case '%' => sb.append("\\%")
+ case '_' => sb.append("\\_")
+ case _ => sb.append(n)
+ }
+ }
+
+ if (b.startsWith("r") || b.startsWith("R")) {
+ b.substring(2, b.length - 1)
+ } else {
+ // Skip the first and last quotations enclosing the string literal.
+ val charBuffer = CharBuffer.wrap(b, 1, b.length - 1)
+
+ while (charBuffer.remaining() > 0) {
+ charBuffer match {
+ case U16_CHAR_PATTERN(cp) =>
+ // \u0000 style 16-bit unicode character literals.
+ sb.append(Integer.parseInt(cp, 16).toChar)
+ charBuffer.position(charBuffer.position() + 6)
+ case U32_CHAR_PATTERN(cp) =>
+ // \U00000000 style 32-bit unicode character literals.
+ // Use Long to treat codePoint as unsigned in the range of 32-bit.
+ val codePoint = JLong.parseLong(cp, 16)
+ if (codePoint < 0x10000) {
+ sb.append((codePoint & 0xFFFF).toChar)
+ } else {
+ val highSurrogate = (codePoint - 0x10000) / 0x400 + 0xD800
+ val lowSurrogate = (codePoint - 0x10000) % 0x400 + 0xDC00
+ sb.append(highSurrogate.toChar)
+ sb.append(lowSurrogate.toChar)
+ }
+ charBuffer.position(charBuffer.position() + 10)
+ case OCTAL_CHAR_PATTERN(cp) =>
+ // \000 style character literals.
+ sb.append(Integer.parseInt(cp, 8).toChar)
+ charBuffer.position(charBuffer.position() + 4)
+ case ESCAPED_CHAR_PATTERN(c) =>
+ // escaped character literals.
+ appendEscapedChar(c.charAt(0))
+ charBuffer.position(charBuffer.position() + 2)
+ case _ =>
+ // non-escaped character literals.
+ sb.append(charBuffer.get())
+ }
+ }
+ sb.toString()
+ }
+ }
+}
case object PostProcessor extends KyuubiSqlBaseParserBaseListener {}
diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/KyuubiTrinoParser.scala
similarity index 59%
copy from kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala
copy to kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/KyuubiTrinoParser.scala
index d87ec766d..c3f79b085 100644
--- a/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/parser/KyuubiParser.scala
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/KyuubiTrinoParser.scala
@@ -15,18 +15,19 @@
* limitations under the License.
*/
-package org.apache.kyuubi.sql.parser
+package org.apache.kyuubi.sql.trino
import org.antlr.v4.runtime._
import org.antlr.v4.runtime.atn.PredictionMode
-import org.antlr.v4.runtime.misc.{Interval, ParseCancellationException}
+import org.antlr.v4.runtime.misc.ParseCancellationException
-import org.apache.kyuubi.sql.{KyuubiSqlBaseLexer, KyuubiSqlBaseParser, KyuubiSqlBaseParserBaseListener}
+import org.apache.kyuubi.sql.{KyuubiSqlBaseLexer, KyuubiTrinoBaseParser}
+import org.apache.kyuubi.sql.parser.{PostProcessor, UpperCaseCharStream}
import org.apache.kyuubi.sql.plan.KyuubiTreeNode
-class KyuubiParser {
+class KyuubiTrinoParser {
- val astBuilder = new KyuubiAstBuilder
+ lazy val astBuilder = new TrinoStatementAstBuilder
def parsePlan(sqlText: String): KyuubiTreeNode = parse(sqlText) { parser =>
astBuilder.visit(parser.singleStatement) match {
@@ -34,13 +35,13 @@ class KyuubiParser {
}
}
- protected def parse[T](command: String)(toResult: KyuubiSqlBaseParser => T): T = {
+ protected def parse[T](command: String)(toResult: KyuubiTrinoBaseParser => T): T = {
val lexer = new KyuubiSqlBaseLexer(
new UpperCaseCharStream(CharStreams.fromString(command)))
lexer.removeErrorListeners()
val tokenStream = new CommonTokenStream(lexer)
- val parser = new KyuubiSqlBaseParser(tokenStream)
+ val parser = new KyuubiTrinoBaseParser(tokenStream)
parser.addParseListener(PostProcessor)
parser.removeErrorListeners()
@@ -60,26 +61,3 @@ class KyuubiParser {
}
}
}
-
-case object PostProcessor extends KyuubiSqlBaseParserBaseListener {}
-
-/* Copied from Apache Spark's to avoid dependency on Spark Internals */
-class UpperCaseCharStream(wrapped: CodePointCharStream) extends CharStream {
- override def consume(): Unit = wrapped.consume()
- override def getSourceName(): String = wrapped.getSourceName
- override def index(): Int = wrapped.index
- override def mark(): Int = wrapped.mark
- override def release(marker: Int): Unit = wrapped.release(marker)
- override def seek(where: Int): Unit = wrapped.seek(where)
- override def size(): Int = wrapped.size
-
- override def getText(interval: Interval): String = wrapped.getText(interval)
-
- // scalastyle:off
- override def LA(i: Int): Int = {
- val la = wrapped.LA(i)
- if (la == 0 || la == IntStream.EOF) la
- else Character.toUpperCase(la)
- }
- // scalastyle:on
-}
diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/TrinoOperations.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/TrinoOperations.scala
new file mode 100644
index 000000000..27c8d87f6
--- /dev/null
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/TrinoOperations.scala
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.sql.trino
+
+import org.apache.kyuubi.sql.plan.KyuubiTreeNode
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// This file contains all Trino JDBC operation nodes which are parsed from statement
+/////////////////////////////////////////////////////////////////////////////////////////
+
+case class TrinoGetSchemas(catalogName: String, schemaPattern: String) extends KyuubiTreeNode {
+ override def name(): String = "Get Schemas"
+}
diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/TrinoStatementAstBuilder.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/TrinoStatementAstBuilder.scala
new file mode 100644
index 000000000..886239e2d
--- /dev/null
+++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/sql/trino/TrinoStatementAstBuilder.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.sql.trino
+
+import org.apache.kyuubi.sql.{KyuubiTrinoBaseParser, KyuubiTrinoBaseParserBaseVisitor}
+import org.apache.kyuubi.sql.parser.KyuubiParser
+import org.apache.kyuubi.sql.plan.{KyuubiTreeNode, PassThroughNode}
+
+class TrinoStatementAstBuilder extends KyuubiTrinoBaseParserBaseVisitor[KyuubiTreeNode] {
+
+ override def visitSingleStatement(
+ ctx: KyuubiTrinoBaseParser.SingleStatementContext): KyuubiTreeNode = {
+ visit(ctx.statement)
+ }
+
+ override def visitPassThrough(ctx: KyuubiTrinoBaseParser.PassThroughContext): KyuubiTreeNode = {
+ PassThroughNode()
+ }
+
+ override def visitGetSchemas(ctx: KyuubiTrinoBaseParser.GetSchemasContext): KyuubiTreeNode = {
+ val catalog = if (ctx.catalog == null) {
+ null
+ } else {
+ KyuubiParser.unescapeSQLString(ctx.catalog.getText)
+ }
+ val schema = if (ctx.schema == null) {
+ null
+ } else {
+ KyuubiParser.unescapeSQLString(ctx.schema.getText)
+ }
+
+ TrinoGetSchemas(catalog, schema)
+ }
+}
diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/parser/trino/KyuubiTrinoStatementSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/parser/trino/KyuubiTrinoStatementSuite.scala
new file mode 100644
index 000000000..9710c276d
--- /dev/null
+++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/parser/trino/KyuubiTrinoStatementSuite.scala
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kyuubi.parser.trino
+
+import org.apache.kyuubi.KyuubiFunSuite
+import org.apache.kyuubi.sql.trino.{KyuubiTrinoParser, TrinoGetSchemas}
+
+class KyuubiTrinoStatementSuite extends KyuubiFunSuite {
+ val parser = new KyuubiTrinoParser()
+
+ test("get schemas") {
+ def check(query: String, catalog: String = null, schema: String = null): Unit = {
+ parser.parsePlan(query) match {
+ case TrinoGetSchemas(catalogName, schemaPattern) =>
+ assert(catalogName == catalog)
+ assert(schemaPattern == schema)
+ case _ => throw new IllegalStateException()
+ }
+ }
+
+ check(
+ """
+ |SELECT TABLE_SCHEM, TABLE_CATALOG FROM system.jdbc.schemas
+ |ORDER BY TABLE_CATALOG, TABLE_SCHEM
+ |""".stripMargin)
+
+ check(
+ """
+ |SELECT TABLE_SCHEM, TABLE_CATALOG FROM system.jdbc.schemas
+ |WHERE TABLE_CATALOG='aaa'
+ |ORDER BY TABLE_CATALOG, TABLE_SCHEM
+ |""".stripMargin,
+ catalog = "aaa")
+
+ check(
+ """
+ |SELECT TABLE_SCHEM, TABLE_CATALOG FROM system.jdbc.schemas
+ |WHERE TABLE_SCHEM LIKE 'aa%'
+ |ORDER BY TABLE_CATALOG, TABLE_SCHEM
+ |""".stripMargin,
+ schema = "aa%")
+
+ check(
+ """
+ |SELECT TABLE_SCHEM, TABLE_CATALOG FROM system.jdbc.schemas
+ |WHERE TABLE_CATALOG='bb' and TABLE_SCHEM LIKE 'bb%'
+ |ORDER BY TABLE_CATALOG, TABLE_SCHEM
+ |""".stripMargin,
+ catalog = "bb",
+ schema = "bb%")
+ }
+}