From 43d60bd1b1278312df91ceec2b67f7e3dda5de13 Mon Sep 17 00:00:00 2001 From: Cheng Pan <379377944@qq.com> Date: Wed, 9 Jun 2021 13:41:00 +0800 Subject: [PATCH] [KYUUBI #670] KyuubiSQLException support sqlState and errorCode ### _Why are the changes needed?_ #670 ### _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 - [x] [Run test](https://kyuubi.readthedocs.io/en/latest/tools/testing.html#running-tests) locally before make a pull request Closes #674 from pan3793/sql-error-code. Closes #670 08c95915 [Cheng Pan] address comments e0808bc5 [Cheng Pan] address comments e14e884b [Cheng Pan] KyuubiSQLException support sqlState and errorCode Authored-by: Cheng Pan <379377944@qq.com> Signed-off-by: ulysses-you --- .../apache/kyuubi/KyuubiSQLException.scala | 29 ++++++++++++++----- .../kyuubi/operation/log/OperationLog.scala | 2 +- .../KyuubiAuthenticationFactory.scala | 2 +- .../kyuubi/KyuubiSQLExceptionSuite.scala | 9 +++++- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/KyuubiSQLException.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/KyuubiSQLException.scala index 7739d3be8..324172a0c 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/KyuubiSQLException.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/KyuubiSQLException.scala @@ -26,7 +26,20 @@ import scala.collection.JavaConverters._ import org.apache.hive.service.rpc.thrift.{TStatus, TStatusCode} -class KyuubiSQLException(msg: String, cause: Throwable) extends SQLException(msg, cause) { +/** + * @param reason a description of the exception + * @param sqlState an XOPEN or SQL:2003 code identifying the exception + * @param vendorCode a database vendor-specific exception code + * @param cause the underlying reason for this [[SQLException]] + * (which is saved for later retrieval by the `getCause()` method); + * may be null indicating the cause is non-existent or unknown. + */ +class KyuubiSQLException(reason: String, sqlState: String, vendorCode: Int, cause: Throwable) + extends SQLException(reason, sqlState, vendorCode, cause) { + + // for reflection + def this(msg: String, cause: Throwable) = this(msg, null, 0, cause) + /** * Converts current object to a [[TStatus]] object * @@ -47,22 +60,24 @@ object KyuubiSQLException { private final val HEAD_MARK: String = "*" private final val SEPARATOR: Char = ':' - def apply(msg: String, throwable: Throwable): KyuubiSQLException = { - new KyuubiSQLException(msg, findCause(throwable)) + def apply( + msg: String, + cause: Throwable = null, + sqlState: String = null, + vendorCode: Int = 0): KyuubiSQLException = { + new KyuubiSQLException(msg, sqlState, vendorCode, findCause(cause)) } def apply(cause: Throwable): KyuubiSQLException = { val theCause = findCause(cause) - new KyuubiSQLException(theCause.getMessage, theCause) + apply(theCause.getMessage, theCause) } - def apply(msg: String): KyuubiSQLException = new KyuubiSQLException(msg, null) - def apply(tStatus: TStatus): KyuubiSQLException = { val msg = tStatus.getErrorMessage val cause = toCause(tStatus.getInfoMessages.asScala) cause match { case k: KyuubiSQLException if k.getMessage == msg => k - case _ => apply(msg, cause) + case _ => apply(msg, cause, tStatus.getSqlState, tStatus.getErrorCode) } } diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/log/OperationLog.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/log/OperationLog.scala index fa95b26c7..2976f3e0d 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/log/OperationLog.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/operation/log/OperationLog.scala @@ -119,7 +119,7 @@ class OperationLog(path: Path) extends Logging { case e: IOException => val absPath = path.toAbsolutePath val opHandle = absPath.getFileName - throw new KyuubiSQLException(s"Operation[$opHandle] log file $absPath is not found", e) + throw KyuubiSQLException(s"Operation[$opHandle] log file $absPath is not found", e) } val tColumn = TColumn.stringVal(new TStringColumn(logs, ByteBuffer.allocate(0))) val tRow = new TRowSet(0, new java.util.ArrayList[TRow](logs.size())) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala index 32c18674c..736b6e7f2 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala @@ -117,7 +117,7 @@ object KyuubiAuthenticationFactory { } } catch { case e: IOException => - throw new KyuubiSQLException( + throw KyuubiSQLException( "Failed to validate proxy privilege of " + realUser + " for " + proxyUser, e) } } diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/KyuubiSQLExceptionSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/KyuubiSQLExceptionSuite.scala index 76964180e..55fab7c79 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/KyuubiSQLExceptionSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/KyuubiSQLExceptionSuite.scala @@ -30,7 +30,7 @@ class KyuubiSQLExceptionSuite extends KyuubiFunSuite { val e0 = new KyuubiException(msg0) val e1 = new KyuubiException(msg1, e0) - val e2 = new KyuubiSQLException(msg2, e1) + val e2 = KyuubiSQLException(msg2, e1) assert(e2.toTStatus === KyuubiSQLException.toTStatus(e2)) val e3 = KyuubiSQLException(e2.toTStatus) @@ -51,6 +51,13 @@ class KyuubiSQLExceptionSuite extends KyuubiFunSuite { val e5 = KyuubiSQLException(e0) assert(e5.getMessage === msg0) assert(e5.getCause === e0) + + val ts1 = KyuubiSQLException(msg2, e0, "01001", 1).toTStatus + assert(ts1.getStatusCode === TStatusCode.ERROR_STATUS) + assert(ts1.getSqlState === "01001") + assert(ts1.getErrorCode === 1) + assert(ts1.getErrorMessage === msg2) + assert(ts1.getInfoMessages.get(0).startsWith("*")) } test("find the root cause") {