Skip to content

Commit 514f149

Browse files
committed
Add support for LIMIT on DELETE statements in EET
1 parent 6ac8455 commit 514f149

2 files changed

Lines changed: 40 additions & 5 deletions

File tree

src/sqlancer/common/gen/EETDMLGenerator.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package sqlancer.common.gen;
22

3+
import java.util.ArrayList;
4+
import java.util.List;
5+
36
import sqlancer.common.ast.newast.Expression;
47
import sqlancer.common.oracle.EETTransformer;
58
import sqlancer.common.schema.AbstractTable;
@@ -122,17 +125,40 @@ default String selectRowIdsStatement(T table) {
122125
}
123126

124127
/**
125-
* SQL that deletes the rows of {@code table} matching {@code predicate}.
128+
* SQL that deletes the rows of {@code table} matching {@code predicate}, optionally limited to the first
129+
* {@code limit} rows.
130+
*
131+
* <p>
132+
* When {@code limit} is non-null, the statement is ordered by {@code orderByColumns} followed by
133+
* {@link #ROW_ID_COLUMN} as a tiebreaker. Because the identifiers are unique, this is always a total order (even when
134+
* the ordering columns tie), so the "first {@code limit}" rows are identical for the original and
135+
* transformed statements. Varying the ordering columns exercises more access
136+
* paths than the row id alone would. The caller must pass the same {@code orderByColumns} and {@code limit}
137+
* to both statements; neither is transformed.
126138
*
127139
* @param table
128140
* the table to delete from
129141
* @param predicate
130142
* the WHERE predicate; rendered via {@link #asString}
143+
* @param orderByColumns
144+
* the columns to order by before the row-id tiebreaker (may be empty); only used when {@code limit} is
145+
* non-null
146+
* @param limit
147+
* the maximum number of rows to delete, or {@code null} for no limit
131148
*
132149
* @return the SQL statement
133150
*/
134-
default String deleteStatement(T table, E predicate) {
135-
return "DELETE FROM " + table.getName() + " WHERE " + asString(predicate);
151+
default String deleteStatement(T table, E predicate, List<C> orderByColumns, Integer limit) {
152+
String statement = "DELETE FROM " + table.getName() + " WHERE " + asString(predicate);
153+
if (limit != null) {
154+
List<String> orderBy = new ArrayList<>();
155+
for (C column : orderByColumns) {
156+
orderBy.add(column.getName());
157+
}
158+
orderBy.add(ROW_ID_COLUMN); // unique tiebreaker: guarantees a total order regardless of the columns above
159+
statement += " ORDER BY " + String.join(", ", orderBy) + " LIMIT " + limit;
160+
}
161+
return statement;
136162
}
137163

138164
/**

src/sqlancer/common/oracle/EETDMLOracle.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,17 @@ public void check() throws SQLException {
8484
// The WHERE predicate is evaluated in a boolean context.
8585
E transformedPredicate = transformer.transform(predicate, true);
8686

87-
String originalDelete = gen.deleteStatement(table, predicate);
88-
String transformedDelete = gen.deleteStatement(table, transformedPredicate);
87+
// Optionally cap the DELETE with a LIMIT. The limit and its ordering (a random column subset, made a total
88+
// order by the row-id tiebreaker) are decided once and applied identically to both statements, so the capped
89+
// row set is deterministic and equal across the runs while still exercising varied orderings.
90+
Integer limit = null;
91+
List<C> orderByColumns = List.of();
92+
if (Randomly.getBoolean()) {
93+
limit = (int) Randomly.getNotCachedInteger(0, 10);
94+
orderByColumns = Randomly.subset(table.getColumns());
95+
}
96+
String originalDelete = gen.deleteStatement(table, predicate, orderByColumns, limit);
97+
String transformedDelete = gen.deleteStatement(table, transformedPredicate, orderByColumns, limit);
8998
generatedQueryString = originalDelete;
9099

91100
// Add the auxiliary column outside the try, then guard everything after it with the finally that drops it:

0 commit comments

Comments
 (0)