提交 2bbf093a authored 作者: Thomas Mueller's avatar Thomas Mueller

Could not use the same linked table multiple times in the same query.

上级 e995b56b
......@@ -18,7 +18,8 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>A bug in the server-less multi-connection mode has been fixed.
<ul><li>Could not use the same linked table multiple times in the same query.
</li><li>A bug in the server-less multi-connection mode has been fixed.
</li><li>Column names could not be named "UNIQUE" (with the quotes).
</li><li>New system function TRANSACTION_ID() to get the current transaction
identifier for a session.
......
......@@ -407,6 +407,7 @@ Of course, patches are always welcome, but are not always applied as is. Patches
</li><li>PostgreSQL compatibility: test DbVisualizer and Squirrel SQL using a new PostgreSQL JDBC driver.
</li><li>RunScript should be able to read from system in (or quite mode for Shell).
</li><li>Natural join: support select x from dual natural join dual.
</li><li>Optimize IN(...) for DELETE and UPDATE.
</li></ul>
<h2>Not Planned</h2>
......
......@@ -6,6 +6,7 @@
*/
package org.h2.index;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
......@@ -14,7 +15,7 @@ import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableLink;
import org.h2.value.DataType;
import org.h2.value.Value;
......@@ -23,15 +24,24 @@ import org.h2.value.Value;
*/
public class LinkedCursor implements Cursor {
private Session session;
private final TableLink tableLink;
private final PreparedStatement prep;
private final String sql;
private final Session session;
private final ResultSet rs;
private Row current;
private ResultSet rs;
private Table table;
LinkedCursor(Table table, ResultSet rs, Session session) {
LinkedCursor(TableLink tableLink, ResultSet rs, Session session, String sql, PreparedStatement prep) {
this.session = session;
this.table = table;
this.tableLink = tableLink;
this.rs = rs;
this.sql = sql;
this.prep = prep;
}
private void closeResultSetAndReusePreparedStatement() throws SQLException {
rs.close();
tableLink.reusePreparedStatement(prep, sql);
}
public Row get() {
......@@ -50,13 +60,13 @@ public class LinkedCursor implements Cursor {
public boolean next() throws SQLException {
boolean result = rs.next();
if (!result) {
rs.close();
closeResultSetAndReusePreparedStatement();
current = null;
return false;
}
current = table.getTemplateRow();
current = tableLink.getTemplateRow();
for (int i = 0; i < current.getColumnCount(); i++) {
Column col = table.getColumn(i);
Column col = tableLink.getColumn(i);
Value v = DataType.readValue(session, rs, i + 1, col.getType());
current.setValue(i, v);
}
......
......@@ -10,7 +10,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.message.Message;
......@@ -70,7 +69,7 @@ public class LinkedIndex extends BaseIndex {
String sql = buff.toString();
synchronized (link.getConnection()) {
try {
PreparedStatement prep = link.getPreparedStatement(sql);
PreparedStatement prep = link.getPreparedStatement(sql, false);
for (int i = 0, j = 0; i < row.getColumnCount(); i++) {
Value v = row.getValue(i);
if (v != null && v != ValueNull.INSTANCE) {
......@@ -81,7 +80,7 @@ public class LinkedIndex extends BaseIndex {
prep.executeUpdate();
rowCount++;
} catch (SQLException e) {
throw wrapException(sql, e);
throw link.wrapException(sql, e);
}
}
}
......@@ -119,7 +118,7 @@ public class LinkedIndex extends BaseIndex {
String sql = buff.toString();
synchronized (link.getConnection()) {
try {
PreparedStatement prep = link.getPreparedStatement(sql);
PreparedStatement prep = link.getPreparedStatement(sql, true);
int j = 0;
for (int i = 0; first != null && i < first.getColumnCount(); i++) {
Value v = first.getValue(i);
......@@ -136,9 +135,9 @@ public class LinkedIndex extends BaseIndex {
}
}
ResultSet rs = prep.executeQuery();
return new LinkedCursor(table, rs, session);
return new LinkedCursor(link, rs, session, sql, prep);
} catch (SQLException e) {
throw wrapException(sql, e);
throw link.wrapException(sql, e);
}
}
}
......@@ -209,7 +208,7 @@ public class LinkedIndex extends BaseIndex {
String sql = buff.toString();
synchronized (link.getConnection()) {
try {
PreparedStatement prep = link.getPreparedStatement(sql);
PreparedStatement prep = link.getPreparedStatement(sql, false);
for (int i = 0, j = 0; i < row.getColumnCount(); i++) {
Value v = row.getValue(i);
if (!isNull(v)) {
......@@ -220,7 +219,7 @@ public class LinkedIndex extends BaseIndex {
int count = prep.executeUpdate();
rowCount -= count;
} catch (SQLException e) {
throw wrapException(sql, e);
throw link.wrapException(sql, e);
}
}
}
......@@ -261,7 +260,7 @@ public class LinkedIndex extends BaseIndex {
synchronized (link.getConnection()) {
try {
int j = 1;
PreparedStatement prep = link.getPreparedStatement(sql);
PreparedStatement prep = link.getPreparedStatement(sql, false);
for (int i = 0; i < newRow.getColumnCount(); i++) {
newRow.getValue(i).set(prep, j);
j++;
......@@ -277,15 +276,11 @@ public class LinkedIndex extends BaseIndex {
// this has no effect but at least it allows to debug the update count
rowCount = rowCount + count - count;
} catch (SQLException e) {
throw wrapException(sql, e);
throw link.wrapException(sql, e);
}
}
}
private SQLException wrapException(String sql, SQLException e) {
return Message.getSQLException(ErrorCode.ERROR_ACCESSING_LINKED_TABLE_2, new String[] { sql, e.toString() }, e);
}
public long getRowCount(Session session) {
return rowCount;
}
......
......@@ -367,13 +367,29 @@ public class TableLink extends Table {
}
}
public long getRowCount(Session session) throws SQLException {
PreparedStatement prep = getPreparedStatement("SELECT COUNT(*) FROM " + qualifiedTableName);
ResultSet rs = prep.executeQuery();
rs.next();
long count = rs.getLong(1);
rs.close();
return count;
public synchronized long getRowCount(Session session) throws SQLException {
String sql = "SELECT COUNT(*) FROM " + qualifiedTableName;
try {
PreparedStatement prep = getPreparedStatement(sql, false);
ResultSet rs = prep.executeQuery();
rs.next();
long count = rs.getLong(1);
rs.close();
return count;
} catch (SQLException e) {
throw wrapException(sql, e);
}
}
/**
* Wrap a SQL exception that occurred while accessing a linked table.
*
* @param sql the SQL statement
* @param e the SQL exception from the remote database
* @return the wrapped SQL exception
*/
public SQLException wrapException(String sql, SQLException e) {
return Message.getSQLException(ErrorCode.ERROR_ACCESSING_LINKED_TABLE_2, new String[] { sql, e.toString() }, e);
}
public String getQualifiedTable() {
......@@ -384,10 +400,12 @@ public class TableLink extends Table {
* Get a prepared statement object for the given statement. Prepared
* statements are kept in a hash map to avoid re-creating them.
*
* @param sql the SQL statement.
* @param sql the SQL statement
* @param exclusive if the prepared statement must be removed from the map
* until reusePreparedStatement is called (only required for queries)
* @return the prepared statement
*/
public PreparedStatement getPreparedStatement(String sql) throws SQLException {
public PreparedStatement getPreparedStatement(String sql, boolean exclusive) throws SQLException {
Trace trace = database.getTrace(Trace.TABLE);
if (trace.isDebugEnabled()) {
trace.debug(getName() + ":\n" + sql);
......@@ -400,6 +418,9 @@ public class TableLink extends Table {
prep = conn.getConnection().prepareStatement(sql);
prepared.put(sql, prep);
}
if (exclusive) {
prepared.remove(sql);
}
return prep;
}
......@@ -502,4 +523,8 @@ public class TableLink extends Table {
return ROW_COUNT_APPROXIMATION;
}
public void reusePreparedStatement(PreparedStatement prep, String sql) {
prepared.put(sql, prep);
}
}
......@@ -34,6 +34,7 @@ public class TestLinkedTable extends TestBase {
public void test() throws SQLException {
// testLinkAutoAdd();
testNestedQueriesToSameTable();
testSharedConnection();
testMultipleSchemas();
testReadOnlyLinkedTable();
......@@ -66,6 +67,27 @@ public class TestLinkedTable extends TestBase {
// cb.close();
// }
private void testNestedQueriesToSameTable() throws SQLException {
if (config.memory || !SysProperties.SHARE_LINKED_CONNECTIONS) {
return;
}
org.h2.Driver.load();
deleteDb("linkedTable");
String url = getURL("linkedTable", true);
String user = getUser();
String password = getPassword();
Connection ca = getConnection(url, user, password);
Statement sa = ca.createStatement();
sa.execute("CREATE TABLE TEST(ID INT) AS SELECT 1");
ca.close();
Connection cb = DriverManager.getConnection("jdbc:h2:mem:two", "sa", "sa");
Statement sb = cb.createStatement();
sb.execute("CREATE LINKED TABLE T1(NULL, '" + url + "', '"+user+"', '"+password+"', 'TEST')");
sb.executeQuery("SELECT * FROM DUAL A LEFT OUTER JOIN T1 A ON A.ID=1 LEFT OUTER JOIN T1 B ON B.ID=1");
sb.execute("DROP ALL OBJECTS");
cb.close();
}
private void testSharedConnection() throws SQLException {
if (config.memory || !SysProperties.SHARE_LINKED_CONNECTIONS) {
return;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论