提交 6b606ae5 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 43fa251a
......@@ -282,7 +282,7 @@ It looks like the development of this database has stopped. The last release was
<td>Simple, small and fast object relational mapping.</td>
</tr><tr>
<td><a href="http://docs.codewave.de/mytunesrss3/">MyTunesRss</a></td>
<td>MyTunesRSS lets you listen to your music whereever you are.</td>
<td>MyTunesRSS lets you listen to your music wherever you are.</td>
</tr><tr>
<td><a href="http://www.polepos.org">PolePosition</a></td>
<td>Open source database benchmark.</td>
......
......@@ -42,11 +42,11 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 / TODO (Build xx)</h3><ul>
<li>New experimental feature MVCC (multi version concurrency control).
Can be set as a option when opening the database (jdbc:h2:test;MVCC=TRUE)
or as a system property (-Dh2.mvcc=true).
or as a system property (-Dh2.mvcc=true). This is work-in-progress, use it at your own risk. Feedback is welcome.
</li><li>Creating more than 10 views that depend on each other was very slow. Reconnecting was slow as well. Fixed.
</li><li>When used as as Servlet, the H2 Console did not work with SSL (using Tomcat). Fixed.
</li><li>When altering a table with foreign key constraint, if there was no manual index created
for the referenced columns, the automatically created index was dropped while still being used.
Fixed.
for the referenced columns, the automatically created index was dropped while still being used. Fixed.
</li><li>Check and foreign key constraints now checks if the existing data is consistent (this can be disabled by appending NOCHECK).
It is also possible to check existing data when re-enabling referential integrity for a table.
</li><li>Some unit tests failed on Linux because the file system works differently. The unit tests are fixed and should work now.
......@@ -1137,6 +1137,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Provide a simple, lightweight O/R mapping tool
</li><li>Provide an Java SQL builder with standard and H2 syntax
</li><li>Data compression for in-memory database
</li><li>Trace: write os, file system, vm,... when opening the database
</li><li>Trace: write dangerous operations (set log 0,...) in every case (including when opening the database)
</li></ul>
<h3>Not Planned</h3>
......
......@@ -53,7 +53,8 @@ Welcome to H2, the free SQL database. The main feature of H2 are:
<tr><td style="border: 0px; background-color: #eee;">
<h3>Support</h3>
<p>
<a href="http://groups.google.com/group/h2-database" target="_top">Google Group: Help and Discussion</a><br /><br />
<a href="http://groups.google.com/group/h2-database" target="_top">English Google Group</a><br />
<a href="http://groups.google.co.jp/group/h2-database-jp" target="_top">Japanese Google Group</a><br /><br />
Or send an e-mail to: <br />
<script type="text/javascript">
<!--
......@@ -70,8 +71,8 @@ Welcome to H2, the free SQL database. The main feature of H2 are:
</script>
</p>
</td></tr>
</table>
</td></tr>
</table>
</td></tr>
<tr><td colspan="3" style="border: 0px">&nbsp;</td></tr>
......
......@@ -9,6 +9,7 @@ import java.util.HashSet;
import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Alias;
import org.h2.expression.Expression;
......@@ -51,15 +52,16 @@ public abstract class Query extends Prepared {
}
public final boolean sameResultAsLast(Session session, Value[] params, Value[] lastParams, long lastEvaluated) throws SQLException {
Database db = session.getDatabase();
for(int i=0; i<params.length; i++) {
if(!session.getDatabase().areEqual(lastParams[i], params[i])) {
if(!db.areEqual(lastParams[i], params[i])) {
return false;
}
}
if(!isEverything(ExpressionVisitor.DETERMINISTIC) || !isEverything(ExpressionVisitor.INDEPENDENT)) {
return false;
}
if(getMaxDataModificationId() > lastEvaluated) {
if(db.getModificationDataId() > lastEvaluated && getMaxDataModificationId() > lastEvaluated) {
return false;
}
return true;
......
......@@ -31,6 +31,7 @@ public class MultiVersionCursor implements Cursor {
}
private void loadNext(boolean base) throws SQLException {
synchronized(index) {
if(base) {
if(baseCursor.next()) {
baseRow = baseCursor.getSearchRow();
......@@ -45,29 +46,37 @@ public class MultiVersionCursor implements Cursor {
}
}
}
}
public Row get() throws SQLException {
synchronized(index) {
if(SysProperties.CHECK && end) {
throw Message.getInternalError();
}
return onBase ? baseCursor.get() : deltaCursor.get();
}
}
public int getPos() {
synchronized(index) {
if(SysProperties.CHECK && end) {
throw Message.getInternalError();
}
return onBase ? baseCursor.getPos() : deltaCursor.getPos();
}
}
public SearchRow getSearchRow() throws SQLException {
synchronized(index) {
if(SysProperties.CHECK && end) {
throw Message.getInternalError();
}
return onBase ? baseCursor.getSearchRow() : deltaCursor.getSearchRow();
}
}
public boolean next() throws SQLException {
synchronized(index) {
if(SysProperties.CHECK && end) {
throw Message.getInternalError();
}
......@@ -147,4 +156,5 @@ public class MultiVersionCursor implements Cursor {
return true;
}
}
}
}
......@@ -29,7 +29,7 @@ public class MultiVersionIndex implements Index {
this.delta = new TreeIndex(table, -1, "DELTA" ,base.getColumns(), deltaIndexType);
}
public void add(Session session, Row row) throws SQLException {
public synchronized void add(Session session, Row row) throws SQLException {
base.add(session, row);
// for example rolling back an delete operation
removeIfExists(session, row);
......@@ -39,11 +39,11 @@ public class MultiVersionIndex implements Index {
}
}
public void close(Session session) throws SQLException {
public synchronized void close(Session session) throws SQLException {
base.close(session);
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
public synchronized Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
Cursor baseCursor = base.find(session, first, last);
Cursor deltaCursor = delta.find(session, first, last);
return new MultiVersionCursor(session, this, baseCursor, deltaCursor);
......@@ -53,7 +53,7 @@ public class MultiVersionIndex implements Index {
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
public synchronized SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......@@ -65,7 +65,7 @@ public class MultiVersionIndex implements Index {
return base.needRebuild();
}
private boolean removeIfExists(Session session, Row row) throws SQLException {
private synchronized boolean removeIfExists(Session session, Row row) throws SQLException {
// maybe it was inserted by the same session just before
Cursor c = delta.find(session, row, row);
while(c.next()) {
......@@ -78,7 +78,7 @@ public class MultiVersionIndex implements Index {
return false;
}
public void remove(Session session, Row row) throws SQLException {
public synchronized void remove(Session session, Row row) throws SQLException {
base.remove(session, row);
if(removeIfExists(session, row)) {
// added and deleted in the same transaction: no change
......@@ -87,16 +87,16 @@ public class MultiVersionIndex implements Index {
}
}
public void remove(Session session) throws SQLException {
public synchronized void remove(Session session) throws SQLException {
base.remove(session);
}
public void truncate(Session session) throws SQLException {
public synchronized void truncate(Session session) throws SQLException {
delta.truncate(session);
base.truncate(session);
}
public void commit(int operation, Row row) throws SQLException {
public synchronized void commit(int operation, Row row) throws SQLException {
removeIfExists(null, row);
}
......@@ -153,7 +153,6 @@ public class MultiVersionIndex implements Index {
}
public long getRowCount(Session session) {
// TODO
return base.getRowCount(session);
}
......@@ -169,7 +168,7 @@ public class MultiVersionIndex implements Index {
return base.isNull(newRow);
}
public void removeChildrenAndResources(Session session) throws SQLException {
public synchronized void removeChildrenAndResources(Session session) throws SQLException {
table.removeIndex(this);
remove(session);
}
......
......@@ -5,6 +5,7 @@
package org.h2.index;
import java.sql.SQLException;
import java.util.Iterator;
import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -18,11 +19,15 @@ public class ScanCursor implements Cursor {
private Row row;
private final Session session;
private final boolean multiVersion;
private Iterator deleted;
ScanCursor(Session session, ScanIndex scan, boolean multiVersion) {
this.session = session;
this.scan = scan;
this.multiVersion = multiVersion;
if(multiVersion) {
deleted = scan.getDeleted();
}
row = null;
}
......@@ -45,11 +50,15 @@ public class ScanCursor implements Cursor {
public boolean next() throws SQLException {
if(multiVersion) {
while(true) {
if(deleted.hasNext()) {
row = (Row) deleted.next();
} else {
row = scan.getNextRow(session, row);
}
if(row == null) {
break;
}
if(row.getSessionId() == 0 || row.getSessionId() == session.getId()) {
if(row.getSessionId() == 0 || row.getSessionId() == session.getId() || row.getDeleted()) {
break;
}
}
......
......@@ -5,7 +5,10 @@
package org.h2.index;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.message.Message;
......@@ -32,6 +35,7 @@ public class ScanIndex extends BaseIndex {
private boolean containsLargeObject;
private int rowCountDiff;
private HashMap sessionRowCount;
private HashSet deleted;
public ScanIndex(TableData table, int id, Column[] columns, IndexType indexType)
throws SQLException {
......@@ -122,12 +126,20 @@ public class ScanIndex extends BaseIndex {
rows.set(key, row);
}
}
if(database.isMultiVersion()) {
if(deleted != null) {
deleted.add(row);
}
incrementRowCount(session.getId(), 1);
}
rowCount++;
}
public void commit(int operation, Row row) throws SQLException {
if(database.isMultiVersion()) {
if(deleted != null && operation == UndoLogRecord.DELETE) {
deleted.remove(row);
}
incrementRowCount(row.getSessionId(), operation == UndoLogRecord.DELETE ? 1 : -1);
}
}
......@@ -160,7 +172,13 @@ public class ScanIndex extends BaseIndex {
rows.set(key, free);
firstFree = key;
}
if(database.isMultiVersion()) {
if(deleted == null) {
deleted = new HashSet();
}
deleted.add(row);
incrementRowCount(session.getId(), -1);
}
rowCount--;
}
......@@ -234,4 +252,8 @@ public class ScanIndex extends BaseIndex {
throw Message.getUnsupportedException();
}
public Iterator getDeleted() {
return deleted == null ? Collections.EMPTY_LIST.iterator() : deleted.iterator();
}
}
......@@ -30,6 +30,7 @@ public class ViewIndex extends BaseIndex {
private int[] masks;
private String planSQL;
private Query query;
private Session session;
public ViewIndex(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
super(view, 0, null, null, IndexType.createNonUnique(false));
......@@ -45,11 +46,16 @@ public class ViewIndex extends BaseIndex {
this.originalParameters = index.originalParameters;
this.recursive = index.recursive;
this.masks = masks;
this.session = session;
columns = new Column[0];
query = getQuery(session, masks);
planSQL = query.getPlanSQL();
}
public Session getSession() {
return session;
}
public String getPlanSQL() {
return planSQL;
}
......
......@@ -259,6 +259,12 @@ public class TableData extends Table implements RecordReader {
public void removeRow(Session session, Row row) throws SQLException {
lastModificationId = database.getNextModificationDataId();
if(database.isMultiVersion()) {
if(row.getDeleted()) {
int testingWrongExceptionConcurrentUpdateOrSo;
throw Message.getSQLException(ErrorCode.LOCK_TIMEOUT_1);
}
}
for (int i = indexes.size() - 1; i >= 0; i--) {
Index index = (Index) indexes.get(i);
index.remove(session, row);
......
......@@ -8,6 +8,7 @@ import java.sql.SQLException;
import org.h2.command.Prepared;
import org.h2.command.dml.Query;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Index;
......@@ -16,7 +17,9 @@ import org.h2.index.ViewIndex;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.util.IntArray;
import org.h2.util.ObjectArray;
import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils;
import org.h2.value.Value;
......@@ -29,6 +32,9 @@ public class TableView extends Table {
private ViewIndex index;
private boolean recursive;
private SQLException createException;
private SmallLRUCache indexCache = new SmallLRUCache(Constants.VIEW_INDEX_CACHE_SIZE);
private long lastModificationCheck;
private long maxDataModificationId;
public TableView(Schema schema, int id, String name, String querySQL, ObjectArray params, String[] columnNames, Session session, boolean recursive) throws SQLException {
super(schema, id, name, false);
......@@ -101,7 +107,12 @@ public class TableView extends Table {
public PlanItem getBestPlanItem(Session session, int[] masks) throws SQLException {
PlanItem item = new PlanItem();
item.cost = index.getCost(session, masks);
Index i2 = new ViewIndex(this, index, session, masks);
IntArray masksArray = new IntArray(masks == null ? new int[0] : masks);
ViewIndex i2 = (ViewIndex) indexCache.get(masksArray);
if(i2 == null || i2.getSession() != session) {
i2 = new ViewIndex(this, index, session, masks);
indexCache.put(masksArray, i2);
}
item.setIndex(i2);
return item;
}
......@@ -233,7 +244,14 @@ public class TableView extends Table {
if(viewQuery == null) {
return Long.MAX_VALUE;
}
return viewQuery.getMaxDataModificationId();
// if nothing was modified in the database since the last check, and the last is known, then we don't need to check again
// this speeds up nested views
long dbMod = database.getModificationDataId();
if(dbMod > lastModificationCheck && maxDataModificationId <= dbMod) {
maxDataModificationId = viewQuery.getMaxDataModificationId();
lastModificationCheck = dbMod;
}
return maxDataModificationId;
}
public Index getUniqueIndex() {
......
......@@ -95,39 +95,29 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
test.printSystem();
int testMVCC;
System.setProperty("h2.mvcc", "true");
//int testMVCC;
// System.setProperty("h2.mvcc", "true");
/*
add link to:
http://zvikico.typepad.com/problog/2007/08/h2-database-eng.html
m2-repo
m.i.a.
documented?:
problem: new table created with constraints (so pointing to the same index), then constraint deleted: index is deleted (even though it is used).
Another problem: two constraints using the same index, one constraint is deleted.
Possible solution: never delete indexes? What about unique indexes? Create another index if one exists but it belongs to a constraint?
only admins can use nested tables:
CREATE USER TEST PASSWORD 'TEST';
SELECT * FROM (SELECT * FROM DUAL);
m2-repo
add MVCC
improve documentation of 'mixed mode' usage.
test and document fulltext search
improve documentation of mixed mode
clustered tables (test)
clustered tables: test, document
add to maven
Switching off and switching on constraints could be made transactional.
Add version number. Install directory: h2-1.0, jar file: h2-1.0.jar
search for japanese: works, but is it ok?
......@@ -652,6 +642,7 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
new TestTransaction().runTest(this);
new TestTriggersConstraints().runTest(this);
new TestTwoPhaseCommit().runTest(this);
new TestView().runTest(this);
// server
new TestNestedLoop().runTest(this);
......
......@@ -26,9 +26,9 @@ postgresql.datetime = TIMESTAMP
derby.datetime = TIMESTAMP
oracle.datetime = TIMESTAMP
#test1 = org.h2.test.bench.BenchSimple
#test2 = org.h2.test.bench.BenchA
#test3 = org.h2.test.bench.BenchB
test1 = org.h2.test.bench.BenchC
test1 = org.h2.test.bench.BenchSimple
test2 = org.h2.test.bench.BenchA
test3 = org.h2.test.bench.BenchB
test4 = org.h2.test.bench.BenchC
size = 400
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.h2.test.TestBase;
public class TestView extends TestBase {
public void test() throws Exception {
deleteDb("view");
Connection conn = getConnection("view");
Statement s = conn.createStatement();
s.execute("create table t0(id int primary key)");
s.execute("insert into t0 values(1), (2), (3)");
for(int i=0; i<30; i++) {
s.execute("create view t" + (i+1) + " as select * from t" + i);
s.execute("select * from t" + (i+1));
ResultSet rs = s.executeQuery("select count(*) from t" + (i+1) + " where id=2");
check(rs.next());
check(rs.getInt(1), 1);
}
conn.close();
conn = getConnection("view");
conn.close();
deleteDb("view");
}
}
......@@ -19,6 +19,7 @@ public class TestMVCC {
}
void test() throws Exception {
// TODO Prio 1: exception when deleting / updating the same in two transactions
// TODO Prio 1: document: exclusive table lock still used when altering tables, adding indexes, select ... for update; table level locks are checked
// TODO Prio 1: make unit test work
// TODO Prio 1: free up disk space (for deleted rows and old versions of updated rows) on commit
......@@ -43,6 +44,24 @@ public class TestMVCC {
c1.setAutoCommit(false);
c2.setAutoCommit(false);
s1.execute("CREATE TABLE TEST(ID INT)");
s1.execute("INSERT INTO TEST VALUES(1)");
c1.commit();
// test(s2, "SELECT COUNT(*) FROM TEST", "1");
System.out.println(s1.executeUpdate("DELETE FROM TEST"));
ResultSet rs = s2.executeQuery("SELECT * FROM TEST");
while(rs.next()) {
System.out.println(" " + rs.getString(1));
}
//
test(s2, "SELECT COUNT(*) FROM TEST", "1");
System.out.println(s2.executeUpdate("DELETE FROM TEST"));
test(s1, "SELECT COUNT(*) FROM TEST", "0");
test(s2, "SELECT COUNT(*) FROM TEST", "0");
c1.commit();
c2.commit();
s1.execute("DROP TABLE TEST");
s1.execute("CREATE TABLE TEST(ID INT)");
s1.execute("INSERT INTO TEST VALUES(1)");
c1.commit();
......@@ -98,19 +117,69 @@ public class TestMVCC {
c1.commit();
Random random = new Random(1);
s1.execute("CREATE TABLE TEST(ID INT IDENTITY, NAME VARCHAR)");
Statement s;
Connection c;
for(int i=0; i<1000; i++) {
if(random.nextBoolean()) {
s = s1;
c = c1;
} else {
s = s2;
c = c2;
}
switch(random.nextInt(5)) {
case 0:
s.execute("INSERT INTO TEST(NAME) VALUES('Hello')");
break;
case 1:
s.execute("UPDATE TEST SET NAME=" + i + " WHERE ID=" + random.nextInt(i));
break;
case 2:
s.execute("DELETE FROM TEST WHERE ID=" + random.nextInt(i));
break;
case 3:
c.commit();
break;
case 4:
c.rollback();
break;
}
s1.execute("SELECT * FROM TEST ORDER BY ID");
s2.execute("SELECT * FROM TEST ORDER BY ID");
}
s1.execute("DROP TABLE TEST");
c1.commit();
c2.commit();
random = new Random(1);
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
for(int i=0; i<100; i++) {
switch(random.nextInt(3)) {
for(int i=0; i<1000; i++) {
if(random.nextBoolean()) {
s = s1;
c = c1;
} else {
s = s2;
c = c2;
}
switch(random.nextInt(5)) {
case 0:
s1.execute("INSERT INTO TEST VALUES("+ i + ", 'Hello')");
s.execute("INSERT INTO TEST VALUES("+ i + ", 'Hello')");
break;
case 1:
s1.execute("UPDATE TEST SET NAME=" + i + " WHERE ID=" + random.nextInt(i));
s.execute("UPDATE TEST SET NAME=" + i + " WHERE ID=" + random.nextInt(i));
break;
case 2:
s1.execute("DELETE FROM TEST WHERE ID=" + random.nextInt(i));
s.execute("DELETE FROM TEST WHERE ID=" + random.nextInt(i));
break;
case 3:
c.commit();
break;
case 4:
c.rollback();
break;
}
s1.execute("SELECT * FROM TEST ORDER BY ID");
s2.execute("SELECT * FROM TEST ORDER BY ID");
}
s1.execute("DROP TABLE TEST");
......
......@@ -503,3 +503,4 @@ mathematicians instantiation homepage supporter grained tilde subscribe baseline
finalizer textbase newsfeeds quicksort
prio zvikico incrementally nocheck differently eng admins problog nio though typepad channels rolling
lightweight builder
tunes elephant codewave incorrectly mytunesrss speeds cte honoured httpdocs department whereever dog dept edh oops flower music appends research plant
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论