Skip to main content

Command Palette

Search for a command to run...

Postgres PostgresqlBadGrammarException Fix

Published
2 min read

Caused by: io.r2dbc.postgresql.ExceptionFactory$PostgresqlBadGrammarException: column "status" is of type user_status_enum but expression is of type character varying

I Encountered This Error When developing my E_COMMERCE BACKEND

It typically means the that r2dbc is trying to insert a string type to a field the if defined as Enum in your postgres database.

To Fix the issue Follow these steps

Configure the ConnectionFactory to convert string values to the Enum type

package org.abraham.user_service.config;



import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
import io.r2dbc.postgresql.codec.EnumCodec;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import lombok.AllArgsConstructor;
import org.abraham.user_service.dto.AddressType;
import org.abraham.user_service.dto.UserRoles;
import org.abraham.user_service.dto.UserStatus;
import org.reactivestreams.Publisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.r2dbc.spi.ConnectionFactoryOptions.*;



@Configuration
@AllArgsConstructor
public class R2dbcConfig extends AbstractR2dbcConfiguration {

    private R2dbcProperties r2dbcProperties;

    @Override
    @Bean
    public ConnectionFactory connectionFactory() {
        Map<String, String> options = new HashMap<>();
        options.put("lock_timeout", "10s");

        return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
                .host("localhost")
                .port(5434)  // optional, defaults to 5432
                .username(r2dbcProperties.getUsername())
                .password(r2dbcProperties.getPassword())
                .database("user_service_db")  // optional
                .options(options) // optional
                .codecRegistrar(EnumCodec.builder().withEnum("user_status_enum", UserStatus.class).build())
                .codecRegistrar(EnumCodec.builder().withEnum("address_type_enum", AddressType.class).build())
                .codecRegistrar(EnumCodec.builder().withEnum("user_roles", UserRoles.class).build())
                .build());
    }

    @Override
    protected List<Object> getCustomConverters() {
        return List.of(
                new EnumConverters.UserStatusToStringConverter(),
                new EnumConverters.StringToUserStatusConverter()
        );
    }

}

Upto this point Everything should woul perfectly. From this explanation

Postgres Enum Types

Applications may make use of Postgres enumerated types by using EnumCodec to map custom types to Java enum types. EnumCodec requires the Postgres OID and the Java to map enum values to the Postgres protocol and to materialize Enum instances from Postgres results. You can configure a CodecRegistrar through EnumCodec.builder() for one or more enumeration type mappings. Make sure to use different Java enum types otherwise the driver is not able to distinguish between Postgres OIDs.

Example:

SQL:

CREATE TYPE my_enum AS ENUM ('FIRST', 'SECOND');

Java Model:

enum MyEnumType {
  FIRST, SECOND;
}

Codec Registration:

PostgresqlConnectionConfiguration.builder()
        .codecRegistrar(EnumCodec.builder().withEnum("my_enum",MyEnumType.class).build());

When available, the driver registers also an array variant of the codec.

here is the github link for more explanation: https://github.com/pgjdbc/r2dbc-postgresql