提交 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.
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
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.
</p><p>
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.
<h3>Version 1.0 (Current)</h3>
<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>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.
......@@ -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>Shutdown compact
</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>Stop the server: close all open databases first
</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>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: allow tables without primary key
</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.
</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>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></ul>
......
......@@ -258,7 +258,7 @@ To start the Server from the command line with the default settings, run
<pre>
java org.h2.tools.Server
</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>
java org.h2.tools.Server -?
</pre>
......
......@@ -3359,7 +3359,7 @@ public class Parser {
columns.add(new Column(cols[i], Value.STRING, 0, 0));
}
int id = database.allocateObjectId(true, true);
recursiveTable = schema.createTable(tempViewName, id, columns, false);
recursiveTable = schema.createTable(tempViewName, id, columns, false, false);
recursiveTable.setTemporary(true);
session.addLocalTempTable(recursiveTable);
String querySQL = StringCache.getNew(sqlCommand.substring(parseIndex));
......@@ -3842,6 +3842,11 @@ public class Parser {
AlterTableAddConstraint command = new AlterTableAddConstraint(session, table.getSchema());
command.setTableName(table.getName());
command.setType(type);
if(readIf("CHECK")) {
command.setCheckExisting(true);
} else if(readIf("NOCHECK")) {
command.setCheckExisting(false);
}
return command;
} else if(readIf("RENAME")) {
read("TO");
......@@ -4045,6 +4050,12 @@ public class Parser {
}
return null;
}
if(readIf("NOCHECK")) {
command.setCheckExisting(false);
} else {
readIf("CHECK");
command.setCheckExisting(true);
}
command.setTableName(tableName);
command.setConstraintName(name);
command.setComment(comment);
......@@ -4193,6 +4204,9 @@ public class Parser {
read("LOGGED");
}
}
if(readIf("CLUSTERED")) {
command.setClustered(true);
}
return command;
}
......
......@@ -47,6 +47,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
private Expression checkExpression;
private Index index, refIndex;
private String comment;
private boolean checkExisting;
public AlterTableAddConstraint(Session session, Schema schema) {
super(session, schema);
......@@ -81,6 +82,9 @@ public class AlterTableAddConstraint extends SchemaCommand {
check.setExpression(checkExpression);
check.setTableFilter(filter);
constraint = check;
if(checkExisting) {
check.checkExistingData(session);
}
break;
}
case UNIQUE: {
......@@ -151,6 +155,9 @@ public class AlterTableAddConstraint extends SchemaCommand {
ref.setRefTable(refTable);
ref.setRefColumns(refColumns);
ref.setRefIndex(refIndex, isRefOwner);
if(checkExisting) {
ref.checkExistingData(session);
}
constraint = ref;
refTable.addConstraint(constraint);
ref.setDeleteAction(session, deleteAction);
......@@ -158,10 +165,10 @@ public class AlterTableAddConstraint extends SchemaCommand {
break;
}
case REFERENTIAL_INTEGRITY_TRUE:
table.setCheckForeignKeyConstraints(true);
table.setCheckForeignKeyConstraints(session, true, checkExisting);
return 0;
case REFERENTIAL_INTEGRITY_FALSE:
table.setCheckForeignKeyConstraints(false);
table.setCheckForeignKeyConstraints(session, false, false);
return 0;
default:
throw Message.getInternalError("type="+type);
......@@ -304,4 +311,8 @@ public class AlterTableAddConstraint extends SchemaCommand {
this.comment = comment;
}
public void setCheckExisting(boolean b) {
this.checkExisting = b;
}
}
......@@ -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
// 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;
TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent);
TableData newTable = getSchema().createTable(tempName, id, newColumns, persistent, false);
newTable.setComment(table.getComment());
execute(newTable.getCreateSQL());
newTable = (TableData) newTable.getSchema().getTableOrView(session, newTable.getName());
......@@ -274,7 +274,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
buff.append(" FROM ");
buff.append(table.getSQL());
String sql = buff.toString();
newTable.setCheckForeignKeyConstraints(false);
newTable.setCheckForeignKeyConstraints(session, false, false);
try {
execute(sql);
} catch(SQLException e) {
......@@ -282,7 +282,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
execute("DROP TABLE " + newTable.getSQL());
throw e;
}
newTable.setCheckForeignKeyConstraints(true);
newTable.setCheckForeignKeyConstraints(session, true, false);
String tableName = table.getName();
table.setModified();
for(int i=0; i<columns.length; i++) {
......
......@@ -41,6 +41,7 @@ public class CreateTable extends SchemaCommand {
private boolean onCommitTruncate;
private Query asQuery;
private String comment;
private boolean clustered;
public CreateTable(Session session, Schema schema) {
super(session, schema);
......@@ -120,7 +121,7 @@ public class CreateTable extends SchemaCommand {
}
}
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.setTemporary(temporary);
table.setGlobalTemporary(globalTemporary);
......@@ -235,4 +236,8 @@ public class CreateTable extends SchemaCommand {
this.comment = comment;
}
public void setClustered(boolean clustered) {
this.clustered = clustered;
}
}
......@@ -21,44 +21,45 @@ import org.h2.message.TraceSystem;
*/
public class SysProperties {
public static final int MIN_WRITE_DELAY = SysProperties.getIntSetting("h2.minWriteDelay", 5);
public static final boolean CHECK = SysProperties.getBooleanSetting("h2.check", true);
public static final boolean CHECK2 = SysProperties.getBooleanSetting("h2.check2", false);
public static final boolean OPTIMIZE_MIN_MAX = SysProperties.getBooleanSetting("h2.optimizeMinMax", true);
public static final boolean OPTIMIZE_IN = SysProperties.getBooleanSetting("h2.optimizeIn", true);
public static final int REDO_BUFFER_SIZE = SysProperties.getIntSetting("h2.redoBufferSize", 256 * 1024);
public static final boolean RECOMPILE_ALWAYS = SysProperties.getBooleanSetting("h2.recompileAlways", false);
public static final boolean OPTIMIZE_SUBQUERY_CACHE = SysProperties.getBooleanSetting("h2.optimizeSubqueryCache", true);
public static final boolean OVERFLOW_EXCEPTIONS = SysProperties.getBooleanSetting("h2.overflowExceptions", true);
public static final boolean LOG_ALL_ERRORS = SysProperties.getBooleanSetting("h2.logAllErrors", false);
public static final String LOG_ALL_ERRORS_FILE = SysProperties.getStringSetting("h2.logAllErrorsFile", "h2errors.txt");
public static final int SERVER_CACHED_OBJECTS = SysProperties.getIntSetting("h2.serverCachedObjects", 64);
public static final int SERVER_SMALL_RESULT_SET_SIZE = SysProperties.getIntSetting("h2.serverSmallResultSetSize", 100);
public static final int EMERGENCY_SPACE_INITIAL = SysProperties.getIntSetting("h2.emergencySpaceInitial", 1 * 1024 * 1024);
public static final int EMERGENCY_SPACE_MIN = SysProperties.getIntSetting("h2.emergencySpaceMin", 128 * 1024);
public static final boolean OBJECT_CACHE = SysProperties.getBooleanSetting("h2.objectCache", true);
public static final int OBJECT_CACHE_SIZE = SysProperties.getIntSetting("h2.objectCacheSize", 1024);
public static final int OBJECT_CACHE_MAX_PER_ELEMENT_SIZE = SysProperties.getIntSetting("h2.objectCacheMaxPerElementSize", 4096);
public static final String CLIENT_TRACE_DIRECTORY = SysProperties.getStringSetting("h2.clientTraceDirectory", "trace.db/");
public static final int MAX_FILE_RETRY = Math.max(1, SysProperties.getIntSetting("h2.maxFileRetry", 16));
public static final boolean ALLOW_BIG_DECIMAL_EXTENSIONS = SysProperties.getBooleanSetting("h2.allowBigDecimalExtensions", false);
public static final boolean INDEX_LOOKUP_NEW = SysProperties.getBooleanSetting("h2.indexLookupNew", true);
public static final boolean TRACE_IO = SysProperties.getBooleanSetting("h2.traceIO", false);
public static final int DATASOURCE_TRACE_LEVEL = SysProperties.getIntSetting("h2.dataSourceTraceLevel", TraceSystem.ERROR);
public static final int CACHE_SIZE_DEFAULT = SysProperties.getIntSetting("h2.cacheSizeDefault", 16 * 1024);
public static final int CACHE_SIZE_INDEX_SHIFT = SysProperties.getIntSetting("h2.cacheSizeIndexShift", 3);
public static final int DEFAULT_MAX_MEMORY_UNDO = SysProperties.getIntSetting("h2.defaultMaxMemoryUndo", 50000);
public static final boolean OPTIMIZE_NOT = SysProperties.getBooleanSetting("h2.optimizeNot", true);
public static final boolean OPTIMIZE_TWO_EQUALS = SysProperties.getBooleanSetting("h2.optimizeTwoEquals", true);
public static final int DEFAULT_LOCK_MODE = SysProperties.getIntSetting("h2.defaultLockMode", Constants.LOCK_MODE_READ_COMMITTED);
public static boolean runFinalize = SysProperties.getBooleanSetting("h2.runFinalize", true);
public static String scriptDirectory = SysProperties.getStringSetting("h2.scriptDirectory", "");
public static String baseDir = SysProperties.getStringSetting("h2.baseDir", null);
public static boolean multiThreadedKernel = SysProperties.getBooleanSetting("h2.multiThreadedKernel", false);
public static boolean lobCloseBetweenReads = SysProperties.getBooleanSetting("h2.lobCloseBetweenReads", false);
public static final boolean MVCC = getBooleanSetting("h2.mvcc", false);
public static final int MIN_WRITE_DELAY = getIntSetting("h2.minWriteDelay", 5);
public static final boolean CHECK = getBooleanSetting("h2.check", true);
public static final boolean CHECK2 = getBooleanSetting("h2.check2", false);
public static final boolean OPTIMIZE_MIN_MAX = getBooleanSetting("h2.optimizeMinMax", true);
public static final boolean OPTIMIZE_IN = getBooleanSetting("h2.optimizeIn", true);
public static final int REDO_BUFFER_SIZE = getIntSetting("h2.redoBufferSize", 256 * 1024);
public static final boolean RECOMPILE_ALWAYS = getBooleanSetting("h2.recompileAlways", false);
public static final boolean OPTIMIZE_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true);
public static final boolean OVERFLOW_EXCEPTIONS = getBooleanSetting("h2.overflowExceptions", true);
public static final boolean LOG_ALL_ERRORS = getBooleanSetting("h2.logAllErrors", false);
public static final String LOG_ALL_ERRORS_FILE = getStringSetting("h2.logAllErrorsFile", "h2errors.txt");
public static final int SERVER_CACHED_OBJECTS = getIntSetting("h2.serverCachedObjects", 64);
public static final int SERVER_SMALL_RESULT_SET_SIZE = getIntSetting("h2.serverSmallResultSetSize", 100);
public static final int EMERGENCY_SPACE_INITIAL = getIntSetting("h2.emergencySpaceInitial", 1 * 1024 * 1024);
public static final int EMERGENCY_SPACE_MIN = getIntSetting("h2.emergencySpaceMin", 128 * 1024);
public static final boolean OBJECT_CACHE = getBooleanSetting("h2.objectCache", true);
public static final int OBJECT_CACHE_SIZE = getIntSetting("h2.objectCacheSize", 1024);
public static final int OBJECT_CACHE_MAX_PER_ELEMENT_SIZE = getIntSetting("h2.objectCacheMaxPerElementSize", 4096);
public static final String CLIENT_TRACE_DIRECTORY = getStringSetting("h2.clientTraceDirectory", "trace.db/");
public static final int MAX_FILE_RETRY = Math.max(1, getIntSetting("h2.maxFileRetry", 16));
public static final boolean ALLOW_BIG_DECIMAL_EXTENSIONS = getBooleanSetting("h2.allowBigDecimalExtensions", false);
public static final boolean INDEX_LOOKUP_NEW = getBooleanSetting("h2.indexLookupNew", true);
public static final boolean TRACE_IO = getBooleanSetting("h2.traceIO", false);
public static final int DATASOURCE_TRACE_LEVEL = getIntSetting("h2.dataSourceTraceLevel", TraceSystem.ERROR);
public static final int CACHE_SIZE_DEFAULT = getIntSetting("h2.cacheSizeDefault", 16 * 1024);
public static final int CACHE_SIZE_INDEX_SHIFT = getIntSetting("h2.cacheSizeIndexShift", 3);
public static final int DEFAULT_MAX_MEMORY_UNDO = getIntSetting("h2.defaultMaxMemoryUndo", 50000);
public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true);
public static final boolean OPTIMIZE_TWO_EQUALS = getBooleanSetting("h2.optimizeTwoEquals", true);
public static final int DEFAULT_LOCK_MODE = getIntSetting("h2.defaultLockMode", Constants.LOCK_MODE_READ_COMMITTED);
public static boolean runFinalize = getBooleanSetting("h2.runFinalize", true);
public static String scriptDirectory = getStringSetting("h2.scriptDirectory", "");
public static String baseDir = getStringSetting("h2.baseDir", null);
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
public static final boolean LOB_FILES_IN_DIRECTORIES = SysProperties.getBooleanSetting("h2.lobFilesInDirectories", false);
public static final int LOB_FILES_PER_DIRECTORY = SysProperties.getIntSetting("h2.lobFilesPerDirectory", 256);
public static final boolean LOB_FILES_IN_DIRECTORIES = getBooleanSetting("h2.lobFilesInDirectories", false);
public static final int LOB_FILES_PER_DIRECTORY = getIntSetting("h2.lobFilesPerDirectory", 256);
private static boolean getBooleanSetting(String name, boolean defaultValue) {
String s = System.getProperty(name);
......
......@@ -45,6 +45,7 @@ public abstract class Constraint extends SchemaObject {
public abstract String getCreateSQLWithoutIndexes();
public abstract boolean isBefore();
public abstract String getShortDescription();
public abstract void checkExistingData(Session session) throws SQLException;
public Table getTable() {
return table;
......
......@@ -11,6 +11,7 @@ import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.table.Column;
......@@ -55,6 +56,7 @@ public class ConstraintCheck extends Constraint {
}
buff.append(" CHECK");
buff.append(StringUtils.enclose(expr.getSQL()));
buff.append(" NOCHECK");
return buff.toString();
}
......@@ -112,4 +114,22 @@ public class ConstraintCheck extends Constraint {
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;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.schema.Schema;
......@@ -129,6 +130,7 @@ public class ConstraintReferential extends Constraint {
buff.append(" ON UPDATE ");
appendAction(buff, updateAction);
}
buff.append(" NOCHECK");
return buff.toString();
}
......@@ -531,4 +533,47 @@ public class ConstraintReferential extends Constraint {
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());
}
}
}
......@@ -130,4 +130,8 @@ public class ConstraintUnique extends Constraint {
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 {
cols.add(new Column("HEAD", Value.INT, 0, 0));
cols.add(new Column("TYPE", Value.INT, 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);
objectIds.set(0);
// there could be views on system tables, so they must be added first
......@@ -1514,4 +1514,8 @@ public class Database implements DataHandler {
return referentialIntegrity;
}
public boolean isStarting() {
return starting;
}
}
......@@ -191,6 +191,12 @@ public class Session implements SessionInterface {
logSystem.commit(this);
}
if(undoLog.size() > 0) {
if(SysProperties.MVCC) {
while (undoLog.size() > 0) {
UndoLogRecord entry = undoLog.getAndRemoveLast();
entry.commit();
}
}
undoLog.clear();
}
if(!ddl) {
......@@ -287,7 +293,7 @@ public class Session implements SessionInterface {
// otherwise rollback will try to rollback a not-inserted row
if(SysProperties.CHECK) {
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) {
throw Message.getInternalError();
}
......
......@@ -214,6 +214,10 @@ public class Comparison extends Condition {
return BIGGER;
case SMALLER:
return BIGGER_EQUAL;
case IS_NULL:
return IS_NOT_NULL;
case IS_NOT_NULL:
return IS_NULL;
default:
throw Message.getInternalError("type="+compareType);
}
......
......@@ -7,6 +7,7 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -21,13 +22,19 @@ public class BtreeCursor implements Cursor {
private Row currentRow;
private boolean first;
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.last = last;
first = true;
}
Session getSession() {
return session;
}
void setStackPosition(int position) {
top.position = position;
}
......@@ -55,7 +62,7 @@ public class BtreeCursor implements Cursor {
public Row get() throws SQLException {
if(currentRow == null && currentSearchRow != null) {
currentRow = index.getRow(currentSearchRow.getPos());
currentRow = index.getRow(session, currentSearchRow.getPos());
}
return currentRow;
}
......@@ -71,8 +78,7 @@ public class BtreeCursor implements Cursor {
public boolean next() throws SQLException {
if (first) {
first = false;
return currentSearchRow != null;
}
} else {
top.page.next(this, top.position);
if(currentSearchRow != null && last != null) {
if (index.compareRows(currentSearchRow, last) > 0) {
......@@ -80,6 +86,18 @@ public class BtreeCursor implements Cursor {
currentRow = null;
}
}
}
if(SysProperties.MVCC) {
if(currentSearchRow != null) {
while(true) {
Row r = get();
int sessionId = r.getSessionId();
if(sessionId == 0 || sessionId == session.getId()) {
break;
}
}
}
}
return currentSearchRow != null;
}
......
......@@ -51,12 +51,12 @@ public class BtreeIndex extends Index implements RecordReader {
truncate(session);
needRebuild = true;
} else {
Record rec = storage.getRecordIfStored(headPos);
Record rec = storage.getRecordIfStored(session, headPos);
if(rec != null && (rec instanceof BtreeHead)) {
head = (BtreeHead) rec;
}
if(head != null && head.getConsistent()) {
setRoot((BtreePage) storage.getRecord(head.getRootPosition()));
setRoot((BtreePage) storage.getRecord(session, head.getRootPosition()));
needRebuild = false;
rowCount = table.getRowCount();
} else {
......@@ -114,8 +114,8 @@ public class BtreeIndex extends Index implements RecordReader {
storage.addRecord(session, p, Storage.ALLOCATE_POS);
}
public BtreePage getPage(int i) throws SQLException {
return (BtreePage) storage.getRecord(i);
public BtreePage getPage(Session session, int i) throws SQLException {
return (BtreePage) storage.getRecord(session, i);
}
public void flush(Session session) throws SQLException {
......@@ -178,11 +178,11 @@ public class BtreeIndex extends Index implements RecordReader {
throw Message.getSQLException(ErrorCode.OBJECT_CLOSED);
}
if(first==null) {
BtreeCursor cursor = new BtreeCursor(this, last);
BtreeCursor cursor = new BtreeCursor(session, this, last);
root.first(cursor);
return cursor;
} else {
BtreeCursor cursor = new BtreeCursor(this, last);
BtreeCursor cursor = new BtreeCursor(session, this, last);
if (!root.findFirst(cursor, first)) {
cursor.setCurrentRow(null);
}
......@@ -203,12 +203,12 @@ public class BtreeIndex extends Index implements RecordReader {
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();
if (c == 'N') {
return new BtreeNode(this, s);
} else if (c == 'L') {
return new BtreeLeaf(this, s);
return new BtreeLeaf(this, session, s);
} else if (c == 'H') {
return new BtreeHead(s);
} else {
......@@ -237,8 +237,8 @@ public class BtreeIndex extends Index implements RecordReader {
return rows;
}
public Row getRow(int pos) throws SQLException {
return tableData.getRow(pos);
public Row getRow(Session session, int pos) throws SQLException {
return tableData.getRow(session, pos);
}
private void flushHead(Session session) throws SQLException {
......@@ -297,7 +297,7 @@ public class BtreeIndex extends Index implements RecordReader {
}
return ValueNull.INSTANCE;
} else {
SearchRow row = root.getLast();
SearchRow row = root.getLast(session);
if(row != null) {
Value v = row.getValue(columnIndex[0]);
return v;
......
......@@ -30,7 +30,7 @@ public class BtreeLeaf extends BtreePage {
private boolean writePos;
private int cachedRealByteCount;
BtreeLeaf(BtreeIndex index, DataPage s) throws SQLException {
BtreeLeaf(BtreeIndex index, Session session,DataPage s) throws SQLException {
super(index);
writePos = s.readByte() == 'P';
if(writePos) {
......@@ -38,7 +38,7 @@ public class BtreeLeaf extends BtreePage {
// should be 1, but may not be 1
pageData = new ObjectArray(size);
for(int i=0; i<size; i++) {
Row r = index.getRow(s.readInt());
Row r = index.getRow(session, s.readInt());
pageData.add(r);
}
} else {
......@@ -258,7 +258,7 @@ public class BtreeLeaf extends BtreePage {
return size;
}
SearchRow getLast() throws SQLException {
SearchRow getLast(Session sessioni) throws SQLException {
if(pageData.size()==0) {
if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
......@@ -268,7 +268,7 @@ public class BtreeLeaf extends BtreePage {
return (SearchRow)pageData.get(pageData.size()-1);
}
SearchRow getFirst() throws SQLException {
SearchRow getFirst(Session session) throws SQLException {
if(pageData.size()==0) {
if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
......
......@@ -56,12 +56,12 @@ public class BtreeNode extends BtreePage {
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);
if(r == null) {
int p = pageChildren.get(i+1);
BtreePage page = index.getPage(p);
r = page.getFirst();
BtreePage page = index.getPage(session, p);
r = page.getFirst(session);
pageData.set(i, r);
}
return r;
......@@ -91,7 +91,7 @@ public class BtreeNode extends BtreePage {
}
}
int at = l;
BtreePage page = index.getPage(pageChildren.get(at));
BtreePage page = index.getPage(session, pageChildren.get(at));
int splitPoint = page.add(newRow, session);
if (splitPoint == 0) {
return 0;
......@@ -130,7 +130,7 @@ public class BtreeNode extends BtreePage {
}
int at = l;
// 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);
if(first == null) {
// the first row didn't change - nothing to do here
......@@ -216,7 +216,7 @@ public class BtreeNode extends BtreePage {
}
}
if(l>=pageData.size()) {
BtreePage page = index.getPage(pageChildren.get(l));
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l);
boolean result = page.findFirst(cursor, compare);
if(result) {
......@@ -225,7 +225,7 @@ public class BtreeNode extends BtreePage {
cursor.pop();
return false;
}
BtreePage page = index.getPage(pageChildren.get(l));
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(l));
cursor.push(this, l);
if(page.findFirst(cursor, compare)) {
return true;
......@@ -236,7 +236,7 @@ public class BtreeNode extends BtreePage {
SearchRow row = getData(i);
int comp = index.compareRows(row, compare);
if (comp >=0) {
page = index.getPage(pageChildren.get(i));
page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i);
if(page.findFirst(cursor, compare)) {
return true;
......@@ -244,7 +244,7 @@ public class BtreeNode extends BtreePage {
cursor.pop();
}
}
page = index.getPage(pageChildren.get(i));
page = index.getPage(cursor.getSession(), pageChildren.get(i));
cursor.push(this, i);
boolean result = page.findFirst(cursor, compare);
if(result) {
......@@ -258,7 +258,7 @@ public class BtreeNode extends BtreePage {
i++;
if (i <= pageData.size()) {
cursor.setStackPosition(i);
BtreePage page = index.getPage(pageChildren.get(i));
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(i));
page.first(cursor);
return;
}
......@@ -278,7 +278,7 @@ public class BtreeNode extends BtreePage {
public void first(BtreeCursor cursor) throws SQLException {
cursor.push(this, 0);
BtreePage page = index.getPage(pageChildren.get(0));
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(0));
page.first(cursor);
}
......@@ -331,24 +331,24 @@ public class BtreeNode extends BtreePage {
return size + index.getRecordOverhead();
}
SearchRow getLast() throws SQLException {
SearchRow getLast(Session session) throws SQLException {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page");
}
for(int i=pageChildren.size()-1; i>=0; i--) {
BtreePage page = index.getPage(pageChildren.get(i));
BtreePage page = index.getPage(session, pageChildren.get(i));
if(page != null) {
return page.getLast();
return page.getLast(session);
}
}
return null;
}
SearchRow getFirst() throws SQLException {
SearchRow getFirst(Session session) throws SQLException {
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) {
return page.getFirst();
return page.getFirst(session);
}
}
return null;
......
......@@ -39,8 +39,8 @@ public abstract class BtreePage extends Record {
abstract SearchRow remove(Session session, Row row, int level) throws SQLException;
abstract BtreePage split(Session session, int splitPoint) throws SQLException;
abstract boolean findFirst(BtreeCursor cursor, SearchRow row) throws SQLException;
abstract SearchRow getFirst() throws SQLException;
abstract SearchRow getLast() throws SQLException;
abstract SearchRow getFirst(Session session) throws SQLException;
abstract SearchRow getLast(Session session) throws SQLException;
abstract void next(BtreeCursor cursor, int i) throws SQLException;
abstract void first(BtreeCursor cursor) throws SQLException;
abstract int getRealByteCount() throws SQLException;
......
......@@ -106,7 +106,7 @@ public class HashIndex extends Index {
int key = first.getValue(columns[0].getColumnId()).getInt();
int pos = intMap.get(key);
if(pos != IntIntHashMap.NOT_FOUND) {
result = tableData.getRow(pos);
result = tableData.getRow(session, pos);
} else {
result = null;
}
......@@ -115,7 +115,7 @@ public class HashIndex extends Index {
if (pos == null) {
result = null;
} else {
result = tableData.getRow(pos.intValue());
result = tableData.getRow(session, pos.intValue());
}
}
return new HashCursor(result);
......
......@@ -65,7 +65,7 @@ public class LinearHashIndex extends Index implements RecordReader {
truncate(session);
needRebuild = true;
} else {
head = (LinearHashHead) storage.getRecord(pos);
head = (LinearHashHead) storage.getRecord(session, pos);
}
}
......@@ -149,16 +149,16 @@ public class LinearHashIndex extends Index implements RecordReader {
record.key = key;
record.home = home;
record.value = value;
int free = getNextFree(home);
int free = getNextFree(session, home);
while (true) {
LinearHashBucket bucket = getBucket(index);
LinearHashBucket bucket = getBucket(session, index);
if (bucket.getRecordSize() < RECORDS_PER_BUCKET) {
addRecord(session, bucket, record);
break;
}
// this bucket is full
int foreign = getForeignHome(index);
int foreign = getForeignHome(session, index);
if (foreign >= 0 && foreign != home) {
// move out foreign records - add this record - add foreign
// records again
......@@ -174,7 +174,7 @@ public class LinearHashIndex extends Index implements RecordReader {
continue;
}
int nextFree = getNextFree(free);
int nextFree = getNextFree(session, free);
if (nextFree < 0) {
// trace.debug("split because no chain " + head.bucketCount);
split(session);
......@@ -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)
bucket = getBucket(index);
bucket = getBucket(session, index);
bucket.setNext(session, free);
free = nextFree;
if (getForeignHome(free) >= 0) {
if (getForeignHome(session, free) >= 0) {
throw Message.getInternalError("next already linked");
}
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--) {
LinearHashBucket bucket = getBucket(i);
LinearHashBucket bucket = getBucket(session, i);
if (bucket.getRecordSize() >= RECORDS_PER_BUCKET) {
continue;
}
if (getForeignHome(i) < 0 && i != excluding) {
if (getForeignHome(session, i) < 0 && i != excluding) {
return i;
}
}
return -1;
}
private int getForeignHome(int bucketId) throws SQLException {
LinearHashBucket bucket = getBucket(bucketId);
private int getForeignHome(Session session, int bucketId) throws SQLException {
LinearHashBucket bucket = getBucket(session, bucketId);
for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry record = bucket.getRecord(i);
if (record.home != bucketId) {
......@@ -247,8 +247,8 @@ public class LinearHashIndex extends Index implements RecordReader {
// moves all records of a bucket to the array (including chained)
private void moveOut(Session session, int home, ObjectArray storeIn) throws SQLException {
LinearHashBucket bucket = getBucket(home);
int foreign = getForeignHome(home);
LinearHashBucket bucket = getBucket(session, home);
int foreign = getForeignHome(session, home);
while (true) {
for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry r = bucket.getRecord(i);
......@@ -263,7 +263,7 @@ public class LinearHashIndex extends Index implements RecordReader {
// and therefore all home records have been found
// (and it would be an error to set next to -1)
moveOut(session, foreign, storeIn);
if(SysProperties.CHECK && getBucket(foreign).getNextBucket() != -1) {
if(SysProperties.CHECK && getBucket(session, foreign).getNextBucket() != -1) {
throw Message.getInternalError("moveOut "+foreign);
}
return;
......@@ -276,7 +276,7 @@ public class LinearHashIndex extends Index implements RecordReader {
break;
}
bucket.setNext(session, -1);
bucket = getBucket(next);
bucket = getBucket(session, next);
}
}
......@@ -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.key == null) {
r.key = getKey(tableData.getRow(r.value));
r.key = getKey(tableData.getRow(session, r.value));
}
if(database.compareTypeSave(r.key, key)==0) {
return true;
......@@ -327,21 +327,21 @@ public class LinearHashIndex extends Index implements RecordReader {
return false;
}
private int get(Value key) throws SQLException {
private int get(Session session, Value key) throws SQLException {
int hash = key.hashCode();
int home = getPos(hash);
LinearHashBucket bucket = getBucket(home);
LinearHashBucket bucket = getBucket(session, home);
while (true) {
for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry r = bucket.getRecord(i);
if(isEquals(r, hash, key)) {
if(isEquals(session, r, hash, key)) {
return r.value;
}
}
if (bucket.getNextBucket() < 0) {
return -1;
}
bucket = getBucket(bucket.getNextBucket());
bucket = getBucket(session, bucket.getNextBucket());
}
}
......@@ -364,10 +364,10 @@ public class LinearHashIndex extends Index implements RecordReader {
int home = getPos(hash);
int now = home;
while (true) {
LinearHashBucket bucket = getBucket(now);
LinearHashBucket bucket = getBucket(session, now);
for (int i = 0; i < bucket.getRecordSize(); i++) {
LinearHashEntry r = bucket.getRecord(i);
if(isEquals(r, hash, key)) {
if(isEquals(session, r, hash, key)) {
removeRecord(session, bucket, i);
if (home != now) {
ObjectArray old = new ObjectArray();
......@@ -389,7 +389,7 @@ public class LinearHashIndex extends Index implements RecordReader {
return i * blocksPerBucket + firstBucketBlock;
}
private LinearHashBucket getBucket(int i) throws SQLException {
private LinearHashBucket getBucket(Session session, int i) throws SQLException {
readCount++;
if(SysProperties.CHECK && i >= head.bucketCount) {
throw Message.getInternalError("get="+i+" max="+head.bucketCount);
......@@ -398,7 +398,7 @@ public class LinearHashIndex extends Index implements RecordReader {
// return (LinearHashBucket) buckets.get(i);
i = getBlockId(i);
// System.out.println("getBucket "+i);
LinearHashBucket bucket = (LinearHashBucket) storage.getRecord(i);
LinearHashBucket bucket = (LinearHashBucket) storage.getRecord(session, i);
return bucket;
}
......@@ -430,7 +430,7 @@ public class LinearHashIndex extends Index implements RecordReader {
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();
if (c == 'B') {
return new LinearHashBucket(this, s);
......@@ -449,7 +449,7 @@ public class LinearHashIndex extends Index implements RecordReader {
public void add(Session session, Row row) throws SQLException {
Value key = getKey(row);
if(get(key) != -1) {
if(get(session, key) != -1) {
// TODO index duplicate key for hash indexes: is this allowed?
throw getDuplicateKeyException();
}
......@@ -482,11 +482,11 @@ public class LinearHashIndex extends Index implements RecordReader {
// TODO hash index: should additionally check if values are the same
throw Message.getInternalError();
}
int key = get(getKey(first));
int key = get(session, getKey(first));
if(key == -1) {
return new LinearHashCursor(null);
}
return new LinearHashCursor(tableData.getRow(key));
return new LinearHashCursor(tableData.getRow(session, key));
}
public long getCost(int[] masks) throws SQLException {
......
......@@ -6,6 +6,7 @@ package org.h2.index;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -16,12 +17,18 @@ import org.h2.result.SearchRow;
public class ScanCursor implements Cursor {
private ScanIndex scan;
private Row row;
private final Session session;
ScanCursor(ScanIndex scan) {
ScanCursor(Session session, ScanIndex scan) {
this.session = session;
this.scan = scan;
row = null;
}
Session getSession() {
return session;
}
public Row get() {
return row;
}
......@@ -35,7 +42,7 @@ public class ScanCursor implements Cursor {
}
public boolean next() throws SQLException {
row = scan.getNextRow(row);
row = scan.getNextRow(session, row);
return row != null;
}
}
......@@ -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) {
return (Row) storage.getRecord(key);
return (Row) storage.getRecord(session, key);
}
return (Row) rows.get(key);
}
......@@ -139,7 +139,7 @@ public class ScanIndex extends Index {
}
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 {
......@@ -150,7 +150,7 @@ public class ScanIndex extends Index {
return cost;
}
Row getNextRow(Row row) throws SQLException {
Row getNextRow(Session session, Row row) throws SQLException {
if(storage == null) {
int key;
if (row == null) {
......@@ -173,7 +173,7 @@ public class ScanIndex extends Index {
if (pos < 0) {
return null;
}
return (Row) storage.getRecord(pos);
return (Row) storage.getRecord(session, pos);
}
public int getColumnIndex(Column col) {
......
......@@ -148,9 +148,11 @@ ALTER TABLE TEST ADD CREATEDATE TIMESTAMP
"
"Commands (DDL)","ALTER TABLE ADD CONSTRAINT","
ALTER TABLE tableName ADD constraint
ALTER TABLE tableName ADD constraint [CHECK|NOCHECK]
","
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)
"
......@@ -243,11 +245,11 @@ ALTER TABLE TEST DROP CONSTRAINT UNIQUE_NAME
"
"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.
Enabling it does not check existing data.
Use SET REFERENTIAL_INTEGRITY to disable it for all tables.
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).
","
ALTER TABLE TEST SET REFERENTIAL_INTEGRITY FALSE
"
......
......@@ -255,8 +255,8 @@ public class Schema extends DbObject {
map.remove(objName);
}
public TableData createTable(String tempName, int id, ObjectArray newColumns, boolean persistent) throws SQLException {
return new TableData(this, tempName, id, newColumns, persistent);
public TableData createTable(String tempName, int id, ObjectArray newColumns, boolean persistent, boolean clustered) throws SQLException {
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 {
......
......@@ -358,7 +358,7 @@ public class DiskFile implements CacheWriter {
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 {
int owner = getPageOwner(getPage(pos));
if(owner != storageId) {
......@@ -377,10 +377,10 @@ public class DiskFile implements CacheWriter {
} catch (Exception e) {
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) {
throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF);
}
......@@ -413,7 +413,7 @@ public class DiskFile implements CacheWriter {
s.readInt();
}
s.check(blockCount*BLOCK_SIZE);
Record r = reader.read(s);
Record r = reader.read(session, s);
r.setStorageId(storageId);
r.setPos(pos);
r.setBlockCount(blockCount);
......
......@@ -6,6 +6,7 @@ package org.h2.store;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.util.CacheObject;
/**
......@@ -13,6 +14,7 @@ import org.h2.util.CacheObject;
*/
public abstract class Record extends CacheObject {
private boolean deleted;
private int sessionId;
private int storageId;
private int lastLog = LogSystem.LOG_WRITTEN;
private int lastPos = LogSystem.LOG_WRITTEN;
......@@ -34,10 +36,20 @@ public abstract class Record extends CacheObject {
return false;
}
public void setDeleted(boolean deleted) {
public void setDeleted(Session session, boolean deleted) {
this.sessionId = session.getId();
this.deleted = deleted;
}
public int getSessionId() {
int testing;
return sessionId;
}
public void commit() {
this.sessionId = 0;
}
public boolean getDeleted() {
return deleted;
}
......
......@@ -5,10 +5,11 @@
package org.h2.store;
import java.sql.SQLException;
import org.h2.engine.Session;
/**
* @author Thomas
*/
public interface RecordReader {
Record read(DataPage s) throws SQLException;
Record read(Session session, DataPage s) throws SQLException;
}
......@@ -54,12 +54,12 @@ public class Storage {
recordCount++;
}
public Record getRecord(int pos) throws SQLException {
return file.getRecord(pos, reader, id);
public Record getRecord(Session session, int pos) throws SQLException {
return file.getRecord(session, pos, reader, id);
}
public Record getRecordIfStored(int pos) throws SQLException {
return file.getRecordIfStored(pos, reader, id);
public Record getRecordIfStored(Session session, int pos) throws SQLException {
return file.getRecordIfStored(session, pos, reader, id);
}
/**
......@@ -110,7 +110,7 @@ public class Storage {
}
public void updateRecord(Session session, Record record) throws SQLException {
record.setDeleted(false);
record.setDeleted(session, false);
file.updateRecord(session, record);
}
......@@ -118,7 +118,7 @@ public class Storage {
record.setStorageId(id);
int size = file.getRecordOverhead() + record.getByteCount(dummy);
size = MathUtils.roundUp(size, DiskFile.BLOCK_SIZE);
record.setDeleted(false);
record.setDeleted(session, false);
int blockCount = size / DiskFile.BLOCK_SIZE;
if(pos == ALLOCATE_POS) {
pos = allocate(blockCount);
......@@ -133,11 +133,11 @@ public class Storage {
}
public void removeRecord(Session session, int pos) throws SQLException {
Record record = getRecord(pos);
Record record = getRecord(session, pos);
if(SysProperties.CHECK && record.getDeleted()) {
throw Message.getInternalError("duplicate delete " + pos);
}
record.setDeleted(true);
record.setDeleted(session, true);
int blockCount = record.getBlockCount();
free(pos, blockCount);
recordCount--;
......
......@@ -140,4 +140,8 @@ public class UndoLogRecord {
public Table getTable() {
return table;
}
public void commit() {
row.commit();
}
}
......@@ -450,8 +450,14 @@ public abstract class Table extends SchemaObject {
return false;
}
public void setCheckForeignKeyConstraints(boolean b) {
checkForeignKeyConstraints = b;
public void setCheckForeignKeyConstraints(Session session, boolean enabled, boolean checkExisting) throws SQLException {
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() {
......@@ -488,4 +494,8 @@ public abstract class Table extends SchemaObject {
this.onCommitTruncate = onCommitTruncate;
}
public boolean isClustered() {
return false;
}
}
......@@ -48,15 +48,19 @@ public class TableData extends Table implements RecordReader {
private boolean globalTemporary;
private final ObjectArray indexes = new ObjectArray();
private long lastModificationId;
private final boolean clustered;
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);
Column[] cols = new Column[columns.size()];
columns.toArray(cols);
setColumns(cols);
this.clustered = clustered;
if(!clustered) {
scanIndex = new ScanIndex(this, id, cols, IndexType.createScan(persistent));
indexes.add(scanIndex);
}
traceLock = database.getTrace(Trace.LOCK);
}
......@@ -67,8 +71,8 @@ public class TableData extends Table implements RecordReader {
}
}
public Row getRow(int key) throws SQLException {
return scanIndex.getRow(key);
public Row getRow(Session session, int key) throws SQLException {
return scanIndex.getRow(session, key);
}
public void addRow(Session session, Row row) throws SQLException {
......@@ -152,7 +156,7 @@ public class TableData extends Table implements RecordReader {
index = new TreeIndex(this, indexId, indexName, cols, indexType);
}
}
if(index.needRebuild()) {
if(index.needRebuild() && rowCount > 0) {
try {
Index scan = getScanIndex(session);
long remaining = scan.getRowCount();
......@@ -278,6 +282,9 @@ public class TableData extends Table implements RecordReader {
if(lockMode == Constants.LOCK_MODE_OFF) {
return;
}
if(SysProperties.MVCC) {
return;
}
long max = System.currentTimeMillis() + session.getLockTimeout();
synchronized(database) {
while (true) {
......@@ -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();
Value[] data = new Value[len];
for(int i=0; i<len; i++) {
......@@ -476,4 +483,8 @@ public class TableData extends Table implements RecordReader {
return lastModificationId;
}
public boolean isClustered() {
return clustered;
}
}
......@@ -303,8 +303,15 @@ public class TableFilter implements ColumnResolver {
public Row get() throws SQLException {
if(current == null && currentSearchRow != null) {
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;
}
......
......@@ -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
......
......@@ -31,7 +31,7 @@ public class WebServlet extends HttpServlet {
Enumeration en = config.getInitParameterNames();
ArrayList list = new ArrayList();
while(en.hasMoreElements()) {
String name = (String) en.nextElement();
String name = en.nextElement().toString();
String value = config.getInitParameter(name);
if(!name.startsWith("-")) {
name = "-" + name;
......@@ -90,13 +90,13 @@ public class WebServlet extends HttpServlet {
Properties attributes = new Properties();
Enumeration en = req.getAttributeNames();
while(en.hasMoreElements()) {
String name = (String) en.nextElement();
String value = (String) req.getAttribute(name);
String name = en.nextElement().toString();
String value = req.getAttribute(name).toString();
attributes.put(name, value);
}
en = req.getParameterNames();
while(en.hasMoreElements()) {
String name = (String) en.nextElement();
String name = en.nextElement().toString();
String value = req.getParameter(name);
attributes.put(name, value);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论