All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.SQLServerException Maven / Gradle / Ivy

Go to download

Microsoft JDBC Driver for SQL Server. The Azure Key Vault feature in Microsoft JDBC Driver for SQL Server depends on Azure SDK for JAVA and Azure Active Directory Library For Java.

There is a newer version: 12.7.0.jre11-preview
Show newest version
/*
 * 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;

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy