forked from sqlancer/sqlancer
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathH2Schema.java
More file actions
230 lines (198 loc) · 8.22 KB
/
H2Schema.java
File metadata and controls
230 lines (198 loc) · 8.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
package sqlancer.h2;
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.schema.AbstractRelationalTable;
import sqlancer.common.schema.AbstractSchema;
import sqlancer.common.schema.AbstractTableColumn;
import sqlancer.common.schema.AbstractTables;
import sqlancer.common.schema.TableIndex;
import sqlancer.h2.H2Provider.H2GlobalState;
import sqlancer.h2.H2Schema.H2Table;
public class H2Schema extends AbstractSchema<H2GlobalState, H2Table> {
public enum H2DataType {
INT, BOOL, VARCHAR, DOUBLE, BINARY;
public static H2DataType getRandom() {
return Randomly.fromOptions(values());
}
}
public static class H2CompositeDataType {
static final int NO_PRECISION = -1;
private final H2DataType dataType;
private final int size;
private final int precision;
public H2CompositeDataType(H2DataType dataType, int size, int precision) {
this.dataType = dataType;
this.size = size;
this.precision = precision;
}
public H2DataType getPrimitiveDataType() {
return dataType;
}
public static H2CompositeDataType getRandom() {
H2DataType primitiveType = Randomly.fromOptions(H2DataType.INT, H2DataType.BOOL, H2DataType.DOUBLE,
H2DataType.BINARY);
int size = -1;
int precision = NO_PRECISION;
switch (primitiveType) {
case INT:
size = Randomly.fromOptions(1, 2, 4, 8);
break;
case DOUBLE:
size = Randomly.fromOptions(4, 8);
if (Randomly.getBoolean()) {
if (size == 4) {
precision = (int) Randomly.getNotCachedInteger(1, 25); // TODO: documentation states 0 as lower
// bound
} else {
precision = (int) Randomly.getNotCachedInteger(25, 54);
}
}
break;
case VARCHAR:
case BINARY:
precision = (int) Randomly.getNotCachedInteger(0, Integer.MAX_VALUE);
break;
default:
break;
}
return new H2CompositeDataType(primitiveType, size, precision);
}
@Override
public String toString() {
switch (dataType) {
case INT:
switch (size) {
case 1:
return "TINYINT";
case 2:
return Randomly.fromOptions("SMALLINT", "INT2");
case 4:
return Randomly.fromOptions("INT", "INTEGER", "MEDIUMINT", "INT4", "SIGNED");
case 8:
return Randomly.fromOptions("BIGINT", "INT8");
default:
throw new AssertionError(size);
}
case DOUBLE:
switch (size) {
case 4:
if (precision == NO_PRECISION) {
return Randomly.fromOptions("REAL", "FLOAT4");
} else {
assert precision >= 0 && precision <= 24;
return String.format("FLOAT(%d)", precision);
}
case 8:
if (precision == NO_PRECISION) {
return Randomly.fromOptions("DOUBLE", "DOUBLE PRECISION", "FLOAT8", "FLOAT");
} else {
assert precision >= 25 && precision <= 53;
return String.format("FLOAT(%d)", precision);
}
default:
throw new AssertionError(size);
}
case VARCHAR:
return /* String varCharType = */ Randomly.fromOptions("VARCHAR", "VARCHAR_IGNORECASE");
// if (precision == NO_PRECISION) {
// return varCharType;
// } else {
// return String.format("%s(%d)", varCharType, precision);
// }
case BINARY:
return "BINARY";
// return String.format("BINARY(%d)", precision);
default:
return dataType.toString();
}
}
}
public static class H2Column extends AbstractTableColumn<H2Table, H2CompositeDataType> {
public H2Column(String name, H2CompositeDataType columnType) {
super(name, null, columnType);
}
}
public static class H2Tables extends AbstractTables<H2Table, H2Column> {
public H2Tables(List<H2Table> tables) {
super(tables);
}
}
public H2Schema(List<H2Table> databaseTables) {
super(databaseTables);
}
public H2Tables getRandomTableNonEmptyTables() {
return new H2Tables(Randomly.nonEmptySubset(getDatabaseTables()));
}
public static class H2Table extends AbstractRelationalTable<H2Column, TableIndex, H2GlobalState> {
public H2Table(String tableName, List<H2Column> columns) {
super(tableName, columns, Collections.emptyList(), tableName.startsWith("V"));
}
}
public static H2Schema fromConnection(SQLConnection con, String databaseName) throws SQLException {
List<H2Table> databaseTables = new ArrayList<>();
List<String> tableNames = getTableNames(con);
for (String tableName : tableNames) {
List<H2Column> databaseColumns = getTableColumns(con, tableName);
H2Table t = new H2Table(tableName, databaseColumns);
for (H2Column c : databaseColumns) {
c.setTable(t);
}
databaseTables.add(t);
}
return new H2Schema(databaseTables);
}
private static List<String> getTableNames(SQLConnection con) throws SQLException {
List<String> tableNames = new ArrayList<>();
try (Statement s = con.createStatement()) {
try (ResultSet rs = s.executeQuery("SHOW TABLES")) {
while (rs.next()) {
tableNames.add(rs.getString("TABLE_NAME"));
}
}
}
return tableNames;
}
private static List<H2Column> getTableColumns(SQLConnection con, String tableName) throws SQLException {
List<H2Column> columns = new ArrayList<>();
try (Statement s = con.createStatement()) {
try (ResultSet rs = s.executeQuery(String.format("SHOW COLUMNS FROM %s;", tableName))) {
while (rs.next()) {
String columnName = rs.getString("COLUMN_NAME");
String columnType = rs.getString("TYPE");
H2DataType primitiveType = getColumnType(columnType);
H2Column c = new H2Column(columnName,
new H2CompositeDataType(primitiveType, -1, -1 /* TODO: read size and precision */));
columns.add(c);
}
}
}
return columns;
}
private static H2DataType getColumnType(String columnType) {
if (columnType.startsWith("INTEGER") || columnType.startsWith("SMALLINT") || columnType.startsWith("TINYINT")
|| columnType.startsWith("BIGINT")) {
return H2DataType.INT;
} else if (columnType.startsWith("BOOLEAN")) {
return H2DataType.BOOL;
} else if (columnType.startsWith("CHARACTER VARYING")) {
return H2DataType.VARCHAR;
} else if (columnType.startsWith("DOUBLE") || columnType.startsWith("DECFLOAT") || columnType.startsWith("REAL")
|| columnType.startsWith("FLOAT")) {
return H2DataType.DOUBLE;
} else if (columnType.startsWith("NUMERIC")) {
return H2DataType.INT;
} else if (columnType.contentEquals("NULL")) {
return H2DataType.INT; // for a NULL view column
} else if (columnType.startsWith("BINARY")) {
return H2DataType.BINARY;
} else {
throw new AssertionError(columnType);
}
}
}