-
Notifications
You must be signed in to change notification settings - Fork 435
/
Copy pathSQLServerException.java
399 lines (357 loc) · 14.7 KB
/
SQLServerException.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/*
* Microsoft JDBC Driver for SQL Server
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
package com.microsoft.sqlserver.jdbc;
import java.text.MessageFormat;
import java.util.UUID;
import java.util.logging.Level;
/**
* SQLServerException is thrown from any point in the driver that throws a java.sql.SQLException. SQLServerException handles both SQL 92 and XOPEN
* state codes. They are switchable via a user specified connection property. SQLServerExceptions are written to any open log files the user has
* specified.
*/
enum SQLState {
STATEMENT_CANCELED ("HY008"),
DATA_EXCEPTION_NOT_SPECIFIC ("22000"),
DATA_EXCEPTION_DATETIME_FIELD_OVERFLOW ("22008"),
DATA_EXCEPTION_LENGTH_MISMATCH ("22026"),
COL_NOT_FOUND ("42S22");
private final String sqlStateCode;
final String getSQLStateCode() {
return sqlStateCode;
}
SQLState(String sqlStateCode) {
this.sqlStateCode = sqlStateCode;
}
}
enum DriverError {
NOT_SET(0);
private final int errorCode;
final int getErrorCode() {
return errorCode;
}
DriverError(int errorCode) {
this.errorCode = errorCode;
}
}
public final class SQLServerException extends java.sql.SQLException {
static final String EXCEPTION_XOPEN_CONNECTION_CANT_ESTABLISH = "08001";
static final String EXCEPTION_XOPEN_CONNECTION_DOES_NOT_EXIST = "08003";
static final String EXCEPTION_XOPEN_CONNECTION_FAILURE = "08006"; // After connection was connected OK
static final String LOG_CLIENT_CONNECTION_ID_PREFIX = " ClientConnectionId:";
// SQL error values (from sqlerrorcodes.h)
static final int LOGON_FAILED = 18456;
static final int PASSWORD_EXPIRED = 18488;
static java.util.logging.Logger exLogger = java.util.logging.Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerException");
// Facility for driver-specific error codes
static final int DRIVER_ERROR_NONE = 0;
static final int DRIVER_ERROR_FROM_DATABASE = 2;
static final int DRIVER_ERROR_IO_FAILED = 3;
static final int DRIVER_ERROR_INVALID_TDS = 4;
static final int DRIVER_ERROR_SSL_FAILED = 5;
static final int DRIVER_ERROR_UNSUPPORTED_CONFIG = 6;
static final int DRIVER_ERROR_INTERMITTENT_TLS_FAILED = 7;
static final int ERROR_SOCKET_TIMEOUT = 8;
private int driverErrorCode = DRIVER_ERROR_NONE;
final int getDriverErrorCode() {
return driverErrorCode;
}
final void setDriverErrorCode(int value) {
driverErrorCode = value;
}
/**
* Log an exception to the driver log file
*
* @param o
* the io buffer that generated the exception
* @param errText
* the excception message
* @param bStack
* true to generate the stack trace
*/
/* L0 */ private void logException(Object o,
String errText,
boolean bStack) {
String id = "";
if (o != null)
id = o.toString();
if (exLogger.isLoggable(Level.FINE))
exLogger.fine("*** SQLException:" + id + " " + this.toString() + " " + errText);
if (bStack) {
if (exLogger.isLoggable(Level.FINE)) {
StringBuilder sb = new StringBuilder(100);
StackTraceElement st[] = this.getStackTrace();
for (int i = 0; i < st.length; i++)
sb.append(st[i].toString());
Throwable t = this.getCause();
if (t != null) {
sb.append("\n caused by " + t + "\n");
StackTraceElement tst[] = t.getStackTrace();
for (int i = 0; i < tst.length; i++)
sb.append(tst[i].toString());
}
exLogger.fine(sb.toString());
}
}
}
static String getErrString(String errCode) {
return SQLServerResource.getResource(errCode);
}
/**
* Make a new SQLException
*
* @param errText
* the excception message
* @param errState
* the excpeption state
* @param driverError
* @param cause
* The exception that caused this exception
*/
public SQLServerException(String errText,
SQLState sqlState,
DriverError driverError,
Throwable cause) {
this(errText, sqlState.getSQLStateCode(), driverError.getErrorCode(), cause);
}
public SQLServerException(String errText,
String errState,
int errNum,
Throwable cause) {
super(errText, errState, errNum);
initCause(cause);
logException(null, errText, true);
ActivityCorrelator.setCurrentActivityIdSentFlag(); // set the activityid flag so that we don't send the current ActivityId later.
}
public SQLServerException(String errText,
Throwable cause) {
super(errText);
initCause(cause);
logException(null, errText, true);
ActivityCorrelator.setCurrentActivityIdSentFlag();
}
/* L0 */ public SQLServerException(Object obj,
String errText,
String errState,
int errNum,
boolean bStack) {
super(errText, errState, errNum);
logException(obj, errText, bStack);
ActivityCorrelator.setCurrentActivityIdSentFlag();
}
/**
* Make a new SQLException
*
* @param obj
* @param errText
* the exception message
* @param errState
* the exception state
* @param streamError
* the StreamError object
* @param bStack
* true to generate the stack trace
*/
/* L0 */ public SQLServerException(Object obj,
String errText,
String errState,
StreamError streamError,
boolean bStack) {
super(errText, errState, streamError.getErrorNumber());
// Log SQL error with info from StreamError.
errText = "Msg " + streamError.getErrorNumber() + ", Level " + streamError.getErrorSeverity() + ", State " + streamError.getErrorState()
+ ", " + errText;
logException(obj, errText, bStack);
}
/**
* Build a new SQL Exception from an error detected by the driver.
*
* @param con
* the connection
* @param obj
* @param errText
* the excception message
* @param state
* he excpeption state
* @param bStack
* true to generate the stack trace
* @throws SQLServerException
*/
/* L0 */static void makeFromDriverError(SQLServerConnection con,
Object obj,
String errText,
String state,
boolean bStack) throws SQLServerException {
// The sql error code is 0 since the error was not returned from the database
// The state code is supplied by the calling code as XOPEN compliant.
// XOPEN is used as the primary code here since they are published free
// The SQL 99 states must be purchased from ANSII..
String stateCode = "";
// close the connection on a connection failure.
if (state != null) // Many are null since XOPEN errors do not cover internal driver errors
stateCode = state;
if (con == null || !con.xopenStates)
stateCode = mapFromXopen(state);
SQLServerException theException = new SQLServerException(obj, SQLServerException.checkAndAppendClientConnId(errText, con), stateCode, 0,
bStack);
if ((null != state && state.equals(EXCEPTION_XOPEN_CONNECTION_FAILURE)) && (null != con)) {
con.notifyPooledConnection(theException);
// note this close wont close the connection if there is an associated pooled connection.
con.close();
}
throw theException;
}
/**
* Build a new SQL Exception from a streamError detected by the driver.
*
* @param con
* the connection
* @param obj
* @param errText
* the excception message
* @param streamError
* @param bStack
* true to generate the stack trace
* @throws SQLServerException
*/
/* L0 */ static void makeFromDatabaseError(SQLServerConnection con,
Object obj,
String errText,
StreamError streamError,
boolean bStack) throws SQLServerException {
String state = generateStateCode(con, streamError.getErrorNumber(), streamError.getErrorState());
SQLServerException theException = new SQLServerException(obj, SQLServerException.checkAndAppendClientConnId(errText, con), state, streamError,
bStack);
theException.setDriverErrorCode(DRIVER_ERROR_FROM_DATABASE);
// Close the connection if we get a severity 20 or higher error class (nClass is severity of error).
if ((streamError.getErrorSeverity() >= 20) && (null != con)) {
con.notifyPooledConnection(theException);
con.close();
}
throw theException;
}
// This code is same as the conversion logic that previously existed in connecthelper.
static void ConvertConnectExceptionToSQLServerException(String hostName,
int portNumber,
SQLServerConnection conn,
Exception ex) throws SQLServerException {
Exception connectException = ex;
// Throw the exception if exception was caught by code above (stored in connectException).
if (connectException != null) {
MessageFormat formDetail = new MessageFormat(SQLServerException.getErrString("R_tcpOpenFailed"));
Object[] msgArgsDetail = {connectException.getMessage()};
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_tcpipConnectionFailed"));
Object[] msgArgs = {hostName, Integer.toString(portNumber), formDetail.format(msgArgsDetail)};
String s = form.format(msgArgs);
SQLServerException.makeFromDriverError(conn, conn, s, SQLServerException.EXCEPTION_XOPEN_CONNECTION_CANT_ESTABLISH, false);
}
}
/**
* Map XOPEN states.
*
* @param state
* the state
* @return the mapped state
*/
/* L0 */ static String mapFromXopen(String state) {
// Exceptions generated by the driver (not the database) are instanced with an XOPEN state code
// since the SQL99 states cant be located on the web (must pay) and the XOPEN states appear to
// be specific. Therefore if the driver is in SQL 99 mode we must map to SQL 99 state codes.
// SQL99 values based on previous SQLServerConnect code and some inet values..
if (state == null)
return null;
if (state.equals("07009"))
return "S1093";
// Connection (network) failure after connection made
if (state.equals(SQLServerException.EXCEPTION_XOPEN_CONNECTION_CANT_ESTABLISH))
return "08S01";
if (state.equals(SQLServerException.EXCEPTION_XOPEN_CONNECTION_FAILURE))
return "08S01";
// if (state.equals(SQLServerException.EXCEPTION_XOPEN_NETWORK_ERROR))
// return "S0022"; //Previous SQL99 state code for bad column name
return "";
}
/**
* Generate the JDBC state code based on the error number returned from the database
*
* @param con
* the connection
* @param errNum
* the error number
* @param databaseState
* the database state
* @return the state code
*/
/* L0 */ static String generateStateCode(SQLServerConnection con,
int errNum,
int databaseState) {
// Generate a SQL 99 or XOPEN state from a database generated error code
boolean xopenStates = (con != null && con.xopenStates);
if (xopenStates) {
switch (errNum) {
case 4060:
return "08001"; // Database name undefined at loging
case 18456:
return "08001"; // username password wrong at login
case 2714:
return "42S01"; // Table already exists
case 208:
return "42S02"; // Table not found
case 207:
return "42S22"; // Column not found
// case 156: return "42000"; //Invalid syntax
}
return "42000"; // Use XOPEN 'Syntax error or access violation'
// The error code came from the db but XOPEN does not have a specific case for it.
}
else {
switch (errNum) {
// case 18456: return "08001"; //username password wrong at login
case 8152:
return "22001"; // String data right truncation
case 515: // 2.2705
case 547:
return "23000"; // Integrity constraint violation
case 2601:
return "23000"; // Integrity constraint violation
case 2714:
return "S0001"; // table already exists
case 208:
return "S0002"; // table not found
case 1205:
return "40001"; // deadlock detected
case 2627:
return "23000"; // DPM 4.04. Primary key violation
}
return "S000" + databaseState;
}
}
/**
* Append ClientConnectionId to an error message if applicable
*
* @param errMsg
* - the orginal error message.
* @param conn
* - the SQLServerConnection object
* @return error string concated by ClientConnectionId(in string format) if applicable, otherwise, return original error string.
*/
static String checkAndAppendClientConnId(String errMsg,
SQLServerConnection conn) throws SQLServerException {
if (null != conn && conn.attachConnId()) {
UUID clientConnId = conn.getClientConIdInternal();
assert null != clientConnId;
StringBuilder sb = new StringBuilder(errMsg);
// This syntex of adding connection id is matched in a retry logic. If anything changes here, make
// necessary changes to enableSSL() function's exception handling mechanism.
sb.append(LOG_CLIENT_CONNECTION_ID_PREFIX);
sb.append(clientConnId.toString());
return sb.toString();
}
else
return errMsg;
}
}