17 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
|
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
|
|
5 |
*
|
|
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
|
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
|
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
|
|
9 |
* language governing permissions and limitations under the License.
|
|
|
10 |
*
|
|
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
|
|
12 |
*/
|
|
|
13 |
|
|
|
14 |
package org.openconcerto.sql.utils;
|
|
|
15 |
|
156 |
ilm |
16 |
import org.openconcerto.sql.Log;
|
63 |
ilm |
17 |
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
|
17 |
ilm |
18 |
import org.openconcerto.sql.model.FieldRef;
|
19 |
ilm |
19 |
import org.openconcerto.sql.model.SQLBase;
|
63 |
ilm |
20 |
import org.openconcerto.sql.model.SQLDataSource;
|
17 |
ilm |
21 |
import org.openconcerto.sql.model.SQLField;
|
156 |
ilm |
22 |
import org.openconcerto.sql.model.SQLRow;
|
17 |
ilm |
23 |
import org.openconcerto.sql.model.SQLSelect;
|
|
|
24 |
import org.openconcerto.sql.model.SQLSyntax;
|
|
|
25 |
import org.openconcerto.sql.model.SQLSystem;
|
|
|
26 |
import org.openconcerto.sql.model.SQLTable;
|
|
|
27 |
import org.openconcerto.sql.model.Where;
|
|
|
28 |
import org.openconcerto.sql.request.UpdateBuilder;
|
156 |
ilm |
29 |
import org.openconcerto.utils.DecimalUtils;
|
|
|
30 |
import org.openconcerto.utils.Tuple2;
|
|
|
31 |
import org.openconcerto.utils.Tuple2.List2;
|
73 |
ilm |
32 |
import org.openconcerto.utils.convertor.NumberConvertor;
|
17 |
ilm |
33 |
|
19 |
ilm |
34 |
import java.math.BigDecimal;
|
17 |
ilm |
35 |
import java.sql.Connection;
|
|
|
36 |
import java.sql.SQLException;
|
|
|
37 |
import java.sql.Statement;
|
156 |
ilm |
38 |
import java.util.ArrayList;
|
17 |
ilm |
39 |
import java.util.Collections;
|
|
|
40 |
import java.util.List;
|
156 |
ilm |
41 |
import java.util.logging.Level;
|
17 |
ilm |
42 |
|
156 |
ilm |
43 |
import net.jcip.annotations.GuardedBy;
|
|
|
44 |
|
17 |
ilm |
45 |
/**
|
|
|
46 |
* Reorder some or all rows of a table.
|
|
|
47 |
*
|
|
|
48 |
* @author Sylvain
|
|
|
49 |
*/
|
|
|
50 |
public abstract class ReOrder {
|
|
|
51 |
|
156 |
ilm |
52 |
@GuardedBy("this")
|
|
|
53 |
private static boolean AUTO_FIX_NULLS = false;
|
|
|
54 |
|
|
|
55 |
public static synchronized void setAutoFixNulls(boolean b) {
|
|
|
56 |
AUTO_FIX_NULLS = b;
|
|
|
57 |
}
|
|
|
58 |
|
|
|
59 |
public static synchronized boolean isAutoFixNulls() {
|
|
|
60 |
return AUTO_FIX_NULLS;
|
|
|
61 |
}
|
|
|
62 |
|
|
|
63 |
public static BigDecimal makeRoom(final SQLTable t, final BigDecimal roomNeeded, final boolean after, final BigDecimal destOrder) throws SQLException {
|
|
|
64 |
return makeRoom(t, roomNeeded, after, destOrder, 100);
|
|
|
65 |
}
|
|
|
66 |
|
|
|
67 |
/**
|
|
|
68 |
* Make sure that there's no rows with order in the passed range. This method accomplishes this
|
|
|
69 |
* by re-ordering an increasing number of rows. This method only changes orders greater than or
|
|
|
70 |
* equal to <code>destOrder</code> and the first row re-ordered (<code>destOrder</code> if
|
|
|
71 |
* <code>!after</code> the next one otherwise) always has <code>destOrder + roomNeeded</code> as
|
|
|
72 |
* order :
|
|
|
73 |
*
|
|
|
74 |
* <pre>
|
|
|
75 |
* "row foo" 1.0
|
|
|
76 |
* "row bar" 2.0
|
|
|
77 |
* "row baz" 3.0
|
|
|
78 |
* "row qux" 4.0
|
|
|
79 |
* If <code>roomNeeded</code> is 2 after order 2.0, then the new values will be :
|
|
|
80 |
* "row foo" 1.0
|
|
|
81 |
* "row bar" 2.0
|
|
|
82 |
* "row baz" 4.0
|
|
|
83 |
* "row qux" 5.0
|
|
|
84 |
* If on the other hand, one wants the room before 2.0, then :
|
|
|
85 |
* "row foo" 1.0
|
|
|
86 |
* "row bar" 4.0
|
|
|
87 |
* "row baz" 5.0
|
|
|
88 |
* "row qux" 6.0
|
|
|
89 |
* </pre>
|
|
|
90 |
*
|
|
|
91 |
* @param t the table.
|
|
|
92 |
* @param roomNeeded the size of the requested free range, e.g 10.
|
|
|
93 |
* @param after <code>true</code> if the free range should begin after <code>destOrder</code>,
|
|
|
94 |
* <code>false</code> if it should end before <code>destOrder</code>.
|
|
|
95 |
* @param destOrder the start or end of the range.
|
|
|
96 |
* @param initialCount the initial size of the range to re-order if there's no room.
|
|
|
97 |
* @return the smallest possibly used order <code>>=</code> destOrder.
|
|
|
98 |
* @throws SQLException if an error occurs.
|
|
|
99 |
*/
|
|
|
100 |
public static BigDecimal makeRoom(final SQLTable t, final BigDecimal roomNeeded, final boolean after, final BigDecimal destOrder, final int initialCount) throws SQLException {
|
|
|
101 |
if (roomNeeded.signum() <= 0)
|
|
|
102 |
throw new IllegalArgumentException("Negative roomNeeded");
|
|
|
103 |
if (initialCount < 1)
|
|
|
104 |
throw new IllegalArgumentException("Initial count too small");
|
|
|
105 |
final BigDecimal newFirst = destOrder.add(roomNeeded);
|
|
|
106 |
// reorder to squeeze rows upwards
|
|
|
107 |
// since we keep increasing count, we will eventually reorder all rows afterwards
|
|
|
108 |
// NOTE since we only go in one direction (from destOrder and upwards), there shouldn't be
|
|
|
109 |
// any DB deadlocks
|
|
|
110 |
int count = Math.max(initialCount, roomNeeded.intValue() + 1);
|
|
|
111 |
final int tableRowCount = t.getRowCount();
|
|
|
112 |
boolean reordered = false;
|
|
|
113 |
while (!reordered) {
|
|
|
114 |
// only push destRow upwards if we want to add before
|
|
|
115 |
reordered = ReOrder.create(t, destOrder, !after, count, newFirst).exec();
|
|
|
116 |
if (!reordered && count > tableRowCount)
|
|
|
117 |
throw new IllegalStateException("Unable to reorder " + count + " rows in " + t);
|
|
|
118 |
count *= 10;
|
|
|
119 |
}
|
|
|
120 |
return after ? destOrder : newFirst;
|
|
|
121 |
}
|
|
|
122 |
|
|
|
123 |
/**
|
|
|
124 |
* Get a number of free order values after/before the passed row,
|
|
|
125 |
* {@link #makeRoom(SQLTable, BigDecimal, boolean, BigDecimal, int) making room} if needed.
|
|
|
126 |
*
|
|
|
127 |
* @param rowCount the number of order values needed.
|
|
|
128 |
* @param after <code>true</code> if the free values should begin after <code>r</code>,
|
|
|
129 |
* <code>false</code> if they should end before <code>r</code>.
|
|
|
130 |
* @param r the row that is before or after the returned orders.
|
|
|
131 |
* @return a list of <code>rowCount</code> free orders and the new order for the passed row
|
|
|
132 |
* (only changed if there was not enough free values).
|
|
|
133 |
* @throws SQLException if an error occurs.
|
|
|
134 |
*/
|
|
|
135 |
public static Tuple2<List<BigDecimal>, BigDecimal> getFreeOrderValuesFor(final int rowCount, final boolean after, final SQLRow r) throws SQLException {
|
|
|
136 |
return getFreeOrderValuesFor(rowCount, after, r, isAutoFixNulls());
|
|
|
137 |
}
|
|
|
138 |
|
|
|
139 |
public static Tuple2<List<BigDecimal>, BigDecimal> getFreeOrderValuesFor(final int rowCount, final boolean after, final SQLRow r, final boolean autoFixNulls) throws SQLException {
|
|
|
140 |
if (rowCount == 0)
|
|
|
141 |
return Tuple2.<List<BigDecimal>, BigDecimal> create(Collections.<BigDecimal> emptyList(), null);
|
|
|
142 |
// both rows are locked FOR UPDATE, so there shouldn't be any row that can get between them
|
|
|
143 |
// in this transaction, as the only way to do that is to call fetchThisAndSequentialRow()
|
|
|
144 |
List2<SQLRow> seqRows = r.fetchThisAndSequentialRow(after);
|
|
|
145 |
if (seqRows == null)
|
|
|
146 |
throw new IllegalStateException("Couldn't find " + r);
|
|
|
147 |
assert seqRows.get0().equals(r) : "fetchThisAndSequentialRow() failed";
|
|
|
148 |
if (seqRows.get0().getOrder() == null) {
|
|
|
149 |
if (autoFixNulls)
|
|
|
150 |
Log.get().log(Level.WARNING, "Re-order table with null orders : " + r);
|
|
|
151 |
else
|
|
|
152 |
throw new IllegalStateException("Row with null order : " + r);
|
|
|
153 |
if (!ReOrder.create(r.getTable()).exec())
|
|
|
154 |
throw new IllegalStateException("Couldn't re-order table with null orders : " + r.getTable());
|
|
|
155 |
seqRows = r.fetchThisAndSequentialRow(after);
|
|
|
156 |
if (seqRows == null || seqRows.get0().getOrder() == null)
|
|
|
157 |
throw new IllegalStateException("Re-order table with null orders failed : " + seqRows);
|
|
|
158 |
}
|
|
|
159 |
final BigDecimal destOrder = seqRows.get0().getOrder();
|
|
|
160 |
if (destOrder.compareTo(ReOrder.MIN_ORDER) < 0)
|
|
|
161 |
throw new IllegalStateException(seqRows.get0() + " has invalid order : " + destOrder);
|
|
|
162 |
BigDecimal newRowOrder = destOrder;
|
|
|
163 |
final SQLRow otherRow = seqRows.get1();
|
|
|
164 |
final BigDecimal inc;
|
|
|
165 |
BigDecimal newOrder;
|
|
|
166 |
if (after && otherRow == null) {
|
|
|
167 |
// dernière ligne de la table
|
|
|
168 |
inc = ReOrder.DISTANCE;
|
|
|
169 |
newOrder = destOrder.add(inc);
|
|
|
170 |
} else {
|
|
|
171 |
final BigDecimal otherOrder;
|
|
|
172 |
if (otherRow != null) {
|
|
|
173 |
otherOrder = otherRow.getOrder();
|
|
|
174 |
} else {
|
|
|
175 |
// première ligne
|
|
|
176 |
otherOrder = ReOrder.MIN_ORDER;
|
|
|
177 |
}
|
|
|
178 |
if (otherOrder.compareTo(ReOrder.MIN_ORDER) < 0)
|
|
|
179 |
throw new IllegalStateException(otherRow + " has invalid order : " + otherOrder);
|
|
|
180 |
|
|
|
181 |
// ULP * 10 to give a little breathing room
|
|
|
182 |
final BigDecimal minDistance = r.getTable().getOrderULP().scaleByPowerOfTen(1);
|
|
|
183 |
final BigDecimal places = BigDecimal.valueOf(rowCount + 1);
|
|
|
184 |
// the minimum room to fit rowCount
|
|
|
185 |
final BigDecimal roomNeeded = minDistance.multiply(places);
|
|
|
186 |
final BigDecimal roomAvailable = otherOrder.subtract(destOrder).abs();
|
|
|
187 |
|
|
|
188 |
if (roomAvailable.compareTo(roomNeeded) < 0) {
|
|
|
189 |
newRowOrder = makeRoom(r.getTable(), roomNeeded, after, destOrder);
|
|
|
190 |
inc = minDistance;
|
|
|
191 |
newOrder = after ? destOrder.add(inc) : destOrder;
|
|
|
192 |
} else {
|
|
|
193 |
inc = roomAvailable.divide(places, DecimalUtils.HIGH_PRECISION);
|
|
|
194 |
newOrder = (after ? destOrder : otherOrder).add(inc);
|
|
|
195 |
}
|
|
|
196 |
}
|
|
|
197 |
assert inc.signum() > 0;
|
|
|
198 |
final List<BigDecimal> orders = new ArrayList<>(rowCount);
|
|
|
199 |
for (int i = 0; i < rowCount; i++) {
|
|
|
200 |
orders.add(DecimalUtils.round(newOrder, r.getTable().getOrderDecimalDigits()));
|
|
|
201 |
newOrder = newOrder.add(inc);
|
|
|
202 |
}
|
|
|
203 |
assert after && newRowOrder.compareTo(orders.get(0)) < 0 || !after && orders.get(rowCount - 1).compareTo(newRowOrder) < 0;
|
|
|
204 |
return Tuple2.create(orders, newRowOrder);
|
|
|
205 |
}
|
|
|
206 |
|
19 |
ilm |
207 |
// must be zero so that we can work on negative numbers without breaking the unique constraint
|
|
|
208 |
public static final BigDecimal MIN_ORDER = BigDecimal.ZERO;
|
|
|
209 |
// preferred distance
|
|
|
210 |
public static final BigDecimal DISTANCE = BigDecimal.ONE;
|
|
|
211 |
|
17 |
ilm |
212 |
static public ReOrder create(final SQLTable t) {
|
|
|
213 |
return create(t, ALL);
|
|
|
214 |
}
|
|
|
215 |
|
|
|
216 |
static public ReOrder create(final SQLTable t, final int first, final int count) {
|
73 |
ilm |
217 |
return create(t, BigDecimal.valueOf(first), true, count, null);
|
17 |
ilm |
218 |
}
|
|
|
219 |
|
73 |
ilm |
220 |
/**
|
|
|
221 |
* Create a {@link ReOrder} for some rows of the passed table.
|
|
|
222 |
*
|
|
|
223 |
* @param t which table to reorder.
|
|
|
224 |
* @param first the first order to change.
|
|
|
225 |
* @param inclusive <code>true</code> if the row with the order <code>first</code> must be
|
|
|
226 |
* changed.
|
|
|
227 |
* @param count the number of orders (not rows) to change.
|
|
|
228 |
* @param newFirst the order the row with the order <code>first</code> will have after the
|
|
|
229 |
* change.
|
|
|
230 |
* @return a new instance.
|
|
|
231 |
* @throws IllegalArgumentException if <code>count</code> is negative or if
|
|
|
232 |
* <code>newFirst</code> isn't between <code>first</code> and <code>first + count</code>
|
|
|
233 |
* .
|
|
|
234 |
*/
|
|
|
235 |
static public ReOrder create(final SQLTable t, final BigDecimal first, final boolean inclusive, final int count, final BigDecimal newFirst) {
|
|
|
236 |
return create(t, new Some(t, first, inclusive, count, newFirst == null ? first : newFirst));
|
|
|
237 |
}
|
|
|
238 |
|
17 |
ilm |
239 |
static private ReOrder create(final SQLTable t, final Spec spec) {
|
|
|
240 |
final SQLSystem system = t.getBase().getServer().getSQLSystem();
|
|
|
241 |
if (system == SQLSystem.MYSQL) {
|
|
|
242 |
return new ReOrderMySQL(t, spec);
|
|
|
243 |
} else if (system == SQLSystem.POSTGRESQL)
|
|
|
244 |
return new ReOrderPostgreSQL(t, spec);
|
|
|
245 |
else if (system == SQLSystem.H2)
|
|
|
246 |
return new ReOrderH2(t, spec);
|
|
|
247 |
else
|
|
|
248 |
throw new IllegalArgumentException(system + " not supported");
|
|
|
249 |
}
|
|
|
250 |
|
|
|
251 |
protected final SQLTable t;
|
|
|
252 |
protected final Spec spec;
|
|
|
253 |
|
|
|
254 |
protected ReOrder(final SQLTable t, final Spec spec) {
|
|
|
255 |
this.t = t;
|
|
|
256 |
if (!this.t.isOrdered())
|
|
|
257 |
throw new IllegalArgumentException(t + " is not ordered");
|
|
|
258 |
this.spec = spec;
|
|
|
259 |
}
|
|
|
260 |
|
|
|
261 |
protected final boolean isAll() {
|
|
|
262 |
return this.spec == ALL;
|
|
|
263 |
}
|
|
|
264 |
|
19 |
ilm |
265 |
protected final BigDecimal getFirstToReorder() {
|
|
|
266 |
return this.spec.getFirstToReorder();
|
|
|
267 |
}
|
|
|
268 |
|
73 |
ilm |
269 |
protected final boolean isFirstToReorderInclusive() {
|
|
|
270 |
return this.spec.isFirstToReorderInclusive();
|
|
|
271 |
}
|
|
|
272 |
|
19 |
ilm |
273 |
protected final BigDecimal getFirstOrderValue() {
|
17 |
ilm |
274 |
return this.spec.getFirst();
|
|
|
275 |
}
|
|
|
276 |
|
|
|
277 |
protected final String getWhere() {
|
|
|
278 |
final Where w = this.spec.getWhere(null);
|
|
|
279 |
return w == null ? "" : " where " + w;
|
|
|
280 |
}
|
|
|
281 |
|
|
|
282 |
protected final Where getWhere(final FieldRef f) {
|
|
|
283 |
return this.spec.getWhere(f);
|
|
|
284 |
}
|
|
|
285 |
|
73 |
ilm |
286 |
public abstract List<String> getSQL(Connection conn, BigDecimal inc) throws SQLException;
|
17 |
ilm |
287 |
|
|
|
288 |
// MAYBE return affected IDs
|
73 |
ilm |
289 |
public final boolean exec() throws SQLException {
|
156 |
ilm |
290 |
final SQLTable t = this.t;
|
|
|
291 |
return SQLUtils.executeAtomic(this.t.getBase().getDataSource(), new ConnectionHandlerNoSetup<Boolean, SQLException>() {
|
17 |
ilm |
292 |
@Override
|
156 |
ilm |
293 |
public Boolean handle(SQLDataSource ds) throws SQLException, SQLException {
|
63 |
ilm |
294 |
final Connection conn = ds.getConnection();
|
17 |
ilm |
295 |
final Statement stmt = conn.createStatement();
|
156 |
ilm |
296 |
// reorder all, undef must be at 0
|
|
|
297 |
if (isAll() && t.getUndefinedIDNumber() != null) {
|
|
|
298 |
final UpdateBuilder updateUndef = new UpdateBuilder(t).setObject(t.getOrderField(), MIN_ORDER);
|
|
|
299 |
updateUndef.setWhere(new Where(t.getKey(), "=", t.getUndefinedID()));
|
17 |
ilm |
300 |
stmt.execute(updateUndef.asString());
|
|
|
301 |
}
|
73 |
ilm |
302 |
stmt.execute("SELECT " + ReOrder.this.spec.getInc());
|
|
|
303 |
final BigDecimal inc = NumberConvertor.toBigDecimal((Number) SQLDataSource.SCALAR_HANDLER.handle(stmt.getResultSet()));
|
|
|
304 |
// needed since the cast in getInc() rounds so if the real increment is 0.006 it
|
|
|
305 |
// might get rounded to 0.01 and thus the last rows will overlap non moved rows
|
156 |
ilm |
306 |
if (inc.compareTo(t.getOrderULP().scaleByPowerOfTen(1)) < 0)
|
73 |
ilm |
307 |
return false;
|
|
|
308 |
for (final String s : getSQL(conn, inc)) {
|
17 |
ilm |
309 |
stmt.execute(s);
|
|
|
310 |
}
|
|
|
311 |
// MAYBE fire only changed IDs
|
156 |
ilm |
312 |
t.fireTableModified(SQLRow.NONEXISTANT_ID, Collections.singletonList(t.getOrderField().getName()));
|
73 |
ilm |
313 |
return true;
|
17 |
ilm |
314 |
}
|
|
|
315 |
});
|
|
|
316 |
}
|
|
|
317 |
|
|
|
318 |
// *** specs
|
|
|
319 |
|
|
|
320 |
static private class Some implements Spec {
|
|
|
321 |
|
|
|
322 |
private final SQLTable t;
|
19 |
ilm |
323 |
private final BigDecimal firstToReorder;
|
73 |
ilm |
324 |
private final boolean firstToReorderInclusive;
|
19 |
ilm |
325 |
private final BigDecimal first;
|
|
|
326 |
private final BigDecimal lastToReorder;
|
17 |
ilm |
327 |
|
73 |
ilm |
328 |
public Some(final SQLTable t, final BigDecimal first, final boolean inclusive, final int count, final BigDecimal newFirst) {
|
17 |
ilm |
329 |
this.t = t;
|
|
|
330 |
if (count <= 0)
|
19 |
ilm |
331 |
throw new IllegalArgumentException("Negative Count : " + count);
|
73 |
ilm |
332 |
if (first.compareTo(newFirst) > 0)
|
|
|
333 |
throw new IllegalArgumentException("New first before first : " + first + " > " + newFirst);
|
|
|
334 |
final BigDecimal originalLastToReorder = first.add(BigDecimal.valueOf(count));
|
|
|
335 |
if (newFirst.compareTo(originalLastToReorder) >= 0)
|
|
|
336 |
throw new IllegalArgumentException("New first after last to reorder : " + newFirst + " >= " + originalLastToReorder);
|
19 |
ilm |
337 |
// the row with MIN_ORDER cannot be displayed since no row can be moved before it
|
|
|
338 |
// so don't change it
|
73 |
ilm |
339 |
if (first.compareTo(MIN_ORDER) <= 0) {
|
|
|
340 |
this.firstToReorder = MIN_ORDER;
|
|
|
341 |
this.firstToReorderInclusive = false;
|
156 |
ilm |
342 |
// make some room before the first non MIN_ORDER row so that another one can came
|
19 |
ilm |
343 |
// before it
|
73 |
ilm |
344 |
this.first = MIN_ORDER.add(DISTANCE).max(newFirst);
|
|
|
345 |
// try to keep asked value
|
|
|
346 |
this.lastToReorder = originalLastToReorder.compareTo(this.first) > 0 ? originalLastToReorder : this.first.add(BigDecimal.valueOf(count));
|
19 |
ilm |
347 |
} else {
|
73 |
ilm |
348 |
this.firstToReorder = first;
|
|
|
349 |
this.firstToReorderInclusive = inclusive;
|
|
|
350 |
this.first = newFirst;
|
|
|
351 |
this.lastToReorder = originalLastToReorder;
|
19 |
ilm |
352 |
}
|
|
|
353 |
assert this.getFirstToReorder().compareTo(this.getFirst()) <= 0 && this.getFirst().compareTo(this.getLast()) < 0 && this.getLast().compareTo(this.getLastToReorder()) <= 0;
|
17 |
ilm |
354 |
}
|
|
|
355 |
|
|
|
356 |
@Override
|
|
|
357 |
public final String getInc() {
|
|
|
358 |
final SQLField oF = this.t.getOrderField();
|
142 |
ilm |
359 |
final SQLSyntax syntax = SQLSyntax.get(this.t);
|
19 |
ilm |
360 |
|
|
|
361 |
// last order of the whole table
|
73 |
ilm |
362 |
final SQLSelect selTableLast = new SQLSelect(true);
|
19 |
ilm |
363 |
selTableLast.addSelect(oF, "MAX");
|
|
|
364 |
|
17 |
ilm |
365 |
// cast inc to order type to avoid truncation error
|
41 |
ilm |
366 |
final String avgDistance = " cast( " + getLast() + " - " + this.getFirst() + " as " + syntax.getOrderType() + " ) / ( count(*) -1)";
|
19 |
ilm |
367 |
// if the last order of this Spec is the last order of the table, we can use whatever
|
|
|
368 |
// increment we want, we won't span over existing rows. This can be useful when
|
|
|
369 |
// reordering densely packed rows, but this means that lastOrderValue won't be equal to
|
|
|
370 |
// getLastToReorder().
|
|
|
371 |
final String res = "CASE WHEN max(" + SQLBase.quoteIdentifier(oF.getName()) + ") = (" + selTableLast.asString() + ") then " + ALL.getInc() + " else " + avgDistance + " end";
|
|
|
372 |
return res + " FROM " + this.t.getSQLName().quote() + " where " + this.getWhere(null).getClause();
|
17 |
ilm |
373 |
}
|
|
|
374 |
|
|
|
375 |
@Override
|
|
|
376 |
public final Where getWhere(FieldRef order) {
|
|
|
377 |
if (order == null)
|
|
|
378 |
order = this.t.getOrderField();
|
|
|
379 |
else if (order.getField() != this.t.getOrderField())
|
|
|
380 |
throw new IllegalArgumentException();
|
73 |
ilm |
381 |
return new Where(order, this.getFirstToReorder(), this.firstToReorderInclusive, this.getLastToReorder(), true);
|
17 |
ilm |
382 |
}
|
|
|
383 |
|
|
|
384 |
@Override
|
19 |
ilm |
385 |
public final BigDecimal getFirstToReorder() {
|
|
|
386 |
return this.firstToReorder;
|
|
|
387 |
}
|
|
|
388 |
|
73 |
ilm |
389 |
@Override
|
|
|
390 |
public boolean isFirstToReorderInclusive() {
|
|
|
391 |
return this.firstToReorderInclusive;
|
|
|
392 |
}
|
|
|
393 |
|
19 |
ilm |
394 |
private final BigDecimal getLastToReorder() {
|
|
|
395 |
return this.lastToReorder;
|
|
|
396 |
}
|
|
|
397 |
|
|
|
398 |
@Override
|
|
|
399 |
public BigDecimal getFirst() {
|
17 |
ilm |
400 |
return this.first;
|
|
|
401 |
}
|
|
|
402 |
|
19 |
ilm |
403 |
public final BigDecimal getLast() {
|
|
|
404 |
return this.getLastToReorder();
|
|
|
405 |
}
|
17 |
ilm |
406 |
}
|
|
|
407 |
|
|
|
408 |
static private Spec ALL = new Spec() {
|
|
|
409 |
@Override
|
|
|
410 |
public String getInc() {
|
19 |
ilm |
411 |
return String.valueOf(DISTANCE);
|
17 |
ilm |
412 |
}
|
|
|
413 |
|
|
|
414 |
@Override
|
|
|
415 |
public final Where getWhere(final FieldRef order) {
|
|
|
416 |
return null;
|
|
|
417 |
}
|
|
|
418 |
|
|
|
419 |
@Override
|
19 |
ilm |
420 |
public BigDecimal getFirstToReorder() {
|
|
|
421 |
return MIN_ORDER;
|
17 |
ilm |
422 |
}
|
19 |
ilm |
423 |
|
|
|
424 |
@Override
|
73 |
ilm |
425 |
public boolean isFirstToReorderInclusive() {
|
|
|
426 |
return true;
|
|
|
427 |
}
|
|
|
428 |
|
|
|
429 |
@Override
|
19 |
ilm |
430 |
public BigDecimal getFirst() {
|
|
|
431 |
return getFirstToReorder();
|
|
|
432 |
}
|
17 |
ilm |
433 |
};
|
|
|
434 |
|
|
|
435 |
static interface Spec {
|
|
|
436 |
String getInc();
|
|
|
437 |
|
|
|
438 |
Where getWhere(final FieldRef order);
|
|
|
439 |
|
19 |
ilm |
440 |
// before reorder
|
|
|
441 |
BigDecimal getFirstToReorder();
|
|
|
442 |
|
73 |
ilm |
443 |
boolean isFirstToReorderInclusive();
|
|
|
444 |
|
19 |
ilm |
445 |
// the first order value after reorder
|
|
|
446 |
BigDecimal getFirst();
|
17 |
ilm |
447 |
}
|
|
|
448 |
}
|