Skip to content

Commit

Permalink
Associate client exceptions with SQL state and ErrorDetails
Browse files Browse the repository at this point in the history
We now provide error details for client-side exceptions for easier handling of failures.

[resolves #538]

Signed-off-by: Mark Paluch <mpaluch@vmware.com>
  • Loading branch information
mp911de committed Aug 8, 2022
1 parent e224fc5 commit 67806b6
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 15 deletions.
18 changes: 15 additions & 3 deletions src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactory.java
Expand Up @@ -18,6 +18,8 @@

import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.unix.DomainSocketAddress;
import io.r2dbc.postgresql.api.ErrorDetails;
import io.r2dbc.postgresql.api.PostgresqlException;
import io.r2dbc.postgresql.authentication.AuthenticationHandler;
import io.r2dbc.postgresql.authentication.PasswordAuthenticationHandler;
import io.r2dbc.postgresql.authentication.SASLAuthenticationHandler;
Expand Down Expand Up @@ -262,10 +264,20 @@ private Mono<IsolationLevel> getIsolationLevel(io.r2dbc.postgresql.api.Postgresq
})).defaultIfEmpty(IsolationLevel.READ_COMMITTED).last();
}

static class PostgresConnectionException extends R2dbcNonTransientResourceException {
static class PostgresConnectionException extends R2dbcNonTransientResourceException implements PostgresqlException {

public PostgresConnectionException(String msg, @Nullable Throwable cause) {
super(msg, cause);
private static final String CONNECTION_DOES_NOT_EXIST = "08003";

private final ErrorDetails errorDetails;

public PostgresConnectionException(String reason, @Nullable Throwable cause) {
super(reason, CONNECTION_DOES_NOT_EXIST, 0, null, cause);
this.errorDetails = ErrorDetails.fromCodeAndMessage(CONNECTION_DOES_NOT_EXIST, reason);
}

@Override
public ErrorDetails getErrorDetails() {
return this.errorDetails;
}

}
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/io/r2dbc/postgresql/api/ErrorDetails.java
Expand Up @@ -23,6 +23,7 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -130,6 +131,22 @@ public static ErrorDetails fromMessage(String message) {
return new ErrorDetails(Collections.singletonMap(MESSAGE, message));
}

/**
* Create a new {@link ErrorDetails}
*
* @param code the error code
* @param message the error message
* @return the {@link ErrorDetails} object
*/
public static ErrorDetails fromCodeAndMessage(String code, String message) {

Map<FieldType, String> details = new LinkedHashMap<>(2);
details.put(CODE, code);
details.put(MESSAGE, message);

return new ErrorDetails(details);
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
Expand Up @@ -22,6 +22,8 @@
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.r2dbc.postgresql.api.ErrorDetails;
import io.r2dbc.postgresql.api.PostgresqlException;
import io.r2dbc.spi.R2dbcPermissionDeniedException;
import reactor.core.publisher.Mono;

Expand Down Expand Up @@ -86,10 +88,18 @@ SslHandler getSslHandler() {
/**
* Postgres-specific {@link R2dbcPermissionDeniedException}.
*/
static final class PostgresqlSslException extends R2dbcPermissionDeniedException {
static final class PostgresqlSslException extends R2dbcPermissionDeniedException implements PostgresqlException {

PostgresqlSslException(String msg) {
super(msg);
private final ErrorDetails errorDetails;

PostgresqlSslException(String reason) {
super(reason, ReactorNettyClient.CONNECTION_FAILURE, 0, (String) null);
this.errorDetails = ErrorDetails.fromCodeAndMessage(ReactorNettyClient.CONNECTION_FAILURE, reason);
}

@Override
public ErrorDetails getErrorDetails() {
return this.errorDetails;
}

}
Expand Down
54 changes: 45 additions & 9 deletions src/main/java/io/r2dbc/postgresql/client/ReactorNettyClient.java
Expand Up @@ -28,6 +28,8 @@
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.r2dbc.postgresql.api.ErrorDetails;
import io.r2dbc.postgresql.api.PostgresqlException;
import io.r2dbc.postgresql.message.backend.BackendKeyData;
import io.r2dbc.postgresql.message.backend.BackendMessage;
import io.r2dbc.postgresql.message.backend.BackendMessageDecoder;
Expand Down Expand Up @@ -88,6 +90,8 @@
*/
public final class ReactorNettyClient implements Client {

static final String CONNECTION_FAILURE = "08006";

private static final Logger logger = Loggers.getLogger(ReactorNettyClient.class);

private static final boolean DEBUG_ENABLED = logger.isDebugEnabled();
Expand Down Expand Up @@ -535,38 +539,70 @@ public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {

}

static class PostgresConnectionClosedException extends R2dbcNonTransientResourceException {
static class PostgresConnectionClosedException extends R2dbcNonTransientResourceException implements PostgresqlException {

private final ErrorDetails errorDetails;

public PostgresConnectionClosedException(String reason) {
super(reason);
super(reason, CONNECTION_FAILURE, 0, (String) null);
this.errorDetails = ErrorDetails.fromCodeAndMessage(CONNECTION_FAILURE, reason);
}

public PostgresConnectionClosedException(String reason, @Nullable Throwable cause) {
super(reason, cause);
super(reason, CONNECTION_FAILURE, 0, null, cause);
this.errorDetails = ErrorDetails.fromCodeAndMessage(CONNECTION_FAILURE, reason);
}

@Override
public ErrorDetails getErrorDetails() {
return this.errorDetails;
}

}

static class PostgresConnectionException extends R2dbcNonTransientResourceException {
static class PostgresConnectionException extends R2dbcNonTransientResourceException implements PostgresqlException {

private final static ErrorDetails ERROR_DETAILS = ErrorDetails.fromCodeAndMessage(CONNECTION_FAILURE, "An I/O error occurred while sending to the backend or receiving from the backend");

public PostgresConnectionException(Throwable cause) {
super(cause);
super(ERROR_DETAILS.getMessage(), ERROR_DETAILS.getCode(), 0, null, cause);
}

@Override
public ErrorDetails getErrorDetails() {
return ERROR_DETAILS;
}

}

static class RequestQueueException extends R2dbcTransientResourceException {
static class RequestQueueException extends R2dbcTransientResourceException implements PostgresqlException {

private final ErrorDetails errorDetails;

public RequestQueueException(String message) {
super(message);
super(message, CONNECTION_FAILURE, 0, (String) null);
this.errorDetails = ErrorDetails.fromCodeAndMessage(CONNECTION_FAILURE, message);
}

@Override
public ErrorDetails getErrorDetails() {
return this.errorDetails;
}

}

static class ResponseQueueException extends R2dbcNonTransientResourceException {
static class ResponseQueueException extends R2dbcNonTransientResourceException implements PostgresqlException {

private final ErrorDetails errorDetails;

public ResponseQueueException(String message) {
super(message);
super(message, CONNECTION_FAILURE, 0, (String) null);
this.errorDetails = ErrorDetails.fromCodeAndMessage(CONNECTION_FAILURE, message);
}

@Override
public ErrorDetails getErrorDetails() {
return this.errorDetails;
}

}
Expand Down

0 comments on commit 67806b6

Please sign in to comment.