提交 211fbd5d authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 d44bff9d
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.*;
class Column {
private TestSynth config;
private String name;
private int type;
private int precision;
private int scale;
private boolean isNullable;
private boolean isPrimaryKey;
// TODO test isAutoincrement;
private static int[] TYPES = {
Types.INTEGER,
Types.VARCHAR,
Types.DECIMAL,
Types.DATE,
Types.TIME,
Types.TIMESTAMP,
Types.BOOLEAN,
Types.BINARY,
Types.VARBINARY,
Types.CLOB,
Types.BLOB,
Types.DOUBLE,
Types.BIGINT,
Types.TIMESTAMP,
Types.BIT,
Types.BOOLEAN,
};
Column(TestSynth config) {
this.config = config;
}
Column(ResultSetMetaData meta, int index) throws SQLException {
name = meta.getColumnLabel(index);
type = meta.getColumnType(index);
switch(type) {
case Types.DECIMAL:
precision = meta.getPrecision(index);
scale = meta.getScale(index);
break;
case Types.BLOB:
case Types.BINARY:
case Types.VARBINARY:
case Types.CLOB:
case Types.LONGVARCHAR:
case Types.DATE:
case Types.TIME:
case Types.INTEGER:
case Types.VARCHAR:
case Types.CHAR:
case Types.BIGINT:
case Types.NUMERIC:
case Types.TIMESTAMP:
case Types.NULL:
case Types.LONGVARBINARY:
case Types.DOUBLE:
case Types.REAL:
case Types.OTHER:
case Types.BIT:
case Types.BOOLEAN:
break;
default:
throw new Error("type="+type);
}
}
public static boolean isConditionType(TestSynth config, int type) {
switch(config.getMode()) {
case TestSynth.H2:
case TestSynth.H2_MEM:
return true;
case TestSynth.MYSQL:
case TestSynth.HSQLDB:
case TestSynth.POSTGRESQL:
switch(type) {
case Types.INTEGER:
case Types.VARCHAR:
case Types.DECIMAL:
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
case Types.DOUBLE:
case Types.BIGINT:
case Types.BOOLEAN:
case Types.BIT:
return true;
case Types.BINARY:
case Types.VARBINARY:
case Types.BLOB:
case Types.CLOB:
case Types.LONGVARCHAR:
case Types.LONGVARBINARY:
return false;
default:
throw new Error("type="+type);
}
default:
throw new Error("type="+type);
}
}
String getTypeName() {
switch(type) {
case Types.INTEGER:
return "INT";
case Types.VARCHAR:
return "VARCHAR("+precision+")";
case Types.DECIMAL:
return "NUMERIC("+precision+", "+scale+")";
case Types.DATE:
return "DATE";
case Types.TIME:
return "TIME";
case Types.TIMESTAMP:
return "TIMESTAMP";
case Types.BINARY:
case Types.VARBINARY:
if(config.is(TestSynth.POSTGRESQL)) {
return "BYTEA";
}
return "BINARY("+precision+")";
case Types.CLOB: {
if(config.is(TestSynth.HSQLDB)) {
return "LONGVARCHAR";
} else if(config.is(TestSynth.POSTGRESQL)) {
return "TEXT";
}
return "CLOB";
}
case Types.BLOB: {
if(config.is(TestSynth.HSQLDB)) {
return "LONGVARBINARY";
}
return "BLOB";
}
case Types.DOUBLE:
if(config.is(TestSynth.POSTGRESQL)) {
return "DOUBLE PRECISION";
}
return "DOUBLE";
case Types.BIGINT:
return "BIGINT";
case Types.BOOLEAN:
case Types.BIT:
return "BOOLEAN";
default:
throw new Error("type="+type);
}
}
public String getCreateSQL() {
String sql = name + " " + getTypeName();
if(!isNullable) {
sql += " NOT NULL";
}
return sql;
}
public String getName() {
return name;
}
public Value getRandomValue() {
return Value.getRandom(config, type, precision, scale, isNullable);
}
public Value getRandomValueNotNull() {
return Value.getRandom(config, type, precision, scale, false);
}
public static Column getRandomColumn(TestSynth config) {
Column column = new Column(config);
column.name = "C_" + config.randomIdentifier();
int randomType;
while(true) {
randomType = TYPES[config.random().getLog(TYPES.length)];
if(config.is(TestSynth.POSTGRESQL) && (randomType == Types.BINARY || randomType == Types.VARBINARY || randomType == Types.BLOB)) {
continue;
}
break;
}
column.type = randomType;
column.precision = config.random().getInt(20)+2;
column.scale = config.random().getInt(column.precision);
column.isNullable = config.random().getBoolean(50);
return column;
}
public boolean isPrimaryKey() {
return isPrimaryKey;
}
public void setPrimaryKey(boolean b) {
isPrimaryKey = b;
}
public void setNullable(boolean b) {
isNullable = b;
}
public int getType() {
return type;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.SQLException;
import java.util.HashMap;
class Command {
TestSynth config;
static final int CONNECT = 0, RESET = 1, DISCONNECT = 2,
CREATE_TABLE = 3, INSERT = 4, DROP_TABLE = 5,
SELECT = 6, DELETE = 7, UPDATE = 8, COMMIT = 9, ROLLBACK = 10,
AUTOCOMMIT_ON = 11, AUTOCOMMIT_OFF = 12,
CREATE_INDEX = 13, DROP_INDEX = 14, END = 15;
private int type;
private Table table;
private HashMap tables;
private Index index;
private Column[] columns;
private Value[] values;
private String condition;
private int nextAlias;
private String order;
String[] selectList;
private String join = "";
private Result result;
static Command getDropTable(TestSynth config, Table table) {
return new Command(config, Command.DROP_TABLE, table);
}
Command getCommit(TestSynth config) {
return new Command(config, Command.COMMIT);
}
Command getRollback(TestSynth config) {
return new Command(config, Command.ROLLBACK);
}
Command getSetAutoCommit(TestSynth config, boolean auto) {
int type = auto ? Command.AUTOCOMMIT_ON : Command.AUTOCOMMIT_OFF;
return new Command(config, type);
}
static Command getConnect(TestSynth config) {
return new Command(config, CONNECT);
}
static Command getReset(TestSynth config) {
return new Command(config, RESET);
}
static Command getDisconnect(TestSynth config) {
return new Command(config, DISCONNECT);
}
static Command getEnd(TestSynth config) {
return new Command(config, END);
}
static Command getCreateTable(TestSynth config, Table table) {
return new Command(config, CREATE_TABLE, table);
}
static Command getCreateIndex(TestSynth config, Index index) {
return new Command(config, CREATE_INDEX, index);
}
static Command getRandomSelect(TestSynth config, Table table) {
Command command = new Command(config, Command.SELECT, table, "M");
command.selectList = Expression.getRandomSelectList(config, command);
// TODO group by, having, joins
command.condition = Expression.getRandomCondition(config, command).getSQL();
command.order = Expression.getRandomOrder(config, command);
return command;
}
static Command getRandomSelectJoin(TestSynth config, Table table) {
Command command = new Command(config, Command.SELECT, table, "M");
int len = config.random().getLog(5)+1;
String globalJoinCondition = "";
for(int i=0; i<len; i++) {
Table t2 = config.randomTable();
String alias = "J" + i;
command.addSubqueryTable(alias, t2);
Expression joinOn = Expression.getRandomJoinOn(config, command, alias);
if(config.random().getBoolean(50)) {
// regular join
if(globalJoinCondition.length()>0) {
globalJoinCondition += " AND ";
}
globalJoinCondition += " (" + joinOn.getSQL() + ") ";
command.addJoin(", "+t2.getName()+" "+alias);
} else {
String join = " JOIN "+t2.getName()+" "+alias+" ON "+joinOn.getSQL();
if(config.random().getBoolean(20)) {
command.addJoin(" LEFT OUTER"+join);
} else {
command.addJoin(" INNER"+join);
}
}
}
command.selectList = Expression.getRandomSelectList(config, command);
// TODO group by, having
String cond = Expression.getRandomCondition(config, command).getSQL();
if(globalJoinCondition.length()>0) {
if(cond!=null) {
cond = "(" + globalJoinCondition + " ) AND (" + cond + ")";
} else {
cond = globalJoinCondition;
}
}
command.condition = cond;
command.order = Expression.getRandomOrder(config, command);
return command;
}
static Command getRandomDelete(TestSynth config, Table table) {
Command command = new Command(config, Command.DELETE, table);
command.condition = Expression.getRandomCondition(config, command).getSQL();
return command;
}
static Command getRandomUpdate(TestSynth config, Table table) {
Command command = new Command(config, Command.UPDATE, table);
command.prepareUpdate();
return command;
}
static Command getRandomInsert(TestSynth config, Table table) {
Command command = new Command(config, Command.INSERT, table);
command.prepareInsert();
return command;
}
private Command(TestSynth config, int type) {
this.config = config;
this.type = type;
}
private Command(TestSynth config, int type, Table table) {
this.config = config;
this.type = type;
this.table = table;
}
private Command(TestSynth config, int type, Table table, String alias) {
this.config = config;
this.type = type;
this.table = table;
this.tables = new HashMap();
this.tables.put(alias, table);
}
private Command(TestSynth config, int type, Index index) {
this.config = config;
this.type = type;
this.index = index;
}
Command(int type, String alias, Table table) {
this.type = type;
if(alias == null) {
alias = table.getName();
}
addSubqueryTable(alias, table);
this.table = table;
}
void addSubqueryTable(String alias, Table t) {
tables.put(alias, t);
}
void removeSubqueryTable(String alias) {
tables.remove(alias);
}
void prepareInsert() {
Column[] c;
if(config.random().getBoolean(70)) {
c = table.getColumns();
} else {
int len = config.random().getInt(table.getColumnCount()-1)+1;
c = columns = table.getRandomColumns(len);
}
values = new Value[c.length];
for(int i=0; i<c.length; i++) {
values[i] = c[i].getRandomValue();
}
}
void prepareUpdate() {
int len = config.random().getLog(table.getColumnCount()-1)+1;
Column[] c = columns = table.getRandomColumns(len);
values = new Value[c.length];
for(int i=0; i<c.length; i++) {
values[i] = c[i].getRandomValue();
}
condition = Expression.getRandomCondition(config, this).getSQL();
}
private Result select(DbInterface db) throws SQLException {
String sql = "SELECT ";
for(int i=0; i<selectList.length; i++) {
if(i>0) {
sql += ", ";
}
sql += selectList[i];
}
sql += " FROM " + table.getName() + " M";
sql += " "+join;
if(condition!=null) {
sql += " WHERE " + condition;
}
if(order.trim().length()>0) {
sql += " ORDER BY " + order;
}
return db.select(sql);
}
Result run(DbInterface db) throws Exception {
try {
switch (type) {
case CONNECT:
db.connect();
result = new Result("connect");
break;
case RESET:
db.reset();
result = new Result("reset");
break;
case DISCONNECT:
db.disconnect();
result = new Result("disconnect");
break;
case END:
db.end();
result = new Result("disconnect");
break;
case CREATE_TABLE:
db.createTable(table);
result = new Result("createTable");
break;
case DROP_TABLE:
db.dropTable(table);
result = new Result("dropTable");
break;
case CREATE_INDEX:
db.createIndex(index);
result = new Result("createIndex");
break;
case DROP_INDEX:
db.dropIndex(index);
result = new Result("dropIndex");
break;
case INSERT:
result = db.insert(table, columns, values);
break;
case SELECT:
result = select(db);
break;
case DELETE:
result = db.delete(table, condition);
break;
case UPDATE:
result = db.update(table, columns, values, condition);
break;
case AUTOCOMMIT_ON:
db.setAutoCommit(true);
result = new Result("setAutoCommit true");
break;
case AUTOCOMMIT_OFF:
db.setAutoCommit(false);
result = new Result("setAutoCommit false");
break;
case COMMIT:
db.commit();
result = new Result("commit");
break;
case ROLLBACK:
db.rollback();
result = new Result("rollback");
break;
default:
throw new Error("internal");
}
} catch (SQLException e) {
result = new Result("", e);
}
return result;
}
public String getNextTableAlias() {
return "S" + nextAlias++;
}
public String getRandomTableAlias() {
if(tables == null) {
return null;
}
Object[] list = tables.keySet().toArray();
int i = config.random().getInt(list.length);
return (String)list[i];
}
public Table getTable(String alias) {
if(alias == null) {
return table;
}
return (Table)tables.get(alias);
}
public void addJoin(String string) {
join += string;
}
static Command getSelectAll(TestSynth config, Table table) {
Command command = new Command(config, Command.SELECT, table, "M");
command.selectList = new String[]{"*"};
command.order = "";
return command;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
class DbConnection implements DbInterface {
private TestSynth config;
private int id;
private String driver;
private String url;
private String user;
private String password;
private Connection conn;
private Connection sentinel;
private boolean useSentinel;
DbConnection(TestSynth config, String driver, String url, String user, String password, int id, boolean useSentinel) {
this.config = config;
this.driver = driver;
this.url = url;
this.user = user;
this.password = password;
this.id = id;
this.useSentinel = useSentinel;
log("url="+url);
}
public void reset() throws SQLException {
log("reset;");
DatabaseMetaData meta = conn.getMetaData();
Statement stat = conn.createStatement();
ArrayList tables = new ArrayList();
ResultSet rs = meta.getTables(null, null, null, new String[] { "TABLE"});
while (rs.next()) {
String schemaName = rs.getString("TABLE_SCHEM");
if(!"INFORMATION_SCHEMA".equals(schemaName)) {
tables.add(rs.getString("TABLE_NAME"));
}
}
while (tables.size() > 0) {
int dropped = 0;
for (int i = 0; i < tables.size(); i++) {
try {
String table = (String) tables.get(i);
stat.execute("DROP TABLE " + table);
dropped++;
tables.remove(i);
i--;
} catch (SQLException e) {
// maybe a referencial integrity
}
}
// could not drop any table and still tables to drop
if (dropped == 0 && tables.size() > 0) {
throw new Error("Cannot drop "+tables);
}
}
}
public void connect() throws Exception {
if(useSentinel && sentinel == null) {
sentinel = getConnection();
}
log("connect to "+url+";");
conn = getConnection();
}
private Connection getConnection() throws Exception {
log("(getConnection to "+url+");");
if(driver==null) {
return config.getConnection("synth");
} else {
Class.forName(driver);
return DriverManager.getConnection(url, user, password);
}
}
public void disconnect() throws SQLException {
log("disconnect "+url+";");
conn.close();
}
public void end() throws SQLException {
log("end "+url+";");
if(sentinel != null) {
sentinel.close();
sentinel = null;
}
}
public void createTable(Table table) throws SQLException {
execute(table.getCreateSQL());
}
public void dropTable(Table table) throws SQLException {
execute(table.getDropSQL());
}
public void createIndex(Index index) throws SQLException {
execute(index.getCreateSQL());
index.getTable().addIndex(index);
}
public void dropIndex(Index index) throws SQLException {
execute(index.getDropSQL());
index.getTable().removeIndex(index);
}
public Result insert(Table table, Column[] c, Value[] v) throws SQLException {
String sql = table.getInsertSQL(c, v);
execute(sql);
return new Result(sql, 1);
}
private void execute(String sql) throws SQLException {
log(sql+";");
conn.createStatement().execute(sql);
}
public Result select(String sql) throws SQLException {
log(sql+";");
Statement stat = conn.createStatement();
Result result = new Result(config, sql, stat.executeQuery(sql));
return result;
}
public Result delete(Table table, String condition) throws SQLException {
String sql = "DELETE FROM " + table.getName();
if(condition!=null) {
sql += " WHERE " + condition;
}
log(sql+";");
Statement stat = conn.createStatement();
Result result = new Result(sql, stat.executeUpdate(sql));
return result;
}
public Result update(Table table, Column[] columns, Value[] values, String condition) throws SQLException {
String sql = "UPDATE " + table.getName() + " SET ";
for(int i=0; i<columns.length; i++) {
if(i>0) {
sql += ", ";
}
sql += columns[i].getName() + "=" + values[i].getSQL();
}
if(condition!=null) {
sql += " WHERE " + condition;
}
log(sql+";");
Statement stat = conn.createStatement();
Result result = new Result(sql, stat.executeUpdate(sql));
return result;
}
public void setAutoCommit(boolean b) throws SQLException {
log("set autoCommit " + b+";");
conn.setAutoCommit(b);
}
public void commit() throws SQLException {
log("commit;");
conn.commit();
}
public void rollback() throws SQLException {
log("rollback;");
conn.rollback();
}
private void log(String s) {
config.log(id, s);
}
public String toString() {
return url;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.SQLException;
public interface DbInterface {
void reset() throws SQLException;
void connect() throws Exception;
void disconnect() throws SQLException;
void end() throws SQLException;
void createTable(Table table) throws SQLException;
void dropTable(Table table) throws SQLException;
void createIndex(Index index) throws SQLException;
void dropIndex(Index index) throws SQLException;
Result insert(Table table, Column[] c, Value[] v) throws SQLException;
Result select(String sql) throws SQLException;
Result delete(Table table, String condition) throws SQLException;
Result update(Table table, Column[] columns, Value[] values, String condition) throws SQLException;
void setAutoCommit(boolean b) throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.SQLException;
import java.util.ArrayList;
public class DbState implements DbInterface {
private TestSynth config;
private ArrayList tables = new ArrayList();
private ArrayList indexes = new ArrayList();
boolean connected;
boolean autoCommit;
DbState(TestSynth config) {
this.config = config;
}
public void reset() throws SQLException {
tables = new ArrayList();
indexes = new ArrayList();
}
public void connect() throws SQLException {
connected = true;
}
public void disconnect() throws SQLException {
connected = false;
}
public void createTable(Table table) throws SQLException {
tables.add(table);
}
public void dropTable(Table table) throws SQLException {
tables.remove(table);
}
public void createIndex(Index index) throws SQLException {
indexes.add(index);
}
public void dropIndex(Index index) throws SQLException {
indexes.remove(index);
}
public Result insert(Table table, Column[] c, Value[] v) throws SQLException {
return null;
}
public Result select(String sql) throws SQLException {
return null;
}
public Result delete(Table table, String condition) throws SQLException {
return null;
}
public Result update(Table table, Column[] columns, Value[] values, String condition) {
return null;
}
public void setAutoCommit(boolean b) throws SQLException {
autoCommit = b;
}
public void commit() throws SQLException {
}
public void rollback() throws SQLException {
}
public Table randomTable() {
if(tables.size() == 0) {
return null;
}
int i = config.random().getInt(tables.size());
return (Table) tables.get(i);
}
public void end() throws SQLException {
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Types;
import java.util.ArrayList;
public class Expression {
boolean isCondition;
private String sql;
private TestSynth config;
private Command command;
public static String[] getRandomSelectList(TestSynth config, Command command) {
if (config.random().getBoolean(30)) {
return new String[] { "*" };
}
ArrayList exp = new ArrayList();
String sql = "";
if (config.random().getBoolean(10)) {
sql += "DISTINCT ";
}
int len = config.random().getLog(8) + 1;
for (int i = 0; i < len; i++) {
sql += getRandomExpression(config, command).getSQL();
sql += " AS A" + i + " ";
exp.add(sql);
sql = "";
}
String[] list = new String[exp.size()];
exp.toArray(list);
return list;
}
public static Expression getRandomCondition(TestSynth config, Command command) {
Expression condition = new Expression(config, command, true);
if (config.random().getBoolean(50)) {
condition.create();
}
return condition;
}
public static Expression getRandomExpression(TestSynth config, Command command) {
Expression expression = new Expression(config, command, false);
String alias = command.getRandomTableAlias();
Column column = command.getTable(alias).getRandomConditionColumn();
if(column == null) {
expression.createValue();
} else {
expression.createExpression(alias, column);
}
return expression;
}
private void createValue() {
Value v = Column.getRandomColumn(config).getRandomValue();
sql = v.getSQL();
}
public static Expression getRandomJoinOn(TestSynth config, Command command, String alias) {
Expression expression = new Expression(config, command, true);
expression.createJoinComparison(alias);
return expression;
}
public static String getRandomOrder(TestSynth config, Command command) {
int len = config.random().getLog(6);
String sql = "";
for (int i = 0; i < len; i++) {
if (i > 0) {
sql += ", ";
}
int max = command.selectList.length;
int idx = config.random().getInt(max);
// sql += getRandomExpression(command).getSQL();
// if (max > 1 && config.random().getBoolean(50)) {
sql += "A" + idx;
// } else {
// sql += String.valueOf(idx + 1);
// }
if (config.random().getBoolean(50)) {
if (config.random().getBoolean(10)) {
sql += " ASC";
} else {
sql += " DESC";
}
}
}
return sql;
}
public String getSQL() {
return sql.trim().length() == 0 ? null : sql.trim();
}
private Expression(TestSynth config, Command command, boolean isCondition) {
this.config = config;
this.isCondition = isCondition;
this.command = command;
sql = "";
}
private boolean is(int percent) {
return config.random().getBoolean(percent);
}
private String oneOf(String[] list) {
int i = config.random().getInt(list.length);
if (!sql.endsWith(" ")) {
sql += " ";
}
sql += list[i] + " ";
return list[i];
}
private String getColumnName(String alias, Column column) {
if (alias == null) {
return column.getName();
}
return alias + "." + column.getName();
}
private void createJoinComparison(String alias) {
int len = config.random().getLog(5) + 1;
for (int i = 0; i < len; i++) {
if (i > 0) {
sql += "AND ";
}
Column column = command.getTable(alias).getRandomConditionColumn();
if(column==null) {
sql += "1=1";
return;
}
sql += getColumnName(alias, column);
sql += "=";
String a2;
do {
a2 = command.getRandomTableAlias();
} while (a2.equals(alias));
Table t2 = command.getTable(a2);
Column c2 = t2.getRandomColumnOfType(column.getType());
if (c2 == null) {
sql += column.getRandomValue().getSQL();
} else {
sql += getColumnName(a2, c2);
}
sql += " ";
}
}
private void create() {
createComparison();
while (is(50)) {
oneOf(new String[] { "AND", "OR" });
createComparison();
}
}
// private void createSubquery() {
//// String alias = command.getRandomTableAlias();
//// Table t1 = command.getTable(alias);
// Database db = command.getDatabase();
// Table t2 = db.getRandomTable();
// String a2 = command.getNextTableAlias();
// sql += "SELECT * FROM " + t2.getName() + " " + a2 + " WHERE ";
// command.addSubqueryTable(a2, t2);
// createComparison();
// command.removeSubqueryTable(a2);
// }
private void createComparison() {
if (is(5)) {
sql += " NOT( ";
createComparisonSub();
sql += ")";
} else {
createComparisonSub();
}
}
private void createComparisonSub() {
/*
if (is(10)) {
sql += " EXISTS(";
createSubquery();
sql += ")";
return;
} else */if (is(10)) {
sql += "(";
create();
sql += ")";
return;
}
String alias = command.getRandomTableAlias();
Column column = command.getTable(alias).getRandomConditionColumn();
if(column==null) {
if(is(50)) {
sql += "1=1";
} else {
sql += "1=0";
}
return;
}
boolean columnFirst = is(90);
if (columnFirst) {
sql += getColumnName(alias, column);
} else {
Value v = column.getRandomValue();
sql += v.getSQL();
}
if (is(10)) {
oneOf(new String[] { "IS NULL", "IS NOT NULL" });
} else if (is(10)) {
oneOf(new String[] { "BETWEEN", "NOT BETWEEN" });
Value v = column.getRandomValue();
sql += v.getSQL();
sql += " AND ";
v = column.getRandomValue();
sql += v.getSQL();
// } else if (is(10)) {
//// oneOf(new String[] { "IN", "NOT IN" });
// sql += " IN ";
// sql += "(";
// int len = config.random().getInt(8) + 1;
// for (int i = 0; i < len; i++) {
// if (i > 0) {
// sql += ", ";
// }
// sql += column.getRandomValueNotNull().getSQL();
// }
// sql += ")";
} else {
if(column.getType()==Types.VARCHAR) {
oneOf(new String[] { "=", "=", "=", "<", ">", "<=", ">=", "<>", "LIKE",
"NOT LIKE" });
} else {
oneOf(new String[] { "=", "=", "=", "<", ">", "<=", ">=", "<>" });
}
if (columnFirst) {
Value v = column.getRandomValue();
sql += v.getSQL();
} else {
sql += getColumnName(alias, column);
}
}
}
public boolean isEmpty() {
return sql == null || sql.trim().length() == 0;
}
void createExpression(String alias, Column type) {
boolean op = is(20);
// no null values if there is an operation
boolean allowNull = !op;
// boolean allowNull =true;
createTerm(alias, type, true);
if (op) {
switch (type.getType()) {
case Types.INTEGER:
if(config.is(TestSynth.POSTGRESQL)) {
oneOf(new String[] { "+", "-", "/" });
} else {
oneOf(new String[] { "+", "-", "*", "/" });
}
createTerm(alias, type, allowNull);
break;
case Types.DECIMAL:
oneOf(new String[] { "+", "-", "*" });
createTerm(alias, type, allowNull);
break;
case Types.VARCHAR:
sql += " || ";
createTerm(alias, type, allowNull);
break;
case Types.BLOB:
case Types.CLOB:
case Types.DATE:
break;
}
}
}
void createTerm(String alias, Column type, boolean allowNull) {
int dt = type.getType();
if (is(5) && (dt == Types.INTEGER) || (dt == Types.DECIMAL)) {
sql += " - ";
allowNull = false;
}
if (is(10)) {
sql += "(";
createTerm(alias, type, allowNull);
sql += ")";
return;
}
if (is(20)) {
// if (is(10)) {
// sql += "CAST(";
// // TODO cast
// Column c = Column.getRandomColumn(config);
// createTerm(alias, c, allowNull);
// sql += " AS ";
// sql += type.getTypeName();
// sql += ")";
// return;
// }
switch (dt) {
// case Types.INTEGER:
// String function = oneOf(new String[] { "LENGTH" /*, "MOD" */ });
// sql += "(";
// createTerm(alias, type, allowNull);
// sql += ")";
// break;
case Types.VARCHAR:
oneOf(new String[] { "LOWER", "UPPER" });
sql += "(";
createTerm(alias, type, allowNull);
sql += ")";
break;
default:
createTerm(alias, type, allowNull);
}
return;
}
if (is(60)) {
String a2 = command.getRandomTableAlias();
Column column = command.getTable(a2).getRandomColumnOfType(dt);
if (column != null) {
sql += getColumnName(a2, column);
return;
}
}
Value v = Value.getRandom(config, dt, 20, 2, allowNull);
sql += v.getSQL();
}
public String toString() {
throw new Error("hey!");
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
public class Index {
Table table;
String name;
Column[] columns;
boolean unique;
Index(Table table, String name, Column[] columns, boolean unique) {
this.table = table;
this.name = name;
this.columns = columns;
this.unique = unique;
}
public String getName() {
return name;
}
public String getCreateSQL() {
String sql = "CREATE ";
if(unique) {
sql += "UNIQUE ";
}
sql += "INDEX " + name + " ON " + table.getName() + "(";
for(int i=0; i<columns.length; i++) {
if(i>0) {
sql += ", ";
}
sql += columns[i].getName();
}
sql += ")";
return sql;
}
public String getDropSQL() {
return "DROP INDEX " + name;
}
public Table getTable() {
return table;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Random;
public class RandomGen {
private Random random = new Random();
// private TestSynth config;
public RandomGen(TestSynth config) {
// this.config = config;
random.setSeed(12);
}
public int getInt(int max) {
return max==0 ? 0 : random.nextInt(max);
}
public double nextGaussian() {
return random.nextGaussian();
}
public int getLog(int max) {
if(max==0) {
return 0;
}
while(true) {
int d = Math.abs((int)(random.nextGaussian()/2.*max));
if(d<max) {
return d;
}
}
}
public void getBytes(byte[] data) {
random.nextBytes(data);
}
public boolean getBoolean(int percent) {
return random.nextInt(100) <= percent;
}
public String randomString(int len) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<len; i++) {
String from = (i%2==0)?"bdfghklmnpqrst":"aeiou";
buff.append(from.charAt(getInt(from.length())));
}
return buff.toString();
}
public int getRandomInt() {
switch(random.nextInt(10)) {
case 0:
return Integer.MAX_VALUE;
case 1:
return Integer.MIN_VALUE;
case 2:
return random.nextInt();
case 3:
case 4:
return 0;
case 5:
return (int)(random.nextGaussian()*2000)-200;
default:
return (int)(random.nextGaussian()*20)-5;
}
}
public long getRandomLong() {
switch(random.nextInt(10)) {
case 0:
return Long.MAX_VALUE;
case 1:
return Long.MIN_VALUE;
case 2:
return random.nextLong();
case 3:
case 4:
return 0;
case 5:
return (int)(random.nextGaussian()*20000)-2000;
default:
return (int)(random.nextGaussian()*200)-50;
}
}
public double getRandomDouble() {
switch(random.nextInt(10)) {
case 0:
return Double.MIN_VALUE;
case 1:
return Double.MAX_VALUE;
case 2:
return Float.MIN_VALUE;
case 3:
return Float.MAX_VALUE;
case 4:
return random.nextDouble();
case 5:
case 6:
return 0;
case 7:
return random.nextGaussian()*20000.-2000.;
default:
return random.nextGaussian()*200.-50.;
}
}
public boolean nextBoolean() {
return random.nextBoolean();
}
public int[] getIntArray() {
switch(random.nextInt(10)) {
case 0:
return null;
default:
int len = getInt(100);
int[] list = new int[len];
for(int i=0; i<len; i++) {
list[i] = getRandomInt();
}
return list;
}
}
public Object getByteArray() {
switch(random.nextInt(10)) {
case 0:
return null;
default:
int len = getInt(100);
byte[] list = new byte[len];
random.nextBytes(list);
return list;
}
}
public Time randomTime() {
if(random.nextInt(10)==0) {
return null;
}
StringBuffer buff = new StringBuffer();
buff.append(getInt(24));
buff.append(':');
buff.append(getInt(24));
buff.append(':');
buff.append(getInt(24));
return Time.valueOf(buff.toString());
}
public Timestamp randomTimestamp() {
if(random.nextInt(10)==0) {
return null;
}
StringBuffer buff = new StringBuffer();
buff.append(getInt(10) + 2000);
buff.append('-');
buff.append(getInt(12) + 1);
buff.append('-');
buff.append(getInt(29) + 1);
buff.append(' ');
buff.append(getInt(24));
buff.append(':');
buff.append(getInt(60));
buff.append(':');
buff.append(getInt(60));
// TODO test timestamp nanos
return Timestamp.valueOf(buff.toString());
}
public Date randomDate() {
if(random.nextInt(10)==0) {
return null;
}
StringBuffer buff = new StringBuffer();
buff.append(getInt(10) + 2000);
buff.append('-');
buff.append(getInt(11) + 1);
buff.append('-');
buff.append(getInt(29) + 1);
return Date.valueOf(buff.toString());
}
public String modify(String sql) {
int len = getLog(10);
for(int i=0; i<len; i++) {
int pos = getInt(sql.length());
if(getBoolean(50)) {
String badChars = "abcABCDEF\u00ef\u00f6\u00fcC1230=<>+\"\\*%&/()=?$_-.:,;{}[]"; // auml ouml uuml
char bad = badChars.charAt(getInt(badChars.length()));
sql = sql.substring(0, pos) + bad + sql.substring(pos);
} else {
if(pos >= sql.length()) {
sql = sql.substring(0, pos);
} else {
sql = sql.substring(0, pos) + sql.substring(pos+1);
}
}
}
return sql;
}
public void setSeed(int seed) {
random.setSeed(seed);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.*;
import java.util.*;
class Result implements Comparable {
static final int SUCCESS=0, BOOLEAN=1, INT=2, EXCEPTION=3, RESULTSET=4;
private int type;
private boolean bool;
private int intValue;
private SQLException exception;
private ArrayList rows;
private ArrayList header;
String sql;
Result(String sql) {
this.sql = sql;
type = SUCCESS;
}
Result(String sql, SQLException e) {
this.sql = sql;
type = EXCEPTION;
exception = e;
}
Result(String sql, boolean b) {
this.sql = sql;
type = BOOLEAN;
this.bool = b;
}
Result(String sql, int i) {
this.sql = sql;
type = INT;
this.intValue = i;
}
Result(TestSynth config, String sql, ResultSet rs) {
this.sql = sql;
type = RESULTSET;
try {
rows = new ArrayList();
header = new ArrayList();
ResultSetMetaData meta = rs.getMetaData();
int len = meta.getColumnCount();
Column[] cols = new Column[len];
for(int i=0; i<len; i++) {
cols[i] = new Column(meta, i+1);
}
while(rs.next()) {
Row row = new Row(config, rs, len);
rows.add(row);
}
Collections.sort(rows);
} catch(SQLException e) {
// type = EXCEPTION;
// exception = e;
e.printStackTrace();
throw new Error("error reading result set");
}
}
public String toString() {
switch(type) {
case SUCCESS:
return "success";
case BOOLEAN:
return "boolean: " + this.bool;
case INT:
return "int: " + this.intValue;
case EXCEPTION: {
StringWriter w = new StringWriter();
exception.printStackTrace(new PrintWriter(w));
return "exception: "+exception.getSQLState()+": "+exception.getMessage() + "\r\n"+w.toString();
}
case RESULTSET:
String result = "ResultSet { // size=" + rows.size() + "\r\n ";
for(int i=0; i<header.size(); i++) {
Column column = (Column)header.get(i);
result += column.toString() + "; ";
}
result += "} = {\r\n";
for(int i=0; i<rows.size(); i++) {
Row row = (Row)rows.get(i);
result += " { " + row.toString() + "};\r\n";
}
return result + "}";
default:
throw new Error("internal");
}
}
public int compareTo(Object o) {
Result r = (Result)o;
switch(type) {
case EXCEPTION:
if(r.type != EXCEPTION) {
return 1;
}
return 0;
// return exception.getSQLState().compareTo(r.exception.getSQLState());
case BOOLEAN:
case INT:
case SUCCESS:
case RESULTSET:
return toString().compareTo(r.toString());
default:
throw new Error("internal");
}
}
public void log() {
switch(type) {
case SUCCESS:
System.out.println("> ok");
break;
case EXCEPTION:
System.out.println("> exception");
break;
case INT:
if(intValue==0) {
System.out.println("> ok");
} else {
System.out.println("> update count: "+intValue);
}
break;
case RESULTSET:
System.out.println("> rs "+rows.size());
break;
}
System.out.println();
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.*;
class Row implements Comparable {
private Value[] data;
public Row(TestSynth config, ResultSet rs, int len) throws SQLException {
data = new Value[len];
for(int i = 0; i<len; i++) {
data[i] = Value.read(config, rs, i+1);
}
}
public String toString() {
String s = "";
for(int i = 0; i<data.length; i++) {
Object o = data[i];
s += o==null ? "NULL" : o.toString();
s += "; ";
}
return s;
}
public int compareTo(Object o) {
Row r2 = (Row)o;
int result = 0;
for(int i=0; i<data.length && result==0; i++) {
Object o1 = data[i];
Object o2 = r2.data[i];
if(o1==null) {
result = (o2==null) ? 0 : -1;
} else if(o2==null) {
result = 1;
} else {
result = o1.toString().compareTo(o2.toString());
}
}
return result;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.util.ArrayList;
class Table {
private TestSynth config;
private String name;
private boolean temporary;
private boolean globalTemporary;
private Column[] columns;
private Column[] primaryKeys;
private ArrayList indexes = new ArrayList();
Table(TestSynth config) {
this.config = config;
}
public static Table newRandomTable(TestSynth config) {
Table table = new Table(config);
table.name = "T_" + config.randomIdentifier();
// there is a difference between local temp tables for persistent and in-memory mode
// table.temporary = config.random().getBoolean(10);
// if(table.temporary) {
// if(config.getMode() == TestSynth.H2_MEM) {
// table.globalTemporary = false;
// } else {
// table.globalTemporary = config.random().getBoolean(50);
// }
// }
int len = config.random().getLog(10) + 1;
table.columns = new Column[len];
for(int i=0; i<len; i++) {
Column col = Column.getRandomColumn(config);
table.columns[i] = col;
}
if(config.random().getBoolean(90)) {
int pkLen = config.random().getLog(len);
table.primaryKeys = new Column[pkLen];
for(int i=0; i<pkLen; i++) {
Column pk = null;
do {
pk = table.columns[config.random().getInt(len)];
} while(pk.isPrimaryKey());
table.primaryKeys[i] = pk;
pk.setPrimaryKey(true);
pk.setNullable(false);
}
}
return table;
}
public Index newRandomIndex() {
String indexName = "I_" + config.randomIdentifier();
int len = config.random().getLog(getColumnCount()-1)+1;
boolean unique = config.random().getBoolean(50);
Column[] cols = getRandomColumns(len);
Index index = new Index(this, indexName, cols, unique);
return index;
}
public String getDropSQL() {
return "DROP TABLE " + name;
}
public String getCreateSQL() {
String sql = "CREATE ";
if(temporary) {
if(globalTemporary) {
sql += "GLOBAL ";
} else {
sql += "LOCAL ";
}
sql += "TEMPORARY ";
}
sql += "TABLE " + name + "(";
for(int i=0; i<columns.length; i++) {
if(i>0) {
sql += ", ";
}
Column column = columns[i];
sql += column.getCreateSQL();
if(primaryKeys!=null && primaryKeys.length==1 && primaryKeys[0]==column) {
sql += " PRIMARY KEY";
}
}
if(primaryKeys != null && primaryKeys.length>1) {
sql += ", ";
sql += "PRIMARY KEY(";
for(int i=0; i<primaryKeys.length; i++) {
if(i>0) {
sql += ", ";
}
Column column = primaryKeys[i];
sql += column.getName();
}
sql += ")";
}
sql += ")";
return sql;
}
public String getInsertSQL(Column[] c, Value[] v) {
String sql = "INSERT INTO " + name;
if(c!=null) {
sql += "(";
for(int i=0; i<c.length; i++) {
if(i>0) {
sql += ", ";
}
sql += c[i].getName();
}
sql += ")";
}
sql += " VALUES(";
for(int i=0; i<v.length; i++) {
if(i>0) {
sql += ", ";
}
sql += v[i].getSQL();
}
sql += ")";
return sql;
}
public String getName() {
return name;
}
public Column getRandomConditionColumn() {
ArrayList list = new ArrayList();
for(int i=0; i<columns.length; i++) {
if(Column.isConditionType(config, columns[i].getType())) {
list.add(columns[i]);
}
}
if(list.size() == 0) {
return null;
}
return (Column) list.get(config.random().getInt(list.size()));
}
public Column getRandomColumn() {
return columns[config.random().getInt(columns.length)];
}
public int getColumnCount() {
return columns.length;
}
public Column getRandomColumnOfType(int type) {
ArrayList list = new ArrayList();
for(int i=0; i<columns.length; i++) {
if(columns[i].getType()==type) {
list.add(columns[i]);
}
}
if(list.size() == 0) {
return null;
}
return (Column) list.get(config.random().getInt(list.size()));
}
public Column[] getRandomColumns(int len) {
int[] index = new int[columns.length];
for(int i=0; i<columns.length; i++) {
index[i] = i;
}
for(int i=0; i<columns.length; i++) {
int temp = index[i];
int r = index[config.random().getInt(columns.length)];
index[i] = index[r];
index[r] = temp;
}
Column[] c = new Column[len];
for(int i=0; i<len; i++) {
c[i] = columns[index[i]];
}
return c;
}
public Column[] getColumns() {
return columns;
}
public void addIndex(Index index) {
indexes.add(index);
}
public void removeIndex(Index index) {
indexes.remove(index);
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.tools.DeleteDbFiles;
public class TestBtreeIndex extends TestBase {
public void test() throws Exception {
Random random = new Random();
while(true) {
int seed = random.nextInt();
testCase(seed);
}
}
public void testCase(int seed) throws Exception {
BASE_DIR = "dataIndex";
testOne(seed);
BASE_DIR = "data";
}
private void testOne(int seed) throws Exception {
Class.forName("org.h2.Driver");
printTime("testIndex " + seed);
Random random = new Random(seed);
int distinct, prefixLength;
if(random.nextBoolean()) {
distinct=random.nextInt(8000)+1;
prefixLength=random.nextInt(8000)+1;
} else if(random.nextBoolean()) {
distinct=random.nextInt(16000)+1;
prefixLength=random.nextInt(100)+1;
} else {
distinct=random.nextInt(10)+1;
prefixLength=random.nextInt(10)+1;
}
boolean delete = random.nextBoolean();
StringBuffer buff = new StringBuffer();
for(int j=0; j<prefixLength; j++) {
buff.append("x");
}
String prefix = buff.toString();
DeleteDbFiles.execute(BASE_DIR, null, true);
Connection conn = DriverManager.getConnection("jdbc:h2:" +BASE_DIR + "/index", "sa", "sa");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE a(text VARCHAR PRIMARY KEY)");
PreparedStatement prepInsert = conn.prepareStatement("INSERT INTO a VALUES(?)");
PreparedStatement prepDelete = conn.prepareStatement("DELETE FROM a WHERE text=?");
PreparedStatement prepDeleteAllButOne = conn.prepareStatement("DELETE FROM a WHERE text <> ?");
int count=0;
for(int i=0; i<1000; i++) {
int y = random.nextInt(distinct);
try {
prepInsert.setString(1, prefix + y);
prepInsert.executeUpdate();
count ++;
} catch(SQLException e) {
if(e.getSQLState().equals("23001")) {
// ignore
} else {
e.printStackTrace();
break;
}
}
if(delete && random.nextInt(10) == 1) {
if(random.nextInt(4) == 1) {
try {
prepDeleteAllButOne.setString(1, prefix + y);
int deleted = prepDeleteAllButOne.executeUpdate();
if(deleted < count-1) {
System.out.println("ERROR deleted:"+deleted);
System.out.println("new TestBtreeIndex().");
}
count -= deleted;
} catch(SQLException e) {
e.printStackTrace();
break;
}
} else {
try {
prepDelete.setString(1, prefix + y);
int deleted = prepDelete.executeUpdate();
if(deleted > 1) {
System.out.println("ERROR deleted:"+deleted);
System.out.println("new TestIndex().");
}
count -= deleted;
} catch(SQLException e) {
e.printStackTrace();
break;
}
}
}
}
ResultSet rs = conn.createStatement().executeQuery("SELECT text FROM a ORDER BY text");
int testCount = 0;
while(rs.next()) {
testCount++;
}
if(testCount != count) {
System.out.println("ERROR count:"+count+" testCount:"+testCount);
System.out.println("new TestIndex().");
}
rs = conn.createStatement().executeQuery("SELECT text, count(*) FROM a GROUP BY text HAVING COUNT(*)>1");
if(rs.next()) {
System.out.println("ERROR");
System.out.println("new TestIndex().");
}
conn.close();
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import org.h2.jdbc.JdbcConnection;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.test.db.TestScript;
import org.h2.util.RandomUtils;
/**
* This is sometimes called 'Fuzz Testing'
*/
public class TestCrashAPI extends TestBase {
public static final Class[] INTERFACES = {
Connection.class,
PreparedStatement.class,
Statement.class,
ResultSet.class,
ResultSetMetaData.class,
Savepoint.class
};
private ArrayList objects = new ArrayList();
private HashMap classMethods = new HashMap();
private RandomGen random = new RandomGen(null);
private ArrayList statements = new ArrayList();
private int openCount;
private long callCount;
private String DIR = "synth";
private void deleteDb(int seed) {
try {
deleteDb(BASE_DIR + "/" + DIR, null);
} catch(Exception e) {
// ignore
}
}
private Connection getConnection(int seed, boolean delete) throws Exception {
openCount++;
if(delete) {
deleteDb(seed);
}
// can not use FILE_LOCK=NO, otherwise something could be written into the database in the finalizer
String add = ""; // ";STORAGE=TEXT";
// int testing;
// if(openCount>=10) {
// add = ";LOG=2";
// System.out.println("now open " + openCount);
// add += ";TRACE_LEVEL_FILE=3";
// config.logMode = 2;
// }
String url = getURL(DIR + "/crashapi" + seed, true) + add;
Connection conn = null;
// System.gc();
conn = DriverManager.getConnection(url, "sa", "");
int len = random.getInt(50);
int start = random.getInt(statements.size() - len);
int end = start + len;
Statement stat = conn.createStatement();
stat.execute("SET LOCK_TIMEOUT 10");
stat.execute("SET WRITE_DELAY 0");
stat.execute("SCRIPT NOPASSWORDS NOSETTINGS");
for(int i=start; i<end && i<statements.size(); i++) {
try {
stat.execute("SELECT * FROM TEST WHERE ID=1");
} catch(Throwable t) {
printIfBad(seed, -i, -1, t);
}
try {
stat.execute("SELECT * FROM TEST WHERE ID=1 OR ID=1");
} catch(Throwable t) {
printIfBad(seed, -i, -1, t);
}
String sql = (String) statements.get(i);
try {
stat.execute(sql);
} catch(Throwable t) {
printIfBad(seed, -i, -1, t);
}
}
if(random.nextBoolean()) {
try {
conn.commit();
} catch(Throwable t) {
printIfBad(seed, 0, -1, t);
}
}
return conn;
}
private void testOne(int seed) throws Exception {
printTime("TestCrashAPI " + seed);
openCount=0;
random = new RandomGen(null);
random.setSeed(seed);
Connection c1 = getConnection(seed, true);
Connection conn = null;
for(int i=0; i<2000; i++) {
//if(i % 10 == 0) {
// for(int j=0; j<objects.size(); j++) {
// System.out.print(objects.get(j));
// System.out.print(" ");
// }
// System.out.println();
// Thread.sleep(1);
//}
if(objects.size() == 0) {
try {
conn = getConnection(seed, false);
} catch(SQLException e) {
if(e.getSQLState().equals("08004")) {
// Wrong user/password [08004]
try {
c1.createStatement().execute("SET PASSWORD ''");
} catch(Throwable t) {
// power off or so
break;
}
try {
conn = getConnection(seed, false);
} catch(Throwable t) {
printIfBad(seed, -i, -1, t);
}
} else if(e.getSQLState().equals("90098")){
// The database has been closed
break;
} else {
printIfBad(seed, -i, -1, e);
}
}
objects.add(conn);
}
int objectId = random.getInt(objects.size());
if(random.getBoolean(1)) {
objects.remove(objectId);
continue;
}
if(random.getInt(2000) == 0 && conn != null) {
((JdbcConnection)conn).setPowerOffCount(random.getInt(50));
}
Object o = objects.get(objectId);
if(o==null) {
objects.remove(objectId);
continue;
}
Class in = getJdbcInterface(o);
ArrayList methods = (ArrayList)classMethods.get(in);
Method m = (Method) methods.get(random.getInt(methods.size()));
Object o2 = callRandom(seed, i, objectId, o, m);
if(o2 != null) {
objects.add(o2);
}
}
try {
if(conn != null) {
conn.close();
}
c1.close();
} catch(Throwable t) {
printIfBad(seed, -101010, -1, t);
try {
deleteDb(null, "test");
} catch(Throwable t2) {
printIfBad(seed, -101010, -1, t2);
}
}
objects.clear();
}
private void printError(int seed, int id, Throwable t) {
StringWriter writer = new StringWriter();
t.printStackTrace(new PrintWriter(writer));
String s = writer.toString();
System.out.println("new TestCrashAPI().init(test).testCase("+seed+"); // Bug " + s.hashCode()+" seed="+seed+" id=" + id + " callCount=" + callCount+" openCount="+openCount + " " + t.getMessage());
t.printStackTrace();
}
private Object callRandom(int seed, int id, int objectId, Object o, Method m) throws Exception {
Class[] paramClasses = m.getParameterTypes();
Object[] params = new Object[paramClasses.length];
for(int i=0; i<params.length; i++) {
params[i] = getRandomParam(id, paramClasses[i]);
}
Object result = null;
try {
callCount++;
result = m.invoke(o, params);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
printIfBad(seed, id, objectId, t);
}
if(result == null) {
return null;
}
Class in = getJdbcInterface(result);
if(in == null) {
return null;
}
return result;
}
private void printIfBad(int seed, int id, int objectId, Throwable t) {
if(t instanceof BatchUpdateException) {
// do nothing
} else if(t instanceof SQLException) {
SQLException s = (SQLException) t;
String state = s.getSQLState();
if(state == null) {
printError(seed, id, s);
} else if(state.equals("90008")) {
if(objectId >= 0) {
// The object is already closed [90008]
// TODO at least call a few more times after close - maybe there is still an error
objects.remove(objectId);
}
} else if(state.equals("HY000")) {
// General error [HY000]
printError(seed, id, s);
}
} else {
printError(seed, id, t);
}
}
private Object getRandomParam(int id, Class type) {
if(type == int.class) {
return new Integer(random.getRandomInt());
} else if(type == byte.class) {
return new Byte((byte)random.getRandomInt());
} else if(type == short.class) {
return new Short((short)random.getRandomInt());
} else if(type == long.class) {
return new Long(random.getRandomLong());
} else if(type == float.class) {
return new Float(random.getRandomDouble());
} else if(type == boolean.class) {
return new Boolean(random.nextBoolean());
} else if(type == double.class) {
return new Double(random.getRandomDouble());
} else if(type == String.class) {
if(random.getInt(10) == 0) {
return null;
} else {
int randomId = random.getInt(statements.size());
String sql = (String)statements.get(randomId);
if(random.getInt(10)==0) {
sql = random.modify(sql);
}
return sql;
}
} else if(type == int[].class) {
// TODO test with 'shared' arrays (make sure database creates a copy)
return random.getIntArray();
} else if(type == java.io.Reader.class) {
return null;
} else if(type == java.sql.Array.class) {
return null;
} else if(type == byte[].class) {
// TODO test with 'shared' arrays (make sure database creates a copy)
return random.getByteArray();
} else if(type == Map.class) {
return null;
} else if(type == Object.class) {
return null;
} else if(type == java.sql.Date.class) {
return random.randomDate();
} else if(type == java.sql.Time.class) {
return random.randomTime();
} else if(type == java.sql.Timestamp.class) {
return random.randomTimestamp();
} else if(type == java.io.InputStream.class) {
return null;
} else if(type == String[].class) {
return null;
} else if(type == java.sql.Clob.class) {
return null;
} else if(type == java.sql.Blob.class) {
return null;
} else if(type == Savepoint.class) {
// TODO should use generated savepoints
return null;
} else if(type == Calendar.class) {
return Calendar.getInstance();
} else if(type == java.net.URL.class) {
return null;
} else if(type == java.math.BigDecimal.class) {
return new java.math.BigDecimal("" + random.getRandomDouble());
} else if(type == java.sql.Ref.class) {
return null;
}
return null;
}
private Class getJdbcInterface(Object o) {
Class[] list = o.getClass().getInterfaces();
for(int i=0; i<list.length; i++) {
Class in = list[i];
if(classMethods.get(in) != null) {
return in;
}
}
return null;
}
private void initMethods() {
for(int i=0; i<INTERFACES.length; i++) {
Class inter = INTERFACES[i];
classMethods.put(inter, new ArrayList());
}
for(int i=0; i<INTERFACES.length; i++) {
Class inter = INTERFACES[i];
ArrayList list = (ArrayList) classMethods.get(inter);
Method[] methods = inter.getMethods();
for(int j=0; j<methods.length; j++) {
Method m = methods[j];
list.add(m);
}
}
}
public TestBase init(TestAll conf) throws Exception {
super.init(conf);
if (config.logMode == 0) {
error("Log mode 0 may corrupt the db, can't test");
}
BASE_DIR = "dataCrash";
startServerIfRequired();
TestScript script = new TestScript();
ArrayList add = script.getAllStatements(config, "org/h2/test/test.in.txt");
initMethods();
Class.forName("org.h2.Driver");
statements.addAll(add);
return this;
}
public void testCase(int i) throws Exception {
BASE_DIR = "dataCrash";
testOne(i);
BASE_DIR = "data";
}
public void test() throws Exception {
for(int i=0; i<Integer.MAX_VALUE; i++) {
int seed = RandomUtils.nextInt(Integer.MAX_VALUE);
testCase(seed);
deleteDb(seed);
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.util.StringUtils;
public class TestJoin extends TestBase {
private ArrayList connections = new ArrayList();
private Random random;
private int paramCount;
private StringBuffer buff;
public void test() throws Exception {
BASE_DIR = "dataJoin";
testJoin();
BASE_DIR = "data";
}
private void testJoin() throws Exception {
deleteDb("join");
String shortestFailed = null;
Connection c1 = getConnection("join");
connections.add(c1);
Class.forName("org.postgresql.Driver");
Connection c2 = DriverManager.getConnection("jdbc:postgresql:test", "sa", "sa");
connections.add(c2);
// Class.forName("com.mysql.jdbc.Driver");
// Connection c2 = DriverManager.getConnection("jdbc:mysql://localhost/test", "sa", "sa");
// connections.add(c2);
// Class.forName("org.hsqldb.jdbcDriver");
// Connection c2 = DriverManager.getConnection("jdbc:hsqldb:join", "sa", "");
// connections.add(c2);
/*
DROP TABLE ONE;
DROP TABLE TWO;
CREATE TABLE ONE(A INT PRIMARY KEY, B INT);
INSERT INTO ONE VALUES(0, NULL);
INSERT INTO ONE VALUES(1, 0);
INSERT INTO ONE VALUES(2, 1);
INSERT INTO ONE VALUES(3, 4);
CREATE TABLE TWO(A INT PRIMARY KEY, B INT);
INSERT INTO TWO VALUES(0, NULL);
INSERT INTO TWO VALUES(1, 0);
INSERT INTO TWO VALUES(2, 2);
INSERT INTO TWO VALUES(3, 3);
INSERT INTO TWO VALUES(4, NULL);
*/
execute("DROP TABLE ONE", null, true);
execute("DROP TABLE TWO", null, true);
execute("CREATE TABLE ONE(A INT PRIMARY KEY, B INT)", null);
execute("INSERT INTO ONE VALUES(0, NULL)", null);
execute("INSERT INTO ONE VALUES(1, 0)", null);
execute("INSERT INTO ONE VALUES(2, 1)", null);
execute("INSERT INTO ONE VALUES(3, 4)", null);
execute("CREATE TABLE TWO(A INT PRIMARY KEY, B INT)", null);
execute("INSERT INTO TWO VALUES(0, NULL)", null);
execute("INSERT INTO TWO VALUES(1, 0)", null);
execute("INSERT INTO TWO VALUES(2, 2)", null);
execute("INSERT INTO TWO VALUES(3, 3)", null);
execute("INSERT INTO TWO VALUES(4, NULL)", null);
random = new Random();
long start = System.currentTimeMillis();
for(int i=0;; i++) {
paramCount = 0;
buff = new StringBuffer();
long time = System.currentTimeMillis();
if(time - start > 5000) {
System.out.println("i:"+i);
start = time;
}
buff.append("SELECT ");
int tables = 1 + random.nextInt(5);
for(int j=0; j<tables; j++) {
if(j > 0) {
buff.append(", ");
}
buff.append("T" + (char)('0' + j) + ".A");
}
buff.append(" FROM ");
appendRandomTable();
buff.append(" T0 ");
for(int j=1; j<tables; j++) {
if(random.nextBoolean()) {
buff.append("INNER");
} else {
// if(random.nextInt(4)==1) {
// buff.append("RIGHT");
// } else {
buff.append("LEFT");
// }
}
buff.append(" JOIN ");
appendRandomTable();
buff.append(" T");
buff.append((char)('0' + j));
buff.append(" ON ");
appendRandomCondition(j);
}
if(random.nextBoolean()) {
buff.append("WHERE ");
appendRandomCondition(tables-1);
}
String sql = buff.toString();
Object[] params = new Object[paramCount];
for(int j=0; j<paramCount; j++) {
params[j] = random.nextInt(4)==1 ? null : new Integer(random.nextInt(10)-3);
}
try {
execute(sql, params);
} catch(Exception e) {
// System.out.println("/*FAIL*/ " + sql+" " +e);
if(shortestFailed == null || shortestFailed.length() > sql.length()) {
System.out.println("/*SHORT*/ " + sql);
shortestFailed = sql;
}
}
}
// c1.close();
// c2.close();
}
private void appendRandomTable() {
if(random.nextBoolean()) {
buff.append("ONE");
} else {
buff.append("TWO");
}
}
private void appendRandomCondition(int j) {
if(random.nextInt(10)==1) {
buff.append("NOT ");
appendRandomCondition(j);
} else if(random.nextInt(5)==1) {
buff.append("(");
appendRandomCondition(j);
if(random.nextBoolean()) {
buff.append(") OR (");
} else {
buff.append(") AND (");
}
appendRandomCondition(j);
buff.append(")");
} else {
if(j>0 && random.nextBoolean()) {
buff.append("T" + (char)('0' + j-1) + ".A=T" + (char)('0' + j)+".A ");
} else {
appendRandomConditionPart(j);
}
}
}
private void appendRandomConditionPart(int j) {
int t1 = j <= 1 ? 0 : random.nextInt(j + 1);
int t2 = j <= 1 ? 0 : random.nextInt(j + 1);
String c1 = random.nextBoolean() ? "A" : "B";
String c2 = random.nextBoolean() ? "A" : "B";
buff.append("T" + (char)('0' + t1));
buff.append("."+c1);
if(random.nextInt(4)==1) {
if(random.nextInt(5)==1) {
buff.append(" IS NOT NULL");
} else {
buff.append(" IS NULL");
}
} else {
if(random.nextInt(5)==1) {
switch(random.nextInt(5)) {
case 0:
buff.append(">");
break;
case 1:
buff.append("<");
break;
case 2:
buff.append("<=");
break;
case 3:
buff.append(">=");
break;
case 4:
buff.append("<>");
break;
}
} else {
buff.append("=");
}
if(random.nextBoolean()) {
buff.append("T" + (char)('0' + t2));
buff.append("."+c2);
} else {
buff.append(random.nextInt(5)-1);
}
}
buff.append(" ");
}
private void execute(String sql, Object[] params) throws Exception {
execute(sql, params, false);
}
private void execute(String sql, Object[] params, boolean ignoreDifference) throws Exception {
String first = null;
for(int i=0; i<connections.size(); i++) {
Connection conn = (Connection) connections.get(i);
String s;
try {
Statement stat;
boolean result;
if(params == null || params.length==0) {
stat = conn.createStatement();
result = stat.execute(sql);
} else {
PreparedStatement prep = conn.prepareStatement(sql);
stat = prep;
for(int j=0; j<params.length; j++) {
prep.setObject(j+1, params[j]);
}
result = prep.execute();
}
if(result) {
ResultSet rs = stat.getResultSet();
s = "rs: " + readResult(rs);
} else {
s = "updateCount: " + stat.getUpdateCount();
}
} catch(SQLException e) {
// e.printStackTrace();
s = "exception";
}
if(i==0) {
first = s;
} else {
if(!ignoreDifference && !s.equals(first)) {
// System.out.println("FAIL " + sql);
// System.out.println("first="+first);
// System.out.println("now="+s);
throw new Exception("FAIL");
}
}
}
}
private String readResult(ResultSet rs) throws SQLException {
StringBuffer buff = new StringBuffer();
ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
for(int i=0; i<columnCount; i++) {
if(i>0) {
buff.append(",");
}
buff.append(StringUtils.toUpperEnglish(meta.getColumnLabel(i+1)));
}
buff.append(":\n");
String result = buff.toString();
ArrayList list = new ArrayList();
while(rs.next()) {
buff = new StringBuffer();
for(int i=0; i<columnCount; i++) {
if(i>0) {
buff.append(",");
}
buff.append(rs.getString(i+1));
}
list.add(buff.toString());
}
Collections.sort(list);
for(int i=0; i<list.size(); i++) {
result += list.get(i) + "\n";
}
return result;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random;
import org.h2.test.TestBase;
public class TestKill extends TestBase {
Connection conn;
int accounts = 10;
Random random = new Random(1);
private String DIR = "synth";
public void test() throws Exception {
String connect = "";
connect = ";MAX_LOG_SIZE=10;THROTTLE=80";
String url = getURL(DIR+"/kill" + connect, true);
String user = getUser();
String password = getPassword();
String[] procDef = new String[]{
"java.exe", "-cp", "bin",
"org.h2.test.synth.TestKillProcess",
url, user, password,
BASE_DIR,
""+accounts
};
for(int i=0;; i++) {
printTime("TestKill " + i);
if(i % 10 == 0) {
trace("deleting db...");
deleteDb(BASE_DIR, "kill");
}
conn = getConnection(url);
createTables();
checkData();
initData();
conn.close();
Process proc = Runtime.getRuntime().exec(procDef);
// while(true) {
// int ch = proc.getErrorStream().read();
// if(ch < 0) {
// break;
// }
// System.out.print((char)ch);
// }
int runtime = random.nextInt(10000);
trace("running...");
Thread.sleep(runtime);
trace("stopping...");
proc.destroy();
proc.waitFor();
trace("stopped");
}
}
private void createTables() throws SQLException {
trace("createTables...");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE IF NOT EXISTS ACCOUNT(ID INT PRIMARY KEY, SUM INT)");
stat.execute("CREATE TABLE IF NOT EXISTS LOG(ID IDENTITY, ACCOUNTID INT, AMOUNT INT, FOREIGN KEY(ACCOUNTID) REFERENCES ACCOUNT(ID))");
stat.execute("CREATE TABLE IF NOT EXISTS TEST_A(ID INT PRIMARY KEY, DATA VARCHAR)");
stat.execute("CREATE TABLE IF NOT EXISTS TEST_B(ID INT PRIMARY KEY, DATA VARCHAR)");
}
private void initData() throws SQLException {
trace("initData...");
conn.createStatement().execute("DROP TABLE LOG");
conn.createStatement().execute("DROP TABLE ACCOUNT");
conn.createStatement().execute("DROP TABLE TEST_A");
conn.createStatement().execute("DROP TABLE TEST_B");
createTables();
PreparedStatement prep = conn.prepareStatement("INSERT INTO ACCOUNT VALUES(?, 0)");
for(int i=0; i<accounts; i++) {
prep.setInt(1, i);
prep.execute();
}
PreparedStatement p1 = conn.prepareStatement("INSERT INTO TEST_A VALUES(?, '')");
PreparedStatement p2 = conn.prepareStatement("INSERT INTO TEST_B VALUES(?, '')");
for(int i=0; i<accounts; i++) {
p1.setInt(1, i);
p2.setInt(1, i);
p1.execute();
p2.execute();
}
}
private void checkData() throws Exception {
trace("checkData...");
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM ACCOUNT ORDER BY ID");
PreparedStatement prep = conn.prepareStatement("SELECT SUM(AMOUNT) FROM LOG WHERE ACCOUNTID=?");
while(rs.next()) {
int account = rs.getInt(1);
int sum = rs.getInt(2);
prep.setInt(1, account);
ResultSet rs2 = prep.executeQuery();
rs2.next();
int sumLog = rs2.getInt(1);
check(sumLog, sum);
trace("account="+account+" sum="+sum);
}
PreparedStatement p1 = conn.prepareStatement("SELECT * FROM TEST_A WHERE ID=?");
PreparedStatement p2 = conn.prepareStatement("SELECT * FROM TEST_B WHERE ID=?");
for(int i=0; i<accounts; i++) {
p1.setInt(1, i);
p2.setInt(1, i);
ResultSet r1 = p1.executeQuery();
ResultSet r2 = p2.executeQuery();
boolean hasData = r1.next();
check(r2.next(), hasData);
if(hasData) {
String d1 = r1.getString("DATA");
String d2 = r2.getString("DATA");
check(d1, d2);
checkFalse(r1.next());
checkFalse(r2.next());
trace("test: data="+d1);
} else {
trace("test: empty");
}
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Random;
import org.h2.tools.FileBase;
public class TestKillProcess {
public static void main(String[] args) throws Exception {
try {
Class.forName("org.h2.Driver");
String url = args[0], user = args[1], password= args[2];
String BASE_DIR = args[3];
int accounts = Integer.parseInt(args[4]);
Random random = new Random();
Connection conn1 = DriverManager.getConnection(url, user, password);
PreparedStatement prep1a = conn1.prepareStatement("INSERT INTO LOG(ACCOUNTID, AMOUNT) VALUES(?, ?)");
PreparedStatement prep1b = conn1.prepareStatement("UPDATE ACCOUNT SET SUM=SUM+? WHERE ID=?");
conn1.setAutoCommit(false);
long time = System.currentTimeMillis();
String d = null;
for(int i=0; ; i++) {
long t = System.currentTimeMillis();
if(t > time + 1000) {
ArrayList list = FileBase.getDatabaseFiles(BASE_DIR, "kill", true);
System.out.println("inserting... i:"+i+" d:" + d+" files:" + list.size());
time = t;
}
if(i>10000) {
// System.out.println("halt");
// Runtime.getRuntime().halt(0);
// conn.createStatement().execute("SHUTDOWN IMMEDIATELY");
// System.exit(0);
}
int account = random.nextInt(accounts);
int value = random.nextInt(100);
prep1a.setInt(1, account);
prep1a.setInt(2, value);
prep1a.execute();
prep1b.setInt(1, value);
prep1b.setInt(2, account);
prep1b.execute();
conn1.commit();
if(random.nextInt(100) < 2) {
d = "D" + random.nextInt(1000);
account = random.nextInt(accounts);
conn1.createStatement().execute("UPDATE TEST_A SET DATA='" + d + "' WHERE ID=" + account);
conn1.createStatement().execute("UPDATE TEST_B SET DATA='" + d + "' WHERE ID=" + account);
}
}
} catch(Throwable e) {
e.printStackTrace();
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.h2.test.TestBase;
public class TestMulti extends TestBase {
public volatile boolean stop;
public void test() throws Exception {
Class.forName("org.h2.Driver");
deleteDb(BASE_DIR, "openClose");
// int len = getSize(5, 100);
int len = 10;
TestMultiThread[] threads = new TestMultiThread[len];
for(int i=0; i<len; i++) {
threads[i] = new TestMultiNews(this);
}
threads[0].first();
for(int i=0; i<len; i++) {
threads[i].start();
}
Thread.sleep(10000);
this.stop = true;
for(int i=0; i<len; i++) {
threads[i].join();
}
threads[0].finalTest();
}
public Connection getConnection() throws SQLException {
final String url = "jdbc:h2:"+BASE_DIR+"/openClose;LOCK_MODE=3;DB_CLOSE_DELAY=-1";
Connection conn = DriverManager.getConnection(url, "sa", "");
return conn;
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.*;
public class TestMultiNews extends TestMultiThread {
private static final String PREFIX_URL = "http://feeds.wizbangblog.com/WizbangFullFeed?m=";
int len = 10000;
Connection conn;
TestMultiNews(TestMulti base) throws SQLException {
super(base);
conn = base.getConnection();
}
void operation() throws SQLException {
if(random.nextInt(10)==0) {
conn.close();
conn = base.getConnection();
} else if(random.nextInt(10)==0) {
if(random.nextBoolean()) {
conn.commit();
} else {
conn.rollback();
}
} else if(random.nextInt(10)==0) {
conn.setAutoCommit(random.nextBoolean());
} else {
if(random.nextBoolean()) {
PreparedStatement prep;
if(random.nextBoolean()) {
prep = conn.prepareStatement(
"SELECT * FROM NEWS WHERE FLINK = ?");
} else {
prep = conn.prepareStatement(
"SELECT * FROM NEWS WHERE FVALUE = ?");
}
prep.setString(1, PREFIX_URL + random.nextInt(len));
ResultSet rs = prep.executeQuery();
if(!rs.next()) {
throw new SQLException("expected one row, got none");
}
if(rs.next()) {
throw new SQLException("expected one row, got more");
}
} else {
PreparedStatement prep = conn.prepareStatement(
"UPDATE NEWS SET FSTATE = ? WHERE FID = ?");
prep.setInt(1, random.nextInt(100));
prep.setInt(2, random.nextInt(len));
int count = prep.executeUpdate();
if(count != 1) {
throw new SQLException("expected one row, got " + count);
}
}
}
}
void begin() throws SQLException {
}
void end() throws SQLException {
conn.close();
}
void finalTest() throws Exception {
}
void first() throws SQLException {
Connection conn = base.getConnection();
Statement stat = conn.createStatement();
stat.execute(
"CREATE TABLE TEST (ID IDENTITY, NAME VARCHAR)");
stat.execute(
"CREATE TABLE NEWS (FID NUMERIC(19) PRIMARY KEY, FCOMMENTS LONGVARCHAR, " +
"FLINK VARCHAR(255), FSTATE INTEGER, FVALUE VARCHAR(255))");
stat.execute(
"CREATE INDEX IF NOT EXISTS NEWS_GUID_VALUE_INDEX ON NEWS(FVALUE)");
stat.execute(
"CREATE INDEX IF NOT EXISTS NEWS_LINK_INDEX ON NEWS(FLINK)");
stat.execute(
"CREATE INDEX IF NOT EXISTS NEWS_STATE_INDEX ON NEWS(FSTATE)");
PreparedStatement prep = conn.prepareStatement(
"INSERT INTO NEWS (FID, FCOMMENTS, FLINK, FSTATE, FVALUE) VALUES " +
"(?, ?, ?, ?, ?) ");
PreparedStatement prep2 = conn.prepareStatement(
"INSERT INTO TEST (NAME) VALUES (?)");
for(int i=0; i<len; i++) {
int x = random.nextInt(10) * 128;
StringBuffer buff = new StringBuffer();
while(buff.length() < x) {
buff.append("Test ");
buff.append(buff.length());
buff.append(' ');
}
String comment = buff.toString();
prep.setInt(1, i); // FID
prep.setString(2, comment); // FCOMMENTS
prep.setString(3, PREFIX_URL + i); // FLINK
prep.setInt(4, 0); // FSTATE
prep.setString(5, PREFIX_URL + i); // FVALUE
prep.execute();
prep2.setString(1, comment);
prep2.execute();
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestMultiNewsSimple extends TestMultiThread {
Connection conn;
static int newsCount = 10000;
static int getNewsCount() {
return newsCount;
}
TestMultiNewsSimple(TestMulti base) throws SQLException {
super(base);
conn = base.getConnection();
}
void first() throws SQLException {
Connection conn = base.getConnection();
conn.createStatement().execute("create table news(fid identity, fstate int default 0, text varchar default '')");
PreparedStatement prep = conn.prepareStatement("insert into news() values()");
for(int i=0; i<newsCount; i++) {
prep.executeUpdate();
}
conn.createStatement().execute("update news set text = 'Text' || fid");
conn.close();
}
void begin() throws SQLException {
}
void end() throws SQLException {
conn.close();
}
void operation() throws SQLException {
if(random.nextInt(10)==0) {
conn.setAutoCommit(random.nextBoolean());
} else if(random.nextInt(10)==0) {
if(random.nextBoolean()) {
conn.commit();
} else {
// conn.rollback();
}
} else {
if(random.nextBoolean()) {
PreparedStatement prep = conn.prepareStatement("update news set fstate = ? where fid = ?");
prep.setInt(1, random.nextInt(getNewsCount()));
prep.setInt(2, random.nextInt(10));
prep.execute();
} else {
PreparedStatement prep = conn.prepareStatement("select * from news where fid = ?");
prep.setInt(1, random.nextInt(getNewsCount()));
ResultSet rs = prep.executeQuery();
if(!rs.next()) {
System.out.println("No row found");
// throw new Error("No row found");
}
if(rs.next()) {
System.out.println("Multiple rows found");
// throw new Error("Multiple rows found");
}
}
}
}
void finalTest() throws SQLException {
// TODO Auto-generated method stub
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Random;
public class TestMultiOrder extends TestMultiThread {
Connection conn;
PreparedStatement insertLine;
private static final String[] ITEMS = new String[]{"Apples", "Oranges", "Bananas", "Coffee"};
static int customerCount;
static int orderCount;
static int orderLineCount;
TestMultiOrder(TestMulti base) throws SQLException {
super(base);
conn = base.getConnection();
}
void begin() throws SQLException {
insertLine = conn.prepareStatement("insert into orderline(orderid, lineid, text, amount) values(?, ?, ?, ?)");
insertCustomer();
}
void end() throws SQLException {
conn.close();
}
void operation() throws SQLException {
if(random.nextInt(10)==0) {
insertCustomer();
} else {
insertOrder();
}
}
private void insertOrder() throws SQLException {
PreparedStatement prep = conn.prepareStatement("insert into orders(customer_id , total) values(?, ?)");
prep.setInt(1, random.nextInt(getCustomerCount()));
BigDecimal total = new BigDecimal("0");
prep.setBigDecimal(2, total);
prep.executeUpdate();
ResultSet rs = prep.getGeneratedKeys();
rs.next();
int orderId = rs.getInt(1);
int lines = random.nextInt(20);
for(int i=0; i<lines; i++) {
insertLine.setInt(1, orderId);
insertLine.setInt(2, i);
insertLine.setString(3, ITEMS[random.nextInt(ITEMS.length)]);
BigDecimal amount = new BigDecimal(random.nextInt(100) + "." + random.nextInt(10));
insertLine.setBigDecimal(4, amount);
total = total.add(amount);
insertLine.addBatch();
}
insertLine.executeBatch();
increaseOrderLines(lines);
prep = conn.prepareStatement("update orders set total = ? where id = ?");
prep.setBigDecimal(1, total);
prep.setInt(2, orderId);
increaseOrders();
prep.execute();
}
private void insertCustomer() throws SQLException {
PreparedStatement prep = conn.prepareStatement("insert into customer(id, name) values(?, ?)");
int customerId = getNextCustomerId();
prep.setInt(1, customerId);
prep.setString(2, getString(customerId));
prep.execute();
}
private String getString(int id) {
StringBuffer buff = new StringBuffer();
Random rnd = new Random(id);
int len = rnd.nextInt(40);
for(int i=0; i<len; i++) {
String s = "bcdfghklmnprstwz";
char c = s.charAt(rnd.nextInt(s.length()));
buff.append(i == 0 ? Character.toUpperCase(c) : c);
s = "aeiou ";
buff.append(s.charAt(rnd.nextInt(s.length())));
}
return buff.toString();
}
synchronized int getNextCustomerId() {
return customerCount++;
}
synchronized int increaseOrders() {
return orderCount++;
}
synchronized int increaseOrderLines(int count) {
return orderLineCount+=count;
}
public int getCustomerCount() {
return customerCount;
}
void first() throws SQLException {
Connection conn = base.getConnection();
conn.createStatement().execute("drop table customer if exists");
conn.createStatement().execute("drop table orders if exists");
conn.createStatement().execute("drop table orderline if exists");
conn.createStatement().execute("create table customer(id int primary key, name varchar, account decimal)");
conn.createStatement().execute("create table orders(id int identity primary key, customer_id int, total decimal)");
conn.createStatement().execute("create table orderline(orderid int, lineid int, text varchar, amount decimal, primary key(orderid, lineid))");
conn.close();
}
void finalTest() throws Exception {
conn = base.getConnection();
ResultSet rs = conn.createStatement().executeQuery("select count(*) from customer");
rs.next();
base.check(rs.getInt(1), customerCount);
// System.out.println("customers: " + rs.getInt(1));
rs = conn.createStatement().executeQuery("select count(*) from orders");
rs.next();
base.check(rs.getInt(1), orderCount);
// System.out.println("orders: " + rs.getInt(1));
rs = conn.createStatement().executeQuery("select count(*) from orderline");
rs.next();
base.check(rs.getInt(1), orderLineCount);
// System.out.println("orderlines: " + rs.getInt(1));
conn.close();
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.SQLException;
import java.util.Random;
abstract class TestMultiThread extends Thread {
TestMulti base;
Random random = new Random();
TestMultiThread(TestMulti base) throws SQLException {
this.base = base;
}
abstract void first() throws SQLException;
abstract void operation() throws SQLException;
abstract void begin() throws SQLException;
abstract void end() throws SQLException;
abstract void finalTest() throws Exception;
public void run() {
try {
while(!base.stop) {
operation();
}
end();
} catch(Throwable e) {
e.printStackTrace();
System.out.println("FAIL: " + e.toString());
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.bnf.Bnf;
import org.h2.bnf.RuleHead;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.util.RandomUtils;
public class TestRandomSQL extends TestBase {
private int dbId;
private boolean showSQL = true;
private ArrayList statements;
private int seed;
private boolean exitOnError = true;
private Bnf bnf;
private void processException(String sql, SQLException e) {
if(e.getSQLState().equals("HY000")) {
System.out.println("new TestRandomSQL().init(test).testCase("+seed+"); // FAIL: " + e.toString());
e.printStackTrace();
if(exitOnError) {
new Error(sql, e).printStackTrace();
System.exit(0);
}
}
}
private String getDatabaseName() {
// return "dataSynth/randomsql" + dbId+";TRACE_LEVEL_FILE=3";
return "dataSynth/randomsql" + dbId;
}
private Connection connect() throws Exception {
while(true) {
try {
return getConnection(getDatabaseName());
} catch(SQLException e) {
dbId--;
try {
deleteDb(getDatabaseName());
} catch(Exception e2) {
// ignore
}
dbId++;
try {
deleteDb(getDatabaseName());
} catch(Exception e2) {
// ignore
}
dbId++;
try {
deleteDb(getDatabaseName());
} catch(SQLException e2) {
dbId++;
deleteDb(getDatabaseName());
}
}
}
}
public TestBase init(TestAll conf) throws Exception {
super.init(conf);
bnf = Bnf.getInstance(null);
bnf.linkStatements();
statements = bnf.getStatements();
// go backwards so we can append at the end
for(int i=statements.size() - 1; i>=0; i--) {
RuleHead r = (RuleHead) statements.get(i);
String topic = r.getTopic();
int weight = 0;
if(topic.equals("select")) {
weight = 50;
} else if(topic.equals("createtable")) {
weight = 20;
} else if(topic.equals("insert")) {
weight = 20;
} else if(topic.startsWith("update")) {
weight = 10;
} else if(topic.startsWith("delete")) {
weight = 5;
} else if(topic.startsWith("drop")) {
weight = 5;
}
if(showSQL) {
System.out.println(r.getTopic());
}
for(int j=0; j<weight; j++) {
statements.add(r);
}
}
return this;
}
private void testWithSeed(Bnf config) throws Exception {
config.getRandom().setSeed(seed);
Connection conn = null;
try {
conn = connect();
} catch(SQLException e) {
processException("connect", e);
conn = connect();
}
Statement stat = conn.createStatement();
for(int i=0; i<statements.size(); i++) {
int sid = config.getRandom().nextInt(statements.size());
RuleHead r = (RuleHead) statements.get(sid);
String rand = r.getRule().random(config, 0);
if(rand.length() > 0) {
try {
if(showSQL) {
System.out.println(i+" "+rand);
}
Thread.yield();
if(rand.indexOf("TRACE_LEVEL_SYSTEM_OUT") < 0) {
stat.execute(rand);
}
} catch(SQLException e) {
processException(rand, e);
}
}
}
try {
conn.close();
} catch(SQLException e) {
processException("conn.close", e);
}
}
public void testCase(int i) throws Exception {
seed = i;
try {
deleteDb(getDatabaseName());
} catch(SQLException e) {
processException("deleteDb", e);
}
testWithSeed(bnf);
}
public void test() throws Exception {
exitOnError = false;
showSQL = false;
for(int a=0; ; a++) {
int seed = RandomUtils.nextInt(Integer.MAX_VALUE);
System.out.println("a:" + a + " seed:" + seed);
testCase(seed);
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.test.TestBase;
public class TestSimpleIndex extends TestBase {
Connection conn;
Statement stat;
RandomGen random;
public void test() throws Exception {
deleteDb("simpleIndex");
conn = getConnection("simpleIndex");
random = new RandomGen(null);
stat = conn.createStatement();
for(int i=0; i<10000; i++) {
testIndex(i);
}
}
private void testIndex(int seed) throws Exception {
random.setSeed(seed);
String unique = random.nextBoolean() ? "UNIQUE " : "";
int len = random.getInt(2) + 1;
StringBuffer buff = new StringBuffer();
for(int i=0; i<len; i++) {
if(i > 0) {
buff.append(", ");
}
buff.append((char)('A' + random.getInt(3)));
}
String cols = buff.toString();
execute("CREATE MEMORY TABLE TEST_M(A INT, B INT, C INT, DATA VARCHAR(255))");
execute("CREATE CACHED TABLE TEST_D(A INT, B INT, C INT, DATA VARCHAR(255))");
execute("CREATE MEMORY TABLE TEST_MI(A INT, B INT, C INT, DATA VARCHAR(255))");
execute("CREATE CACHED TABLE TEST_DI(A INT, B INT, C INT, DATA VARCHAR(255))");
execute("CREATE " + unique + "INDEX M ON TEST_MI("+cols+")");
execute("CREATE " + unique + "INDEX D ON TEST_DI("+cols+")");
for(int i=0; i<100; i++) {
println("i="+i);
testRows(i);
}
execute("DROP INDEX M");
execute("DROP INDEX D");
execute("DROP TABLE TEST_M");
execute("DROP TABLE TEST_D");
execute("DROP TABLE TEST_MI");
execute("DROP TABLE TEST_DI");
}
private void testRows(int id) throws Exception {
String a = randomValue(), b = randomValue(), c = randomValue();
String data = a + "/" + b + "/" + c;
String sql = "VALUES("+a+", "+b+", "+c+", '" + data + "')";
boolean em, ed;
// if(id==73) {
// print("halt");
// }
try {
execute("INSERT INTO TEST_MI " + sql);
em = false;
} catch(SQLException e) {
em = true;
}
try {
execute("INSERT INTO TEST_DI " + sql);
ed = false;
} catch(SQLException e) {
ed = true;
}
if(em != ed) {
error("different result: ");
}
if(!em) {
execute("INSERT INTO TEST_M " + sql);
execute("INSERT INTO TEST_D " + sql);
}
StringBuffer buff = new StringBuffer("WHERE 1=1");
int len = random.getLog(10);
for(int i=0; i<len; i++) {
buff.append(" AND ");
buff.append('A' + random.getInt(3));
switch(random.getInt(10)) {
case 0:
buff.append("<");
buff.append((random.getInt(100) - 50));
break;
case 1:
buff.append("<=");
buff.append((random.getInt(100) - 50));
break;
case 2:
buff.append(">");
buff.append((random.getInt(100) - 50));
break;
case 3:
buff.append(">=");
buff.append((random.getInt(100) - 50));
break;
case 4:
buff.append("<>");
buff.append((random.getInt(100) - 50));
break;
case 5:
buff.append(" IS NULL");
break;
case 6:
buff.append(" IS NOT NULL");
break;
default:
buff.append("=");
buff.append((random.getInt(100) - 50));
}
}
String where = buff.toString();
String r1 = getResult("SELECT DATA FROM TEST_M " + where + " ORDER BY DATA");
String r2 = getResult("SELECT DATA FROM TEST_D " + where + " ORDER BY DATA");
String r3 = getResult("SELECT DATA FROM TEST_MI " + where + " ORDER BY DATA");
String r4 = getResult("SELECT DATA FROM TEST_DI " + where + " ORDER BY DATA");
check(r1, r2);
check(r1, r3);
check(r1, r4);
}
private String getResult(String sql) throws Exception {
ResultSet rs = stat.executeQuery(sql);
StringBuffer buff = new StringBuffer();
while(rs.next()) {
buff.append(rs.getString(1));
buff.append("; ");
}
rs.close();
return buff.toString();
}
private String randomValue() {
return random.getInt(10) == 0 ? "NULL" : ""+(random.getInt(100) - 50);
}
private void execute(String sql) throws Exception {
try {
println(sql + ";");
stat.execute(sql);
println("> update count: 1");
} catch(SQLException e) {
println("> exception");
throw e;
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.util.ArrayList;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
import org.h2.util.RandomUtils;
// TODO hsqldb: call 1||null should return 1 but returns null
// TODO hsqldb: call mod(1) should return invalid parameter count but returns null
public class TestSynth extends TestBase {
static final int H2 = 0, H2_MEM = 1, HSQLDB = 2, MYSQL = 3, POSTGRESQL = 4;
private DbState db = new DbState(this);
private ArrayList databases;
private ArrayList commands;
private RandomGen random = new RandomGen(this);
private boolean showError, showLog;
private boolean stopImmediately;
private int mode;
private String DIR = "synth";
public boolean is(int isType) {
return mode == isType;
}
public TestSynth() {
}
public RandomGen random() {
return random;
}
public String randomIdentifier() {
int len = random.getLog(8)+2;
while(true) {
return random.randomString(len);
}
}
private void add(Command command) throws Exception {
command.run(db);
commands.add(command);
}
private void addRandomCommands() throws Exception {
switch(random.getInt(20)) {
case 0: {
add(Command.getDisconnect(this));
add(Command.getConnect(this));
break;
}
case 1: {
Table table = Table.newRandomTable(this);
add(Command.getCreateTable(this, table));
break;
}
case 2: {
Table table = randomTable();
add(Command.getCreateIndex(this, table.newRandomIndex()));
break;
}
case 3:
case 4:
case 5: {
Table table = randomTable();
add(Command.getRandomInsert(this, table));
break;
}
case 6:
case 7:
case 8: {
Table table = randomTable();
add(Command.getRandomUpdate(this, table));
break;
}
case 9:
case 10: {
Table table = randomTable();
add(Command.getRandomDelete(this, table));
break;
}
default: {
Table table = randomTable();
add(Command.getRandomSelect(this, table));
}
}
}
private void testRun(int seed) throws Exception {
random.setSeed(seed);
commands = new ArrayList();
add(Command.getConnect(this));
add(Command.getReset(this));
for(int i=0; i<1; i++) {
Table table = Table.newRandomTable(this);
add(Command.getCreateTable(this, table));
add(Command.getCreateIndex(this, table.newRandomIndex()));
}
for(int i=0; i<100; i++) {
addRandomCommands();
}
// for (int i = 0; i < 20; i++) {
// Table table = randomTable();
// add(Command.getRandomInsert(this, table));
// }
// for (int i = 0; i < 100; i++) {
// Table table = randomTable();
// add(Command.getRandomSelect(this, table));
// }
// for (int i = 0; i < 10; i++) {
// Table table = randomTable();
// add(Command.getRandomUpdate(this, table));
// }
// for (int i = 0; i < 30; i++) {
// Table table = randomTable();
// add(Command.getRandomSelect(this, table));
// }
// for (int i = 0; i < 50; i++) {
// Table table = randomTable();
// add(Command.getRandomDelete(this, table));
// }
// for (int i = 0; i < 10; i++) {
// Table table = randomTable();
// add(Command.getRandomSelect(this, table));
// }
// while(true) {
// Table table = randomTable();
// if(table == null) {
// break;
// }
// add(Command.getDropTable(this, table));
// }
add(Command.getDisconnect(this));
add(Command.getEnd(this));
for(int i=0; i<commands.size(); i++) {
Command command = (Command) commands.get(i);
boolean stop = process(seed, i, command);
if(stop) {
break;
}
}
}
private boolean process(int seed, int id, Command command) throws Exception {
try {
ArrayList results = new ArrayList();
for(int i=0; i<databases.size(); i++) {
DbInterface db = (DbInterface)databases.get(i);
Result result = command.run(db);
results.add(result);
if(showError && i==0) {
// result.log();
}
}
compareResults(results);
} catch(Error e) {
if(showError) {
e.printStackTrace();
}
System.out.println("new TestSynth().init(test).testCase(" + seed+"); // id="+id +" " + e.toString());
if(stopImmediately) {
System.exit(0);
}
return true;
}
return false;
}
private void compareResults(ArrayList results) {
Result original = (Result) results.get(0);
for (int i = 1; i < results.size(); i++) {
Result copy = (Result) results.get(i);
if (original.compareTo(copy) != 0) {
if (showError) {
throw new Error("Results don't match: original (0): \r\n" + original + "\r\nother:\r\n" + copy);
} else {
throw new Error("Results don't match");
}
}
}
}
public Table randomTable() {
return db.randomTable();
}
public void log(int id, String s) {
if(showLog && id==0) {
System.out.println(s);
}
}
public int getMode() {
return mode;
}
private void addDatabase(String className, String url, String user, String password, boolean useSentinel) {
DbConnection db = new DbConnection(this, className, url, user, password, databases.size(), useSentinel);
databases.add(db);
}
// java -cp .;..\..\java\mysql.jar;..\..\java\ldbc.jar;..\..\java\postgresql-8.0-311.jdbc3.jar org.h2.test.TestAll
public TestBase init(TestAll conf) throws Exception {
super.init(conf);
BASE_DIR = "dataSynth";
deleteDb("synth");
databases = new ArrayList();
// mode = HSQLDB;
// addDatabase("org.hsqldb.jdbcDriver", "jdbc:hsqldb:test", "sa", "" );
// addDatabase("org.h2.Driver", "jdbc:h2:synth;mode=hsqldb", "sa", "");
// mode = POSTGRESQL;
// addDatabase("org.postgresql.Driver", "jdbc:postgresql:test", "sa", "sa");
// addDatabase("org.h2.Driver", "jdbc:h2:synth;mode=postgresql", "sa", "");
mode = H2_MEM;
Class.forName("org.h2.Driver");
addDatabase("org.h2.Driver", "jdbc:h2:mem:synth", "sa", "", true);
addDatabase("org.h2.Driver", "jdbc:h2:"+BASE_DIR+"/"+DIR+"/synth", "sa", "", false);
// addDatabase("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "sa", "");
// addDatabase("org.h2.Driver", "jdbc:h2:synth;mode=mysql", "sa", "");
// addDatabase("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/test", "sa", "");
// addDatabase("org.ldbc.jdbc.jdbcDriver", "jdbc:ldbc:mysql://localhost/test", "sa", "");
// addDatabase("org.h2.Driver", "jdbc:h2:inmemory:synth", "sa", "");
// MySQL: NOT is bound to column: NOT ID = 1 means (NOT ID) = 1 instead of NOT (ID=1)
for (int i = 0; i < databases.size(); i++) {
DbConnection conn = (DbConnection) databases.get(i);
System.out.println(i + " = " + conn.toString());
}
showError = true;
showLog = false;
// stopImmediately = true;
// showLog = true;
// testRun(110600); // id=27 java.lang.Error: Results don't match: original (0):
// System.exit(0);
BASE_DIR = "data";
return this;
}
public void testCase(int i) throws Exception {
BASE_DIR = "dataCrash";
deleteDb(BASE_DIR, DIR+"/synth");
try {
printTime("TestSynth " + i);
testRun(i);
} catch (Error e) {
System.out.println(e.toString());
e.printStackTrace();
System.exit(0);
}
BASE_DIR = "data";
}
public void test() throws Exception {
while(true) {
int seed = RandomUtils.nextInt(Integer.MAX_VALUE);
testCase(seed);
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Random;
import org.h2.test.TestBase;
public class TestThreads extends TestBase implements Runnable {
public TestThreads() {
}
public void test() throws Exception {
deleteDb("threads");
Connection conn = getConnection("threads;MAX_LOG_SIZE=1");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST_A(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("CREATE TABLE TEST_B(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("CREATE TABLE TEST_C(ID INT PRIMARY KEY, NAME VARCHAR)");
int len = 1000;
insertRows(conn, "TEST_A", len);
insertRows(conn, "TEST_B", len);
insertRows(conn, "TEST_C", len);
maxId = len;
int threadCount = 4;
Thread[] threads = new Thread[threadCount];
for(int i=0; i<threadCount; i++) {
String table = random.nextBoolean() ? null : getRandomTable();
int op = random.nextInt(OP_TYPES);
op = i % 2 == 1 ? RECONNECT : CHECKPOINT;
threads[i] = new Thread(new TestThreads(this, op, table));
}
for(int i=0; i<threadCount; i++) {
threads[i].start();
}
Thread.sleep(10000);
stop = true;
for(int i=0; i<threadCount; i++) {
threads[i].join();
}
conn.close();
conn = getConnection("threads");
checkTable(conn, "TEST_A");
checkTable(conn, "TEST_B");
checkTable(conn, "TEST_C");
conn.close();
}
private void insertRows(Connection conn, String tableName, int len) throws Exception {
PreparedStatement prep = conn.prepareStatement("INSERT INTO " +tableName+" VALUES(?, 'Hi')");
for(int i=0; i<len; i++) {
prep.setInt(1, i);
prep.execute();
}
}
private void checkTable(Connection conn, String tableName) throws Exception {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * FROM "+tableName+" ORDER BY ID");
while(rs.next()) {
int id = rs.getInt(1);
String name = rs.getString(2);
System.out.println("id="+id+" name="+name);
}
}
private int maxId = 1;
private volatile boolean stop;
private TestThreads master;
private int type;
private String table;
private Random random = new Random();
private static final int INSERT=0, UPDATE=1, DELETE=2;
private static final int SELECT_ONE=3, SELECT_ALL=4, CHECKPOINT=5, RECONNECT=6;
private static final int OP_TYPES = RECONNECT+1;
private int getMaxId() {
return maxId;
}
private synchronized int incrementMaxId() {
return maxId++;
}
TestThreads(TestThreads master, int type, String table) {
this.master = master;
this.type = type;
this.table = table;
}
private String getRandomTable() {
return "TEST_" + (char)('A' + random.nextInt(3));
}
public void run() {
try {
String t = table == null ? getRandomTable() : table;
Connection conn = master.getConnection("threads");
Statement stat = conn.createStatement();
ResultSet rs;
int max = master.getMaxId();
int rid = random.nextInt(max);
for(int i=0; !master.stop; i++) {
switch(type) {
case INSERT:
max = master.incrementMaxId();
stat.execute("INSERT INTO "+t+"(ID, NAME) VALUES("+max+", 'Hello')");
break;
case UPDATE:
stat.execute("UPDATE "+t+" SET NAME='World "+rid+"' WHERE ID="+rid);
break;
case DELETE:
stat.execute("DELETE FROM "+t+" WHERE ID="+rid);
break;
case SELECT_ALL:
rs = stat.executeQuery("SELECT * FROM "+t+" ORDER BY ID");
while(rs.next()) {
// nothing
}
break;
case SELECT_ONE:
rs = stat.executeQuery("SELECT * FROM "+t+" WHERE ID=" + rid);
while(rs.next()) {
// nothing
}
break;
case CHECKPOINT:
stat.execute("CHECKPOINT");
break;
case RECONNECT:
conn.close();
conn = master.getConnection("threads");
break;
}
}
conn.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
/*
* Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
public class Value {
private int type;
private Object data;
private TestSynth config;
private Value(TestSynth config, int type, Object data) {
this.config = config;
this.type = type;
this.data = data;
}
String getSQL() {
if(data == null) {
return "NULL";
}
switch(type) {
case Types.DECIMAL:
case Types.NUMERIC:
case Types.BIGINT:
case Types.INTEGER:
case Types.DOUBLE:
case Types.REAL:
return data.toString();
case Types.CLOB:
case Types.VARCHAR:
case Types.CHAR:
case Types.OTHER:
case Types.LONGVARCHAR:
return "'" + data.toString() + "'";
case Types.BLOB:
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return getBlobSQL();
case Types.DATE:
return getDateSQL((Date)data);
case Types.TIME:
return getTimeSQL((Time)data);
case Types.TIMESTAMP:
return getTimestampSQL((Timestamp)data);
case Types.BOOLEAN:
case Types.BIT:
return (String)data;
default:
throw new Error("type="+type);
}
}
private static Date randomDate(TestSynth config) {
return config.random().randomDate();
}
private static Double randomDouble(TestSynth config) {
return new Double(config.random().getInt(100)/10.);
}
private static Long randomLong(TestSynth config) {
return new Long(config.random().getInt(1000));
}
private static Time randomTime(TestSynth config) {
return config.random().randomTime();
}
private static Timestamp randomTimestamp(TestSynth config) {
return config.random().randomTimestamp();
}
private String getTimestampSQL(Timestamp ts) {
String s = "'"+ts.toString()+"'";
if(config.getMode() != TestSynth.HSQLDB) {
s = "TIMESTAMP " + s;
}
return s;
}
private String getDateSQL(Date date) {
String s = "'"+date.toString()+"'";
if(config.getMode() != TestSynth.HSQLDB) {
s = "DATE " + s;
}
return s;
}
private String getTimeSQL(Time time) {
String s = "'"+time.toString()+"'";
if(config.getMode() != TestSynth.HSQLDB) {
s = "TIME " + s;
}
return s;
}
private String getBlobSQL() {
byte[] bytes = (byte[]) data;
// StringBuffer buff = new StringBuffer("X'");
StringBuffer buff = new StringBuffer("'");
for(int i=0; i<bytes.length; i++) {
int c = bytes[i] & 0xff;
buff.append(Integer.toHexString(c >> 4 & 0xf));
buff.append(Integer.toHexString(c & 0xf));
}
buff.append("'");
return buff.toString();
}
public static Value read(TestSynth config, ResultSet rs, int index) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
Object data;
int type = meta.getColumnType(index);
switch(type) {
case Types.REAL:
case Types.DOUBLE:
data = new Double(rs.getDouble(index));
break;
case Types.BIGINT:
data = new Long(rs.getLong(index));
break;
case Types.DECIMAL:
case Types.NUMERIC:
data = rs.getBigDecimal(index);
break;
case Types.BLOB:
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
data = rs.getBytes(index);
break;
case Types.OTHER:
case Types.CLOB:
case Types.VARCHAR:
case Types.LONGVARCHAR:
case Types.CHAR:
data = rs.getString(index);
break;
case Types.DATE:
data = rs.getDate(index);
break;
case Types.TIME:
data = rs.getTime(index);
break;
case Types.TIMESTAMP:
data = rs.getTimestamp(index);
break;
case Types.INTEGER:
data = new Integer(rs.getInt(index));
break;
case Types.NULL:
data = null;
break;
case Types.BOOLEAN:
case Types.BIT:
data = rs.getBoolean(index) ? "TRUE" : "FALSE";
break;
default:
throw new Error("type="+type);
}
if(rs.wasNull()) {
data = null;
}
return new Value(config, type, data);
}
public static Value getRandom(TestSynth config, int type, int precision, int scale, boolean mayBeNull) {
Object data;
if(mayBeNull && config.random().getBoolean(20)) {
return new Value(config, type, null);
}
switch(type) {
case Types.BIGINT:
data = randomLong(config);
break;
case Types.DOUBLE:
data = randomDouble(config);
break;
case Types.DECIMAL:
data = randomDecimal(config, precision, scale);
break;
case Types.VARBINARY:
case Types.BINARY:
case Types.BLOB:
data = randomBytes(config, precision);
break;
case Types.CLOB:
case Types.VARCHAR:
data = config.random().randomString(config.random().getInt(precision));
break;
case Types.DATE:
data = randomDate(config);
break;
case Types.TIME:
data = randomTime(config);
break;
case Types.TIMESTAMP:
data = randomTimestamp(config);
break;
case Types.INTEGER:
data = randomInt(config);
break;
case Types.BOOLEAN:
case Types.BIT:
data = config.random().getBoolean(50) ? "TRUE" : "FALSE";
break;
default:
throw new Error("type="+type);
}
return new Value(config, type, data);
}
private static Object randomInt(TestSynth config) {
int value;
if(config.is(TestSynth.POSTGRESQL)) {
value = config.random().getInt(1000000);
} else {
value = config.random().getRandomInt();
}
return new Integer(value);
}
private static byte[] randomBytes(TestSynth config, int max) {
int len = config.random().getLog(max);
byte[] data = new byte[len];
config.random().getBytes(data);
return data;
}
private static BigDecimal randomDecimal(TestSynth config, int precision, int scale) {
int len = config.random().getLog(precision-scale)+scale;
if(len==0) {
len++;
}
StringBuffer buff = new StringBuffer();
for(int i=0; i<len; i++) {
buff.append((char) ('0' + config.random().getInt(10)));
}
buff.insert(len - scale, '.');
if(config.random().getBoolean(20)) {
buff.insert(0, '-');
}
return new BigDecimal(buff.toString());
}
public int compareTo(Object o) {
Value v = (Value)o;
if(type != v.type) {
throw new Error("compare "+type+" "+v.type+" "+data+" "+v.data);
}
if(data==null) {
return (v.data==null) ? 0 : -1;
} else if(v.data==null) {
return 1;
}
switch(type) {
case Types.DECIMAL:
return ((BigDecimal)data).compareTo((BigDecimal)v.data);
case Types.BLOB:
case Types.VARBINARY:
case Types.BINARY:
return compareBytes((byte[])data, (byte[])v.data);
case Types.CLOB:
case Types.VARCHAR:
return data.toString().compareTo(v.data.toString());
case Types.DATE:
return ((Date)data).compareTo((Date)v.data);
case Types.INTEGER:
return ((Integer)data).compareTo((Integer)v.data);
default:
throw new Error("type="+type);
}
}
static int compareBytes(byte[] a, byte[] b) {
int al = a.length, bl = b.length;
int len = Math.min(al, bl);
for(int i=0; i<len; i++) {
int x = a[i] & 0xff;
int y = b[i] & 0xff;
if(x==y) {
continue;
}
return x>y ? 1 : -1;
}
return al==bl ? 0 : al > bl ? 1 : -1;
}
public String toString() {
return getSQL();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论