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

--no commit message

--no commit message
上级 23ecaae9
...@@ -1312,7 +1312,7 @@ of a database and re-build the database from the script. ...@@ -1312,7 +1312,7 @@ of a database and re-build the database from the script.
The database keeps most frequently used data and index pages in the main memory. The database keeps most frequently used data and index pages in the main memory.
The amount of memory used for caching can be changed using the setting The amount of memory used for caching can be changed using the setting
CACHE_SIZE. This setting can be set in the database connection URL CACHE_SIZE. This setting can be set in the database connection URL
(jdbc:h2:~/test;CACHE_SIZE=200000), or it can be changed at runtime using (jdbc:h2:~/test;CACHE_SIZE=131072), or it can be changed at runtime using
SET CACHE_SIZE size. SET CACHE_SIZE size.
</p><p> </p><p>
This database supports two cache page replacement algorithms: LRU (the default) and This database supports two cache page replacement algorithms: LRU (the default) and
......
...@@ -40,7 +40,9 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -40,7 +40,9 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3> <h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / TODO (Build xx)</h3><ul> <h3>Version 1.0 / TODO (Build xx)</h3><ul>
<li>Some unit tests failed on Linux because the file system works differently. The unit tests are fixed and should work now. <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.
</li><li>Can now incrementally translate the documentation. See also FAQ. </li><li>Can now incrementally translate the documentation. See also FAQ.
</li><li>Improved error messages: some tools can't show the root cause of an exception. </li><li>Improved error messages: some tools can't show the root cause of an exception.
Adding the message of the root cause to the message of the thrown exception now where it makes sense. Adding the message of the root cause to the message of the thrown exception now where it makes sense.
...@@ -845,13 +847,10 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -845,13 +847,10 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Migrate database tool (also from other database engines) </li><li>Migrate database tool (also from other database engines)
</li><li>Shutdown compact </li><li>Shutdown compact
</li><li>Optimization of distinct with index: select distinct name from test </li><li>Optimization of distinct with index: select distinct name from test
</li><li>Forum: email notification doesn't work? test or disable or document
</li><li>Document server mode, embedded mode, web app mode, dual mode (server+embedded) </li><li>Document server mode, embedded mode, web app mode, dual mode (server+embedded)
</li><li>Stop the server: close all open databases first </li><li>Stop the server: close all open databases first
</li><li>SET variable { TO | = } { value | 'value' | DEFAULT } </li><li>SET variable { TO | = } { value | 'value' | DEFAULT }
</li><li>Running totals: select @running:=if(@previous=t.ID,@running,0)+t.NUM as TOTAL, @previous:=t.ID </li><li>Running totals: select @running:=if(@previous=t.ID,@running,0)+t.NUM as TOTAL, @previous:=t.ID
</li><li>Option to globally disable / enable referential integrity checks
</li><li>Support SET REFERENTIAL_INTEGRITY {TRUE|FALSE}
</li><li>Better support large transactions, large updates / deletes: use less memory </li><li>Better support large transactions, large updates / deletes: use less memory
</li><li>Better support large transactions, large updates / deletes: allow tables without primary key </li><li>Better support large transactions, large updates / deletes: allow tables without primary key
</li><li>Support Oracle RPAD and LPAD(string, n[, pad]) (truncate the end if longer) </li><li>Support Oracle RPAD and LPAD(string, n[, pad]) (truncate the end if longer)
...@@ -1123,6 +1122,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch. ...@@ -1123,6 +1122,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Support SCOPE_IDENTITY() to avoid problems when inserting rows in a trigger </li><li>Support SCOPE_IDENTITY() to avoid problems when inserting rows in a trigger
</li><li>Support DESCRIBE like MySQL or Oracle (DESC|DESCRIBE {[schema.]object[@connect_identifier]}) </li><li>Support DESCRIBE like MySQL or Oracle (DESC|DESCRIBE {[schema.]object[@connect_identifier]})
</li><li>Set a connection read only (Connection.setReadOnly) </li><li>Set a connection read only (Connection.setReadOnly)
</li><li>In MySQL mode, for AUTO_INCREMENT columns, don't set the primary key
</li><li>Use JDK 1.4 file locking to create the lock file (but not yet by default); writing a system property to detect concurrent access from the same VM (different classloaders).
</li><li>Read-only sessions (Connection.setReadOnly) </li><li>Read-only sessions (Connection.setReadOnly)
</li></ul> </li></ul>
......
...@@ -258,7 +258,7 @@ To start the Server from the command line with the default settings, run ...@@ -258,7 +258,7 @@ To start the Server from the command line with the default settings, run
<pre> <pre>
java org.h2.tools.Server java org.h2.tools.Server
</pre> </pre>
This will start the Server with the default options. To get the list of options, run This will start the Server with the default options. To get the list of options and default values, run
<pre> <pre>
java org.h2.tools.Server -? java org.h2.tools.Server -?
</pre> </pre>
......
...@@ -3359,7 +3359,7 @@ public class Parser { ...@@ -3359,7 +3359,7 @@ public class Parser {
columns.add(new Column(cols[i], Value.STRING, 0, 0)); columns.add(new Column(cols[i], Value.STRING, 0, 0));
} }
int id = database.allocateObjectId(true, true); int id = database.allocateObjectId(true, true);
recursiveTable = schema.createTable(tempViewName, id, columns, false); recursiveTable = schema.createTable(tempViewName, id, columns, false, false);
recursiveTable.setTemporary(true); recursiveTable.setTemporary(true);
session.addLocalTempTable(recursiveTable); session.addLocalTempTable(recursiveTable);
String querySQL = StringCache.getNew(sqlCommand.substring(parseIndex)); String querySQL = StringCache.getNew(sqlCommand.substring(parseIndex));
...@@ -3842,6 +3842,11 @@ public class Parser { ...@@ -3842,6 +3842,11 @@ public class Parser {
AlterTableAddConstraint command = new AlterTableAddConstraint(session, table.getSchema()); AlterTableAddConstraint command = new AlterTableAddConstraint(session, table.getSchema());
command.setTableName(table.getName()); command.setTableName(table.getName());
command.setType(type); command.setType(type);
if(readIf("CHECK")) {
command.setCheckExisting(true);
} else if(readIf("NOCHECK")) {
command.setCheckExisting(false);
}
return command; return command;
} else if(readIf("RENAME")) { } else if(readIf("RENAME")) {
read("TO"); read("TO");
...@@ -4045,6 +4050,12 @@ public class Parser { ...@@ -4045,6 +4050,12 @@ public class Parser {
} }
return null; return null;
} }
if(readIf("NOCHECK")) {
command.setCheckExisting(false);
} else {
readIf("CHECK");
command.setCheckExisting(true);
}
command.setTableName(tableName); command.setTableName(tableName);
command.setConstraintName(name); command.setConstraintName(name);
command.setComment(comment); command.setComment(comment);
...@@ -4193,6 +4204,9 @@ public class Parser { ...@@ -4193,6 +4204,9 @@ public class Parser {
read("LOGGED"); read("LOGGED");
} }
} }
if(readIf("CLUSTERED")) {
command.setClustered(true);
}
return command; return command;
} }
......
...@@ -47,6 +47,7 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -47,6 +47,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
private Expression checkExpression; private Expression checkExpression;
private Index index, refIndex; private Index index, refIndex;
private String comment; private String comment;
private boolean checkExisting;
public AlterTableAddConstraint(Session session, Schema schema) { public AlterTableAddConstraint(Session session, Schema schema) {
super(session, schema); super(session, schema);
...@@ -81,6 +82,9 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -81,6 +82,9 @@ public class AlterTableAddConstraint extends SchemaCommand {
check.setExpression(checkExpression); check.setExpression(checkExpression);
check.setTableFilter(filter); check.setTableFilter(filter);
constraint = check; constraint = check;
if(checkExisting) {
check.checkExistingData(session);
}
break; break;
} }
case UNIQUE: { case UNIQUE: {
...@@ -151,6 +155,9 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -151,6 +155,9 @@ public class AlterTableAddConstraint extends SchemaCommand {
ref.setRefTable(refTable); ref.setRefTable(refTable);
ref.setRefColumns(refColumns); ref.setRefColumns(refColumns);
ref.setRefIndex(refIndex, isRefOwner); ref.setRefIndex(refIndex, isRefOwner);
if(checkExisting) {
ref.checkExistingData(session);
}
constraint = ref; constraint = ref;
refTable.addConstraint(constraint); refTable.addConstraint(constraint);
ref.setDeleteAction(session, deleteAction); ref.setDeleteAction(session, deleteAction);
...@@ -158,10 +165,10 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -158,10 +165,10 @@ public class AlterTableAddConstraint extends SchemaCommand {
break; break;
} }
case REFERENTIAL_INTEGRITY_TRUE: case REFERENTIAL_INTEGRITY_TRUE:
table.setCheckForeignKeyConstraints(true); table.setCheckForeignKeyConstraints(session, true, checkExisting);
return 0; return 0;
case REFERENTIAL_INTEGRITY_FALSE: case REFERENTIAL_INTEGRITY_FALSE:
table.setCheckForeignKeyConstraints(false); table.setCheckForeignKeyConstraints(session, false, false);
return 0; return 0;
default: default:
throw Message.getInternalError("type="+type); throw Message.getInternalError("type="+type);
...@@ -304,4 +311,8 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -304,4 +311,8 @@ public class AlterTableAddConstraint extends SchemaCommand {
this.comment = comment; this.comment = comment;
} }
public void setCheckExisting(boolean b) {
this.checkExisting = b;
}
} }
...@@ -198,7 +198,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -198,7 +198,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
// can't just use this table, because most column objects are 'shared' with the old table // can't just use this table, because most column objects are 'shared' with the old table
// still need a new id because using 0 would mean: the new table tries to use the rows of the table 0 (the script table) // still need a new id because using 0 would mean: the new table tries to use the rows of the table 0 (the script table)
int id = -1; int id = -1;
TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent); TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent, false);
newTable.setComment(table.getComment()); newTable.setComment(table.getComment());
execute(newTable.getCreateSQL()); execute(newTable.getCreateSQL());
newTable = (TableData) newTable.getSchema().getTableOrView(session, newTable.getName()); newTable = (TableData) newTable.getSchema().getTableOrView(session, newTable.getName());
...@@ -274,7 +274,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -274,7 +274,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
buff.append(" FROM "); buff.append(" FROM ");
buff.append(table.getSQL()); buff.append(table.getSQL());
String sql = buff.toString(); String sql = buff.toString();
newTable.setCheckForeignKeyConstraints(false); newTable.setCheckForeignKeyConstraints(session, false, false);
try { try {
execute(sql); execute(sql);
} catch(SQLException e) { } catch(SQLException e) {
...@@ -282,7 +282,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -282,7 +282,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
execute("DROP TABLE " + newTable.getSQL()); execute("DROP TABLE " + newTable.getSQL());
throw e; throw e;
} }
newTable.setCheckForeignKeyConstraints(true); newTable.setCheckForeignKeyConstraints(session, true, false);
String tableName = table.getName(); String tableName = table.getName();
table.setModified(); table.setModified();
for(int i=0; i<columns.length; i++) { for(int i=0; i<columns.length; i++) {
......
...@@ -41,6 +41,7 @@ public class CreateTable extends SchemaCommand { ...@@ -41,6 +41,7 @@ public class CreateTable extends SchemaCommand {
private boolean onCommitTruncate; private boolean onCommitTruncate;
private Query asQuery; private Query asQuery;
private String comment; private String comment;
private boolean clustered;
public CreateTable(Session session, Schema schema) { public CreateTable(Session session, Schema schema) {
super(session, schema); super(session, schema);
...@@ -120,7 +121,7 @@ public class CreateTable extends SchemaCommand { ...@@ -120,7 +121,7 @@ public class CreateTable extends SchemaCommand {
} }
} }
int id = getObjectId(true, true); int id = getObjectId(true, true);
TableData table = getSchema().createTable(tableName, id, columns, persistent); TableData table = getSchema().createTable(tableName, id, columns, persistent, clustered);
table.setComment(comment); table.setComment(comment);
table.setTemporary(temporary); table.setTemporary(temporary);
table.setGlobalTemporary(globalTemporary); table.setGlobalTemporary(globalTemporary);
...@@ -235,4 +236,8 @@ public class CreateTable extends SchemaCommand { ...@@ -235,4 +236,8 @@ public class CreateTable extends SchemaCommand {
this.comment = comment; this.comment = comment;
} }
public void setClustered(boolean clustered) {
this.clustered = clustered;
}
} }
...@@ -21,44 +21,45 @@ import org.h2.message.TraceSystem; ...@@ -21,44 +21,45 @@ import org.h2.message.TraceSystem;
*/ */
public class SysProperties { public class SysProperties {
public static final int MIN_WRITE_DELAY = SysProperties.getIntSetting("h2.minWriteDelay", 5); public static final boolean MVCC = getBooleanSetting("h2.mvcc", false);
public static final boolean CHECK = SysProperties.getBooleanSetting("h2.check", true); public static final int MIN_WRITE_DELAY = getIntSetting("h2.minWriteDelay", 5);
public static final boolean CHECK2 = SysProperties.getBooleanSetting("h2.check2", false); public static final boolean CHECK = getBooleanSetting("h2.check", true);
public static final boolean OPTIMIZE_MIN_MAX = SysProperties.getBooleanSetting("h2.optimizeMinMax", true); public static final boolean CHECK2 = getBooleanSetting("h2.check2", false);
public static final boolean OPTIMIZE_IN = SysProperties.getBooleanSetting("h2.optimizeIn", true); public static final boolean OPTIMIZE_MIN_MAX = getBooleanSetting("h2.optimizeMinMax", true);
public static final int REDO_BUFFER_SIZE = SysProperties.getIntSetting("h2.redoBufferSize", 256 * 1024); public static final boolean OPTIMIZE_IN = getBooleanSetting("h2.optimizeIn", true);
public static final boolean RECOMPILE_ALWAYS = SysProperties.getBooleanSetting("h2.recompileAlways", false); public static final int REDO_BUFFER_SIZE = getIntSetting("h2.redoBufferSize", 256 * 1024);
public static final boolean OPTIMIZE_SUBQUERY_CACHE = SysProperties.getBooleanSetting("h2.optimizeSubqueryCache", true); public static final boolean RECOMPILE_ALWAYS = getBooleanSetting("h2.recompileAlways", false);
public static final boolean OVERFLOW_EXCEPTIONS = SysProperties.getBooleanSetting("h2.overflowExceptions", true); public static final boolean OPTIMIZE_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true);
public static final boolean LOG_ALL_ERRORS = SysProperties.getBooleanSetting("h2.logAllErrors", false); public static final boolean OVERFLOW_EXCEPTIONS = getBooleanSetting("h2.overflowExceptions", true);
public static final String LOG_ALL_ERRORS_FILE = SysProperties.getStringSetting("h2.logAllErrorsFile", "h2errors.txt"); public static final boolean LOG_ALL_ERRORS = getBooleanSetting("h2.logAllErrors", false);
public static final int SERVER_CACHED_OBJECTS = SysProperties.getIntSetting("h2.serverCachedObjects", 64); public static final String LOG_ALL_ERRORS_FILE = getStringSetting("h2.logAllErrorsFile", "h2errors.txt");
public static final int SERVER_SMALL_RESULT_SET_SIZE = SysProperties.getIntSetting("h2.serverSmallResultSetSize", 100); public static final int SERVER_CACHED_OBJECTS = getIntSetting("h2.serverCachedObjects", 64);
public static final int EMERGENCY_SPACE_INITIAL = SysProperties.getIntSetting("h2.emergencySpaceInitial", 1 * 1024 * 1024); public static final int SERVER_SMALL_RESULT_SET_SIZE = getIntSetting("h2.serverSmallResultSetSize", 100);
public static final int EMERGENCY_SPACE_MIN = SysProperties.getIntSetting("h2.emergencySpaceMin", 128 * 1024); public static final int EMERGENCY_SPACE_INITIAL = getIntSetting("h2.emergencySpaceInitial", 1 * 1024 * 1024);
public static final boolean OBJECT_CACHE = SysProperties.getBooleanSetting("h2.objectCache", true); public static final int EMERGENCY_SPACE_MIN = getIntSetting("h2.emergencySpaceMin", 128 * 1024);
public static final int OBJECT_CACHE_SIZE = SysProperties.getIntSetting("h2.objectCacheSize", 1024); public static final boolean OBJECT_CACHE = getBooleanSetting("h2.objectCache", true);
public static final int OBJECT_CACHE_MAX_PER_ELEMENT_SIZE = SysProperties.getIntSetting("h2.objectCacheMaxPerElementSize", 4096); public static final int OBJECT_CACHE_SIZE = getIntSetting("h2.objectCacheSize", 1024);
public static final String CLIENT_TRACE_DIRECTORY = SysProperties.getStringSetting("h2.clientTraceDirectory", "trace.db/"); public static final int OBJECT_CACHE_MAX_PER_ELEMENT_SIZE = getIntSetting("h2.objectCacheMaxPerElementSize", 4096);
public static final int MAX_FILE_RETRY = Math.max(1, SysProperties.getIntSetting("h2.maxFileRetry", 16)); public static final String CLIENT_TRACE_DIRECTORY = getStringSetting("h2.clientTraceDirectory", "trace.db/");
public static final boolean ALLOW_BIG_DECIMAL_EXTENSIONS = SysProperties.getBooleanSetting("h2.allowBigDecimalExtensions", false); public static final int MAX_FILE_RETRY = Math.max(1, getIntSetting("h2.maxFileRetry", 16));
public static final boolean INDEX_LOOKUP_NEW = SysProperties.getBooleanSetting("h2.indexLookupNew", true); public static final boolean ALLOW_BIG_DECIMAL_EXTENSIONS = getBooleanSetting("h2.allowBigDecimalExtensions", false);
public static final boolean TRACE_IO = SysProperties.getBooleanSetting("h2.traceIO", false); public static final boolean INDEX_LOOKUP_NEW = getBooleanSetting("h2.indexLookupNew", true);
public static final int DATASOURCE_TRACE_LEVEL = SysProperties.getIntSetting("h2.dataSourceTraceLevel", TraceSystem.ERROR); public static final boolean TRACE_IO = getBooleanSetting("h2.traceIO", false);
public static final int CACHE_SIZE_DEFAULT = SysProperties.getIntSetting("h2.cacheSizeDefault", 16 * 1024); public static final int DATASOURCE_TRACE_LEVEL = getIntSetting("h2.dataSourceTraceLevel", TraceSystem.ERROR);
public static final int CACHE_SIZE_INDEX_SHIFT = SysProperties.getIntSetting("h2.cacheSizeIndexShift", 3); public static final int CACHE_SIZE_DEFAULT = getIntSetting("h2.cacheSizeDefault", 16 * 1024);
public static final int DEFAULT_MAX_MEMORY_UNDO = SysProperties.getIntSetting("h2.defaultMaxMemoryUndo", 50000); public static final int CACHE_SIZE_INDEX_SHIFT = getIntSetting("h2.cacheSizeIndexShift", 3);
public static final boolean OPTIMIZE_NOT = SysProperties.getBooleanSetting("h2.optimizeNot", true); public static final int DEFAULT_MAX_MEMORY_UNDO = getIntSetting("h2.defaultMaxMemoryUndo", 50000);
public static final boolean OPTIMIZE_TWO_EQUALS = SysProperties.getBooleanSetting("h2.optimizeTwoEquals", true); public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true);
public static final int DEFAULT_LOCK_MODE = SysProperties.getIntSetting("h2.defaultLockMode", Constants.LOCK_MODE_READ_COMMITTED); public static final boolean OPTIMIZE_TWO_EQUALS = getBooleanSetting("h2.optimizeTwoEquals", true);
public static boolean runFinalize = SysProperties.getBooleanSetting("h2.runFinalize", true); public static final int DEFAULT_LOCK_MODE = getIntSetting("h2.defaultLockMode", Constants.LOCK_MODE_READ_COMMITTED);
public static String scriptDirectory = SysProperties.getStringSetting("h2.scriptDirectory", ""); public static boolean runFinalize = getBooleanSetting("h2.runFinalize", true);
public static String baseDir = SysProperties.getStringSetting("h2.baseDir", null); public static String scriptDirectory = getStringSetting("h2.scriptDirectory", "");
public static boolean multiThreadedKernel = SysProperties.getBooleanSetting("h2.multiThreadedKernel", false); public static String baseDir = getStringSetting("h2.baseDir", null);
public static boolean lobCloseBetweenReads = SysProperties.getBooleanSetting("h2.lobCloseBetweenReads", false); public static boolean multiThreadedKernel = getBooleanSetting("h2.multiThreadedKernel", false);
public static boolean lobCloseBetweenReads = getBooleanSetting("h2.lobCloseBetweenReads", false);
// TODO: also remove DataHandler.allocateObjectId, createTempFile when setting this to true and removing it // TODO: also remove DataHandler.allocateObjectId, createTempFile when setting this to true and removing it
public static final boolean LOB_FILES_IN_DIRECTORIES = SysProperties.getBooleanSetting("h2.lobFilesInDirectories", false); public static final boolean LOB_FILES_IN_DIRECTORIES = getBooleanSetting("h2.lobFilesInDirectories", false);
public static final int LOB_FILES_PER_DIRECTORY = SysProperties.getIntSetting("h2.lobFilesPerDirectory", 256); public static final int LOB_FILES_PER_DIRECTORY = getIntSetting("h2.lobFilesPerDirectory", 256);
private static boolean getBooleanSetting(String name, boolean defaultValue) { private static boolean getBooleanSetting(String name, boolean defaultValue) {
String s = System.getProperty(name); String s = System.getProperty(name);
......
...@@ -45,6 +45,7 @@ public abstract class Constraint extends SchemaObject { ...@@ -45,6 +45,7 @@ public abstract class Constraint extends SchemaObject {
public abstract String getCreateSQLWithoutIndexes(); public abstract String getCreateSQLWithoutIndexes();
public abstract boolean isBefore(); public abstract boolean isBefore();
public abstract String getShortDescription(); public abstract String getShortDescription();
public abstract void checkExistingData(Session session) throws SQLException;
public Table getTable() { public Table getTable() {
return table; return table;
......
...@@ -11,6 +11,7 @@ import org.h2.engine.Session; ...@@ -11,6 +11,7 @@ import org.h2.engine.Session;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.table.Column; import org.h2.table.Column;
...@@ -55,6 +56,7 @@ public class ConstraintCheck extends Constraint { ...@@ -55,6 +56,7 @@ public class ConstraintCheck extends Constraint {
} }
buff.append(" CHECK"); buff.append(" CHECK");
buff.append(StringUtils.enclose(expr.getSQL())); buff.append(StringUtils.enclose(expr.getSQL()));
buff.append(" NOCHECK");
return buff.toString(); return buff.toString();
} }
...@@ -111,5 +113,23 @@ public class ConstraintCheck extends Constraint { ...@@ -111,5 +113,23 @@ public class ConstraintCheck extends Constraint {
public boolean isBefore() { public boolean isBefore() {
return true; return true;
} }
public void checkExistingData(Session session) throws SQLException {
if(session.getDatabase().isStarting()) {
// don't check at startup
return;
}
StringBuffer buff = new StringBuffer();
buff.append("SELECT 1 FROM ");
buff.append(filter.getTable().getSQL());
buff.append(" WHERE NOT(");
buff.append(expr.getSQL());
buff.append(")");
String sql = buff.toString();
LocalResult r = session.prepare(sql).query(1);
if(r.next()) {
throw Message.getSQLException(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, getName());
}
}
} }
...@@ -16,6 +16,7 @@ import org.h2.expression.Parameter; ...@@ -16,6 +16,7 @@ import org.h2.expression.Parameter;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.schema.Schema; import org.h2.schema.Schema;
...@@ -129,6 +130,7 @@ public class ConstraintReferential extends Constraint { ...@@ -129,6 +130,7 @@ public class ConstraintReferential extends Constraint {
buff.append(" ON UPDATE "); buff.append(" ON UPDATE ");
appendAction(buff, updateAction); appendAction(buff, updateAction);
} }
buff.append(" NOCHECK");
return buff.toString(); return buff.toString();
} }
...@@ -531,4 +533,47 @@ public class ConstraintReferential extends Constraint { ...@@ -531,4 +533,47 @@ public class ConstraintReferential extends Constraint {
return false; return false;
} }
public void checkExistingData(Session session) throws SQLException {
if(session.getDatabase().isStarting()) {
// don't check at startup
return;
}
StringBuffer buff = new StringBuffer();
buff.append("SELECT 1 FROM (SELECT ");
for(int i=0; i<columns.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(columns[i].getSQL());
}
buff.append(" FROM ");
buff.append(table.getSQL());
buff.append(" ORDER BY ");
for(int i=0; i<columns.length; i++) {
if(i>0) {
buff.append(", ");
}
buff.append(columns[i].getSQL());
}
buff.append(") C WHERE NOT EXISTS(SELECT 1 FROM ");
buff.append(refTable.getSQL());
buff.append(" P WHERE ");
for(int i=0; i<columns.length; i++) {
if(i>0) {
buff.append(" AND ");
}
buff.append("C.");
buff.append(columns[i].getSQL());
buff.append("=");
buff.append("P.");
buff.append(refColumns[i].getSQL());
}
buff.append(")");
String sql = buff.toString();
LocalResult r = session.prepare(sql).query(1);
if(r.next()) {
throw Message.getSQLException(ErrorCode.REFERENTIAL_INTEGRITY_VIOLATED_PARENT_MISSING_1, getShortDescription());
}
}
} }
...@@ -129,5 +129,9 @@ public class ConstraintUnique extends Constraint { ...@@ -129,5 +129,9 @@ public class ConstraintUnique extends Constraint {
public boolean isBefore() { public boolean isBefore() {
return true; return true;
} }
public void checkExistingData(Session session) throws SQLException {
// no need to check: when creating the unique index any problems are found
}
} }
...@@ -473,7 +473,7 @@ public class Database implements DataHandler { ...@@ -473,7 +473,7 @@ public class Database implements DataHandler {
cols.add(new Column("HEAD", Value.INT, 0, 0)); cols.add(new Column("HEAD", Value.INT, 0, 0));
cols.add(new Column("TYPE", Value.INT, 0, 0)); cols.add(new Column("TYPE", Value.INT, 0, 0));
cols.add(new Column("SQL", Value.STRING, 0, 0)); cols.add(new Column("SQL", Value.STRING, 0, 0));
meta = mainSchema.createTable("SYS", 0, cols, persistent); meta = mainSchema.createTable("SYS", 0, cols, persistent, false);
metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, new Column[]{columnId}, IndexType.createPrimaryKey(false, false), Index.EMPTY_HEAD, null); metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, new Column[]{columnId}, IndexType.createPrimaryKey(false, false), Index.EMPTY_HEAD, null);
objectIds.set(0); objectIds.set(0);
// there could be views on system tables, so they must be added first // there could be views on system tables, so they must be added first
...@@ -1513,5 +1513,9 @@ public class Database implements DataHandler { ...@@ -1513,5 +1513,9 @@ public class Database implements DataHandler {
public boolean getReferentialIntegrity() { public boolean getReferentialIntegrity() {
return referentialIntegrity; return referentialIntegrity;
} }
public boolean isStarting() {
return starting;
}
} }
...@@ -191,6 +191,12 @@ public class Session implements SessionInterface { ...@@ -191,6 +191,12 @@ public class Session implements SessionInterface {
logSystem.commit(this); logSystem.commit(this);
} }
if(undoLog.size() > 0) { if(undoLog.size() > 0) {
if(SysProperties.MVCC) {
while (undoLog.size() > 0) {
UndoLogRecord entry = undoLog.getAndRemoveLast();
entry.commit();
}
}
undoLog.clear(); undoLog.clear();
} }
if(!ddl) { if(!ddl) {
...@@ -287,7 +293,7 @@ public class Session implements SessionInterface { ...@@ -287,7 +293,7 @@ public class Session implements SessionInterface {
// otherwise rollback will try to rollback a not-inserted row // otherwise rollback will try to rollback a not-inserted row
if(SysProperties.CHECK) { if(SysProperties.CHECK) {
int lockMode = database.getLockMode(); int lockMode = database.getLockMode();
if(lockMode != Constants.LOCK_MODE_OFF) { if(lockMode != Constants.LOCK_MODE_OFF && !SysProperties.MVCC) {
if(locks.indexOf(log.getTable())<0 && log.getTable().getTableType() != Table.TABLE_LINK) { if(locks.indexOf(log.getTable())<0 && log.getTable().getTableType() != Table.TABLE_LINK) {
throw Message.getInternalError(); throw Message.getInternalError();
} }
......
...@@ -214,6 +214,10 @@ public class Comparison extends Condition { ...@@ -214,6 +214,10 @@ public class Comparison extends Condition {
return BIGGER; return BIGGER;
case SMALLER: case SMALLER:
return BIGGER_EQUAL; return BIGGER_EQUAL;
case IS_NULL:
return IS_NOT_NULL;
case IS_NOT_NULL:
return IS_NULL;
default: default:
throw Message.getInternalError("type="+compareType); throw Message.getInternalError("type="+compareType);
} }
......
...@@ -7,6 +7,7 @@ package org.h2.index; ...@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Session;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
...@@ -21,12 +22,18 @@ public class BtreeCursor implements Cursor { ...@@ -21,12 +22,18 @@ public class BtreeCursor implements Cursor {
private Row currentRow; private Row currentRow;
private boolean first; private boolean first;
private SearchRow last; private SearchRow last;
private Session session;
BtreeCursor(BtreeIndex index, SearchRow last) { BtreeCursor(Session session, BtreeIndex index, SearchRow last) {
this.session = session;
this.index = index; this.index = index;
this.last = last; this.last = last;
first = true; first = true;
} }
Session getSession() {
return session;
}
void setStackPosition(int position) { void setStackPosition(int position) {
top.position = position; top.position = position;
...@@ -55,7 +62,7 @@ public class BtreeCursor implements Cursor { ...@@ -55,7 +62,7 @@ public class BtreeCursor implements Cursor {
public Row get() throws SQLException { public Row get() throws SQLException {
if(currentRow == null && currentSearchRow != null) { if(currentRow == null && currentSearchRow != null) {
currentRow = index.getRow(currentSearchRow.getPos()); currentRow = index.getRow(session, currentSearchRow.getPos());
} }
return currentRow; return currentRow;
} }
...@@ -71,13 +78,24 @@ public class BtreeCursor implements Cursor { ...@@ -71,13 +78,24 @@ public class BtreeCursor implements Cursor {
public boolean next() throws SQLException { public boolean next() throws SQLException {
if (first) { if (first) {
first = false; first = false;
return currentSearchRow != null; } else {
top.page.next(this, top.position);
if(currentSearchRow != null && last != null) {
if (index.compareRows(currentSearchRow, last) > 0) {
currentSearchRow = null;
currentRow = null;
}
}
} }
top.page.next(this, top.position); if(SysProperties.MVCC) {
if(currentSearchRow != null && last != null) { if(currentSearchRow != null) {
if (index.compareRows(currentSearchRow, last) > 0) { while(true) {
currentSearchRow = null; Row r = get();
currentRow = null; int sessionId = r.getSessionId();
if(sessionId == 0 || sessionId == session.getId()) {
break;
}
}
} }
} }
return currentSearchRow != null; return currentSearchRow != null;
......
...@@ -51,12 +51,12 @@ public class BtreeIndex extends Index implements RecordReader { ...@@ -51,12 +51,12 @@ public class BtreeIndex extends Index implements RecordReader {
truncate(session); truncate(session);
needRebuild = true; needRebuild = true;
} else { } else {
Record rec = storage.getRecordIfStored(headPos); Record rec = storage.getRecordIfStored(session, headPos);
if(rec != null && (rec instanceof BtreeHead)) { if(rec != null && (rec instanceof BtreeHead)) {
head = (BtreeHead) rec; head = (BtreeHead) rec;
} }
if(head != null && head.getConsistent()) { if(head != null && head.getConsistent()) {
setRoot((BtreePage) storage.getRecord(head.getRootPosition())); setRoot((BtreePage) storage.getRecord(session, head.getRootPosition()));
needRebuild = false; needRebuild = false;
rowCount = table.getRowCount(); rowCount = table.getRowCount();
} else { } else {
...@@ -114,8 +114,8 @@ public class BtreeIndex extends Index implements RecordReader { ...@@ -114,8 +114,8 @@ public class BtreeIndex extends Index implements RecordReader {
storage.addRecord(session, p, Storage.ALLOCATE_POS); storage.addRecord(session, p, Storage.ALLOCATE_POS);
} }
public BtreePage getPage(int i) throws SQLException { public BtreePage getPage(Session session, int i) throws SQLException {
return (BtreePage) storage.getRecord(i); return (BtreePage) storage.getRecord(session, i);
} }
public void flush(Session session) throws SQLException { public void flush(Session session) throws SQLException {
...@@ -178,11 +178,11 @@ public class BtreeIndex extends Index implements RecordReader { ...@@ -178,11 +178,11 @@ public class BtreeIndex extends Index implements RecordReader {
throw Message.getSQLException(ErrorCode.OBJECT_CLOSED); throw Message.getSQLException(ErrorCode.OBJECT_CLOSED);
} }
if(first==null) { if(first==null) {
BtreeCursor cursor = new BtreeCursor(this, last); BtreeCursor cursor = new BtreeCursor(session, this, last);
root.first(cursor); root.first(cursor);
return cursor; return cursor;
} else { } else {
BtreeCursor cursor = new BtreeCursor(this, last); BtreeCursor cursor = new BtreeCursor(session, this, last);
if (!root.findFirst(cursor, first)) { if (!root.findFirst(cursor, first)) {
cursor.setCurrentRow(null); cursor.setCurrentRow(null);
} }
...@@ -203,12 +203,12 @@ public class BtreeIndex extends Index implements RecordReader { ...@@ -203,12 +203,12 @@ public class BtreeIndex extends Index implements RecordReader {
return 10 * getCostRangeIndex(masks, tableData.getRowCount()); return 10 * getCostRangeIndex(masks, tableData.getRowCount());
} }
public Record read(DataPage s) throws SQLException { public Record read(Session session, DataPage s) throws SQLException {
char c = (char) s.readByte(); char c = (char) s.readByte();
if (c == 'N') { if (c == 'N') {
return new BtreeNode(this, s); return new BtreeNode(this, s);
} else if (c == 'L') { } else if (c == 'L') {
return new BtreeLeaf(this, s); return new BtreeLeaf(this, session, s);
} else if (c == 'H') { } else if (c == 'H') {
return new BtreeHead(s); return new BtreeHead(s);
} else { } else {
...@@ -237,8 +237,8 @@ public class BtreeIndex extends Index implements RecordReader { ...@@ -237,8 +237,8 @@ public class BtreeIndex extends Index implements RecordReader {
return rows; return rows;
} }
public Row getRow(int pos) throws SQLException { public Row getRow(Session session, int pos) throws SQLException {
return tableData.getRow(pos); return tableData.getRow(session, pos);
} }
private void flushHead(Session session) throws SQLException { private void flushHead(Session session) throws SQLException {
...@@ -297,7 +297,7 @@ public class BtreeIndex extends Index implements RecordReader { ...@@ -297,7 +297,7 @@ public class BtreeIndex extends Index implements RecordReader {
} }
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} else { } else {
SearchRow row = root.getLast(); SearchRow row = root.getLast(session);
if(row != null) { if(row != null) {
Value v = row.getValue(columnIndex[0]); Value v = row.getValue(columnIndex[0]);
return v; return v;
......
...@@ -30,7 +30,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -30,7 +30,7 @@ public class BtreeLeaf extends BtreePage {
private boolean writePos; private boolean writePos;
private int cachedRealByteCount; private int cachedRealByteCount;
BtreeLeaf(BtreeIndex index, DataPage s) throws SQLException { BtreeLeaf(BtreeIndex index, Session session,DataPage s) throws SQLException {
super(index); super(index);
writePos = s.readByte() == 'P'; writePos = s.readByte() == 'P';
if(writePos) { if(writePos) {
...@@ -38,7 +38,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -38,7 +38,7 @@ public class BtreeLeaf extends BtreePage {
// should be 1, but may not be 1 // should be 1, but may not be 1
pageData = new ObjectArray(size); pageData = new ObjectArray(size);
for(int i=0; i<size; i++) { for(int i=0; i<size; i++) {
Row r = index.getRow(s.readInt()); Row r = index.getRow(session, s.readInt());
pageData.add(r); pageData.add(r);
} }
} else { } else {
...@@ -258,7 +258,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -258,7 +258,7 @@ public class BtreeLeaf extends BtreePage {
return size; return size;
} }
SearchRow getLast() throws SQLException { SearchRow getLast(Session sessioni) throws SQLException {
if(pageData.size()==0) { if(pageData.size()==0) {
if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) { if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page"); throw Message.getInternalError("Empty btree page");
...@@ -268,7 +268,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -268,7 +268,7 @@ public class BtreeLeaf extends BtreePage {
return (SearchRow)pageData.get(pageData.size()-1); return (SearchRow)pageData.get(pageData.size()-1);
} }
SearchRow getFirst() throws SQLException { SearchRow getFirst(Session session) throws SQLException {
if(pageData.size()==0) { if(pageData.size()==0) {
if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) { if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page"); throw Message.getInternalError("Empty btree page");
......
...@@ -56,12 +56,12 @@ public class BtreeNode extends BtreePage { ...@@ -56,12 +56,12 @@ public class BtreeNode extends BtreePage {
this.pageData = pageData; this.pageData = pageData;
} }
protected SearchRow getData(int i) throws SQLException { protected SearchRow getData(Session session, int i) throws SQLException {
SearchRow r = (SearchRow) pageData.get(i); SearchRow r = (SearchRow) pageData.get(i);
if(r == null) { if(r == null) {
int p = pageChildren.get(i+1); int p = pageChildren.get(i+1);
BtreePage page = index.getPage(p); BtreePage page = index.getPage(session, p);
r = page.getFirst(); r = page.getFirst(session);
pageData.set(i, r); pageData.set(i, r);
} }
return r; return r;
...@@ -91,7 +91,7 @@ public class BtreeNode extends BtreePage { ...@@ -91,7 +91,7 @@ public class BtreeNode extends BtreePage {
} }
} }
int at = l; int at = l;
BtreePage page = index.getPage(pageChildren.get(at)); BtreePage page = index.getPage(session, pageChildren.get(at));
int splitPoint = page.add(newRow, session); int splitPoint = page.add(newRow, session);
if (splitPoint == 0) { if (splitPoint == 0) {
return 0; return 0;
...@@ -130,7 +130,7 @@ public class BtreeNode extends BtreePage { ...@@ -130,7 +130,7 @@ public class BtreeNode extends BtreePage {
} }
int at = l; int at = l;
// merge is not implemented to allow concurrent usage of btrees // merge is not implemented to allow concurrent usage of btrees
BtreePage page = index.getPage(pageChildren.get(at)); BtreePage page = index.getPage(session, pageChildren.get(at));
SearchRow first = page.remove(session, oldRow, level+1); SearchRow first = page.remove(session, oldRow, level+1);
if(first == null) { if(first == null) {
// the first row didn't change - nothing to do here // the first row didn't change - nothing to do here
...@@ -216,7 +216,7 @@ public class BtreeNode extends BtreePage { ...@@ -216,7 +216,7 @@ public class BtreeNode extends BtreePage {
} }
} }
if(l>=pageData.size()) { if(l>=pageData.size()) {
BtreePage page = index.getPage(pageChildren.get(l)); BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l); cursor.push(this, l);
boolean result = page.findFirst(cursor, compare); boolean result = page.findFirst(cursor, compare);
if(result) { if(result) {
...@@ -225,7 +225,7 @@ public class BtreeNode extends BtreePage { ...@@ -225,7 +225,7 @@ public class BtreeNode extends BtreePage {
cursor.pop(); cursor.pop();
return false; return false;
} }
BtreePage page = index.getPage(pageChildren.get(l)); BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l); cursor.push(this, l);
if(page.findFirst(cursor, compare)) { if(page.findFirst(cursor, compare)) {
return true; return true;
...@@ -236,7 +236,7 @@ public class BtreeNode extends BtreePage { ...@@ -236,7 +236,7 @@ public class BtreeNode extends BtreePage {
SearchRow row = getData(i); SearchRow row = getData(i);
int comp = index.compareRows(row, compare); int comp = index.compareRows(row, compare);
if (comp >=0) { if (comp >=0) {
page = index.getPage(pageChildren.get(i)); page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i); cursor.push(this, i);
if(page.findFirst(cursor, compare)) { if(page.findFirst(cursor, compare)) {
return true; return true;
...@@ -244,7 +244,7 @@ public class BtreeNode extends BtreePage { ...@@ -244,7 +244,7 @@ public class BtreeNode extends BtreePage {
cursor.pop(); cursor.pop();
} }
} }
page = index.getPage(pageChildren.get(i)); page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i); cursor.push(this, i);
boolean result = page.findFirst(cursor, compare); boolean result = page.findFirst(cursor, compare);
if(result) { if(result) {
...@@ -258,7 +258,7 @@ public class BtreeNode extends BtreePage { ...@@ -258,7 +258,7 @@ public class BtreeNode extends BtreePage {
i++; i++;
if (i <= pageData.size()) { if (i <= pageData.size()) {
cursor.setStackPosition(i); cursor.setStackPosition(i);
BtreePage page = index.getPage(pageChildren.get(i)); BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(i));
page.first(cursor); page.first(cursor);
return; return;
} }
...@@ -278,7 +278,7 @@ public class BtreeNode extends BtreePage { ...@@ -278,7 +278,7 @@ public class BtreeNode extends BtreePage {
public void first(BtreeCursor cursor) throws SQLException { public void first(BtreeCursor cursor) throws SQLException {
cursor.push(this, 0); cursor.push(this, 0);
BtreePage page = index.getPage(pageChildren.get(0)); BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(0));
page.first(cursor); page.first(cursor);
} }
...@@ -331,24 +331,24 @@ public class BtreeNode extends BtreePage { ...@@ -331,24 +331,24 @@ public class BtreeNode extends BtreePage {
return size + index.getRecordOverhead(); return size + index.getRecordOverhead();
} }
SearchRow getLast() throws SQLException { SearchRow getLast(Session session) throws SQLException {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) { if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page"); throw Message.getInternalError("Empty btree page");
} }
for(int i=pageChildren.size()-1; i>=0; i--) { for(int i=pageChildren.size()-1; i>=0; i--) {
BtreePage page = index.getPage(pageChildren.get(i)); BtreePage page = index.getPage(session, pageChildren.get(i));
if(page != null) { if(page != null) {
return page.getLast(); return page.getLast(session);
} }
} }
return null; return null;
} }
SearchRow getFirst() throws SQLException { SearchRow getFirst(Session session) throws SQLException {
for(int i=0; i<pageChildren.size(); i++) { for(int i=0; i<pageChildren.size(); i++) {
BtreePage page = index.getPage(pageChildren.get(i)); BtreePage page = index.getPage(session, pageChildren.get(i));
if(page != null) { if(page != null) {
return page.getFirst(); return page.getFirst(session);
} }
} }
return null; return null;
......
...@@ -39,8 +39,8 @@ public abstract class BtreePage extends Record { ...@@ -39,8 +39,8 @@ public abstract class BtreePage extends Record {
abstract SearchRow remove(Session session, Row row, int level) throws SQLException; abstract SearchRow remove(Session session, Row row, int level) throws SQLException;
abstract BtreePage split(Session session, int splitPoint) throws SQLException; abstract BtreePage split(Session session, int splitPoint) throws SQLException;
abstract boolean findFirst(BtreeCursor cursor, SearchRow row) throws SQLException; abstract boolean findFirst(BtreeCursor cursor, SearchRow row) throws SQLException;
abstract SearchRow getFirst() throws SQLException; abstract SearchRow getFirst(Session session) throws SQLException;
abstract SearchRow getLast() throws SQLException; abstract SearchRow getLast(Session session) throws SQLException;
abstract void next(BtreeCursor cursor, int i) throws SQLException; abstract void next(BtreeCursor cursor, int i) throws SQLException;
abstract void first(BtreeCursor cursor) throws SQLException; abstract void first(BtreeCursor cursor) throws SQLException;
abstract int getRealByteCount() throws SQLException; abstract int getRealByteCount() throws SQLException;
......
...@@ -106,7 +106,7 @@ public class HashIndex extends Index { ...@@ -106,7 +106,7 @@ public class HashIndex extends Index {
int key = first.getValue(columns[0].getColumnId()).getInt(); int key = first.getValue(columns[0].getColumnId()).getInt();
int pos = intMap.get(key); int pos = intMap.get(key);
if(pos != IntIntHashMap.NOT_FOUND) { if(pos != IntIntHashMap.NOT_FOUND) {
result = tableData.getRow(pos); result = tableData.getRow(session, pos);
} else { } else {
result = null; result = null;
} }
...@@ -115,7 +115,7 @@ public class HashIndex extends Index { ...@@ -115,7 +115,7 @@ public class HashIndex extends Index {
if (pos == null) { if (pos == null) {
result = null; result = null;
} else { } else {
result = tableData.getRow(pos.intValue()); result = tableData.getRow(session, pos.intValue());
} }
} }
return new HashCursor(result); return new HashCursor(result);
......
...@@ -65,7 +65,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -65,7 +65,7 @@ public class LinearHashIndex extends Index implements RecordReader {
truncate(session); truncate(session);
needRebuild = true; needRebuild = true;
} else { } else {
head = (LinearHashHead) storage.getRecord(pos); head = (LinearHashHead) storage.getRecord(session, pos);
} }
} }
...@@ -149,16 +149,16 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -149,16 +149,16 @@ public class LinearHashIndex extends Index implements RecordReader {
record.key = key; record.key = key;
record.home = home; record.home = home;
record.value = value; record.value = value;
int free = getNextFree(home); int free = getNextFree(session, home);
while (true) { while (true) {
LinearHashBucket bucket = getBucket(index); LinearHashBucket bucket = getBucket(session, index);
if (bucket.getRecordSize() < RECORDS_PER_BUCKET) { if (bucket.getRecordSize() < RECORDS_PER_BUCKET) {
addRecord(session, bucket, record); addRecord(session, bucket, record);
break; break;
} }
// this bucket is full // this bucket is full
int foreign = getForeignHome(index); int foreign = getForeignHome(session, index);
if (foreign >= 0 && foreign != home) { if (foreign >= 0 && foreign != home) {
// move out foreign records - add this record - add foreign // move out foreign records - add this record - add foreign
// records again // records again
...@@ -174,7 +174,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -174,7 +174,7 @@ public class LinearHashIndex extends Index implements RecordReader {
continue; continue;
} }
int nextFree = getNextFree(free); int nextFree = getNextFree(session, free);
if (nextFree < 0) { if (nextFree < 0) {
// trace.debug("split because no chain " + head.bucketCount); // trace.debug("split because no chain " + head.bucketCount);
split(session); split(session);
...@@ -183,32 +183,32 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -183,32 +183,32 @@ public class LinearHashIndex extends Index implements RecordReader {
} }
// it's possible that the bucket was removed from the cache (if searching for a bucket with space scanned many buckets) // it's possible that the bucket was removed from the cache (if searching for a bucket with space scanned many buckets)
bucket = getBucket(index); bucket = getBucket(session, index);
bucket.setNext(session, free); bucket.setNext(session, free);
free = nextFree; free = nextFree;
if (getForeignHome(free) >= 0) { if (getForeignHome(session, free) >= 0) {
throw Message.getInternalError("next already linked"); throw Message.getInternalError("next already linked");
} }
index = bucket.getNextBucket(); index = bucket.getNextBucket();
} }
} }
private int getNextFree(int excluding) throws SQLException { private int getNextFree(Session session, int excluding) throws SQLException {
for (int i = head.bucketCount - 1; i >= 0; i--) { for (int i = head.bucketCount - 1; i >= 0; i--) {
LinearHashBucket bucket = getBucket(i); LinearHashBucket bucket = getBucket(session, i);
if (bucket.getRecordSize() >= RECORDS_PER_BUCKET) { if (bucket.getRecordSize() >= RECORDS_PER_BUCKET) {
continue; continue;
} }
if (getForeignHome(i) < 0 && i != excluding) { if (getForeignHome(session, i) < 0 && i != excluding) {
return i; return i;
} }
} }
return -1; return -1;
} }
private int getForeignHome(int bucketId) throws SQLException { private int getForeignHome(Session session, int bucketId) throws SQLException {
LinearHashBucket bucket = getBucket(bucketId); LinearHashBucket bucket = getBucket(session, bucketId);
for (int i = 0; i < bucket.getRecordSize(); i++) { for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry record = bucket.getRecord(i); LinearHashEntry record = bucket.getRecord(i);
if (record.home != bucketId) { if (record.home != bucketId) {
...@@ -247,8 +247,8 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -247,8 +247,8 @@ public class LinearHashIndex extends Index implements RecordReader {
// moves all records of a bucket to the array (including chained) // moves all records of a bucket to the array (including chained)
private void moveOut(Session session, int home, ObjectArray storeIn) throws SQLException { private void moveOut(Session session, int home, ObjectArray storeIn) throws SQLException {
LinearHashBucket bucket = getBucket(home); LinearHashBucket bucket = getBucket(session, home);
int foreign = getForeignHome(home); int foreign = getForeignHome(session, home);
while (true) { while (true) {
for (int i = 0; i < bucket.getRecordSize(); i++) { for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry r = bucket.getRecord(i); LinearHashEntry r = bucket.getRecord(i);
...@@ -263,7 +263,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -263,7 +263,7 @@ public class LinearHashIndex extends Index implements RecordReader {
// and therefore all home records have been found // and therefore all home records have been found
// (and it would be an error to set next to -1) // (and it would be an error to set next to -1)
moveOut(session, foreign, storeIn); moveOut(session, foreign, storeIn);
if(SysProperties.CHECK && getBucket(foreign).getNextBucket() != -1) { if(SysProperties.CHECK && getBucket(session, foreign).getNextBucket() != -1) {
throw Message.getInternalError("moveOut "+foreign); throw Message.getInternalError("moveOut "+foreign);
} }
return; return;
...@@ -276,7 +276,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -276,7 +276,7 @@ public class LinearHashIndex extends Index implements RecordReader {
break; break;
} }
bucket.setNext(session, -1); bucket.setNext(session, -1);
bucket = getBucket(next); bucket = getBucket(session, next);
} }
} }
...@@ -315,10 +315,10 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -315,10 +315,10 @@ public class LinearHashIndex extends Index implements RecordReader {
// } // }
} }
private boolean isEquals(LinearHashEntry r, int hash, Value key) throws SQLException { private boolean isEquals(Session session, LinearHashEntry r, int hash, Value key) throws SQLException {
if (r.hash == hash) { if (r.hash == hash) {
if(r.key == null) { if(r.key == null) {
r.key = getKey(tableData.getRow(r.value)); r.key = getKey(tableData.getRow(session, r.value));
} }
if(database.compareTypeSave(r.key, key)==0) { if(database.compareTypeSave(r.key, key)==0) {
return true; return true;
...@@ -327,21 +327,21 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -327,21 +327,21 @@ public class LinearHashIndex extends Index implements RecordReader {
return false; return false;
} }
private int get(Value key) throws SQLException { private int get(Session session, Value key) throws SQLException {
int hash = key.hashCode(); int hash = key.hashCode();
int home = getPos(hash); int home = getPos(hash);
LinearHashBucket bucket = getBucket(home); LinearHashBucket bucket = getBucket(session, home);
while (true) { while (true) {
for (int i = 0; i < bucket.getRecordSize(); i++) { for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry r = bucket.getRecord(i); LinearHashEntry r = bucket.getRecord(i);
if(isEquals(r, hash, key)) { if(isEquals(session, r, hash, key)) {
return r.value; return r.value;
} }
} }
if (bucket.getNextBucket() < 0) { if (bucket.getNextBucket() < 0) {
return -1; return -1;
} }
bucket = getBucket(bucket.getNextBucket()); bucket = getBucket(session, bucket.getNextBucket());
} }
} }
...@@ -364,10 +364,10 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -364,10 +364,10 @@ public class LinearHashIndex extends Index implements RecordReader {
int home = getPos(hash); int home = getPos(hash);
int now = home; int now = home;
while (true) { while (true) {
LinearHashBucket bucket = getBucket(now); LinearHashBucket bucket = getBucket(session, now);
for (int i = 0; i < bucket.getRecordSize(); i++) { for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry r = bucket.getRecord(i); LinearHashEntry r = bucket.getRecord(i);
if(isEquals(r, hash, key)) { if(isEquals(session, r, hash, key)) {
removeRecord(session, bucket, i); removeRecord(session, bucket, i);
if (home != now) { if (home != now) {
ObjectArray old = new ObjectArray(); ObjectArray old = new ObjectArray();
...@@ -389,7 +389,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -389,7 +389,7 @@ public class LinearHashIndex extends Index implements RecordReader {
return i * blocksPerBucket + firstBucketBlock; return i * blocksPerBucket + firstBucketBlock;
} }
private LinearHashBucket getBucket(int i) throws SQLException { private LinearHashBucket getBucket(Session session, int i) throws SQLException {
readCount++; readCount++;
if(SysProperties.CHECK && i >= head.bucketCount) { if(SysProperties.CHECK && i >= head.bucketCount) {
throw Message.getInternalError("get="+i+" max="+head.bucketCount); throw Message.getInternalError("get="+i+" max="+head.bucketCount);
...@@ -398,7 +398,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -398,7 +398,7 @@ public class LinearHashIndex extends Index implements RecordReader {
// return (LinearHashBucket) buckets.get(i); // return (LinearHashBucket) buckets.get(i);
i = getBlockId(i); i = getBlockId(i);
// System.out.println("getBucket "+i); // System.out.println("getBucket "+i);
LinearHashBucket bucket = (LinearHashBucket) storage.getRecord(i); LinearHashBucket bucket = (LinearHashBucket) storage.getRecord(session, i);
return bucket; return bucket;
} }
...@@ -430,7 +430,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -430,7 +430,7 @@ public class LinearHashIndex extends Index implements RecordReader {
storage.updateRecord(session, bucket); storage.updateRecord(session, bucket);
} }
public Record read(DataPage s) throws SQLException { public Record read(Session session, DataPage s) throws SQLException {
char c = (char)s.readByte(); char c = (char)s.readByte();
if (c == 'B') { if (c == 'B') {
return new LinearHashBucket(this, s); return new LinearHashBucket(this, s);
...@@ -449,7 +449,7 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -449,7 +449,7 @@ public class LinearHashIndex extends Index implements RecordReader {
public void add(Session session, Row row) throws SQLException { public void add(Session session, Row row) throws SQLException {
Value key = getKey(row); Value key = getKey(row);
if(get(key) != -1) { if(get(session, key) != -1) {
// TODO index duplicate key for hash indexes: is this allowed? // TODO index duplicate key for hash indexes: is this allowed?
throw getDuplicateKeyException(); throw getDuplicateKeyException();
} }
...@@ -482,11 +482,11 @@ public class LinearHashIndex extends Index implements RecordReader { ...@@ -482,11 +482,11 @@ public class LinearHashIndex extends Index implements RecordReader {
// TODO hash index: should additionally check if values are the same // TODO hash index: should additionally check if values are the same
throw Message.getInternalError(); throw Message.getInternalError();
} }
int key = get(getKey(first)); int key = get(session, getKey(first));
if(key == -1) { if(key == -1) {
return new LinearHashCursor(null); return new LinearHashCursor(null);
} }
return new LinearHashCursor(tableData.getRow(key)); return new LinearHashCursor(tableData.getRow(session, key));
} }
public long getCost(int[] masks) throws SQLException { public long getCost(int[] masks) throws SQLException {
......
...@@ -6,6 +6,7 @@ package org.h2.index; ...@@ -6,6 +6,7 @@ package org.h2.index;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
...@@ -16,11 +17,17 @@ import org.h2.result.SearchRow; ...@@ -16,11 +17,17 @@ import org.h2.result.SearchRow;
public class ScanCursor implements Cursor { public class ScanCursor implements Cursor {
private ScanIndex scan; private ScanIndex scan;
private Row row; private Row row;
private final Session session;
ScanCursor(ScanIndex scan) { ScanCursor(Session session, ScanIndex scan) {
this.session = session;
this.scan = scan; this.scan = scan;
row = null; row = null;
} }
Session getSession() {
return session;
}
public Row get() { public Row get() {
return row; return row;
...@@ -35,7 +42,7 @@ public class ScanCursor implements Cursor { ...@@ -35,7 +42,7 @@ public class ScanCursor implements Cursor {
} }
public boolean next() throws SQLException { public boolean next() throws SQLException {
row = scan.getNextRow(row); row = scan.getNextRow(session, row);
return row != null; return row != null;
} }
} }
...@@ -81,9 +81,9 @@ public class ScanIndex extends Index { ...@@ -81,9 +81,9 @@ public class ScanIndex extends Index {
} }
} }
public Row getRow(int key) throws SQLException { public Row getRow(Session session, int key) throws SQLException {
if(storage != null) { if(storage != null) {
return (Row) storage.getRecord(key); return (Row) storage.getRecord(session, key);
} }
return (Row) rows.get(key); return (Row) rows.get(key);
} }
...@@ -139,7 +139,7 @@ public class ScanIndex extends Index { ...@@ -139,7 +139,7 @@ public class ScanIndex extends Index {
} }
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException { public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
return new ScanCursor(this); return new ScanCursor(session, this);
} }
public long getCost(int[] masks) throws SQLException { public long getCost(int[] masks) throws SQLException {
...@@ -150,7 +150,7 @@ public class ScanIndex extends Index { ...@@ -150,7 +150,7 @@ public class ScanIndex extends Index {
return cost; return cost;
} }
Row getNextRow(Row row) throws SQLException { Row getNextRow(Session session, Row row) throws SQLException {
if(storage == null) { if(storage == null) {
int key; int key;
if (row == null) { if (row == null) {
...@@ -173,7 +173,7 @@ public class ScanIndex extends Index { ...@@ -173,7 +173,7 @@ public class ScanIndex extends Index {
if (pos < 0) { if (pos < 0) {
return null; return null;
} }
return (Row) storage.getRecord(pos); return (Row) storage.getRecord(session, pos);
} }
public int getColumnIndex(Column col) { public int getColumnIndex(Column col) {
......
...@@ -148,9 +148,11 @@ ALTER TABLE TEST ADD CREATEDATE TIMESTAMP ...@@ -148,9 +148,11 @@ ALTER TABLE TEST ADD CREATEDATE TIMESTAMP
" "
"Commands (DDL)","ALTER TABLE ADD CONSTRAINT"," "Commands (DDL)","ALTER TABLE ADD CONSTRAINT","
ALTER TABLE tableName ADD constraint ALTER TABLE tableName ADD constraint [CHECK|NOCHECK]
"," ","
Adds a constraint to a table. Adds a constraint to a table.
If NOCHECK is specified, the existing data is not checked for consistency (the default is to check consistency for existing data).
It is not possible to disable checking for unique constraints.
"," ","
ALTER TABLE TEST ADD CONSTRAINT NAME_UNIQUE UNIQUE(NAME) ALTER TABLE TEST ADD CONSTRAINT NAME_UNIQUE UNIQUE(NAME)
" "
...@@ -243,11 +245,11 @@ ALTER TABLE TEST DROP CONSTRAINT UNIQUE_NAME ...@@ -243,11 +245,11 @@ ALTER TABLE TEST DROP CONSTRAINT UNIQUE_NAME
" "
"Commands (DDL)","ALTER TABLE SET"," "Commands (DDL)","ALTER TABLE SET","
ALTER TABLE tableName SET REFERENTIAL_INTEGRITY [TRUE|FALSE] 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.
Enabling it does not check existing data. Enabling it does not check existing data, except if CHECK is specified.
Use SET REFERENTIAL_INTEGRITY to disable it for all tables. Use SET REFERENTIAL_INTEGRITY to disable it for all tables (the global flag and the flag for each table are independent).
"," ","
ALTER TABLE TEST SET REFERENTIAL_INTEGRITY FALSE ALTER TABLE TEST SET REFERENTIAL_INTEGRITY FALSE
" "
......
...@@ -64,6 +64,6 @@ public class Row extends Record implements SearchRow { ...@@ -64,6 +64,6 @@ public class Row extends Record implements SearchRow {
public int getMemorySize() { public int getMemorySize() {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4; return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4;
} }
} }
...@@ -255,8 +255,8 @@ public class Schema extends DbObject { ...@@ -255,8 +255,8 @@ public class Schema extends DbObject {
map.remove(objName); map.remove(objName);
} }
public TableData createTable(String tempName, int id, ObjectArray newColumns, boolean persistent) throws SQLException { public TableData createTable(String tempName, int id, ObjectArray newColumns, boolean persistent, boolean clustered) throws SQLException {
return new TableData(this, tempName, id, newColumns, persistent); return new TableData(this, tempName, id, newColumns, persistent, clustered);
} }
public TableLink createTableLink(int id, String tableName, String driver, String url, String user, String password, String originalTable, boolean emitUpdates, boolean force) throws SQLException { public TableLink createTableLink(int id, String tableName, String driver, String url, String user, String password, String originalTable, boolean emitUpdates, boolean force) throws SQLException {
......
...@@ -358,7 +358,7 @@ public class DiskFile implements CacheWriter { ...@@ -358,7 +358,7 @@ public class DiskFile implements CacheWriter {
return ((long)block * BLOCK_SIZE) + OFFSET; return ((long)block * BLOCK_SIZE) + OFFSET;
} }
synchronized Record getRecordIfStored(int pos, RecordReader reader, int storageId) throws SQLException { synchronized Record getRecordIfStored(Session session, int pos, RecordReader reader, int storageId) throws SQLException {
try { try {
int owner = getPageOwner(getPage(pos)); int owner = getPageOwner(getPage(pos));
if(owner != storageId) { if(owner != storageId) {
...@@ -377,10 +377,10 @@ public class DiskFile implements CacheWriter { ...@@ -377,10 +377,10 @@ public class DiskFile implements CacheWriter {
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
return getRecord(pos, reader, storageId); return getRecord(session, pos, reader, storageId);
} }
synchronized Record getRecord(int pos, RecordReader reader, int storageId) throws SQLException { synchronized Record getRecord(Session session, int pos, RecordReader reader, int storageId) throws SQLException {
if(file == null) { if(file == null) {
throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF); throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF);
} }
...@@ -413,7 +413,7 @@ public class DiskFile implements CacheWriter { ...@@ -413,7 +413,7 @@ public class DiskFile implements CacheWriter {
s.readInt(); s.readInt();
} }
s.check(blockCount*BLOCK_SIZE); s.check(blockCount*BLOCK_SIZE);
Record r = reader.read(s); Record r = reader.read(session, s);
r.setStorageId(storageId); r.setStorageId(storageId);
r.setPos(pos); r.setPos(pos);
r.setBlockCount(blockCount); r.setBlockCount(blockCount);
......
...@@ -6,6 +6,7 @@ package org.h2.store; ...@@ -6,6 +6,7 @@ package org.h2.store;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.util.CacheObject; import org.h2.util.CacheObject;
/** /**
...@@ -13,6 +14,7 @@ import org.h2.util.CacheObject; ...@@ -13,6 +14,7 @@ import org.h2.util.CacheObject;
*/ */
public abstract class Record extends CacheObject { public abstract class Record extends CacheObject {
private boolean deleted; private boolean deleted;
private int sessionId;
private int storageId; private int storageId;
private int lastLog = LogSystem.LOG_WRITTEN; private int lastLog = LogSystem.LOG_WRITTEN;
private int lastPos = LogSystem.LOG_WRITTEN; private int lastPos = LogSystem.LOG_WRITTEN;
...@@ -34,9 +36,19 @@ public abstract class Record extends CacheObject { ...@@ -34,9 +36,19 @@ public abstract class Record extends CacheObject {
return false; return false;
} }
public void setDeleted(boolean deleted) { public void setDeleted(Session session, boolean deleted) {
this.sessionId = session.getId();
this.deleted = deleted; this.deleted = deleted;
} }
public int getSessionId() {
int testing;
return sessionId;
}
public void commit() {
this.sessionId = 0;
}
public boolean getDeleted() { public boolean getDeleted() {
return deleted; return deleted;
......
...@@ -5,10 +5,11 @@ ...@@ -5,10 +5,11 @@
package org.h2.store; package org.h2.store;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Session;
/** /**
* @author Thomas * @author Thomas
*/ */
public interface RecordReader { public interface RecordReader {
Record read(DataPage s) throws SQLException; Record read(Session session, DataPage s) throws SQLException;
} }
...@@ -54,12 +54,12 @@ public class Storage { ...@@ -54,12 +54,12 @@ public class Storage {
recordCount++; recordCount++;
} }
public Record getRecord(int pos) throws SQLException { public Record getRecord(Session session, int pos) throws SQLException {
return file.getRecord(pos, reader, id); return file.getRecord(session, pos, reader, id);
} }
public Record getRecordIfStored(int pos) throws SQLException { public Record getRecordIfStored(Session session, int pos) throws SQLException {
return file.getRecordIfStored(pos, reader, id); return file.getRecordIfStored(session, pos, reader, id);
} }
/** /**
...@@ -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(false); record.setDeleted(session, 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(false); record.setDeleted(session, 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);
...@@ -133,11 +133,11 @@ public class Storage { ...@@ -133,11 +133,11 @@ public class Storage {
} }
public void removeRecord(Session session, int pos) throws SQLException { public void removeRecord(Session session, int pos) throws SQLException {
Record record = getRecord(pos); Record record = getRecord(session, pos);
if(SysProperties.CHECK && record.getDeleted()) { if(SysProperties.CHECK && record.getDeleted()) {
throw Message.getInternalError("duplicate delete " + pos); throw Message.getInternalError("duplicate delete " + pos);
} }
record.setDeleted(true); record.setDeleted(session, true);
int blockCount = record.getBlockCount(); int blockCount = record.getBlockCount();
free(pos, blockCount); free(pos, blockCount);
recordCount--; recordCount--;
......
...@@ -73,17 +73,17 @@ public class UndoLogRecord { ...@@ -73,17 +73,17 @@ public class UndoLogRecord {
} }
break; break;
case DELETE: case DELETE:
try { try {
row.setPos(0); row.setPos(0);
table.addRow(session, row); table.addRow(session, row);
} catch(SQLException e) { } catch(SQLException e) {
if(session.getDatabase().getLockMode()==Constants.LOCK_MODE_OFF && e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) { if(session.getDatabase().getLockMode()==Constants.LOCK_MODE_OFF && e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
// it might have been added by another thread // it might have been added by another thread
// ignore // ignore
} else { } else {
throw e; throw e;
}
} }
}
break; break;
default: default:
throw Message.getInternalError("op=" + operation); throw Message.getInternalError("op=" + operation);
...@@ -140,4 +140,8 @@ public class UndoLogRecord { ...@@ -140,4 +140,8 @@ public class UndoLogRecord {
public Table getTable() { public Table getTable() {
return table; return table;
} }
public void commit() {
row.commit();
}
} }
...@@ -450,8 +450,14 @@ public abstract class Table extends SchemaObject { ...@@ -450,8 +450,14 @@ public abstract class Table extends SchemaObject {
return false; return false;
} }
public void setCheckForeignKeyConstraints(boolean b) { public void setCheckForeignKeyConstraints(Session session, boolean enabled, boolean checkExisting) throws SQLException {
checkForeignKeyConstraints = b; if(enabled && checkExisting) {
for(int i=0; i<constraints.size(); i++) {
Constraint c = (Constraint) constraints.get(i);
c.checkExistingData(session);
}
}
checkForeignKeyConstraints = enabled;
} }
public boolean getCheckForeignKeyConstraints() { public boolean getCheckForeignKeyConstraints() {
...@@ -488,4 +494,8 @@ public abstract class Table extends SchemaObject { ...@@ -488,4 +494,8 @@ public abstract class Table extends SchemaObject {
this.onCommitTruncate = onCommitTruncate; this.onCommitTruncate = onCommitTruncate;
} }
public boolean isClustered() {
return false;
}
} }
...@@ -48,15 +48,19 @@ public class TableData extends Table implements RecordReader { ...@@ -48,15 +48,19 @@ public class TableData extends Table implements RecordReader {
private boolean globalTemporary; private boolean globalTemporary;
private final ObjectArray indexes = new ObjectArray(); private final ObjectArray indexes = new ObjectArray();
private long lastModificationId; private long lastModificationId;
private final boolean clustered;
public TableData(Schema schema, String tableName, int id, ObjectArray columns, public TableData(Schema schema, String tableName, int id, ObjectArray columns,
boolean persistent) throws SQLException { boolean persistent, boolean clustered) throws SQLException {
super(schema, id, tableName, persistent); super(schema, id, tableName, persistent);
Column[] cols = new Column[columns.size()]; Column[] cols = new Column[columns.size()];
columns.toArray(cols); columns.toArray(cols);
setColumns(cols); setColumns(cols);
scanIndex = new ScanIndex(this, id, cols, IndexType.createScan(persistent)); this.clustered = clustered;
indexes.add(scanIndex); if(!clustered) {
scanIndex = new ScanIndex(this, id, cols, IndexType.createScan(persistent));
indexes.add(scanIndex);
}
traceLock = database.getTrace(Trace.LOCK); traceLock = database.getTrace(Trace.LOCK);
} }
...@@ -67,8 +71,8 @@ public class TableData extends Table implements RecordReader { ...@@ -67,8 +71,8 @@ public class TableData extends Table implements RecordReader {
} }
} }
public Row getRow(int key) throws SQLException { public Row getRow(Session session, int key) throws SQLException {
return scanIndex.getRow(key); return scanIndex.getRow(session, key);
} }
public void addRow(Session session, Row row) throws SQLException { public void addRow(Session session, Row row) throws SQLException {
...@@ -152,7 +156,7 @@ public class TableData extends Table implements RecordReader { ...@@ -152,7 +156,7 @@ public class TableData extends Table implements RecordReader {
index = new TreeIndex(this, indexId, indexName, cols, indexType); index = new TreeIndex(this, indexId, indexName, cols, indexType);
} }
} }
if(index.needRebuild()) { if(index.needRebuild() && rowCount > 0) {
try { try {
Index scan = getScanIndex(session); Index scan = getScanIndex(session);
long remaining = scan.getRowCount(); long remaining = scan.getRowCount();
...@@ -278,6 +282,9 @@ public class TableData extends Table implements RecordReader { ...@@ -278,6 +282,9 @@ public class TableData extends Table implements RecordReader {
if(lockMode == Constants.LOCK_MODE_OFF) { if(lockMode == Constants.LOCK_MODE_OFF) {
return; return;
} }
if(SysProperties.MVCC) {
return;
}
long max = System.currentTimeMillis() + session.getLockTimeout(); long max = System.currentTimeMillis() + session.getLockTimeout();
synchronized(database) { synchronized(database) {
while (true) { while (true) {
...@@ -401,7 +408,7 @@ public class TableData extends Table implements RecordReader { ...@@ -401,7 +408,7 @@ public class TableData extends Table implements RecordReader {
} }
} }
public Record read(DataPage s) throws SQLException { public Record read(Session session, DataPage s) throws SQLException {
int len = s.readInt(); int len = s.readInt();
Value[] data = new Value[len]; Value[] data = new Value[len];
for(int i=0; i<len; i++) { for(int i=0; i<len; i++) {
...@@ -475,5 +482,9 @@ public class TableData extends Table implements RecordReader { ...@@ -475,5 +482,9 @@ public class TableData extends Table implements RecordReader {
public long getMaxDataModificationId() { public long getMaxDataModificationId() {
return lastModificationId; return lastModificationId;
} }
public boolean isClustered() {
return clustered;
}
} }
...@@ -303,7 +303,14 @@ public class TableFilter implements ColumnResolver { ...@@ -303,7 +303,14 @@ public class TableFilter implements ColumnResolver {
public Row get() throws SQLException { public Row get() throws SQLException {
if(current == null && currentSearchRow != null) { if(current == null && currentSearchRow != null) {
current = cursor.get(); if(table.isClustered()) {
current = table.getTemplateRow();
for(int i=0; i<currentSearchRow.getColumnCount(); i++) {
current.setValue(i, currentSearchRow.getValue(i));
}
} else {
current = cursor.get();
}
} }
return current; return current;
} }
......
...@@ -94,9 +94,15 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2 ...@@ -94,9 +94,15 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/* /*
add to maven add MVCC
test and document fulltext search
improve documentation of mixed mode
docs: CACHE_SIZE=200000 maybe too much? clustered tables (test)
add to maven
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
......
...@@ -31,7 +31,7 @@ public class WebServlet extends HttpServlet { ...@@ -31,7 +31,7 @@ public class WebServlet extends HttpServlet {
Enumeration en = config.getInitParameterNames(); Enumeration en = config.getInitParameterNames();
ArrayList list = new ArrayList(); ArrayList list = new ArrayList();
while(en.hasMoreElements()) { while(en.hasMoreElements()) {
String name = (String) en.nextElement(); String name = en.nextElement().toString();
String value = config.getInitParameter(name); String value = config.getInitParameter(name);
if(!name.startsWith("-")) { if(!name.startsWith("-")) {
name = "-" + name; name = "-" + name;
...@@ -90,13 +90,13 @@ public class WebServlet extends HttpServlet { ...@@ -90,13 +90,13 @@ public class WebServlet extends HttpServlet {
Properties attributes = new Properties(); Properties attributes = new Properties();
Enumeration en = req.getAttributeNames(); Enumeration en = req.getAttributeNames();
while(en.hasMoreElements()) { while(en.hasMoreElements()) {
String name = (String) en.nextElement(); String name = en.nextElement().toString();
String value = (String) req.getAttribute(name); String value = req.getAttribute(name).toString();
attributes.put(name, value); attributes.put(name, value);
} }
en = req.getParameterNames(); en = req.getParameterNames();
while(en.hasMoreElements()) { while(en.hasMoreElements()) {
String name = (String) en.nextElement(); String name = en.nextElement().toString();
String value = req.getParameter(name); String value = req.getParameter(name);
attributes.put(name, value); attributes.put(name, value);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论