Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrading postgres JDBC driver to 42.3.0 breaks big decimal read after 5 reads (only with Hikari) #2326

Closed
NikolayMetchev opened this issue Oct 27, 2021 · 25 comments · Fixed by #2327

Comments

@NikolayMetchev
Copy link

Describe the issue
f you have a numeric column in a postgres table with a value of 20000 and you read it 5 times on the 5th read it will read it as 2 instead of 20000.

This only breaks if you use the postgres jdbc driver version 42.3.0. If you revert to an older JDBC driver for postgres the bug doesn't manifest.

If you use pure JDBC and avoid hikari altogether the bug also doesn't reproduce.

Changing the version of postgres server also seems to have no affect on this bug.

Driver Version?
42.3.0

Java Version?
11

OS Version?
MacOS/Linuix

PostgreSQL Version?
10.15-13.4

To Reproduce
Please find attached a project with a unit test

Expected behaviour
postgresBug.zip
Every read should result in 20000

Logs
N/A

I have reported this to Hikari as well: brettwooldridge/HikariCP#1865

@davecramer
Copy link
Member

Ya, we switch to binary after 5 uses of the statement. Makes sense... Thanks for the report

@NikolayMetchev
Copy link
Author

Ya, we switch to binary after 5 uses of the statement. Makes sense... Thanks for the report

Could this be the culprit: a417307

@davecramer
Copy link
Member

Quick way to find out... I'll be able to give you something to test in a minute

@davecramer
Copy link
Member

hmmm looks like that class was completely rewritten. It might take a bit longer to sort.
@bokken ?

@NikolayMetchev
Copy link
Author

You should be able to reproduce it by running the attached test. Please let me know if it is unclear.

@davecramer
Copy link
Member

Thx. running embedded test messes with my system, but I will figure it out

@davecramer
Copy link
Member

Why are you using prepareCall ??? (Just curious. I've never seen that before)

@NikolayMetchev
Copy link
Author

Mistake on my part. Changing to prepareStatement results in the same failure.

@bokken
Copy link
Member

bokken commented Oct 27, 2021

Is this only an issue when you explicitly set scale (to 20)?

@NikolayMetchev
Copy link
Author

No, I am setting the scale so that the comparison of BigDecimal doesn't fail in the test.

@NikolayMetchev
Copy link
Author

Although actually if I change the type of the column to be numeric without explicit scale the test does pass...
So setting the scale explicitly in the DB seems to be the culprit.

@davecramer
Copy link
Member

davecramer commented Oct 27, 2021 via email

@NikolayMetchev
Copy link
Author

NikolayMetchev commented Oct 27, 2021

On Wed, Oct 27, 2021, 12:07 PM Nikolay Metchev @.***> wrote: Although actually if I change the type of the column to be numeric without explicit scale the test does pass... So setting the scale explicitly in the DB seems to be the culprit.
The pure jdbc test does not transfer in binary

— You are receiving this because you commented. Reply to this email directly, view it on GitHub <#2326 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADDH5SUXRBQRPBYR4REGO3UJAWVNANCNFSM5G2SM5ZQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

How do I force it to transfer in binary?

@davecramer
Copy link
Member

prepareThreshold | Integer | 5 | Statement prepare threshold. A value of -1 stands for forceBinary

@NikolayMetchev
Copy link
Author

I figured it out:

import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.zonky.test.db.postgres.embedded.EmbeddedPostgres
import org.junit.jupiter.api.Test
import org.postgresql.jdbc.PgConnection
import java.math.BigDecimal
import java.sql.Connection

class PostgresBug {
    private val edb = EmbeddedPostgres.start()

    @Test
    fun runTestWithHikari() {
        val cfg = HikariConfig().also {
            it.dataSource = edb.postgresDatabase
        }
        val ds = HikariDataSource(cfg)
        runTest { ds.connection }
    }

    @Test
    fun runTestWithPureJDBC() {
        runTest { forceBinary ->
            val connection = edb.postgresDatabase.connection
            if (forceBinary) {
                (connection as PgConnection).forceBinary = true
            }
            connection
        }
    }

    private fun runTest(getConnection: (Boolean) -> Connection) {
        val twentyK = BigDecimal("20000")
        getConnection(false).use { conn ->
            conn.prepareStatement(
                """
                    create table test_table
                    (
                        numeric_column numeric(40,20) not null
                    );
                    INSERT INTO TEST_TABLE VALUES(20000);
                """
            ).execute()
        }

        repeat(100) { i ->
            getConnection(true).use { conn ->
                val rs = conn.prepareStatement("SELECT * FROM TEST_TABLE").executeQuery()
                rs.use {
                    rs.next()
                    val bigDecimal = rs.getBigDecimal(1)

                    assert(bigDecimal.compareTo(twentyK) == 0) { "$i, $bigDecimal != $twentyK" }
                }
            }
        }
    }
}

@NikolayMetchev
Copy link
Author

With the above code both tests fail...

@bokken
Copy link
Member

bokken commented Oct 27, 2021

I can recreate with simple unit test of the binary decoding of numeric.
For now, disabling binary support for numeric (and numeric array) is probably best work around.

@NikolayMetchev
Copy link
Author

My workaround was to revert to a previous version of the JDBC driver.

@pyranja
Copy link

pyranja commented Oct 28, 2021

We were also affected by this problem. Our table definitions do not set a scale or precision for NUMERIC/DECIMAL values though.

@davecramer
Copy link
Member

@pyranja thx for the report. Until we figure this out, the best thing is to downgrade. I'm working on it.

@NikolayMetchev
Copy link
Author

Does maven central have a way of removing that version. This bug is quite obscure and it caused a real production incident for us, so worth avoiding it for others.

@davecramer
Copy link
Member

Not that I am aware of. The best we could do is release a version that doesn't have the problem

bokken added a commit to bokken/pgjdbc that referenced this issue Oct 28, 2021
binary numeric values which represented integers multiples of 10,000
from 10,000-9,990,000 were not decoded correctly

fixes pgjdbc#2326
davecramer pushed a commit that referenced this issue Oct 28, 2021
* fix: numeric binary decode for even 10 thousands

binary numeric values which represented integers multiples of 10,000
from 10,000-9,990,000 were not decoded correctly

fixes #2326

* fix: numeric binary decode for even 10 thousands

more test values

* fix: numeric binary decode for even 10 thousands
@davecramer
Copy link
Member

Can people please check
pgjdbc v42.3.1-rc1 is ready for preview.

Git SHA: b3050e6
Staging repository: https://oss.sonatype.org/content/repositories/orgpostgresql-1287

@faespsenar
Copy link

Unfortunately PostgreSQL JDBC 42.3.1 is causing connection leaks with Hikari and C3p0

@davecramer
Copy link
Member

davecramer commented Nov 26, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants