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 ql/src/java/org/apache/hadoop/hive/ql/Driver.java ql/src/java/org/apache/hadoop/hive/ql/Driver.java index 68e7003..b5eebd8 100644 --- ql/src/java/org/apache/hadoop/hive/ql/Driver.java +++ ql/src/java/org/apache/hadoop/hive/ql/Driver.java @@ -1,4 +1,3 @@ - /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -968,8 +967,7 @@ public CommandProcessorResponse run(String command, boolean alreadyCompiled) } public CommandProcessorResponse compileAndRespond(String command) { - return new CommandProcessorResponse(compileInternal(command), - errorMessage, SQLState); + return createProcessorResponse(compileInternal(command)); } private int compileInternal(String command) { @@ -995,7 +993,7 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp downstreamError = null; if (!validateConfVariables()) { - return new CommandProcessorResponse(12, errorMessage, SQLState); + return createProcessorResponse(12); } HiveDriverRunHookContext hookContext = new HiveDriverRunHookContextImpl(conf, command); @@ -1013,7 +1011,7 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp downstreamError = e; console.printError(errorMessage + "\n" + org.apache.hadoop.util.StringUtils.stringifyException(e)); - return new CommandProcessorResponse(12, errorMessage, SQLState); + return createProcessorResponse(12); } // Reset the perf logger @@ -1021,7 +1019,6 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.DRIVER_RUN); perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.TIME_TO_SUBMIT); - int ret; boolean requireLock = false; boolean ckLock = false; try { @@ -1033,16 +1030,17 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp downstreamError = e; console.printError(errorMessage, "\n" + org.apache.hadoop.util.StringUtils.stringifyException(e)); - ret = 10; - return new CommandProcessorResponse(ret, errorMessage, SQLState); + return createProcessorResponse(10); + } + int ret = recordValidTxns(); + if (ret != 0) { + return createProcessorResponse(ret); } - ret = recordValidTxns(); - if (ret != 0) return new CommandProcessorResponse(ret, errorMessage, SQLState); if (!alreadyCompiled) { ret = compileInternal(command); if (ret != 0) { - return new CommandProcessorResponse(ret, errorMessage, SQLState); + return createProcessorResponse(ret); } } @@ -1084,7 +1082,7 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp } catch (LockException e) { // Not much to do here } - return new CommandProcessorResponse(ret, errorMessage, SQLState); + return createProcessorResponse(ret); } } @@ -1096,7 +1094,7 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp } catch (LockException e) { // Nothing to do here } - return new CommandProcessorResponse(ret, errorMessage, SQLState); + return createProcessorResponse(ret); } //if needRequireLock is false, the release here will do nothing because there is no lock @@ -1108,7 +1106,7 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp downstreamError = e; console.printError(errorMessage + "\n" + org.apache.hadoop.util.StringUtils.stringifyException(e)); - return new CommandProcessorResponse(12, errorMessage, SQLState); + return createProcessorResponse(12); } perfLogger.PerfLogEnd(CLASS_NAME, PerfLogger.DRIVER_RUN); @@ -1125,10 +1123,14 @@ private CommandProcessorResponse runInternal(String command, boolean alreadyComp downstreamError = e; console.printError(errorMessage + "\n" + org.apache.hadoop.util.StringUtils.stringifyException(e)); - return new CommandProcessorResponse(12, errorMessage, SQLState); + return createProcessorResponse(12); } - return new CommandProcessorResponse(ret); + return createProcessorResponse(ret); + } + + private CommandProcessorResponse createProcessorResponse(int ret) { + return new CommandProcessorResponse(ret, errorMessage, SQLState, downstreamError); } /** @@ -1447,6 +1449,7 @@ public int execute() throws CommandNeedRetryException { return (0); } + private void setErrorMsgAndDetail(int exitVal, Throwable downstreamError, Task tsk) { this.downstreamError = downstreamError; errorMessage = "FAILED: Execution Error, return code " + exitVal + " from " + tsk.getClass().getName(); diff --git ql/src/java/org/apache/hadoop/hive/ql/processors/CommandProcessorResponse.java ql/src/java/org/apache/hadoop/hive/ql/processors/CommandProcessorResponse.java index 2eb4438..f29a409 100644 --- ql/src/java/org/apache/hadoop/hive/ql/processors/CommandProcessorResponse.java +++ ql/src/java/org/apache/hadoop/hive/ql/processors/CommandProcessorResponse.java @@ -22,7 +22,7 @@ /** * Encapsulates the basic response info returned by classes the implement the - * CommandProcessor interfaace. Typically errorMessage + * CommandProcessor interface. Typically errorMessage * and SQLState will only be set if the responseCode * is not 0. */ @@ -32,23 +32,36 @@ private String SQLState; private Schema resSchema; + private Throwable exception; + public CommandProcessorResponse(int responseCode) { - this(responseCode, null, null); + this(responseCode, null, null, null, null); } public CommandProcessorResponse(int responseCode, String errorMessage, String SQLState) { - this(responseCode, errorMessage, SQLState, null); + this(responseCode, errorMessage, SQLState, null, null); + } + + public CommandProcessorResponse(int responseCode, String errorMessage, String SQLState, Throwable exception) { + this(responseCode, errorMessage, SQLState, null, exception); } public CommandProcessorResponse(int responseCode, String errorMessage, String SQLState, Schema schema) { + this(responseCode, errorMessage, SQLState, schema, null); + } + + public CommandProcessorResponse(int responseCode, String errorMessage, String SQLState, + Schema schema, Throwable exception) { this.responseCode = responseCode; this.errorMessage = errorMessage; this.SQLState = SQLState; this.resSchema = schema; + this.exception = exception; } public int getResponseCode() { return responseCode; } public String getErrorMessage() { return errorMessage; } public String getSQLState() { return SQLState; } public Schema getSchema() { return resSchema; } + public Throwable getException() { return exception; } } diff --git service/src/java/org/apache/hive/service/cli/HiveSQLException.java service/src/java/org/apache/hive/service/cli/HiveSQLException.java index 8c9496e..c72cba3 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,101 @@ 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; + 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(':'); + String fileName = trace[i].getFileName(); + builder.append(fileName == null ? "" : fileName).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. ignore remaining + } + 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 j3 = detail.lastIndexOf(':'); + int j2 = detail.substring(0, j3).lastIndexOf(':'); + String className = detail.substring(0, j1); + String methodName = detail.substring(j1 + 1, j2); + String fileName = detail.substring(j2 + 1, j3); + if (fileName.isEmpty()) { + fileName = null; + } + int lineNumber = Integer.valueOf(detail.substring(j3 + 1)); + trace[i] = new StackTraceElement(className, methodName, fileName, 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/java/org/apache/hive/service/cli/operation/HiveCommandOperation.java service/src/java/org/apache/hive/service/cli/operation/HiveCommandOperation.java index f708650..85a0f9e 100644 --- service/src/java/org/apache/hive/service/cli/operation/HiveCommandOperation.java +++ service/src/java/org/apache/hive/service/cli/operation/HiveCommandOperation.java @@ -109,8 +109,7 @@ public void run() throws HiveSQLException { response = commandProcessor.run(commandArgs); int returnCode = response.getResponseCode(); if (returnCode != 0) { - throw new HiveSQLException("Error while processing statement: " - + response.getErrorMessage(), response.getSQLState(), response.getResponseCode()); + throw toSQLException("Error while processing statement", response); } Schema schema = response.getSchema(); if (schema != null) { diff --git service/src/java/org/apache/hive/service/cli/operation/Operation.java service/src/java/org/apache/hive/service/cli/operation/Operation.java index d6651ba..45fbd61 100644 --- service/src/java/org/apache/hive/service/cli/operation/Operation.java +++ service/src/java/org/apache/hive/service/cli/operation/Operation.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hadoop.hive.ql.processors.CommandProcessorResponse; import org.apache.hive.service.cli.FetchOrientation; import org.apache.hive.service.cli.HiveSQLException; import org.apache.hive.service.cli.OperationHandle; @@ -178,4 +179,13 @@ protected void validateFetchOrientation(FetchOrientation orientation, " is not supported for this resultset", "HY106"); } } + + protected HiveSQLException toSQLException(String prefix, CommandProcessorResponse response) { + HiveSQLException ex = new HiveSQLException(prefix + ": " + response.getErrorMessage(), + response.getSQLState(), response.getResponseCode()); + if (response.getException() != null) { + ex.initCause(response.getException()); + } + return ex; + } } diff --git service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java index 5e7ee93..de54ca1 100644 --- service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java +++ service/src/java/org/apache/hive/service/cli/operation/SQLOperation.java @@ -98,8 +98,7 @@ public void prepare(HiveConf sqlOperationConf) throws HiveSQLException { String subStatement = new VariableSubstitution().substitute(sqlOperationConf, statement); response = driver.compileAndRespond(subStatement); if (0 != response.getResponseCode()) { - throw new HiveSQLException("Error while compiling statement: " - + response.getErrorMessage(), response.getSQLState(), response.getResponseCode()); + throw toSQLException("Error while compiling statement", response); } mResultSchema = driver.getSchema(); @@ -143,8 +142,7 @@ private void runInternal(HiveConf sqlOperationConf) throws HiveSQLException { driver.setTryCount(Integer.MAX_VALUE); response = driver.run(); if (0 != response.getResponseCode()) { - throw new HiveSQLException("Error while processing statement: " - + response.getErrorMessage(), response.getSQLState(), response.getResponseCode()); + throw toSQLException("Error while processing statement", response); } } catch (HiveSQLException e) { // If the operation was cancelled by another thread, 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"); + } +}