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

--no commit message

--no commit message
上级 c794b1f9
......@@ -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.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.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>
<br /><a name="glossary_links"></a>
......
......@@ -535,6 +535,7 @@ CREATE ALIAS IF NOT EXISTS FT_INIT FOR "org.h2.fulltext.FullText.init";
CALL FT_INIT();
</pre>
<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:
</p>
<pre>
......@@ -560,13 +561,16 @@ org.h2.fulltext.FullText.search(conn, text, limit, offset)
<h3>Using the Lucene Fulltext Search</h3>
<p>
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>
<pre>
CREATE ALIAS IF NOT EXISTS FTL_INIT FOR "org.h2.fulltext.FullTextLucene.init";
CALL FTL_INIT();
</pre>
<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:
</p>
<pre>
......
......@@ -28,13 +28,14 @@ public class CommandRemote implements CommandInterface {
private final ObjectArray parameters;
private final Trace trace;
private final String sql;
private final int fetchSize;
private SessionRemote session;
private int id;
private boolean isQuery;
private boolean readonly;
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;
trace = session.getTrace();
this.sql = sql;
......@@ -46,6 +47,7 @@ public class CommandRemote implements CommandInterface {
// set session late because prepare might fail - in this case we don't
// need to close the object
this.session = session;
this.fetchSize = fetchSize;
}
private void prepare(SessionRemote session) throws SQLException {
......@@ -94,7 +96,7 @@ public class CommandRemote implements CommandInterface {
transfer.writeInt(SessionRemote.COMMAND_GET_META_DATA).writeInt(id).writeInt(objectId);
session.done(transfer);
int columnCount = transfer.readInt();
result = new ResultRemote(session, transfer, objectId, columnCount, -1);
result = new ResultRemote(session, transfer, objectId, columnCount, Integer.MAX_VALUE);
break;
} catch (IOException e) {
session.removeServer(i--);
......@@ -123,13 +125,13 @@ public class CommandRemote implements CommandInterface {
session.traceOperation("COMMAND_EXECUTE_QUERY", id);
transfer.writeInt(SessionRemote.COMMAND_EXECUTE_QUERY).writeInt(id).writeInt(objectId).writeInt(
maxRows);
int readRows;
int fetch;
if (session.isClustered() || scrollable) {
readRows = Integer.MAX_VALUE;
fetch = Integer.MAX_VALUE;
} else {
readRows = SysProperties.SERVER_SMALL_RESULT_SET_SIZE;
fetch = fetchSize;
}
transfer.writeInt(readRows);
transfer.writeInt(fetch);
sendParameters(transfer);
session.done(transfer);
int columnCount = transfer.readInt();
......@@ -137,7 +139,7 @@ public class CommandRemote implements CommandInterface {
result.close();
result = null;
}
result = new ResultRemote(session, transfer, objectId, columnCount, readRows);
result = new ResultRemote(session, transfer, objectId, columnCount, fetch);
if (readonly) {
break;
}
......
......@@ -4,6 +4,7 @@
*/
package org.h2.command.dml;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
......@@ -11,9 +12,9 @@ import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Comparator;
import org.h2.command.Parser;
......@@ -44,6 +45,7 @@ import org.h2.schema.TriggerObject;
import org.h2.table.Column;
import org.h2.table.PlanItem;
import org.h2.table.Table;
import org.h2.util.AutoCloseInputStream;
import org.h2.util.ByteUtils;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
......@@ -69,7 +71,7 @@ public class ScriptCommand extends ScriptBase {
private byte[] buffer;
private boolean tempLobTableCreated;
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";
public ScriptCommand(Session session) {
......@@ -338,7 +340,7 @@ public class ScriptCommand extends ScriptBase {
break;
}
buff.append(ByteUtils.convertBytesToString(bytes, len));
buff.append("');");
buff.append("')");
String sql = buff.toString();
add(sql, true);
}
......@@ -358,8 +360,8 @@ public class ScriptCommand extends ScriptBase {
if (len < 0) {
break;
}
buff.append(StringUtils.quoteStringSQL(new String(chars)));
buff.append(", NULL);");
buff.append(StringUtils.quoteStringSQL(new String(chars, 0, len)));
buff.append(", NULL)");
String sql = buff.toString();
add(sql, true);
}
......@@ -374,36 +376,68 @@ public class ScriptCommand extends ScriptBase {
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 {
if (id < 0) {
FileUtils.delete(TEMP_LOB_FILENAME);
return null;
}
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT BDATA FROM SYSTEM_LOB_STREAM WHERE ID=" + id + " ORDER BY PART");
ResultSet rs = getLobStream(conn, "BDATA", id);
OutputStream out = FileUtils.openFileOutputStream(TEMP_LOB_FILENAME, false);
while (rs.next()) {
InputStream in = rs.getBinaryStream(1);
IOUtils.copyAndCloseInput(in, out);
}
out.close();
stat.execute("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=" + id);
return FileUtils.openFileInputStream(TEMP_LOB_FILENAME);
deleteLobStream(conn, id);
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 {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT CDATA FROM SYSTEM_LOB_STREAM WHERE ID=" + id + " ORDER BY PART");
ResultSet rs = getLobStream(conn, "CDATA", id);
Writer out = FileUtils.openFileWriter(TEMP_LOB_FILENAME, false);
while (rs.next()) {
Reader in = new BufferedReader(rs.getCharacterStream(1));
IOUtils.copyAndCloseInput(in, out);
}
out.close();
stat.execute("DELETE FROM SYSTEM_LOB_STREAM WHERE ID=" + id);
return FileUtils.openFileReader(TEMP_LOB_FILENAME);
deleteLobStream(conn, id);
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 {
......
......@@ -9,6 +9,7 @@ import java.util.HashMap;
import java.util.HashSet;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Alias;
import org.h2.expression.Comparison;
......@@ -18,9 +19,12 @@ import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.expression.Wildcard;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
......@@ -62,7 +66,7 @@ public class Select extends Query {
private boolean isGroupQuery;
private boolean isForUpdate;
private double cost;
private boolean isQuickQuery;
private boolean isQuickQuery, isDistinctQuery;
private boolean isPrepared, checkInit;
private boolean sortUsingIndex;
private SortOrder sort;
......@@ -272,6 +276,42 @@ public class Select extends Query {
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 {
if (limitRows != 0 && offset != null) {
// 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 {
if (!sortUsingIndex) {
result.setSortOrder(sort);
}
if (distinct) {
if (distinct && !isDistinctQuery) {
result.setDistinct();
}
topTableFilter.startQuery(session);
......@@ -340,6 +380,8 @@ public class Select extends Query {
queryQuick(columnCount, result);
} else if (isGroupQuery) {
queryGroup(columnCount, result);
} else if (isDistinctQuery) {
queryDistinct(columnCount, result, limitRows);
} else {
queryFlat(columnCount, result, limitRows);
}
......@@ -513,6 +555,31 @@ public class Select extends Query {
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() {
......@@ -647,11 +714,12 @@ public class Select extends Query {
if (isForUpdate) {
buff.append("\nFOR UPDATE");
}
if (isQuickQuery) {
buff.append("\n/* direct lookup query */");
buff.append("\n/* direct lookup */");
}
if (isDistinctQuery) {
buff.append("\n/* distinct */");
}
return buff.toString();
}
......
......@@ -35,6 +35,7 @@ public class SysProperties {
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_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true);
public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true);
......@@ -45,7 +46,7 @@ public class SysProperties {
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 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_MIN = getIntSetting("h2.emergencySpaceMin", 64 * 1024);
public static final boolean OBJECT_CACHE = getBooleanSetting("h2.objectCache", true);
......
......@@ -81,7 +81,7 @@ public class Constants {
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 = "-- 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_MINOR = 0;
......
......@@ -116,7 +116,7 @@ public class Engine {
String value = ci.getProperty(setting);
try {
CommandInterface command = session.prepareCommand("SET " + Parser.quoteIdentifier(setting) + " "
+ value);
+ value, Integer.MAX_VALUE);
command.executeUpdate();
} catch (SQLException e) {
if (!ignoreUnknownSetting) {
......
......@@ -154,7 +154,7 @@ public class Session implements SessionInterface {
this.currentSchemaName = Constants.SCHEMA_MAIN;
}
public CommandInterface prepareCommand(String sql) throws SQLException {
public CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException {
return prepareLocal(sql);
}
......
......@@ -19,9 +19,10 @@ public interface SessionInterface {
* Parse a command and prepare it for execution.
*
* @param sql the SQL statement
* @param fetchSize the number of rows to fetch in one step
* @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.
......
......@@ -38,7 +38,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
public static final int COMMAND_EXECUTE_QUERY = 2;
public static final int COMMAND_EXECUTE_UPDATE = 3;
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_CLOSE = 7;
public static final int COMMAND_COMMIT = 8;
......@@ -92,7 +92,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
private void switchOffAutoCommitIfCluster() throws SQLException {
if (autoCommit && transferList.size() > 1) {
if (switchOffAutoCommit == null) {
switchOffAutoCommit = prepareCommand("SET AUTOCOMMIT FALSE");
switchOffAutoCommit = prepareCommand("SET AUTOCOMMIT FALSE", Integer.MAX_VALUE);
}
// this will call setAutoCommit(false)
switchOffAutoCommit.executeUpdate();
......@@ -225,7 +225,7 @@ public class SessionRemote implements SessionInterface, DataHandler {
}
private void switchOffCluster() throws SQLException {
CommandInterface ci = prepareCommand("SET CLUSTER ''");
CommandInterface ci = prepareCommand("SET CLUSTER ''", Integer.MAX_VALUE);
ci.executeUpdate();
}
......@@ -235,10 +235,10 @@ public class SessionRemote implements SessionInterface, DataHandler {
switchOffCluster();
}
public CommandInterface prepareCommand(String sql) throws SQLException {
public CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException {
synchronized (this) {
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 {
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 void remove(Session session) throws SQLException;
......
......@@ -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 {
return find(session, first, false, last);
}
public Cursor find(Session session, SearchRow first, boolean bigger, SearchRow last) throws SQLException {
if (SysProperties.CHECK && storage == null) {
throw Message.getSQLException(ErrorCode.OBJECT_CLOSED);
}
......@@ -196,7 +208,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return cursor;
} else {
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);
}
return cursor;
......@@ -301,7 +313,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
if (first) {
// TODO optimization: this loops through NULL elements
Cursor cursor = find(session, null, null);
Cursor cursor = find(session, null, false, null);
while (cursor.next()) {
SearchRow row = cursor.getSearchRow();
Value v = row.getValue(columnIds[0]);
......
......@@ -144,7 +144,7 @@ public class BtreeLeaf extends BtreePage {
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();
if (r == 0 && !Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
......@@ -153,7 +153,7 @@ public class BtreeLeaf extends BtreePage {
int i = (l + r) >>> 1;
SearchRow row = (SearchRow) pageData.get(i);
int comp = index.compareRows(row, compare);
if (comp >= 0) {
if (comp > 0 || (!bigger && comp == 0)) {
r = i;
} else {
l = i + 1;
......
......@@ -207,7 +207,7 @@ public class BtreeNode extends BtreePage {
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();
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page");
......@@ -216,7 +216,7 @@ public class BtreeNode extends BtreePage {
int i = (l + r) >>> 1;
SearchRow row = getData(i);
int comp = index.compareRows(row, compare);
if (comp >= 0) {
if (comp > 0 || (!bigger && comp == 0)) {
r = i;
} else {
l = i + 1;
......@@ -225,7 +225,7 @@ public class BtreeNode extends BtreePage {
if (l >= pageData.size()) {
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l);
boolean result = page.findFirst(cursor, compare);
boolean result = page.findFirst(cursor, compare, bigger);
if (result) {
return true;
}
......@@ -234,7 +234,7 @@ public class BtreeNode extends BtreePage {
}
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l);
if (page.findFirst(cursor, compare)) {
if (page.findFirst(cursor, compare, bigger)) {
return true;
}
cursor.pop();
......@@ -245,7 +245,7 @@ public class BtreeNode extends BtreePage {
if (comp >= 0) {
page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i);
if (page.findFirst(cursor, compare)) {
if (page.findFirst(cursor, compare, bigger)) {
return true;
}
cursor.pop();
......@@ -253,7 +253,7 @@ public class BtreeNode extends BtreePage {
}
page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i);
boolean result = page.findFirst(cursor, compare);
boolean result = page.findFirst(cursor, compare, bigger);
if (result) {
return true;
}
......
......@@ -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
abstract SearchRow remove(Session session, Row row, int level) 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 getLast(Session session) throws SQLException;
abstract void next(BtreeCursor cursor, int i) throws SQLException;
......
......@@ -101,6 +101,24 @@ public interface Index extends SchemaObject {
*/
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.
*
......
......@@ -8,7 +8,7 @@ package org.h2.index;
* Represents information about the properties of an index
*/
public class IndexType {
private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan, isDescending;
private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan;
private boolean belongsToConstraint;
public static IndexType createPrimaryKey(boolean persistent, boolean hash) {
......@@ -41,14 +41,6 @@ public class IndexType {
return type;
}
public void setDescending(boolean descending) {
this.isDescending = descending;
}
public boolean getDescending() {
return isDescending;
}
public void setBelongsToConstraint(boolean belongsToConstraint) {
this.belongsToConstraint = belongsToConstraint;
}
......
......@@ -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() {
// TODO in many cases possible, but more complicated
return false;
......
......@@ -453,7 +453,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeCall("getCatalog");
checkClosed();
if (catalog == null) {
CommandInterface cat = prepareCommand("CALL DATABASE()");
CommandInterface cat = prepareCommand("CALL DATABASE()", Integer.MAX_VALUE);
ResultInterface result = cat.executeQuery(0, false);
result.next();
catalog = result.currentRow()[0].getString();
......@@ -743,7 +743,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint()");
}
checkClosed();
CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(null, savepointId));
CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(null, savepointId), Integer.MAX_VALUE);
set.executeUpdate();
JdbcSavepoint savepoint = new JdbcSavepoint(this, savepointId, null, trace, id);
savepointId++;
......@@ -767,7 +767,7 @@ public class JdbcConnection extends TraceObject implements Connection {
debugCodeAssign("Savepoint", TraceObject.SAVEPOINT, id, "setSavepoint(" + quote(name) + ")");
}
checkClosed();
CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(name, 0));
CommandInterface set = prepareCommand("SAVEPOINT " + JdbcSavepoint.getName(name, 0), Integer.MAX_VALUE);
set.executeUpdate();
JdbcSavepoint savepoint = new JdbcSavepoint(this, 0, name, trace, id);
return savepoint;
......@@ -957,12 +957,12 @@ public class JdbcConnection extends TraceObject implements Connection {
}
}
CommandInterface prepareCommand(String sql) throws SQLException {
return session.prepareCommand(sql);
CommandInterface prepareCommand(String sql, int fetchSize) throws SQLException {
return session.prepareCommand(sql, fetchSize);
}
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 {
......
......@@ -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 {
super(session, conn, resultSetType, id, closeWithResultSet);
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 {
......
......@@ -38,18 +38,18 @@ public class JdbcSQLException extends SQLException {
buildMessage();
//#ifdef JDK14
initCause(cause);
//#endif
//#endif
}
/**
* Get the detail error message.
*
*
* @return the message
*/
public String getMessage() {
return message;
}
/**
* INTERNAL
*/
......@@ -62,13 +62,13 @@ public class JdbcSQLException extends SQLException {
*/
public void printStackTrace() {
super.printStackTrace();
//#ifdef JDK13
//#ifdef JDK13
/*
if (cause != null) {
cause.printStackTrace();
}
*/
//#endif
//#endif
if (getNextException() != null) {
getNextException().printStackTrace();
}
......@@ -88,7 +88,7 @@ public class JdbcSQLException extends SQLException {
cause.printStackTrace(s);
}
*/
//#endif
//#endif
if (getNextException() != null) {
getNextException().printStackTrace(s);
}
......@@ -109,7 +109,7 @@ public class JdbcSQLException extends SQLException {
cause.printStackTrace(s);
}
*/
//#endif
//#endif
if (getNextException() != null) {
getNextException().printStackTrace(s);
}
......@@ -122,26 +122,26 @@ public class JdbcSQLException extends SQLException {
public Throwable getOriginalCause() {
return cause;
}
/**
* Returns the SQL statement.
*
*
* @return the SQL statement
*/
*/
public String getSQL() {
return sql;
}
/**
* INTERNAL
*/
public void setSQL(String sql) {
this.sql = sql;
buildMessage();
}
}
private void buildMessage() {
StringBuffer buff = new StringBuffer(originalMessage);
StringBuffer buff = new StringBuffer(originalMessage == null ? "- " : originalMessage);
if (sql != null) {
buff.append("; SQL statement:\n");
buff.append(sql);
......@@ -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
*
*
* @return the string representation
*/
*/
public String toString() {
if (trace == null) {
return super.toString();
......
......@@ -16,10 +16,10 @@ import org.h2.message.TraceObject;
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.
*/
public class JdbcSavepoint extends TraceObject
public class JdbcSavepoint extends TraceObject
//#ifdef JDK14
implements Savepoint
//#endif
......@@ -52,7 +52,7 @@ implements Savepoint
void rollback() throws SQLException {
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 {
......@@ -77,11 +77,11 @@ implements Savepoint
throw logAndConvert(e);
}
}
/**
* Get the name of this savepoint.
* @return the name
*/
*/
public String getSavepointName() throws SQLException {
try {
debugCodeCall("getSavepointName");
......@@ -94,13 +94,13 @@ implements Savepoint
throw logAndConvert(e);
}
}
/**
* INTERNAL
*/
public String toString() {
return getTraceObjectName() + ": id=" + savepointId + " name=" + name;
}
}
......@@ -14,6 +14,7 @@ import java.sql.Statement;
import org.h2.command.CommandInterface;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.SessionInterface;
import org.h2.message.Message;
import org.h2.message.TraceObject;
......@@ -32,9 +33,8 @@ public class JdbcStatement extends TraceObject implements Statement {
protected boolean escapeProcessing = true;
protected int queryTimeout;
protected boolean queryTimeoutSet;
protected int fetchSize;
protected boolean fetchSizeSet;
protected int updateCount;
protected int fetchSize = SysProperties.SERVER_RESULT_SET_FETCH_SIZE;
; protected int updateCount;
private CommandInterface executingCommand;
private ObjectArray batchCommands;
protected int resultSetType;
......@@ -59,7 +59,7 @@ public class JdbcStatement extends TraceObject implements Statement {
sql = conn.translateSQL(sql);
}
synchronized (session) {
CommandInterface command = conn.prepareCommand(sql);
CommandInterface command = conn.prepareCommand(sql, fetchSize);
ResultInterface result;
boolean scrollable = resultSetType != ResultSet.TYPE_FORWARD_ONLY;
setExecutingStatement(command);
......@@ -102,7 +102,7 @@ public class JdbcStatement extends TraceObject implements Statement {
if (escapeProcessing) {
sql = conn.translateSQL(sql);
}
CommandInterface command = conn.prepareCommand(sql);
CommandInterface command = conn.prepareCommand(sql, fetchSize);
synchronized (session) {
setExecutingStatement(command);
try {
......@@ -140,7 +140,7 @@ public class JdbcStatement extends TraceObject implements Statement {
if (escapeProcessing) {
sql = conn.translateSQL(sql);
}
CommandInterface command = conn.prepareCommand(sql);
CommandInterface command = conn.prepareCommand(sql, fetchSize);
boolean returnsResultSet;
synchronized (session) {
setExecutingStatement(command);
......@@ -369,7 +369,9 @@ public class JdbcStatement extends TraceObject implements Statement {
* Sets the number of rows suggested to read in one step.
* This value cannot be higher than the maximum rows (setMaxRows)
* 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
* @throws SQLException if this object is closed
......@@ -381,8 +383,10 @@ public class JdbcStatement extends TraceObject implements Statement {
if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) {
throw Message.getInvalidValueException("" + rows, "rows");
}
if (rows == 0) {
rows = SysProperties.SERVER_RESULT_SET_FETCH_SIZE;
}
fetchSize = rows;
fetchSizeSet = true;
} catch (Throwable e) {
throw logAndConvert(e);
}
......@@ -893,13 +897,13 @@ public class JdbcStatement extends TraceObject implements Statement {
debugCode("setPoolable("+poolable+");");
}
}
/**
* INTERNAL
*/
public String toString() {
return getTraceObjectName();
}
}
......@@ -174,7 +174,7 @@ public class TraceObject {
protected SQLException logAndConvert(Throwable e) {
if (SysProperties.LOG_ALL_ERRORS) {
synchronized (this.getClass()) {
synchronized (TraceObject.class) {
// e.printStackTrace();
try {
Writer writer = FileUtils.openFileWriter(SysProperties.LOG_ALL_ERRORS_FILE, true);
......
......@@ -21,12 +21,13 @@ import org.h2.value.Value;
*/
public class ResultRemote implements ResultInterface {
private final int fetchSize;
private SessionRemote session;
private Transfer transfer;
private int id;
private ResultColumn[] columns;
private Value[] currentRow;
private int rowId, rowCount;
private int rowId, rowCount, rowOffset;
private ObjectArray result;
private ObjectArray lobValues;
......@@ -38,7 +39,7 @@ public class ResultRemote implements ResultInterface {
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 {
this.session = session;
this.transfer = transfer;
......@@ -49,21 +50,9 @@ public class ResultRemote implements ResultInterface {
columns[i] = new ResultColumn(transfer);
}
rowId = -1;
if (rowCount < readRows) {
result = new ObjectArray();
readFully();
sendClose();
}
}
private void readFully() throws SQLException {
while (true) {
Value[] values = fetchRow(false);
if (values == null) {
break;
}
result.add(values);
}
result = new ObjectArray();
this.fetchSize = fetchSize;
fetchRows(false);
}
public String getAlias(int i) {
......@@ -128,15 +117,14 @@ public class ResultRemote implements ResultInterface {
}
public boolean next() throws SQLException {
// TODO optimization: don't need rowCount and fetchRow setting
if (rowId < rowCount) {
rowId++;
remapIfOld();
if (rowId < rowCount) {
if (session == null) {
currentRow = (Value[]) result.get(rowId);
} else {
currentRow = fetchRow(true);
if (rowId - rowOffset >= result.size()) {
fetchRows(true);
}
currentRow = (Value[]) result.get(rowId - rowOffset);
return true;
}
currentRow = null;
......@@ -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) {
session.checkClosed();
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?
}
rowOffset += result.size();
result.clear();
int fetch = Math.min(fetchSize, rowCount - rowOffset);
if (sendFetch) {
session.traceOperation("RESULT_FETCH_ROW", id);
transfer.writeInt(SessionRemote.RESULT_FETCH_ROW).writeInt(id);
session.traceOperation("RESULT_FETCH_ROWS", id);
transfer.writeInt(SessionRemote.RESULT_FETCH_ROWS).writeInt(id).writeInt(fetch);
session.done(transfer);
}
boolean row = transfer.readBoolean();
if (row) {
for (int r = 0; r < fetch; r++) {
boolean row = transfer.readBoolean();
if (!row) {
break;
}
int len = columns.length;
Value[] values = new Value[len];
for (int i = 0; i < len; i++) {
......@@ -223,10 +227,10 @@ public class ResultRemote implements ResultInterface {
lobValues.add(v);
}
}
return values;
} else {
result.add(values);
}
if (rowOffset + result.size() >= rowCount) {
sendClose();
return null;
}
} catch (IOException e) {
throw Message.convertIOException(e, null);
......
......@@ -221,7 +221,7 @@ public class TcpServerThread implements Runnable {
int id = transfer.readInt();
int objectId = transfer.readInt();
int maxRows = transfer.readInt();
int readRows = transfer.readInt();
int fetchSize = transfer.readInt();
Command command = (Command) cache.getObject(id, false);
setParameters(command);
LocalResult result = command.executeQueryLocal(maxRows);
......@@ -233,10 +233,9 @@ public class TcpServerThread implements Runnable {
for (int i = 0; i < columnCount; i++) {
ResultColumn.writeColumn(transfer, result, i);
}
if (rowCount < readRows) {
for (int i = 0; i <= rowCount; i++) {
sendRow(result);
}
int fetch = Math.min(rowCount, fetchSize);
for (int i = 0; i < fetch; i++) {
sendRow(result);
}
transfer.flush();
break;
......@@ -263,11 +262,14 @@ public class TcpServerThread implements Runnable {
}
break;
}
case SessionRemote.RESULT_FETCH_ROW: {
case SessionRemote.RESULT_FETCH_ROWS: {
int id = transfer.readInt();
int count = transfer.readInt();
LocalResult result = (LocalResult) cache.getObject(id, false);
transfer.writeInt(SessionRemote.STATUS_OK);
sendRow(result);
for (int i = 0; i < count; i++) {
sendRow(result);
}
transfer.flush();
break;
}
......@@ -303,13 +305,14 @@ public class TcpServerThread implements Runnable {
}
private void sendRow(LocalResult result) throws IOException, SQLException {
boolean n = result.next();
transfer.writeBoolean(n);
if (n) {
if (result.next()) {
transfer.writeBoolean(true);
Value[] v = result.currentRow();
for (int i = 0; i < result.getVisibleColumnCount(); i++) {
transfer.writeValue(v[i]);
}
} else {
transfer.writeBoolean(false);
}
}
......
......@@ -265,13 +265,13 @@ public class FileStore {
if (synchronousMode && newLength > fileLength) {
extendByWriting(newLength);
} else {
file.setLength(newLength);
file.setFileLength(newLength);
}
fileLength = newLength;
} catch (IOException e) {
if (freeUpDiskSpace()) {
try {
file.setLength(newLength);
file.setFileLength(newLength);
} catch (IOException e2) {
throw Message.convertIOException(e2, name);
}
......@@ -292,7 +292,7 @@ public class FileStore {
}
if (SysProperties.CHECK2 && len % Constants.FILE_BLOCK_SIZE != 0) {
long newLength = len + Constants.FILE_BLOCK_SIZE - (len % Constants.FILE_BLOCK_SIZE);
file.setLength(newLength);
file.setFileLength(newLength);
fileLength = newLength;
throw Message.getInternalError("unaligned file length " + name + " len " + len);
}
......
......@@ -64,6 +64,6 @@ public interface FileObject {
*
* @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 {
this.pos = (int) pos;
}
public void setLength(long newLength) throws IOException {
public void setFileLength(long newLength) throws IOException {
this.length = (int) newLength;
if (length != data.length) {
byte[] n = new byte[length];
......
......@@ -4,51 +4,27 @@
*/
package org.h2.store.fs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.h2.util.FileUtils;
/**
* This class is wraps a RandomAccessFile.
* This class is extends a RandomAccessFile.
*/
public class FileObjectDisk implements FileObject {
private RandomAccessFile file;
public class FileObjectDisk extends RandomAccessFile implements FileObject {
FileObjectDisk(RandomAccessFile file) {
this.file = file;
}
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 FileObjectDisk(String fileName, String mode) throws FileNotFoundException {
super(fileName, mode);
}
public void sync() throws IOException {
file.getFD().sync();
getFD().sync();
}
public void setLength(long newLength) throws IOException {
FileUtils.setLength(file, newLength);
public void setFileLength(long newLength) throws IOException {
FileUtils.setLength(this, newLength);
}
}
......@@ -128,7 +128,7 @@ public class FileObjectMemory implements FileObject {
return length;
}
public void setLength(long l) {
public void setFileLength(long l) {
touch();
if (l < length) {
pos = Math.min(pos, l);
......
......@@ -21,7 +21,7 @@ public class FileObjectOutputStream extends OutputStream {
file.seek(file.length());
} else {
file.seek(0);
file.setLength(0);
file.setFileLength(0);
}
}
......
......@@ -70,7 +70,7 @@ public class FileObjectZip implements FileObject {
this.pos = pos;
}
public void setLength(long newLength) throws IOException {
public void setFileLength(long newLength) throws IOException {
throw new IOException("File is read-only");
}
......
......@@ -10,7 +10,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.sql.SQLException;
......@@ -352,15 +351,15 @@ public class FileSystemDisk extends FileSystem {
public FileObject openFileObject(String fileName, String mode) throws IOException {
fileName = translateFileName(fileName);
RandomAccessFile f;
FileObjectDisk f;
try {
f = new RandomAccessFile(fileName, mode);
f = new FileObjectDisk(fileName, mode);
trace("openRandomAccessFile", fileName, f);
} catch (IOException e) {
freeMemoryAndFinalize();
f = new RandomAccessFile(fileName, mode);
f = new FileObjectDisk(fileName, mode);
}
return new FileObjectDisk(f);
return f;
}
}
......@@ -156,7 +156,7 @@ public class Column {
public long getPrecision() {
return precision;
}
public int getDisplaySize() {
return displaySize;
}
......@@ -401,10 +401,21 @@ public class Column {
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() {
return selectivity == 0 ? Constants.SELECTIVITY_DEFAULT : selectivity;
}
/**
* Set the new selectivity of a column.
*
* @param selectivity the new value
*/
public void setSelectivity(int selectivity) {
selectivity = selectivity < 0 ? 0 : (selectivity > 100 ? 100 : selectivity);
this.selectivity = selectivity;
......
......@@ -736,7 +736,7 @@ public class MetaTable extends Table {
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.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.emergencySpaceMin", "" + SysProperties.EMERGENCY_SPACE_MIN});
add(rows, new String[]{"h2.objectCache", "" + SysProperties.OBJECT_CACHE});
......
......@@ -10,6 +10,7 @@ import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
/**
* Convert a trace file to a java class.
......@@ -71,7 +72,7 @@ public class ConvertTraceFile {
* @throws IOException
*/
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 scriptWriter = new PrintWriter(FileUtils.openFileWriter(script, false));
javaWriter.println("import java.io.*;");
......
......@@ -135,7 +135,7 @@ public class Script {
ResultSet rs = stat.executeQuery("SCRIPT");
while (rs.next()) {
String s = rs.getString(1);
writer.println(s + ";");
writer.println(s);
}
writer.close();
} 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 @@
*/
package org.h2.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.sql.SQLException;
import java.util.Properties;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.message.TraceSystem;
import org.h2.store.fs.FileSystem;
......@@ -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 {
return FileSystem.getInstance(name).getFileName(name);
}
......@@ -156,7 +150,11 @@ public class FileUtils {
public static Writer openFileWriter(String fileName, boolean append) throws SQLException {
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) {
......
......@@ -154,6 +154,8 @@ java org.h2.test.TestAll timer
/*
add tests with select distinct type (code coverage)
documentation: package.html
write to the db file what version was used to create a database
......@@ -532,6 +534,7 @@ Features of H2
}
void testDatabase() throws Exception {
System.out.println("test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc);
beforeTest();
......
......@@ -10,6 +10,7 @@ import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import org.h2.constant.SysProperties;
import org.h2.jdbc.JdbcStatement;
import org.h2.test.TestBase;
......@@ -100,13 +101,13 @@ public class TestStatement extends TestBase {
void testStatement() throws Exception {
Statement stat = conn.createStatement();
//#ifdef JDK14
//#ifdef JDK14
check(ResultSet.HOLD_CURSORS_OVER_COMMIT, conn.getHoldability());
conn.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT);
check(ResultSet.CLOSE_CURSORS_AT_COMMIT, conn.getHoldability());
//#endif
// ignored
stat.setCursorName("x");
// fixed return value
......@@ -115,10 +116,12 @@ public class TestStatement extends TestBase {
stat.setFetchDirection(ResultSet.FETCH_REVERSE);
// ignored
stat.setMaxFieldSize(100);
check(0, stat.getFetchSize());
check(SysProperties.SERVER_RESULT_SET_FETCH_SIZE, stat.getFetchSize());
stat.setFetchSize(10);
check(10, stat.getFetchSize());
stat.setFetchSize(0);
check(SysProperties.SERVER_RESULT_SET_FETCH_SIZE, stat.getFetchSize());
check(ResultSet.TYPE_FORWARD_ONLY, stat.getResultSetType());
Statement stat2 = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);
check(ResultSet.TYPE_SCROLL_SENSITIVE, stat2.getResultSetType());
......@@ -128,8 +131,8 @@ public class TestStatement extends TestBase {
check(!((JdbcStatement) stat2).isClosed());
stat2.close();
check(((JdbcStatement) stat2).isClosed());
ResultSet rs;
int count;
boolean result;
......
......@@ -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;
EXPLAIN SELECT * FROM TEST WHERE ID = (SELECT MAX(ID) FROM TEST);
> 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
drop table test;
......@@ -994,9 +994,9 @@ script nopasswords nosettings blocksize 10;
> DROP ALIAS IF EXISTS SYSTEM_COMBINE_CLOB;
> DROP TABLE IF EXISTS SYSTEM_LOB_STREAM;
> 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, 1, ' ', NULL);;
> INSERT INTO SYSTEM_LOB_STREAM VALUES(0, 2, ' ', 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, 2, ' ', NULL);
> rows: 15
drop table test;
......@@ -1691,11 +1691,11 @@ select * from test order by id;
> rows (ordered): 3
select rownum, (select count(*) from test), rownum from test;
> ROWNUM() SELECT COUNT(*) FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ /* direct lookup query */ ROWNUM()
> -------- --------------------------------------------------------------------------------------- --------
> 1 3 1
> 2 3 2
> 3 3 3
> ROWNUM() SELECT COUNT(*) FROM PUBLIC.TEST /* PUBLIC.TEST_TABLE_SCAN */ /* direct lookup */ ROWNUM()
> -------- --------------------------------------------------------------------------------- --------
> 1 3 1
> 2 3 2
> 3 3 3
> rows: 3
delete from test t0 where rownum<2;
......
......@@ -184,7 +184,7 @@ public class TestFileSystem extends TestBase {
break;
}
case 2: {
f.setLength(pos);
f.setFileLength(pos);
ra.setLength(pos);
if (ra.getFilePointer() > pos) {
f.seek(0);
......
......@@ -14,6 +14,8 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.test.trace.Player;
......@@ -37,6 +39,8 @@ public class TestTools extends TestBase {
return;
}
deleteDb("utils");
testScriptRunscriptLob();
deleteDb("utils");
testServerMain();
testRemove();
testConvertTraceFile();
......@@ -157,7 +161,6 @@ public class TestTools extends TestBase {
deleteDb("toolsConvertTraceFile");
RunScript.main(new String[]{"-url", url, "-user", "sa", "-script", baseDir + "/test.sql"});
testTraceFile(url);
}
private void testTraceFile(String url) throws Exception {
......@@ -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 {
Class.forName("org.h2.Driver");
String url = "jdbc:h2:" + baseDir + "/utils";
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论