17 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
182 |
ilm |
4 |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
|
17 |
ilm |
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.model;
|
|
|
15 |
|
65 |
ilm |
16 |
import org.openconcerto.sql.model.Order.Direction;
|
|
|
17 |
import org.openconcerto.sql.model.Order.Nulls;
|
17 |
ilm |
18 |
import org.openconcerto.sql.model.graph.Path;
|
73 |
ilm |
19 |
import org.openconcerto.sql.model.graph.Step;
|
17 |
ilm |
20 |
import org.openconcerto.utils.CollectionUtils;
|
93 |
ilm |
21 |
import org.openconcerto.utils.Tuple2;
|
17 |
ilm |
22 |
import org.openconcerto.utils.cc.ITransformer;
|
|
|
23 |
|
|
|
24 |
import java.util.ArrayList;
|
182 |
ilm |
25 |
import java.util.Arrays;
|
17 |
ilm |
26 |
import java.util.Collection;
|
|
|
27 |
import java.util.Collections;
|
|
|
28 |
import java.util.HashMap;
|
|
|
29 |
import java.util.HashSet;
|
|
|
30 |
import java.util.List;
|
|
|
31 |
import java.util.Map;
|
|
|
32 |
import java.util.Set;
|
|
|
33 |
|
|
|
34 |
/**
|
|
|
35 |
* @author ILM Informatique 10 mai 2004
|
|
|
36 |
*/
|
|
|
37 |
public final class SQLSelect {
|
|
|
38 |
|
|
|
39 |
public static enum ArchiveMode {
|
|
|
40 |
UNARCHIVED, ARCHIVED, BOTH
|
|
|
41 |
}
|
|
|
42 |
|
132 |
ilm |
43 |
public static enum LockStrength {
|
|
|
44 |
NONE, SHARE, UPDATE
|
|
|
45 |
}
|
|
|
46 |
|
17 |
ilm |
47 |
public static final ArchiveMode UNARCHIVED = ArchiveMode.UNARCHIVED;
|
|
|
48 |
public static final ArchiveMode ARCHIVED = ArchiveMode.ARCHIVED;
|
|
|
49 |
public static final ArchiveMode BOTH = ArchiveMode.BOTH;
|
|
|
50 |
|
|
|
51 |
// [String], eg : [SITE.ID_SITE, AVG(AGE)]
|
|
|
52 |
private final List<String> select;
|
83 |
ilm |
53 |
// names of columns (explicit aliases and field names), e.g. [ID_SITE, null]
|
|
|
54 |
private final List<String> selectNames;
|
|
|
55 |
// e.g. : [|SITE.ID_SITE|], known fields in this select (addRawSelect)
|
|
|
56 |
private final List<FieldRef> selectFields;
|
17 |
ilm |
57 |
private Where where;
|
|
|
58 |
private final List<FieldRef> groupBy;
|
|
|
59 |
private Where having;
|
|
|
60 |
// [String]
|
|
|
61 |
private final List<String> order;
|
|
|
62 |
private final FromClause from;
|
|
|
63 |
// all the tables (and their aliases) in this select
|
|
|
64 |
private final AliasedTables declaredTables;
|
|
|
65 |
// {String}, aliases not to include in the FROM clause
|
|
|
66 |
private final Set<String> joinAliases;
|
|
|
67 |
// [String]
|
|
|
68 |
private final List<SQLSelectJoin> joins;
|
|
|
69 |
|
|
|
70 |
// la politique générale pour l'exclusion des indéfinis
|
|
|
71 |
private boolean generalExcludeUndefined;
|
|
|
72 |
// [SQLTable => Boolean]
|
93 |
ilm |
73 |
private final Map<SQLTable, Boolean> excludeUndefined;
|
17 |
ilm |
74 |
// null key for general
|
|
|
75 |
private final Map<SQLTable, ArchiveMode> archivedPolicy;
|
|
|
76 |
// DISTINCT
|
|
|
77 |
private boolean distinct;
|
132 |
ilm |
78 |
// how to lock returned rows
|
|
|
79 |
private LockStrength lockStrength;
|
17 |
ilm |
80 |
// which tables to wait (avoid SELECT FOR UPDATE/SHARE cannot be applied to the nullable side of
|
|
|
81 |
// an outer join)
|
|
|
82 |
private final List<String> waitTrxTables;
|
|
|
83 |
// number of rows to return
|
|
|
84 |
private Integer limit;
|
93 |
ilm |
85 |
// offset from the start
|
|
|
86 |
private int offset;
|
17 |
ilm |
87 |
|
65 |
ilm |
88 |
/**
|
|
|
89 |
* Create a new SQLSelect.
|
|
|
90 |
*
|
|
|
91 |
* @param base the database of the request.
|
83 |
ilm |
92 |
* @deprecated use {@link #SQLSelect(DBSystemRoot, boolean)}
|
65 |
ilm |
93 |
*/
|
93 |
ilm |
94 |
@Deprecated
|
|
|
95 |
public SQLSelect(final SQLBase base) {
|
17 |
ilm |
96 |
this(base, false);
|
|
|
97 |
}
|
|
|
98 |
|
|
|
99 |
/**
|
|
|
100 |
* Create a new SQLSelect.
|
|
|
101 |
*
|
|
|
102 |
* @param base the database of the request.
|
|
|
103 |
* @param plain whether this request should automatically add a where clause for archived and
|
|
|
104 |
* undefined.
|
65 |
ilm |
105 |
* @deprecated use {@link #SQLSelect(DBSystemRoot, boolean)}
|
17 |
ilm |
106 |
*/
|
93 |
ilm |
107 |
@Deprecated
|
|
|
108 |
public SQLSelect(final SQLBase base, final boolean plain) {
|
65 |
ilm |
109 |
this(base.getDBSystemRoot(), plain);
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
public SQLSelect() {
|
|
|
113 |
this(false);
|
|
|
114 |
}
|
|
|
115 |
|
93 |
ilm |
116 |
public SQLSelect(final boolean plain) {
|
65 |
ilm |
117 |
this((DBSystemRoot) null, plain);
|
|
|
118 |
}
|
|
|
119 |
|
|
|
120 |
/**
|
|
|
121 |
* Create a new SQLSelect.
|
|
|
122 |
*
|
|
|
123 |
* @param sysRoot the database of the request, can be <code>null</code> (it will come from
|
|
|
124 |
* declared tables).
|
|
|
125 |
* @param plain whether this request should automatically add a where clause for archived and
|
|
|
126 |
* undefined.
|
|
|
127 |
*/
|
93 |
ilm |
128 |
public SQLSelect(final DBSystemRoot sysRoot, final boolean plain) {
|
17 |
ilm |
129 |
this.select = new ArrayList<String>();
|
83 |
ilm |
130 |
this.selectNames = new ArrayList<String>();
|
|
|
131 |
this.selectFields = new ArrayList<FieldRef>();
|
17 |
ilm |
132 |
this.where = null;
|
|
|
133 |
this.groupBy = new ArrayList<FieldRef>();
|
|
|
134 |
this.having = null;
|
|
|
135 |
this.order = new ArrayList<String>();
|
|
|
136 |
this.from = new FromClause();
|
65 |
ilm |
137 |
this.declaredTables = new AliasedTables(sysRoot);
|
17 |
ilm |
138 |
this.joinAliases = new HashSet<String>();
|
|
|
139 |
this.joins = new ArrayList<SQLSelectJoin>();
|
|
|
140 |
// false by default cause it slows things down
|
|
|
141 |
this.distinct = false;
|
|
|
142 |
this.excludeUndefined = new HashMap<SQLTable, Boolean>();
|
|
|
143 |
this.archivedPolicy = new HashMap<SQLTable, ArchiveMode>();
|
132 |
ilm |
144 |
// none by default since it requires access rights
|
|
|
145 |
this.lockStrength = LockStrength.NONE;
|
17 |
ilm |
146 |
this.waitTrxTables = new ArrayList<String>();
|
93 |
ilm |
147 |
this.limit = null;
|
|
|
148 |
this.offset = 0;
|
17 |
ilm |
149 |
if (plain) {
|
|
|
150 |
this.generalExcludeUndefined = false;
|
|
|
151 |
this.setArchivedPolicy(BOTH);
|
|
|
152 |
} else {
|
|
|
153 |
this.generalExcludeUndefined = true;
|
|
|
154 |
this.setArchivedPolicy(UNARCHIVED);
|
|
|
155 |
}
|
|
|
156 |
// otherwise getArchiveWhere() fails
|
|
|
157 |
assert this.archivedPolicy.containsKey(null);
|
|
|
158 |
}
|
|
|
159 |
|
|
|
160 |
/**
|
|
|
161 |
* Clone un SQLSelect.
|
|
|
162 |
*
|
|
|
163 |
* @param orig l'instance à cloner.
|
|
|
164 |
*/
|
93 |
ilm |
165 |
public SQLSelect(final SQLSelect orig) {
|
17 |
ilm |
166 |
// ATTN synch les implémentations des attributs (LinkedHashSet, ...)
|
|
|
167 |
this.select = new ArrayList<String>(orig.select);
|
83 |
ilm |
168 |
this.selectNames = new ArrayList<String>(orig.selectNames);
|
|
|
169 |
this.selectFields = new ArrayList<FieldRef>(orig.selectFields);
|
93 |
ilm |
170 |
this.where = orig.where;
|
17 |
ilm |
171 |
this.groupBy = new ArrayList<FieldRef>(orig.groupBy);
|
93 |
ilm |
172 |
this.having = orig.having;
|
17 |
ilm |
173 |
this.order = new ArrayList<String>(orig.order);
|
|
|
174 |
this.from = new FromClause(orig.from);
|
|
|
175 |
this.declaredTables = new AliasedTables(orig.declaredTables);
|
|
|
176 |
this.joinAliases = new HashSet<String>(orig.joinAliases);
|
|
|
177 |
this.joins = new ArrayList<SQLSelectJoin>(orig.joins);
|
|
|
178 |
this.generalExcludeUndefined = orig.generalExcludeUndefined;
|
|
|
179 |
this.excludeUndefined = new HashMap<SQLTable, Boolean>(orig.excludeUndefined);
|
|
|
180 |
this.archivedPolicy = new HashMap<SQLTable, ArchiveMode>(orig.archivedPolicy);
|
|
|
181 |
this.distinct = orig.distinct;
|
|
|
182 |
|
132 |
ilm |
183 |
this.lockStrength = orig.lockStrength;
|
17 |
ilm |
184 |
this.waitTrxTables = new ArrayList<String>(orig.waitTrxTables);
|
83 |
ilm |
185 |
this.limit = orig.limit;
|
93 |
ilm |
186 |
this.offset = orig.offset;
|
17 |
ilm |
187 |
}
|
|
|
188 |
|
93 |
ilm |
189 |
final DBSystemRoot getSystemRoot() {
|
67 |
ilm |
190 |
final DBSystemRoot sysRoot = this.declaredTables.getSysRoot();
|
|
|
191 |
if (sysRoot == null)
|
|
|
192 |
throw new IllegalStateException("No systemRoot supplied (neither in the constructor nor by adding an item)");
|
93 |
ilm |
193 |
return sysRoot;
|
65 |
ilm |
194 |
}
|
|
|
195 |
|
93 |
ilm |
196 |
public final SQLSystem getSQLSystem() {
|
|
|
197 |
return getSystemRoot().getServer().getSQLSystem();
|
|
|
198 |
}
|
|
|
199 |
|
142 |
ilm |
200 |
public final SQLSyntax getSyntax() {
|
|
|
201 |
return getSystemRoot().getSyntax();
|
|
|
202 |
}
|
|
|
203 |
|
17 |
ilm |
204 |
public String asString() {
|
65 |
ilm |
205 |
final SQLSystem sys = this.getSQLSystem();
|
17 |
ilm |
206 |
|
|
|
207 |
final StringBuffer result = new StringBuffer(512);
|
|
|
208 |
result.append("SELECT ");
|
|
|
209 |
if (this.distinct)
|
|
|
210 |
result.append("DISTINCT ");
|
|
|
211 |
result.append(CollectionUtils.join(this.select, ", "));
|
|
|
212 |
|
|
|
213 |
result.append("\n " + this.from.getSQL());
|
|
|
214 |
|
|
|
215 |
// si c'est null, ca marche
|
|
|
216 |
Where archive = this.where;
|
|
|
217 |
// ne pas exclure les archivés et les indéfinis des joins : SQLSelectJoin does it
|
21 |
ilm |
218 |
final Collection<String> fromAliases = CollectionUtils.substract(this.declaredTables.getAliases(), this.joinAliases);
|
|
|
219 |
for (final String alias : fromAliases) {
|
17 |
ilm |
220 |
final SQLTable fromTable = this.declaredTables.getTable(alias);
|
|
|
221 |
// on ignore les lignes archivées
|
|
|
222 |
archive = Where.and(getArchiveWhere(fromTable, alias), archive);
|
|
|
223 |
// on ignore les lignes indéfines
|
|
|
224 |
archive = Where.and(getUndefWhere(fromTable, alias), archive);
|
|
|
225 |
}
|
|
|
226 |
// archive == null si pas d'archive et pas d'undefined
|
142 |
ilm |
227 |
if (archive != null) {
|
17 |
ilm |
228 |
result.append("\n WHERE ");
|
|
|
229 |
result.append(archive.getClause());
|
|
|
230 |
}
|
|
|
231 |
if (!this.groupBy.isEmpty()) {
|
|
|
232 |
result.append("\n GROUP BY ");
|
|
|
233 |
result.append(CollectionUtils.join(this.groupBy, ", ", new ITransformer<FieldRef, String>() {
|
|
|
234 |
@Override
|
93 |
ilm |
235 |
public String transformChecked(final FieldRef input) {
|
17 |
ilm |
236 |
return input.getFieldRef();
|
|
|
237 |
}
|
|
|
238 |
}));
|
|
|
239 |
}
|
|
|
240 |
if (this.having != null) {
|
|
|
241 |
result.append("\n HAVING ");
|
|
|
242 |
result.append(this.having.getClause());
|
|
|
243 |
}
|
|
|
244 |
if (!this.order.isEmpty()) {
|
|
|
245 |
result.append("\n ORDER BY ");
|
|
|
246 |
result.append(CollectionUtils.join(this.order, ", "));
|
|
|
247 |
}
|
93 |
ilm |
248 |
// most systems need to specify both
|
|
|
249 |
if (this.getLimit() != null || this.getOffset() != 0) {
|
|
|
250 |
if (sys == SQLSystem.MSSQL) {
|
|
|
251 |
result.append("\nOFFSET ");
|
|
|
252 |
result.append(this.getOffset());
|
|
|
253 |
result.append(" ROWS");
|
|
|
254 |
if (this.getLimit() != null) {
|
|
|
255 |
result.append(" FETCH NEXT ");
|
|
|
256 |
result.append(this.getLimit());
|
|
|
257 |
result.append(" ROWS ONLY");
|
|
|
258 |
}
|
|
|
259 |
} else {
|
|
|
260 |
final Object actualLimit;
|
|
|
261 |
if (this.getLimit() != null) {
|
|
|
262 |
actualLimit = this.getLimit();
|
|
|
263 |
} else if (sys == SQLSystem.H2) {
|
|
|
264 |
actualLimit = "NULL";
|
|
|
265 |
} else if (sys == SQLSystem.POSTGRESQL) {
|
|
|
266 |
actualLimit = "ALL";
|
|
|
267 |
} else {
|
|
|
268 |
// From the official MySQL manual
|
|
|
269 |
actualLimit = Integer.MAX_VALUE;
|
|
|
270 |
}
|
|
|
271 |
result.append("\nLIMIT ");
|
|
|
272 |
result.append(actualLimit);
|
|
|
273 |
result.append(" OFFSET ");
|
|
|
274 |
result.append(this.getOffset());
|
|
|
275 |
}
|
17 |
ilm |
276 |
}
|
|
|
277 |
// wait for other update trx to finish before selecting
|
132 |
ilm |
278 |
if (this.lockStrength != LockStrength.NONE) {
|
17 |
ilm |
279 |
if (sys.equals(SQLSystem.POSTGRESQL)) {
|
132 |
ilm |
280 |
result.append(this.lockStrength == LockStrength.SHARE ? " FOR SHARE" : " FOR UPDATE");
|
17 |
ilm |
281 |
if (this.waitTrxTables.size() > 0)
|
|
|
282 |
result.append(" OF " + CollectionUtils.join(this.waitTrxTables, ", "));
|
132 |
ilm |
283 |
} else if (sys.equals(SQLSystem.MYSQL)) {
|
|
|
284 |
result.append(this.lockStrength == LockStrength.SHARE ? " LOCK IN SHARE MODE" : " FOR UPDATE");
|
|
|
285 |
} else if (sys.equals(SQLSystem.H2)) {
|
|
|
286 |
result.append(" FOR UPDATE");
|
|
|
287 |
} else {
|
|
|
288 |
throw new IllegalStateException("Unsupported system : " + sys);
|
|
|
289 |
}
|
17 |
ilm |
290 |
}
|
|
|
291 |
|
|
|
292 |
return result.toString();
|
|
|
293 |
}
|
|
|
294 |
|
|
|
295 |
Where getArchiveWhere(final SQLTable table, final String alias) {
|
|
|
296 |
final Where res;
|
|
|
297 |
// null key is the default
|
|
|
298 |
final ArchiveMode m = this.archivedPolicy.containsKey(table) ? this.archivedPolicy.get(table) : this.archivedPolicy.get(null);
|
|
|
299 |
assert m != null : "no default policy";
|
132 |
ilm |
300 |
if (m == BOTH) {
|
|
|
301 |
res = null;
|
|
|
302 |
} else if (table.isArchivable()) {
|
17 |
ilm |
303 |
final Object archiveValue;
|
|
|
304 |
if (table.getArchiveField().getType().getJavaType().equals(Boolean.class)) {
|
|
|
305 |
archiveValue = m == ARCHIVED;
|
|
|
306 |
} else {
|
|
|
307 |
archiveValue = m == ARCHIVED ? 1 : 0;
|
|
|
308 |
}
|
|
|
309 |
res = new Where(this.createRef(alias, table.getArchiveField()), "=", archiveValue);
|
132 |
ilm |
310 |
} else {
|
|
|
311 |
// for tables that aren't archivable, either all rows or no rows
|
|
|
312 |
res = m == ARCHIVED ? Where.FALSE : null;
|
|
|
313 |
}
|
17 |
ilm |
314 |
return res;
|
|
|
315 |
}
|
|
|
316 |
|
|
|
317 |
Where getUndefWhere(final SQLTable table, final String alias) {
|
|
|
318 |
final Where res;
|
|
|
319 |
final Boolean exclude = this.excludeUndefined.get(table);
|
|
|
320 |
if (table.isRowable() && (exclude == Boolean.TRUE || (exclude == null && this.generalExcludeUndefined))) {
|
|
|
321 |
// no need to use NULL_IS_DATA_NEQ since we're in FROM or JOIN and ID cannot be null
|
|
|
322 |
res = new Where(this.createRef(alias, table.getKey()), "!=", table.getUndefinedID());
|
|
|
323 |
} else
|
|
|
324 |
res = null;
|
|
|
325 |
return res;
|
|
|
326 |
}
|
|
|
327 |
|
93 |
ilm |
328 |
@Override
|
17 |
ilm |
329 |
public String toString() {
|
|
|
330 |
return this.asString();
|
|
|
331 |
}
|
|
|
332 |
|
|
|
333 |
/**
|
83 |
ilm |
334 |
* SQL expressions of the SELECT.
|
17 |
ilm |
335 |
*
|
83 |
ilm |
336 |
* @return a list of expressions used by the SELECT, e.g. "T.*, A.f", "count(*)".
|
17 |
ilm |
337 |
*/
|
|
|
338 |
public List<String> getSelect() {
|
83 |
ilm |
339 |
return Collections.unmodifiableList(this.select);
|
17 |
ilm |
340 |
}
|
|
|
341 |
|
|
|
342 |
/**
|
83 |
ilm |
343 |
* Column names of the SELECT. Should always have the same length and same indexes as the result
|
|
|
344 |
* set, i.e. will contain <code>null</code> for computed columns without aliases. But the length
|
|
|
345 |
* may not be equal to that of {@link #getSelect()}, e.g. when using
|
|
|
346 |
* {@link #addSelectStar(TableRef)} which add one expression but all the fields.
|
17 |
ilm |
347 |
*
|
83 |
ilm |
348 |
* @return a list of column names of the SELECT, <code>null</code> for indexes without any.
|
17 |
ilm |
349 |
*/
|
83 |
ilm |
350 |
public List<String> getSelectNames() {
|
|
|
351 |
return Collections.unmodifiableList(this.selectNames);
|
17 |
ilm |
352 |
}
|
|
|
353 |
|
83 |
ilm |
354 |
/**
|
|
|
355 |
* Fields of the SELECT. Should always have the same length and same indexes as the result set,
|
|
|
356 |
* i.e. will contain <code>null</code> for computed columns. But the length may not be equal to
|
|
|
357 |
* that of {@link #getSelect()}, e.g. when using {@link #addSelectStar(TableRef)} which add one
|
|
|
358 |
* expression but all the fields.
|
|
|
359 |
*
|
|
|
360 |
* @return a list of fields used by the SELECT, <code>null</code> for indexes without any.
|
|
|
361 |
*/
|
|
|
362 |
public final List<FieldRef> getSelectFields() {
|
|
|
363 |
return Collections.unmodifiableList(this.selectFields);
|
|
|
364 |
}
|
|
|
365 |
|
17 |
ilm |
366 |
public List<String> getOrder() {
|
|
|
367 |
return this.order;
|
|
|
368 |
}
|
|
|
369 |
|
|
|
370 |
public Where getWhere() {
|
|
|
371 |
return this.where;
|
|
|
372 |
}
|
|
|
373 |
|
93 |
ilm |
374 |
public final boolean contains(final String alias) {
|
17 |
ilm |
375 |
return this.declaredTables.contains(alias);
|
|
|
376 |
}
|
|
|
377 |
|
|
|
378 |
/**
|
|
|
379 |
* Whether this SELECT already references table (eg by a from or a join). For example, if not
|
|
|
380 |
* you can't ORDER BY with a field of that table.
|
|
|
381 |
*
|
|
|
382 |
* @param table the table to test.
|
|
|
383 |
* @return <code>true</code> if table is already in this.
|
|
|
384 |
*/
|
93 |
ilm |
385 |
public final boolean contains(final SQLTable table) {
|
17 |
ilm |
386 |
return this.contains(table.getName());
|
|
|
387 |
}
|
|
|
388 |
|
93 |
ilm |
389 |
private final void addIfNotExist(final TableRef t) {
|
|
|
390 |
if (this.declaredTables.add(t, false))
|
|
|
391 |
this.from.add(t);
|
|
|
392 |
}
|
|
|
393 |
|
17 |
ilm |
394 |
// *** group by / having
|
|
|
395 |
|
93 |
ilm |
396 |
public SQLSelect addGroupBy(final FieldRef f) {
|
17 |
ilm |
397 |
this.groupBy.add(f);
|
|
|
398 |
return this;
|
|
|
399 |
}
|
|
|
400 |
|
93 |
ilm |
401 |
public SQLSelect setHaving(final Where w) {
|
17 |
ilm |
402 |
this.having = w;
|
|
|
403 |
return this;
|
|
|
404 |
}
|
|
|
405 |
|
|
|
406 |
// *** order by
|
|
|
407 |
|
|
|
408 |
/**
|
|
|
409 |
* Ajoute un ORDER BY.
|
|
|
410 |
*
|
|
|
411 |
* @param t a table alias.
|
|
|
412 |
* @return this.
|
|
|
413 |
* @throws IllegalArgumentException si t n'est pas ordonné.
|
|
|
414 |
* @throws IllegalStateException si t n'est pas dans cette requete.
|
|
|
415 |
* @see SQLTable#isOrdered()
|
|
|
416 |
*/
|
93 |
ilm |
417 |
public SQLSelect addOrder(final String t) {
|
65 |
ilm |
418 |
return this.addOrder(this.getTableRef(t));
|
17 |
ilm |
419 |
}
|
|
|
420 |
|
93 |
ilm |
421 |
public SQLSelect addOrder(final TableRef t) {
|
65 |
ilm |
422 |
return this.addOrder(t, true);
|
17 |
ilm |
423 |
}
|
|
|
424 |
|
65 |
ilm |
425 |
/**
|
142 |
ilm |
426 |
* Add an ORDER BY for the passed table.
|
65 |
ilm |
427 |
*
|
|
|
428 |
* @param t the table.
|
|
|
429 |
* @param fieldMustExist if <code>true</code> then <code>t</code> must be
|
142 |
ilm |
430 |
* {@link SQLTable#isOrdered() ordered} or have a {@link SQLTable#isRowable() numeric
|
|
|
431 |
* primary key}.
|
65 |
ilm |
432 |
* @return this.
|
142 |
ilm |
433 |
* @throws IllegalArgumentException if <code>t</code> has no usable order field and
|
|
|
434 |
* <code>mustExist</code> is <code>true</code>.
|
65 |
ilm |
435 |
*/
|
93 |
ilm |
436 |
public SQLSelect addOrder(final TableRef t, final boolean fieldMustExist) {
|
65 |
ilm |
437 |
final SQLField orderField = t.getTable().getOrderField();
|
|
|
438 |
if (orderField != null)
|
|
|
439 |
this.addFieldOrder(t.getField(orderField.getName()));
|
142 |
ilm |
440 |
else if (t.getTable().isRowable())
|
|
|
441 |
this.addFieldOrder(t.getKey());
|
65 |
ilm |
442 |
else if (fieldMustExist)
|
|
|
443 |
throw new IllegalArgumentException("table is not ordered : " + t);
|
|
|
444 |
return this;
|
|
|
445 |
}
|
|
|
446 |
|
93 |
ilm |
447 |
public SQLSelect addFieldOrder(final FieldRef fieldRef) {
|
65 |
ilm |
448 |
return this.addFieldOrder(fieldRef, Order.asc());
|
|
|
449 |
}
|
|
|
450 |
|
93 |
ilm |
451 |
public SQLSelect addFieldOrder(final FieldRef fieldRef, final Direction dir) {
|
65 |
ilm |
452 |
return this.addFieldOrder(fieldRef, dir, null);
|
|
|
453 |
}
|
|
|
454 |
|
93 |
ilm |
455 |
public SQLSelect addFieldOrder(final FieldRef fieldRef, final Direction dir, final Nulls nulls) {
|
17 |
ilm |
456 |
// with Derby if you ORDER BY w/o mentioning the field in the select clause
|
|
|
457 |
// you can't get the table names of columns in a result set.
|
67 |
ilm |
458 |
if (fieldRef.getField().getServer().getSQLSystem().equals(SQLSystem.DERBY))
|
17 |
ilm |
459 |
this.addSelect(fieldRef);
|
|
|
460 |
|
65 |
ilm |
461 |
return this.addRawOrder(fieldRef.getFieldRef() + dir.getSQL() + (nulls == null ? "" : nulls.getSQL()));
|
17 |
ilm |
462 |
}
|
|
|
463 |
|
|
|
464 |
/**
|
|
|
465 |
* Add an ORDER BY that is not an ORDER field.
|
|
|
466 |
*
|
|
|
467 |
* @param selectItem an item that appears in the select, either a field reference or an alias.
|
|
|
468 |
* @return this.
|
|
|
469 |
*/
|
93 |
ilm |
470 |
public SQLSelect addRawOrder(final String selectItem) {
|
17 |
ilm |
471 |
this.order.add(selectItem);
|
|
|
472 |
return this;
|
|
|
473 |
}
|
|
|
474 |
|
|
|
475 |
public SQLSelect clearOrder() {
|
|
|
476 |
this.order.clear();
|
|
|
477 |
return this;
|
|
|
478 |
}
|
|
|
479 |
|
|
|
480 |
/**
|
|
|
481 |
* Ajoute un ORDER BY. Ne fais rien si t n'est pas ordonné.
|
|
|
482 |
*
|
|
|
483 |
* @param t la table.
|
|
|
484 |
* @return this.
|
|
|
485 |
* @throws IllegalStateException si t n'est pas dans cette requete.
|
|
|
486 |
*/
|
93 |
ilm |
487 |
public SQLSelect addOrderSilent(final String t) {
|
65 |
ilm |
488 |
return this.addOrder(this.getTableRef(t), false);
|
17 |
ilm |
489 |
}
|
|
|
490 |
|
|
|
491 |
// *** select
|
|
|
492 |
|
|
|
493 |
/**
|
|
|
494 |
* Ajoute un champ au SELECT.
|
|
|
495 |
*
|
|
|
496 |
* @param f le champ à ajouter.
|
|
|
497 |
* @return this pour pouvoir chaîner.
|
|
|
498 |
*/
|
93 |
ilm |
499 |
public SQLSelect addSelect(final FieldRef f) {
|
17 |
ilm |
500 |
return this.addSelect(f, null);
|
|
|
501 |
}
|
|
|
502 |
|
182 |
ilm |
503 |
public final SQLSelect addAllSelect(final FieldRef... s) {
|
|
|
504 |
return this.addAllSelect(Arrays.asList(s));
|
|
|
505 |
}
|
|
|
506 |
|
17 |
ilm |
507 |
/**
|
|
|
508 |
* Permet d'ajouter plusieurs champs.
|
|
|
509 |
*
|
21 |
ilm |
510 |
* @param s une collection de FieldRef.
|
17 |
ilm |
511 |
* @return this pour pouvoir chaîner.
|
|
|
512 |
*/
|
93 |
ilm |
513 |
public SQLSelect addAllSelect(final Collection<? extends FieldRef> s) {
|
21 |
ilm |
514 |
for (final FieldRef element : s) {
|
|
|
515 |
this.addSelect(element);
|
17 |
ilm |
516 |
}
|
|
|
517 |
return this;
|
|
|
518 |
}
|
|
|
519 |
|
|
|
520 |
/**
|
|
|
521 |
* Permet d'ajouter plusieurs champs d'une même table sans avoir à les préfixer.
|
|
|
522 |
*
|
|
|
523 |
* @param t la table.
|
|
|
524 |
* @param s une collection de nom de champs, eg "NOM".
|
|
|
525 |
* @return this pour pouvoir chaîner.
|
|
|
526 |
*/
|
93 |
ilm |
527 |
public SQLSelect addAllSelect(final TableRef t, final Collection<String> s) {
|
21 |
ilm |
528 |
for (final String fieldName : s) {
|
17 |
ilm |
529 |
this.addSelect(t.getField(fieldName));
|
|
|
530 |
}
|
|
|
531 |
return this;
|
|
|
532 |
}
|
|
|
533 |
|
|
|
534 |
/**
|
|
|
535 |
* Ajoute une fonction d'un champ au SELECT.
|
|
|
536 |
*
|
|
|
537 |
* @param f le champ, eg "PERSON.AGE".
|
|
|
538 |
* @param function la fonction, eg "AVG".
|
|
|
539 |
* @return this pour pouvoir chaîner.
|
|
|
540 |
*/
|
93 |
ilm |
541 |
public SQLSelect addSelect(final FieldRef f, final String function) {
|
17 |
ilm |
542 |
return this.addSelect(f, function, null);
|
|
|
543 |
}
|
|
|
544 |
|
93 |
ilm |
545 |
public SQLSelect addSelect(final FieldRef f, final String function, final String alias) {
|
83 |
ilm |
546 |
final String defaultAlias;
|
17 |
ilm |
547 |
String s = f.getFieldRef();
|
|
|
548 |
if (function != null) {
|
|
|
549 |
s = function + "(" + s + ")";
|
83 |
ilm |
550 |
defaultAlias = function;
|
|
|
551 |
} else {
|
|
|
552 |
defaultAlias = f.getField().getName();
|
17 |
ilm |
553 |
}
|
83 |
ilm |
554 |
return this.addRawSelect(f, s, alias, defaultAlias);
|
17 |
ilm |
555 |
}
|
|
|
556 |
|
|
|
557 |
/**
|
|
|
558 |
* To add an item that is not a field.
|
|
|
559 |
*
|
83 |
ilm |
560 |
* @param expr any legal exp in a SELECT statement (e.g. a constant, a complex function, etc).
|
17 |
ilm |
561 |
* @param alias a name for the expression, may be <code>null</code>.
|
|
|
562 |
* @return this.
|
|
|
563 |
*/
|
93 |
ilm |
564 |
public SQLSelect addRawSelect(final String expr, final String alias) {
|
83 |
ilm |
565 |
return this.addRawSelect(null, expr, alias, null);
|
|
|
566 |
}
|
|
|
567 |
|
|
|
568 |
// private since we can't check that f is used in expr
|
|
|
569 |
// defaultName only used if alias is null
|
93 |
ilm |
570 |
private SQLSelect addRawSelect(final FieldRef f, String expr, final String alias, final String defaultName) {
|
17 |
ilm |
571 |
if (alias != null) {
|
21 |
ilm |
572 |
expr += " as " + SQLBase.quoteIdentifier(alias);
|
17 |
ilm |
573 |
}
|
|
|
574 |
this.select.add(expr);
|
83 |
ilm |
575 |
if (f != null)
|
93 |
ilm |
576 |
this.addIfNotExist(f.getTableRef());
|
83 |
ilm |
577 |
this.selectFields.add(f);
|
|
|
578 |
this.selectNames.add(alias != null ? alias : defaultName);
|
17 |
ilm |
579 |
return this;
|
|
|
580 |
}
|
|
|
581 |
|
|
|
582 |
/**
|
|
|
583 |
* Ajoute une fonction prenant * comme paramètre.
|
|
|
584 |
*
|
|
|
585 |
* @param function la fonction, eg "COUNT".
|
|
|
586 |
* @return this pour pouvoir chaîner.
|
|
|
587 |
*/
|
93 |
ilm |
588 |
public SQLSelect addSelectFunctionStar(final String function) {
|
17 |
ilm |
589 |
return this.addRawSelect(function + "(*)", null);
|
|
|
590 |
}
|
|
|
591 |
|
93 |
ilm |
592 |
public SQLSelect addSelectStar(final TableRef table) {
|
73 |
ilm |
593 |
this.select.add(SQLBase.quoteIdentifier(table.getAlias()) + ".*");
|
93 |
ilm |
594 |
this.addIfNotExist(table);
|
83 |
ilm |
595 |
final List<SQLField> allFields = table.getTable().getOrderedFields();
|
|
|
596 |
this.selectFields.addAll(allFields);
|
|
|
597 |
for (final SQLField f : allFields)
|
|
|
598 |
this.selectNames.add(f.getName());
|
17 |
ilm |
599 |
return this;
|
|
|
600 |
}
|
|
|
601 |
|
93 |
ilm |
602 |
public SQLSelect clearSelect() {
|
|
|
603 |
this.select.clear();
|
|
|
604 |
this.selectFields.clear();
|
|
|
605 |
this.selectNames.clear();
|
|
|
606 |
return this;
|
|
|
607 |
}
|
|
|
608 |
|
17 |
ilm |
609 |
// *** from
|
|
|
610 |
|
93 |
ilm |
611 |
public SQLSelect addFrom(final SQLTable table, final String alias) {
|
|
|
612 |
return this.addFrom(AliasedTable.getTableRef(table, alias));
|
17 |
ilm |
613 |
}
|
|
|
614 |
|
|
|
615 |
/**
|
|
|
616 |
* Explicitely add a table to the from clause. Rarely needed since tables are auto added by
|
|
|
617 |
* addSelect(), setWhere() and addJoin().
|
|
|
618 |
*
|
65 |
ilm |
619 |
* @param t the table to add.
|
17 |
ilm |
620 |
* @return this.
|
|
|
621 |
*/
|
93 |
ilm |
622 |
public SQLSelect addFrom(final TableRef t) {
|
|
|
623 |
this.addIfNotExist(t);
|
17 |
ilm |
624 |
return this;
|
|
|
625 |
}
|
|
|
626 |
|
|
|
627 |
// *** where
|
|
|
628 |
|
|
|
629 |
/**
|
|
|
630 |
* Change la clause where de cette requete.
|
|
|
631 |
*
|
|
|
632 |
* @param w la nouvelle clause, <code>null</code> pour aucune clause.
|
|
|
633 |
* @return this.
|
|
|
634 |
*/
|
93 |
ilm |
635 |
public SQLSelect setWhere(final Where w) {
|
17 |
ilm |
636 |
this.where = w;
|
|
|
637 |
// FIXME si where était non null alors on a ajouté des tables dans FROM
|
|
|
638 |
// qui ne sont peut être plus utiles
|
|
|
639 |
// une solution : ne calculer le from que dans asString() => marche pas car on s'en
|
|
|
640 |
// sert dans addOrder
|
67 |
ilm |
641 |
if (w != null) {
|
21 |
ilm |
642 |
for (final FieldRef f : w.getFields()) {
|
93 |
ilm |
643 |
this.addIfNotExist(f.getTableRef());
|
17 |
ilm |
644 |
}
|
|
|
645 |
}
|
|
|
646 |
return this;
|
|
|
647 |
}
|
|
|
648 |
|
93 |
ilm |
649 |
public SQLSelect setWhere(final FieldRef field, final String op, final int i) {
|
65 |
ilm |
650 |
return this.setWhere(new Where(field, op, i));
|
17 |
ilm |
651 |
}
|
|
|
652 |
|
|
|
653 |
/**
|
|
|
654 |
* Ajoute le Where passé à celui de ce select.
|
|
|
655 |
*
|
|
|
656 |
* @param w le Where à ajouter.
|
|
|
657 |
* @return this.
|
|
|
658 |
*/
|
93 |
ilm |
659 |
public SQLSelect andWhere(final Where w) {
|
17 |
ilm |
660 |
return this.setWhere(Where.and(this.getWhere(), w));
|
|
|
661 |
}
|
|
|
662 |
|
|
|
663 |
// *** join
|
|
|
664 |
|
93 |
ilm |
665 |
/**
|
|
|
666 |
* Add a join to this SELECT.
|
|
|
667 |
*
|
|
|
668 |
* @param joinType can be INNER, LEFT or RIGHT.
|
|
|
669 |
* @param existingAlias an alias for a table already in this select, can be <code>null</code>.
|
|
|
670 |
* @param s how to join a new table, i.e. the {@link Step#getTo() destination} will be added.
|
|
|
671 |
* @param joinedAlias the alias of the new table, can be <code>null</code>.
|
|
|
672 |
* @return the added join.
|
|
|
673 |
*/
|
|
|
674 |
public SQLSelectJoin addJoin(final String joinType, final String existingAlias, final Step s, final String joinedAlias) {
|
|
|
675 |
final TableRef existingTable = this.getTableRef(existingAlias != null ? existingAlias : s.getFrom().getName());
|
|
|
676 |
final TableRef joinedTable = new AliasedTable(s.getTo(), joinedAlias);
|
|
|
677 |
return this.addJoin(new SQLSelectJoin(this, joinType, existingTable, s, joinedTable));
|
|
|
678 |
}
|
|
|
679 |
|
17 |
ilm |
680 |
// simple joins (with foreign field)
|
|
|
681 |
|
|
|
682 |
/**
|
|
|
683 |
* Add a join to this SELECT. Eg if <code>f</code> is |BATIMENT.ID_SITE|, then "join SITE on
|
|
|
684 |
* BATIMENT.ID_SITE = SITE.ID" will be added.
|
|
|
685 |
*
|
|
|
686 |
* @param joinType can be INNER, LEFT or RIGHT.
|
|
|
687 |
* @param f a foreign key, eg |BATIMENT.ID_SITE|.
|
|
|
688 |
* @return the added join.
|
|
|
689 |
*/
|
93 |
ilm |
690 |
public SQLSelectJoin addJoin(final String joinType, final FieldRef f) {
|
17 |
ilm |
691 |
return this.addJoin(joinType, f, null);
|
|
|
692 |
}
|
|
|
693 |
|
|
|
694 |
/**
|
|
|
695 |
* Add a join to this SELECT. Eg if <code>f</code> is bat.ID_SITE and <code>alias</code> is "s",
|
|
|
696 |
* then "join SITE s on bat.ID_SITE = s.ID" will be added.
|
|
|
697 |
*
|
|
|
698 |
* @param joinType can be INNER, LEFT or RIGHT.
|
|
|
699 |
* @param f a foreign key, eg obs.ID_ARTICLE_2.
|
|
|
700 |
* @param alias the alias for joined table, can be <code>null</code>, eg "art2".
|
|
|
701 |
* @return the added join.
|
|
|
702 |
*/
|
93 |
ilm |
703 |
public SQLSelectJoin addJoin(final String joinType, final FieldRef f, final String alias) {
|
|
|
704 |
final Step s = Step.create(f.getField(), org.openconcerto.sql.model.graph.Link.Direction.FOREIGN);
|
|
|
705 |
return this.addJoin(joinType, f.getAlias(), s, alias);
|
17 |
ilm |
706 |
}
|
|
|
707 |
|
|
|
708 |
// arbitrary joins
|
|
|
709 |
|
|
|
710 |
/**
|
|
|
711 |
* Add a join to this SELECT, inferring the joined table from the where.
|
|
|
712 |
*
|
|
|
713 |
* @param joinType can be INNER, LEFT or RIGHT.
|
|
|
714 |
* @param w the where joining the new table.
|
|
|
715 |
* @return the added join.
|
|
|
716 |
* @throws IllegalArgumentException if <code>w</code> hasn't exactly one table not yet
|
|
|
717 |
* {@link #contains(String) contained} in this.
|
|
|
718 |
*/
|
93 |
ilm |
719 |
public SQLSelectJoin addJoin(final String joinType, final Where w) {
|
17 |
ilm |
720 |
final Set<AliasedTable> tables = new HashSet<AliasedTable>();
|
|
|
721 |
for (final FieldRef f : w.getFields()) {
|
|
|
722 |
if (!this.contains(f.getAlias())) {
|
93 |
ilm |
723 |
// since it's a Set, use same class (i.e. SQLTable.equals(AliasedTable) always
|
|
|
724 |
// return false)
|
17 |
ilm |
725 |
tables.add(new AliasedTable(f.getField().getTable(), f.getAlias()));
|
|
|
726 |
}
|
|
|
727 |
}
|
|
|
728 |
if (tables.size() == 0)
|
|
|
729 |
throw new IllegalArgumentException("No tables to add in " + w);
|
|
|
730 |
if (tables.size() > 1)
|
|
|
731 |
throw new IllegalArgumentException("More than one table to add (" + tables + ") in " + w);
|
|
|
732 |
final AliasedTable joinedTable = tables.iterator().next();
|
93 |
ilm |
733 |
return addJoin(joinType, joinedTable, w);
|
17 |
ilm |
734 |
}
|
|
|
735 |
|
93 |
ilm |
736 |
public SQLSelectJoin addJoin(final String joinType, final TableRef joinedTable, final Where w) {
|
|
|
737 |
// try to parse the where to find a Step
|
|
|
738 |
final Tuple2<FieldRef, TableRef> parsed = SQLSelectJoin.parse(w);
|
|
|
739 |
final FieldRef foreignFieldParsed = parsed.get0();
|
|
|
740 |
final Step s;
|
|
|
741 |
final TableRef existingTable;
|
|
|
742 |
if (foreignFieldParsed == null) {
|
|
|
743 |
s = null;
|
|
|
744 |
existingTable = null;
|
|
|
745 |
} else {
|
|
|
746 |
final TableRef srcTableParsed = foreignFieldParsed.getTableRef();
|
|
|
747 |
final TableRef destTableParsed = parsed.get1();
|
|
|
748 |
if (AliasedTable.equals(destTableParsed, joinedTable)) {
|
|
|
749 |
existingTable = srcTableParsed;
|
|
|
750 |
s = Step.create(foreignFieldParsed.getField(), org.openconcerto.sql.model.graph.Link.Direction.FOREIGN);
|
|
|
751 |
} else if (AliasedTable.equals(srcTableParsed, joinedTable)) {
|
|
|
752 |
existingTable = destTableParsed;
|
|
|
753 |
s = Step.create(foreignFieldParsed.getField(), org.openconcerto.sql.model.graph.Link.Direction.REFERENT);
|
|
|
754 |
} else {
|
|
|
755 |
throw new IllegalArgumentException("Joined table " + joinedTable + " isn't referenced in " + w);
|
|
|
756 |
}
|
|
|
757 |
}
|
17 |
ilm |
758 |
|
93 |
ilm |
759 |
return this.addJoin(new SQLSelectJoin(this, joinType, joinedTable, w, s, existingTable));
|
17 |
ilm |
760 |
}
|
|
|
761 |
|
|
|
762 |
/**
|
|
|
763 |
* Add a join that goes backward through a foreign key, eg LEFT JOIN "KD_2006"."BATIMENT" "bat"
|
|
|
764 |
* on "s"."ID" = "bat"."ID_SITE".
|
|
|
765 |
*
|
|
|
766 |
* @param joinType can be INNER, LEFT or RIGHT.
|
|
|
767 |
* @param joinAlias the alias for the joined table, must not exist, eg "bat".
|
|
|
768 |
* @param ff the foreign field, eg |BATIMENT.ID_SITE|.
|
|
|
769 |
* @param foreignTableAlias the alias for the foreign table, must exist, eg "sit" or
|
|
|
770 |
* <code>null</code> for "SITE".
|
|
|
771 |
* @return the added join.
|
|
|
772 |
*/
|
93 |
ilm |
773 |
public SQLSelectJoin addBackwardJoin(final String joinType, final String joinAlias, final SQLField ff, final String foreignTableAlias) {
|
65 |
ilm |
774 |
return this.addBackwardJoin(joinType, new AliasedField(ff, joinAlias), foreignTableAlias);
|
|
|
775 |
}
|
|
|
776 |
|
|
|
777 |
/**
|
|
|
778 |
* Add a join that goes backward through a foreign key, eg LEFT JOIN "KD_2006"."BATIMENT" "bat"
|
|
|
779 |
* on "s"."ID" = "bat"."ID_SITE".
|
|
|
780 |
*
|
|
|
781 |
* @param joinType can be INNER, LEFT or RIGHT.
|
|
|
782 |
* @param ff the foreign field, the alias must not exist, e.g. bat.ID_SITE.
|
|
|
783 |
* @param foreignTableAlias the alias for the foreign table, must exist, e.g. "sit" or
|
|
|
784 |
* <code>null</code> for "SITE".
|
|
|
785 |
* @return the added join.
|
|
|
786 |
*/
|
93 |
ilm |
787 |
public SQLSelectJoin addBackwardJoin(final String joinType, final FieldRef ff, final String foreignTableAlias) {
|
|
|
788 |
final Step s = Step.create(ff.getField(), org.openconcerto.sql.model.graph.Link.Direction.REFERENT);
|
|
|
789 |
return this.addJoin(joinType, foreignTableAlias, s, ff.getAlias());
|
17 |
ilm |
790 |
}
|
|
|
791 |
|
93 |
ilm |
792 |
private final SQLSelectJoin addJoin(final SQLSelectJoin j) {
|
17 |
ilm |
793 |
// first check if the joined table is not already in this from
|
93 |
ilm |
794 |
// avoid this (where the 2nd line already added MOUVEMENT) :
|
|
|
795 |
// sel.addSelect(tableEcriture.getField("NOM"));
|
|
|
796 |
// sel.addSelect(tableMouvement.getField("NUMERO"));
|
|
|
797 |
// sel.addJoin("LEFT", "ECRITURE.ID_MOUVEMENT");
|
|
|
798 |
final boolean added = this.declaredTables.add(j.getJoinedTable(), true);
|
|
|
799 |
// since we passed mustBeNew=true
|
|
|
800 |
assert added;
|
17 |
ilm |
801 |
this.from.add(j);
|
|
|
802 |
this.joinAliases.add(j.getAlias());
|
|
|
803 |
this.joins.add(j);
|
|
|
804 |
return j;
|
|
|
805 |
}
|
|
|
806 |
|
93 |
ilm |
807 |
// ATTN doesn't check if the join is referenced
|
|
|
808 |
private final void removeJoin(final SQLSelectJoin j) {
|
|
|
809 |
if (this.joins.remove(j)) {
|
|
|
810 |
final boolean removed = this.declaredTables.remove(j.getJoinedTable());
|
|
|
811 |
assert removed;
|
|
|
812 |
this.from.remove(j);
|
|
|
813 |
this.joinAliases.remove(j.getAlias());
|
|
|
814 |
}
|
|
|
815 |
}
|
|
|
816 |
|
17 |
ilm |
817 |
public final List<SQLSelectJoin> getJoins() {
|
|
|
818 |
return Collections.unmodifiableList(this.joins);
|
|
|
819 |
}
|
|
|
820 |
|
|
|
821 |
/**
|
|
|
822 |
* Get the join going through <code>ff</code>, regardless of its alias.
|
|
|
823 |
*
|
|
|
824 |
* @param ff a foreign field, eg |BATIMENT.ID_SITE|.
|
93 |
ilm |
825 |
* @return the corresponding join or <code>null</code> if not exactly one is found, e.g. LEFT
|
|
|
826 |
* JOIN "test"."SITE" "s" on "bat"."ID_SITE"="s"."ID"
|
17 |
ilm |
827 |
*/
|
93 |
ilm |
828 |
public final SQLSelectJoin getJoinFromField(final SQLField ff) {
|
|
|
829 |
return CollectionUtils.getSole(getJoinsFromField(ff));
|
|
|
830 |
}
|
|
|
831 |
|
|
|
832 |
public final List<SQLSelectJoin> getJoinsFromField(final SQLField ff) {
|
|
|
833 |
final List<SQLSelectJoin> res = new ArrayList<SQLSelectJoin>();
|
17 |
ilm |
834 |
for (final SQLSelectJoin j : this.joins) {
|
93 |
ilm |
835 |
final Step s = j.getStep();
|
|
|
836 |
if (s != null && ff.equals(s.getSingleField())) {
|
|
|
837 |
res.add(j);
|
17 |
ilm |
838 |
}
|
|
|
839 |
}
|
93 |
ilm |
840 |
return res;
|
17 |
ilm |
841 |
}
|
|
|
842 |
|
|
|
843 |
/**
|
|
|
844 |
* The first join adding the passed table.
|
|
|
845 |
*
|
|
|
846 |
* @param t the table to search for, e.g. /LOCAL/.
|
|
|
847 |
* @return the first matching join or <code>null</code> if none found, eg LEFT JOIN
|
|
|
848 |
* "test"."LOCAL" "l" on "r"."ID_LOCAL"="l"."ID"
|
|
|
849 |
*/
|
93 |
ilm |
850 |
public final SQLSelectJoin findJoinAdding(final SQLTable t) {
|
17 |
ilm |
851 |
for (final SQLSelectJoin j : this.joins) {
|
|
|
852 |
if (j.getJoinedTable().getTable().equals(t)) {
|
|
|
853 |
return j;
|
|
|
854 |
}
|
|
|
855 |
}
|
|
|
856 |
return null;
|
|
|
857 |
}
|
|
|
858 |
|
|
|
859 |
/**
|
|
|
860 |
* The join adding the passed table alias.
|
|
|
861 |
*
|
|
|
862 |
* @param alias a table alias, e.g. "l".
|
|
|
863 |
* @return the matching join or <code>null</code> if none found, eg LEFT JOIN "test"."LOCAL" "l"
|
|
|
864 |
* on "r"."ID_LOCAL"="l"."ID"
|
|
|
865 |
*/
|
|
|
866 |
public final SQLSelectJoin getJoinAdding(final String alias) {
|
|
|
867 |
for (final SQLSelectJoin j : this.joins) {
|
|
|
868 |
if (j.getAlias().equals(alias)) {
|
|
|
869 |
return j;
|
|
|
870 |
}
|
|
|
871 |
}
|
|
|
872 |
return null;
|
|
|
873 |
}
|
|
|
874 |
|
|
|
875 |
/**
|
93 |
ilm |
876 |
* Get the join going through <code>ff</code>, matching its alias but regardless of its
|
|
|
877 |
* direction.
|
17 |
ilm |
878 |
*
|
|
|
879 |
* @param ff a foreign field, eg |BATIMENT.ID_SITE|.
|
93 |
ilm |
880 |
* @return the corresponding join or <code>null</code> if not exactly one is found, eg
|
|
|
881 |
* <code>null</code> if this only contains LEFT JOIN "test"."SITE" "s" on
|
|
|
882 |
* "bat"."ID_SITE"="s"."ID" or if it contains
|
|
|
883 |
*
|
|
|
884 |
* <pre>
|
|
|
885 |
* LEFT JOIN "test"."SITE" s1 on "BATIMENT"."ID_SITE" = s1."ID" and s1."FOO"
|
|
|
886 |
* LEFT JOIN "test"."SITE" s2 on "BATIMENT"."ID_SITE" = s2."ID" and s2."BAR"
|
132 |
ilm |
887 |
* </pre>
|
17 |
ilm |
888 |
*/
|
93 |
ilm |
889 |
public final SQLSelectJoin getJoin(final FieldRef ff) {
|
|
|
890 |
return this.getJoin(ff, null);
|
17 |
ilm |
891 |
}
|
|
|
892 |
|
93 |
ilm |
893 |
public final SQLSelectJoin getJoin(final FieldRef ff, final String foreignAlias) {
|
|
|
894 |
final Step s = Step.create(ff.getField(), org.openconcerto.sql.model.graph.Link.Direction.FOREIGN);
|
|
|
895 |
final List<SQLSelectJoin> res = new ArrayList<SQLSelectJoin>();
|
|
|
896 |
res.addAll(this.getJoins(ff.getAlias(), s, foreignAlias));
|
|
|
897 |
res.addAll(this.getJoins(foreignAlias, s.reverse(), ff.getAlias()));
|
|
|
898 |
|
|
|
899 |
// if we specify both aliases there can't be more than one join
|
|
|
900 |
if (foreignAlias != null && res.size() > 1)
|
|
|
901 |
throw new IllegalStateException("More than one join matched " + ff + " and " + foreignAlias + " :\n" + CollectionUtils.join(res, "\n"));
|
|
|
902 |
return CollectionUtils.getSole(res);
|
|
|
903 |
}
|
|
|
904 |
|
|
|
905 |
/**
|
|
|
906 |
* Get the JOIN matching the passed step.
|
|
|
907 |
*
|
|
|
908 |
* @param fromAlias the alias for the source of the step, <code>null</code> match any alias.
|
|
|
909 |
* @param s the {@link SQLSelectJoin#getStep() step}, cannot be <code>null</code>.
|
|
|
910 |
* @param joinedAlias the alias for the destination of the step, i.e. the
|
|
|
911 |
* {@link SQLSelectJoin#getJoinedTable() added table}, <code>null</code> match any alias.
|
|
|
912 |
* @return the matching joins.
|
|
|
913 |
*/
|
|
|
914 |
public final List<SQLSelectJoin> getJoins(final String fromAlias, final Step s, final String joinedAlias) {
|
|
|
915 |
if (s == null)
|
|
|
916 |
throw new NullPointerException("Null step");
|
|
|
917 |
final List<SQLSelectJoin> res = new ArrayList<SQLSelectJoin>();
|
17 |
ilm |
918 |
for (final SQLSelectJoin j : this.joins) {
|
93 |
ilm |
919 |
final Step joinStep = j.getStep();
|
|
|
920 |
if (s.equals(joinStep) && (fromAlias == null || j.getExistingTable().getAlias().equals(fromAlias)) && (joinedAlias == null || j.getAlias().equals(joinedAlias))) {
|
|
|
921 |
res.add(j);
|
17 |
ilm |
922 |
}
|
|
|
923 |
}
|
93 |
ilm |
924 |
return res;
|
17 |
ilm |
925 |
}
|
|
|
926 |
|
|
|
927 |
/**
|
|
|
928 |
* Assure that there's a path from <code>tableAlias</code> through <code>p</code>, adding the
|
|
|
929 |
* missing joins.
|
|
|
930 |
*
|
|
|
931 |
* @param tableAlias the table at the start, eg "loc".
|
|
|
932 |
* @param p the path that must be added, eg LOCAL-BATIMENT-SITE.
|
|
|
933 |
* @return the alias of the last table of the path, "sit".
|
|
|
934 |
*/
|
93 |
ilm |
935 |
public TableRef assurePath(final String tableAlias, final Path p) {
|
17 |
ilm |
936 |
return this.followPath(tableAlias, p, true);
|
|
|
937 |
}
|
|
|
938 |
|
93 |
ilm |
939 |
public TableRef followPath(final String tableAlias, final Path p) {
|
17 |
ilm |
940 |
return this.followPath(tableAlias, p, false);
|
|
|
941 |
}
|
|
|
942 |
|
|
|
943 |
/**
|
|
|
944 |
* Return the alias at the end of the passed path.
|
|
|
945 |
*
|
|
|
946 |
* @param tableAlias the table at the start, eg "loc".
|
|
|
947 |
* @param p the path to follow, eg LOCAL-BATIMENT-SITE.
|
|
|
948 |
* @param create <code>true</code> if missing joins should be created.
|
|
|
949 |
* @return the alias of the last table of the path or <code>null</code>, eg "sit".
|
|
|
950 |
*/
|
93 |
ilm |
951 |
public TableRef followPath(final String tableAlias, final Path p, final boolean create) {
|
65 |
ilm |
952 |
final TableRef firstTableRef = this.getTableRef(tableAlias);
|
|
|
953 |
final SQLTable firstTable = firstTableRef.getTable();
|
17 |
ilm |
954 |
if (!p.getFirst().equals(firstTable) && !p.getLast().equals(firstTable))
|
|
|
955 |
throw new IllegalArgumentException("neither ends of " + p + " is " + firstTable);
|
|
|
956 |
else if (!p.getFirst().equals(firstTable))
|
|
|
957 |
return followPath(tableAlias, p.reverse(), create);
|
|
|
958 |
|
65 |
ilm |
959 |
TableRef current = firstTableRef;
|
17 |
ilm |
960 |
for (int i = 0; i < p.length(); i++) {
|
73 |
ilm |
961 |
final Step step = p.getStep(i);
|
93 |
ilm |
962 |
final List<SQLSelectJoin> joins = this.getJoins(current.getAlias(), step, null);
|
|
|
963 |
if (joins.size() > 1)
|
|
|
964 |
throw new IllegalStateException("More than one join from " + current + " through " + step + " : " + joins);
|
|
|
965 |
if (joins.size() == 1) {
|
|
|
966 |
current = joins.get(0).getJoinedTable();
|
|
|
967 |
} else if (create) {
|
17 |
ilm |
968 |
// we must add a join
|
65 |
ilm |
969 |
final String uniqAlias = getUniqueAlias("assurePath_" + i);
|
93 |
ilm |
970 |
final SQLSelectJoin createdJoin = this.addJoin("LEFT", current.getAlias(), step, uniqAlias);
|
65 |
ilm |
971 |
current = createdJoin.getJoinedTable();
|
93 |
ilm |
972 |
} else {
|
17 |
ilm |
973 |
return null;
|
93 |
ilm |
974 |
}
|
17 |
ilm |
975 |
}
|
|
|
976 |
|
|
|
977 |
return current;
|
|
|
978 |
}
|
|
|
979 |
|
142 |
ilm |
980 |
public final FieldRef followFieldPath(final IFieldPath fp) {
|
|
|
981 |
return this.followFieldPath(fp.getPath().getFirst().getAlias(), fp);
|
|
|
982 |
}
|
|
|
983 |
|
|
|
984 |
public final FieldRef followFieldPath(final String tableAlias, final IFieldPath fp) {
|
|
|
985 |
return this.followPath(tableAlias, fp.getPath()).getField(fp.getFieldName());
|
|
|
986 |
}
|
|
|
987 |
|
17 |
ilm |
988 |
public boolean isExcludeUndefined() {
|
|
|
989 |
return this.generalExcludeUndefined;
|
|
|
990 |
}
|
|
|
991 |
|
93 |
ilm |
992 |
public void setExcludeUndefined(final boolean excludeUndefined) {
|
17 |
ilm |
993 |
this.generalExcludeUndefined = excludeUndefined;
|
|
|
994 |
}
|
|
|
995 |
|
93 |
ilm |
996 |
public void setExcludeUndefined(final boolean exclude, final SQLTable table) {
|
17 |
ilm |
997 |
this.excludeUndefined.put(table, Boolean.valueOf(exclude));
|
|
|
998 |
}
|
|
|
999 |
|
93 |
ilm |
1000 |
public void setArchivedPolicy(final ArchiveMode policy) {
|
17 |
ilm |
1001 |
this.setArchivedPolicy(null, policy);
|
|
|
1002 |
}
|
|
|
1003 |
|
93 |
ilm |
1004 |
public void setArchivedPolicy(final SQLTable t, final ArchiveMode policy) {
|
17 |
ilm |
1005 |
this.archivedPolicy.put(t, policy);
|
|
|
1006 |
}
|
|
|
1007 |
|
93 |
ilm |
1008 |
public final void setDistinct(final boolean distinct) {
|
17 |
ilm |
1009 |
this.distinct = distinct;
|
|
|
1010 |
}
|
|
|
1011 |
|
|
|
1012 |
/**
|
132 |
ilm |
1013 |
* Whether this SELECT should wait until all current transactions are complete. This prevent a
|
17 |
ilm |
1014 |
* SELECT following an UPDATE from seeing rows as they were before. NOTE that this may conflict
|
|
|
1015 |
* with other clauses (GROUP BY, DISTINCT, etc.).
|
|
|
1016 |
*
|
|
|
1017 |
* @param waitTrx <code>true</code> if this select should wait.
|
132 |
ilm |
1018 |
* @deprecated use {@link #setLockStrength(LockStrength)}
|
17 |
ilm |
1019 |
*/
|
93 |
ilm |
1020 |
public void setWaitPreviousWriteTX(final boolean waitTrx) {
|
132 |
ilm |
1021 |
this.setLockStrength(waitTrx ? LockStrength.SHARE : LockStrength.NONE);
|
17 |
ilm |
1022 |
}
|
|
|
1023 |
|
132 |
ilm |
1024 |
/**
|
|
|
1025 |
* Set the lock strength for the returned rows. NOTE : this is a minimum, e.g. H2 only supports
|
|
|
1026 |
* {@link LockStrength#UPDATE}.
|
|
|
1027 |
*
|
|
|
1028 |
* @param l the new lock strength.
|
|
|
1029 |
* @throws IllegalArgumentException if the {@link #getSQLSystem() system} doesn't support locks.
|
|
|
1030 |
*/
|
|
|
1031 |
public final void setLockStrength(final LockStrength l) throws IllegalArgumentException {
|
|
|
1032 |
final SQLSystem sys = getSQLSystem();
|
|
|
1033 |
if (l != LockStrength.NONE && sys != SQLSystem.POSTGRESQL && sys != SQLSystem.MYSQL && sys != SQLSystem.H2)
|
|
|
1034 |
throw new IllegalArgumentException("This system doesn't support locks : " + sys);
|
|
|
1035 |
this.lockStrength = l;
|
|
|
1036 |
}
|
|
|
1037 |
|
|
|
1038 |
public final LockStrength getLockStrength() {
|
|
|
1039 |
return this.lockStrength;
|
|
|
1040 |
}
|
|
|
1041 |
|
|
|
1042 |
public void addLockedTable(final String table) {
|
|
|
1043 |
if (this.getLockStrength() == LockStrength.NONE)
|
|
|
1044 |
this.setLockStrength(LockStrength.SHARE);
|
17 |
ilm |
1045 |
this.waitTrxTables.add(SQLBase.quoteIdentifier(table));
|
|
|
1046 |
}
|
|
|
1047 |
|
|
|
1048 |
/**
|
|
|
1049 |
* Set the maximum number of rows to return.
|
|
|
1050 |
*
|
|
|
1051 |
* @param limit the number of rows, <code>null</code> meaning no limit
|
|
|
1052 |
* @return this.
|
|
|
1053 |
*/
|
|
|
1054 |
public SQLSelect setLimit(final Integer limit) {
|
|
|
1055 |
this.limit = limit;
|
|
|
1056 |
return this;
|
|
|
1057 |
}
|
|
|
1058 |
|
|
|
1059 |
public final Integer getLimit() {
|
|
|
1060 |
return this.limit;
|
|
|
1061 |
}
|
|
|
1062 |
|
93 |
ilm |
1063 |
/**
|
|
|
1064 |
* Set the number of rows to skip. NOTE: many systems require an ORDER BY, but even if some
|
|
|
1065 |
* don't you should use one to get consistent results.
|
|
|
1066 |
*
|
|
|
1067 |
* @param offset number of rows to skip, <code>0</code> meaning don't skip any.
|
|
|
1068 |
* @return this.
|
|
|
1069 |
*/
|
|
|
1070 |
public SQLSelect setOffset(final int offset) {
|
|
|
1071 |
if (offset < 0)
|
|
|
1072 |
throw new IllegalArgumentException("Negative offset : " + offset);
|
|
|
1073 |
this.offset = offset;
|
|
|
1074 |
return this;
|
|
|
1075 |
}
|
|
|
1076 |
|
|
|
1077 |
public int getOffset() {
|
|
|
1078 |
return this.offset;
|
|
|
1079 |
}
|
|
|
1080 |
|
17 |
ilm |
1081 |
@Override
|
93 |
ilm |
1082 |
public boolean equals(final Object o) {
|
17 |
ilm |
1083 |
if (o instanceof SQLSelect)
|
|
|
1084 |
// MAYBE use instance variables
|
|
|
1085 |
return this.asString().equals(((SQLSelect) o).asString());
|
|
|
1086 |
else
|
|
|
1087 |
return false;
|
|
|
1088 |
}
|
|
|
1089 |
|
|
|
1090 |
@Override
|
|
|
1091 |
public int hashCode() {
|
|
|
1092 |
// don't use asString() which is more CPU intensive
|
|
|
1093 |
return this.select.hashCode() + this.from.getSQL().hashCode() + (this.where == null ? 0 : this.where.hashCode());
|
|
|
1094 |
}
|
|
|
1095 |
|
|
|
1096 |
/**
|
132 |
ilm |
1097 |
* This method will return a query for counting rows of this SELECT.
|
|
|
1098 |
*
|
|
|
1099 |
* @return a query returning a single number, never <code>null</code>.
|
|
|
1100 |
*/
|
|
|
1101 |
public final String getForRowCount() {
|
|
|
1102 |
if (this.getLimit() != null && this.getLimit().intValue() == 0)
|
|
|
1103 |
return "SELECT 0";
|
|
|
1104 |
|
|
|
1105 |
final SQLSelect res = new SQLSelect(this);
|
|
|
1106 |
if (res.clearForRowCount(true)) {
|
|
|
1107 |
return "select count(*) from (" + res.asString() + ") subq";
|
|
|
1108 |
} else {
|
|
|
1109 |
return res.asString();
|
|
|
1110 |
}
|
|
|
1111 |
}
|
|
|
1112 |
|
|
|
1113 |
/**
|
93 |
ilm |
1114 |
* This method will replace the expressions in the {@link #getSelect()} by <code>count(*)</code>
|
|
|
1115 |
* , further it will remove any items not useful for counting rows. This includes
|
|
|
1116 |
* {@link #getOrder()} and left joins.
|
132 |
ilm |
1117 |
*
|
|
|
1118 |
* @throws IllegalStateException if this has GROUP BY, HAVING, OFFSET or LIMIT
|
|
|
1119 |
* @see #getForRowCount()
|
93 |
ilm |
1120 |
*/
|
132 |
ilm |
1121 |
public final void clearForRowCount() throws IllegalStateException {
|
|
|
1122 |
this.clearForRowCount(false);
|
|
|
1123 |
}
|
|
|
1124 |
|
|
|
1125 |
private final boolean clearForRowCount(final boolean allowSubquery) throws IllegalStateException {
|
|
|
1126 |
final boolean hasGroupByOrHaving = !this.groupBy.isEmpty() || this.having != null;
|
|
|
1127 |
if (!allowSubquery && hasGroupByOrHaving)
|
93 |
ilm |
1128 |
throw new IllegalStateException("Group by present");
|
132 |
ilm |
1129 |
final boolean hasOffsetOrLimit = this.getOffset() > 0 || this.getLimit() != null;
|
|
|
1130 |
if (!allowSubquery && hasOffsetOrLimit)
|
|
|
1131 |
throw new IllegalStateException("Offset or limit present");
|
93 |
ilm |
1132 |
|
|
|
1133 |
this.clearSelect();
|
|
|
1134 |
// not needed and some systems require the used fields to be in the select
|
|
|
1135 |
this.clearOrder();
|
|
|
1136 |
// some systems don't support aggregate functions (e.g. count) with this
|
|
|
1137 |
this.setWaitPreviousWriteTX(false);
|
|
|
1138 |
|
|
|
1139 |
final Set<String> requiredAliases;
|
|
|
1140 |
if (this.getWhere() == null) {
|
|
|
1141 |
requiredAliases = Collections.emptySet();
|
|
|
1142 |
} else {
|
|
|
1143 |
requiredAliases = new HashSet<String>();
|
|
|
1144 |
for (final FieldRef f : this.getWhere().getFields()) {
|
|
|
1145 |
requiredAliases.add(f.getTableRef().getAlias());
|
|
|
1146 |
}
|
|
|
1147 |
}
|
|
|
1148 |
for (final SQLSelectJoin j : new ArrayList<SQLSelectJoin>(this.joins)) {
|
|
|
1149 |
if (j.getJoinType().equalsIgnoreCase("left") && !requiredAliases.contains(j.getAlias()))
|
|
|
1150 |
this.removeJoin(j);
|
|
|
1151 |
}
|
|
|
1152 |
|
132 |
ilm |
1153 |
if (hasGroupByOrHaving || hasOffsetOrLimit) {
|
|
|
1154 |
assert allowSubquery;
|
|
|
1155 |
this.addRawSelect("1", null);
|
|
|
1156 |
return true;
|
|
|
1157 |
} else {
|
|
|
1158 |
this.addSelectFunctionStar("count");
|
|
|
1159 |
return false;
|
|
|
1160 |
}
|
93 |
ilm |
1161 |
}
|
|
|
1162 |
|
|
|
1163 |
public final Map<String, TableRef> getTableRefs() {
|
|
|
1164 |
return this.declaredTables.getMap();
|
|
|
1165 |
}
|
|
|
1166 |
|
|
|
1167 |
/**
|
17 |
ilm |
1168 |
* Returns the table designated in this select by name.
|
|
|
1169 |
*
|
|
|
1170 |
* @param name a table name or an alias, eg "OBSERVATION" or "art2".
|
|
|
1171 |
* @return the table named <code>name</code>.
|
|
|
1172 |
* @throws IllegalArgumentException if <code>name</code> is unknown to this select.
|
|
|
1173 |
*/
|
93 |
ilm |
1174 |
public final SQLTable getTable(final String name) {
|
65 |
ilm |
1175 |
return this.getTableRef(name).getTable();
|
17 |
ilm |
1176 |
}
|
|
|
1177 |
|
93 |
ilm |
1178 |
public final TableRef getTableRef(final String alias) {
|
65 |
ilm |
1179 |
final TableRef res = this.declaredTables.getAliasedTable(alias);
|
|
|
1180 |
if (res == null)
|
|
|
1181 |
throw new IllegalArgumentException("alias not in this select : " + alias);
|
|
|
1182 |
return res;
|
|
|
1183 |
}
|
|
|
1184 |
|
17 |
ilm |
1185 |
/**
|
|
|
1186 |
* Return the alias for the passed table.
|
|
|
1187 |
*
|
|
|
1188 |
* @param t a table.
|
|
|
1189 |
* @return the alias for <code>t</code>, or <code>null</code> if <code>t</code> is not exactly
|
|
|
1190 |
* once in this.
|
|
|
1191 |
*/
|
93 |
ilm |
1192 |
public final TableRef getAlias(final SQLTable t) {
|
17 |
ilm |
1193 |
return this.declaredTables.getAlias(t);
|
|
|
1194 |
}
|
|
|
1195 |
|
93 |
ilm |
1196 |
public final List<TableRef> getAliases(final SQLTable t) {
|
17 |
ilm |
1197 |
return this.declaredTables.getAliases(t);
|
|
|
1198 |
}
|
|
|
1199 |
|
93 |
ilm |
1200 |
public final FieldRef getAlias(final SQLField f) {
|
17 |
ilm |
1201 |
return this.getAlias(f.getTable()).getField(f.getName());
|
|
|
1202 |
}
|
|
|
1203 |
|
|
|
1204 |
/**
|
|
|
1205 |
* See http://www.postgresql.org/docs/8.2/interactive/sql-syntax-lexical.html#SQL-SYNTAX-
|
|
|
1206 |
* IDENTIFIERS
|
|
|
1207 |
*/
|
|
|
1208 |
static final int maxAliasLength = 63;
|
|
|
1209 |
|
|
|
1210 |
/**
|
|
|
1211 |
* Return an unused alias in this select.
|
|
|
1212 |
*
|
|
|
1213 |
* @param seed the wanted name, eg "tableAlias".
|
|
|
1214 |
* @return a unique alias with the maximum possible of <code>seed</code>, eg "tableAl_1234".
|
|
|
1215 |
*/
|
|
|
1216 |
public final String getUniqueAlias(String seed) {
|
|
|
1217 |
if (seed.length() > maxAliasLength)
|
|
|
1218 |
seed = seed.substring(0, maxAliasLength);
|
|
|
1219 |
|
|
|
1220 |
if (!this.contains(seed)) {
|
|
|
1221 |
return seed;
|
|
|
1222 |
} else {
|
67 |
ilm |
1223 |
long time = 1;
|
17 |
ilm |
1224 |
for (int i = 0; i < 50; i++) {
|
|
|
1225 |
final String res;
|
|
|
1226 |
final String cat = seed + "_" + time;
|
|
|
1227 |
if (cat.length() > maxAliasLength)
|
|
|
1228 |
res = seed.substring(0, seed.length() - (cat.length() - maxAliasLength)) + "_" + time;
|
|
|
1229 |
else
|
|
|
1230 |
res = cat;
|
|
|
1231 |
if (!this.contains(res))
|
|
|
1232 |
return res;
|
|
|
1233 |
else
|
|
|
1234 |
time += 1;
|
|
|
1235 |
}
|
|
|
1236 |
// quit
|
|
|
1237 |
return null;
|
|
|
1238 |
}
|
|
|
1239 |
}
|
|
|
1240 |
|
93 |
ilm |
1241 |
private final FieldRef createRef(final String alias, final SQLField f) {
|
17 |
ilm |
1242 |
return createRef(alias, f, true);
|
|
|
1243 |
}
|
|
|
1244 |
|
|
|
1245 |
/**
|
|
|
1246 |
* Creates a FieldRef from the passed alias and field.
|
|
|
1247 |
*
|
|
|
1248 |
* @param alias the table alias, eg "obs".
|
|
|
1249 |
* @param f the field, eg |OBSERVATION.ID_TENSION|.
|
|
|
1250 |
* @param mustExist if the table name/alias must already exist in this select.
|
|
|
1251 |
* @return the corresponding FieldRef.
|
|
|
1252 |
* @throws IllegalArgumentException if <code>mustExist</code> is <code>true</code> and this does
|
|
|
1253 |
* not contain alias.
|
|
|
1254 |
*/
|
93 |
ilm |
1255 |
private final FieldRef createRef(final String alias, final SQLField f, final boolean mustExist) {
|
17 |
ilm |
1256 |
if (mustExist && !this.contains(alias))
|
|
|
1257 |
throw new IllegalArgumentException("unknown alias " + alias);
|
|
|
1258 |
return new AliasedField(f, alias);
|
|
|
1259 |
}
|
|
|
1260 |
|
|
|
1261 |
/**
|
|
|
1262 |
* Return all fields known to this instance. NOTE the fields used in ORDER BY are not returned.
|
|
|
1263 |
*
|
|
|
1264 |
* @return all fields known to this instance.
|
|
|
1265 |
*/
|
|
|
1266 |
public final Set<SQLField> getFields() {
|
83 |
ilm |
1267 |
final Set<SQLField> res = new HashSet<SQLField>(this.getSelectFields().size());
|
|
|
1268 |
for (final FieldRef f : this.getSelectFields()) {
|
|
|
1269 |
if (f != null)
|
|
|
1270 |
res.add(f.getField());
|
|
|
1271 |
}
|
17 |
ilm |
1272 |
for (final SQLSelectJoin j : getJoins())
|
|
|
1273 |
res.addAll(getFields(j.getWhere()));
|
|
|
1274 |
res.addAll(getFields(this.getWhere()));
|
|
|
1275 |
for (final FieldRef gb : this.groupBy)
|
|
|
1276 |
res.add(gb.getField());
|
|
|
1277 |
res.addAll(getFields(this.having));
|
|
|
1278 |
// MAYBE add order
|
|
|
1279 |
|
|
|
1280 |
return res;
|
|
|
1281 |
}
|
|
|
1282 |
|
93 |
ilm |
1283 |
private static final Set<SQLField> getFields(final Where w) {
|
17 |
ilm |
1284 |
if (w != null) {
|
|
|
1285 |
final Set<SQLField> res = new HashSet<SQLField>();
|
|
|
1286 |
for (final FieldRef v : w.getFields())
|
|
|
1287 |
res.add(v.getField());
|
|
|
1288 |
return res;
|
|
|
1289 |
} else
|
|
|
1290 |
return Collections.emptySet();
|
|
|
1291 |
}
|
|
|
1292 |
|
|
|
1293 |
}
|