You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by st...@apache.org on 2023/11/15 09:46:20 UTC
(phoenix) branch master updated: PHOENIX-7095 Implement Statement.closeOnCompletion() and fix related close() bugs
This is an automated email from the ASF dual-hosted git repository.
stoty pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push:
new b64a9736b7 PHOENIX-7095 Implement Statement.closeOnCompletion() and fix related close() bugs
b64a9736b7 is described below
commit b64a9736b78ae2d513669849986fad3bb5894279
Author: Istvan Toth <st...@apache.org>
AuthorDate: Thu Nov 2 09:52:07 2023 +0100
PHOENIX-7095 Implement Statement.closeOnCompletion() and fix related close() bugs
also set closeOnCompletion() on the statements generating metadata resultsets
---
.../phoenix/end2end/ClientHashAggregateIT.java | 38 +-
.../java/org/apache/phoenix/end2end/InListIT.java | 7 +-
.../phoenix/end2end/QueryDatabaseMetaDataIT.java | 50 ++
.../org/apache/phoenix/jdbc/PhoenixConnection.java | 45 +-
.../phoenix/jdbc/PhoenixDatabaseMetaData.java | 502 +++++++++++----------
.../org/apache/phoenix/jdbc/PhoenixResultSet.java | 7 +-
.../org/apache/phoenix/jdbc/PhoenixStatement.java | 54 ++-
.../apache/phoenix/jdbc/PhoenixStatementTest.java | 38 ++
8 files changed, 436 insertions(+), 305 deletions(-)
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientHashAggregateIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientHashAggregateIT.java
index b4b8ec731c..c071b7a836 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientHashAggregateIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ClientHashAggregateIT.java
@@ -41,13 +41,10 @@ public class ClientHashAggregateIT extends ParallelStatsDisabledIT {
public void testSalted() throws Exception {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
- Connection conn = DriverManager.getConnection(getUrl(), props);
-
- try {
+
+ try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
String table = createSalted(conn);
testTable(conn, table);
- } finally {
- conn.close();
}
}
@@ -168,36 +165,39 @@ public class ClientHashAggregateIT extends ParallelStatsDisabledIT {
String hashQuery = getQuery(table, true, swap, sort);
String sortQuery = getQuery(table, false, swap, sort);
Statement stmt = conn.createStatement();
- ResultSet hrs = stmt.executeQuery(hashQuery);
- ResultSet srs = stmt.executeQuery(sortQuery);
- try {
+ try (ResultSet hrs = stmt.executeQuery(hashQuery);) {
if (c1 > 0) {
assertTrue(hrs.next());
- assertTrue(srs.next());
- assertEquals(hrs.getInt("v1"), srs.getInt("v1"));
- assertEquals(hrs.getInt("v2"), srs.getInt("v2"));
- assertEquals(hrs.getInt("c"), srs.getInt("c"));
assertEquals(hrs.getInt("v1"), 1);
assertEquals(hrs.getInt("v2"), 2);
assertEquals(hrs.getInt("c"), c1);
}
if (c2 > 0) {
assertTrue(hrs.next());
- assertTrue(srs.next());
- assertEquals(hrs.getInt("v1"), srs.getInt("v1"));
- assertEquals(hrs.getInt("v2"), srs.getInt("v2"));
- assertEquals(hrs.getInt("c"), srs.getInt("c"));
assertEquals(hrs.getInt("v1"), 2);
assertEquals(hrs.getInt("v2"), 1);
assertEquals(hrs.getInt("c"), c2);
}
assertFalse(hrs.next());
+ }
+
+ try (ResultSet srs = stmt.executeQuery(sortQuery)) {
+ if (c1 > 0) {
+ assertTrue(srs.next());
+ assertEquals(srs.getInt("v1"), 1);
+ assertEquals(srs.getInt("v2"), 2);
+ assertEquals(srs.getInt("c"), c1);
+ }
+ if (c2 > 0) {
+ assertTrue(srs.next());
+ assertEquals(srs.getInt("v1"), 2);
+ assertEquals(srs.getInt("v2"), 1);
+ assertEquals(srs.getInt("c"), c2);
+ }
assertFalse(srs.next());
- } finally {
- hrs.close();
- srs.close();
}
+
}
private void dropTable(Connection conn, String table) throws Exception {
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
index 6728f6286e..d154ecdfd5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java
@@ -792,12 +792,13 @@ public class InListIT extends ParallelStatsDisabledIT {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM SYSTEM.CATALOG WHERE " +
"TENANT_ID IN ('', 'FOO')");
+ assertTrue(rs.next());
+ int result1 = rs.getInt(1);
+ assertEquals(0, result1);
ResultSet rs2 = stmt.executeQuery("SELECT COUNT(*) FROM SYSTEM.CATALOG WHERE " +
"TENANT_ID = '' OR TENANT_ID = 'FOO'");
- assertTrue(rs.next());
assertTrue(rs2.next());
- assertEquals(rs.getInt(1), rs2.getInt(1));
- assertEquals(0, rs.getInt(1));
+ assertEquals(result1, rs2.getInt(1));
}
}
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryDatabaseMetaDataIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryDatabaseMetaDataIT.java
index 89dbbde5ad..abdec219d4 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryDatabaseMetaDataIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryDatabaseMetaDataIT.java
@@ -139,6 +139,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(rs.getString("TABLE_NAME"), viewName);
assertEquals(PTableType.VIEW.toString(), rs.getString("TABLE_TYPE"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -163,6 +165,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(rs.getString(3), tableAName);
assertEquals(PTableType.TABLE.toString(), rs.getString(4));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getTables(null, null, null, null);
assertTrue(rs.next());
@@ -214,6 +218,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(PTableType.TABLE.toString(), rs.getString("TABLE_TYPE"));
assertEquals("false", rs.getString(PhoenixDatabaseMetaData.TRANSACTIONAL));
assertEquals(Boolean.FALSE, rs.getBoolean(PhoenixDatabaseMetaData.IS_NAMESPACE_MAPPED));
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getTables(null, tableCSchema, tableC, null);
assertTrue(rs.next());
@@ -227,6 +233,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(tableC, rs.getString("TABLE_NAME"));
assertEquals(PTableType.TABLE.toString(), rs.getString("TABLE_TYPE"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getTables(null, "", "%TABLE", new String[] { PTableType.TABLE.toString() });
assertTrue(rs.next());
@@ -238,6 +246,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(tableS, rs.getString("TABLE_NAME"));
assertEquals(PTableType.TABLE.toString(), rs.getString("TABLE_TYPE"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -257,6 +267,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals("VIEW", rs.getString(1));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -302,6 +314,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(schema4, rs.getString("TABLE_SCHEM"));
assertEquals(seq4, rs.getString("TABLE_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
String foo = generateUniqueName();
String basSchema = generateUniqueName();
@@ -345,6 +359,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(bas, rs.getString("TABLE_NAME"));
assertEquals(PTableType.TABLE.toString(), rs.getString("TABLE_TYPE"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs =
dbmd.getTables(null, "B%", null,
@@ -360,6 +376,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(seq3, rs.getString("TABLE_NAME"));
assertEquals(PhoenixDatabaseMetaData.SEQUENCE_TABLE_TYPE, rs.getString("TABLE_TYPE"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -433,12 +451,16 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(rs.getString(1), schema1);
assertEquals(rs.getString(2), null);
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getSchemas(null, "");
assertTrue(rs.next());
assertEquals(rs.getString(1), null);
assertEquals(rs.getString(2), null);
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getSchemas(null, null);
assertTrue(rs.next());
@@ -452,6 +474,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(schema1, rs.getString("TABLE_SCHEM"));
assertEquals(null, rs.getString("TABLE_CATALOG"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -538,6 +562,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(3, rs.getInt("DECIMAL_DIGITS"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
// Look up only columns in a column family
rs = dbmd.getColumns(null, "", table, "A.");
@@ -555,6 +581,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertTrue(rs.wasNull());
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
// Look up KV columns in a column family
rs = dbmd.getColumns("", "", table, "%.COL%");
@@ -621,6 +649,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(3, rs.getInt("DECIMAL_DIGITS"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
// Look up KV columns in a column family
rs = dbmd.getColumns("", "", table, "B.COL2");
@@ -630,6 +660,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(SchemaUtil.normalizeIdentifier("b"), rs.getString("COLUMN_FAMILY"));
assertEquals(SchemaUtil.normalizeIdentifier("col2"), rs.getString("COLUMN_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
String table2 = generateUniqueName();
ensureTableCreated(getUrl(), table2, TABLE_WITH_SALTING, null);
@@ -637,6 +669,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals(1, rs.getInt("ORDINAL_POSITION"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
@@ -660,6 +694,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(1, rs.getInt("KEY_SEQ"));
assertEquals(null, rs.getString("PK_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getPrimaryKeys(null, schema2, table2);
assertTrue(rs.next());
@@ -693,6 +729,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
// row
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
rs = dbmd.getColumns("", schema2, table2, null);
assertTrue(rs.next());
@@ -735,6 +773,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(SchemaUtil.normalizeIdentifier("key_prefix"), rs.getString("COLUMN_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
String table3 = generateUniqueName();
conn.createStatement().execute(
@@ -749,6 +789,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(1, rs.getInt("KEY_SEQ"));
assertEquals(null, rs.getString("PK_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -811,6 +853,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(SchemaUtil.normalizeIdentifier("b"), rs.getString("COLUMN_FAMILY"));
assertEquals(SchemaUtil.normalizeIdentifier("col5"), rs.getString("COLUMN_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -959,6 +1003,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertTrue(rs.next());
assertEquals(ViewType.MAPPED.name(), rs.getString(PhoenixDatabaseMetaData.VIEW_TYPE));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
String deleteStmt = "DELETE FROM " + tableName;
PreparedStatement ps = pconn.prepareStatement(deleteStmt);
@@ -1172,6 +1218,8 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
assertEquals(schema1, rs.getString("TABLE_SCHEM"));
assertEquals(table1, rs.getString("TABLE_NAME"));
assertFalse(rs.next());
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
@@ -1201,5 +1249,7 @@ public class QueryDatabaseMetaDataIT extends ParallelStatsDisabledIT {
}
}
assertTrue("Could not find REMARKS column", foundRemarksColumn);
+ rs.close();
+ assertTrue(rs.getStatement().isClosed());
}
}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index 3c078d6ecc..c7afcb0b9e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -50,13 +50,12 @@ import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.text.Format;
-
-
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.HashMap;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -120,6 +119,11 @@ import org.apache.phoenix.schema.types.PUnsignedDate;
import org.apache.phoenix.schema.types.PUnsignedTime;
import org.apache.phoenix.schema.types.PUnsignedTimestamp;
import org.apache.phoenix.schema.types.PVarbinary;
+import org.apache.phoenix.thirdparty.com.google.common.annotations.VisibleForTesting;
+import org.apache.phoenix.thirdparty.com.google.common.base.Objects;
+import org.apache.phoenix.thirdparty.com.google.common.base.Strings;
+import org.apache.phoenix.thirdparty.com.google.common.collect.ImmutableMap;
+import org.apache.phoenix.thirdparty.com.google.common.collect.ImmutableMap.Builder;
import org.apache.phoenix.trace.util.Tracing;
import org.apache.phoenix.transaction.PhoenixTransactionContext;
import org.apache.phoenix.util.DateUtil;
@@ -134,13 +138,6 @@ import org.apache.phoenix.util.SQLCloseables;
import org.apache.phoenix.util.SchemaUtil;
import org.apache.phoenix.util.VarBinaryFormatter;
-import org.apache.phoenix.thirdparty.com.google.common.annotations.VisibleForTesting;
-import org.apache.phoenix.thirdparty.com.google.common.base.Objects;
-import org.apache.phoenix.thirdparty.com.google.common.base.Strings;
-import org.apache.phoenix.thirdparty.com.google.common.collect.ImmutableMap;
-import org.apache.phoenix.thirdparty.com.google.common.collect.ImmutableMap.Builder;
-import org.apache.phoenix.thirdparty.com.google.common.collect.Lists;
-
/**
*
* JDBC Connection implementation of Phoenix. Currently the following are
@@ -162,7 +159,7 @@ public class PhoenixConnection implements MetaDataMutated, SQLCloseable, Phoenix
private final Long scn;
private final boolean buildingIndex;
private MutationState mutationState;
- private List<PhoenixStatement> statements = new ArrayList<>();
+ private HashSet<PhoenixStatement> statements = new HashSet<>();
private boolean isAutoFlush = false;
private boolean isAutoCommit = false;
private final PName tenantId;
@@ -720,17 +717,17 @@ public class PhoenixConnection implements MetaDataMutated, SQLCloseable, Phoenix
}
private void closeStatements() throws SQLException {
- List<? extends PhoenixStatement> statements = this.statements;
- // create new list to prevent close of statements
- // from modifying this list.
- this.statements = Lists.newArrayList();
try {
mutationState.rollback();
} catch (SQLException e) {
// ignore any exceptions while rolling back
} finally {
try {
- SQLCloseables.closeAll(statements);
+ // create new set to prevent close of statements from modifying this collection.
+ // TODO This could be optimized out by decoupling closing the stmt and removing it
+ // from the connection.
+ HashSet<? extends PhoenixStatement> statementsCopy = new HashSet<>(this.statements);
+ SQLCloseables.closeAll(statementsCopy);
} finally {
statements.clear();
}
@@ -784,15 +781,13 @@ public class PhoenixConnection implements MetaDataMutated, SQLCloseable, Phoenix
clearMetrics();
}
try {
- if (traceScope != null) {
- traceScope.close();
- }
closeStatements();
if (childConnections != null) {
SQLCloseables.closeAllQuietly(childConnections);
}
-
-
+ if (traceScope != null) {
+ traceScope.close();
+ }
} finally {
services.removeConnection(this);
}
@@ -868,10 +863,6 @@ public class PhoenixConnection implements MetaDataMutated, SQLCloseable, Phoenix
throw new SQLFeatureNotSupportedException();
}
- public List<PhoenixStatement> getStatements() {
- return statements;
- }
-
@Override
public Statement createStatement() throws SQLException {
checkOpen();
@@ -1360,20 +1351,24 @@ public class PhoenixConnection implements MetaDataMutated, SQLCloseable, Phoenix
this.traceScope = traceScope;
}
+ @Override
public Map<String, Map<MetricType, Long>> getMutationMetrics() {
return mutationState.getMutationMetricQueue().aggregate();
}
+ @Override
public Map<String, Map<MetricType, Long>> getReadMetrics() {
return mutationState.getReadMetricQueue() != null ? mutationState
.getReadMetricQueue().aggregate() : Collections
.<String, Map<MetricType, Long>> emptyMap();
}
+ @Override
public boolean isRequestLevelMetricsEnabled() {
return isRequestLevelMetricsEnabled;
}
+ @Override
public void clearMetrics() {
mutationState.getMutationMetricQueue().clearMetrics();
if (mutationState.getReadMetricQueue() != null) {
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
index ada35cc3e2..112cabd7d2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java
@@ -362,7 +362,6 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
public static final byte[] SYSTEM_MUTEX_FAMILY_NAME_BYTES = TABLE_FAMILY_BYTES;
private final PhoenixConnection connection;
- private final ResultSet emptyResultSet;
public static final int MAX_LOCAL_SI_VERSION_DISALLOW = VersionUtil.encodeVersion("0", "98", "8");
public static final int MIN_LOCAL_SI_VERSION_DISALLOW = VersionUtil.encodeVersion("0", "98", "6");
@@ -455,10 +454,15 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
PhoenixDatabaseMetaData(PhoenixConnection connection) throws SQLException {
- this.emptyResultSet = new PhoenixResultSet(ResultIterator.EMPTY_ITERATOR, RowProjector.EMPTY_PROJECTOR, new StatementContext(new PhoenixStatement(connection), false));
this.connection = connection;
}
+ private PhoenixResultSet getEmptyResultSet() throws SQLException {
+ PhoenixStatement stmt = new PhoenixStatement(connection);
+ stmt.closeOnCompletion();
+ return new PhoenixResultSet(ResultIterator.EMPTY_ITERATOR, RowProjector.EMPTY_PROJECTOR, new StatementContext(stmt, false));
+ }
+
@Override
public boolean allProceduresAreCallable() throws SQLException {
return false;
@@ -497,13 +501,13 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern,
String attributeNamePattern) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable)
throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -519,18 +523,19 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getCatalogs() throws SQLException {
PreparedStatement stmt = QueryUtil.getCatalogsStmt(connection);
+ stmt.closeOnCompletion();
return stmt.executeQuery();
}
@Override
public ResultSet getClientInfoProperties() throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern)
throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
public static final String GLOBAL_TENANANTS_ONLY = "null";
@@ -776,177 +781,180 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
}
}
}
- ResultSet rs = getTables(catalog, schemaPattern, tableNamePattern, null);
- while (rs.next()) {
- String schemaName = rs.getString(TABLE_SCHEM);
- String tableName = rs.getString(TABLE_NAME);
- String tenantId = rs.getString(TABLE_CAT);
- String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
- PTable table = PhoenixRuntime.getTableNoCache(connection, fullTableName);
- boolean isSalted = table.getBucketNum()!=null;
- boolean tenantColSkipped = false;
- List<PColumn> columns = table.getColumns();
- int startOffset = isSalted ? 1 : 0;
- columns = Lists.newArrayList(columns.subList(startOffset, columns.size()));
- for (PColumn column : columns) {
- if (isTenantSpecificConnection && column.equals(table.getPKColumns().get(startOffset))) {
- // skip the tenant column
- tenantColSkipped = true;
- continue;
- }
- String columnFamily = column.getFamilyName()!=null ? column.getFamilyName().getString() : null;
- String columnName = column.getName().getString();
- if (cfPattern != null && cfPattern.length() > 0) { // if null or empty, will pick up all columns
- if (columnFamily==null || !match(columnFamily, cfPattern)) {
+ try (ResultSet rs = getTables(catalog, schemaPattern, tableNamePattern, null)) {
+ while (rs.next()) {
+ String schemaName = rs.getString(TABLE_SCHEM);
+ String tableName = rs.getString(TABLE_NAME);
+ String tenantId = rs.getString(TABLE_CAT);
+ String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
+ PTable table = PhoenixRuntime.getTableNoCache(connection, fullTableName);
+ boolean isSalted = table.getBucketNum()!=null;
+ boolean tenantColSkipped = false;
+ List<PColumn> columns = table.getColumns();
+ int startOffset = isSalted ? 1 : 0;
+ columns = Lists.newArrayList(columns.subList(startOffset, columns.size()));
+ for (PColumn column : columns) {
+ if (isTenantSpecificConnection && column.equals(table.getPKColumns().get(startOffset))) {
+ // skip the tenant column
+ tenantColSkipped = true;
continue;
}
- }
- if (colPattern != null && colPattern.length() > 0) {
- if (!match(columnName, colPattern)) {
- continue;
+ String columnFamily = column.getFamilyName()!=null ? column.getFamilyName().getString() : null;
+ String columnName = column.getName().getString();
+ if (cfPattern != null && cfPattern.length() > 0) { // if null or empty, will pick up all columns
+ if (columnFamily==null || !match(columnFamily, cfPattern)) {
+ continue;
+ }
}
- }
- // generate row key
- // TENANT_ID, TABLE_SCHEM, TABLE_NAME , COLUMN_NAME are row key columns
- byte[] rowKey =
- SchemaUtil.getColumnKey(tenantId, schemaName, tableName, columnName, null);
-
- // add one cell for each column info
- List<Cell> cells = Lists.newArrayListWithCapacity(25);
- // DATA_TYPE
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- DATA_TYPE_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PInteger.INSTANCE.toBytes(column.getDataType().getResultSetSqlType())));
- // TYPE_NAME
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(TYPE_NAME), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- column.getDataType().getSqlTypeNameBytes()));
- // COLUMN_SIZE
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, COLUMN_SIZE_BYTES,
+ if (colPattern != null && colPattern.length() > 0) {
+ if (!match(columnName, colPattern)) {
+ continue;
+ }
+ }
+ // generate row key
+ // TENANT_ID, TABLE_SCHEM, TABLE_NAME , COLUMN_NAME are row key columns
+ byte[] rowKey =
+ SchemaUtil.getColumnKey(tenantId, schemaName, tableName, columnName, null);
+
+ // add one cell for each column info
+ List<Cell> cells = Lists.newArrayListWithCapacity(25);
+ // DATA_TYPE
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ DATA_TYPE_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ PInteger.INSTANCE.toBytes(column.getDataType().getResultSetSqlType())));
+ // TYPE_NAME
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(TYPE_NAME), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ column.getDataType().getSqlTypeNameBytes()));
+ // COLUMN_SIZE
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, COLUMN_SIZE_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ column.getMaxLength() != null
+ ? PInteger.INSTANCE.toBytes(column.getMaxLength())
+ : ByteUtil.EMPTY_BYTE_ARRAY));
+ // BUFFER_LENGTH
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(BUFFER_LENGTH), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // DECIMAL_DIGITS
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ DECIMAL_DIGITS_BYTES,
MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- column.getMaxLength() != null
- ? PInteger.INSTANCE.toBytes(column.getMaxLength())
+ column.getScale() != null ? PInteger.INSTANCE.toBytes(column.getScale())
: ByteUtil.EMPTY_BYTE_ARRAY));
- // BUFFER_LENGTH
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(BUFFER_LENGTH), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // DECIMAL_DIGITS
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- DECIMAL_DIGITS_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- column.getScale() != null ? PInteger.INSTANCE.toBytes(column.getScale())
- : ByteUtil.EMPTY_BYTE_ARRAY));
- // NUM_PREC_RADIX
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(NUM_PREC_RADIX), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // NULLABLE
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- NULLABLE_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PInteger.INSTANCE.toBytes(SchemaUtil.getIsNullableInt(column.isNullable()))));
- // REMARKS
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(REMARKS),
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, ByteUtil.EMPTY_BYTE_ARRAY));
- // COLUMN_DEF
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(COLUMN_DEF),
+ // NUM_PREC_RADIX
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(NUM_PREC_RADIX), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // NULLABLE
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ NULLABLE_BYTES,
MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PVarchar.INSTANCE.toBytes(column.getExpressionStr())));
- // SQL_DATA_TYPE
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(SQL_DATA_TYPE), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // SQL_DATETIME_SUB
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(SQL_DATETIME_SUB), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // CHAR_OCTET_LENGTH
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(CHAR_OCTET_LENGTH), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // ORDINAL_POSITION
- int ordinal =
- column.getPosition() + (isSalted ? 0 : 1) - (tenantColSkipped ? 1 : 0);
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- ORDINAL_POSITION_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, PInteger.INSTANCE.toBytes(ordinal)));
- String isNullable =
- column.isNullable() ? Boolean.TRUE.toString() : Boolean.FALSE.toString();
- // IS_NULLABLE
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(IS_NULLABLE), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PVarchar.INSTANCE.toBytes(isNullable)));
- // SCOPE_CATALOG
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(SCOPE_CATALOG), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // SCOPE_SCHEMA
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(SCOPE_SCHEMA), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // SCOPE_TABLE
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(SCOPE_TABLE),
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, ByteUtil.EMPTY_BYTE_ARRAY));
- // SOURCE_DATA_TYPE
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(SOURCE_DATA_TYPE), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // IS_AUTOINCREMENT
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(IS_AUTOINCREMENT), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- ByteUtil.EMPTY_BYTE_ARRAY));
- // ARRAY_SIZE
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, ARRAY_SIZE_BYTES,
+ PInteger.INSTANCE.toBytes(SchemaUtil.getIsNullableInt(column.isNullable()))));
+ // REMARKS
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(REMARKS),
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, ByteUtil.EMPTY_BYTE_ARRAY));
+ // COLUMN_DEF
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(COLUMN_DEF),
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ PVarchar.INSTANCE.toBytes(column.getExpressionStr())));
+ // SQL_DATA_TYPE
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(SQL_DATA_TYPE), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // SQL_DATETIME_SUB
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(SQL_DATETIME_SUB), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // CHAR_OCTET_LENGTH
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(CHAR_OCTET_LENGTH), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // ORDINAL_POSITION
+ int ordinal =
+ column.getPosition() + (isSalted ? 0 : 1) - (tenantColSkipped ? 1 : 0);
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ ORDINAL_POSITION_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, PInteger.INSTANCE.toBytes(ordinal)));
+ String isNullable =
+ column.isNullable() ? Boolean.TRUE.toString() : Boolean.FALSE.toString();
+ // IS_NULLABLE
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(IS_NULLABLE), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ PVarchar.INSTANCE.toBytes(isNullable)));
+ // SCOPE_CATALOG
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(SCOPE_CATALOG), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // SCOPE_SCHEMA
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(SCOPE_SCHEMA), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // SCOPE_TABLE
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(SCOPE_TABLE),
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, ByteUtil.EMPTY_BYTE_ARRAY));
+ // SOURCE_DATA_TYPE
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(SOURCE_DATA_TYPE), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // IS_AUTOINCREMENT
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(IS_AUTOINCREMENT), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ ByteUtil.EMPTY_BYTE_ARRAY));
+ // ARRAY_SIZE
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, ARRAY_SIZE_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ column.getArraySize() != null
+ ? PInteger.INSTANCE.toBytes(column.getArraySize())
+ : ByteUtil.EMPTY_BYTE_ARRAY));
+ // COLUMN_FAMILY
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ COLUMN_FAMILY_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, column.getFamilyName() != null
+ ? column.getFamilyName().getBytes() : ByteUtil.EMPTY_BYTE_ARRAY));
+ // TYPE_ID
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(TYPE_ID), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ PInteger.INSTANCE.toBytes(column.getDataType().getSqlType())));
+ // VIEW_CONSTANT
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ VIEW_CONSTANT_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, column.getViewConstant() != null
+ ? column.getViewConstant() : ByteUtil.EMPTY_BYTE_ARRAY));
+ // MULTI_TENANT
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ MULTI_TENANT_BYTES,
MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- column.getArraySize() != null
- ? PInteger.INSTANCE.toBytes(column.getArraySize())
- : ByteUtil.EMPTY_BYTE_ARRAY));
- // COLUMN_FAMILY
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- COLUMN_FAMILY_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, column.getFamilyName() != null
- ? column.getFamilyName().getBytes() : ByteUtil.EMPTY_BYTE_ARRAY));
- // TYPE_ID
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(TYPE_ID), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PInteger.INSTANCE.toBytes(column.getDataType().getSqlType())));
- // VIEW_CONSTANT
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- VIEW_CONSTANT_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, column.getViewConstant() != null
- ? column.getViewConstant() : ByteUtil.EMPTY_BYTE_ARRAY));
- // MULTI_TENANT
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- MULTI_TENANT_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PBoolean.INSTANCE.toBytes(table.isMultiTenant())));
- // KEY_SEQ_COLUMN
- byte[] keySeqBytes = ByteUtil.EMPTY_BYTE_ARRAY;
- int pkPos = table.getPKColumns().indexOf(column);
- if (pkPos!=-1) {
- short keySeq = (short) (pkPos + 1 - startOffset - (tenantColSkipped ? 1 : 0));
- keySeqBytes = PSmallint.INSTANCE.toBytes(keySeq);
+ PBoolean.INSTANCE.toBytes(table.isMultiTenant())));
+ // KEY_SEQ_COLUMN
+ byte[] keySeqBytes = ByteUtil.EMPTY_BYTE_ARRAY;
+ int pkPos = table.getPKColumns().indexOf(column);
+ if (pkPos!=-1) {
+ short keySeq = (short) (pkPos + 1 - startOffset - (tenantColSkipped ? 1 : 0));
+ keySeqBytes = PSmallint.INSTANCE.toBytes(keySeq);
+ }
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, KEY_SEQ_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, keySeqBytes));
+ Collections.sort(cells, new CellComparatorImpl());
+ Tuple tuple = new MultiKeyValueTuple(cells);
+ tuples.add(tuple);
}
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, KEY_SEQ_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, keySeqBytes));
- Collections.sort(cells, new CellComparatorImpl());
- Tuple tuple = new MultiKeyValueTuple(cells);
- tuples.add(tuple);
}
}
- return new PhoenixResultSet(new MaterializedResultIterator(tuples), GET_COLUMNS_ROW_PROJECTOR, new StatementContext(new PhoenixStatement(connection), false));
+ PhoenixStatement stmt = new PhoenixStatement(connection);
+ stmt.closeOnCompletion();
+ return new PhoenixResultSet(new MaterializedResultIterator(tuples), GET_COLUMNS_ROW_PROJECTOR, new StatementContext(stmt, false));
} finally {
if (connection.getAutoCommit()) {
connection.commit();
@@ -962,7 +970,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable,
String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1012,7 +1020,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1023,12 +1031,12 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern,
String columnNamePattern) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1038,7 +1046,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1046,7 +1054,10 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
boolean approximate) throws SQLException {
PreparedStatement stmt = QueryUtil.getIndexInfoStmt(connection, catalog, schema, table,
unique, approximate);
- if (stmt == null) return emptyResultSet;
+ if (stmt == null) {
+ return getEmptyResultSet();
+ }
+ stmt.closeOnCompletion();
return stmt.executeQuery();
}
@@ -1169,7 +1180,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
public ResultSet getPrimaryKeys(String catalog, String schemaName, String tableName)
throws SQLException {
if (tableName == null || tableName.length() == 0) {
- return emptyResultSet;
+ return getEmptyResultSet();
}
String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
PTable table = PhoenixRuntime.getTableNoCache(connection, fullTableName);
@@ -1187,68 +1198,72 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
try {
List<Tuple> tuples = Lists.newArrayListWithExpectedSize(10);
- ResultSet rs = getTables(catalog, schemaName, tableName, null);
- while (rs.next()) {
- String tenantId = rs.getString(TABLE_CAT);
- for (PColumn column : sorderPkColumns) {
- String columnName = column.getName().getString();
- // generate row key
- // TENANT_ID, TABLE_SCHEM, TABLE_NAME , COLUMN_NAME are row key columns
- byte[] rowKey =
- SchemaUtil.getColumnKey(tenantId, schemaName, tableName, columnName, null);
-
- // add one cell for each column info
- List<Cell> cells = Lists.newArrayListWithCapacity(8);
- // KEY_SEQ_COLUMN
- byte[] keySeqBytes = ByteUtil.EMPTY_BYTE_ARRAY;
- int pkPos = pkColumns.indexOf(column);
- if (pkPos != -1) {
- short keySeq =
- (short) (pkPos + 1 - (isSalted ? 1 : 0) - (tenantColSkipped ? 1 : 0));
- keySeqBytes = PSmallint.INSTANCE.toBytes(keySeq);
- }
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, KEY_SEQ_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, keySeqBytes));
- // PK_NAME
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, PK_NAME_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, table.getPKName() != null
- ? table.getPKName().getBytes() : ByteUtil.EMPTY_BYTE_ARRAY));
- // ASC_OR_DESC
- char sortOrder = column.getSortOrder() == SortOrder.ASC ? 'A' : 'D';
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- ASC_OR_DESC_BYTES, MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- Bytes.toBytes(sortOrder)));
- // DATA_TYPE
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, DATA_TYPE_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PInteger.INSTANCE.toBytes(column.getDataType().getResultSetSqlType())));
- // TYPE_NAME
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(TYPE_NAME), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- column.getDataType().getSqlTypeNameBytes()));
- // COLUMN_SIZE
- cells.add(
- PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, COLUMN_SIZE_BYTES,
+ try (ResultSet rs = getTables(catalog, schemaName, tableName, null)) {
+ while (rs.next()) {
+ String tenantId = rs.getString(TABLE_CAT);
+ for (PColumn column : sorderPkColumns) {
+ String columnName = column.getName().getString();
+ // generate row key
+ // TENANT_ID, TABLE_SCHEM, TABLE_NAME , COLUMN_NAME are row key columns
+ byte[] rowKey =
+ SchemaUtil.getColumnKey(tenantId, schemaName, tableName, columnName, null);
+
+ // add one cell for each column info
+ List<Cell> cells = Lists.newArrayListWithCapacity(8);
+ // KEY_SEQ_COLUMN
+ byte[] keySeqBytes = ByteUtil.EMPTY_BYTE_ARRAY;
+ int pkPos = pkColumns.indexOf(column);
+ if (pkPos != -1) {
+ short keySeq =
+ (short) (pkPos + 1 - (isSalted ? 1 : 0) - (tenantColSkipped ? 1 : 0));
+ keySeqBytes = PSmallint.INSTANCE.toBytes(keySeq);
+ }
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, KEY_SEQ_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, keySeqBytes));
+ // PK_NAME
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, PK_NAME_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, table.getPKName() != null
+ ? table.getPKName().getBytes() : ByteUtil.EMPTY_BYTE_ARRAY));
+ // ASC_OR_DESC
+ char sortOrder = column.getSortOrder() == SortOrder.ASC ? 'A' : 'D';
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ ASC_OR_DESC_BYTES, MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ Bytes.toBytes(sortOrder)));
+ // DATA_TYPE
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, DATA_TYPE_BYTES,
MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- column.getMaxLength() != null
- ? PInteger.INSTANCE.toBytes(column.getMaxLength())
- : ByteUtil.EMPTY_BYTE_ARRAY));
- // TYPE_ID
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
- Bytes.toBytes(TYPE_ID), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
- PInteger.INSTANCE.toBytes(column.getDataType().getSqlType())));
- // VIEW_CONSTANT
- cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, VIEW_CONSTANT_BYTES,
- MetaDataProtocol.MIN_TABLE_TIMESTAMP, column.getViewConstant() != null
- ? column.getViewConstant() : ByteUtil.EMPTY_BYTE_ARRAY));
- Collections.sort(cells, new CellComparatorImpl());
- Tuple tuple = new MultiKeyValueTuple(cells);
- tuples.add(tuple);
+ PInteger.INSTANCE.toBytes(column.getDataType().getResultSetSqlType())));
+ // TYPE_NAME
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(TYPE_NAME), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ column.getDataType().getSqlTypeNameBytes()));
+ // COLUMN_SIZE
+ cells.add(
+ PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, COLUMN_SIZE_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ column.getMaxLength() != null
+ ? PInteger.INSTANCE.toBytes(column.getMaxLength())
+ : ByteUtil.EMPTY_BYTE_ARRAY));
+ // TYPE_ID
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES,
+ Bytes.toBytes(TYPE_ID), MetaDataProtocol.MIN_TABLE_TIMESTAMP,
+ PInteger.INSTANCE.toBytes(column.getDataType().getSqlType())));
+ // VIEW_CONSTANT
+ cells.add(PhoenixKeyValueUtil.newKeyValue(rowKey, TABLE_FAMILY_BYTES, VIEW_CONSTANT_BYTES,
+ MetaDataProtocol.MIN_TABLE_TIMESTAMP, column.getViewConstant() != null
+ ? column.getViewConstant() : ByteUtil.EMPTY_BYTE_ARRAY));
+ Collections.sort(cells, new CellComparatorImpl());
+ Tuple tuple = new MultiKeyValueTuple(cells);
+ tuples.add(tuple);
+ }
}
}
+
+ PhoenixStatement stmt = new PhoenixStatement(connection);
+ stmt.closeOnCompletion();
return new PhoenixResultSet(new MaterializedResultIterator(tuples),
GET_PRIMARY_KEYS_ROW_PROJECTOR,
- new StatementContext(new PhoenixStatement(connection), false));
+ new StatementContext(stmt, false));
} finally {
if (connection.getAutoCommit()) {
connection.commit();
@@ -1259,7 +1274,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern,
String columnNamePattern) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1270,7 +1285,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern)
throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1306,6 +1321,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
PreparedStatement stmt = QueryUtil.getSchemasStmt(connection, catalog, schemaPattern);
+ stmt.closeOnCompletion();
return stmt.executeQuery();
}
@@ -1325,12 +1341,13 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
throws SQLException {
PreparedStatement stmt = QueryUtil.getSuperTablesStmt(connection, catalog, schemaPattern,
tableNamePattern);
+ stmt.closeOnCompletion();
return stmt.executeQuery();
}
@Override
public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1341,7 +1358,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern)
throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
private static final PDatum TABLE_TYPE_DATUM = new PDatum() {
@@ -1390,7 +1407,9 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
*/
@Override
public ResultSet getTableTypes() throws SQLException {
- return new PhoenixResultSet(new MaterializedResultIterator(TABLE_TYPE_TUPLES), TABLE_TYPE_ROW_PROJECTOR, new StatementContext(new PhoenixStatement(connection), false));
+ PhoenixStatement stmt = new PhoenixStatement(connection);
+ stmt.closeOnCompletion();
+ return new PhoenixResultSet(new MaterializedResultIterator(TABLE_TYPE_TUPLES), TABLE_TYPE_ROW_PROJECTOR, new StatementContext(stmt, false));
}
@Override
@@ -1398,7 +1417,10 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
String[] types) throws SQLException {
PreparedStatement stmt = QueryUtil.getTablesStmt(connection, catalog, schemaPattern,
tableNamePattern, types);
- if (stmt == null) return emptyResultSet;
+ if (stmt == null) {
+ return getEmptyResultSet();
+ }
+ stmt.closeOnCompletion();
return stmt.executeQuery();
}
@@ -1409,13 +1431,13 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getTypeInfo() throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types)
throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1431,7 +1453,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException {
- return emptyResultSet;
+ return getEmptyResultSet();
}
@Override
@@ -1911,7 +1933,7 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData {
@Override
public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern,
String columnNamePattern) throws SQLException {
- return this.emptyResultSet;
+ return getEmptyResultSet();
}
@Override
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
index d01e88b88b..8e91d90447 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixResultSet.java
@@ -173,6 +173,7 @@ public class PhoenixResultSet implements PhoenixMonitoredResultSet, SQLCloseable
this.scanner = resultIterator;
this.context = ctx;
this.statement = context.getStatement();
+ statement.setLastResultSet(this);
this.readMetricsQueue = context.getReadMetricsQueue();
this.overAllQueryMetrics = context.getOverallQueryMetrics();
this.queryLogger = context.getQueryLogger() != null ? context.getQueryLogger() : QueryLogger.NO_OP_INSTANCE;
@@ -217,12 +218,14 @@ public class PhoenixResultSet implements PhoenixMonitoredResultSet, SQLCloseable
@Override
public void close() throws SQLException {
- if (isClosed) { return; }
+ if (isClosed) {
+ return;
+ }
try {
scanner.close();
} finally {
isClosed = true;
- statement.getResultSets().remove(this);
+ statement.removeResultSet(this);
overAllQueryMetrics.endQuery();
overAllQueryMetrics.stopResultSetWatch();
if (context.getCurrentTable() != null && context.getCurrentTable().getTable() != null
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index 145d08c1ce..d3df464709 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -218,7 +218,6 @@ import org.apache.phoenix.util.PhoenixContextExecutor;
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.SQLCloseable;
-import org.apache.phoenix.util.SQLCloseables;
import org.apache.phoenix.util.ServerUtil;
import org.apache.phoenix.util.ParseNodeUtil.RewriteResult;
import org.slf4j.Logger;
@@ -278,7 +277,6 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
protected final PhoenixConnection connection;
private static final int NO_UPDATE = -1;
private static final String TABLE_UNKNOWN = "";
- private List<PhoenixResultSet> resultSets = new ArrayList<PhoenixResultSet>();
private QueryPlan lastQueryPlan;
private PhoenixResultSet lastResultSet;
private int lastUpdateCount = NO_UPDATE;
@@ -286,6 +284,7 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
private String lastUpdateTable = TABLE_UNKNOWN;
private Operation lastUpdateOperation;
private boolean isClosed = false;
+ private boolean closeOnCompletion = false;
private int maxRows;
private int fetchSize = -1;
private int queryTimeoutMillis;
@@ -307,7 +306,11 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
}
protected List<PhoenixResultSet> getResultSets() {
- return resultSets;
+ if (lastResultSet != null) {
+ return Collections.singletonList(lastResultSet);
+ } else {
+ return Collections.emptyList();
+ }
}
public PhoenixResultSet newResultSet(ResultIterator iterator, RowProjector projector, StatementContext context) throws SQLException {
@@ -341,6 +344,7 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
boolean success = false;
boolean pointLookup = false;
String tableName = null;
+ clearResultSet();
PhoenixResultSet rs = null;
try {
PhoenixConnection conn = getConnection();
@@ -397,9 +401,8 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
rs =
newResultSet(resultIterator, plan.getProjector(),
plan.getContext());
- resultSets.add(rs);
+ // newResultset sets lastResultset
setLastQueryPlan(plan);
- setLastResultSet(rs);
setLastUpdateCount(NO_UPDATE);
setLastUpdateTable(tableName == null ? TABLE_UNKNOWN : tableName);
setLastUpdateOperation(stmt.getOperation());
@@ -538,6 +541,7 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
MutationState state = null;
MutationPlan plan = null;
final long startExecuteMutationTime = EnvironmentEdgeManager.currentTimeMillis();
+ clearResultSet();
try {
PhoenixConnection conn = getConnection();
if (conn.getQueryServices().isUpgradeRequired() && !conn.isRunningUpgrade()
@@ -566,7 +570,6 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
if (connection.getAutoCommit()) {
connection.commit();
}
- setLastResultSet(null);
setLastQueryPlan(null);
// Unfortunately, JDBC uses an int for update count, so we
// just max out at Integer.MAX_VALUE
@@ -2077,12 +2080,7 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
@Override
public void close() throws SQLException {
try {
- List<PhoenixResultSet> resultSets = this.resultSets;
- // Create new list so that remove of the PhoenixResultSet
- // during closeAll doesn't needless do a linear search
- // on this list.
- this.resultSets = Lists.newArrayList();
- SQLCloseables.closeAll(resultSets);
+ clearResultSet();
} finally {
try {
connection.removeStatement(this);
@@ -2092,6 +2090,30 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
}
}
+ // From the ResultSet javadoc:
+ // A ResultSet object is automatically closed when the Statement object that generated it is
+ // closed, re-executed, or used to retrieve the next result from a sequence of multiple results.
+ private void clearResultSet() throws SQLException {
+ if (lastResultSet != null) {
+ try {
+ lastResultSet.close();
+ } finally {
+ lastResultSet = null;
+ }
+ }
+ }
+
+ // Called from ResultSet.close(). rs is already closed.
+ // We use a separate function to avoid calling close() again
+ void removeResultSet(ResultSet rs) throws SQLException {
+ if (rs == lastResultSet) {
+ lastResultSet = null;
+ if (closeOnCompletion) {
+ this.close();
+ }
+ }
+ }
+
public List<Object> getParameters() {
return Collections.<Object>emptyList();
}
@@ -2321,7 +2343,6 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
@Override
public ResultSet getResultSet() throws SQLException {
ResultSet rs = getLastResultSet();
- setLastResultSet(null);
return rs;
}
@@ -2341,6 +2362,7 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
return ResultSet.TYPE_FORWARD_ONLY;
}
+ @Override
public Operation getUpdateOperation() {
return getLastUpdateOperation();
}
@@ -2468,19 +2490,19 @@ public class PhoenixStatement implements PhoenixMonitoredStatement, SQLCloseable
@Override
public void closeOnCompletion() throws SQLException {
- throw new SQLFeatureNotSupportedException();
+ closeOnCompletion = true;
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
- throw new SQLFeatureNotSupportedException();
+ return closeOnCompletion;
}
private PhoenixResultSet getLastResultSet() {
return lastResultSet;
}
- private void setLastResultSet(PhoenixResultSet lastResultSet) {
+ void setLastResultSet(PhoenixResultSet lastResultSet) {
this.lastResultSet = lastResultSet;
}
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixStatementTest.java b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixStatementTest.java
index 2af97b73f8..882221354c 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixStatementTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixStatementTest.java
@@ -20,6 +20,7 @@ package org.apache.phoenix.jdbc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
@@ -213,4 +214,41 @@ public class PhoenixStatementTest extends BaseConnectionlessQueryTest {
assertNull(ex.getUpdateCounts());
}
+ @Test
+ public void testRecursiveClose() throws SQLException {
+ Connection connection = DriverManager.getConnection(getUrl());
+ Statement stmt1 = connection.createStatement();
+ ResultSet rs11 = stmt1.executeQuery("select * from atable");
+ rs11.close();
+ assertTrue(rs11.isClosed());
+ ResultSet rs12 = stmt1.executeQuery("select * from atable");
+ stmt1.close();
+ assertTrue(stmt1.isClosed());
+ assertTrue(rs12.isClosed());
+
+ Statement stmt2 = connection.createStatement();
+ stmt2.closeOnCompletion();
+ ResultSet rs21 = stmt2.executeQuery("select * from atable");
+ rs21.close();
+ assertTrue(stmt2.isClosed());
+
+ Statement stmt3 = connection.createStatement();
+ ResultSet rs31 = stmt3.executeQuery("select * from atable");
+ stmt3.executeUpdate("upsert into ATABLE VALUES ('1', '2', '3')");
+ assertTrue(rs31.isClosed());
+ ResultSet rs32 = stmt3.executeQuery("select * from atable");
+ ResultSet rs33 = stmt3.executeQuery("select * from atable");
+ assertTrue(rs32.isClosed());
+
+ Statement stmt4 = connection.createStatement();
+ Statement stmt5 = connection.createStatement();
+ ResultSet rs41 = stmt3.executeQuery("select * from atable");
+ ResultSet rs51 = stmt3.executeQuery("select * from atable");
+ connection.close();
+ assertTrue(connection.isClosed());
+ assertTrue(stmt4.isClosed());
+ assertTrue(stmt5.isClosed());
+ assertTrue(rs41.isClosed());
+ assertTrue(rs51.isClosed());
+ }
}