|
1 | 1 | package sqlancer.common.gen; |
2 | 2 |
|
| 3 | +import java.util.ArrayList; |
| 4 | +import java.util.List; |
| 5 | + |
3 | 6 | import sqlancer.common.ast.newast.Expression; |
4 | 7 | import sqlancer.common.oracle.EETTransformer; |
5 | 8 | import sqlancer.common.schema.AbstractTable; |
@@ -122,17 +125,40 @@ default String selectRowIdsStatement(T table) { |
122 | 125 | } |
123 | 126 |
|
124 | 127 | /** |
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. |
126 | 138 | * |
127 | 139 | * @param table |
128 | 140 | * the table to delete from |
129 | 141 | * @param predicate |
130 | 142 | * 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 |
131 | 148 | * |
132 | 149 | * @return the SQL statement |
133 | 150 | */ |
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; |
136 | 162 | } |
137 | 163 |
|
138 | 164 | /** |
|
0 commit comments