diff --git a/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactory.java b/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactory.java index 44e4ed1f..cccd7ba6 100644 --- a/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactory.java +++ b/src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactory.java @@ -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; @@ -262,10 +264,20 @@ private Mono 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; } } diff --git a/src/main/java/io/r2dbc/postgresql/api/ErrorDetails.java b/src/main/java/io/r2dbc/postgresql/api/ErrorDetails.java index ad7967ee..27b47631 100644 --- a/src/main/java/io/r2dbc/postgresql/api/ErrorDetails.java +++ b/src/main/java/io/r2dbc/postgresql/api/ErrorDetails.java @@ -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; @@ -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 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) { diff --git a/src/main/java/io/r2dbc/postgresql/client/AbstractPostgresSSLHandlerAdapter.java b/src/main/java/io/r2dbc/postgresql/client/AbstractPostgresSSLHandlerAdapter.java index 76da3013..6edc7341 100644 --- a/src/main/java/io/r2dbc/postgresql/client/AbstractPostgresSSLHandlerAdapter.java +++ b/src/main/java/io/r2dbc/postgresql/client/AbstractPostgresSSLHandlerAdapter.java @@ -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; @@ -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; } } diff --git a/src/main/java/io/r2dbc/postgresql/client/ReactorNettyClient.java b/src/main/java/io/r2dbc/postgresql/client/ReactorNettyClient.java index 7087d4e6..061835a8 100644 --- a/src/main/java/io/r2dbc/postgresql/client/ReactorNettyClient.java +++ b/src/main/java/io/r2dbc/postgresql/client/ReactorNettyClient.java @@ -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; @@ -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(); @@ -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; } }