diff --git jdbc/src/java/org/apache/hive/jdbc/Utils.java jdbc/src/java/org/apache/hive/jdbc/Utils.java index 87fec11..61e066c 100644 --- jdbc/src/java/org/apache/hive/jdbc/Utils.java +++ jdbc/src/java/org/apache/hive/jdbc/Utils.java @@ -20,12 +20,12 @@ import java.net.URI; import java.sql.SQLException; -import java.sql.Types; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.hive.service.cli.HiveSQLException; import org.apache.hive.service.cli.thrift.TStatus; import org.apache.hive.service.cli.thrift.TStatusCode; @@ -118,8 +118,7 @@ public static void verifySuccess(TStatus status) throws SQLException { public static void verifySuccess(TStatus status, boolean withInfo) throws SQLException { if ((status.getStatusCode() != TStatusCode.SUCCESS_STATUS) && (withInfo && (status.getStatusCode() != TStatusCode.SUCCESS_WITH_INFO_STATUS))) { - throw new SQLException(status.getErrorMessage(), - status.getSqlState(), status.getErrorCode()); + throw new HiveSQLException(status); } } diff --git service/src/java/org/apache/hive/service/cli/HiveSQLException.java service/src/java/org/apache/hive/service/cli/HiveSQLException.java index 8c9496e..f120425 100644 --- service/src/java/org/apache/hive/service/cli/HiveSQLException.java +++ service/src/java/org/apache/hive/service/cli/HiveSQLException.java @@ -19,6 +19,8 @@ package org.apache.hive.service.cli; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import org.apache.hive.service.cli.thrift.TStatus; import org.apache.hive.service.cli.thrift.TStatusCode; @@ -101,7 +103,10 @@ public HiveSQLException(String reason, String sqlState, int vendorCode, Throwabl public HiveSQLException(TStatus status) { // TODO: set correct vendorCode field - super(status.getErrorMessage(), status.getSqlState(), 1); + super(status.getErrorMessage(), status.getSqlState(), status.getErrorCode()); + if (status.getInfoMessages() != null) { + initCause(toCause(status.getInfoMessages())); + } } public TStatus toTStatus() { @@ -110,6 +115,7 @@ public TStatus toTStatus() { tStatus.setSqlState(getSQLState()); tStatus.setErrorCode(getErrorCode()); tStatus.setErrorMessage(getMessage()); + tStatus.setInfoMessages(toString(this)); return tStatus; } @@ -119,7 +125,96 @@ public static TStatus toTStatus(Exception e) { } TStatus tStatus = new TStatus(TStatusCode.ERROR_STATUS); tStatus.setErrorMessage(e.getMessage()); + tStatus.setInfoMessages(toString(e)); return tStatus; } + public static List toString(Throwable ex) { + return toString(ex, null); + } + + static List toString(Throwable cause, StackTraceElement[] parent) { + StackTraceElement[] trace = cause.getStackTrace(); + int m = trace.length - 1; + if (parent != null) { + int n = parent.length - 1; + if (parent != null) { + while (m >= 0 && n >= 0 && trace[m].equals(parent[n])) { + m--; n--; + } + } + } + List detail = enroll(cause, trace, m); + cause = cause.getCause(); + if (cause != null) { + detail.addAll(toString(cause, trace)); + } + return detail; + } + + static List enroll(Throwable ex, StackTraceElement[] trace, int max) { + List details = new ArrayList(); + StringBuilder builder = new StringBuilder(); + builder.append('*').append(ex.getClass().getName()).append(':'); + builder.append(ex.getMessage()).append(':'); + builder.append(trace.length).append(':').append(max); + details.add(builder.toString()); + for (int i = 0; i <= max; i++) { + builder.setLength(0); + builder.append(trace[i].getClassName()).append(':'); + builder.append(trace[i].getMethodName()).append(':'); + builder.append(trace[i].getLineNumber()); + details.add(builder.toString()); + } + return details; + } + + public static Throwable toCause(List details) { + return toStackTrace(details, null, 0); + } + + static Throwable toStackTrace(List details, StackTraceElement[] parent, int index) { + String detail = details.get(index++); + if (!detail.startsWith("*")) { + return null; // should not be happened + } + int i1 = detail.indexOf(':'); + int i3 = detail.lastIndexOf(':'); + int i2 = detail.substring(0, i3).lastIndexOf(':'); + String exceptionClass = detail.substring(1, i1); + String exceptionMessage = detail.substring(i1 + 1, i2); + Throwable ex = newInstance(exceptionClass, exceptionMessage); + + Integer length = Integer.valueOf(detail.substring(i2 + 1, i3)); + Integer unique = Integer.valueOf(detail.substring(i3 + 1)); + + int i = 0; + StackTraceElement[] trace = new StackTraceElement[length]; + for (; i <= unique; i++) { + detail = details.get(index++); + int j1 = detail.indexOf(':'); + int j2 = detail.lastIndexOf(':'); + String className = detail.substring(0, j1); + String methodName = detail.substring(j1 + 1, j2); + int lineNumber = Integer.valueOf(detail.substring(j2 + 1)); + trace[i] = new StackTraceElement(className, methodName, null, lineNumber); + } + int common = trace.length - i; + if (common > 0) { + System.arraycopy(parent, parent.length - common, trace, trace.length - common, common); + } + if (details.size() > index) { + ex.initCause(toStackTrace(details, trace, index)); + } + ex.setStackTrace(trace); + return ex; + } + + private static Throwable newInstance(String className, String message) { + try { + return (Throwable)Class.forName(className).getConstructor(String.class).newInstance(message); + } catch (Exception e) { + return new RuntimeException(className + ":" + message); + } + } } diff --git service/src/test/org/apache/hive/service/cli/TestHiveSQLException.java service/src/test/org/apache/hive/service/cli/TestHiveSQLException.java new file mode 100644 index 0000000..586f595 --- /dev/null +++ service/src/test/org/apache/hive/service/cli/TestHiveSQLException.java @@ -0,0 +1,44 @@ +/** + * 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.hive.service.cli; + +import junit.framework.Assert; +import org.junit.Test; + +public class TestHiveSQLException { + + @Test + public void testExceptionMarshalling() throws Exception { + Exception ex1 = ex1(); + ex1.initCause(ex2()); + Throwable ex = HiveSQLException.toCause(HiveSQLException.toString(ex1)); + Assert.assertSame(RuntimeException.class, ex.getClass()); + Assert.assertEquals("exception1", ex.getMessage()); + Assert.assertSame(UnsupportedOperationException.class, ex.getCause().getClass()); + Assert.assertEquals("exception2", ex.getCause().getMessage()); + } + + private static Exception ex1() { + return new RuntimeException("exception1"); + } + + private static Exception ex2() { + return new UnsupportedOperationException("exception2"); + } +}