diff --git a/database-commons/src/main/java/io/cdap/plugin/db/DBErrorDetailsProvider.java b/database-commons/src/main/java/io/cdap/plugin/db/DBErrorDetailsProvider.java deleted file mode 100644 index cc731d6ac..000000000 --- a/database-commons/src/main/java/io/cdap/plugin/db/DBErrorDetailsProvider.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright © 2024 Cask Data, Inc. - * - * Licensed 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 io.cdap.plugin.db; - -import com.google.common.base.Strings; -import com.google.common.base.Throwables; -import io.cdap.cdap.api.exception.ErrorCategory; -import io.cdap.cdap.api.exception.ErrorCodeType; -import io.cdap.cdap.api.exception.ErrorType; -import io.cdap.cdap.api.exception.ErrorUtils; -import io.cdap.cdap.api.exception.ProgramFailureException; -import io.cdap.cdap.etl.api.exception.ErrorContext; -import io.cdap.cdap.etl.api.exception.ErrorDetailsProvider; - -import java.sql.SQLException; -import java.util.List; - -/** - * A custom ErrorDetailsProvider for Database plugins. - */ -public class DBErrorDetailsProvider implements ErrorDetailsProvider { - - public ProgramFailureException getExceptionDetails(Exception e, ErrorContext errorContext) { - List causalChain = Throwables.getCausalChain(e); - for (Throwable t : causalChain) { - if (t instanceof ProgramFailureException) { - // if causal chain already has program failure exception, return null to avoid double wrap. - return null; - } - if (t instanceof SQLException) { - return getProgramFailureException((SQLException) t, errorContext); - } - if (t instanceof IllegalArgumentException) { - return getProgramFailureException((IllegalArgumentException) t, errorContext); - } - if (t instanceof IllegalStateException) { - return getProgramFailureException((IllegalStateException) t, errorContext); - } - } - return null; - } - - /** - * Get a ProgramFailureException with the given error - * information from {@link SQLException}. - * - * @param e The SQLException to get the error information from. - * @return A ProgramFailureException with the given error information. - */ - private ProgramFailureException getProgramFailureException(SQLException e, ErrorContext errorContext) { - String errorMessage = e.getMessage(); - String sqlState = e.getSQLState(); - int errorCode = e.getErrorCode(); - String errorMessageWithDetails = String.format( - "Error occurred in the phase: '%s' with sqlState: '%s', errorCode: '%s', errorMessage: %s", - errorContext.getPhase(), sqlState, errorCode, errorMessage); - String externalDocumentationLink = getExternalDocumentationLink(); - if (!Strings.isNullOrEmpty(externalDocumentationLink)) { - if (!errorMessageWithDetails.endsWith(".")) { - errorMessageWithDetails = errorMessageWithDetails + "."; - } - errorMessageWithDetails = String.format("%s For more details, see %s", errorMessageWithDetails, - externalDocumentationLink); - } - return ErrorUtils.getProgramFailureException(Strings.isNullOrEmpty(sqlState) ? - new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN) : getErrorCategoryFromSqlState(sqlState), - errorMessage, errorMessageWithDetails, getErrorTypeFromErrorCode(errorCode, sqlState), true, - ErrorCodeType.SQLSTATE, sqlState, externalDocumentationLink, e); - } - - /** - * Get a ProgramFailureException with the given error - * information from {@link IllegalArgumentException}. - * - * @param e The IllegalArgumentException to get the error information from. - * @return A ProgramFailureException with the given error information. - */ - private ProgramFailureException getProgramFailureException(IllegalArgumentException e, ErrorContext errorContext) { - String errorMessage = e.getMessage(); - String errorMessageFormat = "Error occurred in the phase: '%s'. Error message: %s"; - return ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), - errorMessage, - String.format(errorMessageFormat, errorContext.getPhase(), errorMessage), ErrorType.USER, false, e); - } - - /** - * Get a ProgramFailureException with the given error - * information from {@link IllegalStateException}. - * - * @param e The IllegalStateException to get the error information from. - * @return A ProgramFailureException with the given error information. - */ - private ProgramFailureException getProgramFailureException(IllegalStateException e, ErrorContext errorContext) { - String errorMessage = e.getMessage(); - String errorMessageFormat = "Error occurred in the phase: '%s'. Error message: %s"; - return ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), - errorMessage, - String.format(errorMessageFormat, errorContext.getPhase(), errorMessage), ErrorType.SYSTEM, false, e); - } - - /** - * Get the external documentation link for the client errors if available. - * - * @return The external documentation link as a {@link String}. - */ - protected String getExternalDocumentationLink() { - return null; - } - - protected ErrorType getErrorTypeFromErrorCode(int errorCode, String sqlState) { - return ErrorType.UNKNOWN; - } - - protected ErrorCategory getErrorCategoryFromSqlState(String sqlState) { - return new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN); - } -} diff --git a/database-commons/src/main/java/io/cdap/plugin/db/sink/AbstractDBSink.java b/database-commons/src/main/java/io/cdap/plugin/db/sink/AbstractDBSink.java index 3252f70bd..797abfc23 100644 --- a/database-commons/src/main/java/io/cdap/plugin/db/sink/AbstractDBSink.java +++ b/database-commons/src/main/java/io/cdap/plugin/db/sink/AbstractDBSink.java @@ -47,7 +47,6 @@ import io.cdap.plugin.db.ConnectionConfig; import io.cdap.plugin.db.ConnectionConfigAccessor; import io.cdap.plugin.db.DBConfig; -import io.cdap.plugin.db.DBErrorDetailsProvider; import io.cdap.plugin.db.DBRecord; import io.cdap.plugin.db.Operation; import io.cdap.plugin.db.SchemaReader; @@ -313,18 +312,20 @@ private Schema inferSchema(Class driverClass) { } } catch (SQLException e) { // wrap exception to ensure SQLException-child instances not exposed to contexts w/o jdbc driver in classpath + String errorMessage = + String.format("SQL Exception occurred: [Message='%s', SQLState='%s', ErrorCode='%s'].", e.getMessage(), + e.getSQLState(), e.getErrorCode()); String errorMessageWithDetails = String.format("Error while reading table metadata." + "Error message: '%s'. Error code: '%s'. SQLState: '%s'", e.getMessage(), e.getErrorCode(), e.getSQLState()); String externalDocumentationLink = getExternalDocumentationLink(); if (!Strings.isNullOrEmpty(externalDocumentationLink)) { - if (!errorMessageWithDetails.endsWith(".")) { - errorMessageWithDetails = errorMessageWithDetails + "."; + if (!errorMessage.endsWith(".")) { + errorMessage = errorMessage + "."; } - errorMessageWithDetails = String.format("%s For more details, see %s", errorMessageWithDetails, - externalDocumentationLink); + errorMessage = String.format("%s For more details, see %s", errorMessageWithDetails, errorMessage); } throw ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), - e.getMessage(), errorMessageWithDetails, ErrorType.USER, false, ErrorCodeType.SQLSTATE, + errorMessage, errorMessageWithDetails, ErrorType.USER, false, ErrorCodeType.SQLSTATE, e.getSQLState(), externalDocumentationLink, new SQLException(e.getMessage(), e.getSQLState(), e.getErrorCode())); } diff --git a/database-commons/src/main/java/io/cdap/plugin/db/source/AbstractDBSource.java b/database-commons/src/main/java/io/cdap/plugin/db/source/AbstractDBSource.java index e3340fb87..3fd490ada 100644 --- a/database-commons/src/main/java/io/cdap/plugin/db/source/AbstractDBSource.java +++ b/database-commons/src/main/java/io/cdap/plugin/db/source/AbstractDBSource.java @@ -46,7 +46,6 @@ import io.cdap.plugin.db.ConnectionConfig; import io.cdap.plugin.db.ConnectionConfigAccessor; import io.cdap.plugin.db.DBConfig; -import io.cdap.plugin.db.DBErrorDetailsProvider; import io.cdap.plugin.db.DBRecord; import io.cdap.plugin.db.SchemaReader; import io.cdap.plugin.db.TransactionIsolationLevel; @@ -201,18 +200,20 @@ private Schema loadSchemaFromDB(Class driverClass) } catch (SQLException e) { // wrap exception to ensure SQLException-child instances not exposed to contexts without jdbc driver in classpath + String errorMessage = + String.format("SQL Exception occurred: [Message='%s', SQLState='%s', ErrorCode='%s'].", e.getMessage(), + e.getSQLState(), e.getErrorCode()); String errorMessageWithDetails = String.format("Error occurred while trying to get schema from database." + "Error message: '%s'. Error code: '%s'. SQLState: '%s'", e.getMessage(), e.getErrorCode(), e.getSQLState()); String externalDocumentationLink = getExternalDocumentationLink(); if (!Strings.isNullOrEmpty(externalDocumentationLink)) { - if (!errorMessageWithDetails.endsWith(".")) { - errorMessageWithDetails = errorMessageWithDetails + "."; + if (!errorMessage.endsWith(".")) { + errorMessage = errorMessage + "."; } - errorMessageWithDetails = String.format("%s For more details, see %s", errorMessageWithDetails, - externalDocumentationLink); + errorMessage = String.format("%s For more details, see %s", errorMessage, externalDocumentationLink); } throw ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), - e.getMessage(), errorMessageWithDetails, ErrorType.USER, false, ErrorCodeType.SQLSTATE, + errorMessage, errorMessageWithDetails, ErrorType.USER, false, ErrorCodeType.SQLSTATE, e.getSQLState(), externalDocumentationLink, new SQLException(e.getMessage(), e.getSQLState(), e.getErrorCode())); } finally { diff --git a/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlErrorDetailsProvider.java b/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlErrorDetailsProvider.java index 251f0fc74..ca9a2b928 100644 --- a/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlErrorDetailsProvider.java +++ b/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlErrorDetailsProvider.java @@ -17,7 +17,7 @@ package io.cdap.plugin.mysql; import io.cdap.cdap.api.exception.ErrorType; -import io.cdap.plugin.db.DBErrorDetailsProvider; +import io.cdap.plugin.common.db.DBErrorDetailsProvider; import io.cdap.plugin.util.DBUtils; /** @@ -31,7 +31,7 @@ protected String getExternalDocumentationLink() { } @Override - protected ErrorType getErrorTypeFromErrorCode(int errorCode, String sqlState) { + protected ErrorType getErrorTypeFromErrorCodeAndSqlState(int errorCode, String sqlState) { // https://dev.mysql.com/doc/refman/9.0/en/error-message-elements.html#error-code-ranges if (errorCode >= 1000 && errorCode <= 5999) { return ErrorType.USER; diff --git a/postgresql-plugin/src/main/java/io/cdap/plugin/postgres/PostgresErrorDetailsProvider.java b/postgresql-plugin/src/main/java/io/cdap/plugin/postgres/PostgresErrorDetailsProvider.java index 3202a3e28..a7de4e5dc 100644 --- a/postgresql-plugin/src/main/java/io/cdap/plugin/postgres/PostgresErrorDetailsProvider.java +++ b/postgresql-plugin/src/main/java/io/cdap/plugin/postgres/PostgresErrorDetailsProvider.java @@ -19,7 +19,7 @@ import com.google.common.base.Strings; import io.cdap.cdap.api.exception.ErrorCategory; import io.cdap.cdap.api.exception.ErrorType; -import io.cdap.plugin.db.DBErrorDetailsProvider; +import io.cdap.plugin.common.db.DBErrorDetailsProvider; import io.cdap.plugin.util.DBUtils; import java.util.HashMap; @@ -77,7 +77,7 @@ protected String getExternalDocumentationLink() { } @Override - protected ErrorType getErrorTypeFromErrorCode(int errorCode, String sqlState) { + protected ErrorType getErrorTypeFromErrorCodeAndSqlState(int errorCode, String sqlState) { if (!Strings.isNullOrEmpty(sqlState) && sqlState.length() >= 2 && ERROR_CODE_TO_ERROR_TYPE.containsKey(sqlState.substring(0, 2))) { return ERROR_CODE_TO_ERROR_TYPE.get(sqlState.substring(0, 2));