You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2016/06/17 23:22:34 UTC
[1/3] lucene-solr:branch_5_5: SOLR-8612: closing JDBC Statement on
exceptions from JdbcDataSource in DataImportHandler aka DIH (Kristine Jetzke
via Mikhail Khludnev)
Repository: lucene-solr
Updated Branches:
refs/heads/branch_5_5 845f57d16 -> 0cd5356a7
refs/heads/branch_5x b4d81f784 -> 66dd9bc63
refs/heads/branch_6_0 7b79571d9 -> 7e2252cbc
SOLR-8612: closing JDBC Statement on exceptions from JdbcDataSource in DataImportHandler aka DIH (Kristine Jetzke via Mikhail Khludnev)
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/0cd5356a
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/0cd5356a
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/0cd5356a
Branch: refs/heads/branch_5_5
Commit: 0cd5356a7305be90f7817bd00906e09a5ef2d736
Parents: 845f57d
Author: Mikhail Khludnev <mk...@apache.org>
Authored: Thu Jun 2 22:53:15 2016 +0300
Committer: Steve Rowe <sa...@apache.org>
Committed: Fri Jun 17 19:16:39 2016 -0400
----------------------------------------------------------------------
solr/CHANGES.txt | 2 +
.../solr/handler/dataimport/JdbcDataSource.java | 117 +++++++++----
.../handler/dataimport/TestJdbcDataSource.java | 175 +++++++++++++++++++
3 files changed, 257 insertions(+), 37 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0cd5356a/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 9de5260..3e2dbb5 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -81,6 +81,8 @@ Bug Fixes
* SOLR-9165: Spellcheck does not return collations if "maxCollationTries" is used with "cursorMark".
(James Dyer)
+* SOLR-8612: closing JDBC Statement on failures in DataImportHandler (DIH) (Kristine Jetzke via Mikhail Khludnev)
+
Other Changes
----------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0cd5356a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java b/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
index 5bcba69..f3990f0 100644
--- a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
+++ b/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
@@ -29,14 +29,12 @@ import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;
import java.util.concurrent.Callable;
@@ -61,6 +59,8 @@ public class JdbcDataSource extends
private Connection conn;
+ private ResultSetIterator resultSetIterator;
+
private Map<String, Integer> fieldNameVsType = new HashMap<>();
private boolean convertType = false;
@@ -276,15 +276,19 @@ public class JdbcDataSource extends
@Override
public Iterator<Map<String, Object>> getData(String query) {
- ResultSetIterator r = new ResultSetIterator(query);
- return r.getIterator();
+ if (resultSetIterator != null) {
+ resultSetIterator.close();
+ resultSetIterator = null;
+ }
+ resultSetIterator = new ResultSetIterator(query);
+ return resultSetIterator.getIterator();
}
private void logError(String msg, Exception e) {
LOG.warn(msg, e);
}
- private List<String> readFieldNames(ResultSetMetaData metaData)
+ protected List<String> readFieldNames(ResultSetMetaData metaData)
throws SQLException {
List<String> colNames = new ArrayList<>();
int count = metaData.getColumnCount();
@@ -299,35 +303,38 @@ public class JdbcDataSource extends
private Statement stmt = null;
-
+ private List<String> colNames;
+
private Iterator<Map<String, Object>> rSetIterator;
public ResultSetIterator(String query) {
- final List<String> colNames;
try {
Connection c = getConnection();
- stmt = createStatement(c);
+ stmt = createStatement(c, batchSize, maxRows);
LOG.debug("Executing SQL: " + query);
long start = System.nanoTime();
resultSet = executeStatement(stmt, query);
LOG.trace("Time taken for sql :"
+ TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS));
- colNames = readFieldNames(resultSet.getMetaData());
+ setColNames(resultSet);
} catch (Exception e) {
+ close();
wrapAndThrow(SEVERE, e, "Unable to execute query: " + query);
return;
}
if (resultSet == null) {
+ close();
rSetIterator = new ArrayList<Map<String, Object>>().iterator();
return;
}
- rSetIterator = createIterator(stmt, resultSet, convertType, colNames, fieldNameVsType);
+ rSetIterator = createIterator(convertType, fieldNameVsType);
}
- protected Statement createStatement(Connection c) throws SQLException {
+ protected Statement createStatement(final Connection c, final int batchSize, final int maxRows)
+ throws SQLException {
Statement statement = c.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchSize(batchSize);
statement.setMaxRows(maxRows);
@@ -341,18 +348,25 @@ public class JdbcDataSource extends
return null;
}
+ protected void setColNames(final ResultSet resultSet) throws SQLException {
+ if (resultSet != null) {
+ colNames = readFieldNames(resultSet.getMetaData());
+ } else {
+ colNames = Collections.emptyList();
+ }
+ }
- protected Iterator<Map<String,Object>> createIterator(final Statement stmt, final ResultSet resultSet, final boolean convertType,
- final List<String> colNames, final Map<String,Integer> fieldNameVsType) {
+ protected Iterator<Map<String,Object>> createIterator(final boolean convertType,
+ final Map<String,Integer> fieldNameVsType) {
return new Iterator<Map<String,Object>>() {
@Override
public boolean hasNext() {
- return hasnext(resultSet, stmt);
+ return hasnext();
}
@Override
public Map<String,Object> next() {
- return getARow(resultSet, convertType, colNames, fieldNameVsType);
+ return getARow(convertType, fieldNameVsType);
}
@Override
@@ -363,17 +377,16 @@ public class JdbcDataSource extends
- protected Map<String,Object> getARow(ResultSet resultSet, boolean convertType, List<String> colNames,
- Map<String,Integer> fieldNameVsType) {
- if (resultSet == null)
+ protected Map<String,Object> getARow(boolean convertType, Map<String,Integer> fieldNameVsType) {
+ if (getResultSet() == null)
return null;
Map<String, Object> result = new HashMap<>();
- for (String colName : colNames) {
+ for (String colName : getColNames()) {
try {
if (!convertType) {
// Use underlying database's type information except for BigDecimal and BigInteger
// which cannot be serialized by JavaBin/XML. See SOLR-6165
- Object value = resultSet.getObject(colName);
+ Object value = getResultSet().getObject(colName);
if (value instanceof BigDecimal || value instanceof BigInteger) {
result.put(colName, value.toString());
} else {
@@ -387,28 +400,28 @@ public class JdbcDataSource extends
type = Types.VARCHAR;
switch (type) {
case Types.INTEGER:
- result.put(colName, resultSet.getInt(colName));
+ result.put(colName, getResultSet().getInt(colName));
break;
case Types.FLOAT:
- result.put(colName, resultSet.getFloat(colName));
+ result.put(colName, getResultSet().getFloat(colName));
break;
case Types.BIGINT:
- result.put(colName, resultSet.getLong(colName));
+ result.put(colName, getResultSet().getLong(colName));
break;
case Types.DOUBLE:
- result.put(colName, resultSet.getDouble(colName));
+ result.put(colName, getResultSet().getDouble(colName));
break;
case Types.DATE:
- result.put(colName, resultSet.getTimestamp(colName));
+ result.put(colName, getResultSet().getTimestamp(colName));
break;
case Types.BOOLEAN:
- result.put(colName, resultSet.getBoolean(colName));
+ result.put(colName, getResultSet().getBoolean(colName));
break;
case Types.BLOB:
- result.put(colName, resultSet.getBytes(colName));
+ result.put(colName, getResultSet().getBytes(colName));
break;
default:
- result.put(colName, resultSet.getString(colName));
+ result.put(colName, getResultSet().getString(colName));
break;
}
} catch (SQLException e) {
@@ -419,11 +432,13 @@ public class JdbcDataSource extends
return result;
}
- protected boolean hasnext(ResultSet resultSet, Statement stmt) {
- if (resultSet == null)
+ protected boolean hasnext() {
+ if (getResultSet() == null) {
+ close();
return false;
+ }
try {
- if (resultSet.next()) {
+ if (getResultSet().next()) {
return true;
} else {
close();
@@ -438,15 +453,15 @@ public class JdbcDataSource extends
protected void close() {
try {
- if (resultSet != null)
- resultSet.close();
- if (stmt != null)
- stmt.close();
+ if (getResultSet() != null)
+ getResultSet().close();
+ if (getStatement() != null)
+ getStatement().close();
} catch (Exception e) {
logError("Exception while closing result set", e);
} finally {
- resultSet = null;
- stmt = null;
+ setResultSet(null);
+ setStatement(null);
}
}
@@ -454,6 +469,31 @@ public class JdbcDataSource extends
return rSetIterator;
}
+
+ protected final Statement getStatement() {
+ return stmt;
+ }
+
+ protected final void setStatement(Statement stmt) {
+ this.stmt = stmt;
+ }
+
+ protected final ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ protected final void setResultSet(ResultSet resultSet) {
+ this.resultSet = resultSet;
+ }
+
+ protected final List<String> getColNames() {
+ return colNames;
+ }
+
+ protected final void setColNames(List<String> colNames) {
+ this.colNames = colNames;
+ }
+
}
protected Connection getConnection() throws Exception {
@@ -488,6 +528,9 @@ public class JdbcDataSource extends
@Override
public void close() {
+ if (resultSetIterator != null) {
+ resultSetIterator.close();
+ }
try {
closeConnection();
} finally {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0cd5356a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java b/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
index 50a116d..08a936a 100644
--- a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
+++ b/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
@@ -22,11 +22,15 @@ import java.nio.file.Files;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
import java.sql.SQLException;
+import java.sql.Statement;
import java.util.*;
import javax.sql.DataSource;
+import org.apache.solr.handler.dataimport.JdbcDataSource.ResultSetIterator;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.After;
@@ -202,6 +206,177 @@ public class TestJdbcDataSource extends AbstractDataImportHandlerTestCase {
}
@Test
+ public void testClosesStatementWhenExceptionThrownOnExecuteQuery() throws Exception {
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ SQLException sqlException = new SQLException("fake");
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andThrow(sqlException);
+ statement.close();
+
+ mockControl.replay();
+
+ try {
+ jdbcDataSource.getData("query");
+ fail("exception expected");
+ } catch (DataImportHandlerException ex) {
+ assertSame(sqlException, ex.getCause());
+ }
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesStatementWhenResultSetNull() throws Exception {
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(false);
+ statement.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesStatementWhenHasNextCalledAndResultSetNull() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ statement.close();
+
+ mockControl.replay();
+
+ Iterator<Map<String,Object>> data = jdbcDataSource.getData("query");
+
+ ResultSetIterator resultSetIterator = (ResultSetIterator) data.getClass().getDeclaredField("this$1").get(data);
+ resultSetIterator.setResultSet(null);
+
+ data.hasNext();
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesResultSetAndStatementWhenDataSourceIsClosed() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ resultSet.close();
+ statement.close();
+ connection.commit();
+ connection.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+ jdbcDataSource.close();
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesCurrentResultSetIteratorWhenNewOneIsCreated() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ resultSet.close();
+ statement.close();
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("other query")).andReturn(false);
+ statement.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+ jdbcDataSource.getData("other query");
+
+ mockControl.verify();
+ }
+
+ @Test
public void testRetrieveFromDriverManager() throws Exception {
DriverManager.registerDriver(driver);
try {
[3/3] lucene-solr:branch_6_0: SOLR-8612: closing JDBC Statement on
exceptions from JdbcDataSource in DataImportHandler aka DIH (Kristine Jetzke
via Mikhail Khludnev)
Posted by sa...@apache.org.
SOLR-8612: closing JDBC Statement on exceptions from JdbcDataSource in DataImportHandler aka DIH (Kristine Jetzke via Mikhail Khludnev)
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7e2252cb
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7e2252cb
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7e2252cb
Branch: refs/heads/branch_6_0
Commit: 7e2252cbc0b0783b442d0f76d5312ec6f379f0ae
Parents: 7b79571
Author: Mikhail Khludnev <mk...@apache.org>
Authored: Thu Jun 2 22:53:15 2016 +0300
Committer: Steve Rowe <sa...@apache.org>
Committed: Fri Jun 17 19:18:31 2016 -0400
----------------------------------------------------------------------
.../solr/handler/dataimport/JdbcDataSource.java | 117 +++++++++----
.../handler/dataimport/TestJdbcDataSource.java | 175 +++++++++++++++++++
2 files changed, 255 insertions(+), 37 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7e2252cb/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java b/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
index d485651..2dfaae7 100644
--- a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
+++ b/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
@@ -29,14 +29,12 @@ import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;
import java.util.concurrent.Callable;
@@ -60,6 +58,8 @@ public class JdbcDataSource extends
private long connLastUsed = 0;
private Connection conn;
+
+ private ResultSetIterator resultSetIterator;
private Map<String, Integer> fieldNameVsType = new HashMap<>();
@@ -276,15 +276,19 @@ public class JdbcDataSource extends
@Override
public Iterator<Map<String, Object>> getData(String query) {
- ResultSetIterator r = new ResultSetIterator(query);
- return r.getIterator();
+ if (resultSetIterator != null) {
+ resultSetIterator.close();
+ resultSetIterator = null;
+ }
+ resultSetIterator = new ResultSetIterator(query);
+ return resultSetIterator.getIterator();
}
private void logError(String msg, Exception e) {
LOG.warn(msg, e);
}
- private List<String> readFieldNames(ResultSetMetaData metaData)
+ protected List<String> readFieldNames(ResultSetMetaData metaData)
throws SQLException {
List<String> colNames = new ArrayList<>();
int count = metaData.getColumnCount();
@@ -299,35 +303,38 @@ public class JdbcDataSource extends
private Statement stmt = null;
+ private List<String> colNames;
private Iterator<Map<String, Object>> rSetIterator;
public ResultSetIterator(String query) {
- final List<String> colNames;
try {
Connection c = getConnection();
- stmt = createStatement(c);
+ stmt = createStatement(c, batchSize, maxRows);
LOG.debug("Executing SQL: " + query);
long start = System.nanoTime();
resultSet = executeStatement(stmt, query);
LOG.trace("Time taken for sql :"
+ TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS));
- colNames = readFieldNames(resultSet.getMetaData());
+ setColNames(resultSet);
} catch (Exception e) {
+ close();
wrapAndThrow(SEVERE, e, "Unable to execute query: " + query);
return;
}
if (resultSet == null) {
+ close();
rSetIterator = new ArrayList<Map<String, Object>>().iterator();
return;
}
- rSetIterator = createIterator(stmt, resultSet, convertType, colNames, fieldNameVsType);
+ rSetIterator = createIterator(convertType, fieldNameVsType);
}
- protected Statement createStatement(Connection c) throws SQLException {
+ protected Statement createStatement(final Connection c, final int batchSize, final int maxRows)
+ throws SQLException {
Statement statement = c.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchSize(batchSize);
statement.setMaxRows(maxRows);
@@ -340,19 +347,26 @@ public class JdbcDataSource extends
}
return null;
}
+
+ protected void setColNames(final ResultSet resultSet) throws SQLException {
+ if (resultSet != null) {
+ colNames = readFieldNames(resultSet.getMetaData());
+ } else {
+ colNames = Collections.emptyList();
+ }
+ }
-
- protected Iterator<Map<String,Object>> createIterator(Statement stmt, ResultSet resultSet, boolean convertType,
- List<String> colNames, Map<String,Integer> fieldNameVsType) {
+ protected Iterator<Map<String,Object>> createIterator(final boolean convertType,
+ final Map<String,Integer> fieldNameVsType) {
return new Iterator<Map<String,Object>>() {
@Override
public boolean hasNext() {
- return hasnext(resultSet, stmt);
+ return hasnext();
}
@Override
public Map<String,Object> next() {
- return getARow(resultSet, convertType, colNames, fieldNameVsType);
+ return getARow(convertType, fieldNameVsType);
}
@Override
@@ -363,17 +377,16 @@ public class JdbcDataSource extends
- protected Map<String,Object> getARow(ResultSet resultSet, boolean convertType, List<String> colNames,
- Map<String,Integer> fieldNameVsType) {
- if (resultSet == null)
+ protected Map<String,Object> getARow(boolean convertType, Map<String,Integer> fieldNameVsType) {
+ if (getResultSet() == null)
return null;
Map<String, Object> result = new HashMap<>();
- for (String colName : colNames) {
+ for (String colName : getColNames()) {
try {
if (!convertType) {
// Use underlying database's type information except for BigDecimal and BigInteger
// which cannot be serialized by JavaBin/XML. See SOLR-6165
- Object value = resultSet.getObject(colName);
+ Object value = getResultSet().getObject(colName);
if (value instanceof BigDecimal || value instanceof BigInteger) {
result.put(colName, value.toString());
} else {
@@ -387,28 +400,28 @@ public class JdbcDataSource extends
type = Types.VARCHAR;
switch (type) {
case Types.INTEGER:
- result.put(colName, resultSet.getInt(colName));
+ result.put(colName, getResultSet().getInt(colName));
break;
case Types.FLOAT:
- result.put(colName, resultSet.getFloat(colName));
+ result.put(colName, getResultSet().getFloat(colName));
break;
case Types.BIGINT:
- result.put(colName, resultSet.getLong(colName));
+ result.put(colName, getResultSet().getLong(colName));
break;
case Types.DOUBLE:
- result.put(colName, resultSet.getDouble(colName));
+ result.put(colName, getResultSet().getDouble(colName));
break;
case Types.DATE:
- result.put(colName, resultSet.getTimestamp(colName));
+ result.put(colName, getResultSet().getTimestamp(colName));
break;
case Types.BOOLEAN:
- result.put(colName, resultSet.getBoolean(colName));
+ result.put(colName, getResultSet().getBoolean(colName));
break;
case Types.BLOB:
- result.put(colName, resultSet.getBytes(colName));
+ result.put(colName, getResultSet().getBytes(colName));
break;
default:
- result.put(colName, resultSet.getString(colName));
+ result.put(colName, getResultSet().getString(colName));
break;
}
} catch (SQLException e) {
@@ -419,11 +432,13 @@ public class JdbcDataSource extends
return result;
}
- protected boolean hasnext(ResultSet resultSet, Statement stmt) {
- if (resultSet == null)
+ protected boolean hasnext() {
+ if (getResultSet() == null) {
+ close();
return false;
+ }
try {
- if (resultSet.next()) {
+ if (getResultSet().next()) {
return true;
} else {
close();
@@ -438,15 +453,15 @@ public class JdbcDataSource extends
protected void close() {
try {
- if (resultSet != null)
- resultSet.close();
- if (stmt != null)
- stmt.close();
+ if (getResultSet() != null)
+ getResultSet().close();
+ if (getStatement() != null)
+ getStatement().close();
} catch (Exception e) {
logError("Exception while closing result set", e);
} finally {
- resultSet = null;
- stmt = null;
+ setResultSet(null);
+ setStatement(null);
}
}
@@ -454,6 +469,31 @@ public class JdbcDataSource extends
return rSetIterator;
}
+
+ protected final Statement getStatement() {
+ return stmt;
+ }
+
+ protected final void setStatement(Statement stmt) {
+ this.stmt = stmt;
+ }
+
+ protected final ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ protected final void setResultSet(ResultSet resultSet) {
+ this.resultSet = resultSet;
+ }
+
+ protected final List<String> getColNames() {
+ return colNames;
+ }
+
+ protected final void setColNames(List<String> colNames) {
+ this.colNames = colNames;
+ }
+
}
protected Connection getConnection() throws Exception {
@@ -488,6 +528,9 @@ public class JdbcDataSource extends
@Override
public void close() {
+ if (resultSetIterator != null) {
+ resultSetIterator.close();
+ }
try {
closeConnection();
} finally {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7e2252cb/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java b/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
index 50a116d..08a936a 100644
--- a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
+++ b/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
@@ -22,11 +22,15 @@ import java.nio.file.Files;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
import java.sql.SQLException;
+import java.sql.Statement;
import java.util.*;
import javax.sql.DataSource;
+import org.apache.solr.handler.dataimport.JdbcDataSource.ResultSetIterator;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.After;
@@ -202,6 +206,177 @@ public class TestJdbcDataSource extends AbstractDataImportHandlerTestCase {
}
@Test
+ public void testClosesStatementWhenExceptionThrownOnExecuteQuery() throws Exception {
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ SQLException sqlException = new SQLException("fake");
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andThrow(sqlException);
+ statement.close();
+
+ mockControl.replay();
+
+ try {
+ jdbcDataSource.getData("query");
+ fail("exception expected");
+ } catch (DataImportHandlerException ex) {
+ assertSame(sqlException, ex.getCause());
+ }
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesStatementWhenResultSetNull() throws Exception {
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(false);
+ statement.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesStatementWhenHasNextCalledAndResultSetNull() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ statement.close();
+
+ mockControl.replay();
+
+ Iterator<Map<String,Object>> data = jdbcDataSource.getData("query");
+
+ ResultSetIterator resultSetIterator = (ResultSetIterator) data.getClass().getDeclaredField("this$1").get(data);
+ resultSetIterator.setResultSet(null);
+
+ data.hasNext();
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesResultSetAndStatementWhenDataSourceIsClosed() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ resultSet.close();
+ statement.close();
+ connection.commit();
+ connection.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+ jdbcDataSource.close();
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesCurrentResultSetIteratorWhenNewOneIsCreated() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ resultSet.close();
+ statement.close();
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("other query")).andReturn(false);
+ statement.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+ jdbcDataSource.getData("other query");
+
+ mockControl.verify();
+ }
+
+ @Test
public void testRetrieveFromDriverManager() throws Exception {
DriverManager.registerDriver(driver);
try {
[2/3] lucene-solr:branch_5x: SOLR-8612: closing JDBC Statement on
exceptions from JdbcDataSource in DataImportHandler aka DIH (Kristine Jetzke
via Mikhail Khludnev)
Posted by sa...@apache.org.
SOLR-8612: closing JDBC Statement on exceptions from JdbcDataSource in DataImportHandler aka DIH (Kristine Jetzke via Mikhail Khludnev)
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/66dd9bc6
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/66dd9bc6
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/66dd9bc6
Branch: refs/heads/branch_5x
Commit: 66dd9bc63b0492a00bd55a9cc986818ef81afb95
Parents: b4d81f7
Author: Mikhail Khludnev <mk...@apache.org>
Authored: Thu Jun 2 22:53:15 2016 +0300
Committer: Steve Rowe <sa...@apache.org>
Committed: Fri Jun 17 19:17:56 2016 -0400
----------------------------------------------------------------------
.../solr/handler/dataimport/JdbcDataSource.java | 117 +++++++++----
.../handler/dataimport/TestJdbcDataSource.java | 175 +++++++++++++++++++
2 files changed, 255 insertions(+), 37 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/66dd9bc6/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java b/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
index 5bcba69..f3990f0 100644
--- a/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
+++ b/solr/contrib/dataimporthandler/src/java/org/apache/solr/handler/dataimport/JdbcDataSource.java
@@ -29,14 +29,12 @@ import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;
import java.util.concurrent.Callable;
@@ -61,6 +59,8 @@ public class JdbcDataSource extends
private Connection conn;
+ private ResultSetIterator resultSetIterator;
+
private Map<String, Integer> fieldNameVsType = new HashMap<>();
private boolean convertType = false;
@@ -276,15 +276,19 @@ public class JdbcDataSource extends
@Override
public Iterator<Map<String, Object>> getData(String query) {
- ResultSetIterator r = new ResultSetIterator(query);
- return r.getIterator();
+ if (resultSetIterator != null) {
+ resultSetIterator.close();
+ resultSetIterator = null;
+ }
+ resultSetIterator = new ResultSetIterator(query);
+ return resultSetIterator.getIterator();
}
private void logError(String msg, Exception e) {
LOG.warn(msg, e);
}
- private List<String> readFieldNames(ResultSetMetaData metaData)
+ protected List<String> readFieldNames(ResultSetMetaData metaData)
throws SQLException {
List<String> colNames = new ArrayList<>();
int count = metaData.getColumnCount();
@@ -299,35 +303,38 @@ public class JdbcDataSource extends
private Statement stmt = null;
-
+ private List<String> colNames;
+
private Iterator<Map<String, Object>> rSetIterator;
public ResultSetIterator(String query) {
- final List<String> colNames;
try {
Connection c = getConnection();
- stmt = createStatement(c);
+ stmt = createStatement(c, batchSize, maxRows);
LOG.debug("Executing SQL: " + query);
long start = System.nanoTime();
resultSet = executeStatement(stmt, query);
LOG.trace("Time taken for sql :"
+ TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS));
- colNames = readFieldNames(resultSet.getMetaData());
+ setColNames(resultSet);
} catch (Exception e) {
+ close();
wrapAndThrow(SEVERE, e, "Unable to execute query: " + query);
return;
}
if (resultSet == null) {
+ close();
rSetIterator = new ArrayList<Map<String, Object>>().iterator();
return;
}
- rSetIterator = createIterator(stmt, resultSet, convertType, colNames, fieldNameVsType);
+ rSetIterator = createIterator(convertType, fieldNameVsType);
}
- protected Statement createStatement(Connection c) throws SQLException {
+ protected Statement createStatement(final Connection c, final int batchSize, final int maxRows)
+ throws SQLException {
Statement statement = c.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchSize(batchSize);
statement.setMaxRows(maxRows);
@@ -341,18 +348,25 @@ public class JdbcDataSource extends
return null;
}
+ protected void setColNames(final ResultSet resultSet) throws SQLException {
+ if (resultSet != null) {
+ colNames = readFieldNames(resultSet.getMetaData());
+ } else {
+ colNames = Collections.emptyList();
+ }
+ }
- protected Iterator<Map<String,Object>> createIterator(final Statement stmt, final ResultSet resultSet, final boolean convertType,
- final List<String> colNames, final Map<String,Integer> fieldNameVsType) {
+ protected Iterator<Map<String,Object>> createIterator(final boolean convertType,
+ final Map<String,Integer> fieldNameVsType) {
return new Iterator<Map<String,Object>>() {
@Override
public boolean hasNext() {
- return hasnext(resultSet, stmt);
+ return hasnext();
}
@Override
public Map<String,Object> next() {
- return getARow(resultSet, convertType, colNames, fieldNameVsType);
+ return getARow(convertType, fieldNameVsType);
}
@Override
@@ -363,17 +377,16 @@ public class JdbcDataSource extends
- protected Map<String,Object> getARow(ResultSet resultSet, boolean convertType, List<String> colNames,
- Map<String,Integer> fieldNameVsType) {
- if (resultSet == null)
+ protected Map<String,Object> getARow(boolean convertType, Map<String,Integer> fieldNameVsType) {
+ if (getResultSet() == null)
return null;
Map<String, Object> result = new HashMap<>();
- for (String colName : colNames) {
+ for (String colName : getColNames()) {
try {
if (!convertType) {
// Use underlying database's type information except for BigDecimal and BigInteger
// which cannot be serialized by JavaBin/XML. See SOLR-6165
- Object value = resultSet.getObject(colName);
+ Object value = getResultSet().getObject(colName);
if (value instanceof BigDecimal || value instanceof BigInteger) {
result.put(colName, value.toString());
} else {
@@ -387,28 +400,28 @@ public class JdbcDataSource extends
type = Types.VARCHAR;
switch (type) {
case Types.INTEGER:
- result.put(colName, resultSet.getInt(colName));
+ result.put(colName, getResultSet().getInt(colName));
break;
case Types.FLOAT:
- result.put(colName, resultSet.getFloat(colName));
+ result.put(colName, getResultSet().getFloat(colName));
break;
case Types.BIGINT:
- result.put(colName, resultSet.getLong(colName));
+ result.put(colName, getResultSet().getLong(colName));
break;
case Types.DOUBLE:
- result.put(colName, resultSet.getDouble(colName));
+ result.put(colName, getResultSet().getDouble(colName));
break;
case Types.DATE:
- result.put(colName, resultSet.getTimestamp(colName));
+ result.put(colName, getResultSet().getTimestamp(colName));
break;
case Types.BOOLEAN:
- result.put(colName, resultSet.getBoolean(colName));
+ result.put(colName, getResultSet().getBoolean(colName));
break;
case Types.BLOB:
- result.put(colName, resultSet.getBytes(colName));
+ result.put(colName, getResultSet().getBytes(colName));
break;
default:
- result.put(colName, resultSet.getString(colName));
+ result.put(colName, getResultSet().getString(colName));
break;
}
} catch (SQLException e) {
@@ -419,11 +432,13 @@ public class JdbcDataSource extends
return result;
}
- protected boolean hasnext(ResultSet resultSet, Statement stmt) {
- if (resultSet == null)
+ protected boolean hasnext() {
+ if (getResultSet() == null) {
+ close();
return false;
+ }
try {
- if (resultSet.next()) {
+ if (getResultSet().next()) {
return true;
} else {
close();
@@ -438,15 +453,15 @@ public class JdbcDataSource extends
protected void close() {
try {
- if (resultSet != null)
- resultSet.close();
- if (stmt != null)
- stmt.close();
+ if (getResultSet() != null)
+ getResultSet().close();
+ if (getStatement() != null)
+ getStatement().close();
} catch (Exception e) {
logError("Exception while closing result set", e);
} finally {
- resultSet = null;
- stmt = null;
+ setResultSet(null);
+ setStatement(null);
}
}
@@ -454,6 +469,31 @@ public class JdbcDataSource extends
return rSetIterator;
}
+
+ protected final Statement getStatement() {
+ return stmt;
+ }
+
+ protected final void setStatement(Statement stmt) {
+ this.stmt = stmt;
+ }
+
+ protected final ResultSet getResultSet() {
+ return resultSet;
+ }
+
+ protected final void setResultSet(ResultSet resultSet) {
+ this.resultSet = resultSet;
+ }
+
+ protected final List<String> getColNames() {
+ return colNames;
+ }
+
+ protected final void setColNames(List<String> colNames) {
+ this.colNames = colNames;
+ }
+
}
protected Connection getConnection() throws Exception {
@@ -488,6 +528,9 @@ public class JdbcDataSource extends
@Override
public void close() {
+ if (resultSetIterator != null) {
+ resultSetIterator.close();
+ }
try {
closeConnection();
} finally {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/66dd9bc6/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java b/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
index 50a116d..08a936a 100644
--- a/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
+++ b/solr/contrib/dataimporthandler/src/test/org/apache/solr/handler/dataimport/TestJdbcDataSource.java
@@ -22,11 +22,15 @@ import java.nio.file.Files;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
import java.sql.SQLException;
+import java.sql.Statement;
import java.util.*;
import javax.sql.DataSource;
+import org.apache.solr.handler.dataimport.JdbcDataSource.ResultSetIterator;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;
import org.junit.After;
@@ -202,6 +206,177 @@ public class TestJdbcDataSource extends AbstractDataImportHandlerTestCase {
}
@Test
+ public void testClosesStatementWhenExceptionThrownOnExecuteQuery() throws Exception {
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ SQLException sqlException = new SQLException("fake");
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andThrow(sqlException);
+ statement.close();
+
+ mockControl.replay();
+
+ try {
+ jdbcDataSource.getData("query");
+ fail("exception expected");
+ } catch (DataImportHandlerException ex) {
+ assertSame(sqlException, ex.getCause());
+ }
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesStatementWhenResultSetNull() throws Exception {
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(false);
+ statement.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesStatementWhenHasNextCalledAndResultSetNull() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ statement.close();
+
+ mockControl.replay();
+
+ Iterator<Map<String,Object>> data = jdbcDataSource.getData("query");
+
+ ResultSetIterator resultSetIterator = (ResultSetIterator) data.getClass().getDeclaredField("this$1").get(data);
+ resultSetIterator.setResultSet(null);
+
+ data.hasNext();
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesResultSetAndStatementWhenDataSourceIsClosed() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ resultSet.close();
+ statement.close();
+ connection.commit();
+ connection.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+ jdbcDataSource.close();
+
+ mockControl.verify();
+ }
+
+ @Test
+ public void testClosesCurrentResultSetIteratorWhenNewOneIsCreated() throws Exception {
+
+ MockInitialContextFactory.bind("java:comp/env/jdbc/JndiDB", dataSource);
+
+ props.put(JdbcDataSource.JNDI_NAME, "java:comp/env/jdbc/JndiDB");
+ EasyMock.expect(dataSource.getConnection()).andReturn(connection);
+
+ jdbcDataSource.init(context, props);
+
+ connection.setAutoCommit(false);
+
+ Statement statement = mockControl.createMock(Statement.class);
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("query")).andReturn(true);
+ ResultSet resultSet = mockControl.createMock(ResultSet.class);
+ EasyMock.expect(statement.getResultSet()).andReturn(resultSet);
+ ResultSetMetaData metaData = mockControl.createMock(ResultSetMetaData.class);
+ EasyMock.expect(resultSet.getMetaData()).andReturn(metaData);
+ EasyMock.expect(metaData.getColumnCount()).andReturn(0);
+ resultSet.close();
+ statement.close();
+ EasyMock.expect(connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY))
+ .andReturn(statement);
+ statement.setFetchSize(500);
+ statement.setMaxRows(0);
+ EasyMock.expect(statement.execute("other query")).andReturn(false);
+ statement.close();
+
+ mockControl.replay();
+
+ jdbcDataSource.getData("query");
+ jdbcDataSource.getData("other query");
+
+ mockControl.verify();
+ }
+
+ @Test
public void testRetrieveFromDriverManager() throws Exception {
DriverManager.registerDriver(driver);
try {