Subject: [pgjdbc/pgjdbc] issue #3940: BOOLEAN columns return Integer instead of Boolean in GraalVM native image

From: "fishloa (@fishloa)" <[email-masked]>
Date: Mon, 16 Feb 2026 01:16:33 +0000

## Description

When running in a GraalVM native image, `ResultSet.getObject()` for a PostgreSQL `BOOLEAN` column returns `java.lang.Integer` instead of `java.lang.Boolean`. This works correctly on the JVM.

## Environment

- **pgjdbc**: 42.7.9 (managed by Spring Boot 4.0.2)
- **GraalVM**: 25.0.2 (JDK 25)
- **PostgreSQL**: 18
- **Spring Data JDBC**: 4.0.2 (reads columns via reflection)

## Schema

```sql
CREATE TABLE user_emails (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL,
    email VARCHAR NOT NULL UNIQUE,
    is_primary BOOLEAN NOT NULL DEFAULT false,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
```

## Entity

```java
@Table("user_emails")
public class UserEmail {
    @Id private UUID id;
    @Column("is_primary") private boolean primary;
    // ...
}
```

## Error (native image only, works fine on JVM)

```
java.lang.IllegalArgumentException: Can not set boolean field
    place.icomb.horti.entity.UserEmail.primary to java.lang.Integer
```

## Root cause

pgjdbc reports PostgreSQL `BOOLEAN` as JDBC type `Types.BIT` rather than `Types.BOOLEAN`. On the JVM, the internal type-conversion codepaths in `ResultSet.getObject()` still return `java.lang.Boolean` for `BIT(1)`. In a GraalVM native image, those conversion paths appear to be unreachable (or the reflection metadata is missing), so the raw `Integer` (0/1) is returned instead.

## Workaround

Register a Spring Data JDBC reading converter:

```java
@ReadingConverter
static class IntegerToBooleanConverter implements Converter<Integer, Boolean> {
    @Override
    public Boolean convert(Integer source) {
        return source != null && source != 0;
    }
}
```

## Expected behavior

`ResultSet.getObject()` for a `BOOLEAN` column should return `java.lang.Boolean` in both JVM and native image contexts. Ideally pgjdbc would also report `Types.BOOLEAN` instead of `Types.BIT` for PostgreSQL `boolean` columns.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Mon, 16 Feb 2026 11:04:08 +0000

Well that's interesting. 
I wonder what the implications of changing it to boolean would be?
@vlsi  ?

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Mon, 16 Feb 2026 11:17:55 +0000

If GraalVM misbehaves, I would double check the reason for the failure before changing anything in pgjdbc.

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Mon, 16 Feb 2026 11:22:01 +0000

apparently because ```in a GraalVM native image, those conversion paths appear to be unreachable (or the reflection metadata is missing), so the raw Integer (0/1) is returned instead.``` but I'm still curious why we return a bit.

---

From: "vlsi (@vlsi)" <[email-masked]>
Date: Mon, 16 Feb 2026 11:34:53 +0000

AFAIK GraalVM should **keep** the semantics of the program, so I don't think it should make code paths unreachable arbitrarily