Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement | Added new APIs to retrieve SQL Server error information received with SQLServerException #905

Merged
merged 7 commits into from
Dec 17, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -4587,10 +4587,10 @@ void writeTVPRows(TVP value) throws SQLServerException {
int tokenType = tdsReader.peekTokenType();

if (TDS.TDS_ERR == tokenType) {
StreamError databaseError = new StreamError();
SQLServerError databaseError = new SQLServerError();
databaseError.setFromTDS(tdsReader);

SQLServerException.makeFromDatabaseError(con, null, databaseError.getMessage(), databaseError,
SQLServerException.makeFromDatabaseError(con, null, databaseError.getErrorMessage(), databaseError,
false);
}

Expand Down
110 changes: 110 additions & 0 deletions src/main/java/com/microsoft/sqlserver/jdbc/SQLServerError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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.io.Serializable;

/**
* SQLServerError represents a TDS error or message event.
*/
public final class SQLServerError extends StreamPacket implements Serializable {
/**
* Always update serialVersionUID when prompted
*/
private static final long serialVersionUID = -7304033613218700719L;
private String errorMessage = "";
private int errorNumber;
private int errorState;
private int errorSeverity;
private String serverName;
private String procName;
private long lineNumber;

/**
* Returns error message as received from SQL Server
*
* @return Error Message
*/
public String getErrorMessage() {
return errorMessage;
}

/**
* Returns error number as received from SQL Server
*
* @return Error Number
*/
public int getErrorNumber() {
return errorNumber;
}

/**
* Returns error state as received from SQL Server
*
* @return Error State
*/
public int getErrorState() {
return errorState;
}

/**
* Returns Severity of error (as int value) as received from SQL Server
*
* @return Error Severity
*/
public int getErrorSeverity() {
return errorSeverity;
}

/**
* Returns name of the server where exception occurs as received from SQL Server
*
* @return Server Name
*/
public String getServerName() {
return serverName;
}

/**
* Returns name of the stored procedure where exception occurs as received from SQL Server
*
* @return Procedure Name
*/
public String getProcedureName() {
return procName;
}

/**
* Returns line number where the error occurred in Stored Procedure returned by <code>getProcedureName()</code> as
* received from SQL Server
*
* @return Line Number
*/
public long getLineNumber() {
return lineNumber;
}

SQLServerError() {
super(TDS.TDS_ERR);
}

void setFromTDS(TDSReader tdsReader) throws SQLServerException {
if (TDS.TDS_ERR != tdsReader.readUnsignedByte())
assert false;
setContentsFromTDS(tdsReader);
}

void setContentsFromTDS(TDSReader tdsReader) throws SQLServerException {
tdsReader.readUnsignedShort(); // token length (ignored)
errorNumber = tdsReader.readInt();
errorState = tdsReader.readUnsignedByte();
errorSeverity = tdsReader.readUnsignedByte(); // matches master.dbo.sysmessages
errorMessage = tdsReader.readUnicodeString(tdsReader.readUnsignedShort());
serverName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte());
procName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte());
lineNumber = tdsReader.readUnsignedInt();
}
}
62 changes: 37 additions & 25 deletions src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import java.sql.SQLFeatureNotSupportedException;
import java.text.MessageFormat;
import java.util.Map;
cheenamalhotra marked this conversation as resolved.
Show resolved Hide resolved
import java.util.UUID;
import java.util.logging.Level;

Expand Down Expand Up @@ -84,6 +85,7 @@ public final class SQLServerException extends java.sql.SQLException {
static final int DATA_CLASSIFICATION_INVALID_INFORMATION_TYPE_INDEX = 13;

private int driverErrorCode = DRIVER_ERROR_NONE;
private SQLServerError sqlServerError;

final int getDriverErrorCode() {
return driverErrorCode;
Expand All @@ -97,9 +99,9 @@ final void setDriverErrorCode(int value) {
* Logs an exception to the driver log file.
*
* @param o
* the io buffer that generated the exception
* the IO buffer that generated the exception
* @param errText
* the excception message
* the exception message
* @param bStack
* true to generate the stack trace
*/
Expand Down Expand Up @@ -181,17 +183,17 @@ static String getErrString(String errCode) {
* the exception message
* @param errState
* the exception state
* @param streamError
* the StreamError object
* @param sqlServerError
* the SQLServerError object
* @param bStack
* true to generate the stack trace
*/
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;
SQLServerException(Object obj, String errText, String errState, SQLServerError sqlServerError, boolean bStack) {
super(errText, errState, sqlServerError.getErrorNumber());
this.sqlServerError = sqlServerError;
// Log SQL error with info from SQLServerError.
errText = "Msg " + sqlServerError.getErrorNumber() + ", Level " + sqlServerError.getErrorSeverity() + ", State "
+ sqlServerError.getErrorState() + ", " + errText;
logException(obj, errText, bStack);
}

Expand All @@ -202,9 +204,9 @@ static String getErrString(String errCode) {
* the connection
* @param obj
* @param errText
* the excception message
* the exception message
* @param state
* he excpeption state
* the exception state
* @param bStack
* true to generate the stack trace
* @throws SQLServerException
Expand Down Expand Up @@ -235,28 +237,28 @@ static void makeFromDriverError(SQLServerConnection con, Object obj, String errT
}

/**
* Builds a new SQL Exception from a streamError detected by the driver.
* Builds a new SQL Exception from a SQLServerError detected by the driver.
*
* @param con
* the connection
* @param obj
* @param errText
* the excception message
* @param streamError
* the exception message
* @param sqlServerError
* @param bStack
* true to generate the stack trace
* @throws SQLServerException
*/
static void makeFromDatabaseError(SQLServerConnection con, Object obj, String errText, StreamError streamError,
boolean bStack) throws SQLServerException {
String state = generateStateCode(con, streamError.getErrorNumber(), streamError.getErrorState());
static void makeFromDatabaseError(SQLServerConnection con, Object obj, String errText,
SQLServerError sqlServerError, boolean bStack) throws SQLServerException {
String state = generateStateCode(con, sqlServerError.getErrorNumber(), sqlServerError.getErrorState());

SQLServerException theException = new SQLServerException(obj,
SQLServerException.checkAndAppendClientConnId(errText, con), state, streamError, bStack);
SQLServerException.checkAndAppendClientConnId(errText, con), state, sqlServerError, 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)) {
if ((sqlServerError.getErrorSeverity() >= 20) && (null != con)) {
con.notifyPooledConnection(theException);
con.close();
}
Expand Down Expand Up @@ -366,18 +368,18 @@ static String generateStateCode(SQLServerConnection con, int errNum, int databas
* Appends ClientConnectionId to an error message if applicable.
*
* @param errMsg
* - the original error message.
* the original error message
* @param conn
* - the SQLServerConnection object
* @return error string concated by ClientConnectionId(in string format) if applicable, otherwise, return original
* error string.
* the SQLServerConnection object
* @return error string concatenated 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
// This syntax 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());
Expand All @@ -395,4 +397,14 @@ static void throwNotSupportedException(SQLServerConnection con, Object obj) thro
static void throwFeatureNotSupportedException() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported"));
}

/**
* Returns SQLServerError object containing detailed info about exception as received from SQL Server. This API
* returns null if no server error has occurred.
*
* @return SQLServerError
*/
public SQLServerError getSQLServerError() {
return sqlServerError;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5398,7 +5398,7 @@ final RowType nextRow() throws SQLServerException {
if (fetchBufferCurrentRowType.equals(RowType.UNKNOWN)
&& null != fetchBufferTokenHandler.getDatabaseError()) {
SQLServerException.makeFromDatabaseError(stmt.connection, null,
fetchBufferTokenHandler.getDatabaseError().getMessage(),
fetchBufferTokenHandler.getDatabaseError().getErrorMessage(),
fetchBufferTokenHandler.getDatabaseError(), false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException {
executedSqlDirectly = true;

SQLWarning warning = new SQLWarning(
infoToken.msg.getMessage(), SQLServerException.generateStateCode(connection,
infoToken.msg.getErrorMessage(), SQLServerException.generateStateCode(connection,
infoToken.msg.getErrorNumber(), infoToken.msg.getErrorState()),
infoToken.msg.getErrorNumber());

Expand Down Expand Up @@ -1611,7 +1611,7 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException {

// Check for errors first.
if (null != nextResult.getDatabaseError()) {
SQLServerException.makeFromDatabaseError(connection, null, nextResult.getDatabaseError().getMessage(),
SQLServerException.makeFromDatabaseError(connection, null, nextResult.getDatabaseError().getErrorMessage(),
nextResult.getDatabaseError(), false);
}

Expand Down
63 changes: 0 additions & 63 deletions src/main/java/com/microsoft/sqlserver/jdbc/StreamError.java

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/com/microsoft/sqlserver/jdbc/StreamInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
package com.microsoft.sqlserver.jdbc;

final class StreamInfo extends StreamPacket {
final StreamError msg = new StreamError();
final SQLServerError msg = new SQLServerError();

StreamInfo() {
super(TDS.TDS_MSG);
Expand Down
Loading