提交 23d68f4f authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 02b91ecc
......@@ -495,9 +495,27 @@ The database can be used from Microsoft .NET even without using Java, by using I
</li></ul>
<p>
If you want your C# application use H2, you need to add the h2.dll and the
IKVM.OpenJDK.ClassLibrary.dll to your C# solution. See also the short
<a href="http://groups.google.com/group/h2-database/browse_thread/thread/400bc5a9bc9de3fd">C# source code example</a>.
IKVM.OpenJDK.ClassLibrary.dll to your C# solution. Here some sample code:
</p>
<pre>
using System;
using java.sql;
class Test
{
static public void Main()
{
org.h2.Driver.load();
Connection conn = DriverManager.getConnection("jdbc:h2:~/test", "sa", "sa");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT 'Hello World'");
while (rs.next())
{
Console.WriteLine(rs.getString(1));
}
}
}
</pre>
<br /><a name="acid"></a>
<h2>ACID</h2>
......
......@@ -116,9 +116,9 @@ public class RuleList implements Rule {
}
public void addNextTokenList(String query, Sentence sentence) {
if (sentence.stop()) {
// if (sentence.stop()) {
//
}
// }
if (or) {
for (int i = 0; i < list.size(); i++) {
get(i).addNextTokenList(query, sentence);
......
......@@ -3386,19 +3386,20 @@ public class Parser {
CreateSequence command = new CreateSequence(session, getSchema());
command.setIfNotExists(ifNotExists);
command.setSequenceName(sequenceName);
if (readIf("START")) {
readIf("WITH");
command.setStartWith(readExpression());
}
if (readIf("INCREMENT")) {
readIf("BY");
command.setIncrement(readExpression());
}
if (readIf("CACHE")) {
command.setCacheSize(readExpression());
}
if (readIf("BELONGS_TO_TABLE")) {
command.setBelongsToTable(true);
while (true) {
if (readIf("START")) {
readIf("WITH");
command.setStartWith(readExpression());
} else if (readIf("INCREMENT")) {
readIf("BY");
command.setIncrement(readExpression());
} else if (readIf("CACHE")) {
command.setCacheSize(readExpression());
} else if (readIf("BELONGS_TO_TABLE")) {
command.setBelongsToTable(true);
} else {
break;
}
}
return command;
}
......
......@@ -62,8 +62,8 @@ public class CreateView extends SchemaCommand {
} else {
querySQL = select.getSQL();
}
Session s = db.getSystemSession();
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, s, recursive);
Session sysSession = db.getSystemSession();
TableView view = new TableView(getSchema(), id, viewName, querySQL, null, columnNames, sysSession, recursive);
view.setComment(comment);
db.addSchemaObject(session, view);
return 0;
......
......@@ -75,7 +75,14 @@ public class AlterSequence extends SchemaCommand {
}
sequence.setIncrement(incrementValue);
}
db.update(session, sequence);
// need to use the system session, so that the update
// can be committed immediately - not committing it
// would keep other transactions from using the sequence
Session sysSession = db.getSystemSession();
synchronized (sysSession) {
db.update(sysSession, sequence);
sysSession.commit(true);
}
return 0;
}
......
......@@ -386,6 +386,14 @@ public class SysProperties {
* The default result set fetch size when using the server mode.
*/
public static final int SERVER_RESULT_SET_FETCH_SIZE = getIntSetting("h2.serverResultSetFetchSize", 100);
/**
* System property <code>h2.sortNullsHigh</code> (default: false).<br />
* Invert the default sorting behavior for NULL values, such that NULL values
* are sorted to the end of a result set in an ascending sort and to the beginning
* of a result set in a descending sort.
*/
public static final boolean SORT_NULLS_HIGH = getBooleanSetting("h2.sortNullsHigh", false);
/**
* System property <code>h2.traceIO</code> (default: false).<br />
......
......@@ -15,6 +15,7 @@ import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.SearchRow;
......@@ -167,11 +168,12 @@ public class Aggregate extends Expression {
if ((sortType & SortOrder.DESCENDING) != 0) {
first = !first;
}
SearchRow row = index.findFirstOrLast(session, first);
Cursor cursor = index.findFirstOrLast(session, first);
Value v;
if (row == null) {
if (cursor == null) {
v = ValueNull.INSTANCE;
} else {
SearchRow row = cursor.getSearchRow();
v = row.getValue(index.getColumns()[0].getColumnId());
}
return v;
......
......@@ -119,8 +119,9 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
*
* @param session the session
* @param first true for the first value, false for the last
* @return a cursor or null
*/
public abstract SearchRow findFirstOrLast(Session session, boolean first) throws SQLException;
public abstract Cursor findFirstOrLast(Session session, boolean first) throws SQLException;
/**
* Check if this index needs to be re-built.
......
......@@ -90,5 +90,10 @@ public class BtreeCursor implements Cursor {
}
return currentSearchRow != null;
}
public boolean previous() throws SQLException {
top.page.previous(this, top.position);
return currentSearchRow != null;
}
}
......@@ -334,7 +334,7 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return true;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
if (first) {
// TODO optimization: this loops through NULL elements
Cursor cursor = find(session, null, false, null);
......@@ -342,12 +342,26 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
SearchRow row = cursor.getSearchRow();
Value v = row.getValue(columnIds[0]);
if (v != ValueNull.INSTANCE) {
return row;
return cursor;
}
}
return null;
} else {
return getRoot(session).getLast(session);
BtreePage root = getRoot(session);
BtreeCursor cursor = new BtreeCursor(session, this, null);
root.last(cursor);
// TODO optimization: this loops through NULL elements
do {
SearchRow row = cursor.getSearchRow();
if (row == null) {
break;
}
Value v = row.getValue(columnIds[0]);
if (v != ValueNull.INSTANCE) {
return cursor;
}
} while (cursor.previous());
return null;
}
}
......
......@@ -182,6 +182,18 @@ public class BtreeLeaf extends BtreePage {
cursor.pop();
nextUpper(cursor);
}
public void previous(BtreeCursor cursor, int i) throws SQLException {
i--;
if (i >= 0) {
SearchRow r = (SearchRow) pageData.get(i);
cursor.setCurrentRow(r);
cursor.setStackPosition(i);
return;
}
cursor.pop();
previousUpper(cursor);
}
public void first(BtreeCursor cursor) throws SQLException {
if (pageData.size() == 0) {
......@@ -195,6 +207,20 @@ public class BtreeLeaf extends BtreePage {
SearchRow row = (SearchRow) pageData.get(0);
cursor.setCurrentRow(row);
}
public void last(BtreeCursor cursor) throws SQLException {
int last = pageData.size() - 1;
if (last < 0) {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
}
previousUpper(cursor);
return;
}
cursor.push(this, last);
SearchRow row = (SearchRow) pageData.get(last);
cursor.setCurrentRow(row);
}
private void nextUpper(BtreeCursor cursor) throws SQLException {
BtreePosition upper = cursor.pop();
......@@ -205,6 +231,16 @@ public class BtreeLeaf extends BtreePage {
upper.page.next(cursor, upper.position);
}
}
private void previousUpper(BtreeCursor cursor) throws SQLException {
BtreePosition upper = cursor.pop();
if (upper == null) {
cursor.setCurrentRow(null);
} else {
cursor.push(upper.page, upper.position);
upper.page.previous(cursor, upper.position);
}
}
public void prepareWrite() throws SQLException {
if (getRealByteCount() >= DiskFile.BLOCK_SIZE * BLOCKS_PER_PAGE) {
......@@ -263,16 +299,6 @@ public class BtreeLeaf extends BtreePage {
return size;
}
SearchRow getLast(Session session) throws SQLException {
if (pageData.size() == 0) {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
}
return null;
}
return (SearchRow) pageData.get(pageData.size() - 1);
}
SearchRow getFirst(Session session) throws SQLException {
if (pageData.size() == 0) {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
......
......@@ -63,9 +63,9 @@ public class BtreeNode extends BtreePage {
SearchRow r = (SearchRow) pageData.get(i);
if (r == null) {
int p = pageChildren.get(i + 1);
Session session = index.getDatabase().getSystemSession();
BtreePage page = index.getPage(session, p);
r = page.getFirst(session);
Session sysSession = index.getDatabase().getSystemSession();
BtreePage page = index.getPage(sysSession, p);
r = page.getFirst(sysSession);
pageData.set(i, r);
}
return r;
......@@ -272,6 +272,17 @@ public class BtreeNode extends BtreePage {
}
nextUpper(cursor);
}
public void previous(BtreeCursor cursor, int i) throws SQLException {
i--;
if (i >= 0) {
cursor.setStackPosition(i);
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(i));
page.last(cursor);
return;
}
previousUpper(cursor);
}
private void nextUpper(BtreeCursor cursor) throws SQLException {
cursor.pop();
......@@ -283,12 +294,30 @@ public class BtreeNode extends BtreePage {
upper.page.next(cursor, upper.position);
}
}
private void previousUpper(BtreeCursor cursor) throws SQLException {
cursor.pop();
BtreePosition upper = cursor.pop();
if (upper == null) {
cursor.setCurrentRow(null);
} else {
cursor.push(upper.page, upper.position);
upper.page.previous(cursor, upper.position);
}
}
public void first(BtreeCursor cursor) throws SQLException {
cursor.push(this, 0);
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(0));
page.first(cursor);
}
public void last(BtreeCursor cursor) throws SQLException {
int last = pageChildren.size() - 1;
cursor.push(this, last);
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(last));
page.last(cursor);
}
public void prepareWrite() throws SQLException {
if (getRealByteCount() >= DiskFile.BLOCK_SIZE * BLOCKS_PER_PAGE) {
......@@ -339,19 +368,6 @@ public class BtreeNode extends BtreePage {
return size + index.getRecordOverhead();
}
SearchRow getLast(Session session) throws SQLException {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page");
}
for (int i = pageChildren.size() - 1; i >= 0; i--) {
BtreePage page = index.getPage(session, pageChildren.get(i));
if (page != null) {
return page.getLast(session);
}
}
return null;
}
SearchRow getFirst(Session session) throws SQLException {
for (int i = 0; i < pageChildren.size(); i++) {
BtreePage page = index.getPage(session, pageChildren.get(i));
......
......@@ -56,9 +56,10 @@ public abstract class BtreePage extends Record {
abstract BtreePage split(Session session, int splitPoint) 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;
abstract void previous(BtreeCursor cursor, int i) throws SQLException;
abstract void first(BtreeCursor cursor) throws SQLException;
abstract void last(BtreeCursor cursor) throws SQLException;
abstract int getRealByteCount() throws SQLException;
BtreePage(BtreeIndex index) {
......
......@@ -50,5 +50,13 @@ public interface Cursor {
* @return true if another row is available
*/
boolean next() throws SQLException;
/**
* Skip to the previous row if one is available.
* No filtering is made here.
*
* @return true if another row is available
*/
boolean previous() throws SQLException;
}
......@@ -44,5 +44,9 @@ public class FunctionCursor implements Cursor {
}
return row != null;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -76,7 +76,7 @@ public class FunctionIndex extends BaseIndex {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......
......@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -42,4 +43,9 @@ public class HashCursor implements Cursor {
end = true;
return true;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -147,7 +147,7 @@ public class HashIndex extends BaseIndex {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......
......@@ -130,9 +130,9 @@ public interface Index extends SchemaObject {
* @param session the session
* @param first true if the first (lowest for ascending indexes) or last
* value should be returned
* @return the search row with the value
* @return a cursor or null
*/
SearchRow findFirstOrLast(Session session, boolean first) throws SQLException;
Cursor findFirstOrLast(Session session, boolean first) throws SQLException;
/**
* Check if the index needs to be rebuilt.
......
......@@ -61,5 +61,9 @@ public class LinkedCursor implements Cursor {
}
return true;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -162,7 +162,7 @@ public class LinkedIndex extends BaseIndex {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
// TODO optimization: could get the first or last value (in any case;
// maybe not optimized)
throw Message.getUnsupportedException();
......
......@@ -42,5 +42,9 @@ public class MetaCursor implements Cursor {
current = (Row) (index >= rows.size() ? null : rows.get(index++));
return current != null;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -85,7 +85,7 @@ public class MetaIndex extends BaseIndex {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......
......@@ -167,4 +167,9 @@ public class MultiVersionCursor implements Cursor {
}
}
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -78,7 +78,7 @@ public class MultiVersionIndex implements Index {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......
......@@ -51,5 +51,9 @@ public class RangeCursor implements Cursor {
currentRow = new Row(new Value[]{ValueLong.get(current)}, 0);
return current <= max;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -13,8 +13,6 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.table.RangeTable;
import org.h2.value.Value;
import org.h2.value.ValueLong;
/**
* An index for the SYSTEM_RANGE table.
......@@ -75,8 +73,9 @@ public class RangeIndex extends BaseIndex {
return true;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
return new Row(new Value[] { ValueLong.get(first ? min : max) }, 0);
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
long pos = first ? min : max;
return new RangeCursor(pos, pos);
}
}
......@@ -8,6 +8,7 @@ package org.h2.index;
import java.sql.SQLException;
import java.util.Iterator;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -70,4 +71,9 @@ public class ScanCursor implements Cursor {
row = scan.getNextRow(session, row);
return row != null;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -258,7 +258,7 @@ public class ScanIndex extends BaseIndex {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......
......@@ -58,5 +58,11 @@ public class TreeCursor implements Cursor {
}
return node != null;
}
public boolean previous() throws SQLException {
node = tree.previous(node);
return node != null;
}
}
......@@ -336,6 +336,29 @@ public class TreeIndex extends BaseIndex {
}
return x;
}
public TreeNode previous(TreeNode x) {
if (x == null) {
return null;
}
TreeNode l = x.left;
if (l != null) {
x = l;
TreeNode r = x.right;
while (r != null) {
x = r;
r = x.right;
}
return x;
}
TreeNode ch = x;
x = x.parent;
while (x != null && ch == x.left) {
ch = x;
x = x.parent;
}
return x;
}
public void checkRename() throws SQLException {
}
......@@ -348,7 +371,7 @@ public class TreeIndex extends BaseIndex {
return true;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
if (first) {
// TODO optimization: this loops through NULL values
Cursor cursor = find(session, null, null);
......@@ -356,7 +379,7 @@ public class TreeIndex extends BaseIndex {
SearchRow row = cursor.getSearchRow();
Value v = row.getValue(columnIds[0]);
if (v != ValueNull.INSTANCE) {
return row;
return cursor;
}
}
return null;
......@@ -369,9 +392,21 @@ public class TreeIndex extends BaseIndex {
}
x = n;
}
if (x != null) {
return x.row;
if (x == null) {
return null;
}
TreeCursor cursor = new TreeCursor(this, x, null, null);
// TODO optimization: this loops through NULL elements
do {
SearchRow row = cursor.getSearchRow();
if (row == null) {
break;
}
Value v = row.getValue(columnIds[0]);
if (v != ValueNull.INSTANCE) {
return cursor;
}
} while (cursor.previous());
return null;
}
}
......
......@@ -56,5 +56,9 @@ public class ViewCursor implements Cursor {
}
return true;
}
public boolean previous() {
throw Message.getInternalError();
}
}
......@@ -243,7 +243,7 @@ public class ViewIndex extends BaseIndex {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public Cursor findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......
......@@ -16,6 +16,7 @@ import java.sql.RowIdLifetime;
//#endif
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.message.Message;
import org.h2.message.Trace;
......@@ -434,21 +435,21 @@ public class JdbcDatabaseMetaData extends TraceObject implements DatabaseMetaDat
/**
* Checks is NULL values are sorted high (bigger than any non-null values).
*
* @return false
* @return false by default; true if the system property h2.sortNullsHigh is set to true
*/
public boolean nullsAreSortedHigh() {
debugCodeCall("nullsAreSortedHigh");
return false;
return SysProperties.SORT_NULLS_HIGH;
}
/**
* Checks is NULL values are sorted low (smaller than any non-null values).
*
* @return true
* @return true by default; false if the system property h2.sortNullsHigh is set to true
*/
public boolean nullsAreSortedLow() {
debugCodeCall("nullsAreSortedLow");
return true;
return !SysProperties.SORT_NULLS_HIGH;
}
/**
......
......@@ -210,14 +210,17 @@ public class TraceObject {
buff.append(" {");
for (int i = 0; i < params.size(); i++) {
try {
ParameterInterface p = (ParameterInterface) params.get(i);
String s = p.getParamValue().getSQL();
if (i > 0) {
buff.append(", ");
}
buff.append(i + 1);
buff.append(": ");
buff.append(s);
ParameterInterface p = (ParameterInterface) params.get(i);
if (p == null) {
buff.append("unset");
} else {
buff.append(p.getParamValue().getSQL());
}
} catch (SQLException e) {
buff.append("/* ");
buff.append(i + 1);
......
......@@ -7,6 +7,7 @@ package org.h2.result;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Database;
import org.h2.expression.Expression;
import org.h2.util.ObjectArray;
......@@ -21,6 +22,7 @@ import org.h2.value.ValueNull;
public class SortOrder {
public static final int ASCENDING = 0, DESCENDING = 1;
public static final int NULLS_FIRST = 2, NULLS_LAST = 4;
private static final int DEFAULT_NULL_SORT = SysProperties.SORT_NULLS_HIGH ? 1 : -1;
private Database database;
private int len;
......@@ -67,7 +69,7 @@ public class SortOrder {
return aNull ? 1 : -1;
} else {
// see also JdbcDatabaseMetaData.nullsAreSorted*
int comp = aNull ? -1 : 1;
int comp = aNull ? DEFAULT_NULL_SORT : -DEFAULT_NULL_SORT;
return (type & DESCENDING) == 0 ? comp : -comp;
}
}
......
......@@ -92,21 +92,21 @@ public class Sequence extends SchemaObjectBase {
return v;
}
public void flush() throws SQLException {
public synchronized void flush() throws SQLException {
// can not use the session, because it must be committed immediately
// otherwise other threads can not access the sys table.
Session s = database.getSystemSession();
synchronized (this) {
Session sysSession = database.getSystemSession();
synchronized (sysSession) {
// just for this case, use the value with the margin for the script
long realValue = value;
try {
value = valueWithMargin;
database.update(s, this);
database.update(sysSession, this);
} finally {
value = realValue;
}
sysSession.commit(false);
}
s.commit(false);
}
public void close() throws SQLException {
......
......@@ -163,7 +163,10 @@ public class DbContextRule implements Rule {
}
}
}
String q = StringUtils.toUpperEnglish(query.trim());
String q = StringUtils.toUpperEnglish(query);
if (q.trim().length() == 0) {
q = q.trim();
}
DbTableOrView[] tables = schema.tables;
for (int i = 0; i < tables.length; i++) {
DbTableOrView table = tables[i];
......
......@@ -75,12 +75,7 @@ public class FileSystemMemory extends FileSystem {
public String createTempFile(String name, String suffix, boolean deleteOnExit, boolean inTempDir) throws IOException {
name += ".";
for (int i = 0;; i++) {
int test;
// String n = name + RandomUtils.getSecureLong() + suffix;
String n = name + i + suffix;
String n = name + Math.abs(RandomUtils.getSecureLong()) + suffix;
if (!exists(n)) {
// creates the file (not thread safe)
getMemoryFile(n);
......@@ -166,18 +161,24 @@ public class FileSystemMemory extends FileSystem {
public OutputStream openFileOutputStream(String fileName, boolean append) throws SQLException {
try {
return new FileObjectOutputStream(getMemoryFile(fileName), append);
FileObjectMemory obj = getMemoryFile(fileName);
obj.seek(0);
return new FileObjectOutputStream(obj, append);
} catch (IOException e) {
throw Message.convertIOException(e, fileName);
}
}
public InputStream openFileInputStream(String fileName) throws IOException {
return new FileObjectInputStream(getMemoryFile(fileName));
FileObjectMemory obj = getMemoryFile(fileName);
obj.seek(0);
return new FileObjectInputStream(obj);
}
public FileObject openFileObject(String fileName, String mode) throws IOException {
return getMemoryFile(fileName);
FileObjectMemory obj = getMemoryFile(fileName);
obj.seek(0);
return obj;
}
private FileObjectMemory getMemoryFile(String fileName) {
......@@ -188,8 +189,6 @@ public class FileSystemMemory extends FileSystem {
m = new FileObjectMemory(fileName, compress);
MEMORY_FILES.put(fileName, m);
}
// TODO the memory file only supports one pointer
m.seek(0);
return m;
}
}
......
......@@ -747,6 +747,7 @@ public class MetaTable extends Table {
add(rows, new String[]{"h2.scriptDirectory", SysProperties.scriptDirectory});
add(rows, new String[]{"h2.serverCachedObjects", "" + SysProperties.SERVER_CACHED_OBJECTS});
add(rows, new String[]{"h2.serverResultSetFetchSize", "" + SysProperties.SERVER_RESULT_SET_FETCH_SIZE});
add(rows, new String[]{"h2.sortNullsHigh", "" + SysProperties.SORT_NULLS_HIGH});
DiskFile dataFile = database.getDataFile();
if (dataFile != null) {
add(rows, new String[] { "CACHE_TYPE", dataFile.getCache().getTypeName() });
......
......@@ -27,7 +27,6 @@ import org.h2.result.RowList;
import org.h2.result.SearchRow;
import org.h2.result.SimpleRow;
import org.h2.result.SimpleRowValue;
import org.h2.result.SortOrder;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObjectBase;
import org.h2.schema.Sequence;
......@@ -643,12 +642,6 @@ public abstract class Table extends SchemaObjectBase {
for (int i = 1; indexes != null && i < indexes.size(); i++) {
Index index = (Index) indexes.get(i);
if (index.canGetFirstOrLast()) {
IndexColumn idxCol = index.getIndexColumns()[0];
if ((idxCol.sortType & SortOrder.DESCENDING) != 0 && (idxCol.sortType & SortOrder.NULLS_FIRST) == 0) {
// for descending sorted columns, if the NULLs
// are at the end, it does not work for some index types
continue;
}
int idx = index.getColumnIndex(column);
if (idx == 0) {
return index;
......
......@@ -311,7 +311,7 @@ public class Recover implements DataHandler {
}
private void writeDataError(PrintWriter writer, String error, byte[] data, int dumpBlocks) throws IOException {
writer.println("-- ERROR:" + error + " block:" + block + " blockCount:" + blockCount + " storageId:"
writer.println("-- ERROR: " + error + " block:" + block + " blockCount:" + blockCount + " storageId:"
+ storageId + " recordLength: " + recordLength + " valueId:" + valueId);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < dumpBlocks * DiskFile.BLOCK_SIZE; i++) {
......@@ -720,7 +720,7 @@ public class Recover implements DataHandler {
writeDataError(writer, "blockCount<0", s.getBytes(), 1);
blockCount = 1;
continue;
} else if ((blockCount * blockSize) >= Integer.MAX_VALUE / 4) {
} else if ((blockCount * blockSize) >= Integer.MAX_VALUE / 4 || (blockCount * blockSize) < 0) {
writeDataError(writer, "blockCount=" + blockCount, s.getBytes(), 1);
blockCount = 1;
continue;
......@@ -737,8 +737,16 @@ public class Recover implements DataHandler {
if ((blockCount * blockSize) < 0) {
writeDataError(writer, "wrong blockCount", s.getBytes(), 1);
blockCount = 1;
continue;
} else {
store.readFully(s.getBytes(), blockSize, blockCount * blockSize - blockSize);
try {
store.readFully(s.getBytes(), blockSize, blockCount * blockSize - blockSize);
} catch (Throwable e) {
writeDataError(writer, "eof", s.getBytes(), 1);
blockCount = 1;
store = FileStore.open(null, fileName, "r", magic);
continue;
}
}
}
try {
......
......@@ -238,6 +238,9 @@ The recovery tool didn't work correctly for tables without rows.
For years below 1, the YEAR method didn't return the correct value,
and the conversion from date and timestamp to varchar was incorrect.
CSVWRITE caused a NullPointerException when not specifying a nullString.
New system property h2.sortNullsHigh to invert the default sorting behavior
for NULL. The default didn't change.
Altering a sequence didn't unlock the system table when autocommit switched off.
Roadmap:
......@@ -558,28 +561,6 @@ Use a default delay of 1 second before closing a database.
new TestValue().runTest(this);
new TestValueHashMap().runTest(this);
new TestValueMemory().runTest(this);
new TestFile().runTest(this);
new TestFileLock().runTest(this);
new TestFtp().runTest(this);
new TestFileSystem().runTest(this);
new TestIntArray().runTest(this);
new TestIntIntHashMap().runTest(this);
new TestMultiThreadedKernel().runTest(this);
new TestOverflow().runTest(this);
new TestPattern().runTest(this);
new TestReader().runTest(this);
new TestRecovery().runTest(this);
new TestSampleApps().runTest(this);
new TestScriptReader().runTest(this);
runTest("org.h2.test.unit.TestServlet");
new TestSecurity().runTest(this);
new TestStreams().runTest(this);
new TestStringCache().runTest(this);
new TestStringUtils().runTest(this);
new TestTools().runTest(this);
new TestValue().runTest(this);
new TestValueHashMap().runTest(this);
new TestValueMemory().runTest(this);
afterTest();
}
......
......@@ -23,6 +23,7 @@ import org.h2.test.TestBase;
public class TestOptimizations extends TestBase {
public void test() throws Exception {
testMinMaxNullOptimization();
if (config.networked) {
return;
}
......@@ -36,6 +37,63 @@ public class TestOptimizations extends TestBase {
testMinMaxCountOptimization(true);
testMinMaxCountOptimization(false);
}
private void testMinMaxNullOptimization() throws Exception {
deleteDb("optimizations");
Connection conn = getConnection("optimizations");
Statement stat = conn.createStatement();
Random random = new Random(1);
int len = getSize(50, 500);
for (int i = 0; i < len; i++) {
stat.execute("drop table if exists test");
stat.execute("create table test(x int)");
if (random.nextBoolean()) {
int count = random.nextBoolean() ? 1 : 1 + random.nextInt(len);
if (count > 0) {
stat.execute("insert into test select null from system_range(1, " + count + ")");
}
}
int maxExpected = -1;
int minExpected = -1;
if (random.nextInt(10) != 1) {
minExpected = 1;
maxExpected = 1 + random.nextInt(len);
stat.execute("insert into test select x from system_range(1, " + maxExpected + ")");
}
String sql = "create index idx on test(x";
if (random.nextBoolean()) {
sql += " desc";
}
if (random.nextBoolean()) {
if (random.nextBoolean()) {
sql += " nulls first";
} else {
sql += " nulls last";
}
}
sql += ")";
stat.execute(sql);
ResultSet rs = stat.executeQuery("explain select min(x), max(x) from test");
rs.next();
if (!config.mvcc) {
String plan = rs.getString(1);
check(plan.indexOf("direct") > 0);
}
rs = stat.executeQuery("select min(x), max(x) from test");
rs.next();
int min = rs.getInt(1);
if (rs.wasNull()) {
min = -1;
}
int max = rs.getInt(2);
if (rs.wasNull()) {
max = -1;
}
check(minExpected, min);
check(maxExpected, max);
}
conn.close();
}
private void testMultiColumnRangeQuery() throws Exception {
deleteDb("optimizations");
......
......@@ -17,9 +17,23 @@ import org.h2.test.TestBase;
public class TestSequence extends TestBase {
public void test() throws Exception {
testAlterSequence();
testCache();
testTwo();
}
private void testAlterSequence() throws Exception {
deleteDb("sequence");
Connection conn = getConnection("sequence");
Statement stat = conn.createStatement();
stat.execute("create sequence test");
conn.setAutoCommit(false);
stat.execute("alter sequence test restart with 1");
for (int i = 0; i < 40; i++) {
stat.execute("select nextval('test')");
}
conn.close();
}
private void testCache() throws Exception {
if (config.memory) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论