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

Added external table engines support.

上级 20e2a017
......@@ -525,7 +525,7 @@ CREATE SEQUENCE SEQ_ID
CREATE [ CACHED | MEMORY | TEMP | [ GLOBAL | LOCAL ] TEMPORARY ]
TABLE [ IF NOT EXISTS ]
name { { ( { columnDefinition | constraint } [,...] ) [ AS select ] }
| { AS select } } [ NOT PERSISTENT ]
| { AS select } } [ ENGINE tableEngineName ] [ NOT PERSISTENT ]
","
Creates a new table.
......@@ -534,6 +534,9 @@ Cached tables (the default) are persistent, and the number of rows is not limite
Memory tables are persistent, but the index data is kept in main memory,
that means memory tables should not get too large.
The ENGINE option is only required when custom table implementations are used.
The table engine class must implement the interface org.h2.api.TableEngine.
Tables with the NOT PERSISTENT modifier are kept fully in memory, and all
rows are lost when the database is closed.
......
......@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Improved MS SQL Server compatibility: support string concatenation using "+".
<ul><li>User defined table implementation can now be used using CREATE TABLE ... ENGINE.
Thanks to Sergi Vladykin for implementing this feature!
</li><li>Improved MS SQL Server compatibility: support string concatenation using "+".
Thanks to Stepan for the patch!
</li><li>When using the multi-threaded mode, running ANALYZE concurrently in multiple
connections could throw an exception.
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,21 +6,23 @@
*/
package org.h2.api;
import org.h2.table.RecreatableTable;
import org.h2.table.TableBase;
import org.h2.command.ddl.CreateTableData;
/**
* Class for creating custom table implementations
* A class that implements this interface can create custom table
* implementations.
*
* @author Sergi Vladykin
*/
public interface TableEngine {
/**
* Create new table
* @param data for table construction
* @return created table
* Create new table.
*
* @param data the data to construct the table
* @return the created table
*/
RecreatableTable createTable(CreateTableData data);
TableBase createTable(CreateTableData data);
}
......@@ -12,7 +12,7 @@ import org.h2.engine.Session;
import org.h2.result.ResultInterface;
import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.StatementBuilder;
/**
......@@ -37,7 +37,7 @@ public class Analyze extends DefineCommand {
session.getUser().checkAdmin();
// TODO do we need to lock the table?
for (Table table : db.getAllTablesAndViews(false)) {
if (!(table instanceof TableData)) {
if (!(table instanceof RegularTable)) {
continue;
}
StatementBuilder buff = new StatementBuilder("SELECT ");
......
......@@ -263,9 +263,6 @@ public class CreateTable extends SchemaCommand {
this.sortedInsertMode = sortedInsertMode;
}
/**
* @param tableEngine the table engine to set
*/
public void setTableEngine(String tableEngine) {
data.tableEngine = tableEngine;
}
......
......@@ -43,7 +43,7 @@ public class CreateTableData {
public boolean temporary;
/**
* Whether the table global temporary
* Whether the table is global temporary.
*/
public boolean globalTemporary;
......@@ -68,7 +68,7 @@ public class CreateTableData {
public Session session;
/**
* Table engine to use
* The table engine to use for creating the table.
*/
public String tableEngine;
......
......@@ -594,8 +594,10 @@ public class Session extends SessionWithState implements SessionFactory {
if (SysProperties.CHECK) {
int lockMode = database.getLockMode();
if (lockMode != Constants.LOCK_MODE_OFF && !database.isMultiVersion()) {
if (locks.indexOf(log.getTable()) < 0 && !Table.TABLE_LINK.equals(log.getTable().getTableType())
&& !Table.EXTERNAL_TABLE_ENGINE.equals(log.getTable().getTableType())) {
String type = log.getTable().getTableType();
if (locks.indexOf(log.getTable()) < 0
&& !Table.TABLE_LINK.equals(type)
&& !Table.EXTERNAL_TABLE_ENGINE.equals(type)) {
DbException.throwInternalError();
}
}
......
......@@ -12,7 +12,7 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
......@@ -26,10 +26,10 @@ public class HashIndex extends BaseIndex {
*/
protected final int indexColumn;
private final TableData tableData;
private final RegularTable tableData;
private ValueHashMap<Long> rows;
public HashIndex(TableData table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
public HashIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
initBaseIndex(table, id, indexName, columns, indexType);
this.indexColumn = columns[0].column.getColumnId();
this.tableData = table;
......
......@@ -17,7 +17,7 @@ import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -30,11 +30,11 @@ public class MultiVersionIndex implements Index {
private final Index base;
private final TreeIndex delta;
private final TableData table;
private final RegularTable table;
private final Object sync;
private final Column firstColumn;
public MultiVersionIndex(Index base, TableData table) {
public MultiVersionIndex(Index base, RegularTable table) {
this.base = base;
this.table = table;
IndexType deltaIndexType = IndexType.createNonUnique(false);
......
......@@ -10,7 +10,7 @@ import java.util.ArrayList;
import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
/**
* Cursor implementation for non-unique hash index
......@@ -21,11 +21,11 @@ public class NonUniqueHashCursor implements Cursor {
private final Session session;
private final ArrayList<Long> positions;
private final TableData tableData;
private final RegularTable tableData;
private int index = -1;
public NonUniqueHashCursor(Session session, TableData tableData, ArrayList<Long> positions) {
public NonUniqueHashCursor(Session session, RegularTable tableData, ArrayList<Long> positions) {
this.session = session;
this.tableData = tableData;
this.positions = positions;
......
......@@ -12,7 +12,7 @@ import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.New;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
......@@ -25,10 +25,10 @@ import org.h2.value.Value;
public class NonUniqueHashIndex extends HashIndex {
private ValueHashMap<ArrayList<Long>> rows;
private TableData tableData;
private RegularTable tableData;
private long rowCount;
public NonUniqueHashIndex(TableData table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
public NonUniqueHashIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
super(table, id, indexName, columns, indexType);
this.tableData = table;
reset();
......
......@@ -18,7 +18,7 @@ import org.h2.store.Page;
import org.h2.store.PageStore;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.MathUtils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -30,11 +30,11 @@ import org.h2.value.ValueNull;
public class PageBtreeIndex extends PageIndex {
private PageStore store;
private TableData tableData;
private RegularTable tableData;
private boolean needRebuild;
private long rowCount;
public PageBtreeIndex(TableData table, int id, String indexName, IndexColumn[] columns,
public PageBtreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns,
IndexType indexType, boolean create, Session session) {
initBaseIndex(table, id, indexName, columns, indexType);
// int test;
......
......@@ -24,7 +24,7 @@ import org.h2.store.Page;
import org.h2.store.PageStore;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.value.Value;
......@@ -38,7 +38,7 @@ import org.h2.value.ValueNull;
public class PageDataIndex extends PageIndex {
private PageStore store;
private TableData tableData;
private RegularTable tableData;
private long lastKey;
private long rowCount;
private HashSet<Row> delta;
......@@ -48,7 +48,7 @@ public class PageDataIndex extends PageIndex {
private DbException fastDuplicateKeyException;
private int memorySizePerPage;
public PageDataIndex(TableData table, int id, IndexColumn[] columns, IndexType indexType, boolean create, Session session) {
public PageDataIndex(RegularTable table, int id, IndexColumn[] columns, IndexType indexType, boolean create, Session session) {
initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType);
// trace = database.getTrace(Trace.PAGE_STORE + "_di");
......
......@@ -13,7 +13,7 @@ import org.h2.result.SearchRow;
import org.h2.store.PageStore;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
/**
* An index that delegates indexing to the page data index.
......@@ -22,7 +22,7 @@ public class PageDelegateIndex extends PageIndex {
private final PageDataIndex mainIndex;
public PageDelegateIndex(TableData table, int id, String name, IndexType indexType, PageDataIndex mainIndex, boolean create, Session session) {
public PageDelegateIndex(RegularTable table, int id, String name, IndexType indexType, PageDataIndex mainIndex, boolean create, Session session) {
IndexColumn[] cols = IndexColumn.wrap(new Column[] { table.getColumn(mainIndex.getMainIndexColumn())});
this.initBaseIndex(table, id, name, cols, indexType);
this.mainIndex = mainIndex;
......
......@@ -21,7 +21,7 @@ import org.h2.result.SearchRow;
import org.h2.store.LobStorage;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.New;
/**
......@@ -33,13 +33,13 @@ import org.h2.util.New;
public class ScanIndex extends BaseIndex {
private long firstFree = -1;
private ArrayList<Row> rows = New.arrayList();
private TableData tableData;
private RegularTable tableData;
private int rowCountDiff;
private HashMap<Integer, Integer> sessionRowCount;
private HashSet<Row> delta;
private long rowCount;
public ScanIndex(TableData table, int id, IndexColumn[] columns, IndexType indexType) {
public ScanIndex(RegularTable table, int id, IndexColumn[] columns, IndexType indexType) {
initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType);
if (database.isMultiVersion()) {
sessionRowCount = New.hashMap();
......
......@@ -11,13 +11,15 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* The cursor with at most one row.
* A cursor with at most one row.
*/
public class SingleRowCursor implements Cursor {
private Row row;
private boolean end;
/**
* Create a new cursor.
*
* @param row - the single row (if null then cursor is empty)
*/
public SingleRowCursor(Row row) {
......
......@@ -12,7 +12,7 @@ import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -22,10 +22,10 @@ import org.h2.value.ValueNull;
public class TreeIndex extends BaseIndex {
private TreeNode root;
private TableData tableData;
private RegularTable tableData;
private long rowCount;
public TreeIndex(TableData table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
public TreeIndex(RegularTable table, int id, String indexName, IndexColumn[] columns, IndexType indexType) {
initBaseIndex(table, id, indexName, columns, indexType);
tableData = table;
}
......
......@@ -183,7 +183,7 @@ Creates a new sequence."
CREATE [ CACHED | MEMORY | TEMP | [ GLOBAL | LOCAL ] TEMPORARY ]
TABLE [ IF NOT EXISTS ]
name { { ( { columnDefinition | constraint } [,...] ) [ AS select ] }
| { AS select } } [ NOT PERSISTENT ]
| { AS select } } [ ENGINE tableEngineName ] [ NOT PERSISTENT ]
","
Creates a new table."
"Commands (DDL)","CREATE TRIGGER","
......
......@@ -18,7 +18,7 @@ import org.h2.index.PageBtreeIndex;
import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.value.Value;
import org.h2.value.ValueArray;
......@@ -29,7 +29,7 @@ public class ResultTempTable implements ResultExternal {
private static final String COLUMN_NAME = "DATA";
private Session session;
private TableData table;
private RegularTable table;
private SortOrder sort;
private Index index;
private Cursor resultCursor;
......@@ -49,7 +49,7 @@ public class ResultTempTable implements ResultExternal {
data.persistData = true;
data.create = true;
data.session = session;
table = (TableData) schema.createTable(data);
table = (RegularTable) schema.createTable(data);
int indexId = session.getDatabase().allocateObjectId();
IndexColumn indexColumn = new IndexColumn();
indexColumn.column = column;
......
......@@ -23,7 +23,7 @@ import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.table.TableLink;
import org.h2.util.New;
import org.h2.util.Utils;
......@@ -498,7 +498,7 @@ public class Schema extends DbObjectBase {
}
return engine.createTable(data);
}
return new TableData(data);
return new RegularTable(data);
}
}
......
......@@ -41,7 +41,7 @@ import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.table.RegularTable;
import org.h2.util.Cache;
import org.h2.util.CacheLRU;
import org.h2.util.CacheObject;
......@@ -174,7 +174,7 @@ public class PageStore implements CacheWriter {
private PageLog log;
private Schema metaSchema;
private TableData metaTable;
private RegularTable metaTable;
private PageDataIndex metaIndex;
private IntIntHashMap metaRootPageId = new IntIntHashMap();
private HashMap<Integer, PageIndex> metaObjects = New.hashMap();
......@@ -1228,7 +1228,7 @@ public class PageStore implements CacheWriter {
data.persistIndexes = true;
data.create = false;
data.session = systemSession;
metaTable = new TableData(data);
metaTable = new RegularTable(data);
metaIndex = (PageDataIndex) metaTable.getScanIndex(
systemSession);
metaObjects.clear();
......@@ -1315,7 +1315,7 @@ public class PageStore implements CacheWriter {
data.persistIndexes = true;
data.create = false;
data.session = session;
TableData table = new TableData(data);
RegularTable table = new RegularTable(data);
CompareMode mode = CompareMode.getInstance(ops[0], Integer.parseInt(ops[1]));
table.setCompareMode(mode);
meta = table.getScanIndex(session);
......@@ -1324,7 +1324,7 @@ public class PageStore implements CacheWriter {
if (p == null) {
throw DbException.get(ErrorCode.FILE_CORRUPTED_1, "Table not found:" + parent + " for " + row + " meta:" + metaObjects);
}
TableData table = (TableData) p.getTable();
RegularTable table = (RegularTable) p.getTable();
Column[] tableCols = table.getColumns();
IndexColumn[] cols = new IndexColumn[columns.length];
for (int i = 0; i < columns.length; i++) {
......
......@@ -47,7 +47,7 @@ import org.h2.value.Value;
* 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.
*/
public class TableData extends RecreatableTable {
public class RegularTable extends TableBase {
private Index scanIndex;
private long rowCount;
private volatile Session lockExclusive;
......@@ -65,7 +65,7 @@ public class TableData extends RecreatableTable {
*/
private boolean waitForLock;
public TableData(CreateTableData data) {
public RegularTable(CreateTableData data) {
super(data);
if (data.persistData && database.isPersistent()) {
mainIndex = new PageDataIndex(this, data.id, IndexColumn.wrap(getColumns()), IndexType.createScan(data.persistData), data.create, data.session);
......@@ -483,8 +483,8 @@ public class TableData extends RecreatableTable {
buff.append(", ");
}
buff.append(t.toString());
if (t instanceof TableData) {
if (((TableData) t).lockExclusive == s) {
if (t instanceof RegularTable) {
if (((RegularTable) t).lockExclusive == s) {
buff.append(" (exclusive)");
} else {
buff.append(" (shared)");
......@@ -498,7 +498,7 @@ public class TableData extends RecreatableTable {
public ArrayList<Session> checkDeadlock(Session session, Session clash, Set<Session> visited) {
// only one deadlock check at any given time
synchronized (TableData.class) {
synchronized (RegularTable.class) {
if (clash == null) {
// verification is started
clash = session;
......
......@@ -11,15 +11,21 @@ import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
/**
* The base class of a regular table, or a user defined table.
*
* @author Thomas Mueller
* @author Sergi Vladykin
*/
public abstract class RecreatableTable extends Table {
public abstract class TableBase extends Table {
/**
* The table engine used (null for regular tables).
*/
protected final String tableEngine;
private final boolean globalTemporary;
public RecreatableTable(CreateTableData data) {
public TableBase(CreateTableData data) {
super(data.schema, data.id, data.tableName, data.persistIndexes, data.persistData);
this.tableEngine = data.tableEngine;
this.globalTemporary = data.globalTemporary;
......@@ -29,12 +35,10 @@ public abstract class RecreatableTable extends Table {
setColumns(cols);
}
@Override
public String getDropSQL() {
return "DROP TABLE IF EXISTS " + getSQL();
}
@Override
public String getCreateSQL() {
StatementBuilder buff = new StatementBuilder("CREATE ");
if (isTemporary()) {
......
......@@ -22,7 +22,7 @@ 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.TableBase;
import org.h2.table.Table;
import org.h2.test.TestBase;
......@@ -33,13 +33,24 @@ import org.h2.test.TestBase;
*/
public class TestTableEngines extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String[] a) throws Exception {
TestBase.createCaller().init().test();
}
public void test() throws Exception {
if (!config.mvcc) {
if (config.mvcc) {
return;
}
deleteDb("tableEngine");
Connection conn = getConnection("tableEngine");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE t1(id int, name varchar) ENGINE '" + OneRowTableEngine.class.getName() + "'");
stat.execute("CREATE TABLE t1(id int, name varchar) ENGINE \"" + OneRowTableEngine.class.getName() + "\"");
testStatements(stat);
......@@ -61,13 +72,13 @@ public class TestTableEngines extends TestBase {
}
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("INSERT INTO t1 VALUES(2, 'abc')"), 1);
assertEquals(stat.executeUpdate("UPDATE t1 SET name = 'abcdef' WHERE id=2"), 1);
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(3, 'abcdefghi')"), 1);
assertEquals(stat.executeUpdate("DELETE FROM t1 WHERE id=2"), 0);
assertEquals(stat.executeUpdate("DELETE FROM t1 WHERE id=3"), 1);
......@@ -76,40 +87,30 @@ public class TestTableEngines extends TestBase {
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);
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(2, 'abc')"), 1);
assertEquals(stat.executeUpdate("UPDATE t1 SET name = 'abcdef' WHERE id=2"), 1);
assertEquals(stat.executeUpdate("INSERT INTO t1 VALUES(3, 'abcdefghi')"), 1);
rs = stat.executeQuery("SELECT name FROM t1");
assertTrue(rs.next());
assertEquals(rs.getString(1), "ccc");
assertEquals(rs.getString(1), "abcdefghi");
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
* A test table factory.
*/
public static class OneRowTableEngine implements TableEngine {
/**
* Table implementation with one row
* A table implementation with one row.
*/
private static class OneRowTable extends RecreatableTable {
private static class OneRowTable extends TableBase {
/**
* One row scan index
* A scan index for one row.
*/
private class Scan extends BaseIndex {
......@@ -295,9 +296,12 @@ public class TestTableEngines extends TestBase {
}
/**
* Create new OneRowTable
* Create a new OneRowTable.
*
* @param data the meta data of the table to create
* @return the new table
*/
public OneRowTable createTable(final CreateTableData data) {
public OneRowTable createTable(CreateTableData data) {
return new OneRowTable(data);
}
}
......
......@@ -639,4 +639,4 @@ census genealogy scapegoat gov compacted migrating dies typtypmod latch await
counting dtest fallback infix places formal extern destination stdout memmove
stdio printf jchar sizeof stdlib jbyte jint uint ujlong typedef jdouble stdint
jfloat wchar hotspot jvoid std ujint jlong vars jboolean calloc argc strlen
equivalent synchronizes sullivan surname doe stepan
\ No newline at end of file
equivalent synchronizes sullivan surname doe stepan getstart
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论