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

Spellcheck / formatting / docs

上级 b424b429
...@@ -1655,7 +1655,7 @@ Issue 454: Use Charset for type-safety. ...@@ -1655,7 +1655,7 @@ Issue 454: Use Charset for type-safety.
Queries with both LIMIT and OFFSET could throw an IllegalArgumentException. Queries with both LIMIT and OFFSET could throw an IllegalArgumentException.
@changelog_1006_li @changelog_1006_li
MVStore: multiple issues were fixed: 460, 461, 462. MVStore: multiple issues were fixed: 460, 461, 462, 464, 466.
@changelog_1007_li @changelog_1007_li
MVStore: larger stores (multiple GB) are now much faster. MVStore: larger stores (multiple GB) are now much faster.
......
...@@ -1655,7 +1655,7 @@ Centralリポジトリの利用 ...@@ -1655,7 +1655,7 @@ Centralリポジトリの利用
#Queries with both LIMIT and OFFSET could throw an IllegalArgumentException. #Queries with both LIMIT and OFFSET could throw an IllegalArgumentException.
@changelog_1006_li @changelog_1006_li
#MVStore: multiple issues were fixed: 460, 461, 462. #MVStore: multiple issues were fixed: 460, 461, 462, 464, 466.
@changelog_1007_li @changelog_1007_li
#MVStore: larger stores (multiple GB) are now much faster. #MVStore: larger stores (multiple GB) are now much faster.
......
...@@ -550,7 +550,7 @@ changelog_1002_li=The auto-analyze feature now only reads 1000 rows per table in ...@@ -550,7 +550,7 @@ changelog_1002_li=The auto-analyze feature now only reads 1000 rows per table in
changelog_1003_li=The optimization for IN(...) queries combined with OR could result in a strange exception of the type "column x must be included in the group by list". changelog_1003_li=The optimization for IN(...) queries combined with OR could result in a strange exception of the type "column x must be included in the group by list".
changelog_1004_li=Issue 454\: Use Charset for type-safety. changelog_1004_li=Issue 454\: Use Charset for type-safety.
changelog_1005_li=Queries with both LIMIT and OFFSET could throw an IllegalArgumentException. changelog_1005_li=Queries with both LIMIT and OFFSET could throw an IllegalArgumentException.
changelog_1006_li=MVStore\: multiple issues were fixed\: 460, 461, 462. changelog_1006_li=MVStore\: multiple issues were fixed\: 460, 461, 462, 464, 466.
changelog_1007_li=MVStore\: larger stores (multiple GB) are now much faster. changelog_1007_li=MVStore\: larger stores (multiple GB) are now much faster.
changelog_1008_li=When using local temporary tables and not dropping them manually before closing the session, and then killing the process could result in a database that couldn't be opened (except when using the recover tool). changelog_1008_li=When using local temporary tables and not dropping them manually before closing the session, and then killing the process could result in a database that couldn't be opened (except when using the recover tool).
changelog_1009_li=Support TRUNC(timestamp) for improved Oracle compatibility. changelog_1009_li=Support TRUNC(timestamp) for improved Oracle compatibility.
......
...@@ -1334,7 +1334,7 @@ public class Session extends SessionWithState { ...@@ -1334,7 +1334,7 @@ public class Session extends SessionWithState {
} }
return startStatement; return startStatement;
} }
/** /**
* Start a new statement within a transaction. * Start a new statement within a transaction.
*/ */
......
...@@ -1738,7 +1738,7 @@ public class MVStore { ...@@ -1738,7 +1738,7 @@ public class MVStore {
public boolean isClosed() { public boolean isClosed() {
return closed; return closed;
} }
private void stopBackgroundThread() { private void stopBackgroundThread() {
if (backgroundThread == null) { if (backgroundThread == null) {
return; return;
...@@ -1768,7 +1768,7 @@ public class MVStore { ...@@ -1768,7 +1768,7 @@ public class MVStore {
backgroundThread = t; backgroundThread = t;
} }
} }
public int getWriteDelay() { public int getWriteDelay() {
return writeDelay; return writeDelay;
} }
......
...@@ -100,7 +100,12 @@ public class MVTableEngine implements TableEngine { ...@@ -100,7 +100,12 @@ public class MVTableEngine implements TableEngine {
public List<MVTable> getTables() { public List<MVTable> getTables() {
return openTables; return openTables;
} }
/**
* Remove a table.
*
* @param table the table
*/
public void removeTable(MVTable table) { public void removeTable(MVTable table) {
openTables.remove(table); openTables.remove(table);
} }
......
...@@ -269,8 +269,10 @@ public abstract class TestBase { ...@@ -269,8 +269,10 @@ public abstract class TestBase {
} else { } else {
url = name; url = name;
} }
// url = addOption(url, "DEFAULT_TABLE_ENGINE", if (config.mvStore) {
// "org.h2.mvstore.db.MVTableEngine"); url = addOption(url, "DEFAULT_TABLE_ENGINE",
"org.h2.mvstore.db.MVTableEngine");
}
if (!config.memory) { if (!config.memory) {
if (config.smallLog && admin) { if (config.smallLog && admin) {
url = addOption(url, "MAX_LOG_SIZE", "1"); url = addOption(url, "MAX_LOG_SIZE", "1");
......
...@@ -26,8 +26,8 @@ public class BenchB implements Bench, Runnable { ...@@ -26,8 +26,8 @@ public class BenchB implements Bench, Runnable {
private static final int TELLERS = 10; private static final int TELLERS = 10;
private static final int ACCOUNTS = 100000; private static final int ACCOUNTS = 100000;
private int noThreads = 10; private int threadCount = 10;
// master data // master data
private Database database; private Database database;
private int transactionPerClient; private int transactionPerClient;
...@@ -200,8 +200,8 @@ public class BenchB implements Bench, Runnable { ...@@ -200,8 +200,8 @@ public class BenchB implements Bench, Runnable {
} }
private void processTransactions() throws Exception { private void processTransactions() throws Exception {
Thread[] threads = new Thread[noThreads]; Thread[] threads = new Thread[threadCount];
for (int i = 0; i < noThreads; i++) { for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(new BenchB(this, i), "BenchB-" + i); threads[i] = new Thread(new BenchB(this, i), "BenchB-" + i);
} }
for (Thread t : threads) { for (Thread t : threads) {
...@@ -216,8 +216,8 @@ public class BenchB implements Bench, Runnable { ...@@ -216,8 +216,8 @@ public class BenchB implements Bench, Runnable {
public String getName() { public String getName() {
return "BenchB"; return "BenchB";
} }
public void setNoThreads(int noThreads) { public void setThreadCount(int threadCount) {
this.noThreads = noThreads; this.threadCount = threadCount;
} }
} }
...@@ -30,13 +30,9 @@ import org.h2.util.StringUtils; ...@@ -30,13 +30,9 @@ import org.h2.util.StringUtils;
*/ */
class Database { class Database {
public interface DatabaseParentInterface {
boolean isCollect();
void trace(String msg);
}
private static final boolean TRACE = true; private static final boolean TRACE = true;
private DatabaseParentInterface test; private DatabaseTest test;
private int id; private int id;
private String name, url, user, password; private String name, url, user, password;
private final ArrayList<String[]> replace = new ArrayList<String[]>(); private final ArrayList<String[]> replace = new ArrayList<String[]>();
...@@ -49,8 +45,8 @@ class Database { ...@@ -49,8 +45,8 @@ class Database {
private final ArrayList<Object[]> results = new ArrayList<Object[]>(); private final ArrayList<Object[]> results = new ArrayList<Object[]>();
private int totalTime; private int totalTime;
private final AtomicInteger executedStatements = new AtomicInteger(0); private final AtomicInteger executedStatements = new AtomicInteger(0);
private int noThreads; private int threadCount;
private Server serverH2; private Server serverH2;
private Object serverDerby; private Object serverDerby;
private boolean serverHSQLDB; private boolean serverHSQLDB;
...@@ -153,13 +149,15 @@ class Database { ...@@ -153,13 +149,15 @@ class Database {
* @param test the test application * @param test the test application
* @param id the database id * @param id the database id
* @param dbString the configuration string * @param dbString the configuration string
* @param threadCount the number of threads to use
* @return a new database object with the given settings * @return a new database object with the given settings
*/ */
static Database parse(DatabaseParentInterface test, int id, String dbString) { static Database parse(DatabaseTest test, int id, String dbString, int threadCount) {
try { try {
StringTokenizer tokenizer = new StringTokenizer(dbString, ","); StringTokenizer tokenizer = new StringTokenizer(dbString, ",");
Database db = new Database(); Database db = new Database();
db.id = id; db.id = id;
db.threadCount = threadCount;
db.test = test; db.test = test;
db.name = tokenizer.nextToken().trim(); db.name = tokenizer.nextToken().trim();
String driver = tokenizer.nextToken().trim(); String driver = tokenizer.nextToken().trim();
...@@ -177,12 +175,6 @@ class Database { ...@@ -177,12 +175,6 @@ class Database {
} }
} }
static Database parse(DatabaseParentInterface test, int id, String dbString, int noThreads) {
Database db = parse(test, id, dbString);
db.noThreads = noThreads;
return db;
}
/** /**
* Open a new database connection. This connection must be closed * Open a new database connection. This connection must be closed
* by calling conn.close(). * by calling conn.close().
...@@ -463,9 +455,30 @@ class Database { ...@@ -463,9 +455,30 @@ class Database {
int getId() { int getId() {
return id; return id;
} }
int getNoThreads() { int getThreadsCount() {
return noThreads; return threadCount;
}
/**
* The interface used for a test.
*/
public interface DatabaseTest {
/**
* Whether data needs to be collected.
*
* @return true if yes
*/
boolean isCollect();
/**
* Print a message to system out if trace is enabled.
*
* @param msg the message
*/
void trace(String msg);
} }
} }
...@@ -27,7 +27,7 @@ import org.h2.util.JdbcUtils; ...@@ -27,7 +27,7 @@ import org.h2.util.JdbcUtils;
* The main controller class of the benchmark application. * The main controller class of the benchmark application.
* To run the benchmark, call the main method of this class. * To run the benchmark, call the main method of this class.
*/ */
public class TestPerformance implements Database.DatabaseParentInterface { public class TestPerformance implements Database.DatabaseTest {
/** /**
* Whether data should be collected. * Whether data should be collected.
...@@ -97,7 +97,7 @@ public class TestPerformance implements Database.DatabaseParentInterface { ...@@ -97,7 +97,7 @@ public class TestPerformance implements Database.DatabaseParentInterface {
} }
String dbString = prop.getProperty("db" + i); String dbString = prop.getProperty("db" + i);
if (dbString != null) { if (dbString != null) {
Database db = Database.parse(this, i, dbString); Database db = Database.parse(this, i, dbString, 1);
if (db != null) { if (db != null) {
db.setTranslations(prop); db.setTranslations(prop);
dbs.add(db); dbs.add(db);
...@@ -248,17 +248,13 @@ public class TestPerformance implements Database.DatabaseParentInterface { ...@@ -248,17 +248,13 @@ public class TestPerformance implements Database.DatabaseParentInterface {
bench.runTest(); bench.runTest();
} }
/** @Override
* Print a message to system out if trace is enabled. public void trace(String msg) {
*
* @param s the message
*/
public void trace(String s) {
if (trace) { if (trace) {
System.out.println(s); System.out.println(msg);
} }
} }
@Override @Override
public boolean isCollect() { public boolean isCollect() {
return collect; return collect;
......
...@@ -24,7 +24,7 @@ import org.h2.util.JdbcUtils; ...@@ -24,7 +24,7 @@ import org.h2.util.JdbcUtils;
* Used to compare scalability between the old engine and the new MVStore engine. * Used to compare scalability between the old engine and the new MVStore engine.
* Mostly it runs BenchB with various numbers of threads. * Mostly it runs BenchB with various numbers of threads.
*/ */
public class TestScalability implements Database.DatabaseParentInterface { public class TestScalability implements Database.DatabaseTest {
/** /**
* Whether data should be collected. * Whether data should be collected.
...@@ -38,7 +38,7 @@ public class TestScalability implements Database.DatabaseParentInterface { ...@@ -38,7 +38,7 @@ public class TestScalability implements Database.DatabaseParentInterface {
/** /**
* This method is called when executing this sample application. * This method is called when executing this sample application.
* *
* @param args the command line parameters * @param args the command line parameters
*/ */
public static void main(String... args) throws Exception { public static void main(String... args) throws Exception {
...@@ -80,8 +80,8 @@ public class TestScalability implements Database.DatabaseParentInterface { ...@@ -80,8 +80,8 @@ public class TestScalability implements Database.DatabaseParentInterface {
dbs.add(createDbEntry(id++, "H2", 40, h2Url)); dbs.add(createDbEntry(id++, "H2", 40, h2Url));
dbs.add(createDbEntry(id++, "H2", 50, h2Url)); dbs.add(createDbEntry(id++, "H2", 50, h2Url));
dbs.add(createDbEntry(id++, "H2", 100, h2Url)); dbs.add(createDbEntry(id++, "H2", 100, h2Url));
final String mvUrl = "jdbc:h2:data/mvtest;LOCK_TIMEOUT=10000;DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine"; final String mvUrl = "jdbc:h2:data/mvTest;LOCK_TIMEOUT=10000;DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine";
dbs.add(createDbEntry(id++, "MV", 1, mvUrl)); dbs.add(createDbEntry(id++, "MV", 1, mvUrl));
dbs.add(createDbEntry(id++, "MV", 10, mvUrl)); dbs.add(createDbEntry(id++, "MV", 10, mvUrl));
dbs.add(createDbEntry(id++, "MV", 20, mvUrl)); dbs.add(createDbEntry(id++, "MV", 20, mvUrl));
...@@ -89,7 +89,7 @@ public class TestScalability implements Database.DatabaseParentInterface { ...@@ -89,7 +89,7 @@ public class TestScalability implements Database.DatabaseParentInterface {
dbs.add(createDbEntry(id++, "MV", 40, mvUrl)); dbs.add(createDbEntry(id++, "MV", 40, mvUrl));
dbs.add(createDbEntry(id++, "MV", 50, mvUrl)); dbs.add(createDbEntry(id++, "MV", 50, mvUrl));
dbs.add(createDbEntry(id++, "MV", 100, mvUrl)); dbs.add(createDbEntry(id++, "MV", 100, mvUrl));
final BenchB test = new BenchB(); final BenchB test = new BenchB();
testAll(dbs, test, size); testAll(dbs, test, size);
collect = false; collect = false;
...@@ -144,9 +144,9 @@ public class TestScalability implements Database.DatabaseParentInterface { ...@@ -144,9 +144,9 @@ public class TestScalability implements Database.DatabaseParentInterface {
} }
} }
private Database createDbEntry(int id, String namePrefix, int noThreads, String url) { private Database createDbEntry(int id, String namePrefix, int threadCount, String url) {
Database db = Database.parse(this, id, Database db = Database.parse(this, id,
namePrefix + "(" + noThreads + "thread), org.h2.Driver, " + url + ", sa, sa", noThreads); namePrefix + "(" + threadCount + "threads), org.h2.Driver, " + url + ", sa, sa", threadCount);
return db; return db;
} }
...@@ -181,13 +181,13 @@ public class TestScalability implements Database.DatabaseParentInterface { ...@@ -181,13 +181,13 @@ public class TestScalability implements Database.DatabaseParentInterface {
private static void runDatabase(Database db, BenchB bench, int size) throws Exception { private static void runDatabase(Database db, BenchB bench, int size) throws Exception {
bench.init(db, size); bench.init(db, size);
bench.setNoThreads(db.getNoThreads()); bench.setThreadCount(db.getThreadsCount());
bench.runTest(); bench.runTest();
} }
/** /**
* Print a message to system out if trace is enabled. * Print a message to system out if trace is enabled.
* *
* @param s the message * @param s the message
*/ */
@Override @Override
......
db1 = H2(embedded), org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa db1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
#xdb1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine, sa, sa #xdb1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine, sa, sa
...@@ -14,7 +14,7 @@ db1 = H2(embedded), org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MOD ...@@ -14,7 +14,7 @@ db1 = H2(embedded), org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MOD
db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa
db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/derby;create=true, sa, sa db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/derby;create=true, sa, sa
db4 = H2(tcpserver), org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa db4 = H2 (Server), org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
db5 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:hsql://localhost/xdb, sa db5 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:hsql://localhost/xdb, sa
db6 = Derby, org.apache.derby.jdbc.ClientDriver, jdbc:derby://localhost/data/derbyServer;create=true, sa, sa db6 = Derby, org.apache.derby.jdbc.ClientDriver, jdbc:derby://localhost/data/derbyServer;create=true, sa, sa
db7 = PostgreSQL, org.postgresql.Driver, jdbc:postgresql:test, sa, sa db7 = PostgreSQL, org.postgresql.Driver, jdbc:postgresql:test, sa, sa
......
...@@ -31,7 +31,7 @@ import org.h2.util.StringUtils; ...@@ -31,7 +31,7 @@ import org.h2.util.StringUtils;
public class TestScript extends TestBase { public class TestScript extends TestBase {
private static final String FILENAME = "org/h2/test/testScript.sql"; private static final String FILENAME = "org/h2/test/testScript.sql";
private boolean failFast; private boolean failFast;
private boolean alwaysReconnect; private boolean alwaysReconnect;
......
...@@ -328,11 +328,11 @@ public class TestMetaData extends TestBase { ...@@ -328,11 +328,11 @@ public class TestMetaData extends TestBase {
assertEquals(0, meta.getMaxTablesInSelect()); assertEquals(0, meta.getMaxTablesInSelect());
assertEquals(0, meta.getMaxUserNameLength()); assertEquals(0, meta.getMaxUserNameLength());
assertEquals("procedure", meta.getProcedureTerm()); assertEquals("procedure", meta.getProcedureTerm());
assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, meta.getResultSetHoldability()); assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, meta.getResultSetHoldability());
assertEquals(DatabaseMetaData.sqlStateSQL99, meta.getSQLStateType()); assertEquals(DatabaseMetaData.sqlStateSQL99, meta.getSQLStateType());
assertFalse(meta.locatorsUpdateCopy()); assertFalse(meta.locatorsUpdateCopy());
assertEquals("schema", meta.getSchemaTerm()); assertEquals("schema", meta.getSchemaTerm());
assertEquals("\\", meta.getSearchStringEscape()); assertEquals("\\", meta.getSearchStringEscape());
assertEquals("LIMIT,MINUS,ROWNUM,SYSDATE,SYSTIME,SYSTIMESTAMP,TODAY", meta.getSQLKeywords()); assertEquals("LIMIT,MINUS,ROWNUM,SYSDATE,SYSTIME,SYSTIMESTAMP,TODAY", meta.getSQLKeywords());
......
...@@ -109,7 +109,7 @@ int test; ...@@ -109,7 +109,7 @@ int test;
//System.out.println(prof.getTop(10)); //System.out.println(prof.getTop(10));
System.out.println((System.currentTimeMillis() - time) + " " + dbName + " after"); System.out.println((System.currentTimeMillis() - time) + " " + dbName + " after");
} }
private void testReferentialIntegrity() throws Exception { private void testReferentialIntegrity() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
Connection conn; Connection conn;
...@@ -122,7 +122,7 @@ int test; ...@@ -122,7 +122,7 @@ int test;
stat.execute("insert into parent values(1)"); stat.execute("insert into parent values(1)");
stat.execute("insert into child values(2)"); stat.execute("insert into child values(2)");
try { try {
stat.execute("alter table child add constraint cp " + stat.execute("alter table child add constraint cp " +
"foreign key(pid) references parent(id)"); "foreign key(pid) references parent(id)");
fail(); fail();
} catch (SQLException e) { } catch (SQLException e) {
...@@ -137,26 +137,28 @@ int test; ...@@ -137,26 +137,28 @@ int test;
stat.execute("insert into parent values(1)"); stat.execute("insert into parent values(1)");
stat.execute("insert into child values(2)"); stat.execute("insert into child values(2)");
try { try {
stat.execute("alter table child add constraint cp " + stat.execute("alter table child add constraint cp " +
"foreign key(pid) references parent(id)"); "foreign key(pid) references parent(id)");
fail(); fail();
} catch (SQLException e) { } catch (SQLException e) {
// expected // expected
} }
stat.execute("drop table child, parent"); stat.execute("drop table child, parent");
// currently not supported, as previous rows are not visible // currently not supported, as previous rows are not visible
// stat.execute("create table test(id identity, parent bigint, foreign key(parent) references(id))"); // stat.execute("create table test(id identity, parent bigint,
// stat.execute("insert into test values(0, 0), (1, NULL), (2, 1), (3, 3), (4, 3)"); // foreign key(parent) references(id))");
// stat.execute("insert into test values(0, 0), (1, NULL),
// (2, 1), (3, 3), (4, 3)");
// stat.execute("drop table test"); // stat.execute("drop table test");
stat.execute("create table parent(id int, x int)"); stat.execute("create table parent(id int, x int)");
stat.execute("insert into parent values(1, 2)"); stat.execute("insert into parent values(1, 2)");
stat.execute("create table child(id int references parent(id)) as select 1"); stat.execute("create table child(id int references parent(id)) as select 1");
conn.close(); conn.close();
} }
private void testWriteDelay() throws Exception { private void testWriteDelay() throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true); FileUtils.deleteRecursive(getBaseDir(), true);
Connection conn; Connection conn;
......
-- Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
-- Version 1.0, and under the Eclipse Public License, Version 1.0
-- (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
select * from dual where x = x + 1 or x in(2, 0); select * from dual where x = x + 1 or x in(2, 0);
> X > X
......
...@@ -726,4 +726,4 @@ simo unpredictable overtakes conditionally decreases warned coupled spin ...@@ -726,4 +726,4 @@ simo unpredictable overtakes conditionally decreases warned coupled spin
unsynchronized reality cores effort slice addleman koskela ville blocking seen unsynchronized reality cores effort slice addleman koskela ville blocking seen
isam charindex removal getdate jesse fake covers covering cheaper adjacent spot isam charindex removal getdate jesse fake covers covering cheaper adjacent spot
transition anthony goubard netherlands versioned orderable customizer cachable transition anthony goubard netherlands versioned orderable customizer cachable
customizers retains customizers retains scalability
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论