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

redo PR fix_binary_transfer_floating point from brett, removed BigDecimal where possible #2719

Merged
merged 8 commits into from
Jan 12, 2023
34 changes: 29 additions & 5 deletions pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -3376,6 +3375,11 @@ private double readDoubleValue(byte[] bytes, int oid, String targetType) throws
Oid.toString(oid), targetType), PSQLState.DATA_TYPE_MISMATCH);
}

private static final float LONG_MAX_FLOAT = StrictMath.nextDown(Long.MAX_VALUE);
private static final float LONG_MIN_FLOAT = StrictMath.nextUp(Long.MIN_VALUE);
private static final BigDecimal LONG_MAX_BD = BigDecimal.valueOf(Long.MAX_VALUE);
private static final BigDecimal LONG_MIN_BD = BigDecimal.valueOf(Long.MIN_VALUE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these used any more?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed


/**
* <p>Converts any numeric binary field to long value.</p>
*
Expand Down Expand Up @@ -3411,18 +3415,38 @@ private long readLongValue(byte[] bytes, int oid, long minVal, long maxVal, Stri
val = ByteConverter.int8(bytes, 0);
break;
case Oid.FLOAT4:
val = (long) ByteConverter.float4(bytes, 0);
float f = ByteConverter.float4(bytes, 0);
// for float values we know to be within values of long, just cast directly to long
if (f <= LONG_MAX_FLOAT && f >= LONG_MIN_FLOAT) {
val = (long) f;
} else {
throw new PSQLException(GT.tr("Bad value for type {0} : {1}", targetType, f),
PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
}
break;
case Oid.FLOAT8:
val = (long) ByteConverter.float8(bytes, 0);
double d = ByteConverter.float8(bytes, 0);
// for double values within the values of a long, just directly cast to long
if (d <= LONG_MAX_FLOAT && d >= LONG_MIN_FLOAT) {
davecramer marked this conversation as resolved.
Show resolved Hide resolved
val = (long) d;
} else {
throw new PSQLException(GT.tr("Bad value for type {0} : {1}", targetType, d),
PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
}
break;
case Oid.NUMERIC:
Number num = ByteConverter.numeric(bytes);
if (num instanceof BigDecimal) {
val = ((BigDecimal) num).setScale(0 , RoundingMode.DOWN).longValueExact();
if (num instanceof BigDecimal) {
if ( ((BigDecimal) num).compareTo(LONG_MAX_BD) <= 0 && ((BigDecimal) num).compareTo(LONG_MIN_BD) >= 0 ) {
davecramer marked this conversation as resolved.
Show resolved Hide resolved
val = num.longValue();
} else {
throw new PSQLException(GT.tr("Bad value for type {0} : {1}", targetType, num),
PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
}
} else {
val = num.longValue();
}

break;
default:
throw new PSQLException(
Expand Down
96 changes: 80 additions & 16 deletions pgjdbc/src/test/java/org/postgresql/test/jdbc2/ResultSetTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,26 @@ public void setUp() throws Exception {
stmt.executeUpdate("INSERT INTO testboolstring VALUES('1.0', null)");
stmt.executeUpdate("INSERT INTO testboolstring VALUES('0.0', null)");

TestUtil.createTable(con, "testboolfloat", "a float4, b boolean");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES('1.0'::real, true)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES('0.0'::real, false)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(1.000::real, true)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(0.000::real, false)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES('1.001'::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES('-1.001'::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(123.4::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(1.234e2::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(100.00e-2::real, true)");
TestUtil.createTable(con, "testboolfloat", "i int, a float4, b boolean");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(1, '1.0'::real, true)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(2, '0.0'::real, false)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(3, 1.000::real, true)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(4, 0.000::real, false)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(5, '1.001'::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(6, '-1.001'::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(7, 123.4::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(8, 1.234e2::real, null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(9, 100.00e-2::real, true)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(10, '9223371487098961921', null)");
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(11, '10223372036850000000', null)");
String floatVal = Float.toString(StrictMath.nextDown(Long.MAX_VALUE - 1));
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(12, " + floatVal + ", null)");
floatVal = Float.toString(StrictMath.nextDown(Long.MAX_VALUE + 1));
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(13, " + floatVal + ", null)");
floatVal = Float.toString(StrictMath.nextUp(Long.MIN_VALUE - 1));
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(14, " + floatVal + ", null)");
floatVal = Float.toString(StrictMath.nextUp(Long.MIN_VALUE + 1));
stmt.executeUpdate("INSERT INTO testboolfloat VALUES(15, " + floatVal + ", null)");

TestUtil.createTable(con, "testboolint", "a bigint, b boolean");
stmt.executeUpdate("INSERT INTO testboolint VALUES(1, true)");
Expand Down Expand Up @@ -463,7 +473,8 @@ public void testgetByte() throws SQLException {
try {
rs.getByte(1);
fail("Exception expected.");
} catch (Exception e) {
} catch (SQLException e) {
assertEquals(e.getSQLState(),"22003");
}
}
rs.close();
Expand Down Expand Up @@ -507,7 +518,7 @@ public void testgetShort() throws SQLException {
try {
rs.getShort(1);
fail("Exception expected.");
} catch (Exception e) {
} catch (SQLException e) {
}
}
rs.close();
Expand Down Expand Up @@ -569,7 +580,7 @@ public void testgetInt() throws SQLException {
try {
rs.getInt(1);
fail("Exception expected." + rs.getString(1));
} catch (Exception e) {
} catch (SQLException e) {
}
}
rs.close();
Expand Down Expand Up @@ -641,12 +652,65 @@ public void testgetLong() throws SQLException {

while (rs.next()) {
try {
rs.getLong(1);
fail("Exception expected." + rs.getString(1));
} catch (Exception e) {
String s = rs.getString(1);
long l = rs.getLong(1);
fail("Exception expected. " + rs.getString(1));
} catch (SQLException e) {
}
}
rs.close();

rs = con.createStatement().executeQuery("select i, a from testboolfloat order by i");

assertTrue(rs.next());
assertEquals(1, rs.getLong(2));

assertTrue(rs.next());
assertEquals(0, rs.getLong(2));

assertTrue(rs.next());
assertEquals(1, rs.getLong(2));

assertTrue(rs.next());
assertEquals(0, rs.getLong(2));

assertTrue(rs.next());
assertEquals(1, rs.getLong(2));

assertTrue(rs.next());
assertEquals(-1, rs.getLong(2));

assertTrue(rs.next());
assertEquals(123, rs.getLong(2));

assertTrue(rs.next());
assertEquals(123, rs.getLong(2));

assertTrue(rs.next());
assertEquals(1, rs.getLong(2));

assertTrue(rs.next());
// the string value from database trims the significant digits, leading to larger variance than binary
// the liberica jdk gets similar variance, even in forced binary mode
assertEquals(9223371487098961921.0, rs.getLong(2), 1.0e11);

assertTrue(rs.next());
do {
try {
int row = rs.getInt(1);
long l = rs.getLong(2);
if ( row == 12 ) {
assertEquals(9223371487098961920.0, l, 1.0e11);
} else if ( row == 15 ) {
assertEquals(-9223371487098961920.0, l, 1.0e11);
} else {
fail("Exception expected." + rs.getString(2));
}
} catch (SQLException e) {
}
} while (rs.next());

rs.close();
}

@Test
Expand Down