/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.util.ArrayList;
import java.util.Random;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.bnf.RuleHead;
import org.h2.util.New;
/**
* A BNF visitor that generates a random SQL statement.
*/
public class BnfRandom implements BnfVisitor {
private static final boolean SHOW_SYNTAX = false;
private final Bnf config;
private final Random random = new Random();
private final ArrayList<RuleHead> statements = New.arrayList();
private int level;
private String sql;
BnfRandom() throws Exception {
config = Bnf.getInstance(null);
config.linkStatements();
ArrayList<RuleHead> all = config.getStatements();
// go backwards so we can append at the end
for (int i = all.size() - 1; i >= 0; i--) {
RuleHead r = all.get(i);
String topic = r.getTopic().toLowerCase();
int weight = 0;
if (topic.equals("select")) {
weight = 10;
} else if (topic.equals("create table")) {
weight = 20;
} else if (topic.equals("insert")) {
weight = 5;
} else if (topic.startsWith("update")) {
weight = 3;
} else if (topic.startsWith("delete")) {
weight = 3;
} else if (topic.startsWith("drop")) {
weight = 2;
}
if (SHOW_SYNTAX) {
System.out.println(r.getTopic());
}
for (int j = 0; j < weight; j++) {
statements.add(r);
}
}
}
public String getRandomSQL() {
int sid = random.nextInt(statements.size());
RuleHead r = statements.get(sid);
level = 0;
r.getRule().accept(this);
sql = sql.trim();
if (sql.length() > 0) {
if (sql.indexOf("TRACE_LEVEL_") < 0 && sql.indexOf("COLLATION") < 0
&& sql.indexOf("SCRIPT ") < 0 && sql.indexOf("CSVWRITE") < 0
&& sql.indexOf("BACKUP") < 0 && sql.indexOf("DB_CLOSE_DELAY") < 0) {
if (SHOW_SYNTAX) {
System.out.println(" " + sql);
}
return sql;
}
}
return null;
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
if (keyword) {
if (name.startsWith(";")) {
sql = "";
} else {
sql = name.length() > 1 ? " " + name + " " : name;
}
} else if (link != null) {
level++;
link.accept(this);
level--;
} else {
throw new AssertionError(name);
}
}
public void visitRuleFixed(int type) {
sql = getRandomFixed(type);
}
private String getRandomFixed(int type) {
Random r = random;
switch (type) {
case RuleFixed.YMD:
return (1800 + r.nextInt(200)) + "-" + (1 + r.nextInt(12)) + "-" + (1 + r.nextInt(31));
case RuleFixed.HMS:
return (r.nextInt(24)) + "-" + (r.nextInt(60)) + "-" + (r.nextInt(60));
case RuleFixed.NANOS:
return "" + (r.nextInt(100000) + r.nextInt(10000));
case RuleFixed.ANY_UNTIL_EOL:
case RuleFixed.ANY_EXCEPT_SINGLE_QUOTE:
case RuleFixed.ANY_EXCEPT_DOUBLE_QUOTE:
case RuleFixed.ANY_WORD:
case RuleFixed.ANY_EXCEPT_2_DOLLAR:
case RuleFixed.ANY_UNTIL_END: {
StringBuilder buff = new StringBuilder();
int len = r.nextBoolean() ? 1 : r.nextInt(5);
for (int i = 0; i < len; i++) {
buff.append((char) ('A' + r.nextInt('C' - 'A')));
}
return buff.toString();
}
case RuleFixed.HEX_START:
return "0x";
case RuleFixed.CONCAT:
return "||";
case RuleFixed.AZ_UNDERSCORE:
return "" + (char) ('A' + r.nextInt('C' - 'A'));
case RuleFixed.AF:
return "" + (char) ('A' + r.nextInt('F' - 'A'));
case RuleFixed.DIGIT:
return "" + (char) ('0' + r.nextInt(10));
case RuleFixed.OPEN_BRACKET:
return "[";
case RuleFixed.CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
if (or) {
if (level > 10) {
if (level > 1000) {
// better than stack overflow
throw new AssertionError();
}
list.get(0).accept(this);
return;
}
int idx = random.nextInt(list.size());
level++;
list.get(idx).accept(this);
level--;
return;
}
StringBuilder buff = new StringBuilder();
level++;
for (Rule r : list) {
r.accept(this);
buff.append(sql);
}
level--;
sql = buff.toString();
}
public void visitRuleOptional(Rule rule) {
if (level > 10 ? random.nextInt(level) == 1 : random.nextInt(4) == 1) {
level++;
rule.accept(this);
level--;
return;
}
sql = "";
}
public void visitRuleRepeat(boolean comma, Rule rule) {
rule.accept(this);
}
public void setSeed(int seed) {
random.setSeed(seed);
}
public int getStatementCount() {
return statements.size();
}
}