提交 815b32a9 authored 作者: Thomas Mueller's avatar Thomas Mueller

javadocs

上级 57b810ca
......@@ -15,6 +15,7 @@ import org.h2.expression.Expression;
import org.h2.table.Plan;
import org.h2.table.PlanItem;
import org.h2.table.TableFilter;
import org.h2.util.ObjectUtils;
import org.h2.util.Permutations;
/**
......@@ -151,20 +152,20 @@ public class Optimizer {
}
boolean generateRandom = (x & 127) == 0;
if (!generateRandom) {
System.arraycopy(best, 0, list, 0, filters.length);
ObjectUtils.arrayCopy(best, list, filters.length);
if (!shuffleTwo(list)) {
generateRandom = true;
}
}
if (generateRandom) {
switched = new BitSet();
System.arraycopy(filters, 0, best, 0, filters.length);
ObjectUtils.arrayCopy(filters, best, filters.length);
shuffleAll(best);
System.arraycopy(best, 0, list, 0, filters.length);
ObjectUtils.arrayCopy(best, list, filters.length);
}
if (testPlan(list)) {
switched = new BitSet();
System.arraycopy(list, 0, best, 0, filters.length);
ObjectUtils.arrayCopy(list, best, filters.length);
}
}
}
......
......@@ -35,6 +35,7 @@ import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.StringUtils;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
......@@ -205,7 +206,7 @@ public class Select extends Query {
if (columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount];
System.arraycopy(row, 0, r2, 0, distinctColumnCount);
ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
row = r2;
}
result.addRow(row);
......@@ -337,7 +338,7 @@ public class Select extends Query {
if (columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount];
System.arraycopy(row, 0, r2, 0, distinctColumnCount);
ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
row = r2;
}
result.addRow(row);
......
......@@ -33,6 +33,10 @@ public class ConnectionInfo {
* The connection display name.
*/
String name;
/**
* The last time this connection was used.
*/
int lastAccess;
ConnectionInfo() {
......
......@@ -162,6 +162,14 @@ public class DbContents {
return defaultSchemaName;
}
/**
* Add double quotes around an identifier if required.
* For the H2 database, only keywords are quoted; for other databases,
* all identifiers are.
*
* @param identifier the identifier
* @return the quoted identifier
*/
String quoteIdentifier(String identifier) {
if (identifier == null) {
return null;
......
......@@ -237,6 +237,12 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Check if a page is free, that is, if all blocks of the page are not in use.
*
* @param page the page id
* @return true if no blocks are used
*/
boolean isPageFree(int page) {
for (int i = page * BLOCKS_PER_PAGE; i < (page + 1) * BLOCKS_PER_PAGE; i++) {
if (used.get(i)) {
......@@ -492,6 +498,15 @@ public class DiskFile implements CacheWriter {
return ((long) block * BLOCK_SIZE) + OFFSET;
}
/**
* Get the record if it is stored in the file, or null if not.
*
* @param session the session
* @param pos the block id
* @param reader the record reader that can parse the data
* @param storageId the storage id
* @return the record or null
*/
Record getRecordIfStored(Session session, int pos, RecordReader reader, int storageId)
throws SQLException {
synchronized (database) {
......@@ -518,6 +533,15 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Get a record from the cache or read it from the file if required.
*
* @param session the session
* @param pos the block id
* @param reader the record reader that can parse the data
* @param storageId the storage id
* @return the record
*/
Record getRecord(Session session, int pos, RecordReader reader, int storageId) throws SQLException {
synchronized (database) {
if (file == null) {
......@@ -561,6 +585,13 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Allocate space in the file.
*
* @param storage the storage
* @param blockCount the number of blocks required
* @return the position of the first entry
*/
int allocate(Storage storage, int blockCount) throws SQLException {
reuseSpace();
synchronized (database) {
......@@ -666,6 +697,13 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Called after a session deleted a row. This sets the last uncommitted
* delete id in the session. This is used to make sure empty space is not
* re-used before the change is committed.
*
* @param session the session
*/
void uncommittedDelete(Session session) {
if (session != null && logChanges && SysProperties.REUSE_SPACE_QUICKLY) {
int deleteId = session.getLastUncommittedDelete();
......@@ -676,6 +714,11 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Free a page, that is, reset the page owner.
*
* @param page the page
*/
void freePage(int page) throws SQLException {
if (!logChanges) {
setPageOwner(page, FREE_PAGE);
......@@ -686,10 +729,22 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Calculate the page number from a block number.
*
* @param pos the block number
* @return the page number
*/
int getPage(int pos) {
return pos >>> BLOCK_PAGE_PAGE_SHIFT;
}
/**
* Get the storage id of a page.
*
* @param page the page id
* @return the storage id
*/
int getPageOwner(int page) {
if (page * BLOCKS_PER_PAGE > fileBlockCount || page >= pageOwners.size()) {
return FREE_PAGE;
......@@ -736,6 +791,12 @@ public class DiskFile implements CacheWriter {
pageOwners.set(page, storageId);
}
/**
* Mark a number of blocks as used.
*
* @param pos the first block id
* @param blockCount the number of blocks
*/
void setUsed(int pos, int blockCount) {
synchronized (database) {
if (pos + blockCount > fileBlockCount) {
......@@ -812,6 +873,12 @@ public class DiskFile implements CacheWriter {
return used;
}
/**
* Update a record.
*
* @param session the session
* @param record the record
*/
void updateRecord(Session session, Record record) throws SQLException {
synchronized (database) {
record.setChanged(true);
......@@ -911,6 +978,15 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Remove a record from the cache and mark it as deleted. This writes the
* old data to the transaction log.
*
* @param session the session
* @param pos the block id
* @param record the record
* @param blockCount the number of blocks
*/
void removeRecord(Session session, int pos, Record record, int blockCount) throws SQLException {
synchronized (database) {
if (logChanges) {
......@@ -922,6 +998,14 @@ public class DiskFile implements CacheWriter {
}
}
/**
* Add a record to the file. The position of the record must already be set
* before. This method will write the change to the transaction log and will
* update the cache.
*
* @param session the session
* @param record the record
*/
void addRecord(Session session, Record record) throws SQLException {
synchronized (database) {
cache.put(record);
......@@ -931,23 +1015,43 @@ public class DiskFile implements CacheWriter {
}
}
/*
* Must be synchronized externally
/**
* Get the cache. The cache must be synchronized externally.
*
* @return the cache
*/
public Cache getCache() {
return cache;
}
/**
* Free up a number of blocks.
*
* @param pos the position of the first block
* @param blockCount the number of blocks
*/
void free(int pos, int blockCount) {
synchronized (database) {
used.setRange(pos, blockCount, false);
}
}
/**
* Get the overhead for each record in bytes.
*
* @return the overhead
*/
int getRecordOverhead() {
return recordOverhead;
}
/**
* Remove all rows for this storage.
*
* @param session the session
* @param storage the storage
* @param pages the page id array
*/
void truncateStorage(Session session, Storage storage, IntArray pages) throws SQLException {
synchronized (database) {
int storageId = storage.getId();
......
......@@ -55,7 +55,7 @@ public class FileObjectMemory implements FileObject {
return false;
}
CompressItem c = (CompressItem) eldest.getKey();
compress(c.data, c.l);
compress(c.data, c.page);
return true;
}
}
......@@ -64,17 +64,25 @@ public class FileObjectMemory implements FileObject {
* Represents a compressed item.
*/
static class CompressItem {
/**
* The file data.
*/
byte[][] data;
int l;
/**
* The page to compress.
*/
int page;
public int hashCode() {
return data.hashCode() ^ l;
return data.hashCode() ^ page;
}
public boolean equals(Object o) {
if (o instanceof CompressItem) {
CompressItem c = (CompressItem) o;
return c.data == data && c.l == l;
return c.data == data && c.page == page;
}
return false;
}
......@@ -88,19 +96,19 @@ public class FileObjectMemory implements FileObject {
touch();
}
private static void compressLater(byte[][] data, int l) {
private static void compressLater(byte[][] data, int page) {
//## Java 1.4 begin ##
CompressItem c = new CompressItem();
c.data = data;
c.l = l;
c.page = page;
synchronized (LZF) {
COMPRESS_LATER.put(c, c);
}
//## Java 1.4 end ##
}
private static void expand(byte[][] data, int i) {
byte[] d = data[i];
private static void expand(byte[][] data, int page) {
byte[] d = data[page];
if (d.length == BLOCK_SIZE) {
return;
}
......@@ -108,17 +116,23 @@ public class FileObjectMemory implements FileObject {
synchronized (LZF) {
LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE);
}
data[i] = out;
data[page] = out;
}
static void compress(byte[][] data, int i) {
byte[] d = data[i];
/**
* Compress the data in a byte array.
*
* @param data the page array
* @param page which page to compress
*/
static void compress(byte[][] data, int page) {
byte[] d = data[page];
synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
if (len <= BLOCK_SIZE) {
d = new byte[len];
System.arraycopy(BUFFER, 0, d, 0, len);
data[i] = d;
data[page] = d;
}
}
}
......@@ -138,25 +152,25 @@ public class FileObjectMemory implements FileObject {
return length;
}
public void setFileLength(long l) {
public void setFileLength(long newLength) {
touch();
if (l < length) {
pos = Math.min(pos, l);
changeLength(l);
long end = MathUtils.roundUpLong(l, BLOCK_SIZE);
if (end != l) {
int lastBlock = (int) (l >>> BLOCK_SIZE_SHIFT);
expand(data, lastBlock);
byte[] d = data[lastBlock];
for (int i = (int) (l & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
if (newLength < length) {
pos = Math.min(pos, newLength);
changeLength(newLength);
long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
if (end != newLength) {
int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
expand(data, lastPage);
byte[] d = data[lastPage];
for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0;
}
if (compress) {
compressLater(data, lastBlock);
compressLater(data, lastPage);
}
}
} else {
changeLength(l);
changeLength(newLength);
}
}
......@@ -193,9 +207,9 @@ public class FileObjectMemory implements FileObject {
}
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);
byte[] block = data[id];
int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, page);
byte[] block = data[page];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) {
System.arraycopy(b, off, block, blockOffset, l);
......@@ -203,7 +217,7 @@ public class FileObjectMemory implements FileObject {
System.arraycopy(block, blockOffset, b, off, l);
}
if (compress) {
compressLater(data, id);
compressLater(data, page);
}
off += l;
pos += l;
......
......@@ -427,6 +427,13 @@ public class FileSystemDatabase extends FileSystem {
return true;
}
/**
* Update a file in the file system.
*
* @param fileName the file name
* @param b the data
* @param len the number of bytes
*/
synchronized void write(String fileName, byte[] b, int len) {
try {
long id = getId(fileName, false);
......
......@@ -82,7 +82,7 @@ public class FileSystemDisk extends FileSystem {
throw Message.getSQLException(ErrorCode.FILE_RENAME_FAILED_2, new String[]{oldName, newName});
}
void trace(String method, String fileName, Object o) {
private void trace(String method, String fileName, Object o) {
if (SysProperties.TRACE_IO) {
System.out.println("FileSystem." + method + " " + fileName + " " + o);
}
......
......@@ -140,6 +140,13 @@ public class Column {
return isComputed;
}
/**
* Compute the value of this computed column.
*
* @param session the session
* @param row the row
* @return the value
*/
Value computeValue(Session session, Row row) throws SQLException {
synchronized (this) {
computeTableFilter.setSession(session);
......@@ -159,6 +166,12 @@ public class Column {
this.defaultExpression = expression;
}
/**
* Set the table and column id.
*
* @param table the table
* @param columnId the column index
*/
void setTable(Table table, int columnId) {
this.table = table;
this.columnId = columnId;
......@@ -571,8 +584,15 @@ public class Column {
return DataType.getDataType(type);
}
String getCheckConstraintSQL(Session session, String name) throws SQLException {
Expression constraint = getCheckConstraint(session, name);
/**
* Get the check constraint SQL snippet.
*
* @param session the session
* @param asColumnName the column name to use
* @return the SQL snippet
*/
String getCheckConstraintSQL(Session session, String asColumnName) throws SQLException {
Expression constraint = getCheckConstraint(session, asColumnName);
return constraint == null ? "" : constraint.getSQL();
}
......@@ -588,6 +608,14 @@ public class Column {
this.primaryKey = primaryKey;
}
/**
* Visit the default expression, the check constraint, and the sequence (if
* any).
*
* @param visitor the visitor
* @return true if every visited expression returned true, or if there are
* no expressions
*/
boolean isEverything(ExpressionVisitor visitor) {
if (visitor.getType() == ExpressionVisitor.GET_DEPENDENCIES) {
if (sequence != null) {
......
......@@ -13,6 +13,7 @@ import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
/**
* A possible query execution plan. The time required to execute a query depends
......@@ -33,7 +34,7 @@ public class Plan {
*/
public Plan(TableFilter[] filters, int count, Expression condition) {
this.filters = new TableFilter[count];
System.arraycopy(filters, 0, this.filters, 0, count);
ObjectUtils.arrayCopy(filters, this.filters, count);
ObjectArray allCond = new ObjectArray();
ObjectArray all = new ObjectArray();
if (condition != null) {
......
......@@ -13,7 +13,12 @@ import org.h2.index.Index;
* using it.
*/
public class PlanItem {
/**
* The cost.
*/
double cost;
private Index index;
private PlanItem joinPlan;
......
......@@ -521,6 +521,9 @@ public class TableFilter implements ColumnResolver {
return buff.toString();
}
/**
* Remove all index conditions that are not used by the current index.
*/
void removeUnusableIndexConditions() {
for (int i = 0; i < indexConditions.size(); i++) {
IndexCondition cond = (IndexCondition) indexConditions.get(i);
......@@ -557,6 +560,11 @@ public class TableFilter implements ColumnResolver {
return used;
}
/**
* Set the session of this table filter.
*
* @param session
*/
void setSession(Session session) {
this.session = session;
}
......
......@@ -53,7 +53,7 @@ public class TableView extends Table {
initColumnsAndTables(session);
}
Query recompileQuery(Session session) throws SQLException {
private Query recompileQuery(Session session) throws SQLException {
Prepared p = session.prepare(querySQL);
if (!(p instanceof Query)) {
throw Message.getSyntaxError(querySQL, 0);
......@@ -310,7 +310,7 @@ public class TableView extends Table {
}
}
void setOwner(User owner) {
private void setOwner(User owner) {
this.owner = owner;
}
......
......@@ -152,6 +152,9 @@ ShutdownHandler {
stopAll();
}
/**
* Stop all servers that were started using the console.
*/
void stopAll() {
if (web != null && web.isRunning(false)) {
web.stop();
......
......@@ -298,12 +298,21 @@ public class RunScript extends Tool {
* @param fileName the script file
* @param charsetName the character set name or null for UTF-8
* @param continueOnError if execution should be continued if an error occurs
* @throws SQLException
*/
public static void execute(String url, String user, String password, String fileName, String charsetName, boolean continueOnError) throws SQLException {
new RunScript().process(url, user, password, fileName, charsetName, continueOnError);
}
/**
* Executes the SQL commands in a script file against a database.
*
* @param url the database URL
* @param user the user name
* @param password the password
* @param fileName the script file
* @param charsetName the character set name or null for UTF-8
* @param continueOnError if execution should be continued if an error occurs
*/
void process(String url, String user, String password, String fileName, String charsetName, boolean continueOnError) throws SQLException {
try {
org.h2.Driver.load();
......
......@@ -126,18 +126,25 @@ public class Script extends Tool {
}
/**
* Backs up a database to a file.
* Backs up a database to a SQL script file.
*
* @param url the database URL
* @param user the user name
* @param password the password
* @param fileName the script file
* @throws SQLException
*/
public static void execute(String url, String user, String password, String fileName) throws SQLException {
new Script().process(url, user, password, fileName);
}
/**
* Backs up a database to a SQL script file.
*
* @param url the database URL
* @param user the user name
* @param password the password
* @param fileName the script file
*/
void process(String url, String user, String password, String fileName) throws SQLException {
Connection conn = null;
Statement stat = null;
......
......@@ -64,9 +64,25 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData {
* This class holds the data of a result column.
*/
static class Column {
/**
* The column name.
*/
String name;
/**
* The SQL type.
*/
int sqlType;
/**
* The precision.
*/
int precision;
/**
* The scale.
*/
int scale;
}
......
......@@ -107,7 +107,6 @@ public class FileUtils {
* Try to delete a file.
*
* @param fileName the file name
* @return true if it could be deleted
*/
public static void tryDelete(String fileName) {
FileSystem.getInstance(fileName).tryDelete(fileName);
......
......@@ -184,9 +184,7 @@ public class ObjectArray {
* @param array the target array
*/
public void toArray(Object[] array) {
for (int i = 0; i < size; i++) {
array[i] = data[i];
}
ObjectUtils.arrayCopy(data, array, size);
}
/**
......
......@@ -21,6 +21,13 @@ import org.h2.message.Message;
*/
public class ObjectUtils {
/**
* The maximum number of elements to copy using a Java loop. This value was
* found by running tests using the Sun JDK 1.4 and JDK 1.6 on Windows XP.
* The biggest difference is for size smaller than 40 (more than 50% saving).
*/
private static final int MAX_JAVA_LOOP_COPY = 100;
private ObjectUtils() {
// utility class
}
......@@ -169,4 +176,23 @@ public class ObjectUtils {
}
}
/**
* Copy the elements of the source array to the target array.
* System.arraycopy is used for larger arrays, but for very small arrays it
* is faster to use a regular loop.
*
* @param source the source array
* @param target the target array
* @param size the number of elements to copy
*/
public static void arrayCopy(Object[] source, Object[] target, int size) {
if (size > MAX_JAVA_LOOP_COPY) {
System.arraycopy(source, 0, target, 0, size);
} else {
for (int i = 0; i < size; i++) {
target[i] = source[i];
}
}
}
}
......@@ -30,7 +30,15 @@ public class TempFileDeleter {
* Contains information about a file.
*/
static class TempFile {
/**
* The file name.
*/
String fileName;
/**
* The last modified date of this file.
*/
long lastModified;
}
......
......@@ -319,6 +319,14 @@ public abstract class Value {
return o1 > o2 ? t1 : t2;
}
/**
* Check if a value is in the cache that is equal to this value. If yes,
* this value should be used to save memory. If the value is not in the
* cache yet, it is added.
*
* @param v the value to look for
* @return the value in the cache or the value passed
*/
static Value cache(Value v) {
if (SysProperties.OBJECT_CACHE) {
Value[] cache = (Value[]) softCache.get();
......
......@@ -18,6 +18,10 @@ import org.h2.util.DateTimeUtils;
* Implementation of the DATE data type.
*/
public class ValueDate extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 8;
/**
......
......@@ -17,6 +17,10 @@ import org.h2.util.ObjectUtils;
* Implementation of the REAL data type.
*/
public class ValueFloat extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 7;
/**
......
......@@ -19,6 +19,9 @@ import org.h2.util.ObjectUtils;
*/
public class ValueShort extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 5;
/**
......
......@@ -16,6 +16,10 @@ import org.h2.util.DateTimeUtils;
* Implementation of the TIME data type.
*/
public class ValueTime extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 6;
/**
......
......@@ -21,6 +21,10 @@ import org.h2.util.MathUtils;
* Implementation of the TIMESTAMP data type.
*/
public class ValueTimestamp extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 23;
/**
......
......@@ -266,14 +266,12 @@ java org.h2.test.TestAll timer
/*
jazoon
run the performance tests as part of the unit test
measure and improve performance of ObjectArray.toArray()
jazoon
H2 Console should support Java Queries
convert test.in.sql to RunScript syntax
C:\download\Data Concurrency and Consistency.pdf
not tested:
......@@ -290,8 +288,6 @@ create an mbean for each database? server? (jconsole)
in help.csv, use complete examples for functions; add a test case
improve javadocs
option to write complete page right after checkpoint
test case for out of memory (try to corrupt the database using out of memory)
......
......@@ -365,6 +365,12 @@ public abstract class TestBase {
printlnWithTime(time, getClass().getName() + " " + s);
}
/**
* Print a message, prepended with the specified time in milliseconds.
*
* @param time the milliseconds
* @param s the message
*/
static void printlnWithTime(long time, String s) {
String t = "0000000000" + time;
t = t.substring(t.length() - 6);
......@@ -743,9 +749,10 @@ public abstract class TestBase {
* @param data the expected data
* @throws Exception if there is a mismatch
*/
void assertResultSetUnordered(ResultSet rs, String[][] data) throws Exception {
assertResultSet(false, rs, data);
}
// void assertResultSetUnordered(ResultSet rs, String[][] data)
// throws Exception {
// assertResultSet(false, rs, data);
// }
/**
* Check if a result set contains the expected data.
......@@ -755,7 +762,7 @@ public abstract class TestBase {
* @param data the expected data
* @throws Exception if there is a mismatch
*/
void assertResultSet(boolean ordered, ResultSet rs, String[][] data) throws Exception {
private void assertResultSet(boolean ordered, ResultSet rs, String[][] data) throws Exception {
int len = rs.getMetaData().getColumnCount();
int rows = data.length;
if (rows == 0) {
......@@ -815,7 +822,7 @@ public abstract class TestBase {
return true;
}
String[] getData(ResultSet rs, int len) throws SQLException {
private String[] getData(ResultSet rs, int len) throws SQLException {
String[] data = new String[len];
for (int i = 0; i < len; i++) {
data[i] = rs.getString(i + 1);
......@@ -825,7 +832,7 @@ public abstract class TestBase {
return data;
}
String formatRow(String[] row) {
private String formatRow(String[] row) {
String sb = "";
for (int i = 0; i < row.length; i++) {
sb += "{" + row[i] + "}";
......
......@@ -143,11 +143,11 @@ public class BenchC implements Bench {
trace("load done");
}
void trace(String s) {
private void trace(String s) {
action = s;
}
void trace(int i, int max) {
private void trace(int i, int max) {
db.trace(action, i, max);
}
......
......@@ -17,16 +17,39 @@ public class BenchCRandom {
private Random random = new Random(10);
/**
* Get a non-uniform random integer value between min and max.
*
* @param a the bit mask
* @param min the minimum value
* @param max the maximum value
* @return the random value
*/
int getNonUniform(int a, int min, int max) {
int c = 0;
return (((getInt(0, a) | getInt(min, max)) + c) % (max - min + 1))
+ min;
}
/**
* Get a random integer value between min and max.
*
* @param min the minimum value
* @param max the maximum value
* @return the random value
*/
int getInt(int min, int max) {
return max <= min ? min : (random.nextInt(max - min) + min);
}
/**
* Generate a boolean array with this many items set to true (randomly
* distributed).
*
* @param length the size of the array
* @param trueCount the number of true elements
* @return the boolean array
*/
boolean[] getBoolean(int length, int trueCount) {
boolean[] data = new boolean[length];
for (int i = 0, pos; i < trueCount; i++) {
......@@ -38,6 +61,13 @@ public class BenchCRandom {
return data;
}
/**
* Replace a random part of the string with another text.
*
* @param text the original text
* @param replacement the replacement
* @return the patched string
*/
String replace(String text, String replacement) {
int pos = getInt(0, text.length() - replacement.length());
StringBuffer buffer = new StringBuffer(text);
......@@ -45,6 +75,13 @@ public class BenchCRandom {
return buffer.toString();
}
/**
* Get a random number string.
*
* @param min the minimum value
* @param max the maximum value
* @return the number string
*/
String getNumberString(int min, int max) {
int len = getInt(min, max);
char[] buff = new char[len];
......@@ -54,6 +91,11 @@ public class BenchCRandom {
return new String(buff);
}
/**
* Get random address data.
*
* @return the address
*/
String[] getAddress() {
String str1 = getString(10, 20);
String str2 = getString(10, 20);
......@@ -63,10 +105,23 @@ public class BenchCRandom {
return new String[] { str1, str2, city, state, zip };
}
/**
* Get a random string.
*
* @param min the minimum size
* @param max the maximum size
* @return the string
*/
String getString(int min, int max) {
return getString(getInt(min, max));
}
/**
* Get a random string.
*
* @param len the size
* @return the string
*/
String getString(int len) {
char[] buff = new char[len];
for (int i = 0; i < len; i++) {
......@@ -75,6 +130,12 @@ public class BenchCRandom {
return new String(buff);
}
/**
* Generate a random permutation if the values 0 .. length.
*
* @param length the number of elements
* @return the random permutation
*/
int[] getPermutation(int length) {
int[] data = new int[length];
for (int i = 0; i < length; i++) {
......@@ -89,11 +150,23 @@ public class BenchCRandom {
return data;
}
/**
* Create a big decimal value.
*
* @param value the value
* @param scale the scale
* @return the big decimal object
*/
BigDecimal getBigDecimal(int value, int scale) {
return new BigDecimal(new BigInteger(String.valueOf(value)), scale);
}
/**
* Generate a last name composed of three elements
*
* @param i the last name index
* @return the name
*/
String getLastname(int i) {
String[] n = { "BAR", "OUGHT", "ABLE", "PRI", "PRES", "ESE", "ANTI",
"CALLY", "ATION", "EING" };
......
......@@ -39,6 +39,9 @@ public class BenchCThread {
warehouseId = random.getInt(1, bench.warehouses);
}
/**
* Process the list of operations (a 'deck') in random order.
*/
void process() throws Exception {
int[] deck = new int[] { OP_NEW_ORDER, OP_NEW_ORDER, OP_NEW_ORDER,
OP_NEW_ORDER, OP_NEW_ORDER, OP_NEW_ORDER, OP_NEW_ORDER,
......
......@@ -13,6 +13,7 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
......@@ -49,22 +50,45 @@ class Database {
private Object serverDerby;
private boolean serverHSQLDB;
/**
* Get the database name.
*
* @return the database name
*/
String getName() {
return name;
}
/**
* Get the total measured time.
*
* @return the time
*/
int getTotalTime() {
return totalTime;
}
/**
* Get the result array.
*
* @return the result array
*/
ArrayList getResults() {
return results;
}
/**
* Get the random number generator.
*
* @return the generator
*/
Random getRandom() {
return random;
}
/**
* Start the server if the this is a remote connection.
*/
void startServer() throws Exception {
if (url.startsWith("jdbc:h2:tcp:")) {
serverH2 = Server.createTcpServer(new String[0]).start();
......@@ -90,6 +114,9 @@ class Database {
}
}
/**
* Stop the server if this is a remote connection.
*/
void stopServer() throws Exception {
if (serverH2 != null) {
serverH2.stop();
......@@ -110,6 +137,14 @@ class Database {
}
}
/**
* Parse a database configuration and create a database object from it.
*
* @param test the test application
* @param id the database id
* @param dbString the configuration string
* @return a new database object with the given settings
*/
static Database parse(TestPerformance test, int id, String dbString) {
try {
StringTokenizer tokenizer = new StringTokenizer(dbString, ",");
......@@ -133,8 +168,20 @@ class Database {
}
}
Connection getConnection() throws Exception {
Connection conn = DriverManager.getConnection(url, user, password);
/**
* Get the database connection. The connection must be opened first.
*
* @return the connection
*/
Connection getConnection() {
return conn;
}
/**
* Open a database connection.
*/
void openConnection() throws SQLException {
conn = DriverManager.getConnection(url, user, password);
if (url.startsWith("jdbc:derby:")) {
// Derby: use higher cache size
Statement stat = null;
......@@ -159,15 +206,13 @@ class Database {
JdbcUtils.closeSilently(stat);
}
}
return conn;
}
void openConnection() throws Exception {
conn = DriverManager.getConnection(url, user, password);
stat = conn.createStatement();
}
void closeConnection() throws Exception {
/**
* Close the database connection.
*/
void closeConnection() throws SQLException {
// if(!serverHSQLDB && url.startsWith("jdbc:hsqldb:")) {
// stat.execute("SHUTDOWN");
// }
......@@ -176,7 +221,12 @@ class Database {
conn = null;
}
public void setTranslations(Properties prop) {
/**
* Initialize the SQL statement translation of this database.
*
* @param prop the properties with the translations to use
*/
void setTranslations(Properties prop) {
String id = url.substring("jdbc:".length());
id = id.substring(0, id.indexOf(':'));
for (Iterator it = prop.keySet().iterator(); it.hasNext();) {
......@@ -191,12 +241,18 @@ class Database {
}
}
PreparedStatement prepare(String sql) throws Exception {
/**
* Prepare a SQL statement.
*
* @param sql the SQL statement
* @return the prepared statement
*/
PreparedStatement prepare(String sql) throws SQLException {
sql = getSQL(sql);
return conn.prepareStatement(sql);
}
public String getSQL(String sql) {
private String getSQL(String sql) {
for (int i = 0; i < replace.size(); i++) {
String[] pair = (String[]) replace.get(i);
String pattern = pair[0];
......@@ -206,11 +262,21 @@ class Database {
return sql;
}
/**
* Start the benchmark.
*
* @param bench the benchmark
* @param action the action
*/
void start(Bench bench, String action) {
this.action = bench.getName() + ": " + action;
this.startTime = System.currentTimeMillis();
}
/**
* This method is called when the test run ends. This will stop collecting
* data.
*/
void end() {
long time = System.currentTimeMillis() - startTime;
log(action, "ms", (int) time);
......@@ -219,6 +285,11 @@ class Database {
}
}
/**
* Drop a table. Errors are ignored.
*
* @param table the table name
*/
void dropTable(String table) {
try {
update("DROP TABLE " + table);
......@@ -227,13 +298,24 @@ class Database {
}
}
public void update(PreparedStatement prep, String trace) throws Exception {
/**
* Execute an SQL statement.
*
* @param prep the prepared statement
* @param trace the trace message
*/
void update(PreparedStatement prep, String trace) throws SQLException {
test.trace(trace);
prep.executeUpdate();
executedStatements++;
}
public void update(String sql) throws Exception {
/**
* Execute an SQL statement.
*
* @param sql the SQL statement
*/
void update(String sql) throws SQLException {
sql = getSQL(sql);
if (sql.trim().length() > 0) {
executedStatements++;
......@@ -243,18 +325,36 @@ class Database {
}
}
public void setAutoCommit(boolean b) throws Exception {
/**
* Enable or disable auto-commit.
*
* @param b false to disable
*/
void setAutoCommit(boolean b) throws SQLException {
conn.setAutoCommit(b);
}
public void commit() throws Exception {
/**
* Commit a transaction.
*/
void commit() throws SQLException {
conn.commit();
}
public void rollback() throws Exception {
/**
* Roll a transaction back.
*/
void rollback() throws SQLException {
conn.rollback();
}
/**
* Print trace information if trace is enabled.
*
* @param action the action
* @param i the current value
* @param max the maximum value
*/
void trace(String action, int i, int max) {
if (trace) {
long time = System.currentTimeMillis();
......@@ -267,17 +367,37 @@ class Database {
}
}
/**
* If data collection is enabled, add the currently used memory size to the
* log.
*
* @param bench the benchmark
* @param action the action
*/
void logMemory(Bench bench, String action) {
log(bench.getName() + ": " + action, "MB", TestBase.getMemoryUsed());
}
/**
* If data collection is enabled, add this information to the log.
*
* @param action the action
* @param scale the scale
* @param value the value
*/
void log(String action, String scale, int value) {
if (test.collect) {
results.add(new Object[] { action, scale, new Integer(value) });
}
}
public ResultSet query(PreparedStatement prep) throws Exception {
/**
* Execute a query.
*
* @param prep the prepared statement
* @return the result set
*/
ResultSet query(PreparedStatement prep) throws SQLException {
// long time = System.currentTimeMillis();
ResultSet rs = prep.executeQuery();
// time = System.currentTimeMillis() - time;
......@@ -288,7 +408,12 @@ class Database {
return rs;
}
public void queryReadResult(PreparedStatement prep) throws Exception {
/**
* Execute a query and read all rows.
*
* @param prep the prepared statement
*/
void queryReadResult(PreparedStatement prep) throws SQLException {
ResultSet rs = prep.executeQuery();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
......@@ -299,16 +424,22 @@ class Database {
}
}
/**
* Get the number of executed statements.
*
* @return the number of statements
*/
int getExecutedStatements() {
return executedStatements;
}
public int getId() {
/**
* Get the database id.
*
* @return the id
*/
int getId() {
return id;
}
public Connection getCurrentConnection() {
return conn;
}
}
......@@ -27,7 +27,14 @@ import org.h2.util.JdbcUtils;
*/
public class TestPerformance {
/**
* Whether data should be collected.
*/
boolean collect;
/**
* The flag used to enable or disable trace messages.
*/
boolean trace;
/**
......@@ -238,6 +245,11 @@ public class TestPerformance {
bench.runTest();
}
/**
* Print a message to system out if trace is enabled.
*
* @param s the message
*/
void trace(String s) {
if (trace) {
System.out.println(s);
......
......@@ -15,7 +15,11 @@ import java.io.Reader;
*/
public class Tokenizer {
/**
* This token type means no more tokens are available.
*/
static final int TYPE_EOF = -1;
private static final int TYPE_WORD = -2;
private static final int TYPE_NOTHING = -3;
private static final byte WHITESPACE = 1;
......@@ -78,6 +82,9 @@ public class Tokenizer {
return i;
}
/**
* Initialize the tokenizer.
*/
void initToken() {
buffer = new StringBuffer();
}
......@@ -91,6 +98,11 @@ public class Tokenizer {
buffer.append((char) i);
}
/**
* Read the next token and get the token type.
*
* @return the token type
*/
int nextToken() throws IOException {
byte[] ct = charTypes;
int c;
......
......@@ -179,8 +179,14 @@ public class Db {
}
}
static Error convert(Exception e) {
return new Error("Error: " + e.toString(), e);
/**
* Convert a checked exception to a runtime exception.
*
* @param e the checked exception
* @return the runtime exception
*/
static RuntimeException convert(Exception e) {
return new RuntimeException(e.toString(), e);
}
public void setAutoCommit(boolean autoCommit) {
......
......@@ -66,7 +66,12 @@ public class TestDeadlock extends TestBase {
* that execute a statement.
*/
abstract class DoIt extends Thread {
/**
* The operation to execute.
*/
abstract void execute() throws SQLException;
public void run() {
try {
execute();
......
......@@ -21,6 +21,12 @@ public class WebClient {
private String sessionId;
/**
* Open an URL and get the HTML data.
*
* @param url the HTTP URL
* @return the HTML as a string
*/
String get(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
......@@ -36,10 +42,15 @@ public class WebClient {
return result;
}
void readSessionId(String result) {
int idx = result.indexOf("jsessionid=");
String id = result.substring(idx + "jsessionid=".length());
for (int i = 0; i < result.length(); i++) {
/**
* Read the session ID from a URL.
*
* @param url the URL
*/
void readSessionId(String url) {
int idx = url.indexOf("jsessionid=");
String id = url.substring(idx + "jsessionid=".length());
for (int i = 0; i < url.length(); i++) {
char ch = id.charAt(i);
if (!Character.isLetterOrDigit(ch)) {
id = id.substring(0, i);
......@@ -49,6 +60,13 @@ public class WebClient {
this.sessionId = id;
}
/**
* Read the specified HTML page.
*
* @param url the base URL
* @param page the page to read
* @return the HTML page
*/
String get(String url, String page) throws IOException {
if (sessionId != null) {
if (page.indexOf('?') < 0) {
......
......@@ -23,6 +23,12 @@ class OutputCatcher extends Thread {
this.in = in;
}
/**
* Read a line from the output.
*
* @param wait the maximum number of milliseconds to wait
* @return the line
*/
String readLine(long wait) {
long start = System.currentTimeMillis();
while (true) {
......
......@@ -13,7 +13,6 @@ import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
......@@ -97,14 +96,30 @@ public abstract class TestHalt extends TestBase {
private int errorId;
private int sequenceId;
/**
* Initialize the test.
*/
abstract void testInit() throws Exception;
/**
* Check if the database is consistent after a simulated database crash.
*/
abstract void testCheckAfterCrash() throws Exception;
/**
* Wait for some time after the application has been started.
*/
abstract void testWaitAfterAppStart() throws Exception;
/**
* Start the application.
*/
abstract void appStart() throws Exception;
/**
* Run the application.
* @throws Exception
*/
abstract void appRun() throws Exception;
public void test() throws Exception {
......@@ -272,53 +287,55 @@ public abstract class TestHalt extends TestBase {
}
}
public Connection getConnectionHSQLDB() throws Exception {
File lock = new File("test.lck");
while (lock.exists()) {
lock.delete();
System.gc();
}
Class.forName("org.hsqldb.jdbcDriver");
return DriverManager.getConnection("jdbc:hsqldb:test", "sa", "");
}
public Connection getConnectionDerby() throws Exception {
File lock = new File("test3/db.lck");
while (lock.exists()) {
lock.delete();
System.gc();
}
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
try {
return DriverManager.getConnection("jdbc:derby:test3;create=true", "sa", "sa");
} catch (SQLException e) {
Exception e2 = e;
do {
e.printStackTrace();
e = e.getNextException();
} while (e != null);
throw e2;
}
}
void disconnectHSQLDB() {
try {
conn.createStatement().execute("SHUTDOWN");
} catch (Exception e) {
// ignore
}
// super.disconnect();
}
void disconnectDerby() {
// super.disconnect();
try {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
DriverManager.getConnection("jdbc:derby:;shutdown=true", "sa", "sa");
} catch (Exception e) {
// ignore
}
}
// public Connection getConnectionHSQLDB() throws Exception {
// File lock = new File("test.lck");
// while (lock.exists()) {
// lock.delete();
// System.gc();
// }
// Class.forName("org.hsqldb.jdbcDriver");
// return DriverManager.getConnection("jdbc:hsqldb:test", "sa", "");
// }
// public Connection getConnectionDerby() throws Exception {
// File lock = new File("test3/db.lck");
// while (lock.exists()) {
// lock.delete();
// System.gc();
// }
// Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
// try {
// return DriverManager.getConnection(
// "jdbc:derby:test3;create=true", "sa", "sa");
// } catch (SQLException e) {
// Exception e2 = e;
// do {
// e.printStackTrace();
// e = e.getNextException();
// } while (e != null);
// throw e2;
// }
// }
// void disconnectHSQLDB() {
// try {
// conn.createStatement().execute("SHUTDOWN");
// } catch (Exception e) {
// // ignore
// }
// // super.disconnect();
// }
// void disconnectDerby() {
// // super.disconnect();
// try {
// Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
// DriverManager.getConnection(
// "jdbc:derby:;shutdown=true", "sa", "sa");
// } catch (Exception e) {
// // ignore
// }
// }
/**
* Create a random string with the specified length.
......
......@@ -30,7 +30,7 @@ class Column {
private boolean isPrimaryKey;
// TODO test isAutoincrement;
Column(TestSynth config) {
private Column(TestSynth config) {
this.config = config;
}
......@@ -68,7 +68,14 @@ class Column {
}
}
public static boolean isConditionType(TestSynth config, int type) {
/**
* Check if this data type supports comparisons for this database.
*
* @param config the configuration
* @param type the SQL type
* @return true if the value can be used in conditions
*/
static boolean isConditionType(TestSynth config, int type) {
switch (config.getMode()) {
case TestSynth.H2:
case TestSynth.H2_MEM:
......@@ -103,7 +110,7 @@ class Column {
}
}
String getTypeName() {
private String getTypeName() {
switch (type) {
case Types.INTEGER:
return "INT";
......@@ -152,7 +159,7 @@ class Column {
}
}
public String getCreateSQL() {
String getCreateSQL() {
String sql = name + " " + getTypeName();
if (!isNullable) {
sql += " NOT NULL";
......@@ -160,19 +167,25 @@ class Column {
return sql;
}
public String getName() {
String getName() {
return name;
}
public Value getRandomValue() {
Value getRandomValue() {
return Value.getRandom(config, type, precision, scale, isNullable);
}
public Value getRandomValueNotNull() {
return Value.getRandom(config, type, precision, scale, false);
}
// Value getRandomValueNotNull() {
// return Value.getRandom(config, type, precision, scale, false);
// }
public static Column getRandomColumn(TestSynth config) {
/**
* Generate a random column.
*
* @param config the configuration
* @return the column
*/
static Column getRandomColumn(TestSynth config) {
Column column = new Column(config);
column.name = "C_" + config.randomIdentifier();
int randomType;
......@@ -191,19 +204,24 @@ class Column {
return column;
}
public boolean isPrimaryKey() {
boolean getPrimaryKey() {
return isPrimaryKey;
}
public void setPrimaryKey(boolean b) {
void setPrimaryKey(boolean b) {
isPrimaryKey = b;
}
public void setNullable(boolean b) {
void setNullable(boolean b) {
isNullable = b;
}
public int getType() {
/**
* The the column type.
*
* @return the type
*/
int getType() {
return type;
}
......
......@@ -13,8 +13,8 @@ import java.util.ArrayList;
*/
public class DbState implements DbInterface {
boolean connected;
boolean autoCommit;
private boolean connected;
private boolean autoCommit;
private TestSynth config;
private ArrayList tables = new ArrayList();
private ArrayList indexes = new ArrayList();
......@@ -80,6 +80,11 @@ public class DbState implements DbInterface {
// nothing to do
}
/**
* Get a random table.
*
* @return the table
*/
Table randomTable() {
if (tables.size() == 0) {
return null;
......@@ -92,4 +97,8 @@ public class DbState implements DbInterface {
// nothing to do
}
public String toString() {
return "autocommit: " + autoCommit + " connected: " + connected;
}
}
......@@ -13,19 +13,24 @@ import java.util.ArrayList;
* Represents an expression.
*/
public class Expression {
boolean isCondition;
private String sql;
private TestSynth config;
private Command command;
private Expression(TestSynth config, Command command, boolean isCondition) {
private Expression(TestSynth config, Command command) {
this.config = config;
this.isCondition = isCondition;
this.command = command;
sql = "";
}
/**
* Create a random select list.
*
* @param config the configuration
* @param command the command
* @return the select list
*/
static String[] getRandomSelectList(TestSynth config, Command command) {
if (config.random().getBoolean(30)) {
return new String[] { "*" };
......@@ -47,16 +52,23 @@ public class Expression {
return list;
}
/**
* Generate a random condition.
*
* @param config the configuration
* @param command the command
* @return the random condition expression
*/
static Expression getRandomCondition(TestSynth config, Command command) {
Expression condition = new Expression(config, command, true);
Expression condition = new Expression(config, command);
if (config.random().getBoolean(50)) {
condition.create();
}
return condition;
}
static Expression getRandomExpression(TestSynth config, Command command) {
Expression expression = new Expression(config, command, false);
private static Expression getRandomExpression(TestSynth config, Command command) {
Expression expression = new Expression(config, command);
String alias = command.getRandomTableAlias();
Column column = command.getTable(alias).getRandomConditionColumn();
if (column == null) {
......@@ -72,12 +84,27 @@ public class Expression {
sql = v.getSQL();
}
/**
* Generate a random join condition.
*
* @param config the configuration
* @param command the command
* @param alias the alias name
* @return the join condition
*/
static Expression getRandomJoinOn(TestSynth config, Command command, String alias) {
Expression expression = new Expression(config, command, true);
Expression expression = new Expression(config, command);
expression.createJoinComparison(alias);
return expression;
}
/**
* Generate a random sort order list.
*
* @param config the configuration
* @param command the command
* @return the ORDER BY list
*/
static String getRandomOrder(TestSynth config, Command command) {
int len = config.random().getLog(6);
String sql = "";
......@@ -104,6 +131,11 @@ public class Expression {
return sql;
}
/**
* Get the SQL snippet of this expression.
*
* @return the SQL snippet
*/
String getSQL() {
return sql.trim().length() == 0 ? null : sql.trim();
}
......@@ -250,11 +282,7 @@ public class Expression {
}
}
boolean isEmpty() {
return sql == null || sql.trim().length() == 0;
}
void createExpression(String alias, Column type) {
private void createExpression(String alias, Column type) {
boolean op = is(20);
// no null values if there is an operation
boolean allowNull = !op;
......@@ -288,7 +316,7 @@ public class Expression {
}
}
void createTerm(String alias, Column type, boolean allowNull) {
private void createTerm(String alias, Column type, boolean allowNull) {
int dt = type.getType();
if (is(5) && (dt == Types.INTEGER) || (dt == Types.DECIMAL)) {
sql += " - ";
......
......@@ -10,10 +10,10 @@ package org.h2.test.synth.sql;
* Represents an index.
*/
public class Index {
Table table;
String name;
Column[] columns;
boolean unique;
private Table table;
private String name;
private Column[] columns;
private boolean unique;
Index(Table table, String name, Column[] columns, boolean unique) {
this.table = table;
......@@ -22,11 +22,11 @@ public class Index {
this.unique = unique;
}
public String getName() {
String getName() {
return name;
}
public String getCreateSQL() {
String getCreateSQL() {
String sql = "CREATE ";
if (unique) {
sql += "UNIQUE ";
......@@ -42,11 +42,11 @@ public class Index {
return sql;
}
public String getDropSQL() {
String getDropSQL() {
return "DROP INDEX " + name;
}
public Table getTable() {
Table getTable() {
return table;
}
......
......@@ -128,27 +128,27 @@ class Result implements Comparable {
}
}
public void log() {
switch (type) {
case SUCCESS:
System.out.println("> ok");
break;
case EXCEPTION:
System.out.println("> exception");
break;
case INT:
if (intValue == 0) {
System.out.println("> ok");
} else {
System.out.println("> update count: " + intValue);
}
break;
case RESULT_SET:
System.out.println("> rs " + rows.size());
break;
default:
}
System.out.println();
}
// public void log() {
// switch (type) {
// case SUCCESS:
// System.out.println("> ok");
// break;
// case EXCEPTION:
// System.out.println("> exception");
// break;
// case INT:
// if (intValue == 0) {
// System.out.println("> ok");
// } else {
// System.out.println("> update count: " + intValue);
// }
// break;
// case RESULT_SET:
// System.out.println("> rs " + rows.size());
// break;
// default:
// }
// System.out.println();
// }
}
......@@ -24,7 +24,13 @@ class Table {
this.config = config;
}
public static Table newRandomTable(TestSynth config) {
/**
* Create a new random table.
*
* @param config the configuration
* @return the table
*/
static Table newRandomTable(TestSynth config) {
Table table = new Table(config);
table.name = "T_" + config.randomIdentifier();
......@@ -52,7 +58,7 @@ class Table {
Column pk = null;
do {
pk = table.columns[config.random().getInt(len)];
} while (pk.isPrimaryKey());
} while (pk.getPrimaryKey());
table.primaryKeys[i] = pk;
pk.setPrimaryKey(true);
pk.setNullable(false);
......@@ -61,7 +67,12 @@ class Table {
return table;
}
public Index newRandomIndex() {
/**
* Create a new random index.
*
* @return the index
*/
Index newRandomIndex() {
String indexName = "I_" + config.randomIdentifier();
int len = config.random().getLog(getColumnCount() - 1) + 1;
boolean unique = config.random().getBoolean(50);
......@@ -70,11 +81,21 @@ class Table {
return index;
}
public String getDropSQL() {
/**
* Get the DROP TABLE statement for this table.
*
* @return the SQL statement
*/
String getDropSQL() {
return "DROP TABLE " + name;
}
public String getCreateSQL() {
/**
* Get the CREATE TABLE statement for this table.
*
* @return the SQL statement
*/
String getCreateSQL() {
String sql = "CREATE ";
if (temporary) {
if (globalTemporary) {
......@@ -111,7 +132,12 @@ class Table {
return sql;
}
public String getInsertSQL(Column[] c, Value[] v) {
/**
* Get the INSERT statement for this table.
*
* @return the SQL statement
*/
String getInsertSQL(Column[] c, Value[] v) {
String sql = "INSERT INTO " + name;
if (c != null) {
sql += "(";
......@@ -134,11 +160,21 @@ class Table {
return sql;
}
public String getName() {
/**
* Get the table name.
*
* @return the name
*/
String getName() {
return name;
}
public Column getRandomConditionColumn() {
/**
* Get a random column that can be used in a condition.
*
* @return the column
*/
Column getRandomConditionColumn() {
ArrayList list = new ArrayList();
for (int i = 0; i < columns.length; i++) {
if (Column.isConditionType(config, columns[i].getType())) {
......@@ -151,15 +187,21 @@ class Table {
return (Column) list.get(config.random().getInt(list.size()));
}
public Column getRandomColumn() {
Column getRandomColumn() {
return columns[config.random().getInt(columns.length)];
}
public int getColumnCount() {
int getColumnCount() {
return columns.length;
}
public Column getRandomColumnOfType(int type) {
/**
* Get a random column of the specified type.
*
* @param type the type
* @return the column or null if no such column was found
*/
Column getRandomColumnOfType(int type) {
ArrayList list = new ArrayList();
for (int i = 0; i < columns.length; i++) {
if (columns[i].getType() == type) {
......@@ -172,7 +214,13 @@ class Table {
return (Column) list.get(config.random().getInt(list.size()));
}
public Column[] getRandomColumns(int len) {
/**
* Get a number of random column from this table.
*
* @param len the column count
* @return the columns
*/
Column[] getRandomColumns(int len) {
int[] index = new int[columns.length];
for (int i = 0; i < columns.length; i++) {
index[i] = i;
......@@ -190,15 +238,25 @@ class Table {
return c;
}
public Column[] getColumns() {
Column[] getColumns() {
return columns;
}
public void addIndex(Index index) {
/**
* Add this index to the table.
*
* @param index the index to add
*/
void addIndex(Index index) {
indexes.add(index);
}
public void removeIndex(Index index) {
/**
* Remove an index from the table.
*
* @param index the index to remove
*/
void removeIndex(Index index) {
indexes.remove(index);
}
}
......@@ -21,7 +21,30 @@ public class TestSynth extends TestBase {
// TODO hsqldb: call 1||null should return 1 but returns null
// TODO hsqldb: call mod(1) should return invalid parameter count
static final int H2 = 0, H2_MEM = 1, HSQLDB = 2, MYSQL = 3, POSTGRESQL = 4;
/**
* A H2 database connection.
*/
static final int H2 = 0;
/**
* An in-memory H2 database connection.
*/
static final int H2_MEM = 1;
/**
* An HSQLDB database connection.
*/
static final int HSQLDB = 2;
/**
* A MySQL database connection.
*/
static final int MYSQL = 3;
/**
* A PostgreSQL database connection.
*/
static final int POSTGRESQL = 4;
private static final String DIR = "synth";
private DbState db = new DbState(this);
......@@ -32,14 +55,30 @@ public class TestSynth extends TestBase {
private boolean stopImmediately;
private int mode;
/**
* Check whether this database is of the specified type.
*
* @param isType the database type
* @return true if it is
*/
boolean is(int isType) {
return mode == isType;
}
/**
* Get the random number generator.
*
* @return the random number generator
*/
RandomGen random() {
return random;
}
/**
* Get a random identifier.
*
* @return the random identifier
*/
String randomIdentifier() {
int len = random.getLog(8) + 2;
while (true) {
......@@ -193,17 +232,28 @@ public class TestSynth extends TestBase {
}
}
/**
* Get a random table.
*
* @return the table
*/
Table randomTable() {
return db.randomTable();
}
/**
* Print this message if the log is enabled.
*
* @param id the id
* @param s the message
*/
void log(int id, String s) {
if (showLog && id == 0) {
System.out.println(s);
}
}
public int getMode() {
int getMode() {
return mode;
}
......
......@@ -31,6 +31,11 @@ public class Value {
this.data = data;
}
/**
* Convert the value to a SQL string.
*
* @return the SQL string
*/
String getSQL() {
if (data == null) {
return "NULL";
......@@ -127,6 +132,14 @@ public class Value {
return buff.toString();
}
/**
* Read a value from a result set.
*
* @param config the configuration
* @param rs the result set
* @param index the column index
* @return the value
*/
static Value read(TestSynth config, ResultSet rs, int index) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
Object data;
......@@ -184,6 +197,16 @@ public class Value {
return new Value(config, type, data);
}
/**
* Generate a random value.
*
* @param config the configuration
* @param type the value type
* @param precision the precision
* @param scale the scale
* @param mayBeNull if the value may be null or not
* @return the value
*/
static Value getRandom(TestSynth config, int type, int precision, int scale, boolean mayBeNull) {
Object data;
if (mayBeNull && config.random().getBoolean(20)) {
......@@ -263,48 +286,49 @@ public class Value {
return new BigDecimal(buff.toString());
}
int compareTo(Object o) {
Value v = (Value) o;
if (type != v.type) {
throw new Error("compare " + type + " " + v.type + " " + data + " " + v.data);
}
if (data == null) {
return (v.data == null) ? 0 : -1;
} else if (v.data == null) {
return 1;
}
switch (type) {
case Types.DECIMAL:
return ((BigDecimal) data).compareTo((BigDecimal) v.data);
case Types.BLOB:
case Types.VARBINARY:
case Types.BINARY:
return compareBytes((byte[]) data, (byte[]) v.data);
case Types.CLOB:
case Types.VARCHAR:
return data.toString().compareTo(v.data.toString());
case Types.DATE:
return ((Date) data).compareTo((Date) v.data);
case Types.INTEGER:
return ((Integer) data).compareTo((Integer) v.data);
default:
throw new Error("type=" + type);
}
}
// private int compareTo(Object o) {
// Value v = (Value) o;
// if (type != v.type) {
// throw new Error("compare " + type +
// " " + v.type + " " + data + " " + v.data);
// }
// if (data == null) {
// return (v.data == null) ? 0 : -1;
// } else if (v.data == null) {
// return 1;
// }
// switch (type) {
// case Types.DECIMAL:
// return ((BigDecimal) data).compareTo((BigDecimal) v.data);
// case Types.BLOB:
// case Types.VARBINARY:
// case Types.BINARY:
// return compareBytes((byte[]) data, (byte[]) v.data);
// case Types.CLOB:
// case Types.VARCHAR:
// return data.toString().compareTo(v.data.toString());
// case Types.DATE:
// return ((Date) data).compareTo((Date) v.data);
// case Types.INTEGER:
// return ((Integer) data).compareTo((Integer) v.data);
// default:
// throw new Error("type=" + type);
// }
// }
static int compareBytes(byte[] a, byte[] b) {
int al = a.length, bl = b.length;
int len = Math.min(al, bl);
for (int i = 0; i < len; i++) {
int x = a[i] & 0xff;
int y = b[i] & 0xff;
if (x == y) {
continue;
}
return x > y ? 1 : -1;
}
return al == bl ? 0 : al > bl ? 1 : -1;
}
// private static int compareBytes(byte[] a, byte[] b) {
// int al = a.length, bl = b.length;
// int len = Math.min(al, bl);
// for (int i = 0; i < len; i++) {
// int x = a[i] & 0xff;
// int y = b[i] & 0xff;
// if (x == y) {
// continue;
// }
// return x > y ? 1 : -1;
// }
// return al == bl ? 0 : al > bl ? 1 : -1;
// }
public String toString() {
return getSQL();
......
......@@ -16,25 +16,51 @@ import org.h2.test.TestBase;
*/
abstract class TestMultiThread extends Thread {
/**
* The base object.
*/
TestMulti base;
/**
* The random number generator.
*/
Random random = new Random();
TestMultiThread(TestMulti base) {
this.base = base;
}
/**
* Execute statements that need to be executed before starting the thread.
* This includes CREATE TABLE statements.
*/
abstract void first() throws SQLException;
/**
* The main operation to perform. This method is called in a loop.
*/
abstract void operation() throws SQLException;
/**
* Execute statements before entering the loop, but after starting the
* thread.
*/
abstract void begin() throws SQLException;
/**
* This method is called once after the test is stopped.
*/
abstract void end() throws SQLException;
/**
* This method is called once after all threads have been stopped.
* @throws Exception
*/
abstract void finalTest() throws Exception;
public void run() {
try {
begin();
while (!base.stop) {
operation();
}
......
......@@ -49,6 +49,9 @@ class Arg {
return quote(clazz, getValue());
}
/**
* Calculate the value if this is a statement.
*/
void execute() throws Exception {
if (stat != null) {
obj = stat.execute();
......@@ -65,7 +68,7 @@ class Arg {
return obj;
}
String quote(Class clazz, Object value) {
private String quote(Class clazz, Object value) {
if (value == null) {
return null;
} else if (clazz == String.class) {
......
......@@ -45,6 +45,13 @@ class Parser {
read();
}
/**
* Parse a Java statement.
*
* @param player the player
* @param line the statement text
* @return the statement
*/
static Statement parseStatement(Player player, String line) {
Parser p = new Parser(player, line);
p.parseStatement();
......
......@@ -111,6 +111,11 @@ public class Player {
}
}
/**
* Write trace information if trace is enabled.
*
* @param s the message to write
*/
void trace(String s) {
if (trace) {
System.out.println(s);
......@@ -132,6 +137,13 @@ public class Player {
}
}
/**
* Get the class for the given class name.
* Only a limited set of classes is supported.
*
* @param className the class name
* @return the class
*/
static Class getClass(String className) {
for (int i = 0; i < IMPORTED_PACKAGES.length; i++) {
try {
......@@ -143,10 +155,22 @@ public class Player {
throw new Error("Class not found: " + className);
}
void assign(String objectName, Object obj) {
objects.put(objectName, obj);
/**
* Assign an object to a variable.
*
* @param variableName the variable name
* @param obj the object
*/
void assign(String variableName, Object obj) {
objects.put(variableName, obj);
}
/**
* Get an object.
*
* @param name the variable name
* @return the object
*/
Object getObject(String name) {
return objects.get(name);
}
......
......@@ -45,6 +45,11 @@ class Statement {
this.player = player;
}
/**
* Execute the statement.
*
* @return the object returned if this was a method call
*/
Object execute() throws Exception {
if (object == player) {
// there was an exception previously
......@@ -120,19 +125,38 @@ class Statement {
return returnClass;
}
/**
* This statement is an assignment.
*
* @param className the class of the variable
* @param variableName the variable name
*/
void setAssign(String className, String variableName) {
this.assignment = true;
this.assignClass = className;
this.assignVariable = variableName;
}
/**
* This statement is a static method call.
*
* @param className the class name
*/
void setStaticCall(String className) {
this.staticCall = true;
this.staticCallClass = className;
}
void setMethodCall(String objectName, Object object, String methodName) {
this.objectName = objectName;
/**
* This statement is a method call, and the result is assigned to a
* variable.
*
* @param variableName the variable name
* @param object the object
* @param methodName the method name
*/
void setMethodCall(String variableName, Object object, String methodName) {
this.objectName = variableName;
this.object = object;
this.methodName = methodName;
}
......
......@@ -39,6 +39,12 @@ public class FtpClient {
// don't allow construction
}
/**
* Open an FTP connection.
*
* @param url the FTP URL
* @return the ftp client object
*/
static FtpClient open(String url) throws IOException {
FtpClient client = new FtpClient();
client.connect(url);
......@@ -77,6 +83,12 @@ public class FtpClient {
writer.flush();
}
/**
* Login to this FTP server (USER, PASS, SYST, SITE, STRU F, TYPE I).
*
* @param userName the user name
* @param password the password
*/
void login(String userName, String password) throws IOException {
send("USER " + userName);
readCode(331);
......@@ -92,6 +104,9 @@ public class FtpClient {
readCode(200);
}
/**
* Close the connection (QUIT).
*/
void close() throws IOException {
if (socket != null) {
send("QUIT");
......@@ -100,42 +115,75 @@ public class FtpClient {
}
}
/**
* Change the working directory (CWD).
*
* @param dir the new directory
*/
void changeWorkingDirectory(String dir) throws IOException {
send("CWD " + dir);
readCode(250);
}
/**
* Change to the parent directory (CDUP).
*/
void changeDirectoryUp() throws IOException {
send("CDUP");
readCode(250);
}
/**
* Delete a file (DELE).
*
* @param fileName the name of the file to delete
*/
void delete(String fileName) throws IOException {
send("DELE " + fileName);
readCode(250);
}
/**
* Create a directory (MKD).
*
* @param dir the directory to create
*/
void makeDirectory(String dir) throws IOException {
send("MKD " + dir);
readCode(257);
}
/**
* Change the transfer mode (MODE).
*
* @param mode the mode
*/
void mode(String mode) throws IOException {
send("MODE " + mode);
readCode(200);
}
/**
* Change the modified time of a file (MDTM).
*
* @param fileName the file name
*/
void modificationTime(String fileName) throws IOException {
send("MDTM " + fileName);
readCode(213);
}
/**
* Issue a no-operation statement (NOOP).
*/
void noOperation() throws IOException {
send("NOOP");
readCode(200);
}
/**
* Print the working directory (PWD).
*/
String printWorkingDirectory() throws IOException {
send("PWD");
readCode(257);
......@@ -177,6 +225,12 @@ public class FtpClient {
outData = socketData.getOutputStream();
}
/**
* Rename a file (RNFR / RNTO).
*
* @param fromFileName the old file name
* @param toFileName the new file name
*/
void rename(String fromFileName, String toFileName) throws IOException {
send("RNFR " + fromFileName);
readCode(350);
......@@ -184,6 +238,13 @@ public class FtpClient {
readCode(250);
}
/**
* Read a file ([REST] RETR).
*
* @param fileName the file name
* @param out the output stream
* @param restartAt restart at the given position (0 if no restart is required).
*/
void retrieve(String fileName, OutputStream out, long restartAt) throws IOException {
passive();
if (restartAt > 0) {
......@@ -195,11 +256,22 @@ public class FtpClient {
readCode(226);
}
/**
* Remove a directory (RMD).
*
* @param dir the directory to remove
*/
void removeDirectory(String dir) throws IOException {
send("RMD " + dir);
readCode(250);
}
/**
* Get the size of a file (SIZE).
*
* @param fileName the file name
* @return the size
*/
long size(String fileName) throws IOException {
send("SIZE " + fileName);
readCode(250);
......@@ -207,6 +279,12 @@ public class FtpClient {
return size;
}
/**
* Store a file (STOR).
*
* @param fileName the file name
* @param in the input stream
*/
void store(String fileName, InputStream in) throws IOException {
passive();
send("STOR " + fileName);
......@@ -215,6 +293,12 @@ public class FtpClient {
readCode(226);
}
/**
* Get the directory listing (NLST).
*
* @param dir the directory
* @return the listing
*/
String nameList(String dir) throws IOException {
passive();
send("NLST " + dir);
......@@ -226,6 +310,12 @@ public class FtpClient {
return new String(data);
}
/**
* Get the directory listing (LIST).
*
* @param dir the directory
* @return the listing
*/
String list(String dir) throws IOException {
passive();
send("LIST " + dir);
......
......@@ -80,7 +80,7 @@ public class TestExit extends TestBase implements DatabaseEventListener {
app.execute(action);
}
void execute(int action) throws Exception {
private void execute(int action) throws Exception {
Class.forName("org.h2.Driver");
String url = "";
switch (action) {
......
......@@ -251,7 +251,7 @@ public class Build extends BuildBase {
*/
public void javadocImpl() {
mkdir("docs/javadocImpl");
int test;
javadoc(new String[] {
"-sourcepath", "src/main" + File.pathSeparator +
"src/test" + File.pathSeparator + "src/tools" ,
......
......@@ -389,6 +389,14 @@ public class BuildBase {
}
}
/**
* Filter a list of file names.
*
* @param files the original list
* @param keep if matching file names should be kept or removed
* @param pattern the file name pattern
* @return the filtered file list
*/
static FileList filterFiles(FileList files, boolean keep, String pattern) {
boolean start = false;
if (pattern.endsWith("*")) {
......
......@@ -523,4 +523,4 @@ monitor benefit performing conditional significant arithmetic instrumented
doclets extremes instructions printable skips sava sources cms bytecode cfml
cold compiles markup spellchecker interleaved poormans programmed swt railo
clobs resizes precisions scales stopwatch shortly puts captured decremented
him
\ No newline at end of file
him uninterpreted entering composed patched
\ No newline at end of file
......@@ -48,7 +48,7 @@ public class Doclet {
return new Doclet().startDoc(root);
}
boolean startDoc(RootDoc root) throws IOException {
private boolean startDoc(RootDoc root) throws IOException {
ClassDoc[] classes = root.classes();
String[][] options = root.options();
String destDir = System.getProperty("h2.destDir", "docs/javadoc");
......
......@@ -49,6 +49,12 @@ public class PropertiesToUTF8 {
convert("bin/org/h2/server/web/res");
}
/**
* Convert a properties file to a UTF-8 text file.
*
* @param source the name of the properties file
* @param target the target file name
*/
static void propertiesToTextUTF8(String source, String target) throws Exception {
if (!new File(source).exists()) {
return;
......@@ -67,6 +73,13 @@ public class PropertiesToUTF8 {
writer.close();
}
/**
* Convert a translation file (in UTF-8) to a properties file (without
* special characters).
*
* @param source the source file name
* @param target the target file name
*/
static void textUTF8ToProperties(String source, String target) throws Exception {
if (!new File(source).exists()) {
return;
......@@ -147,6 +160,12 @@ public class PropertiesToUTF8 {
}
}
/**
* Store a properties file.
*
* @param p the properties
* @param fileName the file name
*/
static void storeProperties(Properties p, String fileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
p.store(out, null);
......
......@@ -10,11 +10,31 @@ package org.h2.build.indexer;
* Represents a page of the indexer.
*/
public class Page {
/**
* The page id.
*/
int id;
/**
* The file name.
*/
String fileName;
/**
* The title of the page.
*/
String title;
/**
* The total weight of this page.
*/
// TODO page.totalWeight is currently not used
int totalWeight;
/**
* The number of relations between a page and a word.
*/
int relations;
Page(int id, String fileName) {
......
......@@ -10,7 +10,29 @@ package org.h2.build.indexer;
* Represents a weight of a token in a page.
*/
public class Weight {
static final int TITLE = 10000, HEADER = 100, PARAGRAPH = 1;
/**
* The weight of a word in a title.
*/
static final int TITLE = 10000;
/**
* The weight of a word in the header.
*/
static final int HEADER = 100;
/**
* The weight of a word in a paragraph.
*/
static final int PARAGRAPH = 1;
/**
* The page referenced.
*/
Page page;
/**
* The weight value.
*/
int value;
}
......@@ -15,14 +15,25 @@ import java.util.HashMap;
* Represents a word of the full text index.
*/
public class Word {
/**
* The word text.
*/
String name;
HashMap pages = new HashMap();
ArrayList weightList;
private HashMap pages = new HashMap();
private ArrayList weightList;
Word(String name) {
this.name = name;
}
/**
* Add a page to this word.
*
* @param page the page
* @param weight the weight of this word in this page
*/
void addPage(Page page, int weight) {
Weight w = (Weight) pages.get(page);
if (w == null) {
......
......@@ -32,7 +32,7 @@ public class PgTcpRedirect {
new PgTcpRedirect().loop(args);
}
void loop(String[] args) throws Exception {
private void loop(String[] args) throws Exception {
// MySQL protocol:
// http://www.redferni.uklinux.net/mysql/MySQL-Protocol.html
// PostgreSQL protocol:
......@@ -529,6 +529,12 @@ public class PgTcpRedirect {
}
}
/**
* Print the uninterpreted byte array.
*
* @param buffer the byte array
* @param len the length
*/
synchronized void printData(byte[] buffer, int len) {
if (false) {
System.out.print(" ");
......
......@@ -44,7 +44,12 @@ public class Base64 {
}
}
static void main(String[] args) {
/**
* Run the tests.
*
* @param args the command line parameters
*/
public static void main(String[] args) {
check(new String(encode(new byte[] {})), "");
check(new String(encode("A".getBytes())), "QQ==");
check(new String(encode("AB".getBytes())), "QUI=");
......@@ -93,7 +98,7 @@ public class Base64 {
}
}
static byte[] encode(byte[] bin) {
private static byte[] encode(byte[] bin) {
byte[] code = CODE;
int size = bin.length;
int len = ((size + 2) / 3) * 4;
......@@ -121,7 +126,7 @@ public class Base64 {
return enc;
}
static byte[] encodeFast(byte[] bin) {
private static byte[] encodeFast(byte[] bin) {
byte[] code = CODE;
int size = bin.length;
int len = ((size * 4) + 2) / 3;
......@@ -146,7 +151,7 @@ public class Base64 {
return enc;
}
static byte[] trim(byte[] enc) {
private static byte[] trim(byte[] enc) {
byte[] rev = REV;
int j = 0, size = enc.length;
if (size > 1 && enc[size - 2] == '=') {
......@@ -173,7 +178,7 @@ public class Base64 {
return buff;
}
static byte[] decode(byte[] enc) {
private static byte[] decode(byte[] enc) {
enc = trim(enc);
byte[] rev = REV;
int len = enc.length, size = (len * 3) / 4;
......@@ -203,7 +208,7 @@ public class Base64 {
return bin;
}
static byte[] decodeFast(byte[] enc) {
private static byte[] decodeFast(byte[] enc) {
byte[] rev = REV;
int len = enc.length, size = (len * 3) / 4;
byte[] bin = new byte[size];
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论