package sqlancer.hsqldb; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.List; import sqlancer.Randomly; import sqlancer.SQLConnection; import sqlancer.common.DBMSCommon; import sqlancer.common.schema.AbstractRelationalTable; import sqlancer.common.schema.AbstractSchema; import sqlancer.common.schema.AbstractTableColumn; import sqlancer.common.schema.TableIndex; public class HSQLDBSchema extends AbstractSchema { public HSQLDBSchema(List databaseTables) { super(databaseTables); } public static HSQLDBSchema fromConnection(SQLConnection connection, String databaseName) throws SQLException { List databaseTables = new ArrayList<>(); List tableNames = getTableNames(connection); for (String tableName : tableNames) { if (DBMSCommon.matchesIndexName(tableName)) { continue; // TODO: unexpected? } List databaseColumns = getTableColumns(connection, tableName); boolean isView = tableName.startsWith("v"); HSQLDBSchema.HSQLDBTable t = new HSQLDBSchema.HSQLDBTable(tableName, databaseColumns, isView); for (HSQLDBSchema.HSQLDBColumn c : databaseColumns) { c.setTable(t); } databaseTables.add(t); } return new HSQLDBSchema(databaseTables); } private static List getTableNames(SQLConnection con) throws SQLException { List tableNames = new ArrayList<>(); try (Statement s = con.createStatement()) { try (ResultSet rs = s .executeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC';")) { while (rs.next()) { tableNames.add(rs.getString("TABLE_NAME")); } } } return tableNames; } private static List getTableColumns(SQLConnection con, String tableName) throws SQLException { List tableNames = new ArrayList<>(); try (Statement s = con.createStatement()) { String sql = "SELECT COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_SIZE FROM INFORMATION_SCHEMA.SYSTEM_COLUMNS WHERE TABLE_NAME = '%s';"; try (ResultSet rs = s.executeQuery(String.format(sql, tableName))) { while (rs.next()) { HSQLDBDataType dataType = HSQLDBDataType.from(rs.getString("TYPE_NAME")); HSQLDBCompositeDataType compositeDataType = new HSQLDBCompositeDataType(dataType, rs.getInt("COLUMN_SIZE")); HSQLDBColumn column = new HSQLDBColumn(rs.getString("COLUMN_NAME"), null, compositeDataType); tableNames.add(column); } } } return tableNames; } public static class HSQLDBTable extends AbstractRelationalTable { public HSQLDBTable(String tableName, List columns, boolean isView) { super(tableName, columns, Collections.emptyList(), isView); } } public static class HSQLDBColumn extends AbstractTableColumn { public HSQLDBColumn(String name, HSQLDBTable table, HSQLDBCompositeDataType type) { super(name, table, type); } } public enum HSQLDBDataType { INTEGER, DOUBLE, BOOLEAN, CHAR, VARCHAR, BINARY, TIME, DATE, TIMESTAMP, NULL; public static HSQLDBSchema.HSQLDBDataType getRandomWithoutNull() { HSQLDBSchema.HSQLDBDataType dt; do { dt = Randomly.fromOptions(values()); } while (dt == HSQLDBSchema.HSQLDBDataType.NULL); return dt; } public static HSQLDBDataType from(String typeName) { for (HSQLDBDataType value : HSQLDBDataType.values()) { if (value.name().equals(typeName)) { return value; } } return NULL; } } public static class HSQLDBCompositeDataType { private final int size; private final HSQLDBDataType type; public HSQLDBCompositeDataType(HSQLDBDataType type, int size) { this.type = type; this.size = size; } public static HSQLDBCompositeDataType getRandomWithoutNull() { HSQLDBSchema.HSQLDBDataType type = HSQLDBSchema.HSQLDBDataType.getRandomWithoutNull(); return getRandomWithType(type); } public static HSQLDBCompositeDataType getRandomWithType(HSQLDBSchema.HSQLDBDataType type) { int size; switch (type) { case VARCHAR: case CHAR: case TIME: case BINARY: case TIMESTAMP: size = Randomly.fromOptions(4, 6, 8); break; case BOOLEAN: case INTEGER: case DOUBLE: // case UUID: // case OTHER: case DATE: size = 0; break; default: throw new AssertionError(type); } return new HSQLDBSchema.HSQLDBCompositeDataType(type, size); } public HSQLDBDataType getType() { return type; } public int getSize() { return size; } } }