Skip to content

Commit

Permalink
feat: populating sqlstate
Browse files Browse the repository at this point in the history
  • Loading branch information
ptiurin committed Jun 28, 2024
1 parent bb2e0d8 commit e4f8ae8
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 6 deletions.
13 changes: 10 additions & 3 deletions src/main/java/com/firebolt/jdbc/client/FireboltClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.firebolt.jdbc.connection.CacheListener;
import com.firebolt.jdbc.connection.FireboltConnection;
import com.firebolt.jdbc.exception.FireboltException;
import com.firebolt.jdbc.exception.SQLState;
import com.firebolt.jdbc.exception.ServerError;
import com.firebolt.jdbc.exception.ServerError.Error.Location;
import com.firebolt.jdbc.resultset.compress.LZ4InputStream;
Expand Down Expand Up @@ -41,6 +42,7 @@
import java.util.stream.Collectors;

import static java.lang.String.format;
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
import static java.util.Optional.ofNullable;
Expand Down Expand Up @@ -147,7 +149,8 @@ protected void validateResponse(String host, Response response, Boolean isCompre
int statusCode = response.code();
if (!isCallSuccessful(statusCode)) {
if (statusCode == HTTP_UNAVAILABLE) {
throw new FireboltException(format("Could not query Firebolt at %s. The engine is not running.", host), statusCode);
throw new FireboltException(format("Could not query Firebolt at %s. The engine is not running.", host),
statusCode, SQLState.CONNECTION_FAILURE);
}
String errorMessageFromServer = extractErrorMessage(response, isCompress);
ServerError serverError = parseServerError(errorMessageFromServer);
Expand All @@ -156,12 +159,16 @@ protected void validateResponse(String host, Response response, Boolean isCompre
String errorResponseMessage = format(
"Server failed to execute query with the following error:%n%s%ninternal error:%n%s",
processedErrorMessage, getInternalErrorWithHeadersText(response));
if (statusCode == HTTP_UNAUTHORIZED) {
if (statusCode == HTTP_UNAUTHORIZED || statusCode == HTTP_FORBIDDEN) {
// log the error message and throw an exception
log.log(Level.WARNING, "encountered unauthorized or forbidden error");
getConnection().removeExpiredTokens();
throw new FireboltException(format(
"Could not query Firebolt at %s. The operation is not authorized or the token is expired and has been cleared from the cache.%n%s",
host, errorResponseMessage), statusCode, processedErrorMessage);
host, errorResponseMessage), statusCode, processedErrorMessage,
SQLState.INVALID_AUTHORIZATION_SPECIFICATION);
}
log.log(Level.WARNING, "encountered some other error");
throw new FireboltException(errorResponseMessage, statusCode, processedErrorMessage);
}
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/firebolt/jdbc/exception/FireboltException.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,20 @@ public FireboltException(String message, Integer httpStatusCode, String errorMes
this.errorMessageFromServer = errorMessageFromServer;
}

public FireboltException(String message, Integer httpStatusCode, String errorMessageFromServer, SQLState state) {
super(message, state.getCode());
type = getExceptionType(httpStatusCode);
this.errorMessageFromServer = errorMessageFromServer;
}

public FireboltException(String message, Throwable cause) {
this(message, cause, ExceptionType.ERROR);
}

public FireboltException(String message, Throwable cause, SQLState state) {
this(message, cause, ExceptionType.ERROR, state);
}

public FireboltException(String message, ExceptionType type) {
super(message);
this.type = type;
Expand All @@ -59,6 +69,18 @@ public FireboltException(String message, Throwable cause, ExceptionType type) {
errorMessageFromServer = null;
}

public FireboltException(String message, Throwable cause, ExceptionType type, SQLState state) {
super(message, state.getCode(), cause);
this.type = type;
errorMessageFromServer = null;
}

public FireboltException(String message, int httpStatusCode, SQLState state) {
super(message, state.getCode());
type = getExceptionType(httpStatusCode);
errorMessageFromServer = null;
}

private static ExceptionType getExceptionType(Integer httpStatusCode) {
if (httpStatusCode == null) {
return ERROR;
Expand Down
125 changes: 125 additions & 0 deletions src/main/java/com/firebolt/jdbc/exception/SQLState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.firebolt.jdbc.exception;

public enum SQLState {
SUCCESS("00000"),
WARNING("01000"),
NO_DATA("02000"),
STATEMENT_STRING_DATA_RIGHT_TRUNCATION("01004"),
NULL_VALUE_NO_INDICATOR_PARAMETER("22002"),
CONNECTION_EXCEPTION("08001"),
CONNECTION_DOES_NOT_EXIST("08003"),
CONNECTION_FAILURE("08006"),
TRANSACTION_RESOLUTION_UNKNOWN("08007"),
SQL_SYNTAX_ERROR("42000"),
SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION("42601"),
DUPLICATE_KEY_VALUE("23505"),
DATA_EXCEPTION("22000"),
CHARACTER_NOT_IN_REPERTOIRE("22021"),
STRING_DATA_RIGHT_TRUNCATION("22001"),
NUMERIC_VALUE_OUT_OF_RANGE("22003"),
INVALID_DATETIME_FORMAT("22007"),
INVALID_TIME_ZONE_DISPLACEMENT_VALUE("22009"),
INVALID_ESCAPE_CHARACTER("22019"),
INVALID_PARAMETER_VALUE("22023"),
INVALID_CURSOR_STATE("24000"),
INVALID_TRANSACTION_STATE("25000"),
INVALID_AUTHORIZATION_SPECIFICATION("28000"),
INVALID_SQL_STATEMENT_NAME("26000"),
INVALID_CURSOR_NAME("34000"),
INVALID_SCHEMA_NAME("3F000"),
TRANSACTION_ROLLBACK("40000"),
SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION_IN_DIRECT_STATEMENT("2F000"),
INVALID_SQL_DESCRIPTOR_NAME("33000"),
INVALID_CURSOR_POSITION("34000"),
INVALID_CONDITION_NUMBER("35000"),
INVALID_TRANSACTION_TERMINATION("2D000"),
INVALID_CONNECTION_NAME("2E000"),
INVALID_AUTHORIZATION_NAME("28000"),
INVALID_COLUMN_NAME("42703"),
INVALID_COLUMN_DEFINITION("42P16"),
INVALID_CURSOR_DEFINITION("42P11"),
INVALID_DATABASE_DEFINITION("42P15"),
INVALID_FUNCTION_DEFINITION("42P13"),
INVALID_PREPARED_STATEMENT_DEFINITION("42P12"),
INVALID_SCHEMA_DEFINITION("42P14"),
INVALID_TABLE_DEFINITION("42P01"),
INVALID_OBJECT_DEFINITION("42P17"),
WITH_CHECK_OPTION_VIOLATION("44000"),
INSUFFICIENT_RESOURCES("53000"),
DISK_FULL("53100"),
OUT_OF_MEMORY("53200"),
TOO_MANY_CONNECTIONS("53300"),
CONFIGURATION_LIMIT_EXCEEDED("53400"),
PROGRAM_LIMIT_EXCEEDED("54000"),
OBJECT_NOT_IN_PREREQUISITE_STATE("55000"),
OBJECT_IN_USE("55006"),
CANT_CHANGE_RUNTIME_PARAM("55P02"),
LOCK_NOT_AVAILABLE("55P03"),
OPERATOR_INTERVENTION("57000"),
QUERY_CANCELED("57014"),
ADMIN_SHUTDOWN("57P01"),
CRASH_SHUTDOWN("57P02"),
CANNOT_CONNECT_NOW("57P03"),
DATABASE_DROPPED("57P04"),
SYSTEM_ERROR("58000"),
IO_ERROR("58030"),
UNDEFINED_FILE("58P01"),
DUPLICATE_FILE("58P02"),
SNAPSHOT_TOO_OLD("72000"),
CONFIGURATION_FILE_ERROR("F0000"),
LOCK_FILE_EXISTS("F0001"),
FDW_ERROR("HV000"),
FDW_COLUMN_NAME_NOT_FOUND("HV005"),
FDW_DYNAMIC_PARAMETER_VALUE_NEEDED("HV002"),
FDW_FUNCTION_SEQUENCE_ERROR("HV010"),
FDW_INCONSISTENT_DESCRIPTOR_INFORMATION("HV021"),
FDW_INVALID_ATTRIBUTE_VALUE("HV024"),
FDW_INVALID_COLUMN_NAME("HV007"),
FDW_INVALID_COLUMN_NUMBER("HV008"),
FDW_INVALID_DATA_TYPE("HV004"),
FDW_INVALID_DATA_TYPE_DESCRIPTORS("HV006"),
FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER("HV091"),
FDW_INVALID_HANDLE("HV00B"),
FDW_INVALID_OPTION_INDEX("HV00C"),
FDW_INVALID_OPTION_NAME("HV00D"),
FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH("HV090"),
FDW_INVALID_STRING_FORMAT("HV00A"),
FDW_INVALID_USE_OF_NULL_POINTER("HV009"),
FDW_TOO_MANY_HANDLES("HV014"),
FDW_OUT_OF_MEMORY("HV001"),
FDW_NO_SCHEMAS("HV00P"),
FDW_OPTION_NAME_NOT_FOUND("HV00J"),
FDW_REPLY_HANDLE("HV00K"),
FDW_SCHEMA_NOT_FOUND("HV00Q"),
FDW_TABLE_NOT_FOUND("HV00R"),
FDW_UNABLE_TO_CREATE_EXECUTION("HV00L"),
FDW_UNABLE_TO_CREATE_REPLY("HV00M"),
FDW_UNABLE_TO_ESTABLISH_CONNECTION("HV00N"),
PLPGSQL_ERROR("P0000"),
RAISE_EXCEPTION("P0001"),
NO_DATA_FOUND("P0002"),
TOO_MANY_ROWS("P0003"),
ASSERT_FAILURE("P0004"),
INTERNAL_ERROR("XX000"),
DATA_CORRUPTED("XX001"),
INDEX_CORRUPTED("XX002");

private final String code;

SQLState(String code) {
this.code = code;
}

public String getCode() {
return code;
}

public static SQLState fromCode(String sqlState) {
for (SQLState state : SQLState.values()) {
if (state.code.equals(sqlState)) {
return state;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.firebolt.jdbc.connection.FireboltConnectionTokens;
import com.firebolt.jdbc.connection.settings.FireboltProperties;
import com.firebolt.jdbc.exception.FireboltException;
import com.firebolt.jdbc.exception.SQLState;

import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import net.jodah.expiringmap.ExpiringMap;
Expand Down Expand Up @@ -51,7 +53,11 @@ public FireboltConnectionTokens getConnectionTokens(String host, FireboltPropert
} catch (FireboltException e) {
log.log(Level.SEVERE, "Failed to connect to Firebolt", e);
String msg = ofNullable(e.getErrorMessageFromServer()).map(m -> format(ERROR_MESSAGE_FROM_SERVER, m)).orElse(format(ERROR_MESSAGE, e.getMessage()));
throw new FireboltException(msg, e);
SQLState sqlState = SQLState.fromCode(e.getSQLState());
if (sqlState == null) {
throw new FireboltException(msg, e);
}
throw new FireboltException(msg, e, sqlState);
} catch (Exception e) {
log.log(Level.SEVERE, "Failed to connect to Firebolt", e);
throw new FireboltException(format(ERROR_MESSAGE, e.getMessage()), e);
Expand All @@ -69,7 +75,7 @@ private long getCachingDurationInSeconds(long expireInSeconds) {

/**
* Removes connection tokens from the cache.
*
*
* @param host host
* @param loginProperties the login properties linked to the tokens
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.firebolt.jdbc.connection.FireboltConnection;
import com.firebolt.jdbc.connection.FireboltConnectionTokens;
import com.firebolt.jdbc.exception.FireboltException;
import com.firebolt.jdbc.exception.SQLState;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -24,6 +26,7 @@
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_OK;
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -118,7 +121,21 @@ void shouldThrowExceptionWhenStatusCodeIsForbidden() throws Exception {
when(response.code()).thenReturn(HTTP_FORBIDDEN);
when(httpClient.newCall(any())).thenReturn(call);

assertThrows(FireboltException.class,
FireboltException ex = assertThrows(FireboltException.class,
() -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD, ENV));
assertEquals(SQLState.INVALID_AUTHORIZATION_SPECIFICATION.getCode(), ex.getSQLState());
}

@Test
void shouldThrowExceptionWhenStatusCodeIsUnavailable() throws Exception {
Response response = mock(Response.class);
Call call = mock(Call.class);
when(call.execute()).thenReturn(response);
when(response.code()).thenReturn(HTTP_UNAVAILABLE);
when(httpClient.newCall(any())).thenReturn(call);

FireboltException ex = assertThrows(FireboltException.class,
() -> fireboltAuthenticationClient.postConnectionTokens(HOST, USER, PASSWORD, ENV));
assertEquals(SQLState.CONNECTION_FAILURE.getCode(), ex.getSQLState());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.firebolt.jdbc.connection.FireboltConnectionTokens;
import com.firebolt.jdbc.connection.settings.FireboltProperties;
import com.firebolt.jdbc.exception.FireboltException;
import com.firebolt.jdbc.exception.SQLState;

@ExtendWith(MockitoExtension.class)
class FireboltAuthenticationServiceTest {
Expand Down Expand Up @@ -80,6 +81,21 @@ void shouldGetConnectionTokenAfterRemoving() throws SQLException, IOException {

@Test
void shouldThrowExceptionWithServerResponseWhenAResponseIsAvailable() throws SQLException, IOException {
String randomHost = UUID.randomUUID().toString();
Mockito.when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD, ENV))
.thenThrow(new FireboltException("An error happened during authentication", 403, "INVALID PASSWORD",
SQLState.INVALID_AUTHORIZATION_SPECIFICATION));

FireboltException ex = assertThrows(FireboltException.class,
() -> fireboltAuthenticationService.getConnectionTokens(randomHost, PROPERTIES));
assertEquals(
"Failed to connect to Firebolt with the error from the server: INVALID PASSWORD, see logs for more info.",
ex.getMessage());
assertEquals(SQLState.INVALID_AUTHORIZATION_SPECIFICATION.getCode(), ex.getSQLState());
}

@Test
void shouldThrowExceptionNoSQLStateWithServerResponseWhenAResponseIsAvailable() throws SQLException, IOException {
String randomHost = UUID.randomUUID().toString();
Mockito.when(fireboltAuthenticationClient.postConnectionTokens(randomHost, USER, PASSWORD, ENV))
.thenThrow(new FireboltException("An error happened during authentication", 403, "INVALID PASSWORD"));
Expand All @@ -89,6 +105,7 @@ void shouldThrowExceptionWithServerResponseWhenAResponseIsAvailable() throws SQL
assertEquals(
"Failed to connect to Firebolt with the error from the server: INVALID PASSWORD, see logs for more info.",
ex.getMessage());
assertEquals(null, ex.getSQLState());
}

@Test
Expand All @@ -100,6 +117,7 @@ void shouldThrowExceptionWithExceptionMessageWhenAResponseIsNotAvailable() throw
FireboltException ex = assertThrows(FireboltException.class,
() -> fireboltAuthenticationService.getConnectionTokens(randomHost, PROPERTIES));
assertEquals("Failed to connect to Firebolt with the error: NULL!, see logs for more info.", ex.getMessage());
assertEquals(null, ex.getSQLState());
}

}

0 comments on commit e4f8ae8

Please sign in to comment.