Visitar URL original
ResultSetMetaData.getColumnClassName is broken for jsonb · Issue #3926 · pgjdbc/pgjdbc · GitHub
Skip to content

ResultSetMetaData.getColumnClassName is broken for jsonb #3926

Closed

Description

@aditsu

Describe the issue
ResultSetMetaData.getColumnClassName does not return the correct class name for columns of type jsonb.
Specifically, the javadoc says it should return "the fully-qualified name of the class in the Java programming language that would be used by the method ResultSet.getObject to retrieve the value in the specified column"
The method returns "java.lang.String" in recent driver versions, whereas ResultSet.getObject returns instances of org.postgresql.util.PGobject, and that class does not extend java.lang.String.
This is not a problem for the "json" pg type, which is handled perfectly. Only "jsonb" is broken.

Driver Version?
42.7.9 (but I have tested older versions too)

Java Version?
17.0.13

OS Version?
Linux

PostgreSQL Version?
17.7

To Reproduce
Here is my table:

CREATE TABLE public.jsontest (
	id int4 GENERATED ALWAYS AS IDENTITY NOT NULL,
	fld_json json NOT NULL,
	fld_jsonb jsonb NOT NULL,
	CONSTRAINT jsontest_pk PRIMARY KEY (id)
);

I inserted one row with some dummy data.

kotlin test code:

import java.sql.DriverManager
import java.util.Properties

fun main() {
	val url = "jdbc:postgresql://localhost/test"
	val props = Properties().apply {
		setProperty("user", "postgres")
		setProperty("password", "")
	}

	DriverManager.getConnection(url, props).use { conn ->
		val sql = "SELECT id, fld_json, fld_jsonb FROM jsontest"

		val version = conn.metaData.driverVersion
		println("driver version: $version")

		conn.createStatement().use { statement ->
			statement.executeQuery(sql).use { rs ->
				val metaData = rs.metaData

				while (rs.next()) {
					for (i in 2..metaData.columnCount) {
						val name = metaData.getColumnName(i)
						val type = metaData.getColumnTypeName(i)
						val cl = metaData.getColumnClassName(i)
						val value = rs.getObject(i)
						println("$name - type $type, metadata class $cl, value ${value.javaClass}")
						println("instance test: " + Class.forName(cl).isInstance(value))
					}
				}
			}
		}
	}
}

Expected behaviour
I expected the metadata class match the class of the returned value, or at least to be a supertype.
More specifically, I expected getColumnClassName to return "org.postgresql.util.PGobject" like it does for json.
It actually returns "java.lang.String" in recent driver versions, which would still be ok (though very weird and inconsistent with the json type) IF the value was actually returned as a String from getObject, but it is not - the actual value is a PGobject.

Actual results:

I tested this with several different driver versions, and got mainly 2 different outputs:

driver version: 42.2.0
fld_json - type json, metadata class org.postgresql.util.PGobject, value class org.postgresql.util.PGobject
instance test: true
fld_jsonb - type jsonb, metadata class java.lang.Object, value class org.postgresql.util.PGobject
instance test: true

Old versions:
type json - perfect match: PGobject to PGobject
type jsonb - inconsistent but not broken: Object to PGobject

driver version: 42.7.9
fld_json - type json, metadata class org.postgresql.util.PGobject, value class org.postgresql.util.PGobject
instance test: true
fld_jsonb - type jsonb, metadata class java.lang.String, value class org.postgresql.util.PGobject
instance test: false

New versions:
type json - perfect match: PGobject to PGobject
type jsonb - actually broken: String to PGobject

Proposed solution (preferred): getColumnClassName should return "org.postgresql.util.PGobject" like it does for json.
Alternative solution (inferior): getColumnClassName should return "java.lang.String" for both json and jsonb, but at the same time getObject should be modified to return a String value.

See also: bug #2153 - I don't really understand what happened there, but it seems to me that it was broken on purpose?! Very bizarre.

Activity

  1. dkozlov-de commented on Mar 10, 2026

    @dkozlov-de

    I vote for the fix to this problem. This is clearly a bug. Metadata must return correct class data, otherwise, it is absolutely useless. This issue breaks any libraries that provide dynamic data type resolution based on the column metadata. At some point jsonb started being treated as a String. Well, that was a developers' call (even though it is still not clear to me what the reason for that change was). But if that (changing PGobject to String) was done in metadata, retrieving the column data would have to be changed accordingly to return a String instead of PGobject.

  2. davecramer commented on Mar 10, 2026

    @davecramer
    Member

    Well the reason is that it actually does return a string.
    Can you help me out, what would your code do if it found jsonb ? How would you use this data ?

  3. dkozlov-de commented on Mar 10, 2026

    @dkozlov-de

    I would like to clarify my point first. I do not have any problems with treating JSONB column data as a String. It is your call and I respect it. The problem is with resultset still returning PGobject.
    We use org.apache.commons.beanutils.RowSetDynaClass and related classes to dynamically retrieve typed data from a resultset. This library uses column metadata to figure out what the type of a column is. Then when we ask for the column data, it tries to cast the data in resultset to the type provided by the metadata. At this point it fails because metadata says it is a String, but what the resultset returns is still PGobject.
    My point is that if you change the type reported by metadata to String, you should be consistent and change the real type the resultset returns to String as well. Otherwise, the metadata column class information is wrong/misleading and prevents from proper data evaluation.

  4. davecramer commented on Mar 10, 2026

    @davecramer
    Member

    ah, thanks for the clarification.

  5. aditsu commented on Mar 10, 2026

    @aditsu
    Author

    @dkozlov-de I fully agree, I would only add that I think json and jsonb should be handled in a similar fashion by the driver. Either both PGobject (my preference) or both String (also acceptable).

  6. davecramer commented on Mar 11, 2026

    @davecramer
    Member

    Interestingly, this has been this way for 10 years see #640

    given the comments on PR#640 I'm inclined to return the jsonb type as PGObject

  7. dkozlov-de commented on Mar 11, 2026

    @dkozlov-de

    @davecramer - thank you very much for the quick response. Yes, this could be a very old issue. We were still using pretty old version of the driver and decided to upgrade it. That's when we got this problem exposed.

  8. aditsu commented on Mar 11, 2026

    @aditsu
    Author

    I've used json for a long time and was familiar with how the driver returns PGobject, but only started using jsonb very recently and was shocked to find the discrepancy.

  9. davecramer commented on Mar 12, 2026

    @davecramer
    Member

    @vlsi what do you think of committing this?
    I'm thinking we need to do a Major version bump as this changes the current behaviour

  10. added 2 commits that reference this issue on May 12, 2026
    99ccb3d
    716942b
  11. vlsi commented on Jun 20, 2026

    @vlsi
    Member

    Fixed by #3956 and released in 42.7.11. ResultSetMetaData.getColumnClassName now returns org.postgresql.util.PGobject for both json and jsonb, matching what ResultSet.getObject returns. Regression tests testJsonbResultType / testJsonResultType cover it.

    Thanks for the detailed report. Closing as resolved — please reopen if you still see the mismatch on 42.7.11 or later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions