Skip to content

Commit 7f99a12

Browse files
authored
feat: add MySQLUpdateGenerator class (sqlancer#849)
* Add MySQLUpdateGenerator class * Extract common update insert errors in mysql
1 parent 5bcd9ce commit 7f99a12

4 files changed

Lines changed: 76 additions & 8 deletions

File tree

src/sqlancer/mysql/MySQLErrors.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,15 @@ public static void addExpressionErrors(ExpectedErrors errors) {
1616
}
1717
}
1818

19+
public static void addInsertUpdateErrors(ExpectedErrors errors) {
20+
errors.add("doesn't have a default value");
21+
errors.add("Data truncation");
22+
errors.add("Incorrect integer value");
23+
errors.add("Duplicate entry");
24+
errors.add("Data truncated for column");
25+
errors.add("Data truncated for functional index");
26+
errors.add("cannot be null");
27+
errors.add("Incorrect decimal value");
28+
}
29+
1930
}

src/sqlancer/mysql/MySQLProvider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import sqlancer.mysql.gen.MySQLSetGenerator;
3232
import sqlancer.mysql.gen.MySQLTableGenerator;
3333
import sqlancer.mysql.gen.MySQLTruncateTableGenerator;
34+
import sqlancer.mysql.gen.MySQLUpdateGenerator;
3435
import sqlancer.mysql.gen.admin.MySQLFlush;
3536
import sqlancer.mysql.gen.admin.MySQLReset;
3637
import sqlancer.mysql.gen.datadef.MySQLIndexGenerator;
@@ -67,6 +68,7 @@ enum Action implements AbstractAction<MySQLGlobalState> {
6768
String tableName = DBMSCommon.createTableName(g.getSchema().getDatabaseTables().size());
6869
return MySQLTableGenerator.generate(g, tableName);
6970
}), //
71+
UPDATE(MySQLUpdateGenerator::create), //
7072
DELETE(MySQLDeleteGenerator::delete), //
7173
DROP_INDEX(MySQLDropIndex::generate);
7274

@@ -132,6 +134,9 @@ private static int mapActions(MySQLGlobalState globalState, Action a) {
132134
case SELECT_INFO:
133135
nrPerformed = r.getInteger(0, 10);
134136
break;
137+
case UPDATE:
138+
nrPerformed = r.getInteger(0, 10);
139+
break;
135140
case DELETE:
136141
nrPerformed = r.getInteger(0, 10);
137142
break;

src/sqlancer/mysql/gen/MySQLInsertGenerator.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sqlancer.Randomly;
88
import sqlancer.common.query.ExpectedErrors;
99
import sqlancer.common.query.SQLQueryAdapter;
10+
import sqlancer.mysql.MySQLErrors;
1011
import sqlancer.mysql.MySQLGlobalState;
1112
import sqlancer.mysql.MySQLSchema.MySQLColumn;
1213
import sqlancer.mysql.MySQLSchema.MySQLTable;
@@ -88,14 +89,7 @@ private SQLQueryAdapter generateInto() {
8889
}
8990
sb.append(")");
9091
}
91-
errors.add("doesn't have a default value");
92-
errors.add("Data truncation");
93-
errors.add("Incorrect integer value");
94-
errors.add("Duplicate entry");
95-
errors.add("Data truncated for functional index");
96-
errors.add("Data truncated for column");
97-
errors.add("cannot be null");
98-
errors.add("Incorrect decimal value");
92+
MySQLErrors.addInsertUpdateErrors(errors);
9993
return new SQLQueryAdapter(sb.toString(), errors);
10094
}
10195

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package sqlancer.mysql.gen;
2+
3+
import java.sql.SQLException;
4+
import java.util.List;
5+
6+
import sqlancer.Randomly;
7+
import sqlancer.common.gen.AbstractUpdateGenerator;
8+
import sqlancer.common.query.SQLQueryAdapter;
9+
import sqlancer.mysql.MySQLErrors;
10+
import sqlancer.mysql.MySQLGlobalState;
11+
import sqlancer.mysql.MySQLSchema.MySQLColumn;
12+
import sqlancer.mysql.MySQLSchema.MySQLTable;
13+
import sqlancer.mysql.MySQLVisitor;
14+
15+
public class MySQLUpdateGenerator extends AbstractUpdateGenerator<MySQLColumn> {
16+
17+
private final MySQLGlobalState globalState;
18+
private MySQLExpressionGenerator gen;
19+
20+
public MySQLUpdateGenerator(MySQLGlobalState globalState) {
21+
this.globalState = globalState;
22+
}
23+
24+
public static SQLQueryAdapter create(MySQLGlobalState globalState) throws SQLException {
25+
return new MySQLUpdateGenerator(globalState).generate();
26+
}
27+
28+
private SQLQueryAdapter generate() throws SQLException {
29+
MySQLTable table = globalState.getSchema().getRandomTable(t -> !t.isView());
30+
List<MySQLColumn> columns = table.getRandomNonEmptyColumnSubset();
31+
gen = new MySQLExpressionGenerator(globalState).setColumns(table.getColumns());
32+
sb.append("UPDATE ");
33+
sb.append(table.getName());
34+
sb.append(" SET ");
35+
updateColumns(columns);
36+
if (Randomly.getBoolean()) {
37+
sb.append(" WHERE ");
38+
MySQLErrors.addExpressionErrors(errors);
39+
sb.append(MySQLVisitor.asString(gen.generateExpression()));
40+
}
41+
MySQLErrors.addInsertUpdateErrors(errors);
42+
errors.add("doesn't have this option");
43+
44+
return new SQLQueryAdapter(sb.toString(), errors);
45+
}
46+
47+
@Override
48+
protected void updateValue(MySQLColumn column) {
49+
if (Randomly.getBoolean()) {
50+
sb.append(gen.generateConstant());
51+
} else if (Randomly.getBoolean()) {
52+
sb.append("DEFAULT");
53+
} else {
54+
sb.append(MySQLVisitor.asString(gen.generateExpression()));
55+
}
56+
}
57+
58+
}

0 commit comments

Comments
 (0)