com.microsoft.sqlserver.jdbc.SQLServerException Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mssql-jdbc Show documentation
Show all versions of mssql-jdbc Show documentation
Microsoft JDBC Driver for SQL Server.
/*
* 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 final int USER_ACCOUNT_LOCKED = 18486;
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 (StackTraceElement aSt : st) sb.append(aSt.toString());
Throwable t = this.getCause();
if (t != null) {
sb.append("\n caused by " + t + "\n");
StackTraceElement tst[] = t.getStackTrace();
for (StackTraceElement aTst : tst) sb.append(aTst.toString());
}
exLogger.fine(sb.toString());
}
}
}
static String getErrString(String errCode) {
return SQLServerResource.getResource(errCode);
}
/**
* Make a new SQLException
*
* @param errText
* the exception message
* @param sqlState
* the statement
* @param driverError
* the driver error object
* @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
* the object
* @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;
}
}