Skip to content

Commit

Permalink
fix: allow setting arrays with ANSI type name
Browse files Browse the repository at this point in the history
Currently, pgjdbc does not support setting arrays using ANSI type names
like `double precision` or `timestamp with timezone`. For example,
`conn.createArrayOf("double precision", new Object[] {0d})`
fails with the exception "Unable to find server array type for provided
name double precision". This can be worked around by using the PostgreSQL
type name (e.g. `float8`) instead, however it seems like the driver should
be able to handle the same set of array types as PostgreSQL itself.

This isn't the first attempt to fix this issue. In 2020, #1719 was merged
to fix the same problem. The patch caused some introspected types to change
(#1744), and was reverted by #1745. Adding the missing types to `TYPE_ALIASES`
fixes the problem, and avoids the OID collisions that caused #1744.
  • Loading branch information
bremac committed Sep 6, 2023
1 parent 7725a8e commit c062204
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 1 deletion.
14 changes: 13 additions & 1 deletion pgjdbc/src/main/java/org/postgresql/jdbc/TypeInfoCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public class TypeInfoCache implements TypeInfo {
* Additional values used at runtime (including case variants) will be added to the map.
* </p>
*/
private static final ConcurrentMap<String, String> TYPE_ALIASES = new ConcurrentHashMap<>(20);
private static final ConcurrentMap<String, String> TYPE_ALIASES = new ConcurrentHashMap<>(30);

static {
TYPE_ALIASES.put("bool", "bool");
Expand All @@ -131,11 +131,23 @@ public class TypeInfoCache implements TypeInfo {
TYPE_ALIASES.put("int8", "int8");
TYPE_ALIASES.put("bigint", "int8");
TYPE_ALIASES.put("float", "float8");
TYPE_ALIASES.put("real", "float4");
TYPE_ALIASES.put("float4", "float4");
TYPE_ALIASES.put("double", "float8");
TYPE_ALIASES.put("double precision", "float8");
TYPE_ALIASES.put("float8", "float8");
TYPE_ALIASES.put("decimal", "numeric");
TYPE_ALIASES.put("numeric", "numeric");
TYPE_ALIASES.put("character varying", "varchar");
TYPE_ALIASES.put("varchar", "varchar");
TYPE_ALIASES.put("time without time zone", "time");
TYPE_ALIASES.put("time", "time");
TYPE_ALIASES.put("time with time zone", "timetz");
TYPE_ALIASES.put("timetz", "timetz");
TYPE_ALIASES.put("timestamp without time zone", "timestamp");
TYPE_ALIASES.put("timestamp", "timestamp");
TYPE_ALIASES.put("timestamp with time zone", "timestamptz");
TYPE_ALIASES.put("timestamptz", "timestamptz");
}

@SuppressWarnings("method.invocation")
Expand Down
95 changes: 95 additions & 0 deletions pgjdbc/src/test/java/org/postgresql/test/jdbc2/ArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
Expand Down Expand Up @@ -336,6 +338,99 @@ public void testSetPrimitiveArraysObjects() throws SQLException {
rs.close();
}

@Test
public void testSetArraysWithAnsiTypeNames() throws SQLException {
TestUtil.createTable(
conn,
"ansiarraytest",
"floats double precision[], "
+ "reals real[], "
+ "varchars character varying(8)[], "
+ "times time without time zone[], "
+ "timestamps timestamp without time zone[], "
+ "timestampstz timestamp with time zone[]");

PreparedStatement pstmt = conn.prepareStatement("INSERT INTO ansiarraytest VALUES (?,?,?,?,?,?)");

final PGConnection arraySupport = conn.unwrap(PGConnection.class);

pstmt.setArray(1, arraySupport.createArrayOf("double precision", new Object[] {1d, 4d}));
pstmt.setArray(2, arraySupport.createArrayOf("real", new Object[] {0f, 3f}));
pstmt.setObject(
3, arraySupport.createArrayOf("character varying", new String[] {"abc", "f'a", "fa\"b"}));
pstmt.setObject(
4,
arraySupport.createArrayOf(
"time without time zone",
new Object[] {Time.valueOf("12:34:56"), Time.valueOf("03:30:25")}));
pstmt.setObject(
5,
arraySupport.createArrayOf(
"timestamp without time zone",
new Object[] {"2023-09-05 16:21:50", "2012-01-01 13:02:03"}));
pstmt.setObject(
6,
arraySupport.createArrayOf(
"timestamp with time zone",
new Object[] {"1996-01-23 12:00:00-08", "1997-08-16 16:51:00-04"}));

pstmt.executeUpdate();
pstmt.close();

Statement stmt = conn.createStatement();
ResultSet rs =
stmt.executeQuery(
"SELECT floats, reals, varchars, times, timestamps, timestampstz FROM ansiarraytest");
Assert.assertTrue(rs.next());

Array arr = rs.getArray(1);
Assert.assertEquals(Types.DOUBLE, arr.getBaseType());
Double[] doubles = (Double[]) arr.getArray();
Assert.assertEquals(2, doubles.length);
Assert.assertEquals(1d, doubles[0], 0);
Assert.assertEquals(4d, doubles[1], 0);

arr = rs.getArray(2);
Assert.assertEquals(Types.REAL, arr.getBaseType());
Float[] floats = (Float[]) arr.getArray();
Assert.assertEquals(2, floats.length);
Assert.assertEquals(0f, floats[0], 0);
Assert.assertEquals(3f, floats[1], 0);

arr = rs.getArray(3);
Assert.assertEquals(Types.VARCHAR, arr.getBaseType());
String[] strings = (String[]) arr.getArray();
Assert.assertEquals(3, strings.length);
Assert.assertEquals("abc", strings[0]);
Assert.assertEquals("f'a", strings[1]);
Assert.assertEquals("fa\"b", strings[2]);

arr = rs.getArray(4);
Assert.assertEquals(Types.TIME, arr.getBaseType());
Time[] times = (Time[]) arr.getArray();
Assert.assertEquals(2, times.length);
Assert.assertEquals(Time.valueOf("12:34:56"), times[0]);
Assert.assertEquals(Time.valueOf("03:30:25"), times[1]);

arr = rs.getArray(5);
Assert.assertEquals(Types.TIMESTAMP, arr.getBaseType());
Timestamp[] tzarr = (Timestamp[]) arr.getArray();
Assert.assertEquals(2, times.length);
Assert.assertEquals(Timestamp.valueOf("2023-09-05 16:21:50"), tzarr[0]);
Assert.assertEquals(Timestamp.valueOf("2012-01-01 13:02:03"), tzarr[1]);

arr = rs.getArray(6);
Assert.assertEquals(Types.TIMESTAMP, arr.getBaseType());
tzarr = (Timestamp[]) arr.getArray();
Assert.assertEquals(2, times.length);
Assert.assertEquals(822427200000L, tzarr[0].getTime());
Assert.assertEquals(871764660000L, tzarr[1].getTime());

rs.close();

TestUtil.dropTable(conn, "ansiarraytest");
}

@Test
public void testSetNullArrays() throws SQLException {
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO arrtest VALUES (?,?,?)");
Expand Down

0 comments on commit c062204

Please sign in to comment.