提交 20e2a017 authored 作者: Sergi Vladykin's avatar Sergi Vladykin

Added external table engines support

上级 2116380c
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.api;
import org.h2.table.RecreatableTable;
import org.h2.command.ddl.CreateTableData;
/**
* Class for creating custom table implementations
*
* @author Sergi Vladykin
*/
public interface TableEngine {
/**
* Create new table
* @param data for table construction
* @return created table
*/
RecreatableTable createTable(CreateTableData data);
}
...@@ -122,7 +122,6 @@ import org.h2.table.FunctionTable; ...@@ -122,7 +122,6 @@ import org.h2.table.FunctionTable;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.RangeTable; import org.h2.table.RangeTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.util.Utils; import org.h2.util.Utils;
...@@ -3860,7 +3859,7 @@ public class Parser { ...@@ -3860,7 +3859,7 @@ public class Parser {
private Query parserWith() { private Query parserWith() {
String tempViewName = readIdentifierWithSchema(); String tempViewName = readIdentifierWithSchema();
Schema schema = getSchema(); Schema schema = getSchema();
TableData recursiveTable; Table recursiveTable;
read("("); read("(");
ArrayList<Column> columns = New.arrayList(); ArrayList<Column> columns = New.arrayList();
String[] cols = parseColumnList(); String[] cols = parseColumnList();
...@@ -4789,6 +4788,9 @@ public class Parser { ...@@ -4789,6 +4788,9 @@ public class Parser {
command.setQuery(parseSelect()); command.setQuery(parseSelect());
} }
} }
if (readIf("ENGINE")) {
command.setTableEngine(readString());
}
if (temp) { if (temp) {
if (readIf("ON")) { if (readIf("ON")) {
read("COMMIT"); read("COMMIT");
......
...@@ -28,7 +28,6 @@ import org.h2.schema.Sequence; ...@@ -28,7 +28,6 @@ import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject; import org.h2.schema.TriggerObject;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.util.New; import org.h2.util.New;
...@@ -207,7 +206,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -207,7 +206,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
String tempName = db.getTempTableName(session); String tempName = db.getTempTableName(session);
Column[] columns = table.getColumns(); Column[] columns = table.getColumns();
ArrayList<Column> newColumns = New.arrayList(); ArrayList<Column> newColumns = New.arrayList();
TableData newTable = cloneTableStructure(columns, db, tempName, newColumns); Table newTable = cloneTableStructure(columns, db, tempName, newColumns);
List<String> views; List<String> views;
try { try {
views = checkViews(table, newTable); views = checkViews(table, newTable);
...@@ -246,7 +245,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -246,7 +245,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
} }
} }
private TableData cloneTableStructure(Column[] columns, Database db, String tempName, ArrayList<Column> newColumns) { private Table cloneTableStructure(Column[] columns, Database db, String tempName, ArrayList<Column> newColumns) {
for (Column col : columns) { for (Column col : columns) {
newColumns.add(col.getClone()); newColumns.add(col.getClone());
} }
...@@ -282,7 +281,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -282,7 +281,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
data.persistIndexes = table.isPersistIndexes(); data.persistIndexes = table.isPersistIndexes();
data.create = true; data.create = true;
data.session = session; data.session = session;
TableData newTable = getSchema().createTable(data); Table newTable = getSchema().createTable(data);
newTable.setComment(table.getComment()); newTable.setComment(table.getComment());
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
buff.append(newTable.getCreateSQL()); buff.append(newTable.getCreateSQL());
...@@ -312,7 +311,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -312,7 +311,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
newTable.removeChildrenAndResources(session); newTable.removeChildrenAndResources(session);
execute(newTableSQL, true); execute(newTableSQL, true);
newTable = (TableData) newTableSchema.getTableOrView(session, newTableName); newTable = newTableSchema.getTableOrView(session, newTableName);
ArrayList<String> triggers = New.arrayList(); ArrayList<String> triggers = New.arrayList();
for (DbObject child : table.getChildren()) { for (DbObject child : table.getChildren()) {
if (child instanceof Sequence) { if (child instanceof Sequence) {
......
...@@ -19,7 +19,7 @@ import org.h2.schema.Schema; ...@@ -19,7 +19,7 @@ import org.h2.schema.Schema;
import org.h2.schema.Sequence; import org.h2.schema.Sequence;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.TableData; import org.h2.table.Table;
import org.h2.util.New; import org.h2.util.New;
import org.h2.value.DataType; import org.h2.value.DataType;
...@@ -33,7 +33,6 @@ public class CreateTable extends SchemaCommand { ...@@ -33,7 +33,6 @@ public class CreateTable extends SchemaCommand {
private ArrayList<Prepared> constraintCommands = New.arrayList(); private ArrayList<Prepared> constraintCommands = New.arrayList();
private IndexColumn[] pkColumns; private IndexColumn[] pkColumns;
private boolean ifNotExists; private boolean ifNotExists;
private boolean globalTemporary;
private boolean onCommitDrop; private boolean onCommitDrop;
private boolean onCommitTruncate; private boolean onCommitTruncate;
private Query asQuery; private Query asQuery;
...@@ -137,10 +136,9 @@ public class CreateTable extends SchemaCommand { ...@@ -137,10 +136,9 @@ public class CreateTable extends SchemaCommand {
data.id = getObjectId(); data.id = getObjectId();
data.create = create; data.create = create;
data.session = session; data.session = session;
TableData table = getSchema().createTable(data); Table table = getSchema().createTable(data);
table.setComment(comment); table.setComment(comment);
table.setGlobalTemporary(globalTemporary); if (data.temporary && !data.globalTemporary) {
if (data.temporary && !globalTemporary) {
if (onCommitDrop) { if (onCommitDrop) {
table.setOnCommitDrop(true); table.setOnCommitDrop(true);
} }
...@@ -236,7 +234,7 @@ public class CreateTable extends SchemaCommand { ...@@ -236,7 +234,7 @@ public class CreateTable extends SchemaCommand {
} }
public void setGlobalTemporary(boolean globalTemporary) { public void setGlobalTemporary(boolean globalTemporary) {
this.globalTemporary = globalTemporary; data.globalTemporary = globalTemporary;
} }
/** /**
...@@ -265,4 +263,11 @@ public class CreateTable extends SchemaCommand { ...@@ -265,4 +263,11 @@ public class CreateTable extends SchemaCommand {
this.sortedInsertMode = sortedInsertMode; this.sortedInsertMode = sortedInsertMode;
} }
/**
* @param tableEngine the table engine to set
*/
public void setTableEngine(String tableEngine) {
data.tableEngine = tableEngine;
}
} }
...@@ -42,6 +42,11 @@ public class CreateTableData { ...@@ -42,6 +42,11 @@ public class CreateTableData {
*/ */
public boolean temporary; public boolean temporary;
/**
* Whether the table global temporary
*/
public boolean globalTemporary;
/** /**
* Whether the indexes should be persisted. * Whether the indexes should be persisted.
*/ */
...@@ -61,5 +66,10 @@ public class CreateTableData { ...@@ -61,5 +66,10 @@ public class CreateTableData {
* The session. * The session.
*/ */
public Session session; public Session session;
/**
* Table engine to use
*/
public String tableEngine;
} }
...@@ -48,7 +48,6 @@ import org.h2.table.Column; ...@@ -48,7 +48,6 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.MetaTable; import org.h2.table.MetaTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.TableLinkConnection; import org.h2.table.TableLinkConnection;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
...@@ -111,7 +110,7 @@ public class Database implements DataHandler { ...@@ -111,7 +110,7 @@ public class Database implements DataHandler {
private int nextTempTableId; private int nextTempTableId;
private User systemUser; private User systemUser;
private Session systemSession; private Session systemSession;
private TableData meta; private Table meta;
private Index metaIdIndex; private Index metaIdIndex;
private FileLock lock; private FileLock lock;
private WriterThread writer; private WriterThread writer;
......
...@@ -594,7 +594,8 @@ public class Session extends SessionWithState implements SessionFactory { ...@@ -594,7 +594,8 @@ public class Session extends SessionWithState implements SessionFactory {
if (SysProperties.CHECK) { if (SysProperties.CHECK) {
int lockMode = database.getLockMode(); int lockMode = database.getLockMode();
if (lockMode != Constants.LOCK_MODE_OFF && !database.isMultiVersion()) { if (lockMode != Constants.LOCK_MODE_OFF && !database.isMultiVersion()) {
if (locks.indexOf(log.getTable()) < 0 && !Table.TABLE_LINK.equals(log.getTable().getTableType())) { if (locks.indexOf(log.getTable()) < 0 && !Table.TABLE_LINK.equals(log.getTable().getTableType())
&& !Table.EXTERNAL_TABLE_ENGINE.equals(log.getTable().getTableType())) {
DbException.throwInternalError(); DbException.throwInternalError();
} }
} }
......
...@@ -48,7 +48,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -48,7 +48,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
* not yet known * not yet known
* @param newIndexType the index type * @param newIndexType the index type
*/ */
void initBaseIndex(Table newTable, int id, String name, IndexColumn[] newIndexColumns, IndexType newIndexType) { protected void initBaseIndex(Table newTable, int id, String name, IndexColumn[] newIndexColumns, IndexType newIndexType) {
initSchemaObjectBase(newTable.getSchema(), id, name, Trace.INDEX); initSchemaObjectBase(newTable.getSchema(), id, name, Trace.INDEX);
this.indexType = newIndexType; this.indexType = newIndexType;
this.table = newTable; this.table = newTable;
......
...@@ -70,7 +70,7 @@ public class HashIndex extends BaseIndex { ...@@ -70,7 +70,7 @@ public class HashIndex extends BaseIndex {
} else { } else {
result = tableData.getRow(session, pos.intValue()); result = tableData.getRow(session, pos.intValue());
} }
return new HashCursor(result); return new SingleRowCursor(result);
} }
public long getRowCount(Session session) { public long getRowCount(Session session) {
......
...@@ -11,14 +11,16 @@ import org.h2.result.Row; ...@@ -11,14 +11,16 @@ import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
/** /**
* The cursor for a hash index. * The cursor with at most one row.
* At most one row can be accessed.
*/ */
public class HashCursor implements Cursor { public class SingleRowCursor implements Cursor {
private Row row; private Row row;
private boolean end; private boolean end;
HashCursor(Row row) { /**
* @param row - the single row (if null then cursor is empty)
*/
public SingleRowCursor(Row row) {
this.row = row; this.row = row;
} }
......
...@@ -49,7 +49,7 @@ public class ResultTempTable implements ResultExternal { ...@@ -49,7 +49,7 @@ public class ResultTempTable implements ResultExternal {
data.persistData = true; data.persistData = true;
data.create = true; data.create = true;
data.session = session; data.session = session;
table = schema.createTable(data); table = (TableData) schema.createTable(data);
int indexId = session.getDatabase().allocateObjectId(); int indexId = session.getDatabase().allocateObjectId();
IndexColumn indexColumn = new IndexColumn(); IndexColumn indexColumn = new IndexColumn();
indexColumn.column = column; indexColumn.column = column;
......
...@@ -9,6 +9,7 @@ package org.h2.schema; ...@@ -9,6 +9,7 @@ package org.h2.schema;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData; import org.h2.command.ddl.CreateTableData;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
...@@ -25,6 +26,7 @@ import org.h2.table.Table; ...@@ -25,6 +26,7 @@ import org.h2.table.Table;
import org.h2.table.TableData; import org.h2.table.TableData;
import org.h2.table.TableLink; import org.h2.table.TableLink;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.Utils;
/** /**
* A schema as created by the SQL statement * A schema as created by the SQL statement
...@@ -482,11 +484,20 @@ public class Schema extends DbObjectBase { ...@@ -482,11 +484,20 @@ public class Schema extends DbObjectBase {
* Add a table to the schema. * Add a table to the schema.
* *
* @param data the create table information * @param data the create table information
* @return the created {@link TableData} object * @return the created {@link Table} object
*/ */
public TableData createTable(CreateTableData data) { public Table createTable(CreateTableData data) {
synchronized (database) { synchronized (database) {
data.schema = this; data.schema = this;
if (data.tableEngine != null) {
TableEngine engine;
try {
engine = (TableEngine) Utils.loadUserClass(data.tableEngine).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
return engine.createTable(data);
}
return new TableData(data); return new TableData(data);
} }
} }
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.table;
import org.h2.command.ddl.CreateTableData;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
/**
* @author Thomas Mueller
* @author Sergi Vladykin
*/
public abstract class RecreatableTable extends Table {
protected final String tableEngine;
private final boolean globalTemporary;
public RecreatableTable(CreateTableData data) {
super(data.schema, data.id, data.tableName, data.persistIndexes, data.persistData);
this.tableEngine = data.tableEngine;
this.globalTemporary = data.globalTemporary;
setTemporary(data.temporary);
Column[] cols = new Column[data.columns.size()];
data.columns.toArray(cols);
setColumns(cols);
}
@Override
public String getDropSQL() {
return "DROP TABLE IF EXISTS " + getSQL();
}
@Override
public String getCreateSQL() {
StatementBuilder buff = new StatementBuilder("CREATE ");
if (isTemporary()) {
if (isGlobalTemporary()) {
buff.append("GLOBAL ");
} else {
buff.append("LOCAL ");
}
buff.append("TEMPORARY ");
} else if (isPersistIndexes()) {
buff.append("CACHED ");
} else {
buff.append("MEMORY ");
}
buff.append("TABLE ").append(getSQL());
if (comment != null) {
buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
}
buff.append("(\n ");
for (Column column : columns) {
buff.appendExceptFirst(",\n ");
buff.append(column.getCreateSQL());
}
buff.append("\n)");
if (tableEngine != null) {
buff.append("\nENGINE '");
buff.append(tableEngine);
buff.append("'");
}
if (!isPersistIndexes() && !isPersistData()) {
buff.append("\nNOT PERSISTENT");
}
return buff.toString();
}
public boolean isGlobalTemporary() {
return globalTemporary;
}
}
...@@ -75,6 +75,11 @@ public abstract class Table extends SchemaObjectBase { ...@@ -75,6 +75,11 @@ public abstract class Table extends SchemaObjectBase {
*/ */
public static final String VIEW = "VIEW"; public static final String VIEW = "VIEW";
/**
* The table type name for external table engines.
*/
public static final String EXTERNAL_TABLE_ENGINE = "EXTERNAL";
/** /**
* The columns of this table. * The columns of this table.
*/ */
...@@ -101,7 +106,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -101,7 +106,7 @@ public abstract class Table extends SchemaObjectBase {
private boolean onCommitDrop, onCommitTruncate; private boolean onCommitDrop, onCommitTruncate;
private Row nullRow; private Row nullRow;
Table(Schema schema, int id, String name, boolean persistIndexes, boolean persistData) { public Table(Schema schema, int id, String name, boolean persistIndexes, boolean persistData) {
initSchemaObjectBase(schema, id, name, Trace.TABLE); initSchemaObjectBase(schema, id, name, Trace.TABLE);
this.persistIndexes = persistIndexes; this.persistIndexes = persistIndexes;
this.persistData = persistData; this.persistData = persistData;
...@@ -365,7 +370,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -365,7 +370,7 @@ public abstract class Table extends SchemaObjectBase {
* @param session the session * @param session the session
* @return true if it is * @return true if it is
*/ */
boolean isLockedExclusivelyBy(Session session) { public boolean isLockedExclusivelyBy(Session session) {
return false; return false;
} }
......
...@@ -38,8 +38,6 @@ import org.h2.result.SortOrder; ...@@ -38,8 +38,6 @@ import org.h2.result.SortOrder;
import org.h2.schema.SchemaObject; import org.h2.schema.SchemaObject;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode; import org.h2.value.CompareMode;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -49,13 +47,12 @@ import org.h2.value.Value; ...@@ -49,13 +47,12 @@ import org.h2.value.Value;
* in the database. The actual data is not kept here, instead it is kept in the * in the database. The actual data is not kept here, instead it is kept in the
* indexes. There is at least one index, the scan index. * indexes. There is at least one index, the scan index.
*/ */
public class TableData extends Table { public class TableData extends RecreatableTable {
private Index scanIndex; private Index scanIndex;
private long rowCount; private long rowCount;
private volatile Session lockExclusive; private volatile Session lockExclusive;
private HashSet<Session> lockShared = New.hashSet(); private HashSet<Session> lockShared = New.hashSet();
private Trace traceLock; private Trace traceLock;
private boolean globalTemporary;
private final ArrayList<Index> indexes = New.arrayList(); private final ArrayList<Index> indexes = New.arrayList();
private long lastModificationId; private long lastModificationId;
private boolean containsLargeObject; private boolean containsLargeObject;
...@@ -69,19 +66,15 @@ public class TableData extends Table { ...@@ -69,19 +66,15 @@ public class TableData extends Table {
private boolean waitForLock; private boolean waitForLock;
public TableData(CreateTableData data) { public TableData(CreateTableData data) {
super(data.schema, data.id, data.tableName, data.persistIndexes, data.persistData); super(data);
Column[] cols = new Column[data.columns.size()];
data.columns.toArray(cols);
setColumns(cols);
setTemporary(data.temporary);
if (data.persistData && database.isPersistent()) { if (data.persistData && database.isPersistent()) {
mainIndex = new PageDataIndex(this, data.id, IndexColumn.wrap(cols), IndexType.createScan(data.persistData), data.create, data.session); mainIndex = new PageDataIndex(this, data.id, IndexColumn.wrap(getColumns()), IndexType.createScan(data.persistData), data.create, data.session);
scanIndex = mainIndex; scanIndex = mainIndex;
} else { } else {
scanIndex = new ScanIndex(this, data.id, IndexColumn.wrap(cols), IndexType.createScan(data.persistData)); scanIndex = new ScanIndex(this, data.id, IndexColumn.wrap(getColumns()), IndexType.createScan(data.persistData));
} }
indexes.add(scanIndex); indexes.add(scanIndex);
for (Column col : cols) { for (Column col : getColumns()) {
if (DataType.isLargeObject(col.getType())) { if (DataType.isLargeObject(col.getType())) {
containsLargeObject = true; containsLargeObject = true;
memoryPerRow = Row.MEMORY_CALCULATE; memoryPerRow = Row.MEMORY_CALCULATE;
...@@ -554,40 +547,6 @@ public class TableData extends Table { ...@@ -554,40 +547,6 @@ public class TableData extends Table {
} }
} }
public String getDropSQL() {
return "DROP TABLE IF EXISTS " + getSQL();
}
public String getCreateSQL() {
StatementBuilder buff = new StatementBuilder("CREATE ");
if (isTemporary()) {
if (globalTemporary) {
buff.append("GLOBAL ");
} else {
buff.append("LOCAL ");
}
buff.append("TEMPORARY ");
} else if (isPersistIndexes()) {
buff.append("CACHED ");
} else {
buff.append("MEMORY ");
}
buff.append("TABLE ").append(getSQL());
if (comment != null) {
buff.append(" COMMENT ").append(StringUtils.quoteStringSQL(comment));
}
buff.append("(\n ");
for (Column column : columns) {
buff.appendExceptFirst(",\n ");
buff.append(column.getCreateSQL());
}
buff.append("\n)");
if (!isTemporary() && !isPersistIndexes() && !isPersistData()) {
buff.append("\nNOT PERSISTENT");
}
return buff.toString();
}
public boolean isLockedExclusively() { public boolean isLockedExclusively() {
return lockExclusive != null; return lockExclusive != null;
} }
...@@ -686,14 +645,6 @@ public class TableData extends Table { ...@@ -686,14 +645,6 @@ public class TableData extends Table {
return Table.TABLE; return Table.TABLE;
} }
public void setGlobalTemporary(boolean globalTemporary) {
this.globalTemporary = globalTemporary;
}
public boolean isGlobalTemporary() {
return globalTemporary;
}
public long getMaxDataModificationId() { public long getMaxDataModificationId() {
return lastModificationId; return lastModificationId;
} }
......
...@@ -52,6 +52,7 @@ import org.h2.test.db.TestSequence; ...@@ -52,6 +52,7 @@ import org.h2.test.db.TestSequence;
import org.h2.test.db.TestSessionsLocks; import org.h2.test.db.TestSessionsLocks;
import org.h2.test.db.TestSpaceReuse; import org.h2.test.db.TestSpaceReuse;
import org.h2.test.db.TestSpeed; import org.h2.test.db.TestSpeed;
import org.h2.test.db.TestTableEngines;
import org.h2.test.db.TestTempTables; import org.h2.test.db.TestTempTables;
import org.h2.test.db.TestTransaction; import org.h2.test.db.TestTransaction;
import org.h2.test.db.TestTriggersConstraints; import org.h2.test.db.TestTriggersConstraints;
...@@ -502,6 +503,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -502,6 +503,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestSequence().runTest(this); new TestSequence().runTest(this);
new TestSpaceReuse().runTest(this); new TestSpaceReuse().runTest(this);
new TestSpeed().runTest(this); new TestSpeed().runTest(this);
new TestTableEngines().runTest(this);
new TestTempTables().runTest(this); new TestTempTables().runTest(this);
new TestTransaction().runTest(this); new TestTransaction().runTest(this);
new TestTriggersConstraints().runTest(this); new TestTriggersConstraints().runTest(this);
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData;
import org.h2.engine.Session;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.SingleRowCursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.table.RecreatableTable;
import org.h2.table.Table;
import org.h2.test.TestBase;
/**
* The class for external table engines mechanism testing.
*
* @author Sergi Vladykin
*/
public class TestTableEngines extends TestBase {
public void test() throws Exception {
if (!config.mvcc) {
deleteDb("tableEngine");
Connection conn = getConnection("tableEngine");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE t1(id int, name varchar) ENGINE '" + OneRowTableEngine.class.getName() + "'");
testStatements(stat);
stat.close();
conn.close();
if (!config.memory) {
conn = getConnection("tableEngine");
stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT name FROM t1");
assertFalse(rs.next());
rs.close();
testStatements(stat);
stat.close();
conn.close();
}
deleteDb("tableEngine");
}
}
private void testStatements(Statement stat) throws SQLException {
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(2,'aaa')"), 1);
assertEquals(stat.executeUpdate("UPDATE t1 SET name = 'bbb' WHERE id=2"), 1);
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(3,'ccc')"), 1);
assertEquals(stat.executeUpdate("DELETE FROM t1 WHERE id=2"), 0);
assertEquals(stat.executeUpdate("DELETE FROM t1 WHERE id=3"), 1);
ResultSet rs = stat.executeQuery("SELECT name FROM t1");
assertFalse(rs.next());
rs.close();
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(2,'aaa')"), 1);
assertEquals(stat.executeUpdate("UPDATE t1 SET name = 'bbb' WHERE id=2"), 1);
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(3,'ccc')"), 1);
rs = stat.executeQuery("SELECT name FROM t1");
assertTrue(rs.next());
assertEquals(rs.getString(1), "ccc");
assertFalse(rs.next());
rs.close();
}
/**
* Execute only this test
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
TestBase.createCaller().init().test();
}
/**
* Test table factory
*/
public static class OneRowTableEngine implements TableEngine {
/**
* Table implementation with one row
*/
private static class OneRowTable extends RecreatableTable {
/**
* One row scan index
*/
private class Scan extends BaseIndex {
Scan(Table table) {
initBaseIndex(table, table.getId(), table.getName() + "_SCAN",
IndexColumn.wrap(table.getColumns()), IndexType.createScan(false));
}
public long getRowCountApproximation() {
return table.getRowCountApproximation();
}
public long getRowCount(Session session) {
return table.getRowCount(session);
}
@Override
public void checkRename() {
// do nothing
}
@Override
public void truncate(Session session) {
// do nothing
}
@Override
public void remove(Session session) {
// do nothing
}
@Override
public void remove(Session session, Row r) {
// do nothing
}
@Override
public boolean needRebuild() {
return false;
}
@Override
public double getCost(Session session, int[] masks) {
return 0;
}
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
return new SingleRowCursor(row);
}
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
return new SingleRowCursor(row);
}
@Override
public void close(Session session) {
// do nothing
}
@Override
public boolean canGetFirstOrLast() {
return true;
}
@Override
public void add(Session session, Row r) {
// do nothing
}
}
volatile Row row;
private final Index scanIndex;
OneRowTable(CreateTableData data) {
super(data);
scanIndex = new Scan(this);
}
@Override
public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols,
IndexType indexType, boolean create, String indexComment) {
return null;
}
@Override
public void addRow(Session session, Row r) {
this.row = r;
}
@Override
public boolean canDrop() {
return true;
}
@Override
public boolean canGetRowCount() {
return true;
}
@Override
public void checkSupportAlter() {
// do nothing
}
@Override
public void close(Session session) {
// do nothing
}
@Override
public ArrayList<Index> getIndexes() {
return null;
}
@Override
public long getMaxDataModificationId() {
return 0;
}
@Override
public long getRowCount(Session session) {
return getRowCountApproximation();
}
@Override
public long getRowCountApproximation() {
return row == null ? 0 : 1;
}
@Override
public Index getScanIndex(Session session) {
return scanIndex;
}
@Override
public String getTableType() {
return EXTERNAL_TABLE_ENGINE;
}
@Override
public Index getUniqueIndex() {
return null;
}
@Override
public boolean isDeterministic() {
return false;
}
@Override
public boolean isLockedExclusively() {
return false;
}
@Override
public void lock(Session session, boolean exclusive, boolean force) {
// do nothing
}
@Override
public void removeRow(Session session, Row r) {
this.row = null;
}
@Override
public void truncate(Session session) {
row = null;
}
@Override
public void unlock(Session s) {
// do nothing
}
@Override
public void checkRename() {
// do nothing
}
}
/**
* Create new OneRowTable
*/
public OneRowTable createTable(final CreateTableData data) {
return new OneRowTable(data);
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论