提交 8ab34eda authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 c794b1f9
...@@ -958,7 +958,7 @@ INFORMATION_SCHEMA.SETTINGS ...@@ -958,7 +958,7 @@ INFORMATION_SCHEMA.SETTINGS
<tr><td>h2.runFinalize</td><td>true</td><td>Run finalizers to detect unclosed connections</td></tr> <tr><td>h2.runFinalize</td><td>true</td><td>Run finalizers to detect unclosed connections</td></tr>
<tr><td>h2.scriptDirectory</td><td></td><td>Relative or absolute directory where the script files are stored to or read from</td></tr> <tr><td>h2.scriptDirectory</td><td></td><td>Relative or absolute directory where the script files are stored to or read from</td></tr>
<tr><td>h2.serverCachedObjects</td><td>64</td><td>TCP Server: number of cached objects per session</td></tr> <tr><td>h2.serverCachedObjects</td><td>64</td><td>TCP Server: number of cached objects per session</td></tr>
<tr><td>h2.serverSmallResultSetSize</td><td>100</td><td>TCP Server: result sets below this size are sent in one block</td></tr> <tr><td>h2.serverResultSetFetchSize</td><td>100</td><td>The default result set fetch size when using the server mode</td></tr>
</table> </table>
<br /><a name="glossary_links"></a> <br /><a name="glossary_links"></a>
......
...@@ -535,6 +535,7 @@ CREATE ALIAS IF NOT EXISTS FT_INIT FOR "org.h2.fulltext.FullText.init"; ...@@ -535,6 +535,7 @@ CREATE ALIAS IF NOT EXISTS FT_INIT FOR "org.h2.fulltext.FullText.init";
CALL FT_INIT(); CALL FT_INIT();
</pre> </pre>
<p> <p>
You need to initialize it in each database where you want to use it.
Afterwards, you can create a full text index for a table using: Afterwards, you can create a full text index for a table using:
</p> </p>
<pre> <pre>
...@@ -560,13 +561,16 @@ org.h2.fulltext.FullText.search(conn, text, limit, offset) ...@@ -560,13 +561,16 @@ org.h2.fulltext.FullText.search(conn, text, limit, offset)
<h3>Using the Lucene Fulltext Search</h3> <h3>Using the Lucene Fulltext Search</h3>
<p> <p>
To use the Lucene full text search, you need the Lucene library in the classpath. To use the Lucene full text search, you need the Lucene library in the classpath.
To initialize, call: How his is done depends on the application; if you use the H2 Console, you can add the Lucene
jar file to the the environment variables H2DRIVERS or CLASSPATH.
To initialize the Lucene full text search in a database, call:
</p> </p>
<pre> <pre>
CREATE ALIAS IF NOT EXISTS FTL_INIT FOR "org.h2.fulltext.FullTextLucene.init"; CREATE ALIAS IF NOT EXISTS FTL_INIT FOR "org.h2.fulltext.FullTextLucene.init";
CALL FTL_INIT(); CALL FTL_INIT();
</pre> </pre>
<p> <p>
You need to initialize it in each database where you want to use it.
Afterwards, you can create a full text index for a table using: Afterwards, you can create a full text index for a table using:
</p> </p>
<pre> <pre>
......
...@@ -28,13 +28,14 @@ public class CommandRemote implements CommandInterface { ...@@ -28,13 +28,14 @@ public class CommandRemote implements CommandInterface {
private final ObjectArray parameters; private final ObjectArray parameters;
private final Trace trace; private final Trace trace;
private final String sql; private final String sql;
private final int fetchSize;
private SessionRemote session; private SessionRemote session;
private int id; private int id;
private boolean isQuery; private boolean isQuery;
private boolean readonly; private boolean readonly;
private int paramCount; private int paramCount;
public CommandRemote(SessionRemote session, ObjectArray transferList, String sql) throws SQLException { public CommandRemote(SessionRemote session, ObjectArray transferList, String sql, int fetchSize) throws SQLException {
this.transferList = transferList; this.transferList = transferList;
trace = session.getTrace(); trace = session.getTrace();
this.sql = sql; this.sql = sql;
...@@ -46,6 +47,7 @@ public class CommandRemote implements CommandInterface { ...@@ -46,6 +47,7 @@ public class CommandRemote implements CommandInterface {
// set session late because prepare might fail - in this case we don't // set session late because prepare might fail - in this case we don't
// need to close the object // need to close the object
this.session = session; this.session = session;
this.fetchSize = fetchSize;
} }
private void prepare(SessionRemote session) throws SQLException { private void prepare(SessionRemote session) throws SQLException {
...@@ -94,7 +96,7 @@ public class CommandRemote implements CommandInterface { ...@@ -94,7 +96,7 @@ public class CommandRemote implements CommandInterface {
transfer.writeInt(SessionRemote.COMMAND_GET_META_DATA).writeInt(id).writeInt(objectId); transfer.writeInt(SessionRemote.COMMAND_GET_META_DATA).writeInt(id).writeInt(objectId);
session.done(transfer); session.done(transfer);
int columnCount = transfer.readInt(); int columnCount = transfer.readInt();
result = new ResultRemote(session, transfer, objectId, columnCount, -1); result = new ResultRemote(session, transfer, objectId, columnCount, Integer.MAX_VALUE);
break; break;
} catch (IOException e) { } catch (IOException e) {
session.removeServer(i--); session.removeServer(i--);
...@@ -123,13 +125,13 @@ public class CommandRemote implements CommandInterface { ...@@ -123,13 +125,13 @@ public class CommandRemote implements CommandInterface {
session.traceOperation("COMMAND_EXECUTE_QUERY", id); session.traceOperation("COMMAND_EXECUTE_QUERY", id);
transfer.writeInt(SessionRemote.COMMAND_EXECUTE_QUERY).writeInt(id).writeInt(objectId).writeInt( transfer.writeInt(SessionRemote.COMMAND_EXECUTE_QUERY).writeInt(id).writeInt(objectId).writeInt(
maxRows); maxRows);
int readRows; int fetch;
if (session.isClustered() || scrollable) { if (session.isClustered() || scrollable) {
readRows = Integer.MAX_VALUE; fetch = Integer.MAX_VALUE;
} else { } else {
readRows = SysProperties.SERVER_SMALL_RESULT_SET_SIZE; fetch = fetchSize;
} }
transfer.writeInt(readRows); transfer.writeInt(fetch);
sendParameters(transfer); sendParameters(transfer);
session.done(transfer); session.done(transfer);
int columnCount = transfer.readInt(); int columnCount = transfer.readInt();
...@@ -137,7 +139,7 @@ public class CommandRemote implements CommandInterface { ...@@ -137,7 +139,7 @@ public class CommandRemote implements CommandInterface {
result.close(); result.close();
result = null; result = null;
} }
result = new ResultRemote(session, transfer, objectId, columnCount, readRows); result = new ResultRemote(session, transfer, objectId, columnCount, fetch);
if (readonly) { if (readonly) {
break; break;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
package org.h2.command.dml; package org.h2.command.dml;
import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
...@@ -11,9 +12,9 @@ import java.io.OutputStream; ...@@ -11,9 +12,9 @@ import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.Comparator; import java.util.Comparator;
import org.h2.command.Parser; import org.h2.command.Parser;
...@@ -44,6 +45,7 @@ import org.h2.schema.TriggerObject; ...@@ -44,6 +45,7 @@ import org.h2.schema.TriggerObject;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.PlanItem; import org.h2.table.PlanItem;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.util.AutoCloseInputStream;
import org.h2.util.ByteUtils; import org.h2.util.ByteUtils;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
...@@ -69,7 +71,7 @@ public class ScriptCommand extends ScriptBase { ...@@ -69,7 +71,7 @@ public class ScriptCommand extends ScriptBase {
private byte[] buffer; private byte[] buffer;
private boolean tempLobTableCreated; private boolean tempLobTableCreated;
private int nextLobId; private int nextLobId;
private int lobBlockSize = Constants.FILE_BLOCK_SIZE; private int lobBlockSize = Constants.IO_BUFFER_SIZE;
private static final String TEMP_LOB_FILENAME = "system_temp_lob.db"; private static final String TEMP_LOB_FILENAME = "system_temp_lob.db";
public ScriptCommand(Session session) { public ScriptCommand(Session session) {
...@@ -338,7 +340,7 @@ public class ScriptCommand extends ScriptBase { ...@@ -338,7 +340,7 @@ public class ScriptCommand extends ScriptBase {
break; break;
} }
buff.append(ByteUtils.convertBytesToString(bytes, len)); buff.append(ByteUtils.convertBytesToString(bytes, len));
buff.append("');"); buff.append("')");
String sql = buff.toString(); String sql = buff.toString();
add(sql, true); add(sql, true);
} }
...@@ -358,8 +360,8 @@ public class ScriptCommand extends ScriptBase { ...@@ -358,8 +360,8 @@ public class ScriptCommand extends ScriptBase {
if (len < 0) { if (len < 0) {
break; break;
} }
buff.append(StringUtils.quoteStringSQL(new String(chars))); buff.append(StringUtils.quoteStringSQL(new String(chars, 0, len)));
buff.append(", NULL);"); buff.append(", NULL)");
String sql = buff.toString(); String sql = buff.toString();
add(sql, true); add(sql, true);
} }
...@@ -374,36 +376,68 @@ public class ScriptCommand extends ScriptBase { ...@@ -374,36 +376,68 @@ public class ScriptCommand extends ScriptBase {
return id; return id;
} }
// called from the script /**
* Combine a BLOB.
* This method is called from the script.
* When calling with id -1, the file is deleted.
*
* @param conn a connection
* @param id the lob id
* @return a stream for the combined data
*/
public static InputStream combineBlob(Connection conn, int id) throws SQLException, IOException { public static InputStream combineBlob(Connection conn, int id) throws SQLException, IOException {
if (id < 0) { if (id < 0) {
FileUtils.delete(TEMP_LOB_FILENAME); FileUtils.delete(TEMP_LOB_FILENAME);
return null; return null;
} }
Statement stat = conn.createStatement(); ResultSet rs = getLobStream(conn, "BDATA", id);
ResultSet rs = stat.executeQuery("SELECT BDATA FROM SYSTEM_LOB_STREAM WHERE ID=" + id + " ORDER BY PART");
OutputStream out = FileUtils.openFileOutputStream(TEMP_LOB_FILENAME, false); OutputStream out = FileUtils.openFileOutputStream(TEMP_LOB_FILENAME, false);
while (rs.next()) { while (rs.next()) {
InputStream in = rs.getBinaryStream(1); InputStream in = rs.getBinaryStream(1);
IOUtils.copyAndCloseInput(in, out); IOUtils.copyAndCloseInput(in, out);
} }
out.close(); out.close();
stat.execute("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=" + id); deleteLobStream(conn, id);
return FileUtils.openFileInputStream(TEMP_LOB_FILENAME); return openAutoCloseInput();
} }
// called from the script /**
* Combine a CLOB.
* This method is called from the script.
*
* @param conn a connection
* @param id the lob id
* @return a reader for the combined data
*/
public static Reader combineClob(Connection conn, int id) throws SQLException, IOException { public static Reader combineClob(Connection conn, int id) throws SQLException, IOException {
Statement stat = conn.createStatement(); ResultSet rs = getLobStream(conn, "CDATA", id);
ResultSet rs = stat.executeQuery("SELECT CDATA FROM SYSTEM_LOB_STREAM WHERE ID=" + id + " ORDER BY PART");
Writer out = FileUtils.openFileWriter(TEMP_LOB_FILENAME, false); Writer out = FileUtils.openFileWriter(TEMP_LOB_FILENAME, false);
while (rs.next()) { while (rs.next()) {
Reader in = new BufferedReader(rs.getCharacterStream(1)); Reader in = new BufferedReader(rs.getCharacterStream(1));
IOUtils.copyAndCloseInput(in, out); IOUtils.copyAndCloseInput(in, out);
} }
out.close(); out.close();
stat.execute("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=" + id); deleteLobStream(conn, id);
return FileUtils.openFileReader(TEMP_LOB_FILENAME); return IOUtils.getReader(openAutoCloseInput());
}
private static ResultSet getLobStream(Connection conn, String column, int id) throws SQLException {
PreparedStatement prep = conn.prepareStatement(
"SELECT " + column + " FROM SYSTEM_LOB_STREAM WHERE ID=? ORDER BY PART");
prep.setInt(1, id);
return prep.executeQuery();
}
private static void deleteLobStream(Connection conn, int id) throws SQLException {
PreparedStatement prep = conn.prepareStatement("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=?");
prep.setInt(1, id);
prep.execute();
}
private static InputStream openAutoCloseInput() throws IOException {
InputStream in = FileUtils.openFileInputStream(TEMP_LOB_FILENAME);
in = new BufferedInputStream(in);
return new AutoCloseInputStream(in);
} }
private void reset() throws SQLException { private void reset() throws SQLException {
......
...@@ -9,6 +9,7 @@ import java.util.HashMap; ...@@ -9,6 +9,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Alias; import org.h2.expression.Alias;
import org.h2.expression.Comparison; import org.h2.expression.Comparison;
...@@ -18,9 +19,12 @@ import org.h2.expression.ExpressionColumn; ...@@ -18,9 +19,12 @@ import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor; import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter; import org.h2.expression.Parameter;
import org.h2.expression.Wildcard; import org.h2.expression.Wildcard;
import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.LocalResult; import org.h2.result.LocalResult;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
...@@ -62,7 +66,7 @@ public class Select extends Query { ...@@ -62,7 +66,7 @@ public class Select extends Query {
private boolean isGroupQuery; private boolean isGroupQuery;
private boolean isForUpdate; private boolean isForUpdate;
private double cost; private double cost;
private boolean isQuickQuery; private boolean isQuickQuery, isDistinctQuery;
private boolean isPrepared, checkInit; private boolean isPrepared, checkInit;
private boolean sortUsingIndex; private boolean sortUsingIndex;
private SortOrder sort; private SortOrder sort;
...@@ -272,6 +276,42 @@ public class Select extends Query { ...@@ -272,6 +276,42 @@ public class Select extends Query {
return null; return null;
} }
private void queryDistinct(int columnCount, LocalResult result, long limitRows) throws SQLException {
if (limitRows != 0 && offset != null) {
// limitRows must be long, otherwise we get an int overflow if limitRows is at or near Integer.MAX_VALUE
limitRows += offset.getValue(session).getInt();
}
int rowNumber = 0;
setCurrentRowNumber(0);
Index index = topTableFilter.getIndex();
SearchRow first = null;
int columnIndex = index.getColumns()[0].getColumnId();
while (true) {
checkCancelled();
setCurrentRowNumber(rowNumber + 1);
Cursor cursor = index.findNext(session, first, null);
if (!cursor.next()) {
break;
}
SearchRow found = cursor.getSearchRow();
Value value = found.getValue(columnIndex);
if (first == null) {
first = topTableFilter.getTable().getTemplateSimpleRow(true);
}
first.setValue(columnIndex, value);
Value[] row = new Value[1];
row[0] = value;
result.addRow(row);
rowNumber++;
if ((sort == null || sortUsingIndex) && limitRows != 0 && result.getRowCount() >= limitRows) {
break;
}
if (sampleSize > 0 && rowNumber >= sampleSize) {
break;
}
}
}
private void queryFlat(int columnCount, LocalResult result, long limitRows) throws SQLException { private void queryFlat(int columnCount, LocalResult result, long limitRows) throws SQLException {
if (limitRows != 0 && offset != null) { if (limitRows != 0 && offset != null) {
// limitRows must be long, otherwise we get an int overflow if limitRows is at or near Integer.MAX_VALUE // limitRows must be long, otherwise we get an int overflow if limitRows is at or near Integer.MAX_VALUE
...@@ -330,7 +370,7 @@ public class Select extends Query { ...@@ -330,7 +370,7 @@ public class Select extends Query {
if (!sortUsingIndex) { if (!sortUsingIndex) {
result.setSortOrder(sort); result.setSortOrder(sort);
} }
if (distinct) { if (distinct && !isDistinctQuery) {
result.setDistinct(); result.setDistinct();
} }
topTableFilter.startQuery(session); topTableFilter.startQuery(session);
...@@ -340,6 +380,8 @@ public class Select extends Query { ...@@ -340,6 +380,8 @@ public class Select extends Query {
queryQuick(columnCount, result); queryQuick(columnCount, result);
} else if (isGroupQuery) { } else if (isGroupQuery) {
queryGroup(columnCount, result); queryGroup(columnCount, result);
} else if (isDistinctQuery) {
queryDistinct(columnCount, result, limitRows);
} else { } else {
queryFlat(columnCount, result, limitRows); queryFlat(columnCount, result, limitRows);
} }
...@@ -513,6 +555,31 @@ public class Select extends Query { ...@@ -513,6 +555,31 @@ public class Select extends Query {
sortUsingIndex = true; sortUsingIndex = true;
} }
} }
if (SysProperties.OPTIMIZE_DISTINCT && distinct && !isGroupQuery && filters.size() == 1 && expressions.size() == 1 && condition == null) {
Expression expr = (Expression) expressions.get(0);
expr = expr.getNonAliasExpression();
if (expr instanceof ExpressionColumn) {
Column column = ((ExpressionColumn) expr).getColumn();
int selectivity = column.getSelectivity();
Index columnIndex = topTableFilter.getTable().getIndexForColumn(column, true);
if (columnIndex != null && selectivity != Constants.SELECTIVITY_DEFAULT && selectivity < 20) {
// the first column must be ascending
boolean ascending = columnIndex.getIndexColumns()[0].sortType == SortOrder.ASCENDING;
Index current = topTableFilter.getIndex();
// if another index is faster
if (columnIndex.canFindNext() && ascending && (current == null || current.getIndexType().isScan() || columnIndex == current)) {
IndexType type = columnIndex.getIndexType();
// hash indexes don't work, and unique single column indexes don't work
if (!type.isHash() && (!type.isUnique() || columnIndex.getColumns().length > 1)) {
topTableFilter.setIndex(columnIndex);
isDistinctQuery = true;
int test;
System.out.println("##distinct: " + this.sql);
}
}
}
}
}
} }
public double getCost() { public double getCost() {
...@@ -647,11 +714,12 @@ public class Select extends Query { ...@@ -647,11 +714,12 @@ public class Select extends Query {
if (isForUpdate) { if (isForUpdate) {
buff.append("\nFOR UPDATE"); buff.append("\nFOR UPDATE");
} }
if (isQuickQuery) { if (isQuickQuery) {
buff.append("\n/* direct lookup query */"); buff.append("\n/* direct lookup */");
}
if (isDistinctQuery) {
buff.append("\n/* distinct */");
} }
return buff.toString(); return buff.toString();
} }
......
...@@ -35,6 +35,7 @@ public class SysProperties { ...@@ -35,6 +35,7 @@ public class SysProperties {
public static final boolean OPTIMIZE_IN_JOIN = getBooleanSetting("h2.optimizeInJoin", false); public static final boolean OPTIMIZE_IN_JOIN = getBooleanSetting("h2.optimizeInJoin", false);
public static final boolean OPTIMIZE_DISTINCT = getBooleanSetting("h2.optimizeDistinct", true);
public static final boolean OPTIMIZE_MIN_MAX = getBooleanSetting("h2.optimizeMinMax", true); public static final boolean OPTIMIZE_MIN_MAX = getBooleanSetting("h2.optimizeMinMax", true);
public static final boolean OPTIMIZE_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true); public static final boolean OPTIMIZE_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true);
public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true); public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true);
...@@ -45,7 +46,7 @@ public class SysProperties { ...@@ -45,7 +46,7 @@ public class SysProperties {
public static final boolean LOG_ALL_ERRORS = getBooleanSetting("h2.logAllErrors", false); public static final boolean LOG_ALL_ERRORS = getBooleanSetting("h2.logAllErrors", false);
public static final String LOG_ALL_ERRORS_FILE = getStringSetting("h2.logAllErrorsFile", "h2errors.txt"); public static final String LOG_ALL_ERRORS_FILE = getStringSetting("h2.logAllErrorsFile", "h2errors.txt");
public static final int SERVER_CACHED_OBJECTS = getIntSetting("h2.serverCachedObjects", 64); public static final int SERVER_CACHED_OBJECTS = getIntSetting("h2.serverCachedObjects", 64);
public static final int SERVER_SMALL_RESULT_SET_SIZE = getIntSetting("h2.serverSmallResultSetSize", 100); public static final int SERVER_RESULT_SET_FETCH_SIZE = getIntSetting("h2.serverResultSetFetchSize", 100);
public static final int EMERGENCY_SPACE_INITIAL = getIntSetting("h2.emergencySpaceInitial", 256 * 1024); public static final int EMERGENCY_SPACE_INITIAL = getIntSetting("h2.emergencySpaceInitial", 256 * 1024);
public static final int EMERGENCY_SPACE_MIN = getIntSetting("h2.emergencySpaceMin", 64 * 1024); public static final int EMERGENCY_SPACE_MIN = getIntSetting("h2.emergencySpaceMin", 64 * 1024);
public static final boolean OBJECT_CACHE = getBooleanSetting("h2.objectCache", true); public static final boolean OBJECT_CACHE = getBooleanSetting("h2.objectCache", true);
......
...@@ -81,7 +81,7 @@ public class Constants { ...@@ -81,7 +81,7 @@ public class Constants {
public static final int FILE_BLOCK_SIZE = 16; public static final int FILE_BLOCK_SIZE = 16;
public static final String MAGIC_FILE_HEADER_TEXT = "-- H2 0.5/T -- ".substring(0, FILE_BLOCK_SIZE - 1) + "\n"; public static final String MAGIC_FILE_HEADER_TEXT = "-- H2 0.5/T -- ".substring(0, FILE_BLOCK_SIZE - 1) + "\n";
public static final String MAGIC_FILE_HEADER = "-- H2 0.5/B -- ".substring(0, FILE_BLOCK_SIZE - 1) + "\n"; public static final String MAGIC_FILE_HEADER = "-- H2 0.5/B -- ".substring(0, FILE_BLOCK_SIZE - 1) + "\n";
public static final int TCP_DRIVER_VERSION = 4; public static final int TCP_DRIVER_VERSION = 5;
public static final int VERSION_JDBC_MAJOR = 3; public static final int VERSION_JDBC_MAJOR = 3;
public static final int VERSION_JDBC_MINOR = 0; public static final int VERSION_JDBC_MINOR = 0;
......
...@@ -116,7 +116,7 @@ public class Engine { ...@@ -116,7 +116,7 @@ public class Engine {
String value = ci.getProperty(setting); String value = ci.getProperty(setting);
try { try {
CommandInterface command = session.prepareCommand("SET " + Parser.quoteIdentifier(setting) + " " CommandInterface command = session.prepareCommand("SET " + Parser.quoteIdentifier(setting) + " "
+ value); + value, Integer.MAX_VALUE);
command.executeUpdate(); command.executeUpdate();
} catch (SQLException e) { } catch (SQLException e) {
if (!ignoreUnknownSetting) { if (!ignoreUnknownSetting) {
......
...@@ -154,7 +154,7 @@ public class Session implements SessionInterface { ...@@ -154,7 +154,7 @@ public class Session implements SessionInterface {
this.currentSchemaName = Constants.SCHEMA_MAIN; this.currentSchemaName = Constants.SCHEMA_MAIN;
} }
public CommandInterface prepareCommand(String sql) throws SQLException { public CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException {
return prepareLocal(sql); return prepareLocal(sql);
} }
......
...@@ -19,9 +19,10 @@ public interface SessionInterface { ...@@ -19,9 +19,10 @@ public interface SessionInterface {
* Parse a command and prepare it for execution. * Parse a command and prepare it for execution.
* *
* @param sql the SQL statement * @param sql the SQL statement
* @param fetchSize the number of rows to fetch in one step
* @return the prepared command * @return the prepared command
*/ */
CommandInterface prepareCommand(String sql) throws SQLException; CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException;
/** /**
* Roll back pending transactions and close the session. * Roll back pending transactions and close the session.
......
...@@ -38,7 +38,7 @@ public class SessionRemote implements SessionInterface, DataHandler { ...@@ -38,7 +38,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
public static final int COMMAND_EXECUTE_QUERY = 2; public static final int COMMAND_EXECUTE_QUERY = 2;
public static final int COMMAND_EXECUTE_UPDATE = 3; public static final int COMMAND_EXECUTE_UPDATE = 3;
public static final int COMMAND_CLOSE = 4; public static final int COMMAND_CLOSE = 4;
public static final int RESULT_FETCH_ROW = 5; public static final int RESULT_FETCH_ROWS = 5;
public static final int RESULT_RESET = 6; public static final int RESULT_RESET = 6;
public static final int RESULT_CLOSE = 7; public static final int RESULT_CLOSE = 7;
public static final int COMMAND_COMMIT = 8; public static final int COMMAND_COMMIT = 8;
...@@ -92,7 +92,7 @@ public class SessionRemote implements SessionInterface, DataHandler { ...@@ -92,7 +92,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
private void switchOffAutoCommitIfCluster() throws SQLException { private void switchOffAutoCommitIfCluster() throws SQLException {
if (autoCommit && transferList.size() > 1) { if (autoCommit && transferList.size() > 1) {
if (switchOffAutoCommit == null) { if (switchOffAutoCommit == null) {
switchOffAutoCommit = prepareCommand("SET AUTOCOMMIT FALSE"); switchOffAutoCommit = prepareCommand("SET AUTOCOMMIT FALSE", Integer.MAX_VALUE);
} }
// this will call setAutoCommit(false) // this will call setAutoCommit(false)
switchOffAutoCommit.executeUpdate(); switchOffAutoCommit.executeUpdate();
...@@ -225,7 +225,7 @@ public class SessionRemote implements SessionInterface, DataHandler { ...@@ -225,7 +225,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
} }
private void switchOffCluster() throws SQLException { private void switchOffCluster() throws SQLException {
CommandInterface ci = prepareCommand("SET CLUSTER ''"); CommandInterface ci = prepareCommand("SET CLUSTER ''", Integer.MAX_VALUE);
ci.executeUpdate(); ci.executeUpdate();
} }
...@@ -235,10 +235,10 @@ public class SessionRemote implements SessionInterface, DataHandler { ...@@ -235,10 +235,10 @@ public class SessionRemote implements SessionInterface, DataHandler {
switchOffCluster(); switchOffCluster();
} }
public CommandInterface prepareCommand(String sql) throws SQLException { public CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException {
synchronized (this) { synchronized (this) {
checkClosed(); checkClosed();
return new CommandRemote(this, transferList, sql); return new CommandRemote(this, transferList, sql, fetchSize);
} }
} }
......
...@@ -85,6 +85,14 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -85,6 +85,14 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
public abstract Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException; public abstract Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException;
public boolean canFindNext() {
return false;
}
public Cursor findNext(Session session, SearchRow first, SearchRow last) throws SQLException {
throw Message.getInternalError();
}
public abstract double getCost(Session session, int[] masks) throws SQLException; public abstract double getCost(Session session, int[] masks) throws SQLException;
public abstract void remove(Session session) throws SQLException; public abstract void remove(Session session) throws SQLException;
......
...@@ -185,7 +185,19 @@ public class BtreeIndex extends BaseIndex implements RecordReader { ...@@ -185,7 +185,19 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
} }
} }
public boolean canFindNext() {
return true;
}
public Cursor findNext(Session session, SearchRow first, SearchRow last) throws SQLException {
return find(session, first, true, last);
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException { public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
return find(session, first, false, last);
}
public Cursor find(Session session, SearchRow first, boolean bigger, SearchRow last) throws SQLException {
if (SysProperties.CHECK && storage == null) { if (SysProperties.CHECK && storage == null) {
throw Message.getSQLException(ErrorCode.OBJECT_CLOSED); throw Message.getSQLException(ErrorCode.OBJECT_CLOSED);
} }
...@@ -196,7 +208,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader { ...@@ -196,7 +208,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return cursor; return cursor;
} else { } else {
BtreeCursor cursor = new BtreeCursor(session, this, last); BtreeCursor cursor = new BtreeCursor(session, this, last);
if (getRowCount(session) == 0 || !root.findFirst(cursor, first)) { if (getRowCount(session) == 0 || !root.findFirst(cursor, first, bigger)) {
cursor.setCurrentRow(null); cursor.setCurrentRow(null);
} }
return cursor; return cursor;
...@@ -301,7 +313,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader { ...@@ -301,7 +313,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException { public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
if (first) { if (first) {
// TODO optimization: this loops through NULL elements // TODO optimization: this loops through NULL elements
Cursor cursor = find(session, null, null); Cursor cursor = find(session, null, false, null);
while (cursor.next()) { while (cursor.next()) {
SearchRow row = cursor.getSearchRow(); SearchRow row = cursor.getSearchRow();
Value v = row.getValue(columnIds[0]); Value v = row.getValue(columnIds[0]);
......
...@@ -144,7 +144,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -144,7 +144,7 @@ public class BtreeLeaf extends BtreePage {
return n2; return n2;
} }
public boolean findFirst(BtreeCursor cursor, SearchRow compare) throws SQLException { public boolean findFirst(BtreeCursor cursor, SearchRow compare, boolean bigger) throws SQLException {
int l = 0, r = pageData.size(); int l = 0, r = pageData.size();
if (r == 0 && !Constants.ALLOW_EMPTY_BTREE_PAGES && !root) { if (r == 0 && !Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page"); throw Message.getInternalError("Empty btree page");
...@@ -153,7 +153,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -153,7 +153,7 @@ public class BtreeLeaf extends BtreePage {
int i = (l + r) >>> 1; int i = (l + r) >>> 1;
SearchRow row = (SearchRow) pageData.get(i); SearchRow row = (SearchRow) pageData.get(i);
int comp = index.compareRows(row, compare); int comp = index.compareRows(row, compare);
if (comp >= 0) { if (comp > 0 || (!bigger && comp == 0)) {
r = i; r = i;
} else { } else {
l = i + 1; l = i + 1;
......
...@@ -207,7 +207,7 @@ public class BtreeNode extends BtreePage { ...@@ -207,7 +207,7 @@ public class BtreeNode extends BtreePage {
return pageChildren.get(i); return pageChildren.get(i);
} }
public boolean findFirst(BtreeCursor cursor, SearchRow compare) throws SQLException { public boolean findFirst(BtreeCursor cursor, SearchRow compare, boolean bigger) throws SQLException {
int l = 0, r = pageData.size(); int l = 0, r = pageData.size();
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) { if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page"); throw Message.getInternalError("Empty btree page");
...@@ -216,7 +216,7 @@ public class BtreeNode extends BtreePage { ...@@ -216,7 +216,7 @@ public class BtreeNode extends BtreePage {
int i = (l + r) >>> 1; int i = (l + r) >>> 1;
SearchRow row = getData(i); SearchRow row = getData(i);
int comp = index.compareRows(row, compare); int comp = index.compareRows(row, compare);
if (comp >= 0) { if (comp > 0 || (!bigger && comp == 0)) {
r = i; r = i;
} else { } else {
l = i + 1; l = i + 1;
...@@ -225,7 +225,7 @@ public class BtreeNode extends BtreePage { ...@@ -225,7 +225,7 @@ public class BtreeNode extends BtreePage {
if (l >= pageData.size()) { if (l >= pageData.size()) {
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l)); BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l); cursor.push(this, l);
boolean result = page.findFirst(cursor, compare); boolean result = page.findFirst(cursor, compare, bigger);
if (result) { if (result) {
return true; return true;
} }
...@@ -234,7 +234,7 @@ public class BtreeNode extends BtreePage { ...@@ -234,7 +234,7 @@ public class BtreeNode extends BtreePage {
} }
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l)); BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l); cursor.push(this, l);
if (page.findFirst(cursor, compare)) { if (page.findFirst(cursor, compare, bigger)) {
return true; return true;
} }
cursor.pop(); cursor.pop();
...@@ -245,7 +245,7 @@ public class BtreeNode extends BtreePage { ...@@ -245,7 +245,7 @@ public class BtreeNode extends BtreePage {
if (comp >= 0) { if (comp >= 0) {
page = index.getPage(cursor.getSession(), pageChildren.get(i)); page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i); cursor.push(this, i);
if (page.findFirst(cursor, compare)) { if (page.findFirst(cursor, compare, bigger)) {
return true; return true;
} }
cursor.pop(); cursor.pop();
...@@ -253,7 +253,7 @@ public class BtreeNode extends BtreePage { ...@@ -253,7 +253,7 @@ public class BtreeNode extends BtreePage {
} }
page = index.getPage(cursor.getSession(), pageChildren.get(i)); page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i); cursor.push(this, i);
boolean result = page.findFirst(cursor, compare); boolean result = page.findFirst(cursor, compare, bigger);
if (result) { if (result) {
return true; return true;
} }
......
...@@ -39,7 +39,7 @@ public abstract class BtreePage extends Record { ...@@ -39,7 +39,7 @@ public abstract class BtreePage extends Record {
// Returns the new first row in the list; null if no change; the deleted row if not empty // Returns the new first row in the list; null if no change; the deleted row if not empty
abstract SearchRow remove(Session session, Row row, int level) throws SQLException; abstract SearchRow remove(Session session, Row row, int level) throws SQLException;
abstract BtreePage split(Session session, int splitPoint) throws SQLException; abstract BtreePage split(Session session, int splitPoint) throws SQLException;
abstract boolean findFirst(BtreeCursor cursor, SearchRow row) throws SQLException; abstract boolean findFirst(BtreeCursor cursor, SearchRow row, boolean bigger) throws SQLException;
abstract SearchRow getFirst(Session session) throws SQLException; abstract SearchRow getFirst(Session session) throws SQLException;
abstract SearchRow getLast(Session session) throws SQLException; abstract SearchRow getLast(Session session) throws SQLException;
abstract void next(BtreeCursor cursor, int i) throws SQLException; abstract void next(BtreeCursor cursor, int i) throws SQLException;
......
...@@ -101,6 +101,24 @@ public interface Index extends SchemaObject { ...@@ -101,6 +101,24 @@ public interface Index extends SchemaObject {
*/ */
boolean canGetFirstOrLast(); boolean canGetFirstOrLast();
/**
* Check if the index can get the next higher value.
*
* @return true if it can
*/
boolean canFindNext();
/**
* Find a row or a list of rows that is larger and create a cursor to iterate over the result.
*
* @param session the session
* @param higherThan the lower limit (excluding)
* @param last the last row, or null for no limit
* @return the cursor
*/
Cursor findNext(Session session, SearchRow higherThan, SearchRow last) throws SQLException;
/** /**
* Find the lowest or highest value of a column. * Find the lowest or highest value of a column.
* *
......
...@@ -8,7 +8,7 @@ package org.h2.index; ...@@ -8,7 +8,7 @@ package org.h2.index;
* Represents information about the properties of an index * Represents information about the properties of an index
*/ */
public class IndexType { public class IndexType {
private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan, isDescending; private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan;
private boolean belongsToConstraint; private boolean belongsToConstraint;
public static IndexType createPrimaryKey(boolean persistent, boolean hash) { public static IndexType createPrimaryKey(boolean persistent, boolean hash) {
...@@ -41,14 +41,6 @@ public class IndexType { ...@@ -41,14 +41,6 @@ public class IndexType {
return type; return type;
} }
public void setDescending(boolean descending) {
this.isDescending = descending;
}
public boolean getDescending() {
return isDescending;
}
public void setBelongsToConstraint(boolean belongsToConstraint) { public void setBelongsToConstraint(boolean belongsToConstraint) {
this.belongsToConstraint = belongsToConstraint; this.belongsToConstraint = belongsToConstraint;
} }
......
...@@ -63,6 +63,15 @@ public class MultiVersionIndex implements Index { ...@@ -63,6 +63,15 @@ public class MultiVersionIndex implements Index {
} }
} }
public Cursor findNext(Session session, SearchRow first, SearchRow last) throws SQLException {
throw Message.getInternalError();
}
public boolean canFindNext() {
// TODO possible, but more complicated
return false;
}
public boolean canGetFirstOrLast() { public boolean canGetFirstOrLast() {
// TODO in many cases possible, but more complicated // TODO in many cases possible, but more complicated
return false; return false;
......
...@@ -453,7 +453,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -453,7 +453,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeCall("getCatalog"); debugCodeCall("getCatalog");
checkClosed(); checkClosed();
if (catalog == null) { if (catalog == null) {
CommandInterface cat = prepareCommand("CALL DATABASE()"); CommandInterface cat = prepareCommand("CALL DATABASE()", Integer.MAX_VALUE);
ResultInterface result = cat.executeQuery(0, false); ResultInterface result = cat.executeQuery(0, false);
result.next(); result.next();
catalog = result.currentRow()[0].getString(); catalog = result.currentRow()[0].getString();
...@@ -743,7 +743,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -743,7 +743,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint()"); debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint()");
} }
checkClosed(); checkClosed();
CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(null, savepointId)); CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(null, savepointId), Integer.MAX_VALUE);
set.executeUpdate(); set.executeUpdate();
JdbcSavepoint savepoint = new JdbcSavepoint(this, savepointId, null, trace, id); JdbcSavepoint savepoint = new JdbcSavepoint(this, savepointId, null, trace, id);
savepointId++; savepointId++;
...@@ -767,7 +767,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -767,7 +767,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint(" + quote(name) + ")"); debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint(" + quote(name) + ")");
} }
checkClosed(); checkClosed();
CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(name, 0)); CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(name, 0), Integer.MAX_VALUE);
set.executeUpdate(); set.executeUpdate();
JdbcSavepoint savepoint = new JdbcSavepoint(this, 0, name, trace, id); JdbcSavepoint savepoint = new JdbcSavepoint(this, 0, name, trace, id);
return savepoint; return savepoint;
...@@ -957,12 +957,12 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -957,12 +957,12 @@ public class JdbcConnection extends TraceObject implements Connection {
} }
} }
CommandInterface prepareCommand(String sql) throws SQLException { CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException {
return session.prepareCommand(sql); return session.prepareCommand(sql, fetchSize);
} }
CommandInterface prepareCommand(String sql, CommandInterface old) throws SQLException { CommandInterface prepareCommand(String sql, CommandInterface old) throws SQLException {
return old == null ? session.prepareCommand(sql) : old; return old == null ? session.prepareCommand(sql, Integer.MAX_VALUE) : old;
} }
private int translateGetEnd(String sql, int i, char c) throws SQLException { private int translateGetEnd(String sql, int i, char c) throws SQLException {
......
...@@ -1203,7 +1203,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat ...@@ -1203,7 +1203,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
JdbcPreparedStatement(SessionInterface session, JdbcConnection conn, String sql, int resultSetType, int id, boolean closeWithResultSet) throws SQLException { JdbcPreparedStatement(SessionInterface session, JdbcConnection conn, String sql, int resultSetType, int id, boolean closeWithResultSet) throws SQLException {
super(session, conn, resultSetType, id, closeWithResultSet); super(session, conn, resultSetType, id, closeWithResultSet);
setTrace(session.getTrace(), TraceObject.PREPARED_STATEMENT, id); setTrace(session.getTrace(), TraceObject.PREPARED_STATEMENT, id);
command = conn.prepareCommand(sql); command = conn.prepareCommand(sql, fetchSize);
} }
private void setParameter(int parameterIndex, Value value) throws SQLException { private void setParameter(int parameterIndex, Value value) throws SQLException {
......
...@@ -38,18 +38,18 @@ public class JdbcSQLException extends SQLException { ...@@ -38,18 +38,18 @@ public class JdbcSQLException extends SQLException {
buildMessage(); buildMessage();
//#ifdef JDK14 //#ifdef JDK14
initCause(cause); initCause(cause);
//#endif //#endif
} }
/** /**
* Get the detail error message. * Get the detail error message.
* *
* @return the message * @return the message
*/ */
public String getMessage() { public String getMessage() {
return message; return message;
} }
/** /**
* INTERNAL * INTERNAL
*/ */
...@@ -62,13 +62,13 @@ public class JdbcSQLException extends SQLException { ...@@ -62,13 +62,13 @@ public class JdbcSQLException extends SQLException {
*/ */
public void printStackTrace() { public void printStackTrace() {
super.printStackTrace(); super.printStackTrace();
//#ifdef JDK13 //#ifdef JDK13
/* /*
if (cause != null) { if (cause != null) {
cause.printStackTrace(); cause.printStackTrace();
} }
*/ */
//#endif //#endif
if (getNextException() != null) { if (getNextException() != null) {
getNextException().printStackTrace(); getNextException().printStackTrace();
} }
...@@ -88,7 +88,7 @@ public class JdbcSQLException extends SQLException { ...@@ -88,7 +88,7 @@ public class JdbcSQLException extends SQLException {
cause.printStackTrace(s); cause.printStackTrace(s);
} }
*/ */
//#endif //#endif
if (getNextException() != null) { if (getNextException() != null) {
getNextException().printStackTrace(s); getNextException().printStackTrace(s);
} }
...@@ -109,7 +109,7 @@ public class JdbcSQLException extends SQLException { ...@@ -109,7 +109,7 @@ public class JdbcSQLException extends SQLException {
cause.printStackTrace(s); cause.printStackTrace(s);
} }
*/ */
//#endif //#endif
if (getNextException() != null) { if (getNextException() != null) {
getNextException().printStackTrace(s); getNextException().printStackTrace(s);
} }
...@@ -122,26 +122,26 @@ public class JdbcSQLException extends SQLException { ...@@ -122,26 +122,26 @@ public class JdbcSQLException extends SQLException {
public Throwable getOriginalCause() { public Throwable getOriginalCause() {
return cause; return cause;
} }
/** /**
* Returns the SQL statement. * Returns the SQL statement.
* *
* @return the SQL statement * @return the SQL statement
*/ */
public String getSQL() { public String getSQL() {
return sql; return sql;
} }
/** /**
* INTERNAL * INTERNAL
*/ */
public void setSQL(String sql) { public void setSQL(String sql) {
this.sql = sql; this.sql = sql;
buildMessage(); buildMessage();
} }
private void buildMessage() { private void buildMessage() {
StringBuffer buff = new StringBuffer(originalMessage); StringBuffer buff = new StringBuffer(originalMessage == null ? "- " : originalMessage);
if (sql != null) { if (sql != null) {
buff.append("; SQL statement:\n"); buff.append("; SQL statement:\n");
buff.append(sql); buff.append(sql);
...@@ -156,9 +156,9 @@ public class JdbcSQLException extends SQLException { ...@@ -156,9 +156,9 @@ public class JdbcSQLException extends SQLException {
/** /**
* Returns the class name, the message, and in the server mode, the stack trace of the server * Returns the class name, the message, and in the server mode, the stack trace of the server
* *
* @return the string representation * @return the string representation
*/ */
public String toString() { public String toString() {
if (trace == null) { if (trace == null) {
return super.toString(); return super.toString();
......
...@@ -16,10 +16,10 @@ import org.h2.message.TraceObject; ...@@ -16,10 +16,10 @@ import org.h2.message.TraceObject;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
* A savepoint is a point inside a transaction to where a transaction can be rolled back. * A savepoint is a point inside a transaction to where a transaction can be rolled back.
* The tasks that where done before the savepoint are not rolled back in this case. * The tasks that where done before the savepoint are not rolled back in this case.
*/ */
public class JdbcSavepoint extends TraceObject public class JdbcSavepoint extends TraceObject
//#ifdef JDK14 //#ifdef JDK14
implements Savepoint implements Savepoint
//#endif //#endif
...@@ -52,7 +52,7 @@ implements Savepoint ...@@ -52,7 +52,7 @@ implements Savepoint
void rollback() throws SQLException { void rollback() throws SQLException {
checkValid(); checkValid();
conn.prepareCommand("ROLLBACK TO SAVEPOINT " + getName(name, savepointId)).executeUpdate(); conn.prepareCommand("ROLLBACK TO SAVEPOINT " + getName(name, savepointId), Integer.MAX_VALUE).executeUpdate();
} }
private void checkValid() throws SQLException { private void checkValid() throws SQLException {
...@@ -77,11 +77,11 @@ implements Savepoint ...@@ -77,11 +77,11 @@ implements Savepoint
throw logAndConvert(e); throw logAndConvert(e);
} }
} }
/** /**
* Get the name of this savepoint. * Get the name of this savepoint.
* @return the name * @return the name
*/ */
public String getSavepointName() throws SQLException { public String getSavepointName() throws SQLException {
try { try {
debugCodeCall("getSavepointName"); debugCodeCall("getSavepointName");
...@@ -94,13 +94,13 @@ implements Savepoint ...@@ -94,13 +94,13 @@ implements Savepoint
throw logAndConvert(e); throw logAndConvert(e);
} }
} }
/** /**
* INTERNAL * INTERNAL
*/ */
public String toString() { public String toString() {
return getTraceObjectName() + ": id=" + savepointId + " name=" + name; return getTraceObjectName() + ": id=" + savepointId + " name=" + name;
} }
} }
...@@ -14,6 +14,7 @@ import java.sql.Statement; ...@@ -14,6 +14,7 @@ import java.sql.Statement;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.SessionInterface; import org.h2.engine.SessionInterface;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
...@@ -32,9 +33,8 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -32,9 +33,8 @@ public class JdbcStatement extends TraceObject implements Statement {
protected boolean escapeProcessing = true; protected boolean escapeProcessing = true;
protected int queryTimeout; protected int queryTimeout;
protected boolean queryTimeoutSet; protected boolean queryTimeoutSet;
protected int fetchSize; protected int fetchSize = SysProperties.SERVER_RESULT_SET_FETCH_SIZE;
protected boolean fetchSizeSet; ; protected int updateCount;
protected int updateCount;
private CommandInterface executingCommand; private CommandInterface executingCommand;
private ObjectArray batchCommands; private ObjectArray batchCommands;
protected int resultSetType; protected int resultSetType;
...@@ -59,7 +59,7 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -59,7 +59,7 @@ public class JdbcStatement extends TraceObject implements Statement {
sql = conn.translateSQL(sql); sql = conn.translateSQL(sql);
} }
synchronized (session) { synchronized (session) {
CommandInterface command = conn.prepareCommand(sql); CommandInterface command = conn.prepareCommand(sql, fetchSize);
ResultInterface result; ResultInterface result;
boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY; boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY;
setExecutingStatement(command); setExecutingStatement(command);
...@@ -102,7 +102,7 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -102,7 +102,7 @@ public class JdbcStatement extends TraceObject implements Statement {
if (escapeProcessing) { if (escapeProcessing) {
sql = conn.translateSQL(sql); sql = conn.translateSQL(sql);
} }
CommandInterface command = conn.prepareCommand(sql); CommandInterface command = conn.prepareCommand(sql, fetchSize);
synchronized (session) { synchronized (session) {
setExecutingStatement(command); setExecutingStatement(command);
try { try {
...@@ -140,7 +140,7 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -140,7 +140,7 @@ public class JdbcStatement extends TraceObject implements Statement {
if (escapeProcessing) { if (escapeProcessing) {
sql = conn.translateSQL(sql); sql = conn.translateSQL(sql);
} }
CommandInterface command = conn.prepareCommand(sql); CommandInterface command = conn.prepareCommand(sql, fetchSize);
boolean returnsResultSet; boolean returnsResultSet;
synchronized (session) { synchronized (session) {
setExecutingStatement(command); setExecutingStatement(command);
...@@ -369,7 +369,9 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -369,7 +369,9 @@ public class JdbcStatement extends TraceObject implements Statement {
* Sets the number of rows suggested to read in one step. * Sets the number of rows suggested to read in one step.
* This value cannot be higher than the maximum rows (setMaxRows) * This value cannot be higher than the maximum rows (setMaxRows)
* set by the statement or prepared statement, otherwise an exception * set by the statement or prepared statement, otherwise an exception
* is throws. * is throws. Setting the value to 0 will set the default value.
* The default value can be changed using the system property
* h2.serverResultSetFetchSize.
* *
* @param rows the number of rows * @param rows the number of rows
* @throws SQLException if this object is closed * @throws SQLException if this object is closed
...@@ -381,8 +383,10 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -381,8 +383,10 @@ public class JdbcStatement extends TraceObject implements Statement {
if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) { if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) {
throw Message.getInvalidValueException("" + rows, "rows"); throw Message.getInvalidValueException("" + rows, "rows");
} }
if (rows == 0) {
rows = SysProperties.SERVER_RESULT_SET_FETCH_SIZE;
}
fetchSize = rows; fetchSize = rows;
fetchSizeSet = true;
} catch (Throwable e) { } catch (Throwable e) {
throw logAndConvert(e); throw logAndConvert(e);
} }
...@@ -893,13 +897,13 @@ public class JdbcStatement extends TraceObject implements Statement { ...@@ -893,13 +897,13 @@ public class JdbcStatement extends TraceObject implements Statement {
debugCode("setPoolable("+poolable+");"); debugCode("setPoolable("+poolable+");");
} }
} }
/** /**
* INTERNAL * INTERNAL
*/ */
public String toString() { public String toString() {
return getTraceObjectName(); return getTraceObjectName();
} }
} }
...@@ -174,7 +174,7 @@ public class TraceObject { ...@@ -174,7 +174,7 @@ public class TraceObject {
protected SQLException logAndConvert(Throwable e) { protected SQLException logAndConvert(Throwable e) {
if (SysProperties.LOG_ALL_ERRORS) { if (SysProperties.LOG_ALL_ERRORS) {
synchronized (this.getClass()) { synchronized (TraceObject.class) {
// e.printStackTrace(); // e.printStackTrace();
try { try {
Writer writer = FileUtils.openFileWriter(SysProperties.LOG_ALL_ERRORS_FILE, true); Writer writer = FileUtils.openFileWriter(SysProperties.LOG_ALL_ERRORS_FILE, true);
......
...@@ -21,12 +21,13 @@ import org.h2.value.Value; ...@@ -21,12 +21,13 @@ import org.h2.value.Value;
*/ */
public class ResultRemote implements ResultInterface { public class ResultRemote implements ResultInterface {
private final int fetchSize;
private SessionRemote session; private SessionRemote session;
private Transfer transfer; private Transfer transfer;
private int id; private int id;
private ResultColumn[] columns; private ResultColumn[] columns;
private Value[] currentRow; private Value[] currentRow;
private int rowId, rowCount; private int rowId, rowCount, rowOffset;
private ObjectArray result; private ObjectArray result;
private ObjectArray lobValues; private ObjectArray lobValues;
...@@ -38,7 +39,7 @@ public class ResultRemote implements ResultInterface { ...@@ -38,7 +39,7 @@ public class ResultRemote implements ResultInterface {
return 0; return 0;
} }
public ResultRemote(SessionRemote session, Transfer transfer, int id, int columnCount, int readRows) public ResultRemote(SessionRemote session, Transfer transfer, int id, int columnCount, int fetchSize)
throws IOException, SQLException { throws IOException, SQLException {
this.session = session; this.session = session;
this.transfer = transfer; this.transfer = transfer;
...@@ -49,21 +50,9 @@ public class ResultRemote implements ResultInterface { ...@@ -49,21 +50,9 @@ public class ResultRemote implements ResultInterface {
columns[i] = new ResultColumn(transfer); columns[i] = new ResultColumn(transfer);
} }
rowId = -1; rowId = -1;
if (rowCount < readRows) { result = new ObjectArray();
result = new ObjectArray(); this.fetchSize = fetchSize;
readFully(); fetchRows(false);
sendClose();
}
}
private void readFully() throws SQLException {
while (true) {
Value[] values = fetchRow(false);
if (values == null) {
break;
}
result.add(values);
}
} }
public String getAlias(int i) { public String getAlias(int i) {
...@@ -128,15 +117,14 @@ public class ResultRemote implements ResultInterface { ...@@ -128,15 +117,14 @@ public class ResultRemote implements ResultInterface {
} }
public boolean next() throws SQLException { public boolean next() throws SQLException {
// TODO optimization: don't need rowCount and fetchRow setting
if (rowId < rowCount) { if (rowId < rowCount) {
rowId++; rowId++;
remapIfOld();
if (rowId < rowCount) { if (rowId < rowCount) {
if (session == null) { if (rowId - rowOffset >= result.size()) {
currentRow = (Value[]) result.get(rowId); fetchRows(true);
} else {
currentRow = fetchRow(true);
} }
currentRow = (Value[]) result.get(rowId - rowOffset);
return true; return true;
} }
currentRow = null; currentRow = null;
...@@ -190,27 +178,43 @@ public class ResultRemote implements ResultInterface { ...@@ -190,27 +178,43 @@ public class ResultRemote implements ResultInterface {
} }
} }
private Value[] fetchRow(boolean sendFetch) throws SQLException { private void remapIfOld() throws SQLException {
if (session == null) {
return;
}
try {
if (id <= session.getCurrentId() - SysProperties.SERVER_CACHED_OBJECTS / 2) {
// object is too old - we need to map it to a new id
int newId = session.getNextId();
session.traceOperation("CHANGE_ID", id);
transfer.writeInt(SessionRemote.CHANGE_ID).writeInt(id).writeInt(newId);
id = newId;
// TODO remote result set: very old result sets may be
// already removed on the server (theoretically) - how to
// solve this?
}
} catch (IOException e) {
throw Message.convertIOException(e, null);
}
}
private void fetchRows(boolean sendFetch) throws SQLException {
synchronized (session) { synchronized (session) {
session.checkClosed(); session.checkClosed();
try { try {
if (id <= session.getCurrentId() - SysProperties.SERVER_CACHED_OBJECTS / 2) { rowOffset += result.size();
// object is too old - we need to map it to a new id result.clear();
int newId = session.getNextId(); int fetch = Math.min(fetchSize, rowCount - rowOffset);
session.traceOperation("CHANGE_ID", id);
transfer.writeInt(SessionRemote.CHANGE_ID).writeInt(id).writeInt(newId);
id = newId;
// TODO remote result set: very old result sets may be
// already removed on the server (theoretically) - how to
// solve this?
}
if (sendFetch) { if (sendFetch) {
session.traceOperation("RESULT_FETCH_ROW", id); session.traceOperation("RESULT_FETCH_ROWS", id);
transfer.writeInt(SessionRemote.RESULT_FETCH_ROW).writeInt(id); transfer.writeInt(SessionRemote.RESULT_FETCH_ROWS).writeInt(id).writeInt(fetch);
session.done(transfer); session.done(transfer);
} }
boolean row = transfer.readBoolean(); for (int r = 0; r < fetch; r++) {
if (row) { boolean row = transfer.readBoolean();
if (!row) {
break;
}
int len = columns.length; int len = columns.length;
Value[] values = new Value[len]; Value[] values = new Value[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
...@@ -223,10 +227,10 @@ public class ResultRemote implements ResultInterface { ...@@ -223,10 +227,10 @@ public class ResultRemote implements ResultInterface {
lobValues.add(v); lobValues.add(v);
} }
} }
return values; result.add(values);
} else { }
if (rowOffset + result.size() >= rowCount) {
sendClose(); sendClose();
return null;
} }
} catch (IOException e) { } catch (IOException e) {
throw Message.convertIOException(e, null); throw Message.convertIOException(e, null);
......
...@@ -221,7 +221,7 @@ public class TcpServerThread implements Runnable { ...@@ -221,7 +221,7 @@ public class TcpServerThread implements Runnable {
int id = transfer.readInt(); int id = transfer.readInt();
int objectId = transfer.readInt(); int objectId = transfer.readInt();
int maxRows = transfer.readInt(); int maxRows = transfer.readInt();
int readRows = transfer.readInt(); int fetchSize = transfer.readInt();
Command command = (Command) cache.getObject(id, false); Command command = (Command) cache.getObject(id, false);
setParameters(command); setParameters(command);
LocalResult result = command.executeQueryLocal(maxRows); LocalResult result = command.executeQueryLocal(maxRows);
...@@ -233,10 +233,9 @@ public class TcpServerThread implements Runnable { ...@@ -233,10 +233,9 @@ public class TcpServerThread implements Runnable {
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
ResultColumn.writeColumn(transfer, result, i); ResultColumn.writeColumn(transfer, result, i);
} }
if (rowCount < readRows) { int fetch = Math.min(rowCount, fetchSize);
for (int i = 0; i <= rowCount; i++) { for (int i = 0; i < fetch; i++) {
sendRow(result); sendRow(result);
}
} }
transfer.flush(); transfer.flush();
break; break;
...@@ -263,11 +262,14 @@ public class TcpServerThread implements Runnable { ...@@ -263,11 +262,14 @@ public class TcpServerThread implements Runnable {
} }
break; break;
} }
case SessionRemote.RESULT_FETCH_ROW: { case SessionRemote.RESULT_FETCH_ROWS: {
int id = transfer.readInt(); int id = transfer.readInt();
int count = transfer.readInt();
LocalResult result = (LocalResult) cache.getObject(id, false); LocalResult result = (LocalResult) cache.getObject(id, false);
transfer.writeInt(SessionRemote.STATUS_OK); transfer.writeInt(SessionRemote.STATUS_OK);
sendRow(result); for (int i = 0; i < count; i++) {
sendRow(result);
}
transfer.flush(); transfer.flush();
break; break;
} }
...@@ -303,13 +305,14 @@ public class TcpServerThread implements Runnable { ...@@ -303,13 +305,14 @@ public class TcpServerThread implements Runnable {
} }
private void sendRow(LocalResult result) throws IOException, SQLException { private void sendRow(LocalResult result) throws IOException, SQLException {
boolean n = result.next(); if (result.next()) {
transfer.writeBoolean(n); transfer.writeBoolean(true);
if (n) {
Value[] v = result.currentRow(); Value[] v = result.currentRow();
for (int i = 0; i < result.getVisibleColumnCount(); i++) { for (int i = 0; i < result.getVisibleColumnCount(); i++) {
transfer.writeValue(v[i]); transfer.writeValue(v[i]);
} }
} else {
transfer.writeBoolean(false);
} }
} }
......
...@@ -265,13 +265,13 @@ public class FileStore { ...@@ -265,13 +265,13 @@ public class FileStore {
if (synchronousMode && newLength > fileLength) { if (synchronousMode && newLength > fileLength) {
extendByWriting(newLength); extendByWriting(newLength);
} else { } else {
file.setLength(newLength); file.setFileLength(newLength);
} }
fileLength = newLength; fileLength = newLength;
} catch (IOException e) { } catch (IOException e) {
if (freeUpDiskSpace()) { if (freeUpDiskSpace()) {
try { try {
file.setLength(newLength); file.setFileLength(newLength);
} catch (IOException e2) { } catch (IOException e2) {
throw Message.convertIOException(e2, name); throw Message.convertIOException(e2, name);
} }
...@@ -292,7 +292,7 @@ public class FileStore { ...@@ -292,7 +292,7 @@ public class FileStore {
} }
if (SysProperties.CHECK2 && len % Constants.FILE_BLOCK_SIZE != 0) { if (SysProperties.CHECK2 && len % Constants.FILE_BLOCK_SIZE != 0) {
long newLength = len + Constants.FILE_BLOCK_SIZE - (len % Constants.FILE_BLOCK_SIZE); long newLength = len + Constants.FILE_BLOCK_SIZE - (len % Constants.FILE_BLOCK_SIZE);
file.setLength(newLength); file.setFileLength(newLength);
fileLength = newLength; fileLength = newLength;
throw Message.getInternalError("unaligned file length " + name + " len " + len); throw Message.getInternalError("unaligned file length " + name + " len " + len);
} }
......
...@@ -64,6 +64,6 @@ public interface FileObject { ...@@ -64,6 +64,6 @@ public interface FileObject {
* *
* @param newLength the new length * @param newLength the new length
*/ */
void setLength(long newLength) throws IOException; void setFileLength(long newLength) throws IOException;
} }
...@@ -50,7 +50,7 @@ public class FileObjectDatabase implements FileObject { ...@@ -50,7 +50,7 @@ public class FileObjectDatabase implements FileObject {
this.pos = (int) pos; this.pos = (int) pos;
} }
public void setLength(long newLength) throws IOException { public void setFileLength(long newLength) throws IOException {
this.length = (int) newLength; this.length = (int) newLength;
if (length != data.length) { if (length != data.length) {
byte[] n = new byte[length]; byte[] n = new byte[length];
......
...@@ -4,51 +4,27 @@ ...@@ -4,51 +4,27 @@
*/ */
package org.h2.store.fs; package org.h2.store.fs;
import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
/** /**
* This class is wraps a RandomAccessFile. * This class is extends a RandomAccessFile.
*/ */
public class FileObjectDisk implements FileObject { public class FileObjectDisk extends RandomAccessFile implements FileObject {
private RandomAccessFile file;
FileObjectDisk(RandomAccessFile file) { public FileObjectDisk(String fileName, String mode) throws FileNotFoundException {
this.file = file; super(fileName, mode);
}
public long length() throws IOException {
return file.length();
}
public void close() throws IOException {
file.close();
}
public void readFully(byte[] b, int off, int len) throws IOException {
file.readFully(b, off, len);
}
public void seek(long pos) throws IOException {
file.seek(pos);
}
public void write(byte[] b, int off, int len) throws IOException {
file.write(b, off, len);
}
public long getFilePointer() throws IOException {
return file.getFilePointer();
} }
public void sync() throws IOException { public void sync() throws IOException {
file.getFD().sync(); getFD().sync();
} }
public void setLength(long newLength) throws IOException { public void setFileLength(long newLength) throws IOException {
FileUtils.setLength(file, newLength); FileUtils.setLength(this, newLength);
} }
} }
...@@ -128,7 +128,7 @@ public class FileObjectMemory implements FileObject { ...@@ -128,7 +128,7 @@ public class FileObjectMemory implements FileObject {
return length; return length;
} }
public void setLength(long l) { public void setFileLength(long l) {
touch(); touch();
if (l < length) { if (l < length) {
pos = Math.min(pos, l); pos = Math.min(pos, l);
......
...@@ -21,7 +21,7 @@ public class FileObjectOutputStream extends OutputStream { ...@@ -21,7 +21,7 @@ public class FileObjectOutputStream extends OutputStream {
file.seek(file.length()); file.seek(file.length());
} else { } else {
file.seek(0); file.seek(0);
file.setLength(0); file.setFileLength(0);
} }
} }
......
...@@ -70,7 +70,7 @@ public class FileObjectZip implements FileObject { ...@@ -70,7 +70,7 @@ public class FileObjectZip implements FileObject {
this.pos = pos; this.pos = pos;
} }
public void setLength(long newLength) throws IOException { public void setFileLength(long newLength) throws IOException {
throw new IOException("File is read-only"); throw new IOException("File is read-only");
} }
......
...@@ -10,7 +10,6 @@ import java.io.FileOutputStream; ...@@ -10,7 +10,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL; import java.net.URL;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -352,15 +351,15 @@ public class FileSystemDisk extends FileSystem { ...@@ -352,15 +351,15 @@ public class FileSystemDisk extends FileSystem {
public FileObject openFileObject(String fileName, String mode) throws IOException { public FileObject openFileObject(String fileName, String mode) throws IOException {
fileName = translateFileName(fileName); fileName = translateFileName(fileName);
RandomAccessFile f; FileObjectDisk f;
try { try {
f = new RandomAccessFile(fileName, mode); f = new FileObjectDisk(fileName, mode);
trace("openRandomAccessFile", fileName, f); trace("openRandomAccessFile", fileName, f);
} catch (IOException e) { } catch (IOException e) {
freeMemoryAndFinalize(); freeMemoryAndFinalize();
f = new RandomAccessFile(fileName, mode); f = new FileObjectDisk(fileName, mode);
} }
return new FileObjectDisk(f); return f;
} }
} }
...@@ -156,7 +156,7 @@ public class Column { ...@@ -156,7 +156,7 @@ public class Column {
public long getPrecision() { public long getPrecision() {
return precision; return precision;
} }
public int getDisplaySize() { public int getDisplaySize() {
return displaySize; return displaySize;
} }
...@@ -401,10 +401,21 @@ public class Column { ...@@ -401,10 +401,21 @@ public class Column {
return sequence; return sequence;
} }
/**
* Get the selectivity of the column.
* Selectivity 100 means values are unique, 10 means every distinct value appears 10 times on average.
*
* @return the selectivity
*/
public int getSelectivity() { public int getSelectivity() {
return selectivity == 0 ? Constants.SELECTIVITY_DEFAULT : selectivity; return selectivity == 0 ? Constants.SELECTIVITY_DEFAULT : selectivity;
} }
/**
* Set the new selectivity of a column.
*
* @param selectivity the new value
*/
public void setSelectivity(int selectivity) { public void setSelectivity(int selectivity) {
selectivity = selectivity < 0 ? 0 : (selectivity > 100 ? 100 : selectivity); selectivity = selectivity < 0 ? 0 : (selectivity > 100 ? 100 : selectivity);
this.selectivity = selectivity; this.selectivity = selectivity;
......
...@@ -736,7 +736,7 @@ public class MetaTable extends Table { ...@@ -736,7 +736,7 @@ public class MetaTable extends Table {
add(rows, new String[]{"h2.logAllErrors", "" + SysProperties.LOG_ALL_ERRORS}); add(rows, new String[]{"h2.logAllErrors", "" + SysProperties.LOG_ALL_ERRORS});
add(rows, new String[]{"h2.logAllErrorsFile", "" + SysProperties.LOG_ALL_ERRORS_FILE}); add(rows, new String[]{"h2.logAllErrorsFile", "" + SysProperties.LOG_ALL_ERRORS_FILE});
add(rows, new String[]{"h2.serverCachedObjects", "" + SysProperties.SERVER_CACHED_OBJECTS}); add(rows, new String[]{"h2.serverCachedObjects", "" + SysProperties.SERVER_CACHED_OBJECTS});
add(rows, new String[]{"h2.serverSmallResultSetSize", "" + SysProperties.SERVER_SMALL_RESULT_SET_SIZE}); add(rows, new String[]{"h2.serverResultSetFetchSize", "" + SysProperties.SERVER_RESULT_SET_FETCH_SIZE});
add(rows, new String[]{"h2.emergencySpaceInitial", "" + SysProperties.EMERGENCY_SPACE_INITIAL}); add(rows, new String[]{"h2.emergencySpaceInitial", "" + SysProperties.EMERGENCY_SPACE_INITIAL});
add(rows, new String[]{"h2.emergencySpaceMin", "" + SysProperties.EMERGENCY_SPACE_MIN}); add(rows, new String[]{"h2.emergencySpaceMin", "" + SysProperties.EMERGENCY_SPACE_MIN});
add(rows, new String[]{"h2.objectCache", "" + SysProperties.OBJECT_CACHE}); add(rows, new String[]{"h2.objectCache", "" + SysProperties.OBJECT_CACHE});
......
...@@ -10,6 +10,7 @@ import java.sql.SQLException; ...@@ -10,6 +10,7 @@ import java.sql.SQLException;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.util.FileUtils; import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
/** /**
* Convert a trace file to a java class. * Convert a trace file to a java class.
...@@ -71,7 +72,7 @@ public class ConvertTraceFile { ...@@ -71,7 +72,7 @@ public class ConvertTraceFile {
* @throws IOException * @throws IOException
*/ */
private void convertFile(String traceFileName, String javaClassName, String script) throws IOException, SQLException { private void convertFile(String traceFileName, String javaClassName, String script) throws IOException, SQLException {
LineNumberReader reader = new LineNumberReader(FileUtils.openFileReader(traceFileName)); LineNumberReader reader = new LineNumberReader(IOUtils.getReader(FileUtils.openFileInputStream(traceFileName)));
PrintWriter javaWriter = new PrintWriter(FileUtils.openFileWriter(javaClassName + ".java", false)); PrintWriter javaWriter = new PrintWriter(FileUtils.openFileWriter(javaClassName + ".java", false));
PrintWriter scriptWriter = new PrintWriter(FileUtils.openFileWriter(script, false)); PrintWriter scriptWriter = new PrintWriter(FileUtils.openFileWriter(script, false));
javaWriter.println("import java.io.*;"); javaWriter.println("import java.io.*;");
......
...@@ -135,7 +135,7 @@ public class Script { ...@@ -135,7 +135,7 @@ public class Script {
ResultSet rs = stat.executeQuery("SCRIPT"); ResultSet rs = stat.executeQuery("SCRIPT");
while (rs.next()) { while (rs.next()) {
String s = rs.getString(1); String s = rs.getString(1);
writer.println(s + ";"); writer.println(s);
} }
writer.close(); writer.close();
} finally { } finally {
......
package org.h2.util;
import java.io.IOException;
import java.io.InputStream;
/**
* This input stream wrapper closes the base input stream when fully read.
*/
public class AutoCloseInputStream extends InputStream {
private final InputStream in;
private boolean closed;
/**
* Create a new input stream.
*
* @param in the input stream
*/
public AutoCloseInputStream(InputStream in) {
this.in = in;
}
private int autoClose(int x) throws IOException {
if (x < 0) {
close();
}
return x;
}
public void close() throws IOException {
if (!closed) {
in.close();
closed = true;
}
}
public int read(byte[] b, int off, int len) throws IOException {
return closed ? -1 : autoClose(in.read(b, off, len));
}
public int read(byte[] b) throws IOException {
return closed ? -1 : autoClose(in.read(b));
}
public int read() throws IOException {
return closed ? -1 : autoClose(in.read());
}
}
...@@ -4,21 +4,20 @@ ...@@ -4,21 +4,20 @@
*/ */
package org.h2.util; package org.h2.util;
import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.io.Reader; import java.io.UnsupportedEncodingException;
import java.io.Writer; import java.io.Writer;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties; import java.util.Properties;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.store.fs.FileSystem; import org.h2.store.fs.FileSystem;
...@@ -100,11 +99,6 @@ public class FileUtils { ...@@ -100,11 +99,6 @@ public class FileUtils {
} }
} }
public static Reader openFileReader(String fileName) throws IOException {
Reader reader = new InputStreamReader(openFileInputStream(fileName));
return new BufferedReader(reader);
}
public static String getFileName(String name) throws SQLException { public static String getFileName(String name) throws SQLException {
return FileSystem.getInstance(name).getFileName(name); return FileSystem.getInstance(name).getFileName(name);
} }
...@@ -156,7 +150,11 @@ public class FileUtils { ...@@ -156,7 +150,11 @@ public class FileUtils {
public static Writer openFileWriter(String fileName, boolean append) throws SQLException { public static Writer openFileWriter(String fileName, boolean append) throws SQLException {
OutputStream out = FileSystem.getInstance(fileName).openFileOutputStream(fileName, append); OutputStream out = FileSystem.getInstance(fileName).openFileOutputStream(fileName, append);
return new BufferedWriter(new OutputStreamWriter(out)); try {
return new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw Message.convert(e);
}
} }
public static boolean fileStartsWith(String fileName, String prefix) { public static boolean fileStartsWith(String fileName, String prefix) {
......
...@@ -154,6 +154,8 @@ java org.h2.test.TestAll timer ...@@ -154,6 +154,8 @@ java org.h2.test.TestAll timer
/* /*
add tests with select distinct type (code coverage)
documentation: package.html documentation: package.html
write to the db file what version was used to create a database write to the db file what version was used to create a database
...@@ -532,6 +534,7 @@ Features of H2 ...@@ -532,6 +534,7 @@ Features of H2
} }
void testDatabase() throws Exception { void testDatabase() throws Exception {
System.out.println("test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc); System.out.println("test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc);
beforeTest(); beforeTest();
......
...@@ -10,6 +10,7 @@ import java.sql.SQLException; ...@@ -10,6 +10,7 @@ import java.sql.SQLException;
import java.sql.Savepoint; import java.sql.Savepoint;
import java.sql.Statement; import java.sql.Statement;
import org.h2.constant.SysProperties;
import org.h2.jdbc.JdbcStatement; import org.h2.jdbc.JdbcStatement;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -100,13 +101,13 @@ public class TestStatement extends TestBase { ...@@ -100,13 +101,13 @@ public class TestStatement extends TestBase {
void testStatement() throws Exception { void testStatement() throws Exception {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
//#ifdef JDK14 //#ifdef JDK14
check(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability()); check(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability());
conn.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); conn.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT);
check(ResultSet.CLOSE_CURSORS_AT_COMMIT, conn.getHoldability()); check(ResultSet.CLOSE_CURSORS_AT_COMMIT, conn.getHoldability());
//#endif //#endif
// ignored // ignored
stat.setCursorName("x"); stat.setCursorName("x");
// fixed return value // fixed return value
...@@ -115,10 +116,12 @@ public class TestStatement extends TestBase { ...@@ -115,10 +116,12 @@ public class TestStatement extends TestBase {
stat.setFetchDirection(ResultSet.FETCH_REVERSE); stat.setFetchDirection(ResultSet.FETCH_REVERSE);
// ignored // ignored
stat.setMaxFieldSize(100); stat.setMaxFieldSize(100);
check(0, stat.getFetchSize()); check(SysProperties.SERVER_RESULT_SET_FETCH_SIZE, stat.getFetchSize());
stat.setFetchSize(10); stat.setFetchSize(10);
check(10, stat.getFetchSize()); check(10, stat.getFetchSize());
stat.setFetchSize(0);
check(SysProperties.SERVER_RESULT_SET_FETCH_SIZE, stat.getFetchSize());
check(ResultSet.TYPE_FORWARD_ONLY, stat.getResultSetType()); check(ResultSet.TYPE_FORWARD_ONLY, stat.getResultSetType());
Statement stat2 = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); Statement stat2 = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
check(ResultSet.TYPE_SCROLL_SENSITIVE, stat2.getResultSetType()); check(ResultSet.TYPE_SCROLL_SENSITIVE, stat2.getResultSetType());
...@@ -128,8 +131,8 @@ public class TestStatement extends TestBase { ...@@ -128,8 +131,8 @@ public class TestStatement extends TestBase {
check(!((JdbcStatement) stat2).isClosed()); check(!((JdbcStatement) stat2).isClosed());
stat2.close(); stat2.close();
check(((JdbcStatement) stat2).isClosed()); check(((JdbcStatement) stat2).isClosed());
ResultSet rs; ResultSet rs;
int count; int count;
boolean result; boolean result;
......
...@@ -22,4 +22,10 @@ PostgreSQL ...@@ -22,4 +22,10 @@ PostgreSQL
-------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------
Derby
--------------------------------------------------------------------------------------------------------
To call getFD().sync() (which results in the OS call fsync()),
set the system property derby.storage.fileSyncTransactionLog to true true.
See
http://db.apache.org/derby/javadoc/engine/org/apache/derby/iapi/reference/Property.html#FILESYNC_TRANSACTION_LOG
...@@ -212,8 +212,8 @@ explain select * from test where id = 1; ...@@ -212,8 +212,8 @@ explain select * from test where id = 1;
EXPLAIN SELECT * FROM TEST WHERE ID = (SELECT MAX(ID) FROM TEST); EXPLAIN SELECT * FROM TEST WHERE ID = (SELECT MAX(ID) FROM TEST);
> PLAN > PLAN
> ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> SELECT TEST.ID FROM PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_2: ID = (SELECT MAX(ID) FROM PUBLIC.TEST /++ PUBLIC.TEST_TABLE_SCAN ++/ /++ direct lookup query ++/) */ WHERE ID = (SELECT MAX(ID) FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ /* direct lookup query */) > SELECT TEST.ID FROM PUBLIC.TEST /* PUBLIC.PRIMARY_KEY_2: ID = (SELECT MAX(ID) FROM PUBLIC.TEST /++ PUBLIC.TEST_TABLE_SCAN ++/ /++ direct lookup ++/) */ WHERE ID = (SELECT MAX(ID) FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ /* direct lookup */)
> rows: 1 > rows: 1
drop table test; drop table test;
...@@ -994,9 +994,9 @@ script nopasswords nosettings blocksize 10; ...@@ -994,9 +994,9 @@ script nopasswords nosettings blocksize 10;
> DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB; > DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB;
> DROP TABLE IF EXISTS SYSTEM_LOB_STREAM; > DROP TABLE IF EXISTS SYSTEM_LOB_STREAM;
> INSERT INTO PUBLIC.TEST(ID, DATA) VALUES(1, SYSTEM_COMBINE_CLOB(0)); > INSERT INTO PUBLIC.TEST(ID, DATA) VALUES(1, SYSTEM_COMBINE_CLOB(0));
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 0, 'abc ', NULL);; > INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 0, 'abc ', NULL);
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 1, ' ', NULL);; > INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 1, ' ', NULL);
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 2, ' ', NULL);; > INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 2, ' ', NULL);
> rows: 15 > rows: 15
drop table test; drop table test;
...@@ -1691,11 +1691,11 @@ select * from test order by id; ...@@ -1691,11 +1691,11 @@ select * from test order by id;
> rows (ordered): 3 > rows (ordered): 3
select rownum, (select count(*) from test), rownum from test; select rownum, (select count(*) from test), rownum from test;
> ROWNUM() SELECT COUNT(*) FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ /* direct lookup query */ ROWNUM() > ROWNUM() SELECT COUNT(*) FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ /* direct lookup */ ROWNUM()
> -------- --------------------------------------------------------------------------------------- -------- > -------- --------------------------------------------------------------------------------- --------
> 1 3 1 > 1 3 1
> 2 3 2 > 2 3 2
> 3 3 3 > 3 3 3
> rows: 3 > rows: 3
delete from test t0 where rownum<2; delete from test t0 where rownum<2;
......
...@@ -184,7 +184,7 @@ public class TestFileSystem extends TestBase { ...@@ -184,7 +184,7 @@ public class TestFileSystem extends TestBase {
break; break;
} }
case 2: { case 2: {
f.setLength(pos); f.setFileLength(pos);
ra.setLength(pos); ra.setLength(pos);
if (ra.getFilePointer() > pos) { if (ra.getFilePointer() > pos) {
f.seek(0); f.seek(0);
......
...@@ -14,6 +14,8 @@ import java.sql.PreparedStatement; ...@@ -14,6 +14,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types;
import java.util.Random;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.test.trace.Player; import org.h2.test.trace.Player;
...@@ -37,6 +39,8 @@ public class TestTools extends TestBase { ...@@ -37,6 +39,8 @@ public class TestTools extends TestBase {
return; return;
} }
deleteDb("utils"); deleteDb("utils");
testScriptRunscriptLob();
deleteDb("utils");
testServerMain(); testServerMain();
testRemove(); testRemove();
testConvertTraceFile(); testConvertTraceFile();
...@@ -157,7 +161,6 @@ public class TestTools extends TestBase { ...@@ -157,7 +161,6 @@ public class TestTools extends TestBase {
deleteDb("toolsConvertTraceFile"); deleteDb("toolsConvertTraceFile");
RunScript.main(new String[]{"-url", url, "-user", "sa", "-script", baseDir + "/test.sql"}); RunScript.main(new String[]{"-url", url, "-user", "sa", "-script", baseDir + "/test.sql"});
testTraceFile(url); testTraceFile(url);
} }
private void testTraceFile(String url) throws Exception { private void testTraceFile(String url) throws Exception {
...@@ -249,6 +252,60 @@ public class TestTools extends TestBase { ...@@ -249,6 +252,60 @@ public class TestTools extends TestBase {
} }
} }
private void testScriptRunscriptLob() throws Exception {
Class.forName("org.h2.Driver");
String url = "jdbc:h2:" + baseDir + "/utils";
String user = "sa", password = "abc";
String fileName = baseDir + "/b2.sql";
Connection conn = DriverManager.getConnection(url, user, password);
conn.createStatement().execute("CREATE TABLE TEST(ID INT PRIMARY KEY, BDATA BLOB, CDATA CLOB)");
PreparedStatement prep = conn.prepareStatement("INSERT INTO TEST VALUES(?, ?, ?)");
prep.setInt(1, 1);
prep.setNull(2, Types.BLOB);
prep.setNull(3, Types.CLOB);
prep.execute();
prep.setInt(1, 2);
prep.setString(2, "face");
prep.setString(3, "face");
prep.execute();
Random random = new Random(1);
prep.setInt(1, 3);
byte[] large = new byte[getSize(10 * 1024, 100 * 1024)];
random.nextBytes(large);
prep.setBytes(2, large);
String largeText = new String(large, "ISO-8859-1");
prep.setString(3, largeText);
prep.execute();
for (int i = 0; i < 2; i++) {
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM TEST ORDER BY ID");
rs.next();
check(1, rs.getInt(1));
check(rs.getString(2) == null);
check(rs.getString(3) == null);
rs.next();
check(2, rs.getInt(1));
check("face", rs.getString(2));
check("face", rs.getString(3));
rs.next();
check(3, rs.getInt(1));
check(large, rs.getBytes(2));
check(largeText, rs.getString(3));
checkFalse(rs.next());
conn.close();
Script.main(new String[] { "-url", url, "-user", user, "-password", password, "-script", fileName});
DeleteDbFiles.main(new String[] { "-dir", baseDir, "-db", "utils", "-quiet" });
RunScript.main(new String[] { "-url", url, "-user", user, "-password", password, "-script", fileName});
conn = DriverManager.getConnection("jdbc:h2:" + baseDir + "/utils", "sa", "abc");
}
conn.close();
}
private void testScriptRunscript() throws Exception { private void testScriptRunscript() throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
String url = "jdbc:h2:" + baseDir + "/utils"; String url = "jdbc:h2:" + baseDir + "/utils";
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论