diff --git a/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLErrorDetailsProvider.java b/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLErrorDetailsProvider.java index 87f5ed2ec..fe276a27a 100644 --- a/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLErrorDetailsProvider.java +++ b/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLErrorDetailsProvider.java @@ -18,6 +18,7 @@ import io.cdap.plugin.mysql.MysqlErrorDetailsProvider; +import io.cdap.plugin.util.DBUtils; /** * A custom ErrorDetailsProvider for CloudSQL MySQL plugins. @@ -26,6 +27,6 @@ public class CloudSQLMySQLErrorDetailsProvider extends MysqlErrorDetailsProvider @Override protected String getExternalDocumentationLink() { - return "https://cloud.google.com/sql/docs/mysql/error-messages"; + return DBUtils.CLOUDSQLMYSQL_SUPPORTED_DOC_URL; } } diff --git a/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLSource.java b/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLSource.java index aad074ba4..8273169c0 100644 --- a/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLSource.java +++ b/cloudsql-mysql-plugin/src/main/java/io/cdap/plugin/cloudsql/mysql/CloudSQLMySQLSource.java @@ -81,6 +81,11 @@ protected Class getDBRecordType() { return MysqlDBRecord.class; } + @Override + protected String getExternalDocumentationLink() { + return DBUtils.CLOUDSQLMYSQL_SUPPORTED_DOC_URL; + } + @Override protected String createConnectionString() { if (CloudSQLUtil.PRIVATE_INSTANCE.equalsIgnoreCase( 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 index bafdba3e3..22a979d58 100644 --- a/database-commons/src/main/java/io/cdap/plugin/db/DBErrorDetailsProvider.java +++ b/database-commons/src/main/java/io/cdap/plugin/db/DBErrorDetailsProvider.java @@ -19,11 +19,13 @@ 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 io.cdap.plugin.util.DBUtils; import java.sql.SQLException; import java.util.List; @@ -76,7 +78,8 @@ private ProgramFailureException getProgramFailureException(SQLException e, Error externalDocumentationLink); } return ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), - errorMessage, errorMessageWithDetails, getErrorTypeFromErrorCode(errorCode), false, e); + errorMessage, errorMessageWithDetails, getErrorTypeFromErrorCode(errorCode), false, ErrorCodeType.SQLSTATE, + sqlState, externalDocumentationLink, e); } /** diff --git a/database-commons/src/main/java/io/cdap/plugin/db/DBRecord.java b/database-commons/src/main/java/io/cdap/plugin/db/DBRecord.java index a0b1ec14e..a5a9fcf5f 100644 --- a/database-commons/src/main/java/io/cdap/plugin/db/DBRecord.java +++ b/database-commons/src/main/java/io/cdap/plugin/db/DBRecord.java @@ -17,7 +17,6 @@ package io.cdap.plugin.db; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import io.cdap.cdap.api.common.Bytes; import io.cdap.cdap.api.data.format.StructuredRecord; import io.cdap.cdap.api.data.schema.Schema; 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 5d7447fc8..559985758 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 @@ -26,6 +26,7 @@ import io.cdap.cdap.api.data.schema.Schema; import io.cdap.cdap.api.dataset.lib.KeyValue; 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.plugin.PluginConfig; @@ -202,8 +203,17 @@ private Schema loadSchemaFromDB(Class driverClass) // wrap exception to ensure SQLException-child instances not exposed to contexts without jdbc driver in classpath 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 + "."; + } + errorMessageWithDetails = String.format("%s For more details, see %s", errorMessageWithDetails, + externalDocumentationLink); + } throw ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), - e.getMessage(), errorMessageWithDetails, ErrorType.USER, false, new SQLException(e.getMessage(), + e.getMessage(), errorMessageWithDetails, ErrorType.USER, false, ErrorCodeType.SQLSTATE, + e.getSQLState(), externalDocumentationLink, new SQLException(e.getMessage(), e.getSQLState(), e.getErrorCode())); } finally { driverCleanup.destroy(); @@ -363,6 +373,10 @@ protected Class getDBRecordType() { return DBRecord.class; } + protected String getExternalDocumentationLink() { + return null; + } + @Override public void initialize(BatchRuntimeContext context) throws Exception { super.initialize(context); diff --git a/database-commons/src/main/java/io/cdap/plugin/util/DBUtils.java b/database-commons/src/main/java/io/cdap/plugin/util/DBUtils.java index 584c7bb3f..d526d5e36 100644 --- a/database-commons/src/main/java/io/cdap/plugin/util/DBUtils.java +++ b/database-commons/src/main/java/io/cdap/plugin/util/DBUtils.java @@ -60,6 +60,8 @@ public final class DBUtils { private static final Logger LOG = LoggerFactory.getLogger(DBUtils.class); public static final Calendar PURE_GREGORIAN_CALENDAR = createPureGregorianCalender(); + public static final String MYSQL_SUPPORTED_DOC_URL = "https://dev.mysql.com/doc/mysql-errors/9.0/en/"; + public static final String CLOUDSQLMYSQL_SUPPORTED_DOC_URL = "https://cloud.google.com/sql/docs/mysql/error-messages"; // Java by default uses October 15, 1582 as a Gregorian cut over date. // Any timestamp created with time less than this cut over date is treated as Julian date. 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 2f0a8f739..00f6d9810 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 @@ -18,6 +18,7 @@ import io.cdap.cdap.api.exception.ErrorType; import io.cdap.plugin.db.DBErrorDetailsProvider; +import io.cdap.plugin.util.DBUtils; /** * A custom ErrorDetailsProvider for MySQL plugins. @@ -26,7 +27,7 @@ public class MysqlErrorDetailsProvider extends DBErrorDetailsProvider { @Override protected String getExternalDocumentationLink() { - return "https://dev.mysql.com/doc/mysql-errors/9.0/en/"; + return DBUtils.MYSQL_SUPPORTED_DOC_URL; } @Override diff --git a/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlSource.java b/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlSource.java index 173c6d07f..971b76809 100644 --- a/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlSource.java +++ b/mysql-plugin/src/main/java/io/cdap/plugin/mysql/MysqlSource.java @@ -69,6 +69,11 @@ protected Class getDBRecordType() { return MysqlDBRecord.class; } + @Override + protected String getExternalDocumentationLink() { + return DBUtils.MYSQL_SUPPORTED_DOC_URL; + } + @Override protected LineageRecorder getLineageRecorder(BatchSourceContext context) { String fqn = DBUtils.constructFQN("mysql",