/*
* Copyright 2004-2011 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.expression.Expression;
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.TableBase;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.test.TestBase;
import org.h2.value.Value;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
import org.h2.value.ValueString;
/**
* The class for external table engines mechanism testing.
*
* @author Sergi Vladykin
*/
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) {
return;
}
testEarlyFilter();
testSimpleQuery();
}
private void testEarlyFilter() throws SQLException {
deleteDb("tableEngine");
Connection conn = getConnection("tableEngine;EARLY_FILTER=TRUE");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE t1(id int, name varchar) ENGINE \"" + EndlessTableEngine.class.getName() + "\"");
ResultSet rs = stat.executeQuery("SELECT name FROM t1 where id=1 and name is not null");
assertTrue(rs.next());
assertEquals("((ID = 1)\n AND (NAME IS NOT NULL))", rs.getString(1));
rs.close();
conn.close();
deleteDb("tableEngine");
}
private void testSimpleQuery() throws SQLException {
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, '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);
ResultSet rs = stat.executeQuery("SELECT name FROM t1");
assertFalse(rs.next());
rs.close();
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), "abcdefghi");
assertFalse(rs.next());
rs.close();
}
/**
* A test table factory.
*/
public static class OneRowTableEngine implements TableEngine {
/**
* A table implementation with one row.
*/
private static class OneRowTable extends TableBase {
/**
* A scan index for one row.
*/
public 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);
}
public void checkRename() {
// do nothing
}
public void truncate(Session session) {
// do nothing
}
public void remove(Session session) {
// do nothing
}
public void remove(Session session, Row r) {
// do nothing
}
public boolean needRebuild() {
return false;
}
public double getCost(Session session, int[] masks) {
return 0;
}
public Cursor findFirstOrLast(Session session, boolean first) {
return new SingleRowCursor(row);
}
public Cursor find(Session session, SearchRow first, SearchRow last) {
return new SingleRowCursor(row);
}
public void close(Session session) {
// do nothing
}
public boolean canGetFirstOrLast() {
return true;
}
public void add(Session session, Row r) {
// do nothing
}
}
protected Index scanIndex;
volatile Row row;
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 a new OneRowTable.
*
* @param data the meta data of the table to create
* @return the new table
*/
public OneRowTable createTable(CreateTableData data) {
return new OneRowTable(data);
}
}
/**
* A test table factory.
*/
public static class EndlessTableEngine implements TableEngine {
/**
* A table implementation with one row.
*/
private static class EndlessTable extends OneRowTableEngine.OneRowTable {
EndlessTable(CreateTableData data) {
super(data);
row = new Row(new Value[] { ValueInt.get(1), ValueNull.INSTANCE }, 0);
scanIndex = new Auto(this);
}
/**
* A scan index for one row.
*/
public class Auto extends OneRowTableEngine.OneRowTable.Scan {
Auto(Table table) {
super(table);
}
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
return find(filter.getFilterCondition());
}
public Cursor find(Session session, SearchRow first, SearchRow last) {
return find(null);
}
/**
* Search within the table.
*
* @param filter the table filter (optional)
* @return the cursor
*/
private Cursor find(Expression filter) {
if (filter != null) {
row.setValue(1, ValueString.get(filter.getSQL()));
}
return new SingleRowCursor(row);
}
}
}
/**
* Create a new table.
*
* @param data the meta data of the table to create
* @return the new table
*/
public EndlessTable createTable(CreateTableData data) {
return new EndlessTable(data);
}
}
}