Line 1... |
Line 1... |
1 |
/*
|
1 |
/*
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
3 |
*
|
3 |
*
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
4 |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
|
5 |
*
|
5 |
*
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
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
|
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
|
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.
|
9 |
* language governing permissions and limitations under the License.
|
Line 13... |
Line 13... |
13 |
|
13 |
|
14 |
package org.openconcerto.sql.model;
|
14 |
package org.openconcerto.sql.model;
|
15 |
|
15 |
|
16 |
import org.openconcerto.utils.CollectionUtils;
|
16 |
import org.openconcerto.utils.CollectionUtils;
|
17 |
import org.openconcerto.utils.StringUtils;
|
17 |
import org.openconcerto.utils.StringUtils;
|
18 |
import org.openconcerto.utils.cc.ITransformer;
|
- |
|
19 |
|
18 |
|
20 |
import java.util.ArrayList;
|
19 |
import java.util.ArrayList;
|
21 |
import java.util.Arrays;
|
20 |
import java.util.Arrays;
|
22 |
import java.util.Collection;
|
21 |
import java.util.Collection;
|
23 |
import java.util.Collections;
|
22 |
import java.util.Collections;
|
24 |
import java.util.List;
|
23 |
import java.util.List;
|
25 |
import java.util.Map;
|
24 |
import java.util.Map;
|
26 |
import java.util.Map.Entry;
|
25 |
import java.util.Map.Entry;
|
27 |
|
- |
|
- |
|
26 |
import java.util.Objects;
|
28 |
import org.apache.commons.collections.functors.InstanceofPredicate;
|
27 |
import java.util.stream.Collectors;
|
29 |
|
28 |
|
30 |
import net.jcip.annotations.Immutable;
|
29 |
import net.jcip.annotations.Immutable;
|
31 |
|
30 |
|
32 |
/**
|
31 |
/**
|
33 |
* Une clause WHERE dans une requete SQL. Une clause peut être facilement combinée avec d'autre,
|
32 |
* Une clause WHERE dans une requete SQL. Une clause peut être facilement combinée avec d'autre,
|
Line 125... |
Line 124... |
125 |
|
124 |
|
126 |
static public Where notInSubqueries(final FieldRef ref, final List<String> subQueries) {
|
125 |
static public Where notInSubqueries(final FieldRef ref, final List<String> subQueries) {
|
127 |
return subqueries(ref, false, subQueries);
|
126 |
return subqueries(ref, false, subQueries);
|
128 |
}
|
127 |
}
|
129 |
|
128 |
|
- |
|
129 |
static public Where inValues(final FieldRef ref, final Collection<?> values) {
|
- |
|
130 |
return compareValues(ref, RowComparison.IN, values);
|
- |
|
131 |
}
|
- |
|
132 |
|
- |
|
133 |
static public Where notInValues(final FieldRef ref, final Collection<?> values) {
|
- |
|
134 |
return compareValues(ref, RowComparison.NOT_IN, values);
|
- |
|
135 |
}
|
- |
|
136 |
|
- |
|
137 |
static public Where compareValues(final FieldRef ref, final RowComparison cmp, final Collection<?> values) {
|
- |
|
138 |
return compareValues(ref, cmp, NullValue.IS_FORBIDDEN, values);
|
- |
|
139 |
}
|
- |
|
140 |
|
- |
|
141 |
static public Where compareValues(final FieldRef ref, final RowComparison cmp, final NullValue nullMode, final Collection<?> values) {
|
- |
|
142 |
if (values.isEmpty()) {
|
- |
|
143 |
return cmp == RowComparison.IN ? FALSE : TRUE;
|
- |
|
144 |
}
|
- |
|
145 |
return createRaw(getCompareValuesClause(ref.getFieldRef(), cmp, nullMode, values, ref.getField().getType()), ref);
|
- |
|
146 |
}
|
- |
|
147 |
|
130 |
/**
|
148 |
/**
|
131 |
* Create a Where for a field value contained or not contained in sub-queries.
|
149 |
* Create a Where for a field value contained or not contained in sub-queries.
|
132 |
*
|
150 |
*
|
133 |
* @param ref the field.
|
151 |
* @param ref the field.
|
134 |
* @param in <code>true</code> if the field should be contained in the sub-queries.
|
152 |
* @param in <code>true</code> if the field should be contained in the sub-queries.
|
Line 151... |
Line 169... |
151 |
}
|
169 |
}
|
152 |
|
170 |
|
153 |
/**
|
171 |
/**
|
154 |
* To create complex Where not possible with constructors.
|
172 |
* To create complex Where not possible with constructors.
|
155 |
*
|
173 |
*
|
- |
|
174 |
* @param s the syntax to use.
|
156 |
* @param pattern a pattern to be passed to {@link SQLSelect#quote(String, Object...)}, eg
|
175 |
* @param pattern a pattern to be passed to {@link SQLBase#quoteStd(String, Object...)}, eg
|
157 |
* "EXTRACT(YEAR FROM %n) = 3007".
|
176 |
* "EXTRACT(YEAR FROM %n) = 3007".
|
158 |
* @param params the params to be passed to <code>quote()</code>, eg [|MISSION.DATE_DBT|].
|
177 |
* @param params the params to be passed to <code>quote()</code>, eg [|MISSION.DATE_DBT|].
|
159 |
* @return a new Where with the result from <code>quote()</code> as its clause, and all
|
178 |
* @return a new Where with the result from <code>quote()</code> as its clause, and all
|
160 |
* <code>FieldRef</code> in params as its fields, eg {EXTRACT(YEAR FROM "DATE_DBT") =
|
179 |
* <code>FieldRef</code> in params as its fields, eg {EXTRACT(YEAR FROM "DATE_DBT") =
|
161 |
* 3007 , |MISSION.DATE_DBT|}.
|
180 |
* 3007 , |MISSION.DATE_DBT|}.
|
162 |
*/
|
181 |
*/
|
- |
|
182 |
static public Where quote(final SQLSyntax s, final String pattern, final Object... params) {
|
- |
|
183 |
final List<FieldRef> fields = new ArrayList<>();
|
- |
|
184 |
for (final Object param : params) {
|
163 |
@SuppressWarnings("unchecked")
|
185 |
if (param instanceof FieldRef) {
|
164 |
static public Where quote(final String pattern, final Object... params) {
|
186 |
fields.add((FieldRef) param);
|
- |
|
187 |
}
|
- |
|
188 |
}
|
165 |
return new Where(SQLSelect.quote(pattern, params), org.apache.commons.collections.CollectionUtils.select(Arrays.asList(params), new InstanceofPredicate(FieldRef.class)));
|
189 |
return new Where(SQLBase.quote(s, pattern, params), fields);
|
166 |
}
|
190 |
}
|
167 |
|
191 |
|
168 |
static private final String normalizeOperator(final String op) {
|
192 |
static private final String normalizeOperator(final String op) {
|
169 |
String res = op.trim();
|
193 |
String res = op.trim();
|
170 |
if (res.equals("!="))
|
194 |
if (res.equals("!="))
|
171 |
res = "<>";
|
195 |
res = "<>";
|
172 |
return res;
|
196 |
return res;
|
173 |
}
|
197 |
}
|
174 |
|
198 |
|
175 |
static private final String comparison(final FieldRef ref, final String op, final String y) {
|
199 |
static public final String comparison(final FieldRef ref, final String op, final String y) {
|
- |
|
200 |
return comparison(null, ref.getField(), ref.getFieldRef(), op, y);
|
- |
|
201 |
}
|
- |
|
202 |
|
- |
|
203 |
static public final String comparison(final SQLSyntax s, final String x, final String op, final String y) {
|
- |
|
204 |
return comparison(Objects.requireNonNull(s, "Missing syntax"), null, x, op, y);
|
- |
|
205 |
}
|
- |
|
206 |
|
- |
|
207 |
static private final String comparison(SQLSyntax s, final DBStructureItem<?> syntaxSupplier, final String x, final String op, final String y) {
|
- |
|
208 |
Objects.requireNonNull(op, "Missing operator");
|
176 |
if (op == NULL_IS_DATA_EQ || op == NULL_IS_DATA_NEQ) {
|
209 |
if (op == NULL_IS_DATA_EQ || op == NULL_IS_DATA_NEQ) {
|
- |
|
210 |
if (s == null)
|
- |
|
211 |
s = syntaxSupplier.getDBSystemRoot().getSyntax();
|
177 |
return ref.getField().getDBSystemRoot().getSyntax().getNullIsDataComparison(ref.getFieldRef(), op == NULL_IS_DATA_EQ, y);
|
212 |
return s.getNullIsDataComparison(x, op == NULL_IS_DATA_EQ, y);
|
178 |
} else {
|
213 |
} else {
|
179 |
return ref.getFieldRef() + " " + op + " " + y;
|
214 |
return x + ' ' + op + ' ' + y;
|
180 |
}
|
215 |
}
|
181 |
}
|
216 |
}
|
182 |
|
217 |
|
183 |
static private final String getInClause(final FieldRef field1, final boolean in, final String inParens) {
|
218 |
static private final String getInClause(final FieldRef field1, final boolean in, final String inParens) {
|
- |
|
219 |
return getInClause(field1.getFieldRef(), in ? RowComparison.IN : RowComparison.NOT_IN, inParens);
|
- |
|
220 |
}
|
- |
|
221 |
|
- |
|
222 |
static private final String getInClause(final String expr, final RowComparison cmp, final String inParens) {
|
184 |
final String op = in ? " in (" : " not in (";
|
223 |
final String op = cmp == RowComparison.IN ? " in (" : " not in (";
|
185 |
return field1.getFieldRef() + op + inParens + ")";
|
224 |
return expr + op + inParens + ')';
|
- |
|
225 |
}
|
- |
|
226 |
|
- |
|
227 |
static public enum RowComparison {
|
- |
|
228 |
IN, NOT_IN
|
- |
|
229 |
}
|
- |
|
230 |
|
- |
|
231 |
static public enum NullValue {
|
- |
|
232 |
IS_DATA, IS_FORBIDDEN, IS_UNKNOWN
|
- |
|
233 |
}
|
- |
|
234 |
|
- |
|
235 |
static public final String getCompareValuesClause(final String expr, final RowComparison cmp, final Collection<?> values, final SQLType type) {
|
- |
|
236 |
return getCompareValuesClause(expr, cmp, NullValue.IS_FORBIDDEN, values, type);
|
- |
|
237 |
}
|
- |
|
238 |
|
- |
|
239 |
static public final String getCompareValuesClause(final String expr, final RowComparison cmp, final NullValue nullMode, Collection<?> values, final SQLType type) {
|
- |
|
240 |
final boolean addNull;
|
- |
|
241 |
if (nullMode != NullValue.IS_UNKNOWN && values.contains(null)) {
|
- |
|
242 |
if (nullMode == NullValue.IS_FORBIDDEN)
|
- |
|
243 |
throw new IllegalArgumentException("Values contains a null value : " + values);
|
- |
|
244 |
assert nullMode == NullValue.IS_DATA;
|
- |
|
245 |
addNull = true;
|
- |
|
246 |
} else {
|
- |
|
247 |
addNull = false;
|
- |
|
248 |
}
|
- |
|
249 |
if (addNull) {
|
- |
|
250 |
values = values.stream().filter((i) -> i != null).collect(Collectors.toList());
|
- |
|
251 |
}
|
- |
|
252 |
String res = getInClause(expr, cmp, CollectionUtils.join(values, ",", (input) -> type.toString(input)));
|
- |
|
253 |
if (addNull) {
|
- |
|
254 |
if (cmp == RowComparison.IN) {
|
- |
|
255 |
res = expr + " is null or " + res;
|
- |
|
256 |
} else {
|
- |
|
257 |
res = expr + " is not null and " + res;
|
- |
|
258 |
}
|
- |
|
259 |
}
|
- |
|
260 |
return res;
|
186 |
}
|
261 |
}
|
187 |
|
262 |
|
188 |
private final List<FieldRef> fields;
|
263 |
private final List<FieldRef> fields;
|
189 |
private final String clause;
|
264 |
private final String clause;
|
190 |
|
265 |
|
Line 224... |
Line 299... |
224 |
* Crée une clause "field1 in (values)". Some databases won't accept empty values (impossible
|
299 |
* Crée une clause "field1 in (values)". Some databases won't accept empty values (impossible
|
225 |
* where clause), so we return false.
|
300 |
* where clause), so we return false.
|
226 |
*
|
301 |
*
|
227 |
* @param field1 le champs à tester.
|
302 |
* @param field1 le champs à tester.
|
228 |
* @param values les valeurs.
|
303 |
* @param values les valeurs.
|
- |
|
304 |
* @deprecated use {@link #inValues(FieldRef, Collection)}
|
229 |
*/
|
305 |
*/
|
230 |
public Where(final FieldRef field1, final Collection<?> values) {
|
306 |
public Where(final FieldRef field1, final Collection<?> values) {
|
231 |
this(field1, true, values);
|
307 |
this(field1, true, values);
|
232 |
}
|
308 |
}
|
233 |
|
309 |
|
Line 235... |
Line 311... |
235 |
* Construct a clause like "field1 not in (value, ...)".
|
311 |
* Construct a clause like "field1 not in (value, ...)".
|
236 |
*
|
312 |
*
|
237 |
* @param field1 le champs à tester.
|
313 |
* @param field1 le champs à tester.
|
238 |
* @param in <code>true</code> for "in", <code>false</code> for "not in".
|
314 |
* @param in <code>true</code> for "in", <code>false</code> for "not in".
|
239 |
* @param values les valeurs.
|
315 |
* @param values les valeurs.
|
- |
|
316 |
* @deprecated use {@link #inValues(FieldRef, Collection)} or
|
- |
|
317 |
* {@link #notInValues(FieldRef, Collection)}
|
240 |
*/
|
318 |
*/
|
241 |
public Where(final FieldRef field1, final boolean in, final Collection<?> values) {
|
319 |
public Where(final FieldRef field1, final boolean in, final Collection<?> values) {
|
242 |
if (values.isEmpty()) {
|
320 |
if (values.isEmpty()) {
|
243 |
this.fields = Collections.emptyList();
|
321 |
this.fields = Collections.emptyList();
|
244 |
this.clause = in ? FALSE.getClause() : TRUE.getClause();
|
322 |
this.clause = in ? FALSE.getClause() : TRUE.getClause();
|
245 |
} else {
|
323 |
} else {
|
246 |
this.fields = Collections.singletonList(field1);
|
324 |
this.fields = Collections.singletonList(field1);
|
247 |
this.clause = getInClause(field1, in, CollectionUtils.join(values, ",", new ITransformer<Object, String>() {
|
325 |
this.clause = getCompareValuesClause(field1.getFieldRef(), in ? RowComparison.IN : RowComparison.NOT_IN, values, field1.getField().getType());
|
248 |
@Override
|
- |
|
249 |
public String transformChecked(final Object input) {
|
- |
|
250 |
return field1.getField().getType().toString(input);
|
- |
|
251 |
}
|
- |
|
252 |
}));
|
- |
|
253 |
}
|
326 |
}
|
254 |
}
|
327 |
}
|
255 |
|
328 |
|
256 |
public Where(final FieldRef field1, final boolean in, final SQLSelect subQuery) {
|
329 |
public Where(final FieldRef field1, final boolean in, final SQLSelect subQuery) {
|
257 |
this.fields = Collections.singletonList(field1);
|
330 |
this.fields = Collections.singletonList(field1);
|