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

correct mapping for postgres timestamptz type to sql type TIMESTAMP_W… #2715

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
10 changes: 10 additions & 0 deletions pgjdbc/src/main/java/org/postgresql/PGProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,16 @@ public enum PGProperty {
"0",
"The timeout value in seconds max(2147484) used for socket read operations."),

/**
* Enable or disable mapping of PG types with TIMEZONE into SQL types with TIMEZONE.
* The default is {@code false}
*/
SQL_TYPES_WITH_TIMEZONE(
"sqlTypesWithTimezone",
Copy link
Member

Choose a reason for hiding this comment

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

I would say we should select a better name that would be searchable and understandable.

"false",
Copy link
Member

Choose a reason for hiding this comment

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

We might consider enum style property instead as it might be easier to reason.

Copy link
Member

Choose a reason for hiding this comment

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

@vlsi if we used enum style what would the two names be ?

"Enable or disable mapping of PG types with TIMEZONE into SQL types with TIMEZONE."
Copy link
Member

Choose a reason for hiding this comment

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

I might be wrong, however, it looks like the description does not explain the meaning of the property.

+ "The default is (@code false)"),

/**
* Control use of SSL: empty or {@code true} values imply {@code sslmode==verify-full}
*/
Expand Down
16 changes: 16 additions & 0 deletions pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,22 @@ public int getSocketTimeout() {
return PGProperty.SOCKET_TIMEOUT.getIntNoCheck(properties);
}

/**
* @param enabled if PG types with TIMEZONE should map into SQL types with TIMEZONE
* @see PGProperty#SQL_TYPES_WITH_TIMEZONE
*/
public void setSqlTypesWithTimezone(boolean enabled) {
PGProperty.SQL_TYPES_WITH_TIMEZONE.set(properties, enabled);
}

/**
* @return true if SQL types with TIMEZONE is enabled
* @see PGProperty#SQL_TYPES_WITH_TIMEZONE
*/
public boolean getSqlTypesWithTimezone() {
return PGProperty.SQL_TYPES_WITH_TIMEZONE.getBoolean(properties);
}

/**
* @param seconds timeout that is used for sending cancel command
* @see PGProperty#CANCEL_SIGNAL_TIMEOUT
Expand Down
7 changes: 4 additions & 3 deletions pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,11 @@ public PgConnection(HostSpec[] hostSpecs,
rollbackQuery = createQuery("ROLLBACK", false, true).query;

int unknownLength = PGProperty.UNKNOWN_LENGTH.getInt(info);
boolean sqlTypesWithTimezone = PGProperty.SQL_TYPES_WITH_TIMEZONE.getBoolean(info);

// Initialize object handling
@SuppressWarnings("argument")
TypeInfo typeCache = createTypeInfo(this, unknownLength);
TypeInfo typeCache = createTypeInfo(this, unknownLength,sqlTypesWithTimezone);
this.typeCache = typeCache;
initObjectTypes(info);

Expand Down Expand Up @@ -777,8 +778,8 @@ public Object getObject(String type, @Nullable String value, byte @Nullable [] b
}
}

protected TypeInfo createTypeInfo(BaseConnection conn, int unknownLength) {
return new TypeInfoCache(conn, unknownLength);
protected TypeInfo createTypeInfo(BaseConnection conn, int unknownLength,boolean sqlTypesWithTimezone) {
return new TypeInfoCache(conn, unknownLength,sqlTypesWithTimezone);
}

@Override
Expand Down
2 changes: 2 additions & 0 deletions pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,10 @@ public URL getURL(String columnName) throws SQLException {
return getString(columnIndex);
case Types.DATE:
return getDate(columnIndex);
case Types.TIME_WITH_TIMEZONE:
case Types.TIME:
return getTime(columnIndex);
case Types.TIMESTAMP_WITH_TIMEZONE:
case Types.TIMESTAMP:
return getTimestamp(columnIndex, null);
case Types.BINARY:
Expand Down
49 changes: 36 additions & 13 deletions pgjdbc/src/main/java/org/postgresql/jdbc/TypeInfoCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,26 @@ public class TypeInfoCache implements TypeInfo {
{"bit", Oid.BIT, Types.BIT, "java.lang.Boolean", Oid.BIT_ARRAY},
{"date", Oid.DATE, Types.DATE, "java.sql.Date", Oid.DATE_ARRAY},
{"time", Oid.TIME, Types.TIME, "java.sql.Time", Oid.TIME_ARRAY},
{"timetz", Oid.TIMETZ, Types.TIME, "java.sql.Time", Oid.TIMETZ_ARRAY},
{"timestamp", Oid.TIMESTAMP, Types.TIMESTAMP, "java.sql.Timestamp", Oid.TIMESTAMP_ARRAY},
{"timestamptz", Oid.TIMESTAMPTZ, Types.TIMESTAMP, "java.sql.Timestamp",
Oid.TIMESTAMPTZ_ARRAY},
{"refcursor", Oid.REF_CURSOR, Types.REF_CURSOR, "java.sql.ResultSet", Oid.REF_CURSOR_ARRAY},
{"json", Oid.JSON, Types.OTHER, "org.postgresql.util.PGobject", Oid.JSON_ARRAY},
{"point", Oid.POINT, Types.OTHER, "org.postgresql.geometric.PGpoint", Oid.POINT_ARRAY},
{"box", Oid.BOX, Types.OTHER, "org.postgresql.geometric.PGBox", Oid.BOX_ARRAY}
};

// whether withTimeZone or noTimeZone types are used is controlled by the PGProperty SQL_TYPES_WITH_TIMEZONE
private static final Object[][] sqlTypesWithTimezone = {
{"timetz", Oid.TIMETZ, Types.TIME_WITH_TIMEZONE, "java.sql.Time", Oid.TIMETZ_ARRAY},
{"timestamptz", Oid.TIMESTAMPTZ, Types.TIMESTAMP_WITH_TIMEZONE, "java.sql.Timestamp",
Oid.TIMESTAMPTZ_ARRAY}
};

private static final Object[][] sqlTypesNoTimezone = {
{"timetz", Oid.TIMETZ, Types.TIME, "java.sql.Time", Oid.TIMETZ_ARRAY},
{"timestamptz", Oid.TIMESTAMPTZ, Types.TIMESTAMP, "java.sql.Timestamp",
Oid.TIMESTAMPTZ_ARRAY}
};

/**
* PG maps several alias to real type names. When we do queries against pg_catalog, we must use
* the real type, not an alias, so use this mapping.
Expand Down Expand Up @@ -152,21 +162,23 @@ public class TypeInfoCache implements TypeInfo {
}

@SuppressWarnings("method.invocation")
public TypeInfoCache(BaseConnection conn, int unknownLength) {
public TypeInfoCache(BaseConnection conn, int unknownLength,boolean sqlTypeWithTimezone) {
this.conn = conn;
this.unknownLength = unknownLength;
oidToPgName = new HashMap<>((int) Math.round(types.length * 1.5));
pgNameToOid = new HashMap<>((int) Math.round(types.length * 1.5));
javaArrayTypeToOid = new HashMap<>((int) Math.round(types.length * 1.5));
pgNameToJavaClass = new HashMap<>((int) Math.round(types.length * 1.5));
pgNameToPgObject = new HashMap<>((int) Math.round(types.length * 1.5));
pgArrayToPgType = new HashMap<>((int) Math.round(types.length * 1.5));
arrayOidToDelimiter = new HashMap<>((int) Math.round(types.length * 2.5));
final Object[][] timezoneTypes = sqlTypeWithTimezone ? sqlTypesWithTimezone : sqlTypesNoTimezone;
final int typesLength = types.length + timezoneTypes.length;
oidToPgName = new HashMap<Integer, String>((int) Math.round(typesLength * 1.5));
pgNameToOid = new HashMap<String, Integer>((int) Math.round(typesLength * 1.5));
javaArrayTypeToOid = new HashMap<String, Integer>((int) Math.round(typesLength * 1.5));
pgNameToJavaClass = new HashMap<String, String>((int) Math.round(typesLength * 1.5));
pgNameToPgObject = new HashMap<String, Class<? extends PGobject>>((int) Math.round(typesLength * 1.5));
pgArrayToPgType = new HashMap<Integer, Integer>((int) Math.round(typesLength * 1.5));
arrayOidToDelimiter = new HashMap<Integer, Character>((int) Math.round(typesLength * 2.5));

// needs to be synchronized because the iterator is returned
// from getPGTypeNamesWithSQLTypes()
pgNameToSQLType = Collections.synchronizedMap(new HashMap<String, Integer>((int) Math.round(types.length * 1.5)));
oidToSQLType = Collections.synchronizedMap(new HashMap<Integer, Integer>((int) Math.round(types.length * 1.5)));
pgNameToSQLType = Collections.synchronizedMap(new HashMap<String, Integer>((int) Math.round(typesLength * 1.5)));
oidToSQLType = Collections.synchronizedMap(new HashMap<Integer, Integer>((int) Math.round(typesLength * 1.5)));

for (Object[] type : types) {
String pgTypeName = (String) type[0];
Expand All @@ -178,6 +190,17 @@ public TypeInfoCache(BaseConnection conn, int unknownLength) {
addCoreType(pgTypeName, oid, sqlType, javaClass, arrayOid);
}

// add timezone types
for (Object[] type : timezoneTypes) {
String pgTypeName = (String) type[0];
Integer oid = (Integer) type[1];
Integer sqlType = (Integer) type[2];
String javaClass = (String) type[3];
Integer arrayOid = (Integer) type[4];

addCoreType(pgTypeName, oid, sqlType, javaClass, arrayOid);
}

pgNameToJavaClass.put("hstore", Map.class.getName());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public void object3dArrayCopy() throws Exception {

private static final class EncodingConnection implements BaseConnection {
private final Encoding encoding;
private final TypeInfo typeInfo = new TypeInfoCache(this, -1);
private final TypeInfo typeInfo = new TypeInfoCache(this, -1,false);

EncodingConnection(Encoding encoding) {
this.encoding = encoding;
Expand Down