|
27 | 27 | import io.cdap.cdap.etl.api.exception.ErrorDetailsProvider; |
28 | 28 |
|
29 | 29 | import java.sql.SQLException; |
| 30 | +import java.util.HashMap; |
30 | 31 | import java.util.List; |
| 32 | +import java.util.Map; |
31 | 33 |
|
32 | 34 | /** |
33 | 35 | * A custom ErrorDetailsProvider for Database plugins. |
34 | 36 | */ |
35 | 37 | public class DBErrorDetailsProvider implements ErrorDetailsProvider { |
36 | 38 |
|
| 39 | + private static final Map<String, ErrorType> ERROR_CODE_TO_ERROR_TYPE; |
| 40 | + private static final Map<String, ErrorCategory> ERROR_CODE_TO_ERROR_CATEGORY; |
| 41 | + |
| 42 | + static { |
| 43 | + // https://en.wikipedia.org/wiki/SQLSTATE |
| 44 | + ERROR_CODE_TO_ERROR_TYPE = new HashMap<>(); |
| 45 | + ERROR_CODE_TO_ERROR_TYPE.put("01", ErrorType.USER); |
| 46 | + ERROR_CODE_TO_ERROR_TYPE.put("02", ErrorType.USER); |
| 47 | + ERROR_CODE_TO_ERROR_TYPE.put("07", ErrorType.USER); |
| 48 | + ERROR_CODE_TO_ERROR_TYPE.put("08", ErrorType.SYSTEM); |
| 49 | + ERROR_CODE_TO_ERROR_TYPE.put("09", ErrorType.USER); |
| 50 | + ERROR_CODE_TO_ERROR_TYPE.put("0A", ErrorType.SYSTEM); |
| 51 | + ERROR_CODE_TO_ERROR_TYPE.put("0D", ErrorType.USER); |
| 52 | + ERROR_CODE_TO_ERROR_TYPE.put("0E", ErrorType.USER); |
| 53 | + ERROR_CODE_TO_ERROR_TYPE.put("0F", ErrorType.USER); |
| 54 | + ERROR_CODE_TO_ERROR_TYPE.put("0K", ErrorType.USER); |
| 55 | + ERROR_CODE_TO_ERROR_TYPE.put("0L", ErrorType.USER); |
| 56 | + ERROR_CODE_TO_ERROR_TYPE.put("0M", ErrorType.USER); |
| 57 | + ERROR_CODE_TO_ERROR_TYPE.put("0N", ErrorType.USER); |
| 58 | + ERROR_CODE_TO_ERROR_TYPE.put("0P", ErrorType.USER); |
| 59 | + ERROR_CODE_TO_ERROR_TYPE.put("0S", ErrorType.USER); |
| 60 | + ERROR_CODE_TO_ERROR_TYPE.put("0T", ErrorType.USER); |
| 61 | + ERROR_CODE_TO_ERROR_TYPE.put("0U", ErrorType.USER); |
| 62 | + ERROR_CODE_TO_ERROR_TYPE.put("0V", ErrorType.USER); |
| 63 | + ERROR_CODE_TO_ERROR_TYPE.put("0W", ErrorType.USER); |
| 64 | + ERROR_CODE_TO_ERROR_TYPE.put("0X", ErrorType.USER); |
| 65 | + ERROR_CODE_TO_ERROR_TYPE.put("0Y", ErrorType.USER); |
| 66 | + ERROR_CODE_TO_ERROR_TYPE.put("0Z", ErrorType.SYSTEM); |
| 67 | + ERROR_CODE_TO_ERROR_TYPE.put("10", ErrorType.USER); |
| 68 | + ERROR_CODE_TO_ERROR_TYPE.put("20", ErrorType.USER); |
| 69 | + ERROR_CODE_TO_ERROR_TYPE.put("21", ErrorType.USER); |
| 70 | + ERROR_CODE_TO_ERROR_TYPE.put("22", ErrorType.USER); |
| 71 | + ERROR_CODE_TO_ERROR_TYPE.put("23", ErrorType.USER); |
| 72 | + ERROR_CODE_TO_ERROR_TYPE.put("24", ErrorType.USER); |
| 73 | + ERROR_CODE_TO_ERROR_TYPE.put("25", ErrorType.SYSTEM); |
| 74 | + ERROR_CODE_TO_ERROR_TYPE.put("26", ErrorType.USER); |
| 75 | + ERROR_CODE_TO_ERROR_TYPE.put("27", ErrorType.SYSTEM); |
| 76 | + ERROR_CODE_TO_ERROR_TYPE.put("28", ErrorType.USER); |
| 77 | + ERROR_CODE_TO_ERROR_TYPE.put("2B", ErrorType.USER); |
| 78 | + ERROR_CODE_TO_ERROR_TYPE.put("2C", ErrorType.USER); |
| 79 | + ERROR_CODE_TO_ERROR_TYPE.put("2D", ErrorType.USER); |
| 80 | + ERROR_CODE_TO_ERROR_TYPE.put("2E", ErrorType.USER); |
| 81 | + ERROR_CODE_TO_ERROR_TYPE.put("2F", ErrorType.SYSTEM); |
| 82 | + ERROR_CODE_TO_ERROR_TYPE.put("2H", ErrorType.USER); |
| 83 | + ERROR_CODE_TO_ERROR_TYPE.put("30", ErrorType.USER); |
| 84 | + ERROR_CODE_TO_ERROR_TYPE.put("33", ErrorType.USER); |
| 85 | + ERROR_CODE_TO_ERROR_TYPE.put("34", ErrorType.USER); |
| 86 | + ERROR_CODE_TO_ERROR_TYPE.put("35", ErrorType.USER); |
| 87 | + ERROR_CODE_TO_ERROR_TYPE.put("36", ErrorType.SYSTEM); |
| 88 | + ERROR_CODE_TO_ERROR_TYPE.put("38", ErrorType.SYSTEM); |
| 89 | + ERROR_CODE_TO_ERROR_TYPE.put("39", ErrorType.SYSTEM); |
| 90 | + ERROR_CODE_TO_ERROR_TYPE.put("3B", ErrorType.SYSTEM); |
| 91 | + ERROR_CODE_TO_ERROR_TYPE.put("3C", ErrorType.USER); |
| 92 | + ERROR_CODE_TO_ERROR_TYPE.put("3D", ErrorType.USER); |
| 93 | + ERROR_CODE_TO_ERROR_TYPE.put("3F", ErrorType.USER); |
| 94 | + ERROR_CODE_TO_ERROR_TYPE.put("40", ErrorType.SYSTEM); |
| 95 | + ERROR_CODE_TO_ERROR_TYPE.put("42", ErrorType.USER); |
| 96 | + ERROR_CODE_TO_ERROR_TYPE.put("44", ErrorType.USER); |
| 97 | + ERROR_CODE_TO_ERROR_TYPE.put("45", ErrorType.USER); |
| 98 | + ERROR_CODE_TO_ERROR_TYPE.put("46", ErrorType.SYSTEM); |
| 99 | + ERROR_CODE_TO_ERROR_TYPE.put("HW", ErrorType.SYSTEM); |
| 100 | + |
| 101 | + ERROR_CODE_TO_ERROR_CATEGORY = new HashMap<>(); |
| 102 | + ErrorCategory.ErrorCategoryEnum plugin = ErrorCategory.ErrorCategoryEnum.PLUGIN; |
| 103 | + ERROR_CODE_TO_ERROR_CATEGORY.put("01", new ErrorCategory(plugin, "DB Warning")); |
| 104 | + ERROR_CODE_TO_ERROR_CATEGORY.put("02", new ErrorCategory(plugin, "DB No Data")); |
| 105 | + ERROR_CODE_TO_ERROR_CATEGORY.put("07", new ErrorCategory(plugin, "DB Dynamic SQL error")); |
| 106 | + ERROR_CODE_TO_ERROR_CATEGORY.put("08", new ErrorCategory(plugin, "DB Connection Exception")); |
| 107 | + ERROR_CODE_TO_ERROR_CATEGORY.put("09", new ErrorCategory(plugin, "DB Triggered Action Exception")); |
| 108 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0A", new ErrorCategory(plugin, "DB Feature Not Supported")); |
| 109 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0D", new ErrorCategory(plugin, "DB Invalid Target Type Specification")); |
| 110 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0E", new ErrorCategory(plugin, "DB Invalid Schema Name List Specification")); |
| 111 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0F", new ErrorCategory(plugin, "DB Locator Exception")); |
| 112 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0K", new ErrorCategory(plugin, "DB Resignal When Handler Not Active")); |
| 113 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0L", new ErrorCategory(plugin, "DB Invalid Grantor")); |
| 114 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0M", new ErrorCategory(plugin, "DB Invalid SQL-Invoked Procedure Reference")); |
| 115 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0N", new ErrorCategory(plugin, "DB SQL/XML Mapping Error")); |
| 116 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0P", new ErrorCategory(plugin, "DB Invalid Role Specification")); |
| 117 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0S", new ErrorCategory(plugin, "DB Invalid Transform Group Name Specification")); |
| 118 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0T", |
| 119 | + new ErrorCategory(plugin, "DB Target Table Disagrees With Cursor Specification")); |
| 120 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0U", new ErrorCategory(plugin, "DB Attempt To Assign To Non-Updatable Column")); |
| 121 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0V", new ErrorCategory(plugin, "DB Attempt To Assign To Ordering Column")); |
| 122 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0W", new ErrorCategory(plugin, "DB Prohibited Statement Encountered")); |
| 123 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0X", new ErrorCategory(plugin, "DB Invalid Foreign Server Specification")); |
| 124 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0Y", new ErrorCategory(plugin, "DB Pass-Through Specific Condition")); |
| 125 | + ERROR_CODE_TO_ERROR_CATEGORY.put("0Z", new ErrorCategory(plugin, "DB Diagnostics Exception")); |
| 126 | + ERROR_CODE_TO_ERROR_CATEGORY.put("10", new ErrorCategory(plugin, "DB XQuery Error")); |
| 127 | + ERROR_CODE_TO_ERROR_CATEGORY.put("20", new ErrorCategory(plugin, "DB Case Not Found For Case Statement")); |
| 128 | + ERROR_CODE_TO_ERROR_CATEGORY.put("21", new ErrorCategory(plugin, "DB Cardinality Violation")); |
| 129 | + ERROR_CODE_TO_ERROR_CATEGORY.put("22", new ErrorCategory(plugin, "DB Data Exception")); |
| 130 | + ERROR_CODE_TO_ERROR_CATEGORY.put("23", new ErrorCategory(plugin, "DB Integrity Constraint Violation")); |
| 131 | + ERROR_CODE_TO_ERROR_CATEGORY.put("24", new ErrorCategory(plugin, "DB Invalid Cursor State")); |
| 132 | + ERROR_CODE_TO_ERROR_CATEGORY.put("25", new ErrorCategory(plugin, "DB Invalid Transaction State")); |
| 133 | + ERROR_CODE_TO_ERROR_CATEGORY.put("26", new ErrorCategory(plugin, "DB Invalid SQL Statement Name")); |
| 134 | + ERROR_CODE_TO_ERROR_CATEGORY.put("27", new ErrorCategory(plugin, "DB Triggered Data Change Violation")); |
| 135 | + ERROR_CODE_TO_ERROR_CATEGORY.put("28", new ErrorCategory(plugin, "DB Invalid Authorization Specification")); |
| 136 | + ERROR_CODE_TO_ERROR_CATEGORY.put("2B", new ErrorCategory(plugin, "DB Dependent Privilege Descriptors Still Exist")); |
| 137 | + ERROR_CODE_TO_ERROR_CATEGORY.put("2C", new ErrorCategory(plugin, "DB Invalid Character Set Name")); |
| 138 | + ERROR_CODE_TO_ERROR_CATEGORY.put("2D", new ErrorCategory(plugin, "DB Invalid Transaction Termination")); |
| 139 | + ERROR_CODE_TO_ERROR_CATEGORY.put("2E", new ErrorCategory(plugin, "DB Invalid Connection Name")); |
| 140 | + ERROR_CODE_TO_ERROR_CATEGORY.put("2F", new ErrorCategory(plugin, "DB SQL Routine Exception")); |
| 141 | + ERROR_CODE_TO_ERROR_CATEGORY.put("2H", new ErrorCategory(plugin, "DB Invalid Collation Name")); |
| 142 | + ERROR_CODE_TO_ERROR_CATEGORY.put("30", new ErrorCategory(plugin, "DB Invalid SQL Statement Identifier")); |
| 143 | + ERROR_CODE_TO_ERROR_CATEGORY.put("33", new ErrorCategory(plugin, "DB Invalid SQL Descriptor Name")); |
| 144 | + ERROR_CODE_TO_ERROR_CATEGORY.put("34", new ErrorCategory(plugin, "DB Invalid Cursor Name")); |
| 145 | + ERROR_CODE_TO_ERROR_CATEGORY.put("35", new ErrorCategory(plugin, "DB Invalid Condition Number")); |
| 146 | + ERROR_CODE_TO_ERROR_CATEGORY.put("36", new ErrorCategory(plugin, "DB Cursor Sensitivity Exception")); |
| 147 | + ERROR_CODE_TO_ERROR_CATEGORY.put("38", new ErrorCategory(plugin, "DB External Routine Exception")); |
| 148 | + ERROR_CODE_TO_ERROR_CATEGORY.put("39", new ErrorCategory(plugin, "DB External Routine Invocation Exception")); |
| 149 | + ERROR_CODE_TO_ERROR_CATEGORY.put("3B", new ErrorCategory(plugin, "DB Savepoint Exception")); |
| 150 | + ERROR_CODE_TO_ERROR_CATEGORY.put("3C", new ErrorCategory(plugin, "DB Ambiguous Cursor Name")); |
| 151 | + ERROR_CODE_TO_ERROR_CATEGORY.put("3D", new ErrorCategory(plugin, "DB Invalid Catalog Name")); |
| 152 | + ERROR_CODE_TO_ERROR_CATEGORY.put("3F", new ErrorCategory(plugin, "DB Invalid Schema Name")); |
| 153 | + ERROR_CODE_TO_ERROR_CATEGORY.put("40", new ErrorCategory(plugin, "DB Transaction Rollback")); |
| 154 | + ERROR_CODE_TO_ERROR_CATEGORY.put("42", new ErrorCategory(plugin, "DB Syntax Error or Access Rule Violation")); |
| 155 | + ERROR_CODE_TO_ERROR_CATEGORY.put("44", new ErrorCategory(plugin, "DB With Check Option Violation")); |
| 156 | + ERROR_CODE_TO_ERROR_CATEGORY.put("45", new ErrorCategory(plugin, "DB Unhandled User-Defined Exception")); |
| 157 | + ERROR_CODE_TO_ERROR_CATEGORY.put("46", new ErrorCategory(plugin, "DB JAVA DDL")); |
| 158 | + ERROR_CODE_TO_ERROR_CATEGORY.put("HW", new ErrorCategory(plugin, "DB Datalink Exception")); |
| 159 | + } |
| 160 | + |
| 161 | + |
37 | 162 | public ProgramFailureException getExceptionDetails(Exception e, ErrorContext errorContext) { |
38 | 163 | List<Throwable> causalChain = Throwables.getCausalChain(e); |
39 | 164 | for (Throwable t : causalChain) { |
@@ -70,15 +195,15 @@ private ProgramFailureException getProgramFailureException(SQLException e, Error |
70 | 195 | errorContext.getPhase(), sqlState, errorCode, errorMessage); |
71 | 196 | String externalDocumentationLink = getExternalDocumentationLink(); |
72 | 197 | if (!Strings.isNullOrEmpty(externalDocumentationLink)) { |
73 | | - if (!errorMessageWithDetails.endsWith(".")) { |
74 | | - errorMessageWithDetails = errorMessageWithDetails + "."; |
| 198 | + if (!errorMessage.endsWith(".")) { |
| 199 | + errorMessage = errorMessage + "."; |
75 | 200 | } |
76 | | - errorMessageWithDetails = String.format("%s For more details, see %s", errorMessageWithDetails, |
| 201 | + errorMessage = String.format("%s For more details, see %s", errorMessage, |
77 | 202 | externalDocumentationLink); |
78 | 203 | } |
79 | 204 | return ErrorUtils.getProgramFailureException(Strings.isNullOrEmpty(sqlState) ? |
80 | 205 | new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN) : getErrorCategoryFromSqlState(sqlState), |
81 | | - errorMessage, errorMessageWithDetails, getErrorTypeFromErrorCode(errorCode, sqlState), true, |
| 206 | + errorMessage, errorMessageWithDetails, getErrorTypeFromErrorCodeAndSqlState(errorCode, sqlState), true, |
82 | 207 | ErrorCodeType.SQLSTATE, sqlState, externalDocumentationLink, e); |
83 | 208 | } |
84 | 209 |
|
@@ -121,11 +246,42 @@ protected String getExternalDocumentationLink() { |
121 | 246 | return null; |
122 | 247 | } |
123 | 248 |
|
124 | | - protected ErrorType getErrorTypeFromErrorCode(int errorCode, String sqlState) { |
125 | | - return ErrorType.UNKNOWN; |
| 249 | + /** |
| 250 | + * Get the {@link ErrorType} for the given error code and SQL state. |
| 251 | + * Override this method to provide custom error types based on the error code and SQL state. |
| 252 | + * |
| 253 | + * @param errorCode The error code. |
| 254 | + * @param sqlState The SQL state. |
| 255 | + * @return The {@link ErrorType} for the given error code and SQL state. |
| 256 | + */ |
| 257 | + protected ErrorType getErrorTypeFromErrorCodeAndSqlState(int errorCode, String sqlState) { |
| 258 | + // Default implementation uses the SQL state to determine the error type. |
| 259 | + return getErrorTypeFromSqlState(sqlState); |
126 | 260 | } |
127 | 261 |
|
| 262 | + /** |
| 263 | + * Get the {@link ErrorCategory} for the given SQL state. |
| 264 | + * Implements generic error categories based on the SQL state. |
| 265 | + * See <a href="https://en.wikipedia.org/wiki/SQLSTATE">SQLSTATE</a> for more information. |
| 266 | + * Override this method to provide custom error categories based on the SQL state. |
| 267 | + * |
| 268 | + * @param sqlState The SQL state. |
| 269 | + * @return The {@link ErrorCategory} for the given SQL state. |
| 270 | + */ |
128 | 271 | protected ErrorCategory getErrorCategoryFromSqlState(String sqlState) { |
| 272 | + if (!Strings.isNullOrEmpty(sqlState) && sqlState.length() >= 2 && |
| 273 | + ERROR_CODE_TO_ERROR_CATEGORY.containsKey(sqlState.substring(0, 2))) { |
| 274 | + return ERROR_CODE_TO_ERROR_CATEGORY.get(sqlState.substring(0, 2)); |
| 275 | + } |
129 | 276 | return new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN); |
130 | 277 | } |
| 278 | + |
| 279 | + private ErrorType getErrorTypeFromSqlState(String sqlState) { |
| 280 | + if (!Strings.isNullOrEmpty(sqlState) && sqlState.length() >= 2 && |
| 281 | + ERROR_CODE_TO_ERROR_TYPE.containsKey(sqlState.substring(0, 2))) { |
| 282 | + return ERROR_CODE_TO_ERROR_TYPE.get(sqlState.substring(0, 2)); |
| 283 | + } |
| 284 | + return ErrorType.UNKNOWN; |
| 285 | + } |
| 286 | + |
131 | 287 | } |
0 commit comments