/* * 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(); } }