提交 1d66e95d authored 作者: Thomas Mueller's avatar Thomas Mueller

javadoc

上级 4830b307
......@@ -260,8 +260,9 @@
<mkdir dir="docs/javadocImpl"/>
<javadoc
sourcepath="src/main"
packagenames="org.h2.jdbc.*,org.h2.tools.*,org.h2.api.*,org.h2.store.*"
packagenames="org.h2.*"
destDir="docs/javadocImpl"
classpath="${path.lucene.jar};${path.servlet.jar}"
/> <!-- doclet="org.h2.tools.doclet.Doclet" docletpath="bin"-->
<copy todir="docs/javadoc">
<fileset dir="src/docsrc/javadoc" includes="**/*"/>
......
......@@ -16,8 +16,8 @@ import org.h2.message.Message;
import org.h2.message.TraceSystem;
/**
* The database driver. An application should not use this class directly.
* The only thing the application needs to do is load the driver. This can be done
* The database driver. An application should not use this class directly.
* The only thing the application needs to do is load the driver. This can be done
* using Class.forName:
* <pre>
* Class.forName("org.h2.Driver");
......@@ -37,7 +37,7 @@ public class Driver implements java.sql.Driver {
/**
* This method should not be called by an application.
*
*
* @return the new connection
*/
public Connection connect(String url, Properties info) throws SQLException {
......@@ -58,7 +58,7 @@ public class Driver implements java.sql.Driver {
/**
* This method should not be called by an application.
*
*
* @return if the driver understands the URL
*/
public boolean acceptsURL(String url) {
......@@ -67,7 +67,7 @@ public class Driver implements java.sql.Driver {
/**
* This method should not be called by an application.
*
*
* @return the major version number
*/
public int getMajorVersion() {
......@@ -76,7 +76,7 @@ public class Driver implements java.sql.Driver {
/**
* This method should not be called by an application.
*
*
* @return the minor version number
*/
public int getMinorVersion() {
......@@ -85,7 +85,7 @@ public class Driver implements java.sql.Driver {
/**
* This method should not be called by an application.
*
*
* @return a zero length array
*/
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
......@@ -94,13 +94,13 @@ public class Driver implements java.sql.Driver {
/**
* This method should not be called by an application.
*
*
* @return true
*/
public boolean jdbcCompliant() {
return true;
}
/**
* INTERNAL
*/
......
......@@ -15,6 +15,8 @@ import java.sql.SQLException;
public interface Trigger {
int INSERT = 1, UPDATE = 2, DELETE = 4;
/**
* This method is called by the database engine once when initializing the trigger.
*
......@@ -22,8 +24,10 @@ public interface Trigger {
* @param schemaName the name of the schema
* @param triggerName the name of the trigger used in the CREATE TRIGGER statement
* @param tableName the name of the table
* @param before whether the fire method is called before or after the operation is performed
* @param type the operation type: INSERT, UPDATE, or DELETE
*/
void init(Connection conn, String schemaName, String triggerName, String tableName) throws SQLException;
void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException;
/**
* This method is called for each triggered action.
......
......@@ -10,6 +10,7 @@ import java.sql.SQLException;
import java.text.Collator;
import java.util.HashSet;
import org.h2.api.Trigger;
import org.h2.command.ddl.AlterIndexRename;
import org.h2.command.ddl.AlterSequence;
import org.h2.command.ddl.AlterTableAddConstraint;
......@@ -112,7 +113,6 @@ import org.h2.message.Message;
import org.h2.result.SortOrder;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.table.Column;
import org.h2.table.FunctionTable;
import org.h2.table.IndexColumn;
......@@ -3391,11 +3391,11 @@ public class Parser {
int typeMask = 0;
do {
if (readIf("INSERT")) {
typeMask |= TriggerObject.INSERT;
typeMask |= Trigger.INSERT;
} else if (readIf("UPDATE")) {
typeMask |= TriggerObject.UPDATE;
typeMask |= Trigger.UPDATE;
} else if (readIf("DELETE")) {
typeMask |= TriggerObject.DELETE;
typeMask |= Trigger.DELETE;
} else {
throw getSyntaxError();
}
......
......@@ -12,57 +12,152 @@ import org.h2.util.ObjectArray;
* A database object such as a table, an index, or a user.
*/
public interface DbObject {
int TABLE_OR_VIEW = 0;
int INDEX = 1;
int USER = 2;
int SEQUENCE = 3;
int TABLE_OR_VIEW = 0;
int TRIGGER = 4;
int USER = 2;
int CONSTRAINT = 5;
int FUNCTION_ALIAS = 9;
int RIGHT = 8;
int ROLE = 7;
int SETTING = 6;
int CONSTANT = 11;
int ROLE = 7;
int RIGHT = 8;
int FUNCTION_ALIAS = 9;
int SCHEMA = 10;
int COMMENT = 13;
int CONSTANT = 11;
int USER_DATATYPE = 12;
int COMMENT = 13;
int AGGREGATE = 14;
/**
* Tell the object that is was modified.
*/
void setModified();
/**
* Get the last modification id.
*
* @return the modification id
*/
long getModificationId();
/**
* Get the SQL name of this object (may be quoted).
*
* @return the SQL name
*/
String getSQL();
/**
* Get the list of dependent children (for tables, this includes indexes and so on).
*
* @return the list of children
*/
ObjectArray getChildren();
/**
* Get the database.
*
* @return the database
*/
Database getDatabase();
/**
* Get the unique object id.
*
* @return the object id
*/
int getId();
/**
* Get the name.
*
* @return the name
*/
String getName();
/**
* Construct a CREATE ... SQL statement for this object when creating a copy of it.
*
* @param table the new table
* @param quotedName the quoted name
* @return the SQL statement
*/
String getCreateSQLForCopy(Table table, String quotedName);
/**
* Construct the original CREATE ... SQL statement for this object.
*
* @return the SQL statement
*/
String getCreateSQL();
/**
* Construct a DROP ... SQL statement for this object.
*
* @return the SQL statement
*/
String getDropSQL();
/**
* Get the object type.
*
* @return the object type
*/
int getType();
/**
* Delete all dependent children objects and resources of this object.
*
* @param session the session
*/
void removeChildrenAndResources(Session session) throws SQLException;
/**
* Check if renaming is allowed. Does nothing when allowed.
*
* @throws SQLException if renaming is not allowed
*/
void checkRename() throws SQLException;
/**
* Rename the object.
*
* @param newName the new name
*/
void rename(String newName) throws SQLException;
/**
* Check if this object is temporary (for example, a temporary table).
*
* @return true if is temporary
*/
boolean getTemporary();
/**
* Tell this object that it is temporary or not.
*
* @param temporary the new value
*/
void setTemporary(boolean temporary);
/**
* Change the comment of this object.
*
* @param comment the new comment, or null for no comment
*/
void setComment(String comment);
/**
* Get the current comment of this object.
*
* @return the comment, or null if not set
*/
String getComment();
/**
* Get the position of the head record.
*
* @return the head position
*/
int getHeadPos();
}
......@@ -32,7 +32,6 @@ public abstract class Expression {
/**
* Return the data type. The data type may not be known before the optimization phase.
*
* @param session the session
* @return the type
*/
public abstract int getType();
......
......@@ -45,7 +45,6 @@ public class FullText implements Trigger {
* only have one index at any time.
*
* @param conn the connection
* @param name the name of the index (must be unique)
* @param schema the schema name of the table
* @param table the table name
* @param columnList the column list (null for all columns)
......@@ -79,7 +78,7 @@ public class FullText implements Trigger {
private static void indexExistingRows(Connection conn, String schema, String table) throws SQLException {
FullText existing = new FullText();
existing.init(conn, schema, null, table);
existing.init(conn, schema, null, table, false, INSERT);
StringBuffer buff = new StringBuffer("SELECT * FROM ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
ResultSet rs = conn.createStatement().executeQuery(buff.toString());
......@@ -232,7 +231,7 @@ public class FullText implements Trigger {
/**
* INTERNAL
*/
public void init(Connection conn, String schemaName, String triggerName, String tableName) throws SQLException {
public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException {
init(conn);
FullTextSettings setting = FullTextSettings.getInstance(conn);
ArrayList keyList = new ArrayList();
......
......@@ -65,7 +65,6 @@ implements Trigger
* Create a new full text index for a table and column list. Each table may only have one index at any time.
*
* @param conn the connection
* @param name the name of the index (must be unique)
* @param schema the schema name of the table
* @param table the table name
* @param columnList the column list (null for all columns)
......@@ -153,7 +152,7 @@ implements Trigger
* INTERNAL
*/
//#ifdef JDK14
public void init(Connection conn, String schemaName, String triggerName, String tableName) throws SQLException {
public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException {
init(conn);
this.schemaName = schemaName;
this.tableName = tableName;
......@@ -480,7 +479,7 @@ implements Trigger
private static void indexExistingRows(Connection conn, String schema, String table) throws SQLException {
FullTextLucene existing = new FullTextLucene();
existing.init(conn, schema, null, table);
existing.init(conn, schema, null, table, false, INSERT);
StringBuffer buff = new StringBuffer("SELECT * FROM ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
ResultSet rs = conn.createStatement().executeQuery(buff.toString());
......
......@@ -13,7 +13,7 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* @author Thomas
* The cursor implementation for the b tree index.
*/
public class BtreeCursor implements Cursor {
private BtreeIndex index;
......
......@@ -9,6 +9,11 @@ import java.sql.SQLException;
import org.h2.store.DataPage;
import org.h2.store.Record;
/**
* The head page of a b-tree index. There is exactly one head page for each
* such index, and it contains meta data such as the location of the root page.
* Unlike the root page of a b-tree index, the head page always stays at the same place.
*/
public class BtreeHead extends Record {
private int rootPosition;
......@@ -22,11 +27,11 @@ public class BtreeHead extends Record {
rootPosition = s.readInt();
consistent = s.readInt() == 1;
}
public boolean getConsistent() {
return consistent;
}
public void setConsistent(boolean b) {
this.consistent = b;
}
......@@ -48,7 +53,7 @@ public class BtreeHead extends Record {
int getRootPosition() {
return rootPosition;
}
public boolean isPinned() {
return true;
}
......
......@@ -20,6 +20,8 @@ import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* An outer page of a btree index.
*
* Page format:
* L { P(pointers) | D(data) } data.len { data[0].pos [data[0]], ... }
*
......
......@@ -20,6 +20,8 @@ import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* An inner page of a b-tree index.
*
* Page format:
* N children.len children[0..len] data.len { data[0].pos [data[0]], ... }
*
......
......@@ -17,11 +17,11 @@ import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* @author Thomas
* An abstract b-tree page.
*/
public abstract class BtreePage extends Record {
// TODO btree: make sure the indexed data is at most half this size! (and find a solution to work around this problem!)
// TODO memory: the btree page needs a lot of memory (in the cache) - probably better not use ObjectArray but array;
// TODO memory: the btree page needs a lot of memory (in the cache) - probably better not use ObjectArray but array;
// not Row but ValueList / Value (for single key index), int array for row pos
protected static final int BLOCKS_PER_PAGE = 1024 / DiskFile.BLOCK_SIZE;
......
......@@ -5,7 +5,7 @@
package org.h2.index;
/**
* @author Thomas
* Represents a position of a b-tree index.
*/
class BtreePosition {
int position;
......
......@@ -9,12 +9,45 @@ import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* A cursor is a helper object to iterate through an index.
* For indexes are sorted (such as the b tree index), it can iterate
* to the very end of the index. For other indexes that don't support
* that (such as a hash index), only one row is returned.
* The cursor is initially positioned before the first row, that means
* next() must be called before accessing data.
*
*/
public interface Cursor {
/**
* Get the complete current row.
* All column are available.
*
* @return the complete row
*/
Row get() throws SQLException;
/**
* Get the current row.
* Only the data for indexed columns is available in this row.
*
* @return the search row
*/
SearchRow getSearchRow() throws SQLException;
/**
* Get the position of the current row.
*
* @return the position
*/
int getPos();
/**
* Skip to the next row if one is available.
*
* @return true if another row is available
*/
boolean next() throws SQLException;
}
......@@ -11,11 +11,14 @@ import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* A cursor for a function that returns a result set.
*/
public class FunctionCursor implements Cursor {
private LocalResult result;
private Row row;
private Row row;
FunctionCursor(LocalResult result) {
this.result = result;
}
......@@ -23,7 +26,7 @@ public class FunctionCursor implements Cursor {
public Row get() {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
......
......@@ -15,11 +15,15 @@ import org.h2.result.SearchRow;
import org.h2.table.FunctionTable;
import org.h2.table.IndexColumn;
/**
* An index for a function that returns a result set.
* This index can only scan through all rows, search is not supported.
*/
public class FunctionIndex extends BaseIndex {
private FunctionTable functionTable;
private LocalResult result;
public FunctionIndex(FunctionTable functionTable, IndexColumn[] columns, FunctionCall function) {
super(functionTable, 0, null, columns, IndexType.createNonUnique(true));
this.functionTable = functionTable;
......
......@@ -10,7 +10,8 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* @author Thomas
* The cursor for a hash index.
* At most one row can be accessed.
*/
public class HashCursor implements Cursor {
private Row row;
......@@ -23,11 +24,11 @@ public class HashCursor implements Cursor {
public Row get() {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
public int getPos() {
return row.getPos();
}
......
......@@ -20,7 +20,7 @@ import org.h2.value.Value;
import org.h2.value.ValueArray;
/**
* @author Thomas
* An index based on an in-memory hash map.
*/
public class HashIndex extends BaseIndex {
......
......@@ -14,58 +14,210 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
/**
* An index. Indexes are used to speed up searching data.
*/
public interface Index extends SchemaObject {
/**
* Indicates that there is no head record yet.
*/
int EMPTY_HEAD = -1;
/**
* Create a duplicate key exception with a message that contains the index name
*
* @return the exception
*/
SQLException getDuplicateKeyException();
/**
* Get the message to show in a EXPLAIN statement.
*
* @return the plan
*/
String getPlanSQL();
/**
* Close this index.
*
* @param session the session used to write data
*/
void close(Session session) throws SQLException;
/**
* Add a row to the index.
*
* @param session the session to use
* @param row the data
*/
void add(Session session, Row row) throws SQLException;
/**
* Remove a row from the index.
*
* @param session the session
* @param row the data
*/
void remove(Session session, Row row) throws SQLException;
/**
* Find a row or a list of rows and create a cursor to iterate over the result.
*
* @param session the session
* @param first the first row, or null for no limit
* @param last the last row, or null for no limit
* @return the cursor
*/
Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException;
/**
* Estimate the cost to search for rows given the search mask.
*
* @param session the session
* @param masks the search mask
* @return the estimated cost
*/
double getCost(Session session, int[] masks) throws SQLException;
/**
* Remove the index.
*
* @param session the session
*/
void remove(Session session) throws SQLException;
/**
* Remove all rows from the index.
*
* @param session the session
*/
void truncate(Session session) throws SQLException;
/**
* Check if the index can directly look up the lowest or highest value of a column.
*
* @return true if it can
*/
boolean canGetFirstOrLast();
/**
* Find the lowest or highest value of a column.
*
* @param session the session
* @param first true if the first (lowest for ascending indexes) or last value should be returned
* @return the search row with the value
*/
SearchRow findFirstOrLast(Session session, boolean first) throws SQLException;
/**
* Check if the index needs to be rebuilt.
* This method is called after opening an index.
*
* @return true if a rebuild is required.
*/
boolean needRebuild();
/**
* Get the row count of this table, for the given session.
*
* @param session the session
* @return the row count
*/
long getRowCount(Session session);
/**
* Estimate the cost required to search a number of rows.
*
* @param rowCount the row count
* @return the estimated cost
*/
int getLookupCost(long rowCount);
/**
* Estimate the cost required to search one row, and then iterate over the given number of rows.
*
* @param masks the search mask
* @param rowCount the row count
* @return the estimated cost
*/
long getCostRangeIndex(int[] masks, long rowCount) throws SQLException;
/**
* Compare two rows.
*
* @param rowData the first row
* @param compare the second row
* @return 0 if both rows are equal, -1 if the first row is smaller, otherwise 1
*/
int compareRows(SearchRow rowData, SearchRow compare) throws SQLException;
/**
* Check if a row is NULL.
*
* @param newRow
* @return if it is null
*/
boolean isNull(Row newRow);
/**
* Compare the positions of two rows.
*
* @param rowData the first row
* @param compare the second row
* @return 0 if both rows are equal, -1 if the first row is smaller, otherwise 1
*/
int compareKeys(SearchRow rowData, SearchRow compare);
/**
* Get the index of a column in the list of index columns
*
* @param col the column
* @return the index (0 meaning first column)
*/
int getColumnIndex(Column col);
/**
* Get the list of columns as a string.
*
* @return the list of columns
*/
String getColumnListSQL();
/**
* Get the indexed columns as index columns (with ordering information).
*
* @return the index columns
*/
IndexColumn[] getIndexColumns();
/**
* Get the indexed columns.
*
* @return the columns
*/
Column[] getColumns();
/**
* Get the index type.
*
* @return the index type
*/
IndexType getIndexType();
/**
* Get the table on which this index is based.
*
* @return the table
*/
Table getTable();
/**
* Commit the operation for a row. This is only important for multi-version indexes.
*
* @param operation the operation type
* @param row the row
*/
void commit(int operation, Row row) throws SQLException;
}
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
/**
* @author Thomas
* Represents information about the properties of an index
*/
public class IndexType {
private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan, isDescending;
private boolean belongsToConstraint;
public static IndexType createPrimaryKey(boolean persistent, boolean hash) {
IndexType type = new IndexType();
type.isPrimaryKey = true;
type.isPersistent = persistent;
type.isHash = hash;
type.isHash = hash;
type.isUnique = true;
return type;
return type;
}
public static IndexType createUnique(boolean persistent, boolean hash) {
IndexType type = new IndexType();
IndexType type = new IndexType();
type.isUnique = true;
type.isPersistent = persistent;
type.isHash = hash;
return type;
}
public static IndexType createNonUnique(boolean persistent) {
IndexType type = new IndexType();
IndexType type = new IndexType();
type.isPersistent = persistent;
return type;
}
public static IndexType createScan(boolean persistent) {
IndexType type = new IndexType();
IndexType type = new IndexType();
type.isPersistent = persistent;
type.isScan = true;
return type;
}
public void setDescending(boolean descending) {
this.isDescending = descending;
}
public boolean getDescending() {
return isDescending;
}
public void setBelongsToConstraint(boolean belongsToConstraint) {
this.belongsToConstraint = belongsToConstraint;
}
public boolean belongsToConstraint() {
return belongsToConstraint;
}
}
public boolean isHash() {
return isHash;
}
......@@ -69,7 +69,7 @@ public class IndexType {
public boolean isUnique() {
return isUnique;
}
public String getSQL() {
StringBuffer buff = new StringBuffer();
if (isPrimaryKey) {
......@@ -83,7 +83,7 @@ public class IndexType {
}
if (isHash) {
buff.append("HASH ");
}
}
buff.append("INDEX");
}
return buff.toString();
......
......@@ -13,17 +13,17 @@ import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* @author Thomas
* A index page of a linear hash index.
*/
public class LinearHashBucket extends Record {
private LinearHashIndex index;
private int nextBucket;
private ObjectArray records;
private boolean writePos;
public LinearHashBucket(LinearHashIndex index, DataPage s) throws SQLException {
this.index = index;
writePos = s.readByte() == 'P';
writePos = s.readByte() == 'P';
nextBucket = s.readInt();
int len = s.readInt();
records = new ObjectArray();
......@@ -38,8 +38,8 @@ public class LinearHashBucket extends Record {
entry.home = index.getPos(entry.hash);
records.add(entry);
}
}
}
public LinearHashBucket(LinearHashIndex index) {
this.index = index;
this.records = new ObjectArray();
......@@ -49,8 +49,8 @@ public class LinearHashBucket extends Record {
private void update(Session session) throws SQLException {
index.updateBucket(session, this);
}
void setNext(Session session, int nextBucket) throws SQLException {
void setNext(Session session, int nextBucket) throws SQLException {
this.nextBucket = nextBucket;
update(session);
}
......@@ -118,10 +118,10 @@ public class LinearHashBucket extends Record {
writePos = false;
return size + dataSize;
}
}
}
public boolean isEmpty() {
return false;
}
}
......@@ -10,7 +10,7 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* @author Thomas
* The cursor implementation for the linear hash index.
*/
public class LinearHashCursor implements Cursor {
private Row row;
......@@ -23,11 +23,11 @@ public class LinearHashCursor implements Cursor {
public Row get() {
return row;
}
public SearchRow getSearchRow() throws SQLException {
return row;
}
public int getPos() {
return row.getPos();
}
......@@ -40,5 +40,5 @@ public class LinearHashCursor implements Cursor {
end = true;
return true;
}
}
......@@ -7,17 +7,17 @@ package org.h2.index;
import org.h2.value.Value;
/**
* @author Thomas
* An index entry of a linear hash index.
*/
public class LinearHashEntry {
// private LinearHashEntry(int home, int hash, Value key, int value) {
// this.home = home;
// this.hash = hash;
// this.key = key;
// this.value = value;
// }
public int home;
public int hash;
public Value key;
......
......@@ -9,6 +9,9 @@ import java.sql.SQLException;
import org.h2.store.DataPage;
import org.h2.store.Record;
/**
* The head page of a linear hash index.
*/
public class LinearHashHead extends Record {
private LinearHashIndex index;
......@@ -19,7 +22,7 @@ public class LinearHashHead extends Record {
LinearHashHead(LinearHashIndex index) {
this.index = index;
}
LinearHashHead(LinearHashIndex index, DataPage s) {
this.index = index;
baseSize = s.readInt();
......@@ -39,7 +42,7 @@ public class LinearHashHead extends Record {
buff.writeInt(recordCount);
buff.writeInt(bucketCount);
}
public boolean isPinned() {
return true;
}
......
......@@ -26,7 +26,9 @@ import org.h2.value.Value;
import org.h2.value.ValueArray;
/**
* @author Thomas
* A linear hash index implementation.
* Theoretically, this index type should scale better than a b-tree index.
* At this time, this index is not fully tested.
*/
public class LinearHashIndex extends BaseIndex implements RecordReader {
......
......@@ -17,9 +17,8 @@ import org.h2.value.DataType;
import org.h2.value.Value;
/**
* @author Thomas
* The cursor implementation for the linked index.
*/
public class LinkedCursor implements Cursor {
private Session session;
......@@ -36,15 +35,15 @@ public class LinkedCursor implements Cursor {
public Row get() {
return current;
}
public SearchRow getSearchRow() throws SQLException {
return current;
}
public int getPos() {
throw Message.getInternalError();
}
public boolean next() throws SQLException {
boolean result = rs.next();
......
......@@ -20,27 +20,27 @@ import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* @author Thomas
* A linked index is a index for a linked (remote) table.
* It is backed by an index on the remote table which is accessed over JDBC.
*/
public class LinkedIndex extends BaseIndex {
private TableLink link;
private String targetTableName;
public LinkedIndex(TableLink table, int id, IndexColumn[] columns, IndexType indexType) {
super(table, id, null, columns, indexType);
link = table;
targetTableName = link.getQualifiedTable();
}
public String getCreateSQL() {
return null;
}
public void close(Session session) throws SQLException {
}
private boolean isNull(Value v) {
return v == null || v == ValueNull.INSTANCE;
}
......
......@@ -11,29 +11,28 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.util.ObjectArray;
/**
* @author Thomas
* An index for a meta data table.
* This index can only scan through all rows, search is not supported.
*/
public class MetaCursor implements Cursor {
private Row current;
private ObjectArray rows;
private int index;
MetaCursor(ObjectArray rows) {
this.rows = rows;
}
public Row get() {
return current;
}
public SearchRow getSearchRow() throws SQLException {
return current;
}
public int getPos() {
throw Message.getInternalError();
}
......
......@@ -14,22 +14,20 @@ import org.h2.table.IndexColumn;
import org.h2.table.MetaTable;
import org.h2.util.ObjectArray;
/**
* @author Thomas
* The index implementation for meta data tables.
*/
public class MetaIndex extends BaseIndex {
private MetaTable meta;
private boolean scan;
public MetaIndex(MetaTable meta, IndexColumn[] columns, boolean scan) {
super(meta, 0, null, columns, IndexType.createNonUnique(true));
this.meta = meta;
this.scan = scan;
}
public void close(Session session) throws SQLException {
// nothing to do
}
......@@ -77,7 +75,7 @@ public class MetaIndex extends BaseIndex {
public boolean needRebuild() {
return false;
}
public String getCreateSQL() {
return null;
}
......@@ -88,6 +86,6 @@ public class MetaIndex extends BaseIndex {
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
}
}
......@@ -11,8 +11,11 @@ import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* The cursor implementation for the multi-version index.
*/
public class MultiVersionCursor implements Cursor {
private final MultiVersionIndex index;
private final Session session;
private final Cursor baseCursor, deltaCursor;
......@@ -22,7 +25,7 @@ public class MultiVersionCursor implements Cursor {
private boolean onBase;
private boolean end;
private boolean needNewDelta, needNewBase;
MultiVersionCursor(Session session, MultiVersionIndex index, Cursor base, Cursor delta, Object sync) throws SQLException {
this.session = session;
this.index = index;
......@@ -31,7 +34,7 @@ public class MultiVersionCursor implements Cursor {
this.sync = sync;
needNewDelta = needNewBase = true;
}
private void loadNext(boolean base) throws SQLException {
synchronized (sync) {
if (base) {
......
......@@ -17,14 +17,19 @@ import org.h2.table.Table;
import org.h2.table.TableData;
import org.h2.util.ObjectArray;
/**
* A multi-version index is a combination of a regular index,
* and a in-memory tree index that contains uncommitted changes.
* Uncommitted changes can include new rows, and deleted rows.
*/
public class MultiVersionIndex implements Index {
private final Index base;
private final TreeIndex delta;
private final TableData table;
private final Object sync;
public MultiVersionIndex(Index base, TableData table) throws SQLException {
public MultiVersionIndex(Index base, TableData table) throws SQLException {
this.base = base;
this.table = table;
IndexType deltaIndexType = IndexType.createNonUnique(false);
......@@ -62,7 +67,7 @@ public class MultiVersionIndex implements Index {
// TODO in many cases possible, but more complicated
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
throw Message.getUnsupportedException();
}
......@@ -74,7 +79,7 @@ public class MultiVersionIndex implements Index {
public boolean needRebuild() {
return base.needRebuild();
}
private boolean removeIfExists(Session session, Row row) throws SQLException {
// maybe it was inserted by the same session just before
Cursor c = delta.find(session, row, row);
......@@ -111,7 +116,7 @@ public class MultiVersionIndex implements Index {
base.truncate(session);
}
}
public void commit(int operation, Row row) throws SQLException {
synchronized (sync) {
removeIfExists(null, row);
......@@ -137,10 +142,10 @@ public class MultiVersionIndex implements Index {
public Column[] getColumns() {
return base.getColumns();
}
public IndexColumn[] getIndexColumns() {
return base.getIndexColumns();
}
}
public long getCostRangeIndex(int[] masks, long rowCount) throws SQLException {
return base.getCostRangeIndex(masks, rowCount);
......@@ -256,7 +261,7 @@ public class MultiVersionIndex implements Index {
public void setTemporary(boolean temporary) {
base.setTemporary(temporary);
}
void debug(String s, Session session, SearchRow row) throws SQLException {
// System.out.println(this + " " + s + " session:" + (session == null ? -1: session.getId()) + " " + (row == null ? "" : row.getValue(0).getString()));
}
......
......@@ -12,23 +12,26 @@ import org.h2.result.SearchRow;
import org.h2.value.Value;
import org.h2.value.ValueLong;
/**
* The cursor implementation for the range index.
*/
public class RangeCursor implements Cursor {
private boolean beforeFirst;
private long current;
private Row currentRow;
private long min, max;
RangeCursor(long min, long max) {
this.min = min;
this.max = max;
beforeFirst = true;
}
public Row get() {
return currentRow;
}
public SearchRow getSearchRow() throws SQLException {
return currentRow;
}
......
......@@ -15,10 +15,14 @@ import org.h2.table.RangeTable;
import org.h2.value.Value;
import org.h2.value.ValueLong;
/**
* An index for the SYSTEM_RANGE table.
* This index can only scan through all rows, search is not supported.
*/
public class RangeIndex extends BaseIndex {
private long min, max;
private long min, max;
public RangeIndex(RangeTable table, IndexColumn[] columns, long min, long max) {
super(table, 0, "RANGE_INDEX", columns, IndexType.createNonUnique(true));
this.min = min;
......
......@@ -10,9 +10,8 @@ import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* @author Thomas
* The cursor implementation for the scan index.
*/
public class ScanCursor implements Cursor {
private ScanIndex scan;
......
......@@ -26,7 +26,9 @@ import org.h2.value.Value;
import org.h2.value.ValueLob;
/**
* @author Thomas
* The scan index is not really an 'index' in the strict sense, because it can not be used for direct lookup.
* It can only be used to iterate over all rows of a table.
* Each regular table has one such object, even if no primary key or indexes are defined.
*/
public class ScanIndex extends BaseIndex {
private int firstFree = -1;
......
......@@ -9,7 +9,9 @@ import java.sql.SQLException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
/**
* The cursor implementation for a tree index.
*/
public class TreeCursor implements Cursor {
private TreeIndex tree;
private TreeNode node;
......
......@@ -16,7 +16,9 @@ import org.h2.table.TableData;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* The tree index is an in-memory index based on a binary AVL trees.
*/
public class TreeIndex extends BaseIndex {
private TreeNode root;
......
......@@ -6,6 +6,9 @@ package org.h2.index;
import org.h2.result.Row;
/**
* Represents a index node of a tree index.
*/
public class TreeNode {
int balance;
TreeNode left, right, parent;
......
......@@ -14,25 +14,28 @@ import org.h2.table.Table;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* The cursor implementation of a view index.
*/
public class ViewCursor implements Cursor {
private Table table;
private LocalResult result;
private Row current;
ViewCursor(Table table, LocalResult result) {
this.table = table;
this.result = result;
}
public Row get() {
return current;
}
public SearchRow getSearchRow() {
return current;
}
public int getPos() {
throw Message.getInternalError();
}
......@@ -52,5 +55,5 @@ public class ViewCursor implements Cursor {
}
return true;
}
}
......@@ -23,8 +23,7 @@ import org.h2.value.Value;
/**
* This object represents a virtual index for a query.
* Actually it only represents a prepared statement that is
* a SELECT statement.
* Actually it only represents a prepared SELECT statement.
*/
public class ViewIndex extends BaseIndex {
......@@ -36,7 +35,7 @@ public class ViewIndex extends BaseIndex {
private String planSQL;
private Query query;
private Session session;
public ViewIndex(TableView view, String querySQL, ObjectArray originalParameters, boolean recursive) {
super(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = querySQL;
......@@ -44,7 +43,7 @@ public class ViewIndex extends BaseIndex {
this.recursive = recursive;
columns = new Column[0];
}
public ViewIndex(TableView view, ViewIndex index, Session session, int[] masks) throws SQLException {
super(view, 0, null, null, IndexType.createNonUnique(false));
this.querySQL = index.querySQL;
......@@ -56,7 +55,7 @@ public class ViewIndex extends BaseIndex {
query = getQuery(session, masks);
planSQL = query.getPlanSQL();
}
public Session getSession() {
return session;
}
......@@ -75,12 +74,12 @@ public class ViewIndex extends BaseIndex {
public void remove(Session session, Row row) throws SQLException {
throw Message.getUnsupportedException();
}
private static class CostElement {
long evaluatedAt;
double cost;
}
public double getCost(Session session, int[] masks) throws SQLException {
IntArray masksArray = new IntArray(masks == null ? new int[0] : masks);
CostElement cachedCost = (CostElement) costCache.get(masksArray);
......
......@@ -15,13 +15,6 @@ import java.sql.Date;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
//#ifdef JDK16
/*
import java.sql.NClob;
import java.sql.RowId;
import java.sql.SQLXML;
*/
//#endif
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
......@@ -37,10 +30,10 @@ import org.h2.message.Message;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.result.UpdatableRow;
import org.h2.util.ByteUtils;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.ObjectUtils;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
......@@ -437,7 +430,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet {
Value v = get(columnIndex);
if (Constants.SERIALIZE_JAVA_OBJECTS) {
if (v.getType() == Value.JAVA_OBJECT) {
return ByteUtils.deserialize(v.getBytesNoCopy());
return ObjectUtils.deserialize(v.getBytesNoCopy());
}
}
return v.getObject();
......@@ -459,7 +452,7 @@ public class JdbcResultSet extends TraceObject implements ResultSet {
Value v = get(columnName);
if (Constants.SERIALIZE_JAVA_OBJECTS) {
if (v.getType() == Value.JAVA_OBJECT) {
return ByteUtils.deserialize(v.getBytesNoCopy());
return ObjectUtils.deserialize(v.getBytesNoCopy());
}
}
return v.getObject();
......
......@@ -36,25 +36,25 @@ import org.h2.message.Message;
* A data source for H2 database connections. It is a factory for XAConnection and Connection objects.
* This class is usually registered in a JNDI naming service.
*/
public class JdbcDataSource extends TraceObject
public class JdbcDataSource extends TraceObject
//#ifdef JDK14
implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Referenceable
//#endif
{
private static final long serialVersionUID = 1288136338451857771L;
private transient JdbcDataSourceFactory factory;
private transient PrintWriter logWriter;
private int loginTimeout;
private String user = "";
private String password = "";
private String url = "";
static {
org.h2.Driver.load();
}
/**
* Public constructor.
*/
......@@ -63,19 +63,19 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
int id = getNextId(TraceObject.DATA_SOURCE);
setTrace(factory.getTrace(), TraceObject.DATA_SOURCE, id);
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
initFactory();
in.defaultReadObject();
}
private void initFactory() {
factory = new JdbcDataSourceFactory();
}
/**
* Get the login timeout in seconds, 0 meaning no timeout.
*
*
* @return the timeout in seconds
*/
public int getLoginTimeout() throws SQLException {
......@@ -87,7 +87,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
* Set the login timeout in seconds, 0 meaning no timeout.
* The default value is 0.
* This value is ignored by this database.
*
*
* @param timeout the timeout in seconds
*/
public void setLoginTimeout(int timeout) throws SQLException {
......@@ -97,7 +97,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Get the current log writer for this object.
*
*
* @return the log writer
*/
public PrintWriter getLogWriter() throws SQLException {
......@@ -108,7 +108,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Set the current log writer for this object.
* This value is ignored by this database.
*
*
* @param out the log writer
*/
public void setLogWriter(PrintWriter out) throws SQLException {
......@@ -118,7 +118,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Open a new connection using the current URL, user name and password.
*
*
* @return the connection
*/
public Connection getConnection() throws SQLException {
......@@ -128,9 +128,9 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Open a new connection using the current URL and the specified user name and password.
*
* @param the user name
* @param the password
*
* @param user the user name
* @param password the password
* @return the connection
*/
public Connection getConnection(String user, String password) throws SQLException {
......@@ -145,20 +145,20 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
info.setProperty("password", password);
return new JdbcConnection(url, info);
}
/**
* Get the current URL.
*
*
* @return the URL
*/
public String getURL() {
debugCodeCall("getURL");
return url;
}
/**
* Set the current URL.
*
*
* @param url the new URL
*/
public void setURL(String url) {
......@@ -168,7 +168,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Set the current password
*
*
* @param password the new password.
*/
public void setPassword(String password) {
......@@ -178,17 +178,17 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Get the current password.
*
*
* @return the password
*/
public String getPassword() {
debugCodeCall("getPassword");
return password;
}
/**
* Get the current user name.
*
*
* @return the user name
*/
public String getUser() {
......@@ -198,17 +198,17 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Set the current user name.
*
*
* @param user the new user name
*/
public void setUser(String user) {
debugCodeCall("setUser", user);
this.user = user;
}
/**
* Get a new reference for this object, using the current settings.
*
*
* @return the new reference
*/
//#ifdef JDK14
......@@ -226,7 +226,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
/**
* Open a new XA connection using the current URL, user name and password.
*
*
* @return the connection
*/
//#ifdef JDK14
......@@ -235,42 +235,42 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
int id = getNextId(XA_DATA_SOURCE);
return new JdbcXAConnection(factory, id, url, user, password);
}
//#endif
//#endif
/**
* Open a new XA connection using the current URL and the specified user name and password.
*
* @param the user name
* @param the password
*
* @param user the user name
* @param password the password
* @return the connection
*/
*/
//#ifdef JDK14
public XAConnection getXAConnection(String user, String password) throws SQLException {
debugCode("getXAConnection("+quote(user)+", "+quote(password)+");");
int id = getNextId(XA_DATA_SOURCE);
return new JdbcXAConnection(factory, id, url, user, password);
}
//#endif
//#endif
/**
* Open a new XA connection using the current URL, user name and password.
*
*
* @return the connection
*/
*/
//#ifdef JDK14
public PooledConnection getPooledConnection() throws SQLException {
debugCodeCall("getPooledConnection");
return getXAConnection();
}
//#endif
//#endif
/**
* Open a new XA connection using the current URL and the specified user name and password.
*
* @param the user name
* @param the password
*
* @param user the user name
* @param password the password
* @return the connection
*/
*/
//#ifdef JDK14
public PooledConnection getPooledConnection(String user, String password) throws SQLException {
debugCode("getPooledConnection("+quote(user)+", "+quote(password)+");");
......@@ -293,7 +293,7 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
}
*/
//#endif
/**
* INTERNAL
*/
......
......@@ -8,12 +8,15 @@ import java.sql.SQLException;
import org.h2.message.Message;
/**
* Represents an in-doubt transaction (a transaction in the prepare phase).
*/
public class InDoubtTransaction {
public static final int IN_DOUBT = 0, COMMIT = 1, ROLLBACK = 2;
// TODO 2-phase-commit: document sql statements and metadata table
private LogFile log;
private int sessionId;
private int pos;
......@@ -29,7 +32,7 @@ public class InDoubtTransaction {
this.blocks = blocks;
this.state = IN_DOUBT;
}
public void setState(int state) throws SQLException {
switch(state) {
case COMMIT:
......@@ -43,7 +46,7 @@ public class InDoubtTransaction {
}
this.state = state;
}
public String getState() {
switch(state) {
case IN_DOUBT:
......
......@@ -24,15 +24,15 @@ import org.h2.util.MathUtils;
import org.h2.util.ObjectArray;
/**
* Each log file contains a number of log records.
*
* Each transaction log file contains a number of log records.
*
* Header format:
* <pre>
* int logId (<0 means ignore: rolled back already)
* int firstUncommittedLogRecordId (-1 if none)
* int firstUnwrittenLogRecordId (-1 if none)
* </pre>
*
*
* Record format:
* <pre>
* int block size
......@@ -141,14 +141,14 @@ public class LogFile {
if (buff.length() >= buffer.length) {
// special case really long write request: write it without buffering
file.write(buff.getBytes(), 0, buff.length());
pos = getBlock();
pos = getBlock();
return;
}
System.arraycopy(buff.getBytes(), 0, buffer, bufferPos, buff.length());
bufferPos += buff.length();
pos = getBlock() + (bufferPos / BLOCK_SIZE);
}
void commit(Session session) throws SQLException {
DataPage buff = rowBuff;
buff.reset();
......@@ -346,7 +346,7 @@ public class LogFile {
}
go(pos);
}
void go(int pos) throws SQLException {
file.seek((long) pos * BLOCK_SIZE);
}
......@@ -376,7 +376,7 @@ public class LogFile {
}
}
}
void close(boolean delete) throws SQLException {
SQLException closeException = null;
try {
......@@ -404,7 +404,7 @@ public class LogFile {
throw closeException;
}
}
void addSummary(boolean dataFile, byte[] summary) throws SQLException {
DataPage buff = DataPage.create(database, 256);
buff.writeInt(0);
......@@ -455,24 +455,24 @@ public class LogFile {
record.write(buff);
writeBuffer(buff, record);
}
void setFirstUncommittedPos(int firstUncommittedPos) throws SQLException {
this.firstUncommittedPos = firstUncommittedPos;
int pos = getBlock();
writeHeader();
go(pos);
}
int getFirstUncommittedPos() {
return firstUncommittedPos;
}
private void writeHeader() throws SQLException {
file.seek(FileStore.HEADER_LENGTH);
DataPage buff = getHeader();
file.write(buff.getBytes(), 0, buff.length());
}
private DataPage getHeader() {
DataPage buff = rowBuff;
buff.reset();
......@@ -483,7 +483,7 @@ public class LogFile {
buff.fill(3 * BLOCK_SIZE);
return buff;
}
private void readHeader() throws SQLException {
DataPage buff = getHeader();
int len = buff.length();
......
......@@ -4,7 +4,9 @@
*/
package org.h2.log;
/**
* Represents a record in the transaction log.
*/
public class LogRecord {
LogFile log;
int logRecordId;
......
......@@ -22,7 +22,7 @@ import org.h2.util.ObjectUtils;
import org.h2.util.ObjectArray;
/**
* The log system is responsible for the write ahead log mechanism used in this database.
* The transaction log system is responsible for the write ahead log mechanism used in this database.
* A number of {@link LogFile} objects are used (one for each file).
*/
public class LogSystem {
......@@ -134,7 +134,7 @@ public class LogSystem {
LogFile l = (LogFile) activeLogs.get(i);
l.close(false);
}
closed = true;
closed = true;
return;
}
// TODO refactor flushing and closing files when we know what to do exactly
......@@ -220,7 +220,7 @@ public class LogSystem {
return fileChanged;
}
}
private void closeOldFile(LogFile l) throws SQLException {
l.close(deleteOldLogFilesAutomatically && keepFiles == 0);
}
......@@ -292,7 +292,7 @@ public class LogSystem {
state.lastCommitPos = pos;
state.inDoubtTransaction = null;
}
SessionState getOrAddSessionState(int sessionId) {
Integer key = ObjectUtils.getInteger(sessionId);
SessionState state = (SessionState) sessions.get(key);
......
......@@ -6,13 +6,17 @@ package org.h2.log;
import org.h2.store.Storage;
/**
* Represents a redo-log record.
* Such records are only used when recovering.
*/
public class RedoLogRecord {
public Storage storage;
public int sequenceId;
public int recordId;
public int offset;
public byte[] data;
public int getSize() {
// estimated memory size in bytes ((5 variables+myself) * 4 bytes each)
if (data == null) {
......
......@@ -4,7 +4,10 @@
*/
package org.h2.log;
/**
* The session state contains information about when was the last commit of a session.
* It is only used during recovery.
*/
public class SessionState {
int sessionId;
int lastCommitLog;
......
......@@ -15,6 +15,9 @@ import org.h2.store.DataPage;
import org.h2.store.FileStore;
import org.h2.util.ObjectArray;
/**
* Each session keeps a undo log if rollback is required.
*/
public class UndoLog {
private Session session;
private Database database;
......@@ -23,7 +26,7 @@ public class UndoLog {
private FileStore file;
private DataPage rowBuff;
private int memoryUndo;
public UndoLog(Session session) {
this.session = session;
this.database = session.getDatabase();
......@@ -45,7 +48,7 @@ public class UndoLog {
rowBuff = null;
}
}
public UndoLogRecord getAndRemoveLast() throws SQLException {
int i = records.size() - 1;
UndoLogRecord entry = (UndoLogRecord) records.get(i);
......@@ -89,12 +92,12 @@ public class UndoLog {
}
}
}
private void saveIfPossible(UndoLogRecord r, DataPage buff) throws SQLException {
if (!r.isStored() && r.canStore()) {
r.save(buff, file);
memoryUndo--;
}
}
}
......@@ -21,7 +21,7 @@ import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* @author Thomas
* An entry in a undo log.
*/
public class UndoLogRecord {
public static final short INSERT = 0, DELETE = 1;
......@@ -153,7 +153,7 @@ public class UndoLogRecord {
index.commit(operation, row);
}
}
public Row getRow() {
return row;
}
......
......@@ -4,6 +4,11 @@
*/
package org.h2.message;
/**
* This exception wraps a checked exception.
* It is used in methods where checked exceptions are not supported,
* for example in a Comparator.
*/
public class InternalException extends RuntimeException {
private static final long serialVersionUID = -5369631382082604330L;
......@@ -12,7 +17,7 @@ public class InternalException extends RuntimeException {
public InternalException(Exception e) {
cause = e;
}
public Exception getOriginalCause() {
return cause;
}
......
......@@ -63,8 +63,8 @@ public class Message {
* Gets the SQL Exception object for a specific SQLState. Supported
* SQL states are:
*
* @param sqlState - the SQL State
* @param param - the parameter of the message
* @param sqlState the SQL state
* @param p1 the first parameter of the message
* @return the SQLException object
*/
public static JdbcSQLException getSQLException(int sqlState, String p1) {
......@@ -149,8 +149,8 @@ public class Message {
}
return j;
} else {
return new JdbcSQLException(e.getMessage(), sql,
e.getSQLState(),
return new JdbcSQLException(e.getMessage(), sql,
e.getSQLState(),
e.getErrorCode(), e, null);
}
}
......@@ -173,7 +173,7 @@ public class Message {
}
return getSQLException(ErrorCode.GENERAL_ERROR_1, new String[]{e.toString()}, e);
}
public static SQLException convertIOException(IOException e, String message) {
if (message == null) {
return getSQLException(ErrorCode.IO_EXCEPTION_1, new String[]{e.toString()}, e);
......
......@@ -6,14 +6,11 @@ package org.h2.message;
import org.h2.constant.SysProperties;
/**
* @author Thomas
* This class represents a trace module.
*/
public class Trace {
// currently called trace because log mean something else
// TODO trace: java code generation does not always work
private TraceSystem traceSystem;
......
......@@ -18,6 +18,9 @@ import org.h2.util.FileUtils;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
/**
* The base class for objects that can print trace information about themselves.
*/
public class TraceObject {
public static final int CALLABLE_STATEMENT = 0, CONNECTION = 1, DATABASE_META_DATA = 2,
PREPARED_STATEMENT = 3, RESULT_SET = 4, RESULT_SET_META_DATA = 5,
......
......@@ -19,10 +19,12 @@ import org.h2.util.FileUtils;
import org.h2.util.SmallLRUCache;
/**
* The trace mechanism is the logging facility of this database.
* There is usually one trace system per database.
* It is called 'trace' because the term 'log' is already used in the
* database domain and means 'transaction log'.
* It is possible to write after close was called, but that means for each write the
* log file will be opened and closed again (which is slower).
*
* @author Thomas
*/
public class TraceSystem {
public static final int OFF = 0, ERROR = 1, INFO = 2, DEBUG = 3;
......
......@@ -22,6 +22,10 @@ import org.h2.value.Value;
import org.h2.value.ValueArray;
/**
* A local result set contains all row data of a result set.
* This is the object generated by engine,
* and it is also used directly by the ResultSet class in the embedded mode.
* If the result does not fit in memory, it is written to a temporary file.
*/
public class LocalResult implements ResultInterface {
......@@ -40,7 +44,7 @@ public class LocalResult implements ResultInterface {
private int diskOffset;
private boolean isUpdateCount;
private int updateCount;
public static LocalResult read(Session session, ResultSet rs, int maxrows) throws SQLException {
ObjectArray cols = getExpressionColumns(session, rs);
int columnCount = cols.size();
......@@ -56,9 +60,9 @@ public class LocalResult implements ResultInterface {
result.done();
return result;
}
private static ObjectArray getExpressionColumns(Session session, ResultSet rs) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
ObjectArray cols = new ObjectArray(columnCount);
Database db = session == null ? null : session.getDatabase();
......
......@@ -9,7 +9,7 @@ import java.io.IOException;
import org.h2.value.Transfer;
/**
* @author Thomas
* A result set column of a remote result.
*/
public class ResultColumn {
String alias;
......
......@@ -15,6 +15,9 @@ import org.h2.store.FileStore;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* This class implements the disk buffer for the LocalResult class.
*/
class ResultDiskBuffer {
private static final int READ_AHEAD = 128;
......@@ -26,6 +29,15 @@ class ResultDiskBuffer {
private SortOrder sort;
private int columnCount;
/**
* Represents a virtual disk tape for the merge sort algorithm.
* Each virtual disk tape is a region of the temp file.
*/
private static class ResultDiskTape {
long start, end, pos;
ObjectArray buffer = new ObjectArray();
}
public ResultDiskBuffer(Session session, SortOrder sort, int columnCount) throws SQLException {
this.sort = sort;
this.columnCount = columnCount;
......
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.result;
import org.h2.util.ObjectArray;
class ResultDiskTape {
long start, end, pos;
ObjectArray buffer = new ObjectArray();
}
......@@ -8,24 +8,151 @@ import java.sql.SQLException;
import org.h2.value.Value;
/**
* The result interface is used by the LocalResult and ResultRemote class.
* A result may contain rows, or just an update count.
*/
public interface ResultInterface {
/**
* Go to the beginning of the result, that means
* before the first row.
*/
void reset() throws SQLException;
/**
* Get the current row.
*
* @return the row
*/
Value[] currentRow();
/**
* Go to the next row.
*
* @return true if a row exists
*/
boolean next() throws SQLException;
/**
* Get the current row id, starting with 0.
* -1 is returned when next() was not called yet.
*
* @return the row id
*/
int getRowId();
/**
* Get the number of visible columns.
* More columns may exist internally for sorting or grouping.
*
* @return the number of columns
*/
int getVisibleColumnCount();
/**
* Get the number of rows in this object.
*
* @return the number of rows
*/
int getRowCount();
/**
* Close the result and delete any temporary files
*/
void close();
/**
* Get the column alias name for the column.
*
* @param i the column number (starting with 0)
* @return the alias name
*/
String getAlias(int i);
/**
* Get the schema name for the column, if one exists.
*
* @param i the column number (starting with 0)
* @return the schema name or null
*/
String getSchemaName(int i);
/**
* Get the table name for the column, if one exists.
*
* @param i the column number (starting with 0)
* @return the table name or null
*/
String getTableName(int i);
/**
* Get the column name.
*
* @param i the column number (starting with 0)
* @return the column name
*/
String getColumnName(int i);
/**
* Get the column data type.
*
* @param i the column number (starting with 0)
* @return the column data type
*/
int getColumnType(int i);
/**
* Get the precision for this column.
*
* @param i the column number (starting with 0)
* @return the precision
*/
long getColumnPrecision(int i);
/**
* Get the scale for this column.
*
* @param i the column number (starting with 0)
* @return the scale
*/
int getColumnScale(int i);
/**
* Get the display size for this column.
*
* @param i the column number (starting with 0)
* @return the display size
*/
int getDisplaySize(int i);
/**
* Check if this is an auto-increment column.
*
* @param i the column number (starting with 0)
* @return true for auto-increment columns
*/
boolean isAutoIncrement(int i);
/**
* Check if this column is nullable.
*
* @param i the column number (starting with 0)
* @return Column.NULLABLE_*
*/
int getNullable(int i);
/**
* Check if this result is in fact an update count.
*
* @return if it is an update count
*/
boolean isUpdateCount();
/**
* Get the update count for this result.
*
* @return the update count
*/
int getUpdateCount();
}
......@@ -14,6 +14,11 @@ import org.h2.util.ObjectArray;
import org.h2.value.Transfer;
import org.h2.value.Value;
/**
* The client side part of a result set that is kept on the server.
* In many cases, the complete data is kept on the client side,
* but for large results only a subset is in-memory.
*/
public class ResultRemote implements ResultInterface {
private SessionRemote session;
......@@ -228,7 +233,7 @@ public class ResultRemote implements ResultInterface {
}
}
}
public String toString() {
return "columns: " + columns.length + " rows: " + rowCount + " pos: " + rowId;
}
......
......@@ -12,7 +12,7 @@ import org.h2.store.Record;
import org.h2.value.Value;
/**
* @author Thomas
* Represents a row in a table.
*/
public class Row extends Record implements SearchRow {
private final Value[] data;
......@@ -61,11 +61,11 @@ public class Row extends Record implements SearchRow {
public int getColumnCount() {
return data.length;
}
public int getMemorySize() {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4;
}
public String toString() {
StringBuffer buff = new StringBuffer(data.length * 5);
buff.append("( /* pos:");
......
......@@ -6,12 +6,46 @@ package org.h2.result;
import org.h2.value.Value;
/**
* The interface for rows stored in a table, and for partial rows stored in the index.
*/
public interface SearchRow {
/**
* Get the position of the row in the data file.
*
* @return the position
*/
int getPos();
/**
* Get the value for the column
*
* @param index the column number (starting with 0)
* @return the value
*/
Value getValue(int index);
/**
* Get the column count.
*
* @return the column count
*/
int getColumnCount();
void setValue(int idx, Value v);
/**
* Set the value for given column
*
* @param index the column number (starting with 0)
* @param v the new value
*/
void setValue(int index, Value v);
/**
* Set the position (where the row is stored in the data file).
*
* @param pos the position.
*/
void setPos(int pos);
}
......@@ -6,8 +6,11 @@ package org.h2.result;
import org.h2.value.Value;
/**
* Represents a simple row that is not cached separately.
*/
public class SimpleRow implements SearchRow {
private int pos;
private Value[] data;
......@@ -22,11 +25,11 @@ public class SimpleRow implements SearchRow {
public int getPos() {
return pos;
}
public void setPos(int pos) {
this.pos = pos;
}
public void setValue(int i, Value v) {
data[i] = v;
}
......
......@@ -6,17 +6,20 @@ package org.h2.result;
import org.h2.value.Value;
/**
* A simple row that contains data for only one column.
*/
public class SimpleRowValue implements SearchRow {
private int pos;
private int index;
private int virtualColumnCount;
private Value data;
public SimpleRowValue(int columnCount) {
this.virtualColumnCount = columnCount;
}
public int getColumnCount() {
return virtualColumnCount;
}
......@@ -29,10 +32,10 @@ public class SimpleRowValue implements SearchRow {
public void setPos(int pos) {
this.pos = pos;
}
public void setValue(int idx, Value v) {
index = idx;
data = v;
}
}
......@@ -14,7 +14,6 @@ import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* A sort order represents an ORDER BY clause in a query.
*/
......@@ -59,7 +58,7 @@ public class SortOrder {
}
return buff.toString();
}
public static int compareNull(boolean aNull, boolean bNull, int type) {
if ((type & NULLS_FIRST) != 0) {
return aNull ? -1 : 1;
......
......@@ -19,6 +19,10 @@ import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* An updatable row is a link from a ResultSet to a row in the database.
* This class is used for updatable result sets.
*/
public class UpdatableRow {
private SessionInterface session;
......
......@@ -14,11 +14,15 @@ import org.h2.message.Trace;
import org.h2.table.Table;
import org.h2.value.Value;
/**
* A user defined constant as created by the SQL statement
* CREATE CONSTANT
*/
public class Constant extends SchemaObjectBase {
private Value value;
private ValueExpression expression;
public Constant(Schema schema, int id, String name) {
super(schema, id, name, Trace.SCHEMA);
}
......@@ -26,7 +30,7 @@ public class Constant extends SchemaObjectBase {
public String getCreateSQLForCopy(Table table, String quotedName) {
throw Message.getInternalError();
}
public String getDropSQL() {
return null;
}
......
......@@ -24,6 +24,10 @@ import org.h2.table.TableData;
import org.h2.table.TableLink;
import org.h2.util.ObjectArray;
/**
* A schema as created by the SQL statement
* CREATE SCHEMA
*/
public class Schema extends DbObjectBase {
private User owner;
......
......@@ -6,8 +6,15 @@ package org.h2.schema;
import org.h2.engine.DbObject;
/**
* Any database object that is stored in a schema.
*/
public interface SchemaObject extends DbObject {
Schema getSchema();
String getSQL();
/**
* Get the schema in which this object is defined
*
* @return the schema
*/
Schema getSchema();
}
......@@ -6,21 +6,24 @@ package org.h2.schema;
import org.h2.engine.DbObjectBase;
/**
* The base class for classes implementing SchemaObject.
*/
public abstract class SchemaObjectBase extends DbObjectBase implements SchemaObject {
private Schema schema;
protected SchemaObjectBase(Schema schema, int id, String name, String traceModule) {
super(schema.getDatabase(), id, name, traceModule);
this.schema = schema;
}
public Schema getSchema() {
return schema;
}
public String getSQL() {
return schema.getSQL() + "." + super.getSQL();
}
}
......@@ -14,6 +14,10 @@ import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.table.Table;
/**
*A sequence is created using the statement
* CREATE SEQUENCE
*/
public class Sequence extends SchemaObjectBase {
public static final int DEFAULT_CACHE_SIZE = 32;
private long value = 1;
......
......@@ -20,12 +20,11 @@ import org.h2.value.DataType;
import org.h2.value.Value;
/**
* @author Thomas
*A trigger is created using the statement
* CREATE TRIGGER
*/
public class TriggerObject extends SchemaObjectBase {
public static final int INSERT = 1, UPDATE = 2, DELETE = 4;
public static final int DEFAULT_QUEUE_SIZE = 1024;
private boolean before;
......@@ -54,7 +53,7 @@ public class TriggerObject extends SchemaObjectBase {
Connection c2 = session.createConnection(false);
Object obj = session.getDatabase().loadUserClass(triggerClassName).newInstance();
triggerCallback = (Trigger) obj;
triggerCallback.init(c2, getSchema().getName(), getName(), table.getName());
triggerCallback.init(c2, getSchema().getName(), getName(), table.getName(), before, typeMask);
} catch (Throwable e) {
throw Message.getSQLException(ErrorCode.ERROR_CREATING_TRIGGER_OBJECT_3, new String[] { getName(),
triggerClassName, e.toString() }, e);
......@@ -93,17 +92,17 @@ public class TriggerObject extends SchemaObjectBase {
Object[] oldList;
Object[] newList;
boolean fire = false;
if ((typeMask & INSERT) != 0) {
if ((typeMask & Trigger.INSERT) != 0) {
if (oldRow == null && newRow != null) {
fire = true;
}
}
if ((typeMask & UPDATE) != 0) {
if ((typeMask & Trigger.UPDATE) != 0) {
if (oldRow != null && newRow != null) {
fire = true;
}
}
if ((typeMask & DELETE) != 0) {
if ((typeMask & Trigger.DELETE) != 0) {
if (oldRow != null && newRow == null) {
fire = true;
}
......@@ -197,16 +196,16 @@ public class TriggerObject extends SchemaObjectBase {
public String getTypeNameList() {
StringBuffer buff = new StringBuffer();
if ((typeMask & INSERT) != 0) {
if ((typeMask & Trigger.INSERT) != 0) {
buff.append("INSERT");
}
if ((typeMask & UPDATE) != 0) {
if ((typeMask & Trigger.UPDATE) != 0) {
if (buff.length() > 0) {
buff.append(", ");
}
buff.append("UPDATE");
}
if ((typeMask & DELETE) != 0) {
if ((typeMask & Trigger.DELETE) != 0) {
if (buff.length() > 0) {
buff.append(", ");
}
......
......@@ -8,9 +8,8 @@ import org.h2.constant.SysProperties;
import org.h2.message.Message;
/**
* AES-128
* @author Tom
*
* An implementation of the AES block cipher algorithm,
* also known as Rijndael. Only AES-128 is supported by this class.
*/
public class AES implements BlockCipher {
......
......@@ -4,12 +4,47 @@
*/
package org.h2.security;
/**
* A block cipher is a data encryption algorithm that operates on blocks.
*/
public interface BlockCipher {
/*
* Blocks sizes are always multiples of this number
*/
int ALIGN = 16;
/**
* Set the encryption key used for encrypting and decrypting.
*
* @param key the key
*/
void setKey(byte[] key);
/**
* Encrypt a number of bytes. This is done in-place, that
* means the bytes are overwritten.
*
* @param bytes the byte array
* @param off the start index
* @param len the number of bytes to encrypt
*/
void encrypt(byte[] bytes, int off, int len);
/**
* Decrypt a number of bytes. This is done in-place, that
* means the bytes are overwritten.
*
* @param bytes the byte array
* @param off the start index
* @param len the number of bytes to decrypt
*/
void decrypt(byte[] bytes, int off, int len);
/**
* Get the length of the key in bytes.
*
* @return the length of the key
*/
int getKeyLength();
}
......@@ -9,8 +9,11 @@ import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.message.Message;
/**
* A factory to create new block cipher objects.
*/
public class CipherFactory {
public static BlockCipher getBlockCipher(String algorithm) throws SQLException {
if ("XTEA".equalsIgnoreCase(algorithm)) {
return new XTEA();
......@@ -28,5 +31,5 @@ public class CipherFactory {
throw Message.getInvalidValueException(algorithm, "algorithm");
}
}
}
......@@ -6,6 +6,9 @@ package org.h2.security;
import java.util.Arrays;
/**
* This class implements the cryptographic hash function SHA-256.
*/
public class SHA256 {
// TODO maybe implement WHIRLPOOL
......
......@@ -13,6 +13,10 @@ import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.util.RandomUtils;
/**
* A file store that encrypts all data before writing,
* and decrypts all data after reading.
*/
public class SecureFileStore extends FileStore {
private byte[] key;
......@@ -86,7 +90,7 @@ public class SecureFileStore extends FileStore {
this.pos = x;
super.seek(x);
}
public void setLength(long newLength) throws SQLException {
long oldPos = pos;
byte[] buff = new byte[Constants.FILE_BLOCK_SIZE];
......@@ -101,7 +105,7 @@ public class SecureFileStore extends FileStore {
super.setLength(newLength);
}
}
private void xorInitVector(byte[] b, int off, int len, long pos) {
byte[] iv = bufferForInitVector;
while (len > 0) {
......@@ -125,9 +129,9 @@ public class SecureFileStore extends FileStore {
len -= Constants.FILE_BLOCK_SIZE;
}
}
public boolean isEncrypted() {
return true;
}
}
......@@ -8,9 +8,8 @@ import org.h2.constant.SysProperties;
import org.h2.message.Message;
/**
* XTEA with 32 rounds
* @author Tom
*
* An implementation of the XTEA block cipher algorithm.
* This implementation uses 32 rounds.
*/
public class XTEA implements BlockCipher {
......@@ -29,18 +28,18 @@ public class XTEA implements BlockCipher {
int[] r = new int[32];
for (int i = 0, sum = 0; i < 32;) {
r[i++] = sum + key[sum & 3];
sum += DELTA;
r[i++] = sum + key[ (sum >>> 11) & 3];
sum += DELTA;
r[i++] = sum + key[ (sum >>> 11) & 3];
}
k0 = r[0]; k1 = r[1]; k2 = r[2]; k3 = r[3]; k4 = r[4]; k5 = r[5]; k6 = r[6]; k7 = r[7];
k8 = r[8]; k9 = r[9]; k10 = r[10]; k11 = r[11]; k12 = r[12]; k13 = r[13]; k14 = r[14]; k15 = r[15];
k16 = r[16]; k17 = r[17]; k18 = r[18]; k19 = r[19]; k20 = r[20]; k21 = r[21]; k22 = r[22]; k23 = r[23];
k24 = r[24]; k25 = r[25]; k26 = r[26]; k27 = r[27]; k28 = r[28]; k29 = r[29]; k30 = r[30]; k31 = r[31];
k0 = r[0]; k1 = r[1]; k2 = r[2]; k3 = r[3]; k4 = r[4]; k5 = r[5]; k6 = r[6]; k7 = r[7];
k8 = r[8]; k9 = r[9]; k10 = r[10]; k11 = r[11]; k12 = r[12]; k13 = r[13]; k14 = r[14]; k15 = r[15];
k16 = r[16]; k17 = r[17]; k18 = r[18]; k19 = r[19]; k20 = r[20]; k21 = r[21]; k22 = r[22]; k23 = r[23];
k24 = r[24]; k25 = r[25]; k26 = r[26]; k27 = r[27]; k28 = r[28]; k29 = r[29]; k30 = r[30]; k31 = r[31];
}
public void encrypt(byte[] bytes, int off, int len) {
if (SysProperties.CHECK && (len % ALIGN != 0)) {
throw Message.getInternalError("unaligned len " + len);
throw Message.getInternalError("unaligned len " + len);
}
for (int i = off; i < off + len; i += 8) {
encryptBlock(bytes, bytes, i);
......
......@@ -6,14 +6,70 @@ package org.h2.server;
import java.sql.SQLException;
/**
* Classes implementing this interface usually provide a
* TCP/IP listener such as an FTP server.
* The can be started and stopped, and may or may not
* allow remote connections.
*/
public interface Service {
/**
* Initialize the service from command line options.
*
* @param args the command line options
*/
void init(String[] args) throws Exception;
/**
* Get the URL of this service in a human readable form
*
* @return the url
*/
String getURL();
/**
* Start the service. This usually means create the server socket.
* This method must not block.
*/
void start() throws SQLException;
/**
* Listen for incoming connections.
* This method blocks.
*/
void listen();
/**
* Stop the service.
*/
void stop();
/**
* Check if the service is running.
*
* @return if the server is running
*/
boolean isRunning();
/**
* Check if remote connections are allowed.
*
* @return true if remote connections are allowed
*/
boolean getAllowOthers();
/**
* Get the human readable name of the service.
*
* @return the name
*/
String getName();
/**
* Get the human readable short name of the service.
*
* @return the type
*/
String getType();
}
......@@ -4,6 +4,13 @@
*/
package org.h2.server;
/**
* A shutdown handler is a listener for shutdown events.
*/
public interface ShutdownHandler {
/**
* Tell the listener to shut down.
*/
void shutdown();
}
......@@ -24,6 +24,14 @@ import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
/**
* The TCP server implements the native H2 database server protocol.
* It supports multiple client connections to multiple databases
* (many to many). The same database may be opened by multiple clients.
* Also supported is the mixed mode: opening databases in embedded mode,
* and at the same time start a TCP server to allow clients to connect to
* the same database over the network.
*/
public class TcpServer implements Service {
// TODO new feature: implement automatic client / server mode if 'socket'
......@@ -251,7 +259,7 @@ public class TcpServer implements Service {
public String getName() {
return "H2 TCP Server";
}
public void logInternalError(String string) {
if (TcpServer.logInternalErrors) {
System.out.println(string);
......
......@@ -28,6 +28,9 @@ import org.h2.util.SmallMap;
import org.h2.value.Transfer;
import org.h2.value.Value;
/**
* One server thread is opened per client connection.
*/
public class TcpServerThread implements Runnable {
private TcpServer server;
private Session session;
......@@ -42,7 +45,7 @@ public class TcpServerThread implements Runnable {
transfer = new Transfer(null);
transfer.setSocket(socket);
}
private void log(String s) {
server.log(this + " " + s);
}
......
......@@ -18,6 +18,9 @@ import org.h2.engine.Constants;
import org.h2.store.fs.FileSystem;
import org.h2.util.StringUtils;
/**
* The implementation of the control channel of the FTP server.
*/
public class FtpControl extends Thread {
private static final String SERVER_NAME = "Small FTP Server";
......
......@@ -15,6 +15,9 @@ import java.sql.SQLException;
import org.h2.store.fs.FileSystem;
import org.h2.util.IOUtils;
/**
* The implementation of the data channel of the FTP server.
*/
public class FtpData extends Thread {
private FtpServer server;
......
......@@ -26,8 +26,9 @@ import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
/**
* Small FTP Server. Intended for ad-hoc networks in a secure environment. See
* also http://cr.yp.to/ftp.html http://www.ftpguide.com/
* Small FTP Server. Intended for ad-hoc networks in a secure environment.
* Remote connections are possible.
* See also http://cr.yp.to/ftp.html http://www.ftpguide.com/
*/
public class FtpServer implements Service {
......
......@@ -23,7 +23,10 @@ import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
/**
* @author Thomas
* This class implements a subset of the PostgreSQL protocol as described here:
* http://developer.postgresql.org/pgdocs/postgres/protocol.html
* The PostgreSQL catalog is described here:
* http://www.postgresql.org/docs/7.4/static/catalogs.html
*/
public class PgServer implements Service {
......@@ -38,7 +41,7 @@ public class PgServer implements Service {
private String url;
private boolean allowOthers;
private boolean ifExists;
public void init(String[] args) throws Exception {
port = DEFAULT_PORT;
for (int i = 0; i < args.length; i++) {
......@@ -57,7 +60,7 @@ public class PgServer implements Service {
}
org.h2.Driver.load();
url = "pg://localhost:" + port;
// int testing;
// log = true;
}
......@@ -177,7 +180,7 @@ public class PgServer implements Service {
public String getType() {
return "PG";
}
public String getName() {
return "H2 PG Server";
}
......@@ -185,7 +188,7 @@ public class PgServer implements Service {
public boolean getIfExists() {
return ifExists;
}
public static String getIndexColumn(Connection conn, int indexId, Integer ordinalPosition, Boolean pretty)
throws SQLException {
if (ordinalPosition == null || ordinalPosition.intValue() == 0) {
......@@ -207,13 +210,13 @@ public class PgServer implements Service {
return null;
}
}
public static String getCurrentSchema(Connection conn) throws SQLException {
ResultSet rs = conn.createStatement().executeQuery("call schema()");
rs.next();
return rs.getString(1);
}
public static String getEncodingName(int code) throws SQLException {
switch (code) {
case 0:
......@@ -226,15 +229,15 @@ public class PgServer implements Service {
return code < 40 ? "UTF8" : "";
}
}
public static String getVersion() {
return "PostgreSQL 8.1.4 server protocol using H2 " + Constants.getVersion();
}
public static Timestamp getStartTime() {
return new Timestamp(System.currentTimeMillis());
}
public static String getUserById(Connection conn, int id) throws SQLException {
PreparedStatement prep = conn.prepareStatement("SELECT NAME FROM INFORMATION_SCHEMA.USERS WHERE ID=?");
prep.setInt(1, id);
......@@ -244,15 +247,15 @@ public class PgServer implements Service {
}
return null;
}
public static boolean hasDatabasePrivilege(int id, String privilege) {
return true;
}
public static boolean hasTablePrivilege(String table, String privilege) {
return true;
}
public static int getCurrentTid(String table, String id) {
return 1;
}
......
......@@ -35,11 +35,7 @@ import org.h2.util.ObjectUtils;
import org.h2.util.ScriptReader;
/**
* This class implements a subset of the PostgreSQL protocol as described here:
* http://developer.postgresql.org/pgdocs/postgres/protocol.html
* The PostgreSQL catalog is described here:
* http://www.postgresql.org/docs/7.4/static/catalogs.html
* @author Thomas
* One server thread is opened for each client.
*/
public class PgServerThread implements Runnable {
private static final int TYPE_STRING = Types.VARCHAR;
......@@ -87,7 +83,7 @@ public class PgServerThread implements Runnable {
close();
}
}
private String readString() throws IOException {
ByteArrayOutputStream buff = new ByteArrayOutputStream();
while (true) {
......@@ -99,23 +95,23 @@ public class PgServerThread implements Runnable {
}
return new String(buff.toByteArray(), getEncoding());
}
private int readInt() throws IOException {
return dataIn.readInt();
}
private int readShort() throws IOException {
return dataIn.readShort();
}
private byte readByte() throws IOException {
return dataIn.readByte();
}
private void readFully(byte[] buff) throws IOException {
dataIn.readFully(buff);
}
private void error(String message, Exception e) {
if (e != null) {
server.logError(e);
......@@ -507,10 +503,10 @@ public class PgServerThread implements Runnable {
sendErrorResponse(e);
}
}
private void sendNoData() throws IOException {
startMessage('n');
sendMessage();
sendMessage();
}
private void sendRowDescription(ResultSetMetaData meta) throws IOException {
......@@ -555,10 +551,10 @@ public class PgServerThread implements Runnable {
return precision + 4;
}
}
private int getModifier(int type) {
return -1;
}
}
private void sendErrorResponse(String message) throws IOException {
error("Exception: " + message, null);
......@@ -570,17 +566,17 @@ public class PgServerThread implements Runnable {
write('M');
writeString(message);
sendMessage();
}
}
private void sendParseComplete() throws IOException {
startMessage('1');
sendMessage();
}
private void sendBindComplete() throws IOException {
startMessage('2');
sendMessage();
}
}
private void initDb() throws SQLException {
Statement stat = null;
......@@ -637,13 +633,13 @@ public class PgServerThread implements Runnable {
socket = null;
server.remove(this);
}
private void sendAuthenticationCleartextPassword() throws IOException {
startMessage('R');
writeInt(3);
sendMessage();
}
private void sendAuthenticationOk() throws IOException {
startMessage('R');
writeInt(0);
......@@ -660,7 +656,7 @@ public class PgServerThread implements Runnable {
sendBackendKeyData();
sendReadyForQuery();
}
private void sendReadyForQuery() throws IOException {
startMessage('Z');
char c;
......@@ -688,31 +684,31 @@ public class PgServerThread implements Runnable {
write(s.getBytes(getEncoding()));
write(0);
}
private void writeInt(int i) throws IOException {
dataOut.writeInt(i);
}
private void writeShort(int i) throws IOException {
dataOut.writeShort(i);
}
}
private void write(byte[] data) throws IOException {
dataOut.write(data);
}
private void write(int b) throws IOException {
dataOut.write(b);
}
private void startMessage(int messageType) {
this.messageType = messageType;
outBuffer = new ByteArrayOutputStream();
dataOut = new DataOutputStream(outBuffer);
}
private void sendMessage() throws IOException {
dataOut.flush();
dataOut.flush();
byte[] buff = outBuffer.toByteArray();
int len = buff.length;
dataOut = new DataOutputStream(out);
......@@ -721,7 +717,7 @@ public class PgServerThread implements Runnable {
dataOut.write(buff);
dataOut.flush();
}
private void sendParameterStatus(String param, String value) throws IOException {
startMessage('S');
writeString(param);
......@@ -740,14 +736,14 @@ public class PgServerThread implements Runnable {
public void setProcessId(int id) {
this.processId = id;
}
private static class Prepared {
String name;
String sql;
PreparedStatement prep;
int[] paramType;
}
private static class Portal {
String name;
String sql;
......
......@@ -6,6 +6,11 @@ package org.h2.server.web;
import org.h2.util.StringUtils;
/**
* The connection info object is a wrapper for database connection information
* such as the database URL, user name and password.
* This class is used by the H2 Console.
*/
public class ConnectionInfo {
String name, driver, url, user;
int lastAccess;
......
......@@ -8,10 +8,14 @@ import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Keeps the meta data information of a column.
* This class is used by the H2 Console.
*/
public class DbColumn {
String name;
String dataType;
DbColumn(ResultSet rs) throws SQLException {
name = rs.getString("COLUMN_NAME");
String type = rs.getString("TYPE_NAME");
......
......@@ -12,6 +12,10 @@ import java.util.ArrayList;
import org.h2.command.Parser;
import org.h2.util.StringUtils;
/**
* Keeps meta data information about a database.
* This class is used by the H2 Console.
*/
public class DbContents {
DbSchema[] schemas;
DbSchema defaultSchema;
......
......@@ -17,14 +17,18 @@ import org.h2.command.Parser;
import org.h2.message.Message;
import org.h2.util.StringUtils;
/**
* A BNF terminal rule that is linked to the database context information.
* This class is used by the H2 Console, to support auto-complete.
*/
public class DbContextRule implements Rule {
DbContents contents;
int type;
static final int COLUMN = 0, TABLE = 1, TABLE_ALIAS = 2;
public static final int NEW_TABLE_ALIAS = 3;
public static final int COLUMN_ALIAS = 4;
public static final int NEW_TABLE_ALIAS = 3;
public static final int COLUMN_ALIAS = 4;
private static final boolean SUGGEST_TABLE_ALIAS = false;
DbContextRule(DbContents contents, int type) {
this.contents = contents;
this.type = type;
......@@ -65,7 +69,7 @@ public class DbContextRule implements Rule {
default:
}
}
private void addTableAlias(String query, Sentence sentence) {
String q = StringUtils.toUpperEnglish(query.trim());
HashMap map = sentence.getAliases();
......@@ -133,7 +137,7 @@ public class DbContextRule implements Rule {
}
}
}
// private boolean startWithIgnoreCase(String a, String b) {
// if(a.length() < b.length()) {
// return false;
......
......@@ -9,21 +9,25 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* Contains meta data information about a database schema.
* This class is used by the H2 Console.
*/
public class DbSchema {
DbContents contents;
String name;
String quotedName;
DbTableOrView[] tables;
public boolean isDefault;
DbSchema(DbContents contents, String name, boolean isDefault) throws SQLException {
this.contents = contents;
this.name = name;
this.quotedName = contents.quoteIdentifier(name);
this.isDefault = isDefault;
}
public void readTables(DatabaseMetaData meta, String[] tableTypes) throws SQLException {
ResultSet rs = meta.getTables(null, name, null, tableTypes);
ArrayList list = new ArrayList();
......
......@@ -9,13 +9,17 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/**
* Contains meta data information about a table or a view.
* This class is used by the H2 Console.
*/
public class DbTableOrView {
DbSchema schema;
String name;
String quotedName;
boolean isView;
DbColumn[] columns;
DbTableOrView(DbSchema schema, ResultSet rs) throws SQLException {
this.schema = schema;
name = rs.getString("TABLE_NAME");
......@@ -23,7 +27,7 @@ public class DbTableOrView {
isView = "VIEW".equals(type);
quotedName = schema.contents.quoteIdentifier(name);
}
public void readColumns(DatabaseMetaData meta) throws SQLException {
ResultSet rs = meta.getColumns(null, schema.name, name, null);
ArrayList list = new ArrayList();
......
......@@ -8,6 +8,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* A page parser can parse an HTML page and replace the tags there.
* This class is used by the H2 Console.
*/
public class PageParser {
private WebServer server;
private String page;
......
......@@ -38,6 +38,10 @@ import org.h2.util.RandomUtils;
import org.h2.util.Resources;
import org.h2.util.SortedProperties;
/**
* The web server is a simple standalone HTTP server that implements the H2 Console application.
* It is not optimized for performance.
*/
public class WebServer implements Service {
private static final String DEFAULT_LANGUAGE = "en";
......
......@@ -17,15 +17,19 @@ import java.util.Locale;
import org.h2.bnf.Bnf;
import org.h2.message.TraceSystem;
/**
* The web session keeps all data of a user session.
* This class is used by the H2 Console.
*/
public class WebSession {
long lastAccess;
HashMap map = new HashMap();
Locale locale;
WebServer server;
private static final int MAX_HISTORY = 1000;
private ArrayList commandHistory = new ArrayList();
private Connection conn;
private DatabaseMetaData meta;
private DbContents contents = new DbContents();
......@@ -37,15 +41,15 @@ public class WebSession {
private Bnf bnf;
Statement executingStatement;
ResultSet result;
WebSession(WebServer server) {
this.server = server;
}
public void put(String key, Object value) {
map.put(key, value);
}
public Object get(String key) {
if ("sessions".equals(key)) {
return server.getSessions();
......@@ -60,7 +64,7 @@ public class WebSession {
public Bnf getBnf() {
return bnf;
}
void loadBnf() {
try {
Bnf newBnf = Bnf.getInstance(null);
......@@ -91,13 +95,13 @@ public class WebSession {
} catch (Exception e) {
// ok we don't have the bnf
e.printStackTrace();
}
}
}
String getCommand(int id) {
return (String) commandHistory.get(id);
}
void addCommand(String sql) {
if (sql == null) {
return;
......@@ -144,7 +148,7 @@ public class WebSession {
}
contents = new DbContents();
}
DatabaseMetaData getMetaData() {
return meta;
}
......
......@@ -49,6 +49,11 @@ import org.h2.util.ObjectArray;
import org.h2.util.ScriptReader;
import org.h2.util.StringUtils;
/**
* For each request, an object of this class is created.
* Keep-alive is not supported at this time.
* This class is used by the H2 Console.
*/
class WebThread extends Thread implements DatabaseEventListener {
private WebServer server;
private WebSession session;
......
......@@ -19,23 +19,26 @@ import org.h2.table.Table;
import org.h2.util.ObjectArray;
import org.h2.util.TempFileDeleter;
/**
* The writer thread is responsible to flush the transaction log file from time to time.
*/
public class WriterThread extends Thread {
// Thread objects are not garbage collected
// until they returned from the run() method
// Thread objects are not garbage collected
// until they returned from the run() method
// (even if they where never started)
// so if the connection was not closed,
// so if the connection was not closed,
// the database object cannot get reclaimed
// by the garbage collector if we use a hard reference
private volatile WeakReference databaseRef;
private int writeDelay;
private long lastIndexFlush;
private volatile boolean stop;
private WriterThread(Database database, int writeDelay) {
this.databaseRef = new WeakReference(database);
this.writeDelay = writeDelay;
}
public void setWriteDelay(int writeDelay) {
LogSystem log = getLog();
this.writeDelay = writeDelay;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论