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

javadocs

上级 57b810ca
...@@ -15,6 +15,7 @@ import org.h2.expression.Expression; ...@@ -15,6 +15,7 @@ import org.h2.expression.Expression;
import org.h2.table.Plan; import org.h2.table.Plan;
import org.h2.table.PlanItem; import org.h2.table.PlanItem;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.ObjectUtils;
import org.h2.util.Permutations; import org.h2.util.Permutations;
/** /**
...@@ -151,20 +152,20 @@ public class Optimizer { ...@@ -151,20 +152,20 @@ public class Optimizer {
} }
boolean generateRandom = (x & 127) == 0; boolean generateRandom = (x & 127) == 0;
if (!generateRandom) { if (!generateRandom) {
System.arraycopy(best, 0, list, 0, filters.length); ObjectUtils.arrayCopy(best, list, filters.length);
if (!shuffleTwo(list)) { if (!shuffleTwo(list)) {
generateRandom = true; generateRandom = true;
} }
} }
if (generateRandom) { if (generateRandom) {
switched = new BitSet(); switched = new BitSet();
System.arraycopy(filters, 0, best, 0, filters.length); ObjectUtils.arrayCopy(filters, best, filters.length);
shuffleAll(best); shuffleAll(best);
System.arraycopy(best, 0, list, 0, filters.length); ObjectUtils.arrayCopy(best, list, filters.length);
} }
if (testPlan(list)) { if (testPlan(list)) {
switched = new BitSet(); 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; ...@@ -35,6 +35,7 @@ import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.ValueHashMap; import org.h2.util.ValueHashMap;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -205,7 +206,7 @@ public class Select extends Query { ...@@ -205,7 +206,7 @@ public class Select extends Query {
if (columnCount != distinctColumnCount) { if (columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows // remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount]; Value[] r2 = new Value[distinctColumnCount];
System.arraycopy(row, 0, r2, 0, distinctColumnCount); ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
row = r2; row = r2;
} }
result.addRow(row); result.addRow(row);
...@@ -337,7 +338,7 @@ public class Select extends Query { ...@@ -337,7 +338,7 @@ public class Select extends Query {
if (columnCount != distinctColumnCount) { if (columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows // remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount]; Value[] r2 = new Value[distinctColumnCount];
System.arraycopy(row, 0, r2, 0, distinctColumnCount); ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
row = r2; row = r2;
} }
result.addRow(row); result.addRow(row);
......
...@@ -33,6 +33,10 @@ public class ConnectionInfo { ...@@ -33,6 +33,10 @@ public class ConnectionInfo {
* The connection display name. * The connection display name.
*/ */
String name; String name;
/**
* The last time this connection was used.
*/
int lastAccess; int lastAccess;
ConnectionInfo() { ConnectionInfo() {
......
...@@ -162,6 +162,14 @@ public class DbContents { ...@@ -162,6 +162,14 @@ public class DbContents {
return defaultSchemaName; 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) { String quoteIdentifier(String identifier) {
if (identifier == null) { if (identifier == null) {
return null; return null;
......
...@@ -237,6 +237,12 @@ public class DiskFile implements CacheWriter { ...@@ -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) { boolean isPageFree(int page) {
for (int i = page * BLOCKS_PER_PAGE; i < (page + 1) * BLOCKS_PER_PAGE; i++) { for (int i = page * BLOCKS_PER_PAGE; i < (page + 1) * BLOCKS_PER_PAGE; i++) {
if (used.get(i)) { if (used.get(i)) {
...@@ -492,6 +498,15 @@ public class DiskFile implements CacheWriter { ...@@ -492,6 +498,15 @@ public class DiskFile implements CacheWriter {
return ((long) block * BLOCK_SIZE) + OFFSET; 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) Record getRecordIfStored(Session session, int pos, RecordReader reader, int storageId)
throws SQLException { throws SQLException {
synchronized (database) { synchronized (database) {
...@@ -518,6 +533,15 @@ public class DiskFile implements CacheWriter { ...@@ -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 { Record getRecord(Session session, int pos, RecordReader reader, int storageId) throws SQLException {
synchronized (database) { synchronized (database) {
if (file == null) { if (file == null) {
...@@ -561,6 +585,13 @@ public class DiskFile implements CacheWriter { ...@@ -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 { int allocate(Storage storage, int blockCount) throws SQLException {
reuseSpace(); reuseSpace();
synchronized (database) { synchronized (database) {
...@@ -666,6 +697,13 @@ public class DiskFile implements CacheWriter { ...@@ -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) { void uncommittedDelete(Session session) {
if (session != null && logChanges && SysProperties.REUSE_SPACE_QUICKLY) { if (session != null && logChanges && SysProperties.REUSE_SPACE_QUICKLY) {
int deleteId = session.getLastUncommittedDelete(); int deleteId = session.getLastUncommittedDelete();
...@@ -676,6 +714,11 @@ public class DiskFile implements CacheWriter { ...@@ -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 { void freePage(int page) throws SQLException {
if (!logChanges) { if (!logChanges) {
setPageOwner(page, FREE_PAGE); setPageOwner(page, FREE_PAGE);
...@@ -686,10 +729,22 @@ public class DiskFile implements CacheWriter { ...@@ -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) { int getPage(int pos) {
return pos >>> BLOCK_PAGE_PAGE_SHIFT; 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) { int getPageOwner(int page) {
if (page * BLOCKS_PER_PAGE > fileBlockCount || page >= pageOwners.size()) { if (page * BLOCKS_PER_PAGE > fileBlockCount || page >= pageOwners.size()) {
return FREE_PAGE; return FREE_PAGE;
...@@ -736,6 +791,12 @@ public class DiskFile implements CacheWriter { ...@@ -736,6 +791,12 @@ public class DiskFile implements CacheWriter {
pageOwners.set(page, storageId); 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) { void setUsed(int pos, int blockCount) {
synchronized (database) { synchronized (database) {
if (pos + blockCount > fileBlockCount) { if (pos + blockCount > fileBlockCount) {
...@@ -812,6 +873,12 @@ public class DiskFile implements CacheWriter { ...@@ -812,6 +873,12 @@ public class DiskFile implements CacheWriter {
return used; return used;
} }
/**
* Update a record.
*
* @param session the session
* @param record the record
*/
void updateRecord(Session session, Record record) throws SQLException { void updateRecord(Session session, Record record) throws SQLException {
synchronized (database) { synchronized (database) {
record.setChanged(true); record.setChanged(true);
...@@ -911,6 +978,15 @@ public class DiskFile implements CacheWriter { ...@@ -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 { void removeRecord(Session session, int pos, Record record, int blockCount) throws SQLException {
synchronized (database) { synchronized (database) {
if (logChanges) { if (logChanges) {
...@@ -922,6 +998,14 @@ public class DiskFile implements CacheWriter { ...@@ -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 { void addRecord(Session session, Record record) throws SQLException {
synchronized (database) { synchronized (database) {
cache.put(record); cache.put(record);
...@@ -931,23 +1015,43 @@ public class DiskFile implements CacheWriter { ...@@ -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() { public Cache getCache() {
return cache; 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) { void free(int pos, int blockCount) {
synchronized (database) { synchronized (database) {
used.setRange(pos, blockCount, false); used.setRange(pos, blockCount, false);
} }
} }
/**
* Get the overhead for each record in bytes.
*
* @return the overhead
*/
int getRecordOverhead() { int getRecordOverhead() {
return recordOverhead; 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 { void truncateStorage(Session session, Storage storage, IntArray pages) throws SQLException {
synchronized (database) { synchronized (database) {
int storageId = storage.getId(); int storageId = storage.getId();
......
...@@ -55,7 +55,7 @@ public class FileObjectMemory implements FileObject { ...@@ -55,7 +55,7 @@ public class FileObjectMemory implements FileObject {
return false; return false;
} }
CompressItem c = (CompressItem) eldest.getKey(); CompressItem c = (CompressItem) eldest.getKey();
compress(c.data, c.l); compress(c.data, c.page);
return true; return true;
} }
} }
...@@ -64,17 +64,25 @@ public class FileObjectMemory implements FileObject { ...@@ -64,17 +64,25 @@ public class FileObjectMemory implements FileObject {
* Represents a compressed item. * Represents a compressed item.
*/ */
static class CompressItem { static class CompressItem {
/**
* The file data.
*/
byte[][] data; byte[][] data;
int l;
/**
* The page to compress.
*/
int page;
public int hashCode() { public int hashCode() {
return data.hashCode() ^ l; return data.hashCode() ^ page;
} }
public boolean equals(Object o) { public boolean equals(Object o) {
if (o instanceof CompressItem) { if (o instanceof CompressItem) {
CompressItem c = (CompressItem) o; CompressItem c = (CompressItem) o;
return c.data == data && c.l == l; return c.data == data && c.page == page;
} }
return false; return false;
} }
...@@ -88,19 +96,19 @@ public class FileObjectMemory implements FileObject { ...@@ -88,19 +96,19 @@ public class FileObjectMemory implements FileObject {
touch(); touch();
} }
private static void compressLater(byte[][] data, int l) { private static void compressLater(byte[][] data, int page) {
//## Java 1.4 begin ## //## Java 1.4 begin ##
CompressItem c = new CompressItem(); CompressItem c = new CompressItem();
c.data = data; c.data = data;
c.l = l; c.page = page;
synchronized (LZF) { synchronized (LZF) {
COMPRESS_LATER.put(c, c); COMPRESS_LATER.put(c, c);
} }
//## Java 1.4 end ## //## Java 1.4 end ##
} }
private static void expand(byte[][] data, int i) { private static void expand(byte[][] data, int page) {
byte[] d = data[i]; byte[] d = data[page];
if (d.length == BLOCK_SIZE) { if (d.length == BLOCK_SIZE) {
return; return;
} }
...@@ -108,17 +116,23 @@ public class FileObjectMemory implements FileObject { ...@@ -108,17 +116,23 @@ public class FileObjectMemory implements FileObject {
synchronized (LZF) { synchronized (LZF) {
LZF.expand(d, 0, d.length, out, 0, BLOCK_SIZE); 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) { synchronized (LZF) {
int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0); int len = LZF.compress(d, BLOCK_SIZE, BUFFER, 0);
if (len <= BLOCK_SIZE) { if (len <= BLOCK_SIZE) {
d = new byte[len]; d = new byte[len];
System.arraycopy(BUFFER, 0, d, 0, len); System.arraycopy(BUFFER, 0, d, 0, len);
data[i] = d; data[page] = d;
} }
} }
} }
...@@ -138,25 +152,25 @@ public class FileObjectMemory implements FileObject { ...@@ -138,25 +152,25 @@ public class FileObjectMemory implements FileObject {
return length; return length;
} }
public void setFileLength(long l) { public void setFileLength(long newLength) {
touch(); touch();
if (l < length) { if (newLength < length) {
pos = Math.min(pos, l); pos = Math.min(pos, newLength);
changeLength(l); changeLength(newLength);
long end = MathUtils.roundUpLong(l, BLOCK_SIZE); long end = MathUtils.roundUpLong(newLength, BLOCK_SIZE);
if (end != l) { if (end != newLength) {
int lastBlock = (int) (l >>> BLOCK_SIZE_SHIFT); int lastPage = (int) (newLength >>> BLOCK_SIZE_SHIFT);
expand(data, lastBlock); expand(data, lastPage);
byte[] d = data[lastBlock]; byte[] d = data[lastPage];
for (int i = (int) (l & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) { for (int i = (int) (newLength & BLOCK_SIZE_MASK); i < BLOCK_SIZE; i++) {
d[i] = 0; d[i] = 0;
} }
if (compress) { if (compress) {
compressLater(data, lastBlock); compressLater(data, lastPage);
} }
} }
} else { } else {
changeLength(l); changeLength(newLength);
} }
} }
...@@ -193,9 +207,9 @@ public class FileObjectMemory implements FileObject { ...@@ -193,9 +207,9 @@ public class FileObjectMemory implements FileObject {
} }
while (len > 0) { while (len > 0) {
int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK)); int l = (int) Math.min(len, BLOCK_SIZE - (pos & BLOCK_SIZE_MASK));
int id = (int) (pos >>> BLOCK_SIZE_SHIFT); int page = (int) (pos >>> BLOCK_SIZE_SHIFT);
expand(data, id); expand(data, page);
byte[] block = data[id]; byte[] block = data[page];
int blockOffset = (int) (pos & BLOCK_SIZE_MASK); int blockOffset = (int) (pos & BLOCK_SIZE_MASK);
if (write) { if (write) {
System.arraycopy(b, off, block, blockOffset, l); System.arraycopy(b, off, block, blockOffset, l);
...@@ -203,7 +217,7 @@ public class FileObjectMemory implements FileObject { ...@@ -203,7 +217,7 @@ public class FileObjectMemory implements FileObject {
System.arraycopy(block, blockOffset, b, off, l); System.arraycopy(block, blockOffset, b, off, l);
} }
if (compress) { if (compress) {
compressLater(data, id); compressLater(data, page);
} }
off += l; off += l;
pos += l; pos += l;
......
...@@ -427,6 +427,13 @@ public class FileSystemDatabase extends FileSystem { ...@@ -427,6 +427,13 @@ public class FileSystemDatabase extends FileSystem {
return true; 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) { synchronized void write(String fileName, byte[] b, int len) {
try { try {
long id = getId(fileName, false); long id = getId(fileName, false);
......
...@@ -82,7 +82,7 @@ public class FileSystemDisk extends FileSystem { ...@@ -82,7 +82,7 @@ public class FileSystemDisk extends FileSystem {
throw Message.getSQLException(ErrorCode.FILE_RENAME_FAILED_2, new String[]{oldName, newName}); 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) { if (SysProperties.TRACE_IO) {
System.out.println("FileSystem." + method + " " + fileName + " " + o); System.out.println("FileSystem." + method + " " + fileName + " " + o);
} }
......
...@@ -140,6 +140,13 @@ public class Column { ...@@ -140,6 +140,13 @@ public class Column {
return isComputed; 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 { Value computeValue(Session session, Row row) throws SQLException {
synchronized (this) { synchronized (this) {
computeTableFilter.setSession(session); computeTableFilter.setSession(session);
...@@ -159,6 +166,12 @@ public class Column { ...@@ -159,6 +166,12 @@ public class Column {
this.defaultExpression = expression; this.defaultExpression = expression;
} }
/**
* Set the table and column id.
*
* @param table the table
* @param columnId the column index
*/
void setTable(Table table, int columnId) { void setTable(Table table, int columnId) {
this.table = table; this.table = table;
this.columnId = columnId; this.columnId = columnId;
...@@ -571,8 +584,15 @@ public class Column { ...@@ -571,8 +584,15 @@ public class Column {
return DataType.getDataType(type); 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(); return constraint == null ? "" : constraint.getSQL();
} }
...@@ -588,6 +608,14 @@ public class Column { ...@@ -588,6 +608,14 @@ public class Column {
this.primaryKey = primaryKey; 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) { boolean isEverything(ExpressionVisitor visitor) {
if (visitor.getType() == ExpressionVisitor.GET_DEPENDENCIES) { if (visitor.getType() == ExpressionVisitor.GET_DEPENDENCIES) {
if (sequence != null) { if (sequence != null) {
......
...@@ -13,6 +13,7 @@ import org.h2.engine.Session; ...@@ -13,6 +13,7 @@ import org.h2.engine.Session;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor; import org.h2.expression.ExpressionVisitor;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
/** /**
* A possible query execution plan. The time required to execute a query depends * A possible query execution plan. The time required to execute a query depends
...@@ -33,7 +34,7 @@ public class Plan { ...@@ -33,7 +34,7 @@ public class Plan {
*/ */
public Plan(TableFilter[] filters, int count, Expression condition) { public Plan(TableFilter[] filters, int count, Expression condition) {
this.filters = new TableFilter[count]; this.filters = new TableFilter[count];
System.arraycopy(filters, 0, this.filters, 0, count); ObjectUtils.arrayCopy(filters, this.filters, count);
ObjectArray allCond = new ObjectArray(); ObjectArray allCond = new ObjectArray();
ObjectArray all = new ObjectArray(); ObjectArray all = new ObjectArray();
if (condition != null) { if (condition != null) {
......
...@@ -13,7 +13,12 @@ import org.h2.index.Index; ...@@ -13,7 +13,12 @@ import org.h2.index.Index;
* using it. * using it.
*/ */
public class PlanItem { public class PlanItem {
/**
* The cost.
*/
double cost; double cost;
private Index index; private Index index;
private PlanItem joinPlan; private PlanItem joinPlan;
......
...@@ -521,6 +521,9 @@ public class TableFilter implements ColumnResolver { ...@@ -521,6 +521,9 @@ public class TableFilter implements ColumnResolver {
return buff.toString(); return buff.toString();
} }
/**
* Remove all index conditions that are not used by the current index.
*/
void removeUnusableIndexConditions() { void removeUnusableIndexConditions() {
for (int i = 0; i < indexConditions.size(); i++) { for (int i = 0; i < indexConditions.size(); i++) {
IndexCondition cond = (IndexCondition) indexConditions.get(i); IndexCondition cond = (IndexCondition) indexConditions.get(i);
...@@ -557,6 +560,11 @@ public class TableFilter implements ColumnResolver { ...@@ -557,6 +560,11 @@ public class TableFilter implements ColumnResolver {
return used; return used;
} }
/**
* Set the session of this table filter.
*
* @param session
*/
void setSession(Session session) { void setSession(Session session) {
this.session = session; this.session = session;
} }
......
...@@ -53,7 +53,7 @@ public class TableView extends Table { ...@@ -53,7 +53,7 @@ public class TableView extends Table {
initColumnsAndTables(session); initColumnsAndTables(session);
} }
Query recompileQuery(Session session) throws SQLException { private Query recompileQuery(Session session) throws SQLException {
Prepared p = session.prepare(querySQL); Prepared p = session.prepare(querySQL);
if (!(p instanceof Query)) { if (!(p instanceof Query)) {
throw Message.getSyntaxError(querySQL, 0); throw Message.getSyntaxError(querySQL, 0);
...@@ -310,7 +310,7 @@ public class TableView extends Table { ...@@ -310,7 +310,7 @@ public class TableView extends Table {
} }
} }
void setOwner(User owner) { private void setOwner(User owner) {
this.owner = owner; this.owner = owner;
} }
......
...@@ -152,6 +152,9 @@ ShutdownHandler { ...@@ -152,6 +152,9 @@ ShutdownHandler {
stopAll(); stopAll();
} }
/**
* Stop all servers that were started using the console.
*/
void stopAll() { void stopAll() {
if (web != null && web.isRunning(false)) { if (web != null && web.isRunning(false)) {
web.stop(); web.stop();
......
...@@ -298,12 +298,21 @@ public class RunScript extends Tool { ...@@ -298,12 +298,21 @@ public class RunScript extends Tool {
* @param fileName the script file * @param fileName the script file
* @param charsetName the character set name or null for UTF-8 * @param charsetName the character set name or null for UTF-8
* @param continueOnError if execution should be continued if an error occurs * @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 { 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); 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 { void process(String url, String user, String password, String fileName, String charsetName, boolean continueOnError) throws SQLException {
try { try {
org.h2.Driver.load(); org.h2.Driver.load();
......
...@@ -126,18 +126,25 @@ public class Script extends Tool { ...@@ -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 url the database URL
* @param user the user name * @param user the user name
* @param password the password * @param password the password
* @param fileName the script file * @param fileName the script file
* @throws SQLException
*/ */
public static void execute(String url, String user, String password, String fileName) throws SQLException { public static void execute(String url, String user, String password, String fileName) throws SQLException {
new Script().process(url, user, password, fileName); 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 { void process(String url, String user, String password, String fileName) throws SQLException {
Connection conn = null; Connection conn = null;
Statement stat = null; Statement stat = null;
......
...@@ -64,9 +64,25 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData { ...@@ -64,9 +64,25 @@ public class SimpleResultSet implements ResultSet, ResultSetMetaData {
* This class holds the data of a result column. * This class holds the data of a result column.
*/ */
static class Column { static class Column {
/**
* The column name.
*/
String name; String name;
/**
* The SQL type.
*/
int sqlType; int sqlType;
/**
* The precision.
*/
int precision; int precision;
/**
* The scale.
*/
int scale; int scale;
} }
......
...@@ -236,6 +236,6 @@ public class ByteUtils { ...@@ -236,6 +236,6 @@ public class ByteUtils {
byte[] copy = new byte[len]; byte[] copy = new byte[len];
System.arraycopy(b, 0, copy, 0, len); System.arraycopy(b, 0, copy, 0, len);
return copy; return copy;
} }
} }
...@@ -107,7 +107,6 @@ public class FileUtils { ...@@ -107,7 +107,6 @@ public class FileUtils {
* Try to delete a file. * Try to delete a file.
* *
* @param fileName the file name * @param fileName the file name
* @return true if it could be deleted
*/ */
public static void tryDelete(String fileName) { public static void tryDelete(String fileName) {
FileSystem.getInstance(fileName).tryDelete(fileName); FileSystem.getInstance(fileName).tryDelete(fileName);
......
...@@ -184,9 +184,7 @@ public class ObjectArray { ...@@ -184,9 +184,7 @@ public class ObjectArray {
* @param array the target array * @param array the target array
*/ */
public void toArray(Object[] array) { public void toArray(Object[] array) {
for (int i = 0; i < size; i++) { ObjectUtils.arrayCopy(data, array, size);
array[i] = data[i];
}
} }
/** /**
......
...@@ -21,6 +21,13 @@ import org.h2.message.Message; ...@@ -21,6 +21,13 @@ import org.h2.message.Message;
*/ */
public class ObjectUtils { 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() { private ObjectUtils() {
// utility class // utility class
} }
...@@ -168,5 +175,24 @@ public class ObjectUtils { ...@@ -168,5 +175,24 @@ public class ObjectUtils {
throw Message.getSQLException(ErrorCode.DESERIALIZATION_FAILED_1, new String[] { e.toString() }, e); throw Message.getSQLException(ErrorCode.DESERIALIZATION_FAILED_1, new String[] { e.toString() }, e);
} }
} }
/**
* 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 { ...@@ -30,7 +30,15 @@ public class TempFileDeleter {
* Contains information about a file. * Contains information about a file.
*/ */
static class TempFile { static class TempFile {
/**
* The file name.
*/
String fileName; String fileName;
/**
* The last modified date of this file.
*/
long lastModified; long lastModified;
} }
......
...@@ -18,6 +18,10 @@ import org.h2.util.DateTimeUtils; ...@@ -18,6 +18,10 @@ import org.h2.util.DateTimeUtils;
* Implementation of the DATE data type. * Implementation of the DATE data type.
*/ */
public class ValueDate extends Value { public class ValueDate extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 8; static final int PRECISION = 8;
/** /**
......
...@@ -17,6 +17,10 @@ import org.h2.util.ObjectUtils; ...@@ -17,6 +17,10 @@ import org.h2.util.ObjectUtils;
* Implementation of the REAL data type. * Implementation of the REAL data type.
*/ */
public class ValueFloat extends Value { public class ValueFloat extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 7; static final int PRECISION = 7;
/** /**
......
...@@ -19,6 +19,9 @@ import org.h2.util.ObjectUtils; ...@@ -19,6 +19,9 @@ import org.h2.util.ObjectUtils;
*/ */
public class ValueShort extends Value { public class ValueShort extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 5; static final int PRECISION = 5;
/** /**
......
...@@ -16,6 +16,10 @@ import org.h2.util.DateTimeUtils; ...@@ -16,6 +16,10 @@ import org.h2.util.DateTimeUtils;
* Implementation of the TIME data type. * Implementation of the TIME data type.
*/ */
public class ValueTime extends Value { public class ValueTime extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 6; static final int PRECISION = 6;
/** /**
......
...@@ -21,6 +21,10 @@ import org.h2.util.MathUtils; ...@@ -21,6 +21,10 @@ import org.h2.util.MathUtils;
* Implementation of the TIMESTAMP data type. * Implementation of the TIMESTAMP data type.
*/ */
public class ValueTimestamp extends Value { public class ValueTimestamp extends Value {
/**
* The precision in digits.
*/
static final int PRECISION = 23; static final int PRECISION = 23;
/** /**
......
...@@ -266,14 +266,12 @@ java org.h2.test.TestAll timer ...@@ -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 H2 Console should support Java Queries
convert test.in.sql to RunScript syntax
C:\download\Data Concurrency and Consistency.pdf C:\download\Data Concurrency and Consistency.pdf
not tested: not tested:
...@@ -290,8 +288,6 @@ create an mbean for each database? server? (jconsole) ...@@ -290,8 +288,6 @@ create an mbean for each database? server? (jconsole)
in help.csv, use complete examples for functions; add a test case in help.csv, use complete examples for functions; add a test case
improve javadocs
option to write complete page right after checkpoint option to write complete page right after checkpoint
test case for out of memory (try to corrupt the database using out of memory) test case for out of memory (try to corrupt the database using out of memory)
......
...@@ -365,6 +365,12 @@ public abstract class TestBase { ...@@ -365,6 +365,12 @@ public abstract class TestBase {
printlnWithTime(time, getClass().getName() + " " + s); 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) { static void printlnWithTime(long time, String s) {
String t = "0000000000" + time; String t = "0000000000" + time;
t = t.substring(t.length() - 6); t = t.substring(t.length() - 6);
...@@ -743,9 +749,10 @@ public abstract class TestBase { ...@@ -743,9 +749,10 @@ public abstract class TestBase {
* @param data the expected data * @param data the expected data
* @throws Exception if there is a mismatch * @throws Exception if there is a mismatch
*/ */
void assertResultSetUnordered(ResultSet rs, String[][] data) throws Exception { // void assertResultSetUnordered(ResultSet rs, String[][] data)
assertResultSet(false, rs, data); // throws Exception {
} // assertResultSet(false, rs, data);
// }
/** /**
* Check if a result set contains the expected data. * Check if a result set contains the expected data.
...@@ -755,7 +762,7 @@ public abstract class TestBase { ...@@ -755,7 +762,7 @@ public abstract class TestBase {
* @param data the expected data * @param data the expected data
* @throws Exception if there is a mismatch * @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 len = rs.getMetaData().getColumnCount();
int rows = data.length; int rows = data.length;
if (rows == 0) { if (rows == 0) {
...@@ -815,7 +822,7 @@ public abstract class TestBase { ...@@ -815,7 +822,7 @@ public abstract class TestBase {
return true; return true;
} }
String[] getData(ResultSet rs, int len) throws SQLException { private String[] getData(ResultSet rs, int len) throws SQLException {
String[] data = new String[len]; String[] data = new String[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
data[i] = rs.getString(i + 1); data[i] = rs.getString(i + 1);
...@@ -825,7 +832,7 @@ public abstract class TestBase { ...@@ -825,7 +832,7 @@ public abstract class TestBase {
return data; return data;
} }
String formatRow(String[] row) { private String formatRow(String[] row) {
String sb = ""; String sb = "";
for (int i = 0; i < row.length; i++) { for (int i = 0; i < row.length; i++) {
sb += "{" + row[i] + "}"; sb += "{" + row[i] + "}";
......
...@@ -143,11 +143,11 @@ public class BenchC implements Bench { ...@@ -143,11 +143,11 @@ public class BenchC implements Bench {
trace("load done"); trace("load done");
} }
void trace(String s) { private void trace(String s) {
action = s; action = s;
} }
void trace(int i, int max) { private void trace(int i, int max) {
db.trace(action, i, max); db.trace(action, i, max);
} }
......
...@@ -17,16 +17,39 @@ public class BenchCRandom { ...@@ -17,16 +17,39 @@ public class BenchCRandom {
private Random random = new Random(10); 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 getNonUniform(int a, int min, int max) {
int c = 0; int c = 0;
return (((getInt(0, a) | getInt(min, max)) + c) % (max - min + 1)) return (((getInt(0, a) | getInt(min, max)) + c) % (max - min + 1))
+ min; + 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) { int getInt(int min, int max) {
return max <= min ? min : (random.nextInt(max - min) + min); 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[] getBoolean(int length, int trueCount) {
boolean[] data = new boolean[length]; boolean[] data = new boolean[length];
for (int i = 0, pos; i < trueCount; i++) { for (int i = 0, pos; i < trueCount; i++) {
...@@ -38,6 +61,13 @@ public class BenchCRandom { ...@@ -38,6 +61,13 @@ public class BenchCRandom {
return data; 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) { String replace(String text, String replacement) {
int pos = getInt(0, text.length() - replacement.length()); int pos = getInt(0, text.length() - replacement.length());
StringBuffer buffer = new StringBuffer(text); StringBuffer buffer = new StringBuffer(text);
...@@ -45,6 +75,13 @@ public class BenchCRandom { ...@@ -45,6 +75,13 @@ public class BenchCRandom {
return buffer.toString(); 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) { String getNumberString(int min, int max) {
int len = getInt(min, max); int len = getInt(min, max);
char[] buff = new char[len]; char[] buff = new char[len];
...@@ -54,6 +91,11 @@ public class BenchCRandom { ...@@ -54,6 +91,11 @@ public class BenchCRandom {
return new String(buff); return new String(buff);
} }
/**
* Get random address data.
*
* @return the address
*/
String[] getAddress() { String[] getAddress() {
String str1 = getString(10, 20); String str1 = getString(10, 20);
String str2 = getString(10, 20); String str2 = getString(10, 20);
...@@ -63,10 +105,23 @@ public class BenchCRandom { ...@@ -63,10 +105,23 @@ public class BenchCRandom {
return new String[] { str1, str2, city, state, zip }; 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) { String getString(int min, int max) {
return getString(getInt(min, max)); return getString(getInt(min, max));
} }
/**
* Get a random string.
*
* @param len the size
* @return the string
*/
String getString(int len) { String getString(int len) {
char[] buff = new char[len]; char[] buff = new char[len];
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
...@@ -75,6 +130,12 @@ public class BenchCRandom { ...@@ -75,6 +130,12 @@ public class BenchCRandom {
return new String(buff); 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[] getPermutation(int length) {
int[] data = new int[length]; int[] data = new int[length];
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
...@@ -89,11 +150,23 @@ public class BenchCRandom { ...@@ -89,11 +150,23 @@ public class BenchCRandom {
return data; 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) { BigDecimal getBigDecimal(int value, int scale) {
return new BigDecimal(new BigInteger(String.valueOf(value)), 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 getLastname(int i) {
String[] n = { "BAR", "OUGHT", "ABLE", "PRI", "PRES", "ESE", "ANTI", String[] n = { "BAR", "OUGHT", "ABLE", "PRI", "PRES", "ESE", "ANTI",
"CALLY", "ATION", "EING" }; "CALLY", "ATION", "EING" };
......
...@@ -38,7 +38,10 @@ public class BenchCThread { ...@@ -38,7 +38,10 @@ public class BenchCThread {
this.random = random; this.random = random;
warehouseId = random.getInt(1, bench.warehouses); warehouseId = random.getInt(1, bench.warehouses);
} }
/**
* Process the list of operations (a 'deck') in random order.
*/
void process() throws Exception { void process() throws Exception {
int[] deck = new int[] { OP_NEW_ORDER, OP_NEW_ORDER, OP_NEW_ORDER, 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, OP_NEW_ORDER, OP_NEW_ORDER, OP_NEW_ORDER, OP_NEW_ORDER,
......
...@@ -13,6 +13,7 @@ import java.sql.DriverManager; ...@@ -13,6 +13,7 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData; import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
...@@ -49,22 +50,45 @@ class Database { ...@@ -49,22 +50,45 @@ class Database {
private Object serverDerby; private Object serverDerby;
private boolean serverHSQLDB; private boolean serverHSQLDB;
/**
* Get the database name.
*
* @return the database name
*/
String getName() { String getName() {
return name; return name;
} }
/**
* Get the total measured time.
*
* @return the time
*/
int getTotalTime() { int getTotalTime() {
return totalTime; return totalTime;
} }
/**
* Get the result array.
*
* @return the result array
*/
ArrayList getResults() { ArrayList getResults() {
return results; return results;
} }
/**
* Get the random number generator.
*
* @return the generator
*/
Random getRandom() { Random getRandom() {
return random; return random;
} }
/**
* Start the server if the this is a remote connection.
*/
void startServer() throws Exception { void startServer() throws Exception {
if (url.startsWith("jdbc:h2:tcp:")) { if (url.startsWith("jdbc:h2:tcp:")) {
serverH2 = Server.createTcpServer(new String[0]).start(); serverH2 = Server.createTcpServer(new String[0]).start();
...@@ -90,6 +114,9 @@ class Database { ...@@ -90,6 +114,9 @@ class Database {
} }
} }
/**
* Stop the server if this is a remote connection.
*/
void stopServer() throws Exception { void stopServer() throws Exception {
if (serverH2 != null) { if (serverH2 != null) {
serverH2.stop(); serverH2.stop();
...@@ -110,6 +137,14 @@ class Database { ...@@ -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) { static Database parse(TestPerformance test, int id, String dbString) {
try { try {
StringTokenizer tokenizer = new StringTokenizer(dbString, ","); StringTokenizer tokenizer = new StringTokenizer(dbString, ",");
...@@ -133,8 +168,20 @@ class Database { ...@@ -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:")) { if (url.startsWith("jdbc:derby:")) {
// Derby: use higher cache size // Derby: use higher cache size
Statement stat = null; Statement stat = null;
...@@ -159,15 +206,13 @@ class Database { ...@@ -159,15 +206,13 @@ class Database {
JdbcUtils.closeSilently(stat); JdbcUtils.closeSilently(stat);
} }
} }
return conn;
}
void openConnection() throws Exception {
conn = DriverManager.getConnection(url, user, password);
stat = conn.createStatement(); stat = conn.createStatement();
} }
void closeConnection() throws Exception { /**
* Close the database connection.
*/
void closeConnection() throws SQLException {
// if(!serverHSQLDB && url.startsWith("jdbc:hsqldb:")) { // if(!serverHSQLDB && url.startsWith("jdbc:hsqldb:")) {
// stat.execute("SHUTDOWN"); // stat.execute("SHUTDOWN");
// } // }
...@@ -176,7 +221,12 @@ class Database { ...@@ -176,7 +221,12 @@ class Database {
conn = null; 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()); String id = url.substring("jdbc:".length());
id = id.substring(0, id.indexOf(':')); id = id.substring(0, id.indexOf(':'));
for (Iterator it = prop.keySet().iterator(); it.hasNext();) { for (Iterator it = prop.keySet().iterator(); it.hasNext();) {
...@@ -191,12 +241,18 @@ class Database { ...@@ -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); sql = getSQL(sql);
return conn.prepareStatement(sql); return conn.prepareStatement(sql);
} }
public String getSQL(String sql) { private String getSQL(String sql) {
for (int i = 0; i < replace.size(); i++) { for (int i = 0; i < replace.size(); i++) {
String[] pair = (String[]) replace.get(i); String[] pair = (String[]) replace.get(i);
String pattern = pair[0]; String pattern = pair[0];
...@@ -206,11 +262,21 @@ class Database { ...@@ -206,11 +262,21 @@ class Database {
return sql; return sql;
} }
/**
* Start the benchmark.
*
* @param bench the benchmark
* @param action the action
*/
void start(Bench bench, String action) { void start(Bench bench, String action) {
this.action = bench.getName() + ": " + action; this.action = bench.getName() + ": " + action;
this.startTime = System.currentTimeMillis(); this.startTime = System.currentTimeMillis();
} }
/**
* This method is called when the test run ends. This will stop collecting
* data.
*/
void end() { void end() {
long time = System.currentTimeMillis() - startTime; long time = System.currentTimeMillis() - startTime;
log(action, "ms", (int) time); log(action, "ms", (int) time);
...@@ -219,6 +285,11 @@ class Database { ...@@ -219,6 +285,11 @@ class Database {
} }
} }
/**
* Drop a table. Errors are ignored.
*
* @param table the table name
*/
void dropTable(String table) { void dropTable(String table) {
try { try {
update("DROP TABLE " + table); update("DROP TABLE " + table);
...@@ -227,13 +298,24 @@ class Database { ...@@ -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); test.trace(trace);
prep.executeUpdate(); prep.executeUpdate();
executedStatements++; 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); sql = getSQL(sql);
if (sql.trim().length() > 0) { if (sql.trim().length() > 0) {
executedStatements++; executedStatements++;
...@@ -243,18 +325,36 @@ class Database { ...@@ -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); conn.setAutoCommit(b);
} }
public void commit() throws Exception { /**
* Commit a transaction.
*/
void commit() throws SQLException {
conn.commit(); conn.commit();
} }
public void rollback() throws Exception { /**
* Roll a transaction back.
*/
void rollback() throws SQLException {
conn.rollback(); 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) { void trace(String action, int i, int max) {
if (trace) { if (trace) {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
...@@ -267,17 +367,37 @@ class Database { ...@@ -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) { void logMemory(Bench bench, String action) {
log(bench.getName() + ": " + action, "MB", TestBase.getMemoryUsed()); 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) { void log(String action, String scale, int value) {
if (test.collect) { if (test.collect) {
results.add(new Object[] { action, scale, new Integer(value) }); 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(); // long time = System.currentTimeMillis();
ResultSet rs = prep.executeQuery(); ResultSet rs = prep.executeQuery();
// time = System.currentTimeMillis() - time; // time = System.currentTimeMillis() - time;
...@@ -288,7 +408,12 @@ class Database { ...@@ -288,7 +408,12 @@ class Database {
return rs; 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(); ResultSet rs = prep.executeQuery();
ResultSetMetaData meta = rs.getMetaData(); ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount(); int columnCount = meta.getColumnCount();
...@@ -299,16 +424,22 @@ class Database { ...@@ -299,16 +424,22 @@ class Database {
} }
} }
/**
* Get the number of executed statements.
*
* @return the number of statements
*/
int getExecutedStatements() { int getExecutedStatements() {
return executedStatements; return executedStatements;
} }
public int getId() { /**
* Get the database id.
*
* @return the id
*/
int getId() {
return id; return id;
} }
public Connection getCurrentConnection() {
return conn;
}
} }
...@@ -27,7 +27,14 @@ import org.h2.util.JdbcUtils; ...@@ -27,7 +27,14 @@ import org.h2.util.JdbcUtils;
*/ */
public class TestPerformance { public class TestPerformance {
/**
* Whether data should be collected.
*/
boolean collect; boolean collect;
/**
* The flag used to enable or disable trace messages.
*/
boolean trace; boolean trace;
/** /**
...@@ -238,6 +245,11 @@ public class TestPerformance { ...@@ -238,6 +245,11 @@ public class TestPerformance {
bench.runTest(); bench.runTest();
} }
/**
* Print a message to system out if trace is enabled.
*
* @param s the message
*/
void trace(String s) { void trace(String s) {
if (trace) { if (trace) {
System.out.println(s); System.out.println(s);
......
...@@ -15,7 +15,11 @@ import java.io.Reader; ...@@ -15,7 +15,11 @@ import java.io.Reader;
*/ */
public class Tokenizer { public class Tokenizer {
/**
* This token type means no more tokens are available.
*/
static final int TYPE_EOF = -1; static final int TYPE_EOF = -1;
private static final int TYPE_WORD = -2; private static final int TYPE_WORD = -2;
private static final int TYPE_NOTHING = -3; private static final int TYPE_NOTHING = -3;
private static final byte WHITESPACE = 1; private static final byte WHITESPACE = 1;
...@@ -78,6 +82,9 @@ public class Tokenizer { ...@@ -78,6 +82,9 @@ public class Tokenizer {
return i; return i;
} }
/**
* Initialize the tokenizer.
*/
void initToken() { void initToken() {
buffer = new StringBuffer(); buffer = new StringBuffer();
} }
...@@ -91,6 +98,11 @@ public class Tokenizer { ...@@ -91,6 +98,11 @@ public class Tokenizer {
buffer.append((char) i); buffer.append((char) i);
} }
/**
* Read the next token and get the token type.
*
* @return the token type
*/
int nextToken() throws IOException { int nextToken() throws IOException {
byte[] ct = charTypes; byte[] ct = charTypes;
int c; int c;
......
...@@ -179,8 +179,14 @@ public class Db { ...@@ -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) { public void setAutoCommit(boolean autoCommit) {
......
...@@ -66,7 +66,12 @@ public class TestDeadlock extends TestBase { ...@@ -66,7 +66,12 @@ public class TestDeadlock extends TestBase {
* that execute a statement. * that execute a statement.
*/ */
abstract class DoIt extends Thread { abstract class DoIt extends Thread {
/**
* The operation to execute.
*/
abstract void execute() throws SQLException; abstract void execute() throws SQLException;
public void run() { public void run() {
try { try {
execute(); execute();
......
...@@ -21,6 +21,12 @@ public class WebClient { ...@@ -21,6 +21,12 @@ public class WebClient {
private String sessionId; 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 { String get(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
...@@ -36,10 +42,15 @@ public class WebClient { ...@@ -36,10 +42,15 @@ public class WebClient {
return result; return result;
} }
void readSessionId(String result) { /**
int idx = result.indexOf("jsessionid="); * Read the session ID from a URL.
String id = result.substring(idx + "jsessionid=".length()); *
for (int i = 0; i < result.length(); i++) { * @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); char ch = id.charAt(i);
if (!Character.isLetterOrDigit(ch)) { if (!Character.isLetterOrDigit(ch)) {
id = id.substring(0, i); id = id.substring(0, i);
...@@ -49,6 +60,13 @@ public class WebClient { ...@@ -49,6 +60,13 @@ public class WebClient {
this.sessionId = id; 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 { String get(String url, String page) throws IOException {
if (sessionId != null) { if (sessionId != null) {
if (page.indexOf('?') < 0) { if (page.indexOf('?') < 0) {
......
...@@ -23,6 +23,12 @@ class OutputCatcher extends Thread { ...@@ -23,6 +23,12 @@ class OutputCatcher extends Thread {
this.in = in; 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) { String readLine(long wait) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
while (true) { while (true) {
......
...@@ -13,7 +13,6 @@ import java.io.InputStream; ...@@ -13,7 +13,6 @@ import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Random; import java.util.Random;
...@@ -97,14 +96,30 @@ public abstract class TestHalt extends TestBase { ...@@ -97,14 +96,30 @@ public abstract class TestHalt extends TestBase {
private int errorId; private int errorId;
private int sequenceId; private int sequenceId;
/**
* Initialize the test.
*/
abstract void testInit() throws Exception; abstract void testInit() throws Exception;
/**
* Check if the database is consistent after a simulated database crash.
*/
abstract void testCheckAfterCrash() throws Exception; abstract void testCheckAfterCrash() throws Exception;
/**
* Wait for some time after the application has been started.
*/
abstract void testWaitAfterAppStart() throws Exception; abstract void testWaitAfterAppStart() throws Exception;
/**
* Start the application.
*/
abstract void appStart() throws Exception; abstract void appStart() throws Exception;
/**
* Run the application.
* @throws Exception
*/
abstract void appRun() throws Exception; abstract void appRun() throws Exception;
public void test() throws Exception { public void test() throws Exception {
...@@ -272,53 +287,55 @@ public abstract class TestHalt extends TestBase { ...@@ -272,53 +287,55 @@ public abstract class TestHalt extends TestBase {
} }
} }
public Connection getConnectionHSQLDB() throws Exception { // public Connection getConnectionHSQLDB() throws Exception {
File lock = new File("test.lck"); // File lock = new File("test.lck");
while (lock.exists()) { // while (lock.exists()) {
lock.delete(); // lock.delete();
System.gc(); // System.gc();
} // }
Class.forName("org.hsqldb.jdbcDriver"); // Class.forName("org.hsqldb.jdbcDriver");
return DriverManager.getConnection("jdbc:hsqldb:test", "sa", ""); // return DriverManager.getConnection("jdbc:hsqldb:test", "sa", "");
} // }
public Connection getConnectionDerby() throws Exception { // public Connection getConnectionDerby() throws Exception {
File lock = new File("test3/db.lck"); // File lock = new File("test3/db.lck");
while (lock.exists()) { // while (lock.exists()) {
lock.delete(); // lock.delete();
System.gc(); // System.gc();
} // }
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance(); // Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
try { // try {
return DriverManager.getConnection("jdbc:derby:test3;create=true", "sa", "sa"); // return DriverManager.getConnection(
} catch (SQLException e) { // "jdbc:derby:test3;create=true", "sa", "sa");
Exception e2 = e; // } catch (SQLException e) {
do { // Exception e2 = e;
e.printStackTrace(); // do {
e = e.getNextException(); // e.printStackTrace();
} while (e != null); // e = e.getNextException();
throw e2; // } while (e != null);
} // throw e2;
} // }
// }
void disconnectHSQLDB() { // void disconnectHSQLDB() {
try { // try {
conn.createStatement().execute("SHUTDOWN"); // conn.createStatement().execute("SHUTDOWN");
} catch (Exception e) { // } catch (Exception e) {
// ignore // // ignore
} // }
// super.disconnect(); // // super.disconnect();
} // }
void disconnectDerby() { // void disconnectDerby() {
// super.disconnect(); // // super.disconnect();
try { // try {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); // Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
DriverManager.getConnection("jdbc:derby:;shutdown=true", "sa", "sa"); // DriverManager.getConnection(
} catch (Exception e) { // "jdbc:derby:;shutdown=true", "sa", "sa");
// ignore // } catch (Exception e) {
} // // ignore
} // }
// }
/** /**
* Create a random string with the specified length. * Create a random string with the specified length.
......
...@@ -30,7 +30,7 @@ class Column { ...@@ -30,7 +30,7 @@ class Column {
private boolean isPrimaryKey; private boolean isPrimaryKey;
// TODO test isAutoincrement; // TODO test isAutoincrement;
Column(TestSynth config) { private Column(TestSynth config) {
this.config = config; this.config = config;
} }
...@@ -68,7 +68,14 @@ class Column { ...@@ -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()) { switch (config.getMode()) {
case TestSynth.H2: case TestSynth.H2:
case TestSynth.H2_MEM: case TestSynth.H2_MEM:
...@@ -103,7 +110,7 @@ class Column { ...@@ -103,7 +110,7 @@ class Column {
} }
} }
String getTypeName() { private String getTypeName() {
switch (type) { switch (type) {
case Types.INTEGER: case Types.INTEGER:
return "INT"; return "INT";
...@@ -152,7 +159,7 @@ class Column { ...@@ -152,7 +159,7 @@ class Column {
} }
} }
public String getCreateSQL() { String getCreateSQL() {
String sql = name + " " + getTypeName(); String sql = name + " " + getTypeName();
if (!isNullable) { if (!isNullable) {
sql += " NOT NULL"; sql += " NOT NULL";
...@@ -160,19 +167,25 @@ class Column { ...@@ -160,19 +167,25 @@ class Column {
return sql; return sql;
} }
public String getName() { String getName() {
return name; return name;
} }
public Value getRandomValue() { Value getRandomValue() {
return Value.getRandom(config, type, precision, scale, isNullable); return Value.getRandom(config, type, precision, scale, isNullable);
} }
public Value getRandomValueNotNull() { // Value getRandomValueNotNull() {
return Value.getRandom(config, type, precision, scale, false); // 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 column = new Column(config);
column.name = "C_" + config.randomIdentifier(); column.name = "C_" + config.randomIdentifier();
int randomType; int randomType;
...@@ -191,19 +204,24 @@ class Column { ...@@ -191,19 +204,24 @@ class Column {
return column; return column;
} }
public boolean isPrimaryKey() { boolean getPrimaryKey() {
return isPrimaryKey; return isPrimaryKey;
} }
public void setPrimaryKey(boolean b) { void setPrimaryKey(boolean b) {
isPrimaryKey = b; isPrimaryKey = b;
} }
public void setNullable(boolean b) { void setNullable(boolean b) {
isNullable = b; isNullable = b;
} }
public int getType() { /**
* The the column type.
*
* @return the type
*/
int getType() {
return type; return type;
} }
......
...@@ -13,8 +13,8 @@ import java.util.ArrayList; ...@@ -13,8 +13,8 @@ import java.util.ArrayList;
*/ */
public class DbState implements DbInterface { public class DbState implements DbInterface {
boolean connected; private boolean connected;
boolean autoCommit; private boolean autoCommit;
private TestSynth config; private TestSynth config;
private ArrayList tables = new ArrayList(); private ArrayList tables = new ArrayList();
private ArrayList indexes = new ArrayList(); private ArrayList indexes = new ArrayList();
...@@ -80,6 +80,11 @@ public class DbState implements DbInterface { ...@@ -80,6 +80,11 @@ public class DbState implements DbInterface {
// nothing to do // nothing to do
} }
/**
* Get a random table.
*
* @return the table
*/
Table randomTable() { Table randomTable() {
if (tables.size() == 0) { if (tables.size() == 0) {
return null; return null;
...@@ -91,5 +96,9 @@ public class DbState implements DbInterface { ...@@ -91,5 +96,9 @@ public class DbState implements DbInterface {
public void end() { public void end() {
// nothing to do // nothing to do
} }
public String toString() {
return "autocommit: " + autoCommit + " connected: " + connected;
}
} }
...@@ -13,19 +13,24 @@ import java.util.ArrayList; ...@@ -13,19 +13,24 @@ import java.util.ArrayList;
* Represents an expression. * Represents an expression.
*/ */
public class Expression { public class Expression {
boolean isCondition;
private String sql; private String sql;
private TestSynth config; private TestSynth config;
private Command command; private Command command;
private Expression(TestSynth config, Command command, boolean isCondition) { private Expression(TestSynth config, Command command) {
this.config = config; this.config = config;
this.isCondition = isCondition;
this.command = command; this.command = command;
sql = ""; 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) { static String[] getRandomSelectList(TestSynth config, Command command) {
if (config.random().getBoolean(30)) { if (config.random().getBoolean(30)) {
return new String[] { "*" }; return new String[] { "*" };
...@@ -47,16 +52,23 @@ public class Expression { ...@@ -47,16 +52,23 @@ public class Expression {
return list; 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) { 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)) { if (config.random().getBoolean(50)) {
condition.create(); condition.create();
} }
return condition; return condition;
} }
static Expression getRandomExpression(TestSynth config, Command command) { private static Expression getRandomExpression(TestSynth config, Command command) {
Expression expression = new Expression(config, command, false); Expression expression = new Expression(config, command);
String alias = command.getRandomTableAlias(); String alias = command.getRandomTableAlias();
Column column = command.getTable(alias).getRandomConditionColumn(); Column column = command.getTable(alias).getRandomConditionColumn();
if (column == null) { if (column == null) {
...@@ -72,12 +84,27 @@ public class Expression { ...@@ -72,12 +84,27 @@ public class Expression {
sql = v.getSQL(); 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) { 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); expression.createJoinComparison(alias);
return expression; 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) { static String getRandomOrder(TestSynth config, Command command) {
int len = config.random().getLog(6); int len = config.random().getLog(6);
String sql = ""; String sql = "";
...@@ -104,6 +131,11 @@ public class Expression { ...@@ -104,6 +131,11 @@ public class Expression {
return sql; return sql;
} }
/**
* Get the SQL snippet of this expression.
*
* @return the SQL snippet
*/
String getSQL() { String getSQL() {
return sql.trim().length() == 0 ? null : sql.trim(); return sql.trim().length() == 0 ? null : sql.trim();
} }
...@@ -250,11 +282,7 @@ public class Expression { ...@@ -250,11 +282,7 @@ public class Expression {
} }
} }
boolean isEmpty() { private void createExpression(String alias, Column type) {
return sql == null || sql.trim().length() == 0;
}
void createExpression(String alias, Column type) {
boolean op = is(20); boolean op = is(20);
// no null values if there is an operation // no null values if there is an operation
boolean allowNull = !op; boolean allowNull = !op;
...@@ -288,7 +316,7 @@ public class Expression { ...@@ -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(); int dt = type.getType();
if (is(5) && (dt == Types.INTEGER) || (dt == Types.DECIMAL)) { if (is(5) && (dt == Types.INTEGER) || (dt == Types.DECIMAL)) {
sql += " - "; sql += " - ";
......
...@@ -10,10 +10,10 @@ package org.h2.test.synth.sql; ...@@ -10,10 +10,10 @@ package org.h2.test.synth.sql;
* Represents an index. * Represents an index.
*/ */
public class Index { public class Index {
Table table; private Table table;
String name; private String name;
Column[] columns; private Column[] columns;
boolean unique; private boolean unique;
Index(Table table, String name, Column[] columns, boolean unique) { Index(Table table, String name, Column[] columns, boolean unique) {
this.table = table; this.table = table;
...@@ -22,11 +22,11 @@ public class Index { ...@@ -22,11 +22,11 @@ public class Index {
this.unique = unique; this.unique = unique;
} }
public String getName() { String getName() {
return name; return name;
} }
public String getCreateSQL() { String getCreateSQL() {
String sql = "CREATE "; String sql = "CREATE ";
if (unique) { if (unique) {
sql += "UNIQUE "; sql += "UNIQUE ";
...@@ -42,11 +42,11 @@ public class Index { ...@@ -42,11 +42,11 @@ public class Index {
return sql; return sql;
} }
public String getDropSQL() { String getDropSQL() {
return "DROP INDEX " + name; return "DROP INDEX " + name;
} }
public Table getTable() { Table getTable() {
return table; return table;
} }
......
...@@ -128,27 +128,27 @@ class Result implements Comparable { ...@@ -128,27 +128,27 @@ class Result implements Comparable {
} }
} }
public void log() { // public void log() {
switch (type) { // switch (type) {
case SUCCESS: // case SUCCESS:
System.out.println("> ok"); // System.out.println("> ok");
break; // break;
case EXCEPTION: // case EXCEPTION:
System.out.println("> exception"); // System.out.println("> exception");
break; // break;
case INT: // case INT:
if (intValue == 0) { // if (intValue == 0) {
System.out.println("> ok"); // System.out.println("> ok");
} else { // } else {
System.out.println("> update count: " + intValue); // System.out.println("> update count: " + intValue);
} // }
break; // break;
case RESULT_SET: // case RESULT_SET:
System.out.println("> rs " + rows.size()); // System.out.println("> rs " + rows.size());
break; // break;
default: // default:
} // }
System.out.println(); // System.out.println();
} // }
} }
...@@ -24,7 +24,13 @@ class Table { ...@@ -24,7 +24,13 @@ class Table {
this.config = config; 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 table = new Table(config);
table.name = "T_" + config.randomIdentifier(); table.name = "T_" + config.randomIdentifier();
...@@ -52,7 +58,7 @@ class Table { ...@@ -52,7 +58,7 @@ class Table {
Column pk = null; Column pk = null;
do { do {
pk = table.columns[config.random().getInt(len)]; pk = table.columns[config.random().getInt(len)];
} while (pk.isPrimaryKey()); } while (pk.getPrimaryKey());
table.primaryKeys[i] = pk; table.primaryKeys[i] = pk;
pk.setPrimaryKey(true); pk.setPrimaryKey(true);
pk.setNullable(false); pk.setNullable(false);
...@@ -61,7 +67,12 @@ class Table { ...@@ -61,7 +67,12 @@ class Table {
return table; return table;
} }
public Index newRandomIndex() { /**
* Create a new random index.
*
* @return the index
*/
Index newRandomIndex() {
String indexName = "I_" + config.randomIdentifier(); String indexName = "I_" + config.randomIdentifier();
int len = config.random().getLog(getColumnCount() - 1) + 1; int len = config.random().getLog(getColumnCount() - 1) + 1;
boolean unique = config.random().getBoolean(50); boolean unique = config.random().getBoolean(50);
...@@ -70,11 +81,21 @@ class Table { ...@@ -70,11 +81,21 @@ class Table {
return index; return index;
} }
public String getDropSQL() { /**
* Get the DROP TABLE statement for this table.
*
* @return the SQL statement
*/
String getDropSQL() {
return "DROP TABLE " + name; return "DROP TABLE " + name;
} }
public String getCreateSQL() { /**
* Get the CREATE TABLE statement for this table.
*
* @return the SQL statement
*/
String getCreateSQL() {
String sql = "CREATE "; String sql = "CREATE ";
if (temporary) { if (temporary) {
if (globalTemporary) { if (globalTemporary) {
...@@ -111,7 +132,12 @@ class Table { ...@@ -111,7 +132,12 @@ class Table {
return sql; 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; String sql = "INSERT INTO " + name;
if (c != null) { if (c != null) {
sql += "("; sql += "(";
...@@ -134,11 +160,21 @@ class Table { ...@@ -134,11 +160,21 @@ class Table {
return sql; return sql;
} }
public String getName() { /**
* Get the table name.
*
* @return the name
*/
String getName() {
return name; 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(); ArrayList list = new ArrayList();
for (int i = 0; i < columns.length; i++) { for (int i = 0; i < columns.length; i++) {
if (Column.isConditionType(config, columns[i].getType())) { if (Column.isConditionType(config, columns[i].getType())) {
...@@ -151,15 +187,21 @@ class Table { ...@@ -151,15 +187,21 @@ class Table {
return (Column) list.get(config.random().getInt(list.size())); return (Column) list.get(config.random().getInt(list.size()));
} }
public Column getRandomColumn() { Column getRandomColumn() {
return columns[config.random().getInt(columns.length)]; return columns[config.random().getInt(columns.length)];
} }
public int getColumnCount() { int getColumnCount() {
return columns.length; 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(); ArrayList list = new ArrayList();
for (int i = 0; i < columns.length; i++) { for (int i = 0; i < columns.length; i++) {
if (columns[i].getType() == type) { if (columns[i].getType() == type) {
...@@ -172,7 +214,13 @@ class Table { ...@@ -172,7 +214,13 @@ class Table {
return (Column) list.get(config.random().getInt(list.size())); 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]; int[] index = new int[columns.length];
for (int i = 0; i < columns.length; i++) { for (int i = 0; i < columns.length; i++) {
index[i] = i; index[i] = i;
...@@ -190,15 +238,25 @@ class Table { ...@@ -190,15 +238,25 @@ class Table {
return c; return c;
} }
public Column[] getColumns() { Column[] getColumns() {
return columns; 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); 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); indexes.remove(index);
} }
} }
...@@ -21,7 +21,30 @@ public class TestSynth extends TestBase { ...@@ -21,7 +21,30 @@ public class TestSynth extends TestBase {
// TODO hsqldb: call 1||null should return 1 but returns null // TODO hsqldb: call 1||null should return 1 but returns null
// TODO hsqldb: call mod(1) should return invalid parameter count // 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 static final String DIR = "synth";
private DbState db = new DbState(this); private DbState db = new DbState(this);
...@@ -32,14 +55,30 @@ public class TestSynth extends TestBase { ...@@ -32,14 +55,30 @@ public class TestSynth extends TestBase {
private boolean stopImmediately; private boolean stopImmediately;
private int mode; 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) { boolean is(int isType) {
return mode == isType; return mode == isType;
} }
/**
* Get the random number generator.
*
* @return the random number generator
*/
RandomGen random() { RandomGen random() {
return random; return random;
} }
/**
* Get a random identifier.
*
* @return the random identifier
*/
String randomIdentifier() { String randomIdentifier() {
int len = random.getLog(8) + 2; int len = random.getLog(8) + 2;
while (true) { while (true) {
...@@ -193,17 +232,28 @@ public class TestSynth extends TestBase { ...@@ -193,17 +232,28 @@ public class TestSynth extends TestBase {
} }
} }
/**
* Get a random table.
*
* @return the table
*/
Table randomTable() { Table randomTable() {
return db.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) { void log(int id, String s) {
if (showLog && id == 0) { if (showLog && id == 0) {
System.out.println(s); System.out.println(s);
} }
} }
public int getMode() { int getMode() {
return mode; return mode;
} }
......
...@@ -31,6 +31,11 @@ public class Value { ...@@ -31,6 +31,11 @@ public class Value {
this.data = data; this.data = data;
} }
/**
* Convert the value to a SQL string.
*
* @return the SQL string
*/
String getSQL() { String getSQL() {
if (data == null) { if (data == null) {
return "NULL"; return "NULL";
...@@ -127,6 +132,14 @@ public class Value { ...@@ -127,6 +132,14 @@ public class Value {
return buff.toString(); 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 { static Value read(TestSynth config, ResultSet rs, int index) throws SQLException {
ResultSetMetaData meta = rs.getMetaData(); ResultSetMetaData meta = rs.getMetaData();
Object data; Object data;
...@@ -184,6 +197,16 @@ public class Value { ...@@ -184,6 +197,16 @@ public class Value {
return new Value(config, type, data); 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) { static Value getRandom(TestSynth config, int type, int precision, int scale, boolean mayBeNull) {
Object data; Object data;
if (mayBeNull && config.random().getBoolean(20)) { if (mayBeNull && config.random().getBoolean(20)) {
...@@ -263,48 +286,49 @@ public class Value { ...@@ -263,48 +286,49 @@ public class Value {
return new BigDecimal(buff.toString()); return new BigDecimal(buff.toString());
} }
int compareTo(Object o) { // private int compareTo(Object o) {
Value v = (Value) o; // Value v = (Value) o;
if (type != v.type) { // if (type != v.type) {
throw new Error("compare " + type + " " + v.type + " " + data + " " + v.data); // throw new Error("compare " + type +
} // " " + v.type + " " + data + " " + v.data);
if (data == null) { // }
return (v.data == null) ? 0 : -1; // if (data == null) {
} else if (v.data == null) { // return (v.data == null) ? 0 : -1;
return 1; // } else if (v.data == null) {
} // return 1;
switch (type) { // }
case Types.DECIMAL: // switch (type) {
return ((BigDecimal) data).compareTo((BigDecimal) v.data); // case Types.DECIMAL:
case Types.BLOB: // return ((BigDecimal) data).compareTo((BigDecimal) v.data);
case Types.VARBINARY: // case Types.BLOB:
case Types.BINARY: // case Types.VARBINARY:
return compareBytes((byte[]) data, (byte[]) v.data); // case Types.BINARY:
case Types.CLOB: // return compareBytes((byte[]) data, (byte[]) v.data);
case Types.VARCHAR: // case Types.CLOB:
return data.toString().compareTo(v.data.toString()); // case Types.VARCHAR:
case Types.DATE: // return data.toString().compareTo(v.data.toString());
return ((Date) data).compareTo((Date) v.data); // case Types.DATE:
case Types.INTEGER: // return ((Date) data).compareTo((Date) v.data);
return ((Integer) data).compareTo((Integer) v.data); // case Types.INTEGER:
default: // return ((Integer) data).compareTo((Integer) v.data);
throw new Error("type=" + type); // default:
} // throw new Error("type=" + type);
} // }
// }
static int compareBytes(byte[] a, byte[] b) { // private static int compareBytes(byte[] a, byte[] b) {
int al = a.length, bl = b.length; // int al = a.length, bl = b.length;
int len = Math.min(al, bl); // int len = Math.min(al, bl);
for (int i = 0; i < len; i++) { // for (int i = 0; i < len; i++) {
int x = a[i] & 0xff; // int x = a[i] & 0xff;
int y = b[i] & 0xff; // int y = b[i] & 0xff;
if (x == y) { // if (x == y) {
continue; // continue;
} // }
return x > y ? 1 : -1; // return x > y ? 1 : -1;
} // }
return al == bl ? 0 : al > bl ? 1 : -1; // return al == bl ? 0 : al > bl ? 1 : -1;
} // }
public String toString() { public String toString() {
return getSQL(); return getSQL();
......
...@@ -16,25 +16,51 @@ import org.h2.test.TestBase; ...@@ -16,25 +16,51 @@ import org.h2.test.TestBase;
*/ */
abstract class TestMultiThread extends Thread { abstract class TestMultiThread extends Thread {
/**
* The base object.
*/
TestMulti base; TestMulti base;
/**
* The random number generator.
*/
Random random = new Random(); Random random = new Random();
TestMultiThread(TestMulti base) { TestMultiThread(TestMulti base) {
this.base = 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; abstract void first() throws SQLException;
/**
* The main operation to perform. This method is called in a loop.
*/
abstract void operation() throws SQLException; abstract void operation() throws SQLException;
/**
* Execute statements before entering the loop, but after starting the
* thread.
*/
abstract void begin() throws SQLException; abstract void begin() throws SQLException;
/**
* This method is called once after the test is stopped.
*/
abstract void end() throws SQLException; abstract void end() throws SQLException;
/**
* This method is called once after all threads have been stopped.
* @throws Exception
*/
abstract void finalTest() throws Exception; abstract void finalTest() throws Exception;
public void run() { public void run() {
try { try {
begin();
while (!base.stop) { while (!base.stop) {
operation(); operation();
} }
......
...@@ -49,6 +49,9 @@ class Arg { ...@@ -49,6 +49,9 @@ class Arg {
return quote(clazz, getValue()); return quote(clazz, getValue());
} }
/**
* Calculate the value if this is a statement.
*/
void execute() throws Exception { void execute() throws Exception {
if (stat != null) { if (stat != null) {
obj = stat.execute(); obj = stat.execute();
...@@ -65,7 +68,7 @@ class Arg { ...@@ -65,7 +68,7 @@ class Arg {
return obj; return obj;
} }
String quote(Class clazz, Object value) { private String quote(Class clazz, Object value) {
if (value == null) { if (value == null) {
return null; return null;
} else if (clazz == String.class) { } else if (clazz == String.class) {
......
...@@ -45,6 +45,13 @@ class Parser { ...@@ -45,6 +45,13 @@ class Parser {
read(); read();
} }
/**
* Parse a Java statement.
*
* @param player the player
* @param line the statement text
* @return the statement
*/
static Statement parseStatement(Player player, String line) { static Statement parseStatement(Player player, String line) {
Parser p = new Parser(player, line); Parser p = new Parser(player, line);
p.parseStatement(); p.parseStatement();
......
...@@ -111,6 +111,11 @@ public class Player { ...@@ -111,6 +111,11 @@ public class Player {
} }
} }
/**
* Write trace information if trace is enabled.
*
* @param s the message to write
*/
void trace(String s) { void trace(String s) {
if (trace) { if (trace) {
System.out.println(s); System.out.println(s);
...@@ -131,7 +136,14 @@ public class Player { ...@@ -131,7 +136,14 @@ public class Player {
trace("error: " + e.toString()); trace("error: " + e.toString());
} }
} }
/**
* 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) { static Class getClass(String className) {
for (int i = 0; i < IMPORTED_PACKAGES.length; i++) { for (int i = 0; i < IMPORTED_PACKAGES.length; i++) {
try { try {
...@@ -143,10 +155,22 @@ public class Player { ...@@ -143,10 +155,22 @@ public class Player {
throw new Error("Class not found: " + className); 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) { Object getObject(String name) {
return objects.get(name); return objects.get(name);
} }
......
...@@ -45,6 +45,11 @@ class Statement { ...@@ -45,6 +45,11 @@ class Statement {
this.player = player; this.player = player;
} }
/**
* Execute the statement.
*
* @return the object returned if this was a method call
*/
Object execute() throws Exception { Object execute() throws Exception {
if (object == player) { if (object == player) {
// there was an exception previously // there was an exception previously
...@@ -120,19 +125,38 @@ class Statement { ...@@ -120,19 +125,38 @@ class Statement {
return returnClass; 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) { void setAssign(String className, String variableName) {
this.assignment = true; this.assignment = true;
this.assignClass = className; this.assignClass = className;
this.assignVariable = variableName; this.assignVariable = variableName;
} }
/**
* This statement is a static method call.
*
* @param className the class name
*/
void setStaticCall(String className) { void setStaticCall(String className) {
this.staticCall = true; this.staticCall = true;
this.staticCallClass = className; 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.object = object;
this.methodName = methodName; this.methodName = methodName;
} }
......
...@@ -39,6 +39,12 @@ public class FtpClient { ...@@ -39,6 +39,12 @@ public class FtpClient {
// don't allow construction // 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 { static FtpClient open(String url) throws IOException {
FtpClient client = new FtpClient(); FtpClient client = new FtpClient();
client.connect(url); client.connect(url);
...@@ -77,6 +83,12 @@ public class FtpClient { ...@@ -77,6 +83,12 @@ public class FtpClient {
writer.flush(); 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 { void login(String userName, String password) throws IOException {
send("USER " + userName); send("USER " + userName);
readCode(331); readCode(331);
...@@ -92,6 +104,9 @@ public class FtpClient { ...@@ -92,6 +104,9 @@ public class FtpClient {
readCode(200); readCode(200);
} }
/**
* Close the connection (QUIT).
*/
void close() throws IOException { void close() throws IOException {
if (socket != null) { if (socket != null) {
send("QUIT"); send("QUIT");
...@@ -100,42 +115,75 @@ public class FtpClient { ...@@ -100,42 +115,75 @@ public class FtpClient {
} }
} }
/**
* Change the working directory (CWD).
*
* @param dir the new directory
*/
void changeWorkingDirectory(String dir) throws IOException { void changeWorkingDirectory(String dir) throws IOException {
send("CWD " + dir); send("CWD " + dir);
readCode(250); readCode(250);
} }
/**
* Change to the parent directory (CDUP).
*/
void changeDirectoryUp() throws IOException { void changeDirectoryUp() throws IOException {
send("CDUP"); send("CDUP");
readCode(250); readCode(250);
} }
/**
* Delete a file (DELE).
*
* @param fileName the name of the file to delete
*/
void delete(String fileName) throws IOException { void delete(String fileName) throws IOException {
send("DELE " + fileName); send("DELE " + fileName);
readCode(250); readCode(250);
} }
/**
* Create a directory (MKD).
*
* @param dir the directory to create
*/
void makeDirectory(String dir) throws IOException { void makeDirectory(String dir) throws IOException {
send("MKD " + dir); send("MKD " + dir);
readCode(257); readCode(257);
} }
/**
* Change the transfer mode (MODE).
*
* @param mode the mode
*/
void mode(String mode) throws IOException { void mode(String mode) throws IOException {
send("MODE " + mode); send("MODE " + mode);
readCode(200); readCode(200);
} }
/**
* Change the modified time of a file (MDTM).
*
* @param fileName the file name
*/
void modificationTime(String fileName) throws IOException { void modificationTime(String fileName) throws IOException {
send("MDTM " + fileName); send("MDTM " + fileName);
readCode(213); readCode(213);
} }
/**
* Issue a no-operation statement (NOOP).
*/
void noOperation() throws IOException { void noOperation() throws IOException {
send("NOOP"); send("NOOP");
readCode(200); readCode(200);
} }
/**
* Print the working directory (PWD).
*/
String printWorkingDirectory() throws IOException { String printWorkingDirectory() throws IOException {
send("PWD"); send("PWD");
readCode(257); readCode(257);
...@@ -177,6 +225,12 @@ public class FtpClient { ...@@ -177,6 +225,12 @@ public class FtpClient {
outData = socketData.getOutputStream(); 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 { void rename(String fromFileName, String toFileName) throws IOException {
send("RNFR " + fromFileName); send("RNFR " + fromFileName);
readCode(350); readCode(350);
...@@ -184,6 +238,13 @@ public class FtpClient { ...@@ -184,6 +238,13 @@ public class FtpClient {
readCode(250); 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 { void retrieve(String fileName, OutputStream out, long restartAt) throws IOException {
passive(); passive();
if (restartAt > 0) { if (restartAt > 0) {
...@@ -195,11 +256,22 @@ public class FtpClient { ...@@ -195,11 +256,22 @@ public class FtpClient {
readCode(226); readCode(226);
} }
/**
* Remove a directory (RMD).
*
* @param dir the directory to remove
*/
void removeDirectory(String dir) throws IOException { void removeDirectory(String dir) throws IOException {
send("RMD " + dir); send("RMD " + dir);
readCode(250); readCode(250);
} }
/**
* Get the size of a file (SIZE).
*
* @param fileName the file name
* @return the size
*/
long size(String fileName) throws IOException { long size(String fileName) throws IOException {
send("SIZE " + fileName); send("SIZE " + fileName);
readCode(250); readCode(250);
...@@ -207,6 +279,12 @@ public class FtpClient { ...@@ -207,6 +279,12 @@ public class FtpClient {
return size; return size;
} }
/**
* Store a file (STOR).
*
* @param fileName the file name
* @param in the input stream
*/
void store(String fileName, InputStream in) throws IOException { void store(String fileName, InputStream in) throws IOException {
passive(); passive();
send("STOR " + fileName); send("STOR " + fileName);
...@@ -215,6 +293,12 @@ public class FtpClient { ...@@ -215,6 +293,12 @@ public class FtpClient {
readCode(226); readCode(226);
} }
/**
* Get the directory listing (NLST).
*
* @param dir the directory
* @return the listing
*/
String nameList(String dir) throws IOException { String nameList(String dir) throws IOException {
passive(); passive();
send("NLST " + dir); send("NLST " + dir);
...@@ -226,6 +310,12 @@ public class FtpClient { ...@@ -226,6 +310,12 @@ public class FtpClient {
return new String(data); return new String(data);
} }
/**
* Get the directory listing (LIST).
*
* @param dir the directory
* @return the listing
*/
String list(String dir) throws IOException { String list(String dir) throws IOException {
passive(); passive();
send("LIST " + dir); send("LIST " + dir);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论