提交 42ed3064 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 d7086c2f
......@@ -30,6 +30,8 @@ Advanced Topics
Two Phase Commit</a><br />
<a href="#compatibility">
Compatibility</a><br />
<a href="#standards_compliance">
Standards Compliance</a><br />
<a href="#windows_service">
Run as Windows Service</a><br />
<a href="#odbc_driver">
......@@ -338,6 +340,15 @@ Certain words of this list are keywords because they are functions that can be u
for example CURRENT_TIMESTAMP.
</p>
<br /><a name="standards_compliance"></a>
<h2>Standards Compliance</h2>
<p>
This database tries to be as much standard compliant as possible. For the SQL language, ANSI/ISO is the main
standard. There are several versions that refer to the release date: SQL-92, SQL:1999, and SQL:2003.
Unfortunately, the standard documentation is not freely available. Another problem is that important features
are not standardized. Whenever this is the case, this database tries to be compatible to other databases.
</p>
<br /><a name="windows_service"></a>
<h2>Run as Windows Service</h2>
<p>
......
......@@ -108,6 +108,7 @@ via PayPal:
</li><li>Elisabetta Berlini, Italy
</li><li>William Gilbert, USA
</li><li>Antonio Dieguez, Chile
</li><li><a href="http://ontologyworks.com/">Ontology Works, USA</a>
</li></ul>
</div></td></tr></table><!-- analytics --></body></html>
......@@ -61,6 +61,7 @@ Roadmap
</li><li>Support function overloading as in Java (multiple functions with the same name, but different parameter count or data types)
</li><li>Deferred integrity checking (DEFERRABLE INITIALLY DEFERRED)
</li><li>Groovy Stored Procedures (http://groovy.codehaus.org/Groovy+SQL)
</li><li>Linked tables that point to the same database should share the connection ([SHARED])
</li><li>System table / function: cache usage
</li><li>Add a migration guide (list differences between databases)
</li><li>Optimization: automatic index creation suggestion using the trace file?
......@@ -268,7 +269,6 @@ Roadmap
</li><li>Provide an Java SQL builder with standard and H2 syntax
</li><li>Trace: write OS, file system, JVM,... when opening the database
</li><li>Support indexes for views (probably requires materialized views)
</li><li>Linked tables that point to the same database should share the connection
</li><li>Document SET SEARCH_PATH, BEGIN, EXECUTE, parameters
</li><li>Browser: use Desktop.isDesktopSupported and browse when using JDK 1.6
</li><li>Server: use one listener (detect if the request comes from an PG or TCP client)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -439,11 +439,12 @@ public class FullText implements Trigger {
case Types.CHAR:
case Types.VARCHAR:
return data.toString();
case Types.CLOB:
int test;
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.BINARY:
case Types.JAVA_OBJECT:
case Types.CLOB:
case Types.OTHER:
case Types.BLOB:
case Types.STRUCT:
......@@ -486,8 +487,9 @@ public class FullText implements Trigger {
case Types.LONGVARBINARY:
case Types.BINARY:
return quoteBinary((byte[]) data);
case Types.JAVA_OBJECT:
case Types.CLOB:
int test;
case Types.JAVA_OBJECT:
case Types.OTHER:
case Types.BLOB:
case Types.STRUCT:
......
......@@ -425,11 +425,12 @@ implements Trigger
case Types.CHAR:
case Types.VARCHAR:
return data.toString();
case Types.CLOB:
int todo;
case Types.VARBINARY:
case Types.LONGVARBINARY:
case Types.BINARY:
case Types.JAVA_OBJECT:
case Types.CLOB:
case Types.OTHER:
case Types.BLOB:
case Types.STRUCT:
......@@ -471,8 +472,9 @@ implements Trigger
case Types.LONGVARBINARY:
case Types.BINARY:
return quoteBinary((byte[]) data);
case Types.JAVA_OBJECT:
case Types.CLOB:
int test;
case Types.JAVA_OBJECT:
case Types.OTHER:
case Types.BLOB:
case Types.STRUCT:
......
......@@ -38,7 +38,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
protected long rowCount;
protected boolean isMultiVersion;
public void initBaseIndex(Table table, int id, String name, IndexColumn[] indexColumns, IndexType indexType) {
void initBaseIndex(Table table, int id, String name, IndexColumn[] indexColumns, IndexType indexType) {
initSchemaObjectBase(table.getSchema(), id, name, Trace.INDEX);
this.indexType = indexType;
this.table = table;
......@@ -342,7 +342,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
public void commit(int operation, Row row) throws SQLException {
}
public void setMultiVersion(boolean multiVersion) {
void setMultiVersion(boolean multiVersion) {
this.isMultiVersion = multiVersion;
}
......
......@@ -55,9 +55,21 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
private int headPos;
private long lastChange;
/**
* Create a new b tree index with the given properties. If the index does
* not yet exist, a new empty one is created.
*
* @param session the session
* @param table the base table
* @param id the object id
* @param indexName the name of the index
* @param columns the indexed columns
* @param indexType the index type
* @param headPos the position of the index header page, or Index.EMPTY_HEAD
* for a new index
*/
public BtreeIndex(Session session, TableData table, int id, String indexName, IndexColumn[] columns,
IndexType indexType, int headPos) throws SQLException {
// TODO we need to log index data
initBaseIndex(table, id, indexName, columns, indexType);
this.tableData = table;
Database db = table.getDatabase();
......@@ -142,6 +154,11 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return (BtreePage) storage.getRecord(session, i);
}
/**
* Write all changed paged to disk and mark the index as valid.
*
* @param session the session
*/
public void flush(Session session) throws SQLException {
lastChange = 0;
if (storage != null) {
......@@ -320,6 +337,11 @@ public class BtreeIndex extends BaseIndex implements RecordReader {
return storage.getRecordOverhead();
}
/**
* Get the last change time or 0 if the index has not been changed.
*
* @return the last change time or 0
*/
public long getLastChange() {
return lastChange;
}
......
......@@ -55,7 +55,7 @@ public class BtreeLeaf extends BtreePage {
this.pageData = pageData;
}
public int add(Row newRow, Session session) throws SQLException {
int add(Row newRow, Session session) throws SQLException {
int l = 0, r = pageData.size();
while (l < r) {
int i = (l + r) >>> 1;
......@@ -88,7 +88,7 @@ public class BtreeLeaf extends BtreePage {
return splitPoint;
}
public SearchRow remove(Session session, Row oldRow) throws SQLException {
SearchRow remove(Session session, Row oldRow) throws SQLException {
int l = 0, r = pageData.size();
if (r == 0) {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
......@@ -134,7 +134,7 @@ public class BtreeLeaf extends BtreePage {
throw Message.getSQLException(ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1, index.getSQL());
}
public BtreePage split(Session session, int splitPoint) throws SQLException {
BtreePage split(Session session, int splitPoint) throws SQLException {
ObjectArray data = new ObjectArray();
int max = pageData.size();
for (int i = splitPoint; i < max; i++) {
......@@ -148,7 +148,7 @@ public class BtreeLeaf extends BtreePage {
return n2;
}
public boolean findFirst(BtreeCursor cursor, SearchRow compare, boolean bigger) throws SQLException {
boolean findFirst(BtreeCursor cursor, SearchRow compare, boolean bigger) throws SQLException {
int l = 0, r = pageData.size();
if (r == 0 && !Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
......@@ -172,7 +172,7 @@ public class BtreeLeaf extends BtreePage {
return true;
}
public void next(BtreeCursor cursor, int i) throws SQLException {
void next(BtreeCursor cursor, int i) throws SQLException {
i++;
if (i < pageData.size()) {
SearchRow r = (SearchRow) pageData.get(i);
......@@ -184,7 +184,7 @@ public class BtreeLeaf extends BtreePage {
nextUpper(cursor);
}
public void previous(BtreeCursor cursor, int i) throws SQLException {
void previous(BtreeCursor cursor, int i) throws SQLException {
i--;
if (i >= 0) {
SearchRow r = (SearchRow) pageData.get(i);
......@@ -196,7 +196,7 @@ public class BtreeLeaf extends BtreePage {
previousUpper(cursor);
}
public void first(BtreeCursor cursor) throws SQLException {
void first(BtreeCursor cursor) throws SQLException {
if (pageData.size() == 0) {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
......@@ -209,7 +209,7 @@ public class BtreeLeaf extends BtreePage {
cursor.setCurrentRow(row);
}
public void last(BtreeCursor cursor) throws SQLException {
void last(BtreeCursor cursor) throws SQLException {
int last = pageData.size() - 1;
if (last < 0) {
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
......@@ -284,7 +284,7 @@ public class BtreeLeaf extends BtreePage {
}
}
public int getRealByteCount() throws SQLException {
int getRealByteCount() throws SQLException {
if (cachedRealByteCount > 0) {
return cachedRealByteCount;
}
......
......@@ -72,7 +72,7 @@ public class BtreeNode extends BtreePage {
return r;
}
public int add(Row newRow, Session session) throws SQLException {
int add(Row newRow, Session session) throws SQLException {
int l = 0, r = pageData.size();
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page");
......@@ -114,7 +114,7 @@ public class BtreeNode extends BtreePage {
return 0;
}
public SearchRow remove(Session session, Row oldRow) throws SQLException {
SearchRow remove(Session session, Row oldRow) throws SQLException {
int l = 0, r = pageData.size();
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page");
......@@ -180,7 +180,7 @@ public class BtreeNode extends BtreePage {
}
}
public BtreePage split(Session session, int splitPoint) throws SQLException {
BtreePage split(Session session, int splitPoint) throws SQLException {
ObjectArray data = new ObjectArray();
IntArray children = new IntArray();
splitPoint++;
......@@ -209,7 +209,7 @@ public class BtreeNode extends BtreePage {
return pageChildren.get(i);
}
public boolean findFirst(BtreeCursor cursor, SearchRow compare, boolean bigger) throws SQLException {
boolean findFirst(BtreeCursor cursor, SearchRow compare, boolean bigger) throws SQLException {
int l = 0, r = pageData.size();
if (!Constants.ALLOW_EMPTY_BTREE_PAGES && pageChildren.size() == 0) {
throw Message.getInternalError("Empty btree page");
......@@ -263,7 +263,7 @@ public class BtreeNode extends BtreePage {
return false;
}
public void next(BtreeCursor cursor, int i) throws SQLException {
void next(BtreeCursor cursor, int i) throws SQLException {
i++;
if (i <= pageData.size()) {
cursor.setStackPosition(i);
......@@ -274,7 +274,7 @@ public class BtreeNode extends BtreePage {
nextUpper(cursor);
}
public void previous(BtreeCursor cursor, int i) throws SQLException {
void previous(BtreeCursor cursor, int i) throws SQLException {
i--;
if (i >= 0) {
cursor.setStackPosition(i);
......@@ -307,13 +307,13 @@ public class BtreeNode extends BtreePage {
}
}
public void first(BtreeCursor cursor) throws SQLException {
void first(BtreeCursor cursor) throws SQLException {
cursor.push(this, 0);
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(0));
page.first(cursor);
}
public void last(BtreeCursor cursor) throws SQLException {
void last(BtreeCursor cursor) throws SQLException {
int last = pageChildren.size() - 1;
cursor.push(this, last);
BtreePage page = index.getPage(cursor.getSession(), pageChildren.get(last));
......
......@@ -15,7 +15,21 @@ import org.h2.message.Message;
*/
public class InDoubtTransaction {
public static final int IN_DOUBT = 0, COMMIT = 1, ROLLBACK = 2;
/**
* The transaction state meaning this transaction is not committed yet, but
* also not rolled back (in-doubt).
*/
public static final int IN_DOUBT = 0;
/**
* The transaction state meaning this transaction is committed.
*/
public static final int COMMIT = 1;
/**
* The transaction state meaning this transaction is rolled back.
*/
public static final int ROLLBACK = 2;
// TODO 2-phase-commit: document sql statements and metadata table
......@@ -26,7 +40,7 @@ public class InDoubtTransaction {
private int blocks;
private int state;
public InDoubtTransaction(LogFile log, int sessionId, int pos, String transaction, int blocks) {
InDoubtTransaction(LogFile log, int sessionId, int pos, String transaction, int blocks) {
this.log = log;
this.sessionId = sessionId;
this.pos = pos;
......@@ -35,6 +49,12 @@ public class InDoubtTransaction {
this.state = IN_DOUBT;
}
/**
* Change the state of this transaction.
* This will also update the log file.
*
* @param state the new state
*/
public void setState(int state) throws SQLException {
switch(state) {
case COMMIT:
......@@ -49,6 +69,11 @@ public class InDoubtTransaction {
this.state = state;
}
/**
* Get the state of this transaction as a text.
*
* @return the transaction state text
*/
public String getState() {
switch(state) {
case IN_DOUBT:
......@@ -62,6 +87,11 @@ public class InDoubtTransaction {
}
}
/**
* Get the name of the transaction.
*
* @return the transaction name
*/
public String getTransaction() {
return transaction;
}
......
......@@ -51,8 +51,12 @@ import org.h2.util.ObjectArray;
*/
public class LogFile {
private static final int BUFFER_SIZE = 8 * 1024;
/**
* The size of the smallest possible transaction log entry in bytes.
*/
public static final int BLOCK_SIZE = 16;
private static final int BUFFER_SIZE = 8 * 1024;
private LogSystem logSystem;
private Database database;
......@@ -113,11 +117,16 @@ public class LogFile {
return new LogFile(log, id, fileNamePrefix);
}
/**
* Get the name of this transaction log file.
*
* @return the file name
*/
public String getFileName() {
return fileNamePrefix + "." + id + Constants.SUFFIX_LOG_FILE;
}
public int getId() {
int getId() {
return id;
}
......@@ -325,7 +334,7 @@ public class LogFile {
return true;
}
public void redoAllGoEnd() throws SQLException {
void redoAllGoEnd() throws SQLException {
boolean readOnly = logSystem.getDatabase().getReadOnly();
long length = file.length();
if (length <= FileStore.HEADER_LENGTH) {
......@@ -521,11 +530,11 @@ public class LogFile {
return pos;
}
public long getFileSize() throws SQLException {
long getFileSize() throws SQLException {
return file.getFilePointer();
}
public void sync() {
void sync() {
if (file != null) {
file.sync();
}
......
......@@ -31,6 +31,9 @@ import org.h2.util.ObjectUtils;
*/
public class LogSystem {
/**
* This special log position means that the log entry has been written.
*/
public static final int LOG_WRITTEN = -1;
private Database database;
......@@ -53,6 +56,15 @@ public class LogSystem {
private boolean closed;
private String accessMode;
/**
* Create new transaction log object. This will not open or create files
* yet.
*
* @param database the database
* @param fileNamePrefix the name of the database file
* @param readOnly if the log should be opened in read-only mode
* @param accessMode the file access mode (r, rw, rws, rwd)
*/
public LogSystem(Database database, String fileNamePrefix, boolean readOnly, String accessMode) throws SQLException {
this.database = database;
this.readOnly = readOnly;
......@@ -65,14 +77,29 @@ public class LogSystem {
rowBuff = DataPage.create(database, Constants.DEFAULT_DATA_PAGE_SIZE);
}
/**
* Set the maximum log file size in megabytes.
*
* @param maxSize the new maximum log file size
*/
public void setMaxLogSize(long maxSize) {
this.maxLogSize = maxSize;
}
/**
* Get the maximum log file size.
*
* @return the maximum size
*/
public long getMaxLogSize() {
return maxLogSize;
}
/**
* Check if there are any in-doubt transactions.
*
* @return true if there are
*/
public boolean containsInDoubtTransactions() {
return inDoubtTransactions != null && inDoubtTransactions.size() > 0;
}
......@@ -132,6 +159,9 @@ public class LogSystem {
}
}
/**
* Close all log files.
*/
public void close() throws SQLException {
if (database == null) {
return;
......@@ -191,13 +221,17 @@ public class LogSystem {
undo.add(record);
}
public boolean recover() throws SQLException {
/**
* Roll back any uncommitted transactions if required, and apply committed
* changed to the data files.
*/
public void recover() throws SQLException {
if (database == null) {
return false;
return;
}
synchronized (database) {
if (closed) {
return false;
return;
}
undo = new ObjectArray();
for (int i = 0; i < activeLogs.size(); i++) {
......@@ -232,7 +266,7 @@ public class LogSystem {
if (!readOnly && fileChanged && !containsInDoubtTransactions()) {
checkpoint();
}
return fileChanged;
return;
}
}
......@@ -240,6 +274,9 @@ public class LogSystem {
l.close(deleteOldLogFilesAutomatically && keepFiles == 0);
}
/**
* Open all existing transaction log files and create a new one if required.
*/
public void open() throws SQLException {
String path = FileUtils.getParent(fileNamePrefix);
String[] list = FileUtils.listFiles(path);
......@@ -330,6 +367,11 @@ public class LogSystem {
state.inDoubtTransaction = new InDoubtTransaction(log, sessionId, pos, transaction, blocks);
}
/**
* Get the list of in-doubt transactions.
*
* @return the list
*/
public ObjectArray getInDoubtTransactions() {
return inDoubtTransactions;
}
......@@ -338,6 +380,12 @@ public class LogSystem {
sessions.remove(ObjectUtils.getInteger(sessionId));
}
/**
* Prepare a transaction.
*
* @param session the session
* @param transaction the name of the transaction
*/
public void prepareCommit(Session session, String transaction) throws SQLException {
if (database == null || readOnly) {
return;
......@@ -350,6 +398,11 @@ public class LogSystem {
}
}
/**
* Commit the current transaction of the given session.
*
* @param session the session
*/
public void commit(Session session) throws SQLException {
if (database == null || readOnly) {
return;
......@@ -363,6 +416,9 @@ public class LogSystem {
}
}
/**
* Flush all pending changes to the transaction log files.
*/
public void flush() throws SQLException {
if (database == null || readOnly) {
return;
......@@ -404,6 +460,13 @@ public class LogSystem {
}
}
/**
* Add an log entry to the last transaction log file.
*
* @param session the session
* @param file the file
* @param record the record to log
*/
public void add(Session session, DiskFile file, Record record) throws SQLException {
if (database == null) {
return;
......@@ -428,6 +491,10 @@ public class LogSystem {
}
}
/**
* Flush all data to the transaction log files as well as to the data files
* and and switch log files.
*/
public void checkpoint() throws SQLException {
if (readOnly || database == null) {
return;
......@@ -444,6 +511,11 @@ public class LogSystem {
}
}
/**
* Get all active log files.
*
* @return the list of log files
*/
public ObjectArray getActiveLogFiles() {
synchronized (database) {
ObjectArray list = new ObjectArray();
......@@ -483,14 +555,27 @@ public class LogSystem {
return rowBuff;
}
/**
* Enable or disable-flush-on-each-commit.
*
* @param b the new value
*/
public void setFlushOnEachCommit(boolean b) {
flushOnEachCommit = b;
}
/**
* Check if flush-on-each-commit is enabled.
*
* @return true if it is
*/
boolean getFlushOnEachCommit() {
return flushOnEachCommit;
}
/**
* Flush the transaction log file and sync the data to disk.
*/
public void sync() throws SQLException {
if (database == null || readOnly) {
return;
......@@ -503,6 +588,11 @@ public class LogSystem {
}
}
/**
* Enable or disable the transaction log
*
* @param disabled true if the log should be switched off
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
......@@ -512,10 +602,18 @@ public class LogSystem {
file.addRedoLog(storage, recordId, blockCount, rec);
}
/**
* Write a log entry meaning the index summary is invalid.
*/
public void invalidateIndexSummary() throws SQLException {
currentLog.addSummary(false, null);
}
/**
* Increment or decrement the flag to keep (not delete) old log files.
*
* @param incrementDecrement (1 to increment, -1 to decrement)
*/
public synchronized void updateKeepFiles(int incrementDecrement) {
keepFiles += incrementDecrement;
}
......
......@@ -16,7 +16,7 @@ public class SessionState {
int lastCommitPos;
InDoubtTransaction inDoubtTransaction;
public boolean isCommitted(int logId, int pos) {
boolean isCommitted(int logId, int pos) {
if (logId != lastCommitLog) {
return lastCommitLog > logId;
}
......
......@@ -30,11 +30,21 @@ public class UndoLog {
private DataPage rowBuff;
private int memoryUndo;
/**
* Create a new undo log for the given session.
*
* @param session the session
*/
public UndoLog(Session session) {
this.session = session;
this.database = session.getDatabase();
}
/**
* Get the number of active rows in this undo log.
*
* @return the number of rows
*/
public int size() {
if (SysProperties.CHECK && memoryUndo > records.size()) {
throw Message.getInternalError();
......@@ -42,6 +52,10 @@ public class UndoLog {
return records.size();
}
/**
* Clear the undo log. This method is called after the transaction is
* committed.
*/
public void clear() {
records.clear();
memoryUndo = 0;
......@@ -52,6 +66,11 @@ public class UndoLog {
}
}
/**
* Get the last record and remove it from the list of operations.
*
* @return the last record
*/
public UndoLogRecord getAndRemoveLast() throws SQLException {
int i = records.size() - 1;
UndoLogRecord entry = (UndoLogRecord) records.get(i);
......@@ -77,6 +96,11 @@ public class UndoLog {
return entry;
}
/**
* Append an undo log entry to the log.
*
* @param entry the entry
*/
public void add(UndoLogRecord entry) throws SQLException {
records.add(entry);
if (!entry.isStored()) {
......
......@@ -26,7 +26,17 @@ import org.h2.value.Value;
* An entry in a undo log.
*/
public class UndoLogRecord {
public static final short INSERT = 0, DELETE = 1;
/**
* Operation type meaning the row was inserted.
*/
public static final short INSERT = 0;
/**
* Operation type meaning the row was deleted.
*/
public static final short DELETE = 1;
private static final int IN_MEMORY = 0, STORED = 1, IN_MEMORY_READ_POS = 2;
private Table table;
private Row row;
......@@ -34,14 +44,13 @@ public class UndoLogRecord {
private short state;
private int filePos;
public boolean isStored() {
return state == STORED;
}
public boolean canStore() {
return table.getUniqueIndex() != null;
}
/**
* Create a new undo log record
*
* @param table the table
* @param op the operation type
* @param row the row that was deleted or inserted
*/
public UndoLogRecord(Table table, short op, Row row) {
this.table = table;
this.row = row;
......@@ -49,6 +58,20 @@ public class UndoLogRecord {
this.state = IN_MEMORY;
}
boolean isStored() {
return state == STORED;
}
boolean canStore() {
return table.getUniqueIndex() != null;
}
/**
* Un-do the operation. If the row was inserted before, it is deleted now,
* and vice versa.
*
* @param session the session
*/
public void undo(Session session) throws SQLException {
switch (operation) {
case INSERT:
......@@ -97,7 +120,7 @@ public class UndoLogRecord {
}
}
public void save(DataPage buff, FileStore file) throws SQLException {
void save(DataPage buff, FileStore file) throws SQLException {
buff.reset();
buff.writeInt(0);
buff.writeInt(operation);
......@@ -114,11 +137,11 @@ public class UndoLogRecord {
state = STORED;
}
public void seek(FileStore file) throws SQLException {
void seek(FileStore file) throws SQLException {
file.seek(filePos * Constants.FILE_BLOCK_SIZE);
}
public void load(DataPage buff, FileStore file, Session session) throws SQLException {
void load(DataPage buff, FileStore file, Session session) throws SQLException {
int min = Constants.FILE_BLOCK_SIZE;
seek(file);
buff.reset();
......@@ -144,10 +167,19 @@ public class UndoLogRecord {
state = IN_MEMORY_READ_POS;
}
/**
* Get the table.
*
* @return the table
*/
public Table getTable() {
return table;
}
/**
* This method is called after the operation was committed.
* It commits the change to the indexes.
*/
public void commit() throws SQLException {
ObjectArray list = table.getIndexes();
for (int i = 0; i < list.size(); i++) {
......@@ -155,7 +187,12 @@ public class UndoLogRecord {
index.commit(operation, row);
}
}
/**
* Get the row that was deleted or inserted.
*
* @return the row
*/
public Row getRow() {
return row;
}
......
......@@ -34,7 +34,6 @@ public class Trace {
public static final String SCHEMA = "schema";
public static final String DATABASE = "database";
public static final String SESSION = "session";
public static final String AGGREGATE = "aggregate";
Trace(TraceWriter traceWriter, String module) {
this.traceWriter = traceWriter;
......
......@@ -29,14 +29,50 @@ import org.h2.util.SmallLRUCache;
* the log file will be opened and closed again (which is slower).
*/
public class TraceSystem implements TraceWriter {
public static final int OFF = 0, ERROR = 1, INFO = 2, DEBUG = 3;
public static final int ADAPTER = 4;
// max file size is currently 64 MB,
// and then there could be a .old file of the same size
private static final int DEFAULT_MAX_FILE_SIZE = 64 * 1024 * 1024;
/**
* This trace level means nothing should be written.
*/
public static final int OFF = 0;
/**
* This trace level means only errors should be written.
*/
public static final int ERROR = 1;
/**
* This trace level means errors and informational messages should be
* written.
*/
public static final int INFO = 2;
/**
* This trace level means all type of messages should be written.
*/
public static final int DEBUG = 3;
/**
* This trace level means all type of messages should be written, but
* instead of using the trace file the messages should be written to SLF4J.
*/
public static final int ADAPTER = 4;
/**
* The default level for system out trace messages.
*/
public static final int DEFAULT_TRACE_LEVEL_SYSTEM_OUT = OFF;
/**
* The default level for file trace messages.
*/
public static final int DEFAULT_TRACE_LEVEL_FILE = ERROR;
/**
* The default maximum trace file size. It is currently 64 MB. Additionally,
* there could be a .old file of the same size.
*/
private static final int DEFAULT_MAX_FILE_SIZE = 64 * 1024 * 1024;
private static final int CHECK_FILE_TIME = 4000;
private int levelSystemOut = DEFAULT_TRACE_LEVEL_SYSTEM_OUT;
private int levelFile = DEFAULT_TRACE_LEVEL_FILE;
......@@ -54,17 +90,12 @@ public class TraceSystem implements TraceWriter {
private boolean writingErrorLogged;
private TraceWriter writer = this;
public static void traceThrowable(Throwable e) {
PrintWriter writer = DriverManager.getLogWriter();
if (writer != null) {
e.printStackTrace(writer);
}
}
public void setManualEnabling(boolean value) {
this.manualEnabling = value;
}
/**
* Create a new trace system object.
*
* @param fileName the file name
* @param init if the trace system should be initialized
*/
public TraceSystem(String fileName, boolean init) {
this.fileName = fileName;
traces = new SmallLRUCache(100);
......@@ -77,7 +108,35 @@ public class TraceSystem implements TraceWriter {
}
}
}
/**
* Write the exception to the driver manager log writer if configured.
*
* @param e the exception
*/
public static void traceThrowable(Throwable e) {
PrintWriter writer = DriverManager.getLogWriter();
if (writer != null) {
e.printStackTrace(writer);
}
}
/**
* Allow to manually enable the trace option by placing a specially named
* file in the right folder.
*
* @param value the new value
*/
public void setManualEnabling(boolean value) {
this.manualEnabling = value;
}
/**
* Get or create a trace object for this module.
*
* @param module the module name
* @return the trace object
*/
public synchronized Trace getTrace(String module) {
Trace t = (Trace) traces.get(module);
if (t == null) {
......@@ -92,18 +151,38 @@ public class TraceSystem implements TraceWriter {
return level <= max;
}
/**
* Set the trace file name.
*
* @param name the file name
*/
public void setFileName(String name) {
this.fileName = name;
}
/**
* Set the maximum trace file size in bytes.
*
* @param max the maximum size
*/
public void setMaxFileSize(int max) {
this.maxFileSize = max;
}
/**
* Set the trace level to use for System.out
*
* @param level the new level
*/
public void setLevelSystemOut(int level) {
levelSystemOut = level;
}
/**
* Set the file trace level.
*
* @param level the new level
*/
public void setLevelFile(int level) {
if (level == ADAPTER) {
String adapterClass = "org.h2.message.TraceWriterAdapter";
......@@ -255,6 +334,11 @@ public class TraceSystem implements TraceWriter {
}
}
/**
* Close the writers, and the files if required. It is still possible to
* write after closing, however after each write the file is closed again
* (slowing down tracing).
*/
public void close() {
closeWriter();
closed = true;
......
......@@ -160,18 +160,23 @@ java org.h2.test.TestAll timer
/*
jazoon
support CLOB in fulltext index
upload and test javadoc/index.html
test clob fulltext index (lucene and native).
download PostgreSQL docs
in help.csv, use complete examples for functions; add a test case
upload and test javadoc/index.html
improve javadocs
option to write complete page right after checkpoint
upload jazoon
test case for out of memory (try to corrupt the database using out of memory)
analyzer configuration option for the fulltext search
......@@ -227,6 +232,7 @@ Roadmap:
converted to primary key columns.
PostgreSQL compatibility: support for BOOL_OR and BOOL_AND
aggregate functions.
The fulltext search did not support CLOB data types. Fixed.
*/
if (args.length > 0) {
......
......@@ -23,12 +23,16 @@ public class TestFullText extends TestBase {
if (config.memory) {
return;
}
test(false);
test(false, "VARCHAR");
int test;
test(false, "VARCHAR");
testPerformance(false);
String luceneFullTextClassName = "org.h2.fulltext.FullTextLucene";
try {
Class.forName(luceneFullTextClassName);
test(true);
test(true, "VARCHAR");
int test2;
test(true, "VARCHAR");
testPerformance(true);
} catch (ClassNotFoundException e) {
println("Class not found, not tested: " + luceneFullTextClassName);
......@@ -80,7 +84,7 @@ public class TestFullText extends TestBase {
conn.close();
}
private void test(boolean lucene) throws Exception {
private void test(boolean lucene, String dataType) throws Exception {
deleteDb("fullText");
Connection conn = getConnection("fullText");
String prefix = lucene ? "FTL_" : "FT_";
......@@ -89,7 +93,7 @@ public class TestFullText extends TestBase {
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "INIT()");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME " + dataType + ")");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello World')");
stat.execute("CALL " + prefix + "CREATE_INDEX('PUBLIC', 'TEST', NULL)");
ResultSet rs;
......
......@@ -511,4 +511,6 @@ subpackages slowed deactivate throttled noindex expired arizona export
intentional knowing jcl plug facade deployment logback confusion visited
pickle associate subtraction negation multiplication visitors sharp connector
derbynet ado happy derbyclient unspecified federated sysadmin lengths doing
gives clunky
gives clunky cooperative paged conflicts ontology freely regards standards
placing refer informational unlocks
......@@ -375,29 +375,28 @@ public class Doclet {
private boolean doesOverride(MethodDoc method) {
ClassDoc clazz = method.containingClass();
ClassDoc[] ifs = clazz.interfaces();
int pc = method.parameters().length;
String name = method.name();
for (int i = 0;; i++) {
ClassDoc c;
if (i < ifs.length) {
c = ifs[i];
} else {
clazz = clazz.superclass();
if (clazz == null) {
break;
}
c = clazz;
}
MethodDoc[] ms = c.methods();
int parameterCount = method.parameters().length;
return foundMethod(clazz, false, method.name(), parameterCount);
}
private boolean foundMethod(ClassDoc clazz, boolean include, String methodName, int parameterCount) {
if (include) {
MethodDoc[] ms = clazz.methods();
for (int j = 0; j < ms.length; j++) {
MethodDoc m = ms[j];
if (m.name().equals(name) && m.parameters().length == pc) {
if (m.name().equals(methodName) && m.parameters().length == parameterCount) {
return true;
}
}
}
return false;
ClassDoc[] ifs = clazz.interfaces();
for (int i = 0; i < ifs.length; i++) {
if (foundMethod(ifs[i], true, methodName, parameterCount)) {
return true;
}
}
clazz = clazz.superclass();
return clazz != null && foundMethod(clazz, true, methodName, parameterCount);
}
private static String getFirstSentence(Tag[] tags) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论