提交 07240945 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 6b606ae5
...@@ -245,16 +245,23 @@ ...@@ -245,16 +245,23 @@
</jar> </jar>
</target> </target>
<target name="mavenUploadCentral" depends="jar"> <target name="mavenDeployCentral" depends="jar">
<copy tofile="bin/h2-${version.name.maven}.jar" file="bin/h2.jar" /> <copy tofile="bin/h2-${version.name.maven}.jar" file="bin/h2.jar" />
<copy tofile="bin/pom.xml" filtering="true" file="src/installer/pomUpload.xml"> <copy tofile="bin/pom.xml" filtering="true" file="src/installer/pomUpload.xml">
<filterset> <filterset>
<filter token="version" value="${version.name.maven}"/> <filter token="version" value="${version.name.maven}"/>
</filterset> </filterset>
</copy> </copy>
<exec executable="mvn.bat">
<arg value="deploy:deploy-file"/>
<arg value="-Dfile=bin/h2.jar"/>
<arg value="-Durl=file://C:/data/h2database/m2-repo"/>
<arg value="-Dpackaging=jar"/>
<arg value="-DpomFile=bin/pom.xml"/>
</exec>
</target> </target>
<target name="mavenUploadLocal" depends="jar"> <target name="mavenInstallLocal" depends="jar">
<copy tofile="bin/pom.xml" filtering="true" file="src/installer/pom.xml"> <copy tofile="bin/pom.xml" filtering="true" file="src/installer/pom.xml">
<filterset> <filterset>
<filter token="version" value="1.0-SNAPSHOT"/> <filter token="version" value="1.0-SNAPSHOT"/>
...@@ -264,8 +271,6 @@ ...@@ -264,8 +271,6 @@
<arg value="install:install-file"/> <arg value="install:install-file"/>
<arg value="-Dversion=1.0-SNAPSHOT"/> <arg value="-Dversion=1.0-SNAPSHOT"/>
<arg value="-Dfile=bin/h2.jar"/> <arg value="-Dfile=bin/h2.jar"/>
<arg value="-DgroupId=org.h2database"/>
<arg value="-DartifactId=h2"/>
<arg value="-Dpackaging=jar"/> <arg value="-Dpackaging=jar"/>
<arg value="-DpomFile=bin/pom.xml"/> <arg value="-DpomFile=bin/pom.xml"/>
</exec> </exec>
......
...@@ -81,7 +81,7 @@ The version is currently 1.0.&lt;year&gt;&lt;month&gt;&lt;day&gt;. Example: ...@@ -81,7 +81,7 @@ The version is currently 1.0.&lt;year&gt;&lt;month&gt;&lt;day&gt;. Example:
<p> <p>
To build a 'snapshot' H2 .jar file and upload it the to the local Maven 2 repository, execute the following command: To build a 'snapshot' H2 .jar file and upload it the to the local Maven 2 repository, execute the following command:
<pre> <pre>
ant mavenUploadLocal ant mavenInstallLocal
</pre> </pre>
Afterwards, you can include the database in your Maven 2 project as a dependency: Afterwards, you can include the database in your Maven 2 project as a dependency:
<pre> <pre>
......
...@@ -991,7 +991,7 @@ will always enable the trace mode when connecting. ...@@ -991,7 +991,7 @@ will always enable the trace mode when connecting.
If the database files are read-only, then the database is read-only as well. If the database files are read-only, then the database is read-only as well.
It is not possible to create new tables, add or modify data in this database. It is not possible to create new tables, add or modify data in this database.
Only SELECT statements are allowed. Only SELECT statements are allowed.
To create a read-only database, close the database so that the log file is deleted. To create a read-only database, close the database so that the log file gets smaller. Do not delete the log file.
Then, make the database files read-only using the operating system. Then, make the database files read-only using the operating system.
When you open the database now, it is read-only. When you open the database now, it is read-only.
There are two ways an application can find out a database is read-only: There are two ways an application can find out a database is read-only:
......
...@@ -43,6 +43,12 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -43,6 +43,12 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<li>New experimental feature MVCC (multi version concurrency control). <li>New experimental feature MVCC (multi version concurrency control).
Can be set as a option when opening the database (jdbc:h2:test;MVCC=TRUE) Can be set as a option when opening the database (jdbc:h2:test;MVCC=TRUE)
or as a system property (-Dh2.mvcc=true). This is work-in-progress, use it at your own risk. Feedback is welcome. or as a system property (-Dh2.mvcc=true). This is work-in-progress, use it at your own risk. Feedback is welcome.
</li><li>The backup tool (org.h2.tools.Backup) did not work. The restore tool did not work when the -db parameter was used. Fixed.
The documentation of the backup tool has been changed: only one database may be backed up at any time.
</li><li>Opening large read-only databases was very slow. Fixed.
</li><li>New Japanese translation of the error messages thanks to Ikemoto Masahiro. Thanks a lot!
</li><li>Disabling / enabling referential integrity for a table can now be used inside a transaction.
</li><li>Rights checking for dynamic tables (SELECT * FROM (SELECT ...)) did not work. Fixed.
</li><li>Creating more than 10 views that depend on each other was very slow. Reconnecting was slow as well. Fixed. </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 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 </li><li>When altering a table with foreign key constraint, if there was no manual index created
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -150,6 +150,8 @@ ...@@ -150,6 +150,8 @@
90127=The result set is not updatable. The query must select all columns from a unique key. Only one table may be selected. 90127=The result set is not updatable. The query must select all columns from a unique key. Only one table may be selected.
90128=The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY). 90128=The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=Transaction {0} not found 90129=Transaction {0} not found
90130=This method is not allowed for a prepared statement; use a regular statement instead.
90131=Concurrent update in table {0}\: another transaction has updated or deleted the same row
HY000=General error\: {0} HY000=General error\: {0}
HY004=Unknown data type\: {0} HY004=Unknown data type\: {0}
HYC00=Feature not supported HYC00=Feature not supported
......
...@@ -19,10 +19,4 @@ ...@@ -19,10 +19,4 @@
</scm> </scm>
<dependencies> <dependencies>
</dependencies> </dependencies>
<distributionManagement>
<repository>
<id>ftp-repository</id>
<url>ftp://h2database.com/httpdocs/m2-repo</url>
</repository>
</distributionManagement>
</project> </project>
\ No newline at end of file
...@@ -48,6 +48,7 @@ import org.h2.command.ddl.GrantRevoke; ...@@ -48,6 +48,7 @@ import org.h2.command.ddl.GrantRevoke;
import org.h2.command.ddl.PrepareProcedure; import org.h2.command.ddl.PrepareProcedure;
import org.h2.command.ddl.SetComment; import org.h2.command.ddl.SetComment;
import org.h2.command.ddl.TruncateTable; import org.h2.command.ddl.TruncateTable;
import org.h2.command.dml.AlterTableSet;
import org.h2.command.dml.BackupCommand; import org.h2.command.dml.BackupCommand;
import org.h2.command.dml.Call; import org.h2.command.dml.Call;
import org.h2.command.dml.Delete; import org.h2.command.dml.Delete;
...@@ -801,7 +802,9 @@ public class Parser { ...@@ -801,7 +802,9 @@ public class Parser {
s = session; s = session;
} }
String tempViewName = s.getNextTempViewName(); String tempViewName = s.getNextTempViewName();
table = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, s, false); TableView v = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, s, false);
v.setOwner(session.getUser());
table = v;
if(s != database.getSystemSession()) { if(s != database.getSystemSession()) {
table.setOnCommitDrop(true); table.setOnCommitDrop(true);
} }
...@@ -3837,15 +3840,13 @@ public class Parser { ...@@ -3837,15 +3840,13 @@ public class Parser {
read("REFERENTIAL_INTEGRITY"); read("REFERENTIAL_INTEGRITY");
int type; int type;
if(readIf("TRUE")) { if(readIf("TRUE")) {
type = AlterTableAddConstraint.REFERENTIAL_INTEGRITY_TRUE; type = AlterTableSet.REFERENTIAL_INTEGRITY_TRUE;
} else { } else {
read("FALSE"); read("FALSE");
type = AlterTableAddConstraint.REFERENTIAL_INTEGRITY_FALSE; type = AlterTableSet.REFERENTIAL_INTEGRITY_FALSE;
} }
AlterTableAddConstraint command = new AlterTableAddConstraint(session, table.getSchema()); AlterTableSet command = new AlterTableSet(session, table.getSchema(), type);
command.setTableName(table.getName()); command.setTableName(table.getName()); if(readIf("CHECK")) {
command.setType(type);
if(readIf("CHECK")) {
command.setCheckExisting(true); command.setCheckExisting(true);
} else if(readIf("NOCHECK")) { } else if(readIf("NOCHECK")) {
command.setCheckExisting(false); command.setCheckExisting(false);
......
...@@ -33,8 +33,6 @@ import org.h2.util.ObjectArray; ...@@ -33,8 +33,6 @@ import org.h2.util.ObjectArray;
public class AlterTableAddConstraint extends SchemaCommand { public class AlterTableAddConstraint extends SchemaCommand {
public static final int CHECK = 0, UNIQUE = 1, REFERENTIAL = 2, PRIMARY_KEY = 3; public static final int CHECK = 0, UNIQUE = 1, REFERENTIAL = 2, PRIMARY_KEY = 3;
public static final int REFERENTIAL_INTEGRITY_TRUE = 4;
public static final int REFERENTIAL_INTEGRITY_FALSE = 5;
private int type; private int type;
private String constraintName; private String constraintName;
private String tableName; private String tableName;
...@@ -164,12 +162,6 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -164,12 +162,6 @@ public class AlterTableAddConstraint extends SchemaCommand {
ref.setUpdateAction(session, updateAction); ref.setUpdateAction(session, updateAction);
break; break;
} }
case REFERENTIAL_INTEGRITY_TRUE:
table.setCheckForeignKeyConstraints(session, true, checkExisting);
return 0;
case REFERENTIAL_INTEGRITY_FALSE:
table.setCheckForeignKeyConstraints(session, false, false);
return 0;
default: default:
throw Message.getInternalError("type="+type); throw Message.getInternalError("type="+type);
} }
......
/*
* 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.command.dml;
import java.sql.SQLException;
import org.h2.command.ddl.SchemaCommand;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.schema.Schema;
import org.h2.table.Table;
public class AlterTableSet extends SchemaCommand {
private String tableName;
private final int type;
private boolean checkExisting;
public static final int REFERENTIAL_INTEGRITY_TRUE = 4;
public static final int REFERENTIAL_INTEGRITY_FALSE = 5;
public AlterTableSet(Session session, Schema schema, int type) {
super(session, schema);
this.type = type;
}
public void setCheckExisting(boolean b) {
this.checkExisting = b;
}
public boolean isTransactional() {
return true;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public int update() throws SQLException {
Table table = getSchema().getTableOrView(session, tableName);
session.getUser().checkRight(table, Right.ALL);
table.lock(session, true, true);
switch(type) {
case REFERENTIAL_INTEGRITY_TRUE:
table.setCheckForeignKeyConstraints(session, true, checkExisting);
break;
case REFERENTIAL_INTEGRITY_FALSE:
table.setCheckForeignKeyConstraints(session, false, false);
break;
default:
throw Message.getInternalError("type="+type);
}
return 0;
}
public LocalResult queryMeta() {
return null;
}
}
...@@ -322,7 +322,6 @@ public class Select extends Query { ...@@ -322,7 +322,6 @@ public class Select extends Query {
} }
topTableFilter.startQuery(session); topTableFilter.startQuery(session);
topTableFilter.reset(); topTableFilter.reset();
// TODO lock tables of sub queries
topTableFilter.lock(session, isForUpdate, isForUpdate); topTableFilter.lock(session, isForUpdate, isForUpdate);
if(isQuickQuery) { if(isQuickQuery) {
queryQuick(columnCount, result); queryQuick(columnCount, result);
......
...@@ -312,6 +312,7 @@ public class ErrorCode { ...@@ -312,6 +312,7 @@ public class ErrorCode {
public static final int RESULT_SET_NOT_SCROLLABLE = 90128; public static final int RESULT_SET_NOT_SCROLLABLE = 90128;
public static final int TRANSACTION_NOT_FOUND_1 = 90129; public static final int TRANSACTION_NOT_FOUND_1 = 90129;
public static final int METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT = 90130; public static final int METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT = 90130;
public static final int CONCURRENT_UPDATE_1 = 90131;
/** /**
* INTERNAL * INTERNAL
......
...@@ -436,9 +436,7 @@ public class Database implements DataHandler { ...@@ -436,9 +436,7 @@ public class Database implements DataHandler {
log = new LogSystem(this, databaseName, readOnly, accessModeLog); log = new LogSystem(this, databaseName, readOnly, accessModeLog);
openFileData(); openFileData();
openFileIndex(); openFileIndex();
if(!readOnly) { log.recover();
log.recover();
}
fileData.init(); fileData.init();
try { try {
fileIndex.init(); fileIndex.init();
......
...@@ -194,14 +194,16 @@ public class Session implements SessionInterface { ...@@ -194,14 +194,16 @@ public class Session implements SessionInterface {
if(undoLog.size() > 0) { if(undoLog.size() > 0) {
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
ArrayList rows = new ArrayList(); ArrayList rows = new ArrayList();
while (undoLog.size() > 0) { synchronized(database) {
UndoLogRecord entry = undoLog.getAndRemoveLast(); while (undoLog.size() > 0) {
entry.commit(); UndoLogRecord entry = undoLog.getAndRemoveLast();
rows.add(entry.getRow()); entry.commit();
} rows.add(entry.getRow());
for(int i=0; i<rows.size(); i++) { }
Row r = (Row) rows.get(i); for(int i=0; i<rows.size(); i++) {
r.commit(); Row r = (Row) rows.get(i);
r.commit();
}
} }
} }
undoLog.clear(); undoLog.clear();
......
...@@ -13,6 +13,7 @@ import org.h2.security.SHA256; ...@@ -13,6 +13,7 @@ import org.h2.security.SHA256;
import org.h2.table.MetaTable; import org.h2.table.MetaTable;
import org.h2.table.RangeTable; import org.h2.table.RangeTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableView;
import org.h2.util.ByteUtils; import org.h2.util.ByteUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.RandomUtils; import org.h2.util.RandomUtils;
...@@ -78,6 +79,14 @@ public class User extends RightOwner { ...@@ -78,6 +79,14 @@ public class User extends RightOwner {
// everybody has access to the metadata information // everybody has access to the metadata information
return; return;
} }
if(Table.VIEW.equals(table.getTableType())) {
TableView v = (TableView) table;
if(v.getOwner() == this) {
// the owner of a view has access:
// SELECT * FROM (SELECT * FROM ...)
return;
}
}
if(!isRightGrantedRecursive(table, rightMask)) { if(!isRightGrantedRecursive(table, rightMask)) {
throw Message.getSQLException(ErrorCode.NOT_ENOUGH_RIGHTS_FOR_1, table.getSQL()); throw Message.getSQLException(ErrorCode.NOT_ENOUGH_RIGHTS_FOR_1, table.getSQL());
} }
......
...@@ -6,7 +6,6 @@ package org.h2.expression; ...@@ -6,7 +6,6 @@ package org.h2.expression;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import org.h2.command.Parser; import org.h2.command.Parser;
import org.h2.command.dml.Select; import org.h2.command.dml.Select;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
package org.h2.expression; package org.h2.expression;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.Message; import org.h2.message.Message;
......
...@@ -16,22 +16,24 @@ public class MultiVersionCursor implements Cursor { ...@@ -16,22 +16,24 @@ public class MultiVersionCursor implements Cursor {
private final MultiVersionIndex index; private final MultiVersionIndex index;
private final Session session; private final Session session;
private final Cursor baseCursor, deltaCursor; private final Cursor baseCursor, deltaCursor;
private final Object sync;
private SearchRow baseRow; private SearchRow baseRow;
private Row deltaRow; private Row deltaRow;
private boolean onBase; private boolean onBase;
private boolean end; private boolean end;
private boolean needNewDelta, needNewBase; private boolean needNewDelta, needNewBase;
MultiVersionCursor(Session session, MultiVersionIndex index, Cursor base, Cursor delta) throws SQLException { MultiVersionCursor(Session session, MultiVersionIndex index, Cursor base, Cursor delta, Object sync) throws SQLException {
this.session = session; this.session = session;
this.index = index; this.index = index;
this.baseCursor = base; this.baseCursor = base;
this.deltaCursor = delta; this.deltaCursor = delta;
this.sync = sync;
needNewDelta = needNewBase = true; needNewDelta = needNewBase = true;
} }
private void loadNext(boolean base) throws SQLException { private void loadNext(boolean base) throws SQLException {
synchronized(index) { synchronized(sync) {
if(base) { if(base) {
if(baseCursor.next()) { if(baseCursor.next()) {
baseRow = baseCursor.getSearchRow(); baseRow = baseCursor.getSearchRow();
...@@ -49,7 +51,7 @@ public class MultiVersionCursor implements Cursor { ...@@ -49,7 +51,7 @@ public class MultiVersionCursor implements Cursor {
} }
public Row get() throws SQLException { public Row get() throws SQLException {
synchronized(index) { synchronized(sync) {
if(SysProperties.CHECK && end) { if(SysProperties.CHECK && end) {
throw Message.getInternalError(); throw Message.getInternalError();
} }
...@@ -58,7 +60,7 @@ public class MultiVersionCursor implements Cursor { ...@@ -58,7 +60,7 @@ public class MultiVersionCursor implements Cursor {
} }
public int getPos() { public int getPos() {
synchronized(index) { synchronized(sync) {
if(SysProperties.CHECK && end) { if(SysProperties.CHECK && end) {
throw Message.getInternalError(); throw Message.getInternalError();
} }
...@@ -67,7 +69,7 @@ public class MultiVersionCursor implements Cursor { ...@@ -67,7 +69,7 @@ public class MultiVersionCursor implements Cursor {
} }
public SearchRow getSearchRow() throws SQLException { public SearchRow getSearchRow() throws SQLException {
synchronized(index) { synchronized(sync) {
if(SysProperties.CHECK && end) { if(SysProperties.CHECK && end) {
throw Message.getInternalError(); throw Message.getInternalError();
} }
...@@ -76,7 +78,7 @@ public class MultiVersionCursor implements Cursor { ...@@ -76,7 +78,7 @@ public class MultiVersionCursor implements Cursor {
} }
public boolean next() throws SQLException { public boolean next() throws SQLException {
synchronized(index) { synchronized(sync) {
if(SysProperties.CHECK && end) { if(SysProperties.CHECK && end) {
throw Message.getInternalError(); throw Message.getInternalError();
} }
...@@ -99,7 +101,12 @@ public class MultiVersionCursor implements Cursor { ...@@ -99,7 +101,12 @@ public class MultiVersionCursor implements Cursor {
return true; return true;
} }
} }
boolean isThisSession = deltaRow.getSessionId() == session.getId(); int sessionId = deltaRow.getSessionId();
if(sessionId == 0) {
int testing;
System.out.println("sessionId==0");
}
boolean isThisSession = sessionId == session.getId();
boolean isDeleted = deltaRow.getDeleted(); boolean isDeleted = deltaRow.getDeleted();
if(isThisSession && isDeleted) { if(isThisSession && isDeleted) {
needNewDelta = true; needNewDelta = true;
......
...@@ -21,39 +21,47 @@ public class MultiVersionIndex implements Index { ...@@ -21,39 +21,47 @@ public class MultiVersionIndex implements Index {
private final Index base; private final Index base;
private final TreeIndex delta; private final TreeIndex delta;
private final TableData table; private final TableData table;
private final Object sync;
public MultiVersionIndex(Index base, TableData table) throws SQLException { public MultiVersionIndex(Index base, TableData table) throws SQLException {
this.base = base; this.base = base;
this.table = table; this.table = table;
IndexType deltaIndexType = IndexType.createNonUnique(false); IndexType deltaIndexType = IndexType.createNonUnique(false);
this.delta = new TreeIndex(table, -1, "DELTA" ,base.getColumns(), deltaIndexType); this.delta = new TreeIndex(table, -1, "DELTA" ,base.getColumns(), deltaIndexType);
this.sync = base.getDatabase();
} }
public synchronized void add(Session session, Row row) throws SQLException { public void add(Session session, Row row) throws SQLException {
base.add(session, row); synchronized(sync) {
// for example rolling back an delete operation base.add(session, row);
removeIfExists(session, row); // for example rolling back an delete operation
if(row.getSessionId() != 0) { removeIfExists(session, row);
// don't insert rows that are added when creating an index if(row.getSessionId() != 0) {
delta.add(session, row); // don't insert rows that are added when creating an index
delta.add(session, row);
}
} }
} }
public synchronized void close(Session session) throws SQLException { public void close(Session session) throws SQLException {
base.close(session); synchronized(sync) {
base.close(session);
}
} }
public synchronized Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException { public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
Cursor baseCursor = base.find(session, first, last); synchronized(sync) {
Cursor deltaCursor = delta.find(session, first, last); Cursor baseCursor = base.find(session, first, last);
return new MultiVersionCursor(session, this, baseCursor, deltaCursor); Cursor deltaCursor = delta.find(session, first, last);
return new MultiVersionCursor(session, this, baseCursor, deltaCursor, sync);
}
} }
public boolean canGetFirstOrLast(boolean first) { public boolean canGetFirstOrLast(boolean first) {
return false; return false;
} }
public synchronized SearchRow findFirstOrLast(Session session, boolean first) throws SQLException { public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException(); throw Message.getUnsupportedException();
} }
...@@ -65,7 +73,7 @@ public class MultiVersionIndex implements Index { ...@@ -65,7 +73,7 @@ public class MultiVersionIndex implements Index {
return base.needRebuild(); return base.needRebuild();
} }
private synchronized boolean removeIfExists(Session session, Row row) throws SQLException { private boolean removeIfExists(Session session, Row row) throws SQLException {
// maybe it was inserted by the same session just before // maybe it was inserted by the same session just before
Cursor c = delta.find(session, row, row); Cursor c = delta.find(session, row, row);
while(c.next()) { while(c.next()) {
...@@ -78,26 +86,42 @@ public class MultiVersionIndex implements Index { ...@@ -78,26 +86,42 @@ public class MultiVersionIndex implements Index {
return false; return false;
} }
public synchronized void remove(Session session, Row row) throws SQLException { public void remove(Session session, Row row) throws SQLException {
base.remove(session, row); synchronized(sync) {
if(removeIfExists(session, row)) { base.remove(session, row);
// added and deleted in the same transaction: no change if(removeIfExists(session, row)) {
} else { // added and deleted in the same transaction: no change
delta.add(session, row); } else {
int testing;
if(row.getSessionId() == 0) {
System.out.println("stop! " + row);
System.out.flush();
new Error().printStackTrace();
Runtime.getRuntime().halt(1);
}
delta.add(session, row);
}
} }
} }
public synchronized void remove(Session session) throws SQLException { public void remove(Session session) throws SQLException {
base.remove(session); synchronized(sync) {
base.remove(session);
}
} }
public synchronized void truncate(Session session) throws SQLException { public void truncate(Session session) throws SQLException {
delta.truncate(session); synchronized(sync) {
base.truncate(session); delta.truncate(session);
base.truncate(session);
}
} }
public synchronized void commit(int operation, Row row) throws SQLException { public void commit(int operation, Row row) throws SQLException {
removeIfExists(null, row); synchronized(sync) {
removeIfExists(null, row);
}
} }
public int compareKeys(SearchRow rowData, SearchRow compare) { public int compareKeys(SearchRow rowData, SearchRow compare) {
...@@ -168,9 +192,11 @@ public class MultiVersionIndex implements Index { ...@@ -168,9 +192,11 @@ public class MultiVersionIndex implements Index {
return base.isNull(newRow); return base.isNull(newRow);
} }
public synchronized void removeChildrenAndResources(Session session) throws SQLException { public void removeChildrenAndResources(Session session) throws SQLException {
table.removeIndex(this); synchronized(sync) {
remove(session); table.removeIndex(this);
remove(session);
}
} }
public String getSQL() { public String getSQL() {
......
...@@ -52,6 +52,10 @@ public class ScanCursor implements Cursor { ...@@ -52,6 +52,10 @@ public class ScanCursor implements Cursor {
while(true) { while(true) {
if(deleted.hasNext()) { if(deleted.hasNext()) {
row = (Row) deleted.next(); row = (Row) deleted.next();
if(row.getDeleted() && row.getSessionId() == session.getId()) {
row = null;
continue;
}
} else { } else {
row = scan.getNextRow(session, row); row = scan.getNextRow(session, row);
} }
......
...@@ -128,7 +128,7 @@ public class ScanIndex extends BaseIndex { ...@@ -128,7 +128,7 @@ public class ScanIndex extends BaseIndex {
} }
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
if(deleted != null) { if(deleted != null) {
deleted.add(row); deleted.remove(row);
} }
incrementRowCount(session.getId(), 1); incrementRowCount(session.getId(), 1);
} }
......
...@@ -58,7 +58,7 @@ implements ParameterMetaData ...@@ -58,7 +58,7 @@ implements ParameterMetaData
/** /**
* Returns the parameter type. * Returns the parameter type.
* Always returns Types.VARCHAR everything can be passed as a VARCHAR. * Always returns Types.VARCHAR as everything can be passed as a VARCHAR.
* *
* @return Types.VARCHAR * @return Types.VARCHAR
*/ */
......
...@@ -151,6 +151,7 @@ ...@@ -151,6 +151,7 @@
90128=Kann nicht an den Anfang der Resultat-Zeilen springen. M\u00F6gliche L\u00F6sung\: conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY). 90128=Kann nicht an den Anfang der Resultat-Zeilen springen. M\u00F6gliche L\u00F6sung\: conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=Transaktion {0} nicht gefunden 90129=Transaktion {0} nicht gefunden
90130=\#This method is not allowed for a prepared statement; use a regular statement instead. 90130=\#This method is not allowed for a prepared statement; use a regular statement instead.
90131=\#Concurrent update in table {0}\: another transaction has updated or deleted the same row
HY000=Allgemeiner Fehler\: {0} HY000=Allgemeiner Fehler\: {0}
HY004=Unbekannter Datentyp\: {0} HY004=Unbekannter Datentyp\: {0}
HYC00=Dieses Feature wird unterst\u00FCtzt HYC00=Dieses Feature wird unterst\u00FCtzt
......
...@@ -151,6 +151,7 @@ ...@@ -151,6 +151,7 @@
90128=The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY). 90128=The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=Transaction {0} not found 90129=Transaction {0} not found
90130=This method is not allowed for a prepared statement; use a regular statement instead. 90130=This method is not allowed for a prepared statement; use a regular statement instead.
90131=Concurrent update in table {0}\: another transaction has updated or deleted the same row
HY000=General error\: {0} HY000=General error\: {0}
HY004=Unknown data type\: {0} HY004=Unknown data type\: {0}
HYC00=Feature not supported HYC00=Feature not supported
......
差异被折叠。
...@@ -151,6 +151,7 @@ ...@@ -151,6 +151,7 @@
90128=\#The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY). 90128=\#The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=\#Transaction {0} not found 90129=\#Transaction {0} not found
90130=\#This method is not allowed for a prepared statement; use a regular statement instead. 90130=\#This method is not allowed for a prepared statement; use a regular statement instead.
90131=\#Concurrent update in table {0}\: another transaction has updated or deleted the same row
HY000=Blad ogolny\: {0} HY000=Blad ogolny\: {0}
HY004=Nieznany typ danyche\: {0} HY004=Nieznany typ danyche\: {0}
HYC00=Cecha nie jest wspierana HYC00=Cecha nie jest wspierana
......
...@@ -248,6 +248,7 @@ ALTER TABLE TEST DROP CONSTRAINT UNIQUE_NAME ...@@ -248,6 +248,7 @@ ALTER TABLE TEST DROP CONSTRAINT UNIQUE_NAME
ALTER TABLE tableName SET REFERENTIAL_INTEGRITY {FALSE | TRUE [CHECK|NOCHECK]} ALTER TABLE tableName SET REFERENTIAL_INTEGRITY {FALSE | TRUE [CHECK|NOCHECK]}
"," ","
Disables or enables referential integrity checking for a table. Disables or enables referential integrity checking for a table.
This command can be used inside a transaction.
Enabling it does not check existing data, except if CHECK is specified. Enabling it does not check existing data, except if CHECK is specified.
Use SET REFERENTIAL_INTEGRITY to disable it for all tables (the global flag and the flag for each table are independent). Use SET REFERENTIAL_INTEGRITY to disable it for all tables (the global flag and the flag for each table are independent).
"," ","
......
...@@ -66,22 +66,22 @@ public class Row extends Record implements SearchRow { ...@@ -66,22 +66,22 @@ public class Row extends Record implements SearchRow {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4; return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4;
} }
public String toString() { // public String toString() {
int testing; // int testing;
StringBuffer buff = new StringBuffer(data.length*5); // StringBuffer buff = new StringBuffer(data.length*5);
buff.append('('); // buff.append('(');
for(int i=0; i<data.length; i++) { // for(int i=0; i<data.length; i++) {
if(i>0) { // if(i>0) {
buff.append(", "); // buff.append(", ");
} // }
buff.append(data[i].getSQL()); // buff.append(data[i].getSQL());
} // }
buff.append(')'); // buff.append(')');
buff.append(" /* pos: " + getPos() + "*/ "); // buff.append(" /* pos: " + getPos() + "*/ ");
if(getDeleted()) { // if(getDeleted()) {
buff.append(" /* deleted /*"); // buff.append(" /* deleted /*");
} // }
return buff.toString(); // return buff.toString();
} // }
} }
...@@ -66,7 +66,9 @@ public class LogFile { ...@@ -66,7 +66,9 @@ public class LogFile {
unwritten = new ObjectArray(); unwritten = new ObjectArray();
try { try {
readHeader(); readHeader();
writeHeader(); if(!log.getDatabase().getReadOnly()) {
writeHeader();
}
pos = getBlock(); pos = getBlock();
firstUncommittedPos = pos; firstUncommittedPos = pos;
} catch(SQLException e) { } catch(SQLException e) {
...@@ -173,7 +175,7 @@ public class LogFile { ...@@ -173,7 +175,7 @@ public class LogFile {
return s; return s;
} }
private boolean redoOrUndo(boolean undo) throws SQLException { private boolean redoOrUndo(boolean undo, boolean readOnly) throws SQLException {
int pos = getBlock(); int pos = getBlock();
DataPage in = readPage(); DataPage in = readPage();
int blocks = in.readInt(); int blocks = in.readInt();
...@@ -209,6 +211,9 @@ public class LogFile { ...@@ -209,6 +211,9 @@ public class LogFile {
throw Message.getInternalError("can't undo summary"); throw Message.getInternalError("can't undo summary");
} }
} }
if(readOnly && type != 'S') {
return true;
}
if(undo) { if(undo) {
if(logSystem.isSessionCommitted(sessionId, id, pos)) { if(logSystem.isSessionCommitted(sessionId, id, pos)) {
logSystem.removeSession(sessionId); logSystem.removeSession(sessionId);
...@@ -216,7 +221,9 @@ public class LogFile { ...@@ -216,7 +221,9 @@ public class LogFile {
} }
} else { } else {
if(type != 'S') { if(type != 'S') {
logSystem.addUndoLogRecord(this, pos, sessionId); if(!readOnly) {
logSystem.addUndoLogRecord(this, pos, sessionId);
}
} }
} }
int storageId = in.readInt(); int storageId = in.readInt();
...@@ -286,6 +293,7 @@ public class LogFile { ...@@ -286,6 +293,7 @@ public class LogFile {
} }
public void redoAllGoEnd() throws SQLException { public void redoAllGoEnd() throws SQLException {
boolean readOnly = logSystem.getDatabase().getReadOnly();
long length = file.length(); long length = file.length();
if(length<=FileStore.HEADER_LENGTH) { if(length<=FileStore.HEADER_LENGTH) {
return; return;
...@@ -298,7 +306,7 @@ public class LogFile { ...@@ -298,7 +306,7 @@ public class LogFile {
if((long)pos * BLOCK_SIZE >= length) { if((long)pos * BLOCK_SIZE >= length) {
break; break;
} }
boolean more = redoOrUndo(false); boolean more = redoOrUndo(false, readOnly);
if(!more) { if(!more) {
break; break;
} }
...@@ -326,7 +334,7 @@ public class LogFile { ...@@ -326,7 +334,7 @@ public class LogFile {
void undo(int pos) throws SQLException { void undo(int pos) throws SQLException {
go(pos); go(pos);
redoOrUndo(true); redoOrUndo(true, false);
} }
void flush() throws SQLException { void flush() throws SQLException {
......
...@@ -47,7 +47,7 @@ public class LogSystem { ...@@ -47,7 +47,7 @@ public class LogSystem {
this.database = database; this.database = database;
this.readOnly = readOnly; this.readOnly = readOnly;
this.accessMode = accessMode; this.accessMode = accessMode;
if (database == null || readOnly) { if (database == null) {
return; return;
} }
this.fileNamePrefix = fileNamePrefix; this.fileNamePrefix = fileNamePrefix;
...@@ -107,21 +107,31 @@ public class LogSystem { ...@@ -107,21 +107,31 @@ public class LogSystem {
for (int i = 0; i < activeLogs.size(); i++) { for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i); LogFile l = (LogFile) activeLogs.get(i);
if (l.getFirstUncommittedPos() == LOG_WRITTEN) { if (l.getFirstUncommittedPos() == LOG_WRITTEN) {
closeOldFile(l); // must remove the log file first
// if we don't do that, the file is closed but still in the list
activeLogs.remove(i); activeLogs.remove(i);
i--; i--;
closeOldFile(l);
} }
} }
} }
public void close() throws SQLException { public void close() throws SQLException {
if(database == null || readOnly) { if(database == null) {
return; return;
} }
synchronized (database) { synchronized (database) {
if(closed) { if(closed) {
return; return;
} }
if(readOnly) {
for (int i = 0; i < activeLogs.size(); i++) {
LogFile l = (LogFile) activeLogs.get(i);
l.close(false);
}
closed = true;
return;
}
// TODO refactor flushing and closing files when we know what to do exactly // TODO refactor flushing and closing files when we know what to do exactly
SQLException closeException = null; SQLException closeException = null;
try { try {
...@@ -202,7 +212,7 @@ public class LogSystem { ...@@ -202,7 +212,7 @@ public class LogSystem {
boolean fileChanged = undo.size() > 0; boolean fileChanged = undo.size() > 0;
undo = null; undo = null;
storages.clear(); storages.clear();
if (fileChanged && !containsInDoubtTransactions()) { if (!readOnly && fileChanged && !containsInDoubtTransactions()) {
checkpoint(); checkpoint();
} }
return fileChanged; return fileChanged;
......
...@@ -5,9 +5,6 @@ ...@@ -5,9 +5,6 @@
package org.h2.store; package org.h2.store;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Session;
import org.h2.util.CacheObject; import org.h2.util.CacheObject;
/** /**
...@@ -37,13 +34,15 @@ public abstract class Record extends CacheObject { ...@@ -37,13 +34,15 @@ public abstract class Record extends CacheObject {
return false; return false;
} }
public void setDeleted(Session session, boolean deleted) { public void setDeleted(boolean deleted) {
this.sessionId = session.getId();
this.deleted = deleted; this.deleted = deleted;
} }
public void setSessionId(int sessionId) {
this.sessionId = sessionId;
}
public int getSessionId() { public int getSessionId() {
int testing;
return sessionId; return sessionId;
} }
......
...@@ -110,7 +110,7 @@ public class Storage { ...@@ -110,7 +110,7 @@ public class Storage {
} }
public void updateRecord(Session session, Record record) throws SQLException { public void updateRecord(Session session, Record record) throws SQLException {
record.setDeleted(session, false); record.setDeleted(false);
file.updateRecord(session, record); file.updateRecord(session, record);
} }
...@@ -118,7 +118,7 @@ public class Storage { ...@@ -118,7 +118,7 @@ public class Storage {
record.setStorageId(id); record.setStorageId(id);
int size = file.getRecordOverhead() + record.getByteCount(dummy); int size = file.getRecordOverhead() + record.getByteCount(dummy);
size = MathUtils.roundUp(size, DiskFile.BLOCK_SIZE); size = MathUtils.roundUp(size, DiskFile.BLOCK_SIZE);
record.setDeleted(session, false); record.setDeleted(false);
int blockCount = size / DiskFile.BLOCK_SIZE; int blockCount = size / DiskFile.BLOCK_SIZE;
if(pos == ALLOCATE_POS) { if(pos == ALLOCATE_POS) {
pos = allocate(blockCount); pos = allocate(blockCount);
...@@ -137,7 +137,7 @@ public class Storage { ...@@ -137,7 +137,7 @@ public class Storage {
if(SysProperties.CHECK && record.getDeleted()) { if(SysProperties.CHECK && record.getDeleted()) {
throw Message.getInternalError("duplicate delete " + pos); throw Message.getInternalError("duplicate delete " + pos);
} }
record.setDeleted(session, true); record.setDeleted(true);
int blockCount = record.getBlockCount(); int blockCount = record.getBlockCount();
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
int todoMustFreeSpaceOnCommit; int todoMustFreeSpaceOnCommit;
......
...@@ -78,6 +78,9 @@ public class TableData extends Table implements RecordReader { ...@@ -78,6 +78,9 @@ public class TableData extends Table implements RecordReader {
public void addRow(Session session, Row row) throws SQLException { public void addRow(Session session, Row row) throws SQLException {
int i = 0; int i = 0;
lastModificationId = database.getNextModificationDataId(); lastModificationId = database.getNextModificationDataId();
if(database.isMultiVersion()) {
row.setSessionId(session.getId());
}
try { try {
for (; i < indexes.size(); i++) { for (; i < indexes.size(); i++) {
Index index = (Index) indexes.get(i); Index index = (Index) indexes.get(i);
...@@ -261,8 +264,14 @@ public class TableData extends Table implements RecordReader { ...@@ -261,8 +264,14 @@ public class TableData extends Table implements RecordReader {
lastModificationId = database.getNextModificationDataId(); lastModificationId = database.getNextModificationDataId();
if(database.isMultiVersion()) { if(database.isMultiVersion()) {
if(row.getDeleted()) { if(row.getDeleted()) {
int testingWrongExceptionConcurrentUpdateOrSo; throw Message.getSQLException(ErrorCode.CONCURRENT_UPDATE_1, getName());
throw Message.getSQLException(ErrorCode.LOCK_TIMEOUT_1); }
int old = row.getSessionId();
int newId = session.getId();
if(old == 0) {
row.setSessionId(newId);
} else if(old != newId) {
throw Message.getSQLException(ErrorCode.CONCURRENT_UPDATE_1, getName());
} }
} }
for (int i = indexes.size() - 1; i >= 0; i--) { for (int i = indexes.size() - 1; i >= 0; i--) {
......
...@@ -55,14 +55,15 @@ public class TableFilter implements ColumnResolver { ...@@ -55,14 +55,15 @@ public class TableFilter implements ColumnResolver {
private boolean outerJoin; private boolean outerJoin;
private boolean foundOne; private boolean foundOne;
private Expression fullCondition; private Expression fullCondition;
private final boolean rightsChecked;
public TableFilter(Session session, Table table, String alias, boolean rightsChecked, Select select) { public TableFilter(Session session, Table table, String alias, boolean rightsChecked, Select select) throws SQLException {
this.session = session; this.session = session;
this.table = table; this.table = table;
this.alias = alias; this.alias = alias;
this.rightsChecked = rightsChecked;
this.select = select; this.select = select;
if(!rightsChecked) {
session.getUser().checkRight(table, Right.SELECT);
}
} }
public Select getSelect() { public Select getSelect() {
...@@ -74,9 +75,6 @@ public class TableFilter implements ColumnResolver { ...@@ -74,9 +75,6 @@ public class TableFilter implements ColumnResolver {
} }
public void lock(Session session, boolean exclusive, boolean force) throws SQLException { public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
if(!rightsChecked) {
session.getUser().checkRight(table, Right.SELECT);
}
table.lock(session, exclusive, force); table.lock(session, exclusive, force);
for(int i=0; joins != null && i<joins.size(); i++) { for(int i=0; joins != null && i<joins.size(); i++) {
getTableFilter(i).lock(session, exclusive, force); getTableFilter(i).lock(session, exclusive, force);
......
...@@ -10,6 +10,7 @@ import org.h2.command.dml.Query; ...@@ -10,6 +10,7 @@ import org.h2.command.dml.Query;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.index.IndexType; import org.h2.index.IndexType;
...@@ -35,6 +36,7 @@ public class TableView extends Table { ...@@ -35,6 +36,7 @@ public class TableView extends Table {
private SmallLRUCache indexCache = new SmallLRUCache(Constants.VIEW_INDEX_CACHE_SIZE); private SmallLRUCache indexCache = new SmallLRUCache(Constants.VIEW_INDEX_CACHE_SIZE);
private long lastModificationCheck; private long lastModificationCheck;
private long maxDataModificationId; private long maxDataModificationId;
private User owner;
public TableView(Schema schema, int id, String name, String querySQL, ObjectArray params, String[] columnNames, Session session, boolean recursive) throws SQLException { 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); super(schema, id, name, false);
...@@ -44,7 +46,7 @@ public class TableView extends Table { ...@@ -44,7 +46,7 @@ public class TableView extends Table {
index = new ViewIndex(this, querySQL, params, recursive); index = new ViewIndex(this, querySQL, params, recursive);
initColumnsAndTables(session); initColumnsAndTables(session);
} }
private void initColumnsAndTables(Session session) throws SQLException { private void initColumnsAndTables(Session session) throws SQLException {
Column[] cols; Column[] cols;
removeViewFromTables(); removeViewFromTables();
...@@ -274,5 +276,13 @@ public class TableView extends Table { ...@@ -274,5 +276,13 @@ public class TableView extends Table {
t.addView(this); t.addView(this);
} }
} }
public void setOwner(User owner) {
this.owner = owner;
}
public User getOwner() {
return owner;
}
} }
...@@ -38,7 +38,7 @@ public class Backup { ...@@ -38,7 +38,7 @@ public class Backup {
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-file filename (the default is backup.zip) * </li><li>-file filename (the default is backup.zip)
* </li><li>-dir database directory (the default is the current directory) * </li><li>-dir database directory (the default is the current directory)
* </li><li>-db database name (all databases if no name is specified) * </li><li>-db database name (not required if there is only one database)
* </li><li>-quiet does not print progress information * </li><li>-quiet does not print progress information
* </li></ul> * </li></ul>
* *
...@@ -76,7 +76,7 @@ public class Backup { ...@@ -76,7 +76,7 @@ public class Backup {
* *
* @param zipFileName the name of the backup file * @param zipFileName the name of the backup file
* @param directory the directory name * @param directory the directory name
* @param db the database name (null for all databases) * @param db the database name (null if there is only one database)
* @param quiet don't print progress information * @param quiet don't print progress information
* @throws SQLException * @throws SQLException
*/ */
...@@ -101,7 +101,6 @@ public class Backup { ...@@ -101,7 +101,6 @@ public class Backup {
String fileName = (String) list.get(i); String fileName = (String) list.get(i);
if(fileName.endsWith(Constants.SUFFIX_DATA_FILE)) { if(fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
base = FileUtils.getParent(fileName); base = FileUtils.getParent(fileName);
base = FileUtils.getAbsolutePath(fileName);
} }
} }
for(int i=0; i<list.size(); i++) { for(int i=0; i<list.size(); i++) {
......
...@@ -130,6 +130,9 @@ public class Restore { ...@@ -130,6 +130,9 @@ public class Restore {
if(originalDbName == null) { if(originalDbName == null) {
throw new IOException("No database named " + db + " found"); throw new IOException("No database named " + db + " found");
} }
if(originalDbName.startsWith(File.separator)) {
originalDbName = originalDbName.substring(1);
}
} }
in = FileUtils.openFileInputStream(zipFileName); in = FileUtils.openFileInputStream(zipFileName);
ZipInputStream zipIn = new ZipInputStream(in); ZipInputStream zipIn = new ZipInputStream(in);
......
...@@ -244,9 +244,6 @@ public class FileUtils { ...@@ -244,9 +244,6 @@ public class FileUtils {
if(file.exists()) { if(file.exists()) {
for(int i=0; i<SysProperties.MAX_FILE_RETRY; i++) { for(int i=0; i<SysProperties.MAX_FILE_RETRY; i++) {
trace("delete", fileName, null); trace("delete", fileName, null);
if(fileName.indexOf("1459.146") >= 0) {
new Error(fileName).printStackTrace();
}
boolean ok = file.delete(); boolean ok = file.delete();
if(ok) { if(ok) {
return; return;
......
...@@ -100,11 +100,27 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2 ...@@ -100,11 +100,27 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/* /*
only admins can use nested tables: CREATE TABLE TEST(BIRTH TIMESTAMP);
CREATE USER TEST PASSWORD 'TEST'; INSERT INTO TEST VALUES('2006-04-03 10:20:30'), ('2006-04-03 10:20:31');
SELECT * FROM (SELECT * FROM DUAL); SELECT CAST(BIRTH AS DATE) B
FROM TEST GROUP BY CAST(BIRTH AS DATE)
HAVING CAST(BIRTH AS DATE) = '2004-05-05';
SELECT 1 FROM (SELECT CAST(BIRTH AS DATE) B
FROM TEST GROUP BY CAST(BIRTH AS DATE)) A
WHERE A.B = '2004-05-05';
DROP TABLE TEST;
CREATE TABLE TEST (ID integer NOT NULL PRIMARY KEY);
@LOOP 1000 INSERT INTO TEST VALUES(?);
CREATE VIEW TESTVIEW AS SELECT src.ID as VID FROM TEST AS h
INNER JOIN TEST AS src ON h.ID = src.ID GROUP BY src.ID;
-- slow
SELECT COUNT(*) FROM TESTVIEW AS S LEFT JOIN TESTVIEW AS T ON S.VID = T.VID;
DROP VIEW TESTVIEW;
DROP TABLE TEST;
m2-repo add to maven
http://maven.apache.org/guides/mini/guide-central-repository-upload.html
add MVCC add MVCC
...@@ -114,8 +130,6 @@ test and document fulltext search ...@@ -114,8 +130,6 @@ test and document fulltext search
clustered tables: test, document clustered tables: test, document
add to maven
Switching off and switching on constraints could be made transactional. Switching off and switching on constraints could be made transactional.
Add version number. Install directory: h2-1.0, jar file: h2-1.0.jar Add version number. Install directory: h2-1.0, jar file: h2-1.0.jar
......
...@@ -26,20 +26,32 @@ public class TestRights extends TestBase { ...@@ -26,20 +26,32 @@ public class TestRights extends TestBase {
// rights on tables and views // rights on tables and views
executeSuccess("CREATE USER PASS_READER PASSWORD 'abc'"); executeSuccess("CREATE USER PASS_READER PASSWORD 'abc'");
executeSuccess("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
executeSuccess("CREATE TABLE PASS(ID INT PRIMARY KEY, NAME VARCHAR, PASSWORD VARCHAR)"); executeSuccess("CREATE TABLE PASS(ID INT PRIMARY KEY, NAME VARCHAR, PASSWORD VARCHAR)");
executeSuccess("CREATE VIEW PASS_NAME AS SELECT ID, NAME FROM PASS"); executeSuccess("CREATE VIEW PASS_NAME AS SELECT ID, NAME FROM PASS");
executeSuccess("GRANT SELECT ON PASS_NAME TO PASS_READER"); executeSuccess("GRANT SELECT ON PASS_NAME TO PASS_READER");
executeSuccess("GRANT SELECT, INSERT, UPDATE ON TEST TO PASS_READER");
conn.close(); conn.close();
conn = getConnection("rights", "PASS_READER", "abc"); conn = getConnection("rights", "PASS_READER", "abc");
stat = conn.createStatement(); stat = conn.createStatement();
executeSuccess("SELECT * FROM PASS_NAME"); executeSuccess("SELECT * FROM PASS_NAME");
executeSuccess("SELECT * FROM (SELECT * FROM PASS_NAME)");
executeSuccess("SELECT (SELECT NAME FROM PASS_NAME) P FROM PASS_NAME");
executeError("SELECT (SELECT PASSWORD FROM PASS) P FROM PASS_NAME");
executeError("SELECT * FROM PASS"); executeError("SELECT * FROM PASS");
executeError("INSERT INTO TEST SELECT 1, PASSWORD FROM PASS");
executeError("INSERT INTO TEST VALUES(SELECT PASSWORD FROM PASS)");
executeError("UPDATE TEST SET NAME=(SELECT PASSWORD FROM PASS)");
executeError("DELETE FROM TEST WHERE NAME=(SELECT PASSWORD FROM PASS)");
executeError("SELECT * FROM (SELECT * FROM PASS)");
executeError("CREATE VIEW X AS SELECT * FROM PASS_READER");
conn.close(); conn.close();
conn = getConnection("rights"); conn = getConnection("rights");
stat = conn.createStatement(); stat = conn.createStatement();
executeSuccess("DROP TABLE TEST");
executeSuccess("CREATE USER TEST PASSWORD 'abc'"); executeSuccess("CREATE USER TEST PASSWORD 'abc'");
executeSuccess("ALTER USER TEST ADMIN TRUE"); executeSuccess("ALTER USER TEST ADMIN TRUE");
executeSuccess("CREATE TABLE TEST(ID INT)"); executeSuccess("CREATE TABLE TEST(ID INT)");
...@@ -103,6 +115,7 @@ public class TestRights extends TestBase { ...@@ -103,6 +115,7 @@ public class TestRights extends TestBase {
executeError("DELETE FROM ROLE_TABLE"); executeError("DELETE FROM ROLE_TABLE");
executeError("SELECT * FROM HIDDEN"); executeError("SELECT * FROM HIDDEN");
executeError("UPDATE TEST SET ID=0"); executeError("UPDATE TEST SET ID=0");
executeError("CALL SELECT MIN(PASSWORD) FROM PASS");
executeSuccess("SELECT * FROM SUB_TABLE"); executeSuccess("SELECT * FROM SUB_TABLE");
executeSuccess("INSERT INTO SUB_TABLE VALUES(1)"); executeSuccess("INSERT INTO SUB_TABLE VALUES(1)");
executeError("DELETE FROM SUB_TABLE"); executeError("DELETE FROM SUB_TABLE");
......
...@@ -19,9 +19,8 @@ public class TestMVCC { ...@@ -19,9 +19,8 @@ public class TestMVCC {
} }
void test() throws Exception { 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: make unit test work
// 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: free up disk space (for deleted rows and old versions of updated rows) on commit // TODO Prio 1: free up disk space (for deleted rows and old versions of updated rows) on commit
// TODO Prio 1: ScanIndex: never remove uncommitted data from cache (lost sessionId) // TODO Prio 1: ScanIndex: never remove uncommitted data from cache (lost sessionId)
// TODO Prio 1: Test with Hibernate // TODO Prio 1: Test with Hibernate
...@@ -43,20 +42,35 @@ public class TestMVCC { ...@@ -43,20 +42,35 @@ public class TestMVCC {
s2 = c2.createStatement(); s2 = c2.createStatement();
c1.setAutoCommit(false); c1.setAutoCommit(false);
c2.setAutoCommit(false); c2.setAutoCommit(false);
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR(255))");
s1.execute("INSERT INTO TEST VALUES(1, 'Test')");
c1.commit();
test(s1, "select max(id) from test", "1");
s1.execute("INSERT INTO TEST VALUES(2, 'World')");
c1.rollback();
test(s1, "select max(id) from test", "1");
c1.commit();
c2.commit();
s1.execute("DROP TABLE TEST");
s1.execute("create table test as select * from table(id int=(1, 2))");
s1.execute("update test set id=1 where id=1");
s1.execute("select max(id) from test");
test(s1, "select max(id) from test", "2");
c1.commit();
c2.commit();
s1.execute("DROP TABLE TEST");
s1.execute("CREATE TABLE TEST(ID INT)"); s1.execute("CREATE TABLE TEST(ID INT)");
s1.execute("INSERT INTO TEST VALUES(1)"); s1.execute("INSERT INTO TEST VALUES(1)");
c1.commit(); 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"); test(s2, "SELECT COUNT(*) FROM TEST", "1");
System.out.println(s2.executeUpdate("DELETE FROM TEST")); s1.executeUpdate("DELETE FROM TEST");
test(s2, "SELECT COUNT(*) FROM TEST", "1");
test(s1, "SELECT COUNT(*) FROM TEST", "0"); test(s1, "SELECT COUNT(*) FROM TEST", "0");
c1.commit();
test(s2, "SELECT COUNT(*) FROM TEST", "0"); test(s2, "SELECT COUNT(*) FROM TEST", "0");
c1.commit(); c1.commit();
c2.commit(); c2.commit();
......
...@@ -7,6 +7,8 @@ package org.h2.test.unit; ...@@ -7,6 +7,8 @@ package org.h2.test.unit;
import java.sql.*; import java.sql.*;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Backup;
import org.h2.tools.Restore;
import org.h2.tools.Script; import org.h2.tools.Script;
import org.h2.tools.ChangePassword; import org.h2.tools.ChangePassword;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
...@@ -22,7 +24,8 @@ public class TestTools extends TestBase { ...@@ -22,7 +24,8 @@ public class TestTools extends TestBase {
testResourceGenerator(); testResourceGenerator();
testChangePassword(); testChangePassword();
testServer(); testServer();
testBackupRunscript(); testScriptRunscript();
testBackupRestore();
} }
private void testManagementDb() throws Exception { private void testManagementDb() throws Exception {
...@@ -35,7 +38,7 @@ public class TestTools extends TestBase { ...@@ -35,7 +38,7 @@ public class TestTools extends TestBase {
} }
} }
private void testBackupRunscript() throws Exception { private void testScriptRunscript() throws Exception {
Class.forName("org.h2.Driver"); Class.forName("org.h2.Driver");
String url = "jdbc:h2:" + BASE_DIR+ "/utils"; String url = "jdbc:h2:" + BASE_DIR+ "/utils";
String user = "sa", password = "abc"; String user = "sa", password = "abc";
...@@ -53,6 +56,27 @@ public class TestTools extends TestBase { ...@@ -53,6 +56,27 @@ public class TestTools extends TestBase {
conn.close(); conn.close();
} }
private void testBackupRestore() throws Exception {
Class.forName("org.h2.Driver");
String url = "jdbc:h2:" + BASE_DIR+ "/utils";
String user = "sa", password = "abc";
String fileName = BASE_DIR + "/b2.zip";
DeleteDbFiles.main(new String[]{"-dir", BASE_DIR, "-db", "utils", "-quiet"});
Connection conn = DriverManager.getConnection(url, user, password);
conn.createStatement().execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
conn.createStatement().execute("INSERT INTO TEST VALUES(1, 'Hello')");
conn.close();
Backup.main(new String[]{"-file", fileName, "-dir", BASE_DIR, "-db", "utils", "-quiet"});
DeleteDbFiles.main(new String[]{"-dir", BASE_DIR, "-db", "utils", "-quiet"});
Restore.main(new String[]{"-file", fileName, "-dir", BASE_DIR, "-db", "utils", "-quiet"});
conn = DriverManager.getConnection("jdbc:h2:" + BASE_DIR+ "/utils", "sa", "abc");
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM TEST");
check(rs.next());
checkFalse(rs.next());
conn.close();
DeleteDbFiles.main(new String[]{"-dir", BASE_DIR, "-db", "utils", "-quiet"});
}
private void testResourceGenerator() throws Exception { private void testResourceGenerator() throws Exception {
Resources.main(new String[]{"."}); Resources.main(new String[]{"."});
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论