[Git][java-team/libpostgresql-jdbc-java][master] 3 commits: New upstream version 42.4.2
Christoph Berg (@myon)
gitlab at salsa.debian.org
Mon Aug 22 13:25:17 BST 2022
Christoph Berg pushed to branch master at Debian Java Maintainers / libpostgresql-jdbc-java
Commits:
732b021a by Christoph Berg at 2022-08-22T14:24:01+02:00
New upstream version 42.4.2
- - - - -
b10b0927 by Christoph Berg at 2022-08-22T14:24:04+02:00
Update upstream source from tag 'upstream/42.4.2'
Update to upstream version '42.4.2'
with Debian dir ec286e66f6ba8bb40bb9bdeb57f439d71df6644e
- - - - -
3467636e by Christoph Berg at 2022-08-22T14:24:50+02:00
New upstream version 42.4.2.
- - - - -
19 changed files:
- README.md
- debian/changelog
- pom.xml
- src/main/java/org/postgresql/PGProperty.java
- src/main/java/org/postgresql/core/TypeInfo.java
- src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
- src/main/java/org/postgresql/ds/common/BaseDataSource.java
- src/main/java/org/postgresql/gss/GssAction.java
- src/main/java/org/postgresql/gss/MakeGSS.java
- src/main/java/org/postgresql/jdbc/PgCallableStatement.java
- src/main/java/org/postgresql/jdbc/PgConnection.java
- src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
- src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
- src/main/java/org/postgresql/jdbc/PgStatement.java
- src/main/java/org/postgresql/jdbc/TypeInfoCache.java
- src/main/java/org/postgresql/util/DriverInfo.java
- src/main/resources/META-INF/MANIFEST.MF
- + src/test/java/org/postgresql/jdbc/UUIDArrayTest.java
- src/test/java/org/postgresql/test/jdbc2/StatementTest.java
Changes:
=====================================
README.md
=====================================
@@ -118,6 +118,7 @@ In addition to the standard connection parameters the driver supports a number o
| loginTimeout | Integer | 0 | Specify how long to wait for establishment of a database connection.|
| connectTimeout | Integer | 10 | The timeout value used for socket connect operations. |
| socketTimeout | Integer | 0 | The timeout value used for socket read operations. |
+| sslResponseTimeout | Integer | 5000 | Socket timeout waiting for a response from a request for SSL upgrade from the server. |
| tcpKeepAlive | Boolean | false | Enable or disable TCP keep-alive. |
| tcpNoDelay | Boolean | true | Enable or disable TCP no delay. |
| ApplicationName | String | PostgreSQL JDBC Driver | The application name (require server version >= 9.0). If assumeMinServerVersion is set to >= 9.0 this will be sent in the startup packets, otherwise after the connection is made |
=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+libpgjava (42.4.2-1) unstable; urgency=medium
+
+ * New upstream version 42.4.2.
+
+ -- Christoph Berg <myon at debian.org> Mon, 22 Aug 2022 14:24:18 +0200
+
libpgjava (42.4.1-1) unstable; urgency=medium
* New upstream version 42.4.1
=====================================
pom.xml
=====================================
@@ -10,7 +10,7 @@
<artifactId>postgresql</artifactId>
<packaging>jar</packaging>
<name>PostgreSQL JDBC Driver - JDBC 4.2</name>
- <version>42.4.1</version>
+ <version>42.4.2</version>
<description>Java JDBC 4.2 (JRE 8+) driver for PostgreSQL database</description>
<url>https://github.com/pgjdbc/pgjdbc</url>
=====================================
src/main/java/org/postgresql/PGProperty.java
=====================================
@@ -672,6 +672,15 @@ public enum PGProperty {
null,
"A class, implementing javax.security.auth.callback.CallbackHandler that can handle PassworCallback for the ssl password."),
+ /**
+ * <p>After requesting an upgrade to SSL from the server there are reports of the server not responding due to a failover
+ * without a timeout here, the client can wait forever. This timeout will be set before the request and reset after </p>
+ */
+ SSL_RESPONSE_TIMEOUT(
+ "sslResponseTimeout",
+ "5000",
+ "Time in milliseconds we wait for a response from the server after requesting SSL upgrade"),
+
/**
* File containing the root certificate when validating server ({@code sslmode} = {@code
* verify-ca} or {@code verify-full}). Default will be the file {@code root.crt} in {@code
=====================================
src/main/java/org/postgresql/core/TypeInfo.java
=====================================
@@ -36,6 +36,8 @@ public interface TypeInfo {
*/
int getSQLType(String pgTypeName) throws SQLException;
+ int getJavaArrayType(String className) throws SQLException;
+
/**
* Look up the oid for a given postgresql type name. This is the inverse of
* {@link #getPGType(int)}.
=====================================
src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
=====================================
@@ -532,6 +532,17 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
LOGGER.log(Level.FINEST, " FE=> SSLRequest");
+ int sslTimeout = PGProperty.SSL_RESPONSE_TIMEOUT.getInt(info);
+ int currentTimeout = pgStream.getSocket().getSoTimeout();
+
+ // if the current timeout is less than sslTimeout then
+ // use the smaller timeout. We could do something tricky
+ // here to not set it in that case but this is pretty readable
+ if (currentTimeout > 0 && currentTimeout < sslTimeout) {
+ sslTimeout = currentTimeout;
+ }
+
+ pgStream.getSocket().setSoTimeout(sslTimeout);
// Send SSL request packet
pgStream.sendInteger4(8);
pgStream.sendInteger2(1234);
@@ -540,6 +551,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
// Now get the response from the backend, one of N, E, S.
int beresp = pgStream.receiveChar();
+ pgStream.getSocket().setSoTimeout(currentTimeout);
+
switch (beresp) {
case 'E':
LOGGER.log(Level.FINEST, " <=BE SSLError");
=====================================
src/main/java/org/postgresql/ds/common/BaseDataSource.java
=====================================
@@ -354,6 +354,24 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
PGProperty.CONNECT_TIMEOUT.set(properties, connectTimeout);
}
+ /**
+ *
+ * @return SSL ResponseTimeout
+ * @see PGProperty#SSL_RESPONSE_TIMEOUT
+ */
+ public int getSslResponseTimeout() {
+ return PGProperty.SSL_RESPONSE_TIMEOUT.getIntNoCheck(properties);
+ }
+
+ /**
+ *
+ * @param sslResponseTimeout ssl response timeout
+ * @see PGProperty#SSL_RESPONSE_TIMEOUT
+ */
+ public void setSslResponseTimeout(int sslResponseTimeout) {
+ PGProperty.SSL_RESPONSE_TIMEOUT.set(properties,sslResponseTimeout);
+ }
+
/**
* @return protocol version
* @see PGProperty#PROTOCOL_VERSION
=====================================
src/main/java/org/postgresql/gss/GssAction.java
=====================================
@@ -24,12 +24,13 @@ import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.Subject;
-class GssAction implements PrivilegedAction</* @Nullable */ Exception> {
+class GssAction implements PrivilegedAction</* @Nullable */ Exception>, Callable</* @Nullable */ Exception> {
private static final Logger LOGGER = Logger.getLogger(GssAction.class.getName());
private final PGStream pgStream;
@@ -168,4 +169,9 @@ class GssAction implements PrivilegedAction</* @Nullable */ Exception> {
}
return null;
}
+
+ @Override
+ public /* @Nullable */ Exception call() throws Exception {
+ return run();
+ }
}
=====================================
src/main/java/org/postgresql/gss/MakeGSS.java
=====================================
@@ -22,7 +22,6 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.logging.Level;
@@ -74,14 +73,14 @@ public class MakeGSS {
MethodHandle subjectDoAs = null;
try {
subjectDoAs = MethodHandles.lookup().findStatic(Subject.class, "doAs",
- MethodType.methodType(Object.class, Subject.class, PrivilegedExceptionAction.class));
+ MethodType.methodType(Object.class, Subject.class, PrivilegedAction.class));
} catch (NoSuchMethodException | IllegalAccessException ignore) {
}
SUBJECT_DO_AS = subjectDoAs;
MethodHandle subjectCallAs = null;
try {
- subjectDoAs = MethodHandles.lookup().findStatic(Subject.class, "callAs",
+ subjectCallAs = MethodHandles.lookup().findStatic(Subject.class, "callAs",
MethodType.methodType(Object.class, Subject.class, Callable.class));
} catch (NoSuchMethodException | IllegalAccessException ignore) {
}
@@ -93,6 +92,7 @@ public class MakeGSS {
* {@code Subject.getSubject(AccessController.getContext())} in Java before 18.
* @return current Subject or null
*/
+ @SuppressWarnings("deprecation")
private static /* @Nullable */ Subject getCurrentSubject() {
try {
if (SUBJECT_CURRENT != null) {
@@ -102,7 +102,7 @@ public class MakeGSS {
return null;
}
return (Subject) SUBJECT_GET_SUBJECT.invoke(
- ACCESS_CONTROLLER_GET_CONTEXT.invokeExact()
+ ACCESS_CONTROLLER_GET_CONTEXT.invoke()
);
} catch (Throwable e) {
if (e instanceof RuntimeException) {
@@ -163,7 +163,7 @@ public class MakeGSS {
result = (Exception) SUBJECT_DO_AS.invoke(subject, action);
} else if (SUBJECT_CALL_AS != null) {
//noinspection ConstantConditions,unchecked
- result = (Exception) SUBJECT_CALL_AS.invoke(subject, (Callable<Exception>) action);
+ result = (Exception) SUBJECT_CALL_AS.invoke(subject, action);
} else {
throw new PSQLException(
GT.tr("Neither Subject.doAs (Java before 18) nor Subject.callAs (Java 18+) method found"),
=====================================
src/main/java/org/postgresql/jdbc/PgCallableStatement.java
=====================================
@@ -80,79 +80,79 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
@Override
public boolean executeWithFlags(int flags) throws SQLException {
- boolean hasResultSet = super.executeWithFlags(flags);
- int[] functionReturnType = this.functionReturnType;
- if (!isFunction || !returnTypeSet || functionReturnType == null) {
- return hasResultSet;
- }
-
- // If we are executing and there are out parameters
- // callable statement function set the return data
- if (!hasResultSet) {
- throw new PSQLException(GT.tr("A CallableStatement was executed with nothing returned."),
- PSQLState.NO_DATA);
- }
+ synchronized (this) {
+ boolean hasResultSet = super.executeWithFlags(flags);
+ int[] functionReturnType = this.functionReturnType;
+ if (!isFunction || !returnTypeSet || functionReturnType == null) {
+ return hasResultSet;
+ }
- ResultSet rs = castNonNull(getResultSet());
- if (!rs.next()) {
- throw new PSQLException(GT.tr("A CallableStatement was executed with nothing returned."),
- PSQLState.NO_DATA);
- }
+ // If we are executing and there are out parameters
+ // callable statement function set the return data
+ if (!hasResultSet) {
+ throw new PSQLException(GT.tr("A CallableStatement was executed with nothing returned."),
+ PSQLState.NO_DATA);
+ }
- // figure out how many columns
- int cols = rs.getMetaData().getColumnCount();
+ ResultSet rs = castNonNull(getResultSet());
+ if (!rs.next()) {
+ throw new PSQLException(GT.tr("A CallableStatement was executed with nothing returned."),
+ PSQLState.NO_DATA);
+ }
- int outParameterCount = preparedParameters.getOutParameterCount();
+ // figure out how many columns
+ int cols = rs.getMetaData().getColumnCount();
- if (cols != outParameterCount) {
- throw new PSQLException(
- GT.tr("A CallableStatement was executed with an invalid number of parameters"),
- PSQLState.SYNTAX_ERROR);
- }
+ int outParameterCount = preparedParameters.getOutParameterCount();
- // reset last result fetched (for wasNull)
- lastIndex = 0;
-
- // allocate enough space for all possible parameters without regard to in/out
- /* @Nullable */ Object[] callResult = new Object[preparedParameters.getParameterCount() + 1];
- this.callResult = callResult;
-
- // move them into the result set
- for (int i = 0, j = 0; i < cols; i++, j++) {
- // find the next out parameter, the assumption is that the functionReturnType
- // array will be initialized with 0 and only out parameters will have values
- // other than 0. 0 is the value for java.sql.Types.NULL, which should not
- // conflict
- while (j < functionReturnType.length && functionReturnType[j] == 0) {
- j++;
+ if (cols != outParameterCount) {
+ throw new PSQLException(
+ GT.tr("A CallableStatement was executed with an invalid number of parameters"),
+ PSQLState.SYNTAX_ERROR);
}
- callResult[j] = rs.getObject(i + 1);
- int columnType = rs.getMetaData().getColumnType(i + 1);
+ // reset last result fetched (for wasNull)
+ lastIndex = 0;
+
+ // allocate enough space for all possible parameters without regard to in/out
+ /* @Nullable */ Object[] callResult = new Object[preparedParameters.getParameterCount() + 1];
+ this.callResult = callResult;
+
+ // move them into the result set
+ for (int i = 0, j = 0; i < cols; i++, j++) {
+ // find the next out parameter, the assumption is that the functionReturnType
+ // array will be initialized with 0 and only out parameters will have values
+ // other than 0. 0 is the value for java.sql.Types.NULL, which should not
+ // conflict
+ while (j < functionReturnType.length && functionReturnType[j] == 0) {
+ j++;
+ }
- if (columnType != functionReturnType[j]) {
- // this is here for the sole purpose of passing the cts
- if (columnType == Types.DOUBLE && functionReturnType[j] == Types.REAL) {
- // return it as a float
- Object result = callResult[j];
- if (result != null) {
- callResult[j] = ((Double) result).floatValue();
+ callResult[j] = rs.getObject(i + 1);
+ int columnType = rs.getMetaData().getColumnType(i + 1);
+
+ if (columnType != functionReturnType[j]) {
+ // this is here for the sole purpose of passing the cts
+ if (columnType == Types.DOUBLE && functionReturnType[j] == Types.REAL) {
+ // return it as a float
+ Object result = callResult[j];
+ if (result != null) {
+ callResult[j] = ((Double) result).floatValue();
+ }
+ } else if (columnType == Types.REF_CURSOR && functionReturnType[j] == Types.OTHER) {
+ // For backwards compatibility reasons we support that ref cursors can be
+ // registered with both Types.OTHER and Types.REF_CURSOR so we allow
+ // this specific mismatch
+ } else {
+ throw new PSQLException(GT.tr(
+ "A CallableStatement function was executed and the out parameter {0} was of type {1} however type {2} was registered.",
+ i + 1, "java.sql.Types=" + columnType, "java.sql.Types=" + functionReturnType[j]),
+ PSQLState.DATA_TYPE_MISMATCH);
}
- } else if (columnType == Types.REF_CURSOR && functionReturnType[j] == Types.OTHER) {
- // For backwards compatibility reasons we support that ref cursors can be
- // registered with both Types.OTHER and Types.REF_CURSOR so we allow
- // this specific mismatch
- } else {
- throw new PSQLException(GT.tr(
- "A CallableStatement function was executed and the out parameter {0} was of type {1} however type {2} was registered.",
- i + 1, "java.sql.Types=" + columnType, "java.sql.Types=" + functionReturnType[j]),
- PSQLState.DATA_TYPE_MISMATCH);
}
- }
- }
- rs.close();
- synchronized (this) {
+ }
+ rs.close();
result = null;
}
return false;
=====================================
src/main/java/org/postgresql/jdbc/PgConnection.java
=====================================
@@ -1454,8 +1454,13 @@ public class PgConnection implements BaseConnection {
statement.execute("IDENTIFY_SYSTEM");
statement.close();
} else {
- if (checkConnectionQuery == null) {
- checkConnectionQuery = prepareStatement("");
+ PreparedStatement checkConnectionQuery;
+ synchronized (this) {
+ checkConnectionQuery = this.checkConnectionQuery;
+ if (checkConnectionQuery == null) {
+ checkConnectionQuery = prepareStatement("");
+ this.checkConnectionQuery = checkConnectionQuery;
+ }
}
checkConnectionQuery.executeUpdate();
}
=====================================
src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
=====================================
@@ -2696,11 +2696,11 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
long longTypOid = typeInfo.intOidToLong(typOid);
int sqlType = typeInfo.getSQLType(typOid);
- sqlwhen.append(" when oid = ").append(longTypOid).append(" then ").append(sqlType);
+ sqlwhen.append(" when base_type.oid = ").append(longTypOid).append(" then ").append(sqlType);
}
sql += sqlwhen.toString();
- sql += " else " + java.sql.Types.OTHER + " end from pg_type where oid=t.typbasetype) "
+ sql += " else " + java.sql.Types.OTHER + " end from pg_type base_type where base_type.oid=t.typbasetype) "
+ "else null end as base_type "
+ "from pg_catalog.pg_type t, pg_catalog.pg_namespace n where t.typnamespace = n.oid and n.nspname != 'pg_catalog' and n.nspname != 'pg_toast'";
=====================================
src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
=====================================
@@ -130,11 +130,13 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
*/
@Override
public ResultSet executeQuery() throws SQLException {
- if (!executeWithFlags(0)) {
- throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
- }
+ synchronized (this) {
+ if (!executeWithFlags(0)) {
+ throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
+ }
- return getSingleResultSet();
+ return getSingleResultSet();
+ }
}
@Override
@@ -146,16 +148,20 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
@Override
public int executeUpdate() throws SQLException {
- executeWithFlags(QueryExecutor.QUERY_NO_RESULTS);
- checkNoResultUpdate();
- return getUpdateCount();
+ synchronized (this) {
+ executeWithFlags(QueryExecutor.QUERY_NO_RESULTS);
+ checkNoResultUpdate();
+ return getUpdateCount();
+ }
}
@Override
public long executeLargeUpdate() throws SQLException {
- executeWithFlags(QueryExecutor.QUERY_NO_RESULTS);
- checkNoResultUpdate();
- return getLargeUpdateCount();
+ synchronized (this) {
+ executeWithFlags(QueryExecutor.QUERY_NO_RESULTS);
+ checkNoResultUpdate();
+ return getLargeUpdateCount();
+ }
}
@Override
@@ -167,20 +173,22 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
@Override
public boolean execute() throws SQLException {
- return executeWithFlags(0);
+ synchronized (this) {
+ return executeWithFlags(0);
+ }
}
public boolean executeWithFlags(int flags) throws SQLException {
try {
- checkClosed();
+ synchronized (this) {
+ checkClosed();
- if (connection.getPreferQueryMode() == PreferQueryMode.SIMPLE) {
- flags |= QueryExecutor.QUERY_EXECUTE_AS_SIMPLE;
- }
+ if (connection.getPreferQueryMode() == PreferQueryMode.SIMPLE) {
+ flags |= QueryExecutor.QUERY_EXECUTE_AS_SIMPLE;
+ }
- execute(preparedQuery, preparedParameters, flags);
+ execute(preparedQuery, preparedParameters, flags);
- synchronized (this) {
checkClosed();
return (result != null && result.getResultSet() != null);
}
@@ -731,18 +739,31 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
}
}
+ private Class<?> getArrayType(Class<?> type) {
+ Class<?> subType = type.getComponentType();
+ while (subType != null) {
+ type = subType;
+ subType = type.getComponentType();
+ }
+ return type;
+ }
+
private <A extends /* @NonNull */ Object> void setObjectArray(int parameterIndex, A in) throws SQLException {
final ArrayEncoding.ArrayEncoder<A> arraySupport = ArrayEncoding.getArrayEncoder(in);
final TypeInfo typeInfo = connection.getTypeInfo();
- final int oid = arraySupport.getDefaultArrayTypeOid();
+ int oid = arraySupport.getDefaultArrayTypeOid();
if (arraySupport.supportBinaryRepresentation(oid) && connection.getPreferQueryMode() != PreferQueryMode.SIMPLE) {
bindBytes(parameterIndex, arraySupport.toBinaryRepresentation(connection, in, oid), oid);
} else {
if (oid == Oid.UNSPECIFIED) {
- throw new SQLFeatureNotSupportedException();
+ Class<?> arrayType = getArrayType(in.getClass());
+ oid = typeInfo.getJavaArrayType(arrayType.getName());
+ if (oid == Oid.UNSPECIFIED) {
+ throw new SQLFeatureNotSupportedException();
+ }
}
final int baseOid = typeInfo.getPGArrayElement(oid);
final String baseType = castNonNull(typeInfo.getPGType(baseOid));
=====================================
src/main/java/org/postgresql/jdbc/PgStatement.java
=====================================
@@ -240,11 +240,13 @@ public class PgStatement implements Statement, BaseStatement {
@Override
public ResultSet executeQuery(String sql) throws SQLException {
- if (!executeWithFlags(sql, 0)) {
- throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
- }
+ synchronized (this) {
+ if (!executeWithFlags(sql, 0)) {
+ throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
+ }
- return getSingleResultSet();
+ return getSingleResultSet();
+ }
}
protected ResultSet getSingleResultSet() throws SQLException {
@@ -262,9 +264,11 @@ public class PgStatement implements Statement, BaseStatement {
@Override
public int executeUpdate(String sql) throws SQLException {
- executeWithFlags(sql, QueryExecutor.QUERY_NO_RESULTS);
- checkNoResultUpdate();
- return getUpdateCount();
+ synchronized (this) {
+ executeWithFlags(sql, QueryExecutor.QUERY_NO_RESULTS);
+ checkNoResultUpdate();
+ return getUpdateCount();
+ }
}
protected final void checkNoResultUpdate() throws SQLException {
@@ -404,17 +408,19 @@ public class PgStatement implements Statement, BaseStatement {
protected final void execute(CachedQuery cachedQuery,
/* @Nullable */ ParameterList queryParameters, int flags)
throws SQLException {
- try {
- executeInternal(cachedQuery, queryParameters, flags);
- } catch (SQLException e) {
- // Don't retry composite queries as it might get partially executed
- if (cachedQuery.query.getSubqueries() != null
- || !connection.getQueryExecutor().willHealOnRetry(e)) {
- throw e;
+ synchronized (this) {
+ try {
+ executeInternal(cachedQuery, queryParameters, flags);
+ } catch (SQLException e) {
+ // Don't retry composite queries as it might get partially executed
+ if (cachedQuery.query.getSubqueries() != null
+ || !connection.getQueryExecutor().willHealOnRetry(e)) {
+ throw e;
+ }
+ cachedQuery.query.close();
+ // Execute the query one more time
+ executeInternal(cachedQuery, queryParameters, flags);
}
- cachedQuery.query.close();
- // Execute the query one more time
- executeInternal(cachedQuery, queryParameters, flags);
}
}
@@ -514,7 +520,10 @@ public class PgStatement implements Statement, BaseStatement {
// No-op.
}
- private volatile boolean isClosed = false;
+ private volatile int isClosed = 0;
+ private static final AtomicIntegerFieldUpdater<PgStatement> IS_CLOSED_UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(
+ PgStatement.class, "isClosed");
@Override
public int getUpdateCount() throws SQLException {
@@ -667,11 +676,8 @@ public class PgStatement implements Statement, BaseStatement {
*/
public final void close() throws SQLException {
// closing an already closed Statement is a no-op.
- synchronized (this) {
- if (isClosed) {
- return;
- }
- isClosed = true;
+ if (!IS_CLOSED_UPDATER.compareAndSet(this, 0, 1)) {
+ return;
}
cancel();
@@ -1090,9 +1096,11 @@ public class PgStatement implements Statement, BaseStatement {
@Override
public long executeLargeUpdate(String sql) throws SQLException {
- executeWithFlags(sql, QueryExecutor.QUERY_NO_RESULTS);
- checkNoResultUpdate();
- return getLargeUpdateCount();
+ synchronized (this) {
+ executeWithFlags(sql, QueryExecutor.QUERY_NO_RESULTS);
+ checkNoResultUpdate();
+ return getLargeUpdateCount();
+ }
}
@Override
@@ -1116,19 +1124,21 @@ public class PgStatement implements Statement, BaseStatement {
@Override
public long executeLargeUpdate(String sql, String /* @Nullable */ [] columnNames) throws SQLException {
- if (columnNames != null && columnNames.length == 0) {
- return executeLargeUpdate(sql);
- }
+ synchronized (this) {
+ if (columnNames != null && columnNames.length == 0) {
+ return executeLargeUpdate(sql);
+ }
- wantsGeneratedKeysOnce = true;
- if (!executeCachedSql(sql, 0, columnNames)) {
- // no resultset returned. What's a pity!
+ wantsGeneratedKeysOnce = true;
+ if (!executeCachedSql(sql, 0, columnNames)) {
+ // no resultset returned. What's a pity!
+ }
+ return getLargeUpdateCount();
}
- return getLargeUpdateCount();
}
public boolean isClosed() throws SQLException {
- return isClosed;
+ return isClosed == 1;
}
public void setPoolable(boolean poolable) throws SQLException {
@@ -1240,15 +1250,17 @@ public class PgStatement implements Statement, BaseStatement {
}
public int executeUpdate(String sql, String /* @Nullable */ [] columnNames) throws SQLException {
- if (columnNames != null && columnNames.length == 0) {
- return executeUpdate(sql);
- }
+ synchronized (this) {
+ if (columnNames != null && columnNames.length == 0) {
+ return executeUpdate(sql);
+ }
- wantsGeneratedKeysOnce = true;
- if (!executeCachedSql(sql, 0, columnNames)) {
- // no resultset returned. What's a pity!
+ wantsGeneratedKeysOnce = true;
+ if (!executeCachedSql(sql, 0, columnNames)) {
+ // no resultset returned. What's a pity!
+ }
+ return getUpdateCount();
}
- return getUpdateCount();
}
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
@@ -1268,12 +1280,14 @@ public class PgStatement implements Statement, BaseStatement {
}
public boolean execute(String sql, String /* @Nullable */ [] columnNames) throws SQLException {
- if (columnNames != null && columnNames.length == 0) {
- return execute(sql);
- }
+ synchronized (this) {
+ if (columnNames != null && columnNames.length == 0) {
+ return execute(sql);
+ }
- wantsGeneratedKeysOnce = true;
- return executeCachedSql(sql, 0, columnNames);
+ wantsGeneratedKeysOnce = true;
+ return executeCachedSql(sql, 0, columnNames);
+ }
}
public int getResultSetHoldability() throws SQLException {
=====================================
src/main/java/org/postgresql/jdbc/TypeInfoCache.java
=====================================
@@ -52,6 +52,8 @@ public class TypeInfoCache implements TypeInfo {
// pgname (String) -> oid (Integer)
private Map<String, Integer> pgNameToOid;
+ private Map<String, Integer> javaArrayTypeToOid;
+
// pgname (String) -> extension pgobject (Class)
private Map<String, Class<? extends PGobject>> pgNameToPgObject;
@@ -140,6 +142,7 @@ public class TypeInfoCache implements TypeInfo {
this.unknownLength = unknownLength;
oidToPgName = new HashMap<Integer, String>((int) Math.round(types.length * 1.5));
pgNameToOid = new HashMap<String, Integer>((int) Math.round(types.length * 1.5));
+ javaArrayTypeToOid = new HashMap<String, Integer>((int) Math.round(types.length * 1.5));
pgNameToJavaClass = new HashMap<String, String>((int) Math.round(types.length * 1.5));
pgNameToPgObject = new HashMap<String, Class<? extends PGobject>>((int) Math.round(types.length * 1.5));
pgArrayToPgType = new HashMap<Integer, Integer>((int) Math.round(types.length * 1.5));
@@ -168,6 +171,7 @@ public class TypeInfoCache implements TypeInfo {
pgNameToJavaClass.put(pgTypeName, javaClass);
pgNameToOid.put(pgTypeName, oid);
oidToPgName.put(oid, pgTypeName);
+ javaArrayTypeToOid.put(javaClass, arrayOid);
pgArrayToPgType.put(arrayOid, oid);
pgNameToSQLType.put(pgTypeName, sqlType);
oidToSQLType.put(oid, sqlType);
@@ -319,6 +323,15 @@ public class TypeInfoCache implements TypeInfo {
return i;
}
+ @Override
+ public synchronized int getJavaArrayType(String className) throws SQLException {
+ Integer oid = javaArrayTypeToOid.get(className);
+ if (oid == null) {
+ return Oid.UNSPECIFIED;
+ }
+ return oid;
+ }
+
public synchronized int getSQLType(int typeOid) throws SQLException {
if (typeOid == Oid.UNSPECIFIED) {
return Types.OTHER;
=====================================
src/main/java/org/postgresql/util/DriverInfo.java
=====================================
@@ -16,13 +16,13 @@ public final class DriverInfo {
// Driver name
public static final String DRIVER_NAME = "PostgreSQL JDBC Driver";
public static final String DRIVER_SHORT_NAME = "PgJDBC";
- public static final String DRIVER_VERSION = "42.4.1";
+ public static final String DRIVER_VERSION = "42.4.2";
public static final String DRIVER_FULL_NAME = DRIVER_NAME + " " + DRIVER_VERSION;
// Driver version
public static final int MAJOR_VERSION = 42;
public static final int MINOR_VERSION = 4;
- public static final int PATCH_VERSION = 1;
+ public static final int PATCH_VERSION = 2;
// JDBC specification
public static final String JDBC_VERSION = "4.2";
=====================================
src/main/resources/META-INF/MANIFEST.MF
=====================================
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Implementation-Title: PostgreSQL JDBC Driver
Bundle-License: BSD-2-Clause
Automatic-Module-Name: org.postgresql.jdbc
-Implementation-Version: 42.4.1
+Implementation-Version: 42.4.2
Specification-Vendor: Oracle Corporation
Specification-Title: JDBC
Implementation-Vendor-Id: org.postgresql
=====================================
src/test/java/org/postgresql/jdbc/UUIDArrayTest.java
=====================================
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2022, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import org.postgresql.core.ServerVersion;
+import org.postgresql.test.TestUtil;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.UUID;
+
+class UUIDArrayTest {
+
+ private static Connection con;
+ private static final String TABLE_NAME = "uuid_table";
+ private static final String INSERT1 = "INSERT INTO " + TABLE_NAME
+ + " (id, data1) VALUES (?, ?)";
+ private static final String INSERT2 = "INSERT INTO " + TABLE_NAME
+ + " (id, data2) VALUES (?, ?)";
+ private static final String SELECT1 = "SELECT data1 FROM " + TABLE_NAME
+ + " WHERE id = ?";
+ private static final String SELECT2 = "SELECT data2 FROM " + TABLE_NAME
+ + " WHERE id = ?";
+ private static final UUID[] uids1 = new UUID[]{UUID.randomUUID(), UUID.randomUUID()};
+ private static final UUID[][] uids2 = new UUID[][]{uids1};
+
+ @BeforeAll
+ public static void setUp() throws Exception {
+ con = TestUtil.openDB();
+ assumeTrue(TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_6));
+ try (Statement stmt = con.createStatement()) {
+ stmt.execute("CREATE TABLE " + TABLE_NAME
+ + " (id int PRIMARY KEY, data1 UUID[], data2 UUID[][])");
+ }
+ }
+
+ @AfterAll
+ public static void tearDown() throws Exception {
+ try (Statement stmt = con.createStatement()) {
+ stmt.execute("DROP TABLE IF EXISTS " + TABLE_NAME);
+ }
+ TestUtil.closeDB(con);
+ }
+
+ @Test
+ void test1DWithCreateArrayOf() throws SQLException {
+ try (Connection c = assertDoesNotThrow(() -> TestUtil.openDB());
+ PreparedStatement stmt1 = c.prepareStatement(INSERT1);
+ PreparedStatement stmt2 = c.prepareStatement(SELECT1)) {
+ stmt1.setInt(1, 100);
+ stmt1.setArray(2, c.createArrayOf("uuid", uids1));
+ stmt1.execute();
+
+ stmt2.setInt(1, 100);
+ stmt2.execute();
+ try (ResultSet rs = stmt2.getResultSet()) {
+ assertTrue(rs.next());
+ UUID[] array = (UUID[])rs.getArray(1).getArray();
+ assertEquals(uids1[0], array[0]);
+ assertEquals(uids1[1], array[1]);
+ }
+ }
+ }
+
+ @Test
+ void test1DWithSetObject() throws SQLException {
+ try (Connection c = assertDoesNotThrow(() -> TestUtil.openDB());
+ PreparedStatement stmt1 = c.prepareStatement(INSERT1);
+ PreparedStatement stmt2 = c.prepareStatement(SELECT1)) {
+ stmt1.setInt(1, 101);
+ stmt1.setObject(2, uids1);
+ stmt1.execute();
+
+ stmt2.setInt(1, 101);
+ stmt2.execute();
+ try (ResultSet rs = stmt2.getResultSet()) {
+ assertTrue(rs.next());
+ UUID[] array = (UUID[])rs.getArray(1).getArray();
+ assertEquals(uids1[0], array[0]);
+ assertEquals(uids1[1], array[1]);
+ }
+ }
+ }
+
+ @Test
+ void test2DWithCreateArrayOf() throws SQLException {
+ try (Connection c = assertDoesNotThrow(() -> TestUtil.openDB());
+ PreparedStatement stmt1 = c.prepareStatement(INSERT2);
+ PreparedStatement stmt2 = c.prepareStatement(SELECT2)) {
+ stmt1.setInt(1, 200);
+ stmt1.setArray(2, c.createArrayOf("uuid", uids2));
+ stmt1.execute();
+
+ stmt2.setInt(1, 200);
+ stmt2.execute();
+ try (ResultSet rs = stmt2.getResultSet()) {
+ assertTrue(rs.next());
+ UUID[][] array = (UUID[][])rs.getArray(1).getArray();
+ assertEquals(uids2[0][0], array[0][0]);
+ assertEquals(uids2[0][1], array[0][1]);
+ }
+ }
+ }
+
+ @Test
+ void test2DWithSetObject() throws SQLException {
+ try (Connection c = assertDoesNotThrow(() -> TestUtil.openDB());
+ PreparedStatement stmt1 = c.prepareStatement(INSERT2);
+ PreparedStatement stmt2 = c.prepareStatement(SELECT2)) {
+ stmt1.setInt(1, 201);
+ stmt1.setObject(2, uids2);
+ stmt1.execute();
+
+ stmt2.setInt(1, 201);
+ stmt2.execute();
+ try (ResultSet rs = stmt2.getResultSet()) {
+ assertTrue(rs.next());
+ UUID[][] array = (UUID[][])rs.getArray(1).getArray();
+ assertEquals(uids2[0][0], array[0][0]);
+ assertEquals(uids2[0][1], array[0][1]);
+ }
+ }
+ }
+}
=====================================
src/test/java/org/postgresql/test/jdbc2/StatementTest.java
=====================================
@@ -32,7 +32,9 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
@@ -861,7 +863,7 @@ public class StatementTest {
public void testCancelQueryWithBrokenNetwork() throws SQLException, IOException, InterruptedException {
// check that stmt.cancel() doesn't hang forever if the network is broken
- ExecutorService executor = Executors.newSingleThreadExecutor();
+ ExecutorService executor = Executors.newCachedThreadPool();
try (StrangeProxyServer proxyServer = new StrangeProxyServer(TestUtil.getServer(), TestUtil.getPort())) {
Properties props = new Properties();
@@ -875,6 +877,9 @@ public class StatementTest {
proxyServer.stopForwardingAllClients();
stmt.cancel();
+ // Note: network is still inaccessible, so the statement execution is still in progress.
+ // So we abort the connection to allow implicit conn.close()
+ conn.abort(executor);
}
}
@@ -939,6 +944,51 @@ public class StatementTest {
}
}
+ @Test(timeout = 10000)
+ public void testConcurrentIsValid() throws Throwable {
+ ExecutorService executor = Executors.newCachedThreadPool();
+ try {
+ List<Future<?>> results = new ArrayList<>();
+ Random rnd = new Random();
+ for (int i = 0; i < 10; i++) {
+ Future<?> future = executor.submit(() -> {
+ try {
+ for (int j = 0; j < 50; j++) {
+ con.isValid(1);
+ try (PreparedStatement ps =
+ con.prepareStatement("select * from generate_series(1,?) as x(id)")) {
+ int limit = rnd.nextInt(10);
+ ps.setInt(1, limit);
+ try (ResultSet r = ps.executeQuery()) {
+ int cnt = 0;
+ String callName = "generate_series(1, " + limit + ") in thread "
+ + Thread.currentThread().getName();
+ while (r.next()) {
+ cnt++;
+ assertEquals(callName + ", row " + cnt, cnt, r.getInt(1));
+ }
+ assertEquals(callName + " number of rows", limit, cnt);
+ }
+ }
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ results.add(future);
+ }
+ for (Future<?> result : results) {
+ // Propagate exception if any
+ result.get();
+ }
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ } finally {
+ executor.shutdown();
+ executor.awaitTermination(10, TimeUnit.SECONDS);
+ }
+ }
+
@Test(timeout = 20000)
public void testFastCloses() throws SQLException {
ExecutorService executor = Executors.newSingleThreadExecutor();
View it on GitLab: https://salsa.debian.org/java-team/libpostgresql-jdbc-java/-/compare/c03c688ea0dacca13f3ef030f715524b5a72d125...3467636e2c6bb4c99fcd92d50283e11444fc12a7
--
View it on GitLab: https://salsa.debian.org/java-team/libpostgresql-jdbc-java/-/compare/c03c688ea0dacca13f3ef030f715524b5a72d125...3467636e2c6bb4c99fcd92d50283e11444fc12a7
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20220822/2fe26988/attachment.htm>
More information about the pkg-java-commits
mailing list