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

--no commit message

--no commit message
上级 d60dee1f
......@@ -99,6 +99,15 @@ There is a restriction when inserting data to this table: When inserting or upda
NULL and values that are not set in the insert statement are both inserted as NULL.
This may not have the desired effect if a default value in the target table is other than NULL.
</p>
<p>
For each linked table a new connection is opened. This can be a problem for some databases when using
many linked tables. For Oracle XE, the maximum number of connection can be increased.
Oracle XE needs to be restarted after changing these values:
</p>
<pre>
alter system set processes=100 scope=spfile;
alter system set sessions=100 scope=spfile;
</pre>
<br /><a name="transaction_isolation"></a>
<h2>Transaction Isolation</h2>
......
......@@ -40,7 +40,10 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0.x (2007-09-x)</h3><ul>
<li>A database can now be opened even if class of a user defined function is not in the classpath.
<li>The database file sizes are now increased at most 32 MB at any time.
</li><li>New method DatabaseEventListener.opened that is called just after opening a database.
</li><li>When using the Console with Internet Explorer 6.0 or 7.0, a Javascript error was thrown after clearing the query.
</li><li>A database can now be opened even if class of a user defined function is not in the classpath.
Trying to call the function will throws an exception.
</li><li>User defined functions and constants may not overload built-in functions and constants.
This didn't work before, but now trying to create such an object didn't fail.
......@@ -743,6 +746,7 @@ via PayPal:
</li><li>Pete Haidinyak, USA
</li><li>Jun Iyama, Japan
</li><li>Antonio Casqueiro, Portugal
</li><li>lumber-mill.co.jp, Japan
</li></ul>
</div></td></tr></table></body></html>
......@@ -25,6 +25,12 @@ public interface DatabaseEventListener extends EventListener {
* @param url - the database URL
*/
void init(String url);
/**
* This method is called after the database has been opened.
* It is save to connect to the database and execute statements at this point.
*/
void opened();
/**
* This method is called if the disk space is very low.
......@@ -58,6 +64,8 @@ public interface DatabaseEventListener extends EventListener {
/**
* This method is called before the database is closed normally.
* It is save to connect to the database and execute statements at this point,
* however the connection must be closed before the method returns.
*/
void closingDatabase();
......
......@@ -2214,14 +2214,16 @@ public class Parser {
s = currentToken;
read();
}
if (".".equals(currentToken) && schemaName.equals(database.getShortName())) {
read(".");
schemaName = s;
if (currentTokenType != IDENTIFIER) {
throw Message.getSyntaxError(sqlCommand, parseIndex, "identifier");
if (".".equals(currentToken)) {
if (schemaName.equalsIgnoreCase(database.getShortName())) {
read(".");
schemaName = s;
if (currentTokenType != IDENTIFIER) {
throw Message.getSyntaxError(sqlCommand, parseIndex, "identifier");
}
s = currentToken;
read();
}
s = currentToken;
read();
}
return s;
}
......
......@@ -6,7 +6,6 @@ package org.h2.engine;
import org.h2.constant.SysProperties;
/*
* Coding rules:
* - boolean CHECK = x > boolean CHECK = Database.CHECK
......@@ -96,6 +95,9 @@ public class Constants {
public static final int IO_BUFFER_SIZE = 4 * 1024;
public static final int IO_BUFFER_SIZE_COMPRESS = 128 * 1024;
public static final int DEFAULT_CACHE_SIZE_LINEAR_INDEX = 64 * 1024;
public static final int FILE_PAGE_SIZE = 8 * 1024;
public static final int FILE_MIN_SIZE = 128 * 1024;
public static final int FILE_MAX_INCREMENT = 32 * 1024 * 1024;
public static final String SUFFIX_DB_FILE = ".db";
public static final String SUFFIX_DATA_FILE = ".data.db";
public static final String SUFFIX_LOG_FILE = ".log.db";
......
......@@ -538,7 +538,7 @@ public class Database implements DataHandler {
emergencyReserve.autoDelete();
emergencyReserve.setLength(SysProperties.EMERGENCY_SPACE_INITIAL);
}
traceSystem.getTrace(Trace.DATABASE).info("opened " + databaseName);
traceSystem.getTrace(Trace.DATABASE).info("opened " + databaseName);
}
private void recompileInvalidViews(Session session) {
......@@ -784,7 +784,7 @@ public class Database implements DataHandler {
void close(boolean fromShutdownHook) {
synchronized (this) {
this.closing = true;
closing = true;
if (sessions.size() > 0) {
if (!fromShutdownHook) {
return;
......@@ -804,8 +804,17 @@ public class Database implements DataHandler {
}
traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName);
if (eventListener != null) {
eventListener.closingDatabase();
// allow the event listener to connect to the database
closing = false;
DatabaseEventListener e = eventListener;
// set it to null, to make sure it's called only once
eventListener = null;
e.closingDatabase();
if (sessions.size() > 0) {
// if a connection was opened, we can't close the database
return;
}
closing = true;
}
try {
if (persistent && fileData != null) {
......@@ -1541,4 +1550,10 @@ public class Database implements DataHandler {
return multiVersion;
}
public void opened() throws SQLException {
if (eventListener != null) {
eventListener.opened();
}
}
}
......@@ -59,6 +59,7 @@ public class Engine {
if (!ci.isUnnamed()) {
databases.put(name, database);
}
database.opened();
}
synchronized (database) {
if (database.isClosing()) {
......
......@@ -98,12 +98,16 @@ function help() {
function setSelection(element) {
if(document.all && !is_opera) {
var range = document.selection.createRange();
var copy = range.duplicate();
copy.moveToElementText(element);
copy.setEndPoint('EndToEnd', range);
element.selectionStart = copy.text.length - range.text.length;
element.selectionEnd = element.selectionStart + range.text.length;
try {
var range = document.selection.createRange();
var copy = range.duplicate();
copy.moveToElementText(element);
copy.setEndPoint('EndToEnd', range);
element.selectionStart = copy.text.length - range.text.length;
element.selectionEnd = element.selectionStart + range.text.length;
} catch (e) {
element.selectionEnd = element.selectionStart = 0;
}
}
}
......
......@@ -463,7 +463,7 @@ public class DiskFile implements CacheWriter {
writeDirectDeleted(pos, blockCount);
} else {
long min = ((long) pos + blockCount) * BLOCK_SIZE;
min = MathUtils.scaleUp50Percent(128 * 1024, min, 8 * 1024) + OFFSET;
min = MathUtils.scaleUp50Percent(Constants.FILE_MIN_SIZE, min, Constants.FILE_PAGE_SIZE, Constants.FILE_MAX_INCREMENT) + OFFSET;
if (min > file.length()) {
file.setLength(min);
database.notifyFileSize(min);
......
......@@ -352,7 +352,7 @@ public class LogFile {
unwritten.clear();
bufferPos = 0;
long min = (long) pos * BLOCK_SIZE;
min = MathUtils.scaleUp50Percent(128 * 1024, min, 8 * 1024);
min = MathUtils.scaleUp50Percent(Constants.FILE_MIN_SIZE, min, Constants.FILE_PAGE_SIZE, Constants.FILE_MAX_INCREMENT);
if (min > file.length()) {
file.setLength(min);
}
......
......@@ -29,7 +29,7 @@ import org.h2.message.TraceSystem;
public class FileUtils {
public static final String MEMORY_PREFIX = "inmemory:";
public static final String MEMORY_PREFIX = "memFS:", MEMORY_PREFIX_LZF = "memLZF:";
private static final HashMap MEMORY_FILES = new HashMap();
// TODO detection of 'case in sensitive filesystem' could maybe implemented using some other means
private static final boolean IS_FILE_SYSTEM_CASE_INSENSITIVE = (File.separatorChar == '\\');
......@@ -336,7 +336,8 @@ public class FileUtils {
public static MemoryFile getMemoryFile(String fileName) {
MemoryFile m = (MemoryFile) MEMORY_FILES.get(fileName);
if (m == null) {
m = new MemoryFile(fileName);
boolean compress = fileName.startsWith(MEMORY_PREFIX_LZF);
m = new MemoryFile(fileName, compress);
MEMORY_FILES.put(fileName, m);
}
return m;
......@@ -351,7 +352,7 @@ public class FileUtils {
}
public static boolean isInMemory(String fileName) {
return fileName.startsWith(MEMORY_PREFIX);
return fileName.startsWith(MEMORY_PREFIX) || fileName.startsWith(MEMORY_PREFIX_LZF);
}
public static String createTempFile(String name, String suffix, boolean deleteOnExit, boolean inTempDir)
......@@ -425,7 +426,7 @@ public class FileUtils {
public static boolean isDirectory(String fileName) {
fileName = translateFileName(fileName);
if (isInMemory(fileName)) {
// TODO inmemory: currently doesn't support directories
// TODO in memory file system currently doesn't support directories
return false;
}
return new File(fileName).isDirectory();
......
......@@ -38,12 +38,18 @@ public class MathUtils {
return (int) i;
}
public static long scaleUp50Percent(long start, long min, long blockSize) {
while (start < min) {
start += start / 2;
start += start % blockSize;
public static long scaleUp50Percent(long start, long min, long blockSize, long maxIncrease) {
long len;
if (min > maxIncrease * 2) {
len = MathUtils.roundUpLong(min, maxIncrease);
} else {
len = start;
while (len < min) {
len += len / 2;
len += len % blockSize;
}
}
return start;
return len;
}
public static BigDecimal setScale(BigDecimal bd, int scale) throws SQLException {
......
......@@ -13,19 +13,20 @@ import org.h2.message.Message;
public class MemoryFile {
private static final int CACHE_SIZE = 8;
private static final boolean COMPRESS = true;
private static final int BLOCK_SIZE_SHIFT = 16;
private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_SHIFT;
private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1;
private String name;
private final boolean compress;
private byte[] magic;
private long length;
private long pos;
private byte[] magic;
private byte[][] data;
private static final CompressLZF LZF = new CompressLZF();
private static final byte[] BUFFER = new byte[BLOCK_SIZE * 2];
private static final byte[] COMPRESSED_BLOCK;
//#ifdef JDK14
static class Cache extends LinkedHashMap {
private static final long serialVersionUID = 5549197956072850355L;
......@@ -61,7 +62,7 @@ public class MemoryFile {
return false;
}
}
private static final Cache LATER = new Cache(CACHE_SIZE);
private static final Cache COMPRESS_LATER = new Cache(CACHE_SIZE);
//#endif
......@@ -71,15 +72,12 @@ public class MemoryFile {
c.data = data;
c.l = l;
synchronized (LZF) {
LATER.put(c, c);
COMPRESS_LATER.put(c, c);
}
//#endif
}
private static void expand(byte[][] data, int i) {
if (!COMPRESS) {
return;
}
byte[] d = data[i];
if (d.length == BLOCK_SIZE) {
return;
......@@ -92,9 +90,6 @@ public class MemoryFile {
}
private static void compress(byte[][] data, int i) {
if (!COMPRESS) {
return;
}
byte[] d = data[i];
synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
......@@ -106,8 +101,16 @@ public class MemoryFile {
}
}
MemoryFile(String name) {
static {
byte[] n = new byte[BLOCK_SIZE];
int len = LZF.compress(n, BLOCK_SIZE, BUFFER, 0);
COMPRESSED_BLOCK = new byte[len];
System.arraycopy(BUFFER, 0, COMPRESSED_BLOCK, 0, len);
}
MemoryFile(String name, boolean compress) {
this.name = name;
this.compress = compress;
data = new byte[0][];
}
......@@ -130,12 +133,16 @@ public class MemoryFile {
long end = MathUtils.roundUpLong(l, BLOCK_SIZE);
if (end != l) {
int lastBlock = (int) (l >>> BLOCK_SIZE_SHIFT);
expand(data, lastBlock);
if (compress) {
expand(data, lastBlock);
}
byte[] d = data[lastBlock];
for (int i = (int) (l & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
compressLater(data, lastBlock);
if (compress) {
compressLater(data, lastBlock);
}
}
} else {
changeLength(l);
......@@ -154,8 +161,11 @@ public class MemoryFile {
byte[][] n = new byte[blocks][];
System.arraycopy(data, 0, n, 0, Math.min(data.length, n.length));
for (int i = data.length; i < blocks; i++) {
n[i] = new byte[BLOCK_SIZE];
compressLater(n, i);
if (compress) {
n[i] = COMPRESSED_BLOCK;
} else {
n[i] = new byte[BLOCK_SIZE];
}
}
data = n;
}
......@@ -177,7 +187,9 @@ public class MemoryFile {
while (len > 0) {
int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
int id = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, id);
if (compress) {
expand(data, id);
}
byte[] block = data[id];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) {
......@@ -185,7 +197,9 @@ public class MemoryFile {
} else {
System.arraycopy(block, blockOffset, b, off, l);
}
compressLater(data, id);
if (compress) {
compressLater(data, id);
}
off += l;
pos += l;
len -= l;
......
......@@ -106,5 +106,8 @@ public class ShowProgress implements DatabaseEventListener {
public void init(String url) {
System.out.println("Initializing the event listener for database " + url);
}
public void opened() {
}
}
......@@ -148,6 +148,29 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript
/*
Ich hatte auch schon unter Windows Probleme mit generateSeed. Eigentlich braucht es das schon. urandom ist nicht genug, siehe http://en.wikipedia.org/wiki/Urandom 'This may be used for less secure applications.' Ich muss mir berlegen wie man dieses Problem lsen kann... Ev. wie folgt: einen Thread starten, generateSeed dort aufrufen. Wenn es lnger als 1 Sekunde dauert, folgendes verwenden:
Ja. Ich knnte ausserdem Secure-Random verzgert initialisieren, d.h. in einem separaten Thread.
SHA-256 Hash von
System.currentTimeMillis() + " " +
System.identityHashCode(new Object()) + " " +
System.freeMemory() + " " +
System.maxMemory() + " " +
System.totalMemory() + " " +
Math.random() + " " +
System.getProperties().toString() + " " +
Arrays.asList(InetAddress.getAllByName(InetAddress.getLocalHost().getHostName())).toString();
Und bei JDK 1.5 zustzlich:
System.nanoTime()
I think it would be a good idea to include the version (build) number
in the Manifest file of h2.jar
Thus, one could find out the build/version number even if the rest of
the distribution is not available.
slow power off test: slow memFS?
DROP TABLE ONE;
DROP TABLE TWO;
CREATE TABLE ONE(A INT PRIMARY KEY, B INT);
......@@ -176,11 +199,17 @@ SELECT T0.A, T1.A FROM TWO T0 INNER JOIN TWO T1 ON NOT (((T0.A=T0.B ) AND (T0.B=
SELECT T0.A, T1.A FROM ONE T0 INNER JOIN ONE T1 ON NOT ((T0.A=T0.B ) AND (T0.B=1 )) OR (T0.B=2 );
document:
maximum file size increment is 16 MB
dont increase database size by more than 128 MB
DROP TABLE IF EXISTS TEST;
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR);
@LOOP 1000000 INSERT INTO TEST VALUES(?, SPACE(100000));
<stop>
<reconnect>
out of memory
shrink newsletter list (migrate to google groups)
see if maven repository is ok, document
......@@ -677,65 +706,67 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
beforeTest();
// db
new TestScriptSimple().runTest(this);
new TestScript().runTest(this);
new TestAutoRecompile().runTest(this);
new TestBackup().runTest(this);
new TestBatchUpdates().runTest(this);
new TestBigDb().runTest(this);
new TestBigResult().runTest(this);
new TestCache().runTest(this);
new TestCases().runTest(this);
new TestCheckpoint().runTest(this);
new TestCluster().runTest(this);
new TestCompatibility().runTest(this);
new TestCsv().runTest(this);
new TestFunctions().runTest(this);
new TestIndex().runTest(this);
if (!SysProperties.MVCC) {
new TestLinkedTable().runTest(this);
}
new TestListener().runTest(this);
new TestLob().runTest(this);
new TestLogFile().runTest(this);
new TestMemoryUsage().runTest(this);
new TestMultiConn().runTest(this);
new TestMultiDimension().runTest(this);
if (!SysProperties.MVCC) {
new TestMultiThread().runTest(this);
}
new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this);
// new TestScriptSimple().runTest(this);
// new TestScript().runTest(this);
// new TestAutoRecompile().runTest(this);
// new TestBackup().runTest(this);
// new TestBatchUpdates().runTest(this);
// new TestBigDb().runTest(this);
// new TestBigResult().runTest(this);
// new TestCache().runTest(this);
// new TestCases().runTest(this);
// new TestCheckpoint().runTest(this);
// new TestCluster().runTest(this);
// new TestCompatibility().runTest(this);
// new TestCsv().runTest(this);
// new TestFunctions().runTest(this);
// new TestIndex().runTest(this);
// if (!SysProperties.MVCC) {
// new TestLinkedTable().runTest(this);
// }
// new TestListener().runTest(this);
// new TestLob().runTest(this);
// new TestLogFile().runTest(this);
// new TestMemoryUsage().runTest(this);
// new TestMultiConn().runTest(this);
// new TestMultiDimension().runTest(this);
// if (!SysProperties.MVCC) {
// new TestMultiThread().runTest(this);
// }
// new TestOpenClose().runTest(this);
// new TestOptimizations().runTest(this);
new TestPowerOff().runTest(this);
new TestReadOnly().runTest(this);
new TestRights().runTest(this);
new TestRunscript().runTest(this);
new TestSQLInjection().runTest(this);
new TestSequence().runTest(this);
new TestSpaceReuse().runTest(this);
new TestSpeed().runTest(this);
new TestTempTables().runTest(this);
new TestTransaction().runTest(this);
new TestTriggersConstraints().runTest(this);
new TestTwoPhaseCommit().runTest(this);
new TestView().runTest(this);
// server
new TestNestedLoop().runTest(this);
// jdbc
new TestCancel().runTest(this);
new TestDataSource().runTest(this);
new TestManyJdbcObjects().runTest(this);
new TestMetaData().runTest(this);
new TestNativeSQL().runTest(this);
new TestPreparedStatement().runTest(this);
new TestResultSet().runTest(this);
new TestStatement().runTest(this);
new TestTransactionIsolation().runTest(this);
new TestUpdatableResultSet().runTest(this);
new TestXA().runTest(this);
new TestZloty().runTest(this);
System.exit(0);
// new TestReadOnly().runTest(this);
// new TestRights().runTest(this);
// new TestRunscript().runTest(this);
// new TestSQLInjection().runTest(this);
// new TestSequence().runTest(this);
// new TestSpaceReuse().runTest(this);
// new TestSpeed().runTest(this);
// new TestTempTables().runTest(this);
// new TestTransaction().runTest(this);
// new TestTriggersConstraints().runTest(this);
// new TestTwoPhaseCommit().runTest(this);
// new TestView().runTest(this);
//
// // server
// new TestNestedLoop().runTest(this);
//
// // jdbc
// new TestCancel().runTest(this);
// new TestDataSource().runTest(this);
// new TestManyJdbcObjects().runTest(this);
// new TestMetaData().runTest(this);
// new TestNativeSQL().runTest(this);
// new TestPreparedStatement().runTest(this);
// new TestResultSet().runTest(this);
// new TestStatement().runTest(this);
// new TestTransactionIsolation().runTest(this);
// new TestUpdatableResultSet().runTest(this);
// new TestXA().runTest(this);
// new TestZloty().runTest(this);
afterTest();
}
......
......@@ -97,7 +97,7 @@ public abstract class TestBase {
if (config.memory) {
url = "mem:" + name;
} else {
if (!name.startsWith("inmemory:") && !name.startsWith(baseDir + "/")) {
if (!name.startsWith("memFS:") && !name.startsWith(baseDir + "/")) {
name = baseDir + "/" + name;
}
if (config.deleteIndex) {
......
......@@ -5,16 +5,19 @@
package org.h2.test.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.api.DatabaseEventListener;
import org.h2.test.TestBase;
import org.h2.util.JdbcUtils;
public class TestListener extends TestBase implements DatabaseEventListener {
private long last, start;
private String url;
public TestListener() {
start = last = System.currentTimeMillis();
......@@ -79,9 +82,33 @@ public class TestListener extends TestBase implements DatabaseEventListener {
}
public void closingDatabase() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, getUser(), getPassword());
conn.createStatement().execute("DROP TABLE TEST2");
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.closeSilently(conn);
}
}
public void init(String url) {
this.url = url;
}
public void opened() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, getUser(), getPassword());
conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TEST2(ID INT)");
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.closeSilently(conn);
}
}
}
......@@ -175,5 +175,8 @@ public class TestMultiConn extends TestBase implements DatabaseEventListener {
public void init(String url) {
}
public void opened() {
}
}
......@@ -171,5 +171,8 @@ public class TestOpenClose extends TestBase implements DatabaseEventListener {
public void init(String url) {
}
public void opened() {
}
}
......@@ -35,7 +35,7 @@ public class TestPowerOff extends TestBase {
if (config.big) {
dir = baseDir;
} else {
dir = "inmemory:";
dir = "memFS:";
}
url = dir + "/" + dbName + ";file_lock=no";
testSummaryCrash();
......
......@@ -11,8 +11,7 @@ import org.h2.test.TestBase;
import org.h2.util.RandomUtils;
// TODO hsqldb: call 1||null should return 1 but returns null
// TODO hsqldb: call mod(1) should return invalid parameter count but returns
// null
// TODO hsqldb: call mod(1) should return invalid parameter count but returns null
public class TestSynth extends TestBase {
static final int H2 = 0, H2_MEM = 1, HSQLDB = 2, MYSQL = 3, POSTGRESQL = 4;
......@@ -243,7 +242,7 @@ public class TestSynth extends TestBase {
// "sa", "");
// addDatabase("org.ldbc.jdbc.jdbcDriver",
// "jdbc:ldbc:mysql://localhost/test", "sa", "");
// addDatabase("org.h2.Driver", "jdbc:h2:inmemory:synth", "sa", "");
// addDatabase("org.h2.Driver", "jdbc:h2:memFS:synth", "sa", "");
// MySQL: NOT is bound to column: NOT ID = 1 means (NOT ID) = 1 instead
// of NOT (ID=1)
......
......@@ -110,5 +110,8 @@ public class TestExit extends TestBase implements DatabaseEventListener {
public void init(String url) {
}
public void opened() {
}
}
......@@ -15,13 +15,19 @@ import org.h2.value.Value;
public class TestFile extends TestBase implements DataHandler {
public void test() throws Exception {
doTest(false);
doTest(true);
}
private void doTest(boolean compress) throws Exception {
byte[] magic = new byte[0];
int len = getSize(1000, 10000);
Random random = new Random();
FileStore mem = null, file = null;
byte[] buffMem = null;
byte[] buffFile = null;
FileUtils.delete("inmemory:test");
String prefix = compress ? "memLZF:" : "memFS:";
FileUtils.delete(prefix + "test");
FileUtils.delete("~/test");
// config.traceTest = true;
......@@ -33,7 +39,7 @@ public class TestFile extends TestBase implements DataHandler {
buffFile = new byte[l];
}
if (file == null) {
mem = FileStore.open(this, "inmemory:test", "rw", magic);
mem = FileStore.open(this, prefix + "test", "rw", magic);
file = FileStore.open(this, "~/test", "rw", magic);
}
check(file.getFilePointer(), mem.getFilePointer());
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论