提交 5773980e authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 b790d91a
......@@ -599,6 +599,8 @@ SQL grammar documentation.
<p>
Usually, the database opens log, data and index files with the access mode 'rw', meaning
read-write (except for read only databases, where the mode 'r' is used).
To open a database in read-only mode if the files are not read-only, use
ACCESS_MODE_DATA=r.
Also supported are 'rws' and 'rwd'.
The access mode used for log files is set via ACCESS_MODE_LOG; for
data and index files use ACCESS_MODE_DATA.
......
......@@ -1118,13 +1118,68 @@ public class ErrorCode {
*/
public static final int FUNCTION_ALIAS_ALREADY_EXISTS_1 = 90076;
private int todo44;
/**
* The error with code <code>90077</code> is thrown when
* trying to drop a system function or a function alias that does not exist.
* Example:
* <pre>
* DROP ALIAS SQRT;
* </pre>
*/
public static final int FUNCTION_ALIAS_NOT_FOUND_1 = 90077;
/**
* The error with code <code>90078</code> is thrown when
* trying to create a schema if an object with this name already exists.
* Example:
* <pre>
* CREATE SCHEMA TEST_SCHEMA;
* CREATE SCHEMA TEST_SCHEMA;
* </pre>
*/
public static final int SCHEMA_ALREADY_EXISTS_1 = 90078;
/**
* The error with code <code>90079</code> is thrown when
* trying to drop a schema that does not exist.
* Example:
* <pre>
* DROP SCHEMA UNKNOWN;
* </pre>
*/
public static final int SCHEMA_NOT_FOUND_1 = 90079;
/**
* The error with code <code>90080</code> is thrown when
* trying to rename a object to a different schema, or when trying to
* create a related object in another schema.
* Example:
* <pre>
* CREATE SCHEMA TEST_SCHEMA;
* CREATE TABLE TEST(ID INT);
* CREATE INDEX TEST_ID ON TEST(ID);
* ALTER INDEX TEST_ID RENAME TO TEST_SCHEMA.IDX_TEST_ID;
* </pre>
*/
public static final int SCHEMA_NAME_MUST_MATCH = 90080;
/**
* The error with code <code>90081</code> is thrown when
* trying to alter a column to not allow NULL, if there
* is already data in the table where this column is NULL.
* Example:
* <pre>
* CREATE TABLE TEST(ID INT);
* INSERT INTO TEST VALUES(NULL);
* ALTER TABLE TEST ALTER COLUMN ID VARCHAR NOT NULL;
* </pre>
*/
public static final int COLUMN_CONTAINS_NULL_VALUES_1 = 90081;
/**
* The error with code <code>90082</code> is thrown when
* trying to drop a system generated sequence.
*/
public static final int SEQUENCE_BELONGS_TO_A_TABLE_1 = 90082;
/**
......@@ -1137,6 +1192,16 @@ public class ErrorCode {
* </pre>
*/
public static final int COLUMN_MAY_BE_REFERENCED_1 = 90083;
/**
* The error with code <code>90084</code> is thrown when
* trying to drop the last column of a table.
* Example:
* <pre>
* CREATE TABLE TEST(ID INT);
* ALTER TABLE TEST DROP COLUMN ID;
* </pre>
*/
public static final int CANNOT_DROP_LAST_COLUMN = 90084;
/**
......@@ -1153,6 +1218,9 @@ public class ErrorCode {
* </pre>
*/
public static final int INDEX_BELONGS_TO_CONSTRAINT_1 = 90085;
private int todo37;
public static final int CLASS_NOT_FOUND_1 = 90086;
public static final int METHOD_NOT_FOUND_1 = 90087;
public static final int UNKNOWN_MODE_1 = 90088;
......
......@@ -51,7 +51,6 @@ import org.h2.util.BitField;
import org.h2.util.ByteUtils;
import org.h2.util.CacheLRU;
import org.h2.util.ClassUtils;
import org.h2.util.DelayedFileDeleter;
import org.h2.util.FileUtils;
import org.h2.util.IOUtils;
import org.h2.util.IntHashMap;
......@@ -160,13 +159,10 @@ public class Database implements DataHandler {
String lockMethodName = ci.removeProperty("FILE_LOCK", null);
this.accessModeLog = ci.removeProperty("ACCESS_MODE_LOG", "rw").toLowerCase();
this.accessModeData = ci.removeProperty("ACCESS_MODE_DATA", "rw").toLowerCase();
int testing;
if ("r".equals(accessModeData)) {
readOnly = true;
accessModeLog = "r";
}
this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
this.textStorage = ci.getTextStorage();
this.databaseURL = ci.getURL();
......@@ -431,12 +427,8 @@ public class Database implements DataHandler {
if (persistent) {
String dataFileName = databaseName + Constants.SUFFIX_DATA_FILE;
if (FileUtils.exists(dataFileName)) {
int testingReadOnly;
// if it is already read-only because ACCESS_MODE_DATA=r
readOnly = readOnly | FileUtils.isReadOnly(dataFileName);
// readOnly = FileUtils.isReadOnly(dataFileName);
textStorage = isTextStorage(dataFileName, textStorage);
}
}
......@@ -933,7 +925,11 @@ public class Database implements DataHandler {
private void stopWriter() {
if (writer != null) {
writer.stopThread();
try {
writer.stopThread();
} catch (SQLException e) {
traceSystem.getTrace(Trace.DATABASE).error("close", e);
}
writer = null;
}
}
......@@ -1366,7 +1362,11 @@ public class Database implements DataHandler {
}
public void deleteLogFileLater(String fileName) throws SQLException {
DelayedFileDeleter.getInstance().deleteLater(fileName);
if (writer != null) {
writer.deleteLogFileLater(fileName);
} else {
FileUtils.delete(fileName);
}
}
public Class loadUserClass(String className) throws SQLException {
......
......@@ -1447,6 +1447,7 @@ public class Function extends Expression implements FunctionCall {
case LTRIM:
case RTRIM:
case TRIM:
min = 1;
max = 2;
break;
case REPLACE:
......
......@@ -103,6 +103,10 @@ public class LogFile {
}
}
int id = Integer.parseInt(s);
if (!FileUtils.exists(fileName)) {
// the file could have been deleted by now (by the DelayedFileDeleter)
return null;
}
return new LogFile(log, id, fileNamePrefix);
}
......
......@@ -266,7 +266,7 @@ if(SysProperties.getIntSetting(SysProperties.H2_LOG_DELETE_DELAY, 0) > 0) {
for(int i=0; i<activeLogs.size();i++) {
LogFile current = (LogFile) activeLogs.get(i);
if(last != null && last.getId() + 1 != current.getId()) {
throw Message.getInternalError("Miissing log file: " + last.getId() + ", " + current.getId());
throw Message.getInternalError("Missing log file: " + last.getId() + ", " + current.getId());
}
last = current;
}
......
......@@ -795,6 +795,7 @@ SET [DATABASE] COLLATION
","
Sets the collation used for comparing strings.
This command can only be executed if there are no tables defined.
See java.test.Collator for details about STRENGTH.
This setting is persistent.
Admin rights are required to execute this command.
","
......
......@@ -41,6 +41,15 @@ import org.h2.util.ObjectArray;
* The disk file is responsible for caching; each object contains a {@link Cache} object.
* Changes in the file are logged in a {@link LogSystem} object.
* Reading and writing to the file is delegated to the {@link FileStore} class.
* <p>
* There are 'blocks' of 128 bytes (DiskFile.BLOCK_SIZE). Each objects own one or more pages;
* each page size is 64 blocks (DiskFile.BLOCKS_PER_PAGE). That is 8 KB page size.
* However pages are not read or written as one unit; only individual objects (multiple blocks at a time)
* are read or written.
* <p>
* Currently there are no in-place updates. Each row occupies one or multiple blocks.
* Row can occupy multiple pages. Rows are always contiguous (except LOBs, they are
* stored in their own files).
*/
public class DiskFile implements CacheWriter {
......
......@@ -16,7 +16,7 @@ import org.h2.security.SecureFileStore;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystem;
import org.h2.util.ByteUtils;
import org.h2.util.DelayedFileDeleter;
import org.h2.util.TempFileDeleter;
/**
* This class is an abstraction of a random access file.
......@@ -163,7 +163,7 @@ public class FileStore {
public void closeAndDeleteSilently() {
if (file != null) {
closeSilently();
DelayedFileDeleter.getInstance().autoDeleteFile(autoDeleteReference, name);
TempFileDeleter.deleteFile(autoDeleteReference, name);
name = null;
}
}
......@@ -324,11 +324,11 @@ public class FileStore {
}
public void autoDelete() {
autoDeleteReference = DelayedFileDeleter.getInstance().addTempFile(name, this);
autoDeleteReference = TempFileDeleter.addFile(name, this);
}
public void stopAutoDelete() {
DelayedFileDeleter.getInstance().stopAutoDelete(autoDeleteReference, name);
TempFileDeleter.stopAutoDelete(autoDeleteReference, name);
autoDeleteReference = null;
}
......
......@@ -16,6 +16,7 @@ import org.h2.log.LogSystem;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.table.Table;
import org.h2.util.FileUtils;
import org.h2.util.ObjectArray;
/**
......@@ -38,6 +39,7 @@ public class WriterThread extends Thread {
private int writeDelay;
private long lastIndexFlush;
private volatile boolean stop;
private String oldLogFile;
private WriterThread(Database database, int writeDelay) {
this.databaseRef = new WeakReference(database);
......@@ -106,6 +108,14 @@ public class WriterThread extends Thread {
public void run() {
while (!stop) {
synchronized (this) {
if (oldLogFile != null) {
FileUtils.tryDelete(oldLogFile);
if (!FileUtils.exists(oldLogFile)) {
oldLogFile = null;
}
}
}
Database database = (Database) databaseRef.get();
if (database == null) {
break;
......@@ -138,8 +148,16 @@ public class WriterThread extends Thread {
databaseRef = null;
}
public void stopThread() {
public void stopThread() throws SQLException {
stop = true;
deleteLogFileLater(null);
}
public synchronized void deleteLogFileLater(String fileName) throws SQLException {
if (oldLogFile != null) {
FileUtils.delete(oldLogFile);
}
oldLogFile = fileName;
}
}
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
/**
* Deletes files later on or if they are not used.
* This class deletes temporary files when they are not used any longer.
*/
public class DelayedFileDeleter extends Thread {
private static DelayedFileDeleter instance;
private final ReferenceQueue queue = new ReferenceQueue();
private final HashMap refMap = new HashMap();
private final HashMap deleteLater = new HashMap();
private long deleteNext;
public synchronized void deleteLater(String fileName) throws SQLException {
int delay = SysProperties.getLogFileDeleteDelay();
if (delay == 0 && deleteLater.size() == 0) {
// shortcut if delay is 0
FileUtils.delete(fileName);
return;
}
if (deleteLater.containsKey(fileName)) {
return;
}
long at = System.currentTimeMillis() + delay;
if (deleteNext != 0 && at <= deleteNext) {
// make sure files are deleted in the correct order
at = deleteNext + 1;
}
deleteNext = at;
deleteLater.put(fileName, ObjectUtils.getLong(at));
}
/**
* Delete at most one old file (after the delay)
*/
private void deleteOld() {
long now = System.currentTimeMillis();
if (deleteNext == 0 || now < deleteNext) {
return;
}
String delete = null;
long oldest = 0;
for (Iterator it = deleteLater.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
long at = ((Long) entry.getValue()).longValue();
if (at < now && (delete == null || at < oldest)) {
delete = (String) entry.getKey();
oldest = at;
}
}
if (delete == null) {
return;
}
try {
FileUtils.delete(delete);
} catch (SQLException e) {
// ignore
}
deleteLater.remove(delete);
}
public static synchronized DelayedFileDeleter getInstance() {
if (instance == null) {
instance = new DelayedFileDeleter();
instance.setDaemon(true);
instance.setPriority(Thread.MIN_PRIORITY);
instance.start();
}
return instance;
}
private DelayedFileDeleter() {
setName("H2 FileDeleter");
}
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// ignore
}
synchronized (this) {
if (refMap.size() != 0) {
deleteUnused();
} else if (deleteLater.size() != 0) {
deleteOld();
} else {
break;
}
}
}
}
/**
* Add a temp file to the queue and delete it if it is no longer referenced.
*
* @param fileName the file name
* @param file the object to track
* @return the reference
*/
public synchronized Reference addTempFile(String fileName, Object file) {
FileUtils.trace("FileDeleter.addFile", fileName, file);
PhantomReference ref = new PhantomReference(file, queue);
refMap.put(ref, fileName);
return ref;
}
/**
* Delete a file now and remove it from the queue.
*
* @param the reference in the queue
* @param fileName the file name
*/
public synchronized void autoDeleteFile(Reference ref, String fileName) {
if (ref != null) {
String f2 = (String) refMap.remove(ref);
if (SysProperties.CHECK && f2 != null && fileName != null && !f2.equals(fileName)) {
throw Message.getInternalError("f2:" + f2 + " f:" + fileName);
}
}
if (fileName != null && FileUtils.exists(fileName)) {
try {
FileUtils.trace("FileDeleter.deleteFile", fileName, null);
FileUtils.delete(fileName);
} catch (Exception e) {
// TODO log such errors?
}
deleteUnused();
}
}
/**
* Delete all unreferenced files that have been garbage collected now.
* This method is called from time to time by the application.
*/
private void deleteUnused() {
while (true) {
Reference ref = queue.poll();
if (ref == null) {
break;
}
autoDeleteFile(ref, null);
}
}
/**
* Remove a file from the list of files to be deleted.
*
* @param ref the reference
* @param fileName the file name
*/
public synchronized void stopAutoDelete(Reference ref, String fileName) {
FileUtils.trace("FileDeleter.stopAutoDelete", fileName, ref);
if (ref != null) {
String f2 = (String) refMap.remove(ref);
if (SysProperties.CHECK && (f2 == null || !f2.equals(fileName))) {
throw Message.getInternalError("f2:" + f2 + " f:" + fileName);
}
}
}
}
/*
* Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
import org.h2.constant.SysProperties;
import org.h2.message.Message;
/**
* This class deletes temporary files when they are not used any longer.
*/
public class TempFileDeleter {
private static final ReferenceQueue QUEUE = new ReferenceQueue();
private static final HashMap REF_MAP = new HashMap();
public static synchronized Reference addFile(String fileName, Object file) {
FileUtils.trace("TempFileDeleter.addFile", fileName, file);
PhantomReference ref = new PhantomReference(file, QUEUE);
REF_MAP.put(ref, fileName);
deleteUnused();
return ref;
}
public static synchronized void deleteFile(Reference ref, String fileName) {
if (ref != null) {
String f2 = (String) REF_MAP.remove(ref);
if (SysProperties.CHECK && f2 != null && fileName != null && !f2.equals(fileName)) {
throw Message.getInternalError("f2:" + f2 + " f:" + fileName);
}
}
if (fileName != null && FileUtils.exists(fileName)) {
try {
FileUtils.trace("TempFileDeleter.deleteFile", fileName, null);
FileUtils.delete(fileName);
} catch (Exception e) {
// TODO log such errors?
}
deleteUnused();
}
}
public static void deleteUnused() {
// Mystery: I don't know how QUEUE could get null, but two independent
// people reported NullPointerException here - if somebody understands
// how it could happen please report it!
// Environment: web application under Tomcat, exception occurs during undeploy
while (QUEUE != null) {
Reference ref = QUEUE.poll();
if (ref == null) {
break;
}
deleteFile(ref, null);
}
}
public static void stopAutoDelete(Reference ref, String fileName) {
FileUtils.trace("TempFileDeleter.stopAutoDelete", fileName, ref);
if (ref != null) {
String f2 = (String) REF_MAP.remove(ref);
if (SysProperties.CHECK && (f2 == null || !f2.equals(fileName))) {
throw Message.getInternalError("f2:" + f2 + " f:" + fileName);
}
}
deleteUnused();
}
}
......@@ -156,7 +156,7 @@ java org.h2.test.TestAll timer
test.printSystem();
int test2;
// System.setProperty(SysProperties.H2_LOG_DELETE_DELAY, "20");
// System.setProperty(SysProperties.H2_LOG_DELETE_DELAY, "2");
// TestRecover.main(new String[0]);
......@@ -164,16 +164,20 @@ int test2;
valentine
create force trigger : test & document
read only databases without having to make the files read-only: test & document
TestSessionsLocks
...?
the user should be allowed to do everything with his own temp tables (and views).
CREATE USER IF NOT EXISTS READER PASSWORD 'READER';
<login as READER>
CREATE LOCAL TEMPORARY TABLE IF NOT EXISTS MY_TEST(ID INT);
INSERT INTO MY_TEST VALUES(1);
SELECT * FROM MY_TEST;
DROP TABLE MY_TEST;
Automate real power off tests
Extend tests that simulate power off
timer test
Can sometimes not delete log file?
Test delayed log files delete
link to new changelog and roadmap, remove pages from google groups
......@@ -187,6 +191,11 @@ Adjust cache memory usage
Test Recovery with MAX_LOG_FILE_SIZE=1; test with various log file sizes
History:
Databases can now be opened even if trigger classes are not in the classpath.
The exception is thrown when trying to fire the trigger.
Opening databases with ACCESS_MODE_DATA=r is now supported.
In this case the database is read-only, but the files don't not need
to be read-only.
Security: The database now waits 200 ms before throwing an exception if
the user name or password don't match, to slow down dictionary attacks.
The value cache is now a soft reference cache. This should help save memory.
......@@ -195,19 +204,7 @@ ALTER TABLE ALTER COLUMN RESTART and ALTER SEQUENCE now support an expressions.
When setting the base directory on the command line, the user directory prefix ('~') was ignored.
Roadmap:
Support CREATE FORCE TRIGGER.
drop all objects;
CREATE TABLE INVOICE(ID INT PRIMARY KEY, AMOUNT DECIMAL);
CREATE TRIGGER INV_INS AFTER INSERT ON INVOICE FOR EACH ROW CALL "org.h2.samples.TriggerSample$MyTrigger" ;
DROP TRIGGER INV_INS;
CREATE TRIGGER INV_INS AFTER INSERT ON INVOICE FOR EACH ROW CALL "org.h2.samples.TriggerSample$MyTrigger" ;
insert into invoice values(1, 2);
disconnect
change to MyTrigger2
connect
insert into invoice values(2, 3);
DROP TRIGGER INV_INS;
BIT VARYING
*/
......@@ -408,7 +405,7 @@ DROP TRIGGER INV_INS;
*/
private void test() throws Exception {
System.out.println();
System.out.println("Test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc);
System.out.println("Test big:"+big+" net:"+networked+" cipher:"+cipher+" memory:"+memory+" log:"+logMode+" diskResult:"+diskResult + " mvcc:" + mvcc + " deleteIndex:" + deleteIndex);
beforeTest();
// db
......
......@@ -24,15 +24,13 @@ public class TestReadOnly extends TestBase {
if (config.memory) {
return;
}
testReadOnlyFileAccessMode();
testReadOnlyFiles();
}
private void testReadOnlyFileAccessMode() {
int todo;
testReadOnlyFiles(true);
if (!config.deleteIndex) {
testReadOnlyFiles(false);
}
}
private void testReadOnlyFiles() throws Exception {
private void testReadOnlyFiles(boolean setReadOnly) throws Exception {
File f = File.createTempFile("test", "temp");
check(f.canWrite());
......@@ -57,9 +55,12 @@ public class TestReadOnly extends TestBase {
check(!conn.isReadOnly());
conn.close();
setReadOnly();
conn = getConnection("readonly");
if (setReadOnly) {
setReadOnly();
conn = getConnection("readonly");
} else {
conn = getConnection("readonly;ACCESS_MODE_DATA=r");
}
check(conn.isReadOnly());
stat = conn.createStatement();
stat.execute("SELECT * FROM TEST");
......@@ -71,7 +72,11 @@ public class TestReadOnly extends TestBase {
}
conn.close();
conn = getConnection("readonly;DB_CLOSE_DELAY=1");
if (setReadOnly) {
conn = getConnection("readonly;DB_CLOSE_DELAY=1");
} else {
conn = getConnection("readonly;DB_CLOSE_DELAY=1;ACCESS_MODE_DATA=r");
}
stat = conn.createStatement();
stat.execute("SELECT * FROM TEST");
try {
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
select rtrim() from dual;
> exception
CREATE TABLE COUNT(X INT);
> ok
CREATE FORCE TRIGGER T_COUNT BEFORE INSERT ON COUNT CALL "com.Unknown";
> ok
INSERT INTO COUNT VALUES(NULL);
> exception
DROP TRIGGER T_COUNT;
> ok
CREATE TABLE ITEMS(ID INT CHECK ID < SELECT MAX(ID) FROM COUNT);
> ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论