/*
* 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.sql;
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;
/**
* Represents a simple value.
*/
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;
}
/**
* Convert the value to a SQL string.
*
* @return the SQL string
*/
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 AssertionError("type=" + type);
}
}
private static Date randomDate(TestSynth config) {
return config.random().randomDate();
}
private static Double randomDouble(TestSynth config) {
return config.random().getInt(100) / 10.;
}
private static Long randomLong(TestSynth config) {
return Long.valueOf(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;
// StringBuilder buff = new StringBuilder("X'");
StringBuilder buff = new StringBuilder("'");
for (byte b : bytes) {
int c = b & 0xff;
buff.append(Integer.toHexString(c >> 4 & 0xf));
buff.append(Integer.toHexString(c & 0xf));
}
buff.append("'");
return buff.toString();
}
/**
* Read a value from a result set.
*
* @param config the configuration
* @param rs the result set
* @param index the column index
* @return the value
*/
static Value read(TestSynth config, ResultSet rs, int index) throws SQLException {
ResultSetMetaData meta = rs.getMetaData();
Object data;
int type = meta.getColumnType(index);
switch (type) {
case Types.REAL:
case Types.DOUBLE:
data = rs.getDouble(index);
break;
case Types.BIGINT:
data = 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 = 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 AssertionError("type=" + type);
}
if (rs.wasNull()) {
data = null;
}
return new Value(config, type, data);
}
/**
* Generate a random value.
*
* @param config the configuration
* @param type the value type
* @param precision the precision
* @param scale the scale
* @param mayBeNull if the value may be null or not
* @return the value
*/
static Value getRandom(TestSynth config, int type, int precision, int scale, boolean mayBeNull) {
Object data;
if (mayBeNull && config.random().getBoolean(20)) {
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 AssertionError("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 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++;
}
StringBuilder buff = new StringBuilder();
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());
}
// private int compareTo(Object o) {
// Value v = (Value) o;
// if (type != v.type) {
// throw new AssertionError("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 AssertionError("type=" + type);
// }
// }
// private static int compareBytes(byte[] a, byte[] b) {
// int al = a.length, bl = b.length;
// int len = Math.min(al, bl);
// for (int i = 0; i < len; i++) {
// int x = a[i] & 0xff;
// int y = b[i] & 0xff;
// if (x == y) {
// continue;
// }
// return x > y ? 1 : -1;
// }
// return al == bl ? 0 : al > bl ? 1 : -1;
// }
public String toString() {
return getSQL();
}
}