Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.jdbc / src / main / java / org / gvsig / fmap / dal / store / jdbc2 / spi / operations / ResultSetForSetProviderOperation.java @ 47779

History | View | Annotate | Download (38.6 KB)

1 45065 jjdelcerro
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24 43020 jjdelcerro
package org.gvsig.fmap.dal.store.jdbc2.spi.operations;
25
26
import java.util.ArrayList;
27 45712 omartinez
import java.util.HashMap;
28 43020 jjdelcerro
import java.util.List;
29 45166 omartinez
import java.util.Map;
30 43358 jjdelcerro
import org.apache.commons.lang3.ArrayUtils;
31 43020 jjdelcerro
import org.apache.commons.lang3.StringUtils;
32 45155 omartinez
import org.gvsig.expressionevaluator.Code;
33 45162 omartinez
import org.gvsig.expressionevaluator.Expression;
34 44198 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionBuilder;
35 46104 omartinez
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_LET;
36 46507 jjdelcerro
import static org.gvsig.expressionevaluator.ExpressionBuilder.VALUE_NULL;
37 46517 fdiaz
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
38 44727 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionUtils;
39 46505 fdiaz
import org.gvsig.expressionevaluator.GeometryExpressionBuilder;
40 46517 fdiaz
import org.gvsig.expressionevaluator.SymbolTable;
41 46401 jjdelcerro
import org.gvsig.fmap.dal.SQLBuilder;
42 46505 fdiaz
import org.gvsig.fmap.dal.SQLBuilder.Column;
43 46401 jjdelcerro
import org.gvsig.fmap.dal.SQLBuilder.OrderByBuilder;
44 46518 fdiaz
import static org.gvsig.fmap.dal.SQLBuilder.PROP_ADD_TABLE_NAME_TO_COLUMNS;
45
import static org.gvsig.fmap.dal.SQLBuilder.PROP_FEATURE_TYPE;
46
import static org.gvsig.fmap.dal.SQLBuilder.PROP_JDBCHELPER;
47
import static org.gvsig.fmap.dal.SQLBuilder.PROP_QUERY;
48
import static org.gvsig.fmap.dal.SQLBuilder.PROP_SYMBOLTABLE;
49
import static org.gvsig.fmap.dal.SQLBuilder.PROP_TABLE;
50
import static org.gvsig.fmap.dal.SQLBuilder.PROP_TABLENAME;
51 44682 jjdelcerro
import org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
52 46518 fdiaz
import org.gvsig.fmap.dal.SQLBuilder.TableNameBuilder;
53 43020 jjdelcerro
import org.gvsig.fmap.dal.exception.DataException;
54 45162 omartinez
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
55
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
56 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
57 45999 omartinez
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
58 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureQuery;
59
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
60 43026 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureQueryOrder.FeatureQueryOrderMember;
61 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureType;
62 46315 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
63 43020 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
64 46324 jjdelcerro
import static org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer.QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER;
65 44058 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
66 43020 jjdelcerro
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
67
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
68
import org.gvsig.fmap.geom.DataTypes;
69 46324 jjdelcerro
import org.gvsig.tools.dataTypes.DataTypeUtils;
70 45999 omartinez
import org.gvsig.tools.dynobject.DynField;
71 43020 jjdelcerro
import org.gvsig.tools.evaluator.Evaluator;
72 46507 jjdelcerro
import org.gvsig.tools.util.ChainedIterable;
73 43020 jjdelcerro
74
public class ResultSetForSetProviderOperation extends AbstractConnectionOperation {
75 45709 omartinez
76 44058 jjdelcerro
    private final TableReference table;
77 43020 jjdelcerro
    private final String baseFilter;
78
    private final String baseOrder;
79
    private final FeatureType storeType;
80
    private final FeatureType setType;
81
    private final FeatureQuery query;
82
    private final long limit;
83
    private final long offset;
84
    private final int fetchSize;
85
86
    public ResultSetForSetProviderOperation(
87
            JDBCHelper helper,
88 44058 jjdelcerro
            TableReference table,
89 43020 jjdelcerro
            String baseFilter,
90
            String baseOrder,
91
            FeatureQuery query,
92
            FeatureType storeType,
93
            FeatureType setType,
94
            long limit,
95
            long offset,
96
            int fetchSize
97 45709 omartinez
    ) {
98 43020 jjdelcerro
        super(helper);
99
        this.table = table;
100
        this.baseFilter = baseFilter;
101
        this.baseOrder = baseOrder;
102
        this.storeType = storeType;
103
        this.setType = setType;
104
        this.query = query;
105
        this.limit = limit;
106
        this.offset = offset;
107 45709 omartinez
        this.fetchSize = fetchSize;
108 43020 jjdelcerro
    }
109
110
    @Override
111 43377 jjdelcerro
    protected Object perform_operation() throws Exception {
112 44678 jjdelcerro
        ResultSetEntry rs = createResultSet();
113 43020 jjdelcerro
        return rs;
114
    }
115 45709 omartinez
116 43377 jjdelcerro
    @Override
117 46315 jjdelcerro
    public Object perform(JDBCConnection conn) throws DataException {
118 45709 omartinez
        throw new UnsupportedOperationException("Not supported yet.");
119 43377 jjdelcerro
    }
120 44678 jjdelcerro
121
    public String getSQL() {
122 43358 jjdelcerro
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
123 43020 jjdelcerro
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
124 44682 jjdelcerro
        String sql = this.getSQL(sqlbuilder, columns, null);
125 44678 jjdelcerro
        return sql;
126
    }
127 45709 omartinez
128 44682 jjdelcerro
    public String getSQL(
129 45709 omartinez
            JDBCSQLBuilderBase sqlbuilder,
130
            List<FeatureAttributeDescriptor> columns,
131 44682 jjdelcerro
            List<String> extraColumnNames
132 45709 omartinez
    ) {
133 46050 omartinez
        double tolerance = -1; //query.getScale();
134 44198 jjdelcerro
        ExpressionBuilder expbuilder = sqlbuilder.expression();
135 46517 fdiaz
136
        expbuilder.setProperty(PROP_FEATURE_TYPE, this.storeType);
137
        expbuilder.setProperty(PROP_TABLE, table);
138 46518 fdiaz
        expbuilder.setProperty(PROP_TABLENAME, table.getTable());
139 46517 fdiaz
        expbuilder.setProperty(PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable());
140
        expbuilder.setProperty(PROP_JDBCHELPER, this.helper);
141
        expbuilder.setProperty(PROP_QUERY, this.query);
142
143 44682 jjdelcerro
        SelectBuilder select = sqlbuilder.select();
144 46505 fdiaz
        select.from().table()
145
                .database(this.table.getDatabase())
146
                .schema(this.table.getSchema())
147
                .name(this.table.getTable());
148
        select.from().subquery(this.table.getSubquery());
149
150
151
        List<ExpressionBuilder.Value> valuesToRemoveFeatureType = new ArrayList<>();
152 46163 jjdelcerro
153
        if( shouldUseACustomSelect() ) {
154 46505 fdiaz
            return processCustomSelect(select, expbuilder, sqlbuilder, valuesToRemoveFeatureType, columns);
155
        }
156
157
158
        Map<String, EditableFeatureAttributeDescriptor> allExtraColumns = getAllExtraColumns(this.setType, this.query);
159
160
        List<String> primaryKeys = new ArrayList<>();
161
        for (FeatureAttributeDescriptor attr : storeType.getPrimaryKey()) {
162
            primaryKeys.add(attr.getName());
163
        }
164
165
        //Don't change order of this processes
166
        process1_SimpleFields(helper, setType, query, sqlbuilder, select, columns, primaryKeys, extraColumnNames);
167
        process2_ComputedFields(helper, setType, query, sqlbuilder, select, extraColumnNames);
168
        process3_Where(helper, storeType, query, sqlbuilder, select);
169
        process4_Aggregates(this.table, this.setType, this.query, allExtraColumns, sqlbuilder, select, extraColumnNames);
170
        process5_GroupBys(this.table, this.setType, this.query, allExtraColumns, sqlbuilder, select, extraColumnNames);
171
        process6_OrderBy(storeType, setType, query, allExtraColumns, sqlbuilder, select, primaryKeys, extraColumnNames, valuesToRemoveFeatureType);
172
173
        if (!StringUtils.isEmpty(baseFilter)) {
174
            select.where().and(expbuilder.toValue(baseFilter));
175
        }
176
177
        if (!StringUtils.isEmpty(baseOrder)) {
178
            select.order_by().custom(baseOrder);
179
        }
180
181
182
        if (limit > 0) {
183
            select.limit(limit);
184
        } else {
185
            select.limit(query == null ? null : query.getLimit());
186
        }
187
        if (offset > 0) {
188
            select.offset(offset);
189
        }
190
        sqlbuilder.setProperties(
191
                null,
192
                PROP_FEATURE_TYPE, this.storeType,
193
                PROP_TABLE, table,
194
                PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
195
                PROP_JDBCHELPER, this.helper,
196
                PROP_QUERY, this.query
197
        );
198
        for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
199
            value.setProperty(PROP_FEATURE_TYPE, null);
200
        }
201
        this.helper.expandCalculedColumns(sqlbuilder);
202
        this.helper.processSpecialFunctions(sqlbuilder, storeType, extraColumnNames);
203 46511 jjdelcerro
        String sql = StringUtils.trim(sqlbuilder.toString());
204 46505 fdiaz
        LOGGER.debug(sql);
205
        return sql;
206
    }
207
208
    private String processCustomSelect(SelectBuilder select, ExpressionBuilder expbuilder, JDBCSQLBuilderBase sqlbuilder, List<ExpressionBuilder.Value> valuesToRemoveFeatureType, List<FeatureAttributeDescriptor> columns) {
209
        String sql = table.getSubquery();
210
        if( StringUtils.containsAny(sql, "${where_filter}", "${and_filter}") ) {
211
            Evaluator filter = query.getFilter();
212
            if (filter != null) {
213
                String sqlfilter = filter.getSQL();
214
                if (!StringUtils.isEmpty(sqlfilter)) {
215
                    if (this.helper.supportFilter(this.storeType, filter)) {
216
                        select.where().set(expbuilder.toValue(sqlfilter));
217 46163 jjdelcerro
                    }
218
                }
219
            }
220 46505 fdiaz
            if (!StringUtils.isEmpty(baseFilter)) {
221
                select.where().and(expbuilder.toValue(baseFilter));
222 46163 jjdelcerro
            }
223 46505 fdiaz
        }
224
        if( StringUtils.containsAny(sql, "${order_by_orderspec}", "${comma_orderspec}") ) {
225
            FeatureQueryOrder order = query.getOrder();
226
            if (order != null) {
227
                for (FeatureQueryOrderMember member : order.members()) {
228
                    String attrName = member.getAttributeName();
229
                    ExpressionBuilder.Variable col = expbuilder.column(attrName);
230
                    select.order_by().value(col).ascending(member.getAscending());
231 46163 jjdelcerro
                }
232
            }
233 46505 fdiaz
        }
234
        if( select.has_where() || select.has_order_by() ) {
235
            sqlbuilder.setProperties(
236
                    null,
237
                    PROP_FEATURE_TYPE, this.storeType,
238
                    PROP_TABLE, table,
239
                    PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
240
                    PROP_JDBCHELPER, this.helper,
241
                    PROP_QUERY, this.query
242
            );
243
            for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
244
                value.setProperty(PROP_FEATURE_TYPE, null);
245 46163 jjdelcerro
            }
246 46505 fdiaz
            if( select.has_where()) {
247
                String s = select.where().toString();
248
                sql = StringUtils.replace(sql, "${where_filter}", "WHERE " + s);
249
                sql = StringUtils.replace(sql, "${and_filter}", "AND (" + s + ")");
250
            }
251
            if( select.has_order_by() ) {
252
                String s = select.order_by().toString();
253
                sql = StringUtils.replace(sql, "${order_by_orderspec}", "ORDER BY " + s);
254
                sql = StringUtils.replace(sql, "${comma_orderspec}", ", "+ s);
255
            }
256 46163 jjdelcerro
        }
257 46505 fdiaz
        for (FeatureAttributeDescriptor attr : storeType) {
258
            columns.add(attr);
259 45712 omartinez
        }
260 46505 fdiaz
        return sql;
261
    }
262
263
    public static void process1_SimpleFields(
264
            JDBCHelper helper,
265
            FeatureType setType,
266
            FeatureQuery query,
267
            SQLBuilder sqlbuilder,
268
            SelectBuilder select,
269
            List<FeatureAttributeDescriptor> columns,
270
            List<String> primaryKeys,
271
            List<String> extraColumnNames
272
        ){
273
        if(!select.from().table().has_name()){
274
            throw new IllegalStateException("Need table in FROM.");
275 45712 omartinez
        }
276 46505 fdiaz
277
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
278
279 43706 jjdelcerro
        List<String> forcedColumns = new ArrayList<>(primaryKeys);
280
281 43358 jjdelcerro
        String[] constantsAttributeNames = null;
282 45709 omartinez
        if (query != null && query.hasConstantsAttributeNames()) {
283 43358 jjdelcerro
            constantsAttributeNames = query.getConstantsAttributeNames();
284
        }
285 46505 fdiaz
286 45709 omartinez
        for (FeatureAttributeDescriptor attr : setType) {
287
            if (attr.isComputed()) {
288 46221 omartinez
                // Campos calculados se a?aden despues para asegurarnos que
289
                // primeros se a?aden los campos reales y al final los calculados
290
                continue;
291
            }
292
            if (attr.isPrimaryKey()) {
293
                forcedColumns.remove(attr.getName());
294
            }
295 46518 fdiaz
            if (ArrayUtils.contains(constantsAttributeNames, attr.getName()) && !attr.isPrimaryKey()) {
296
                select.column().name(attr.getName()).value(VALUE_NULL).as(attr.getName());
297
            } else  if (attr.getType() == DataTypes.GEOMETRY) {
298 46507 jjdelcerro
                select.column().name(attr.getName()).as_geometry();
299
            } else {
300
                select.column().name(attr.getName());
301
            }
302 46221 omartinez
            columns.add(attr);
303
        }
304 46505 fdiaz
305 46221 omartinez
        if (query == null || !query.hasGroupByColumns()) {
306
            for (String attrName : forcedColumns) {
307
                select.column().name(attrName);
308
                FeatureAttributeDescriptor attr = setType.getAttributeDescriptor(attrName);
309
                // Ojo, que cuando estamos pintando una vista NO TENEMOS LA PK y attr vale null!!!!
310
                // No se si deberiamos forzar a que cuando se crea un subfeaturetype se meta siempre
311
                // la pk, o simplemente nunca asumir que la vamos a tener.
312
                if (attr != null) {
313
                    columns.add(attr);
314
                }
315
            }
316
        }
317 46505 fdiaz
    }
318
319
    public static void process2_ComputedFields(JDBCHelper helper, FeatureType setType, FeatureQuery query, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames){
320
        if(!select.from().table().has_name()){
321
            throw new IllegalStateException("Need table in FROM.");
322
        }
323
//        En las columnas del select, si hay funciones de agregado en el select o un GROUP BY
324
//        los campos calculados que no lleven funci?n de agregado no deben incluir el nombre de tabla
325
326 46507 jjdelcerro
        for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType, query) ) {
327 46221 omartinez
            if (attr.isComputed()) {
328 47485 fdiaz
                FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
329
                if( !(attrEmulator instanceof FeatureAttributeEmulatorExpression) ) {
330
                    continue;
331
                }
332
                FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
333
                Expression exp = x.getExpression();
334
                if (exp == null || exp.isEmpty() ) {
335
                    continue;
336
                }
337
                if (!helper.supportExpression(null, exp.getPhrase())) {
338
                    continue;
339
                }
340
341 46518 fdiaz
                ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
342 46507 jjdelcerro
                if( value != VALUE_NULL ) {
343
                    select.column()
344
                            .name(attr.getName())
345
                            .value(value)
346
                            .as(attr.getName());
347
                    if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
348
                        extraColumnNames.add(attr.getName());
349 46505 fdiaz
                    }
350 46507 jjdelcerro
//                    if(query != null && (query.hasGroupByColumns() || query.hasAggregateFunctions())){
351
                        addTableNameToColumns(sqlbuilder, value, true);
352
//                    }
353 46505 fdiaz
                }
354
            }
355
        }
356 46507 jjdelcerro
357 46505 fdiaz
    }
358 46104 omartinez
359 46505 fdiaz
    public static void process4_Aggregates(TableReference table, FeatureType setType, FeatureQuery query, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames) throws RuntimeException {
360
        if(query == null){
361
            return;
362
        }
363
        if(!select.from().table().has_name()){
364
            throw new IllegalStateException("Need table in FROM.");
365
        }
366
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
367
        if (query.hasAggregateFunctions() ) {
368 46507 jjdelcerro
            // Ponemos NULL AS... en las columnas que no tengan funcion de argregado y no esten en el group by.
369
            for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType,query) )  {
370
                final String name = attr.getName();
371
                if( select.has_column(name) ) {
372
//                    if( attr.isComputed() ) {
373
//                        if (!query.isAggregate(name) ) {
374
//                            select.column(name).value(VALUE_NULL).as(name);
375
//                        }
376
//                    } else {
377
                        if ( !query.isAGroupByColumn(name) && !query.isAggregate(name) ) {
378
                            select.column(name).value(VALUE_NULL).as(name);
379
                        }
380
//                    }
381 45709 omartinez
                }
382 45166 omartinez
            }
383 46505 fdiaz
384
            for (Map.Entry<String, String> entry : query.getAggregateFunctions().entrySet()) {
385 46507 jjdelcerro
                String aggregateFunction = entry.getValue();
386
                String aggregateExpression = entry.getKey();
387
                FeatureAttributeDescriptor attr = getAttributeDescriptor(aggregateExpression, setType, allExtraColumns);
388 46505 fdiaz
                if (attr == null) {
389
                    //Aggregate_expression AS name -> name:=aggregate_expression
390 46507 jjdelcerro
                    Expression exp = ExpressionUtils.createExpression(aggregateExpression);
391 46505 fdiaz
                    Code code = exp.getCode();
392
                    if (!(code instanceof Code.Callable)) {
393 46507 jjdelcerro
                        throw new RuntimeException("Not able to use aggregate function with this expression(1): " + aggregateExpression);
394 46505 fdiaz
                    }
395
                    Code.Callable callable = (Code.Callable) code;
396
                    if (!callable.name().equalsIgnoreCase(FUNCTION_LET)) {
397 46507 jjdelcerro
                        throw new RuntimeException("Not able to use aggregate function with this expression(2): " + aggregateExpression);
398 46505 fdiaz
                    }
399
                    String name = ((Code.Identifier) callable.parameters().get(0)).name();
400 46507 jjdelcerro
                    ExpressionBuilder.Value value = callable.parameters().get(1).toValue();
401 46505 fdiaz
402 46507 jjdelcerro
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(aggregateFunction, value);
403 46505 fdiaz
                    select.column(name).value(aggregateExp).as(name);
404
                } else {
405
                    SQLBuilder.SelectColumnBuilder column = select.column(attr.getName());
406 46518 fdiaz
                    ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
407 46507 jjdelcerro
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(aggregateFunction, value);
408
                    addTableNameToColumns(sqlbuilder, aggregateExp, true);
409
                    column.value(aggregateExp).as(attr.getName());
410 45155 omartinez
                }
411 45709 omartinez
            }
412 46505 fdiaz
        }
413
    }
414 46507 jjdelcerro
415 46505 fdiaz
    public static void process5_GroupBys(TableReference table, FeatureType setType, FeatureQuery query, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames) throws RuntimeException {
416
        if(query == null){
417
            return;
418
        }
419
        if(!select.from().table().has_name()){
420
            throw new IllegalStateException("Need table in FROM.");
421
        }
422
        if (query.hasGroupByColumns()) { // if tiene agrupacion
423
            GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
424 46507 jjdelcerro
            for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType,query.getExtraColumns()) ) {
425
                final String name = attr.getName();
426
                if( select.has_column(name) ) {
427
//                    if( attr.isComputed() ) {
428
//                        if (!query.isAggregate(name) ) {
429
//                            select.column(name).value(VALUE_NULL).as(name);
430
//                        }
431
//                    } else {
432
                        if ( !query.isAGroupByColumn(name) && !query.isAggregate(name) ) {
433
                            select.column(name).value(VALUE_NULL).as(name);
434
                        }
435
//                    }
436 46505 fdiaz
                }
437
            }
438
439
            for (String attrName : query.getGroupByColumns()) {
440 46507 jjdelcerro
                FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName,setType, allExtraColumns);
441
                if ( attr == null ) {
442 46505 fdiaz
                    try {
443
                        Code code = ExpressionUtils.compile(attrName);
444
                        select.group_by(code.toValue());
445
                    } catch (Exception ex) {
446
                        throw new RuntimeException("Not able to create column by expression in groupby query", ex);
447 45999 omartinez
                    }
448 46505 fdiaz
                } else {
449 46518 fdiaz
                    ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
450 46507 jjdelcerro
                    if( value instanceof Column ) {
451 46518 fdiaz
//                        ((Column)value).table(select.from().table());
452 46507 jjdelcerro
                        if( select.getColumns().isEmpty() ) {
453
                            // Tratamos de asegurarnos de que al menos hay una columna en el select
454
                            select.column(attr.getName());
455
                        }
456
                    }
457
                    addTableNameToColumns(sqlbuilder, value, true);
458
                    select.group_by(value);
459 45999 omartinez
                }
460 46014 jjdelcerro
            }
461 43020 jjdelcerro
        }
462 46505 fdiaz
    }
463
464
    public static void process3_Where(
465
            JDBCHelper helper,
466
            FeatureType storeType,
467
            FeatureQuery query,
468
            SQLBuilder sqlbuilder,
469
            SelectBuilder select
470
        ) {
471
        if(query == null){
472
            return;
473
        }
474
        if(!select.from().table().has_name()){
475
            throw new IllegalStateException("Need table in FROM.");
476
        }
477
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
478
479
        Evaluator filter = query.getFilter();
480 45709 omartinez
        if (filter != null) {
481 43020 jjdelcerro
            String sqlfilter = filter.getSQL();
482 45709 omartinez
            if (!StringUtils.isEmpty(sqlfilter)) {
483 46505 fdiaz
                if (helper.supportFilter(storeType, filter)) {
484 46507 jjdelcerro
                    ExpressionBuilder.Value value = expbuilder.toValue(sqlfilter);
485
                    addTableNameToColumns(sqlbuilder, value, true);
486
                    select.where().set(value);
487 44198 jjdelcerro
                }
488 43020 jjdelcerro
            }
489
        }
490 46505 fdiaz
    }
491
492
    public static void process6_OrderBy(
493
            FeatureType storeType,
494
            FeatureType setType,
495
            FeatureQuery query,
496
            Map<String, EditableFeatureAttributeDescriptor> allExtraColumns,
497
            SQLBuilder sqlbuilder,
498
            SelectBuilder select,
499
            List<String> primaryKeys,
500
            List<String> extraColumnNames,
501
            List<ExpressionBuilder.Value> valuesToRemoveFeatureType
502
        ) {
503 46507 jjdelcerro
        //
504
        // https://learnsql.com/cookbook/how-to-order-rows-by-group-sum-in-sql/
505
        //
506
507 46505 fdiaz
        if(!select.from().table().has_name()){
508
            throw new IllegalStateException("Need table in FROM.");
509 43020 jjdelcerro
        }
510 46505 fdiaz
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
511
512
        if (query != null) {
513
            FeatureQueryOrder order = query.getOrder();
514
            if (order != null) {
515 46507 jjdelcerro
                if (query.hasAggregateFunctions() || query.hasGroupByColumns()) {
516
                    for (FeatureQueryOrderMember member : order.members()) {
517 46525 fdiaz
                        if(member == null){
518
                            continue;
519
                        }
520 46507 jjdelcerro
                        String attrName = member.getAttributeName();
521
                        FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName, setType, allExtraColumns);
522
                        if( attr == null ) {
523 46525 fdiaz
                            if(member.getEvaluator() == null){
524
                                select.order_by()
525
                                        .column(attrName)
526
                                        .ascending(member.getAscending());
527
                            } else {
528
                                // Order-by una expression en el query
529
                                String sqlorder = member.getEvaluator().getSQL();
530
                                select.order_by()
531
                                        .value(expbuilder.toValue(sqlorder))
532
                                        .ascending(member.getAscending());
533
                            }
534 46507 jjdelcerro
                        } else {
535
                            ExpressionBuilder.Value value;
536
                            if ( query.isAGroupByColumn(attrName) ) {
537 46518 fdiaz
                                value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
538
//                                if( value instanceof Column ) {
539
//                                    ((Column)value).table(select.from().table());
540
//                                }
541 46507 jjdelcerro
                            } else if ( query.isAggregate(attrName) ) {
542
                                String fn = query.getAggregateFunctions().get(attr.getName());
543 46518 fdiaz
                                value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
544
//                                if( value instanceof Column ) {
545
//                                    ((Column)value).table(select.from().table());
546
//                                }
547 46507 jjdelcerro
                                value = expbuilder.function(fn, value);
548
                            } else {
549 46517 fdiaz
                                continue;
550
//                                throw new RuntimeException("Can't order by column '" + attrName + "', it does not have an aggregate function nor is it grouping by it.");
551 46505 fdiaz
                            }
552 46507 jjdelcerro
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
553
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
554 46505 fdiaz
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
555
                                // si el campo no admite nulos pasamos de indicarselo.
556
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
557
                            }
558 46507 jjdelcerro
559
                            boolean expand;
560
                            if ( !query.isAggregate(attrName) ) {
561
                                if (select.has_aggregate_functions()) {
562
                                    expand = true;
563
                                } else {
564
                                    expand = false;
565
                                }
566 46505 fdiaz
                            } else {
567
                                expand = false;
568
                            }
569 46507 jjdelcerro
                            if (select.isGroupBy(attrName)) {
570
                                expand = false;
571
                            }
572
573
                            expandColumns(value, expand, valuesToRemoveFeatureType);
574
                            addTableNameToColumns(sqlbuilder, value, !attr.isComputed());
575
                            select.order_by().value(value).ascending(member.getAscending()).nulls(nullsMode);
576
                        }
577
                    }
578
579
                } else {
580
                    for (FeatureQueryOrderMember member : order.members()) {
581 46525 fdiaz
                        if(member == null){
582
                            continue;
583
                        }
584 46507 jjdelcerro
                        String attrName = member.getAttributeName();
585
                        FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName, setType, allExtraColumns);
586
                        if( attr == null ) {
587 46525 fdiaz
                            if(member.getEvaluator() == null){
588
                                select.order_by()
589
                                        .column(attrName)
590
                                        .ascending(member.getAscending());
591
                            } else {
592
                                // Order-by una expression en el query
593
                                String sqlorder = member.getEvaluator().getSQL();
594
                                select.order_by()
595
                                        .value(expbuilder.toValue(sqlorder))
596
                                        .ascending(member.getAscending());
597
                            }
598 46505 fdiaz
                        } else {
599 46507 jjdelcerro
                            Column column = sqlbuilder.column(attr.getName());
600
                            if( !attr.isComputed() ) {
601
                                column.table(select.from().table());
602
                            }
603
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
604
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
605
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
606
                                // si el campo no admite nulos pasamos de indicarselo.
607
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
608
                            }
609
                            expandColumns(column, false, valuesToRemoveFeatureType);
610
                            addTableNameToColumns(sqlbuilder, column, !attr.isComputed());
611
                            select.order_by().value(column).ascending(member.getAscending()).nulls(nullsMode);
612 45712 omartinez
                        }
613 43020 jjdelcerro
                    }
614
                }
615
            }
616
        }
617 46507 jjdelcerro
        if( query!=null && !query.hasOrder() ) {
618 46505 fdiaz
            // Si no hay orden y hay grupos ordenaremos por estos
619 46507 jjdelcerro
            if( query.hasGroupByColumns() ) { // && isPaginated()) {
620 46505 fdiaz
                // Cuando paginamos debemos ordenar por las columnas del groupby.
621
                // Ordenamos siempre para obtener el mismo resultado cuando paginamos
622
                // y no paginamos.
623 46507 jjdelcerro
                for (String attrName : query.getGroupByColumns()) {
624
                    FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName,setType, allExtraColumns);
625
                    if ( attr != null ) {
626
                        if( query.getOrder().get(attrName)==null ) {
627
                            ExpressionBuilder.Value value;
628 46518 fdiaz
                            value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
629
//                            if( value instanceof Column ) {
630
//                                ((Column)value).table(select.from().table());
631
//                            }
632 46507 jjdelcerro
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
633
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
634
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
635
                                // si el campo no admite nulos pasamos de indicarselo.
636
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
637
                            }
638
                            expandColumns(value, true, valuesToRemoveFeatureType);
639
                            addTableNameToColumns(sqlbuilder, value, true);
640
                            select.order_by().value(value).ascending(true).nulls(nullsMode);
641 46505 fdiaz
                        }
642
                    }
643 45709 omartinez
                }
644
            }
645 46105 omartinez
        }
646 46505 fdiaz
647 46105 omartinez
        if (primaryKeys.isEmpty()) {
648
            // Muy probablemente si no tiene pk sea una vista, asi que
649
            // pasaremos de ordenar y esperemos que la vista este ya ordenada.
650
            select.disable_check_order_and_offset();
651
        } else {
652 46505 fdiaz
            if (query == null || !(query.hasAggregateFunctions() || query.hasGroupByColumns())) {
653 46465 fdiaz
                // Siempre ordenamos por la clave primaria
654 46505 fdiaz
                Object x = query == null ? null : query.getQueryParameter(QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER);
655
                if (x == null || !DataTypeUtils.toBoolean(x)) {
656 46465 fdiaz
                    for (String attrName : primaryKeys) {
657
                        if (select.getOrderBy(attrName) == null) {
658 46505 fdiaz
                            ExpressionBuilder.Variable col = sqlbuilder.column(select.from().table(),attrName);
659 46465 fdiaz
                            select.order_by()
660 46505 fdiaz
                                    .value(col) //column(attrName)
661 46465 fdiaz
                                    .ascending()
662
                                    .nulls(OrderByBuilder.MODE_NULLS_NOT_SPECIFIED)
663
                                    ;
664
                            // No tengo claro que pasa si se esta agrupando, y no se ha
665
                            // incluido en el agrupamiento al PK. En ese caso el select
666
                            // que se genera tendria una pinta tal que asi:
667
                            //   SELECT NULL as pk, ... ORDER BY pk ASC
668
                            // Probablemente en ese caso no tendriamos que meter la PK
669
                            // en el order-by ya que creo que no cambiaria el resultado.
670
                        }
671 46324 jjdelcerro
                    }
672 46105 omartinez
                }
673 45989 fdiaz
            }
674
        }
675 46105 omartinez
676 44678 jjdelcerro
    }
677 46507 jjdelcerro
678 46105 omartinez
    private boolean isPaginated() {
679
        // No tengo claro que (offset==0 && limit>0) sea lo mas correcto,
680
        // Pero cuando se va a paginar y se pide la primera pagina offset es
681
        // 0 y limit>0, y si no ordenamos ya esa primera pagina los resultados
682
        // que se obtienen no son correctos, ya que la primera pagina se saca
683
        // sin ordenar y el resto ordenadas.
684
        // Probablemente deberiamos tener alguna otra forma de detectar que
685
        // estamos paginanado ya que asi no distinguimo si solo queremos
686
        // obtener los primeros elementos sin importarnos su orden.
687
        return (offset > 0 || (offset == 0 && limit > 0));
688
    }
689 45709 omartinez
690 44678 jjdelcerro
    public ResultSetEntry createResultSet() throws DataException {
691
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
692 44682 jjdelcerro
        List<String> extraColumnNames = new ArrayList<>();
693
694 44678 jjdelcerro
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
695 44682 jjdelcerro
        String sql = this.getSQL(sqlbuilder, columns, extraColumnNames);
696 45709 omartinez
697 43020 jjdelcerro
        ResultSetEntry resultSetEntry = this.helper.getResulSetControler().create(
698 45709 omartinez
                sql, fetchSize,
699 44376 jjdelcerro
                columns.toArray(new FeatureAttributeDescriptor[columns.size()]),
700 44682 jjdelcerro
                extraColumnNames.toArray(new String[extraColumnNames.size()])
701 43020 jjdelcerro
        );
702
        return resultSetEntry;
703
    }
704 45709 omartinez
705 46163 jjdelcerro
    private boolean shouldUseACustomSelect() {
706
        if( !table.hasSubquery() ) {
707
            return false;
708
        }
709
        if( this.query == null ) {
710
            return false;
711
        }
712
        if( this.query.isUseSubquery() ) {
713
            return false;
714
        }
715
        if( this.query.hasGroupByColumns() ) {
716
            return false;
717
        }
718
        if( this.query.hasAggregateFunctions() ) {
719
            return false;
720
        }
721
        if( !this.query.hasFilter() && !this.query.hasOrder() ) {
722
            return true;
723
        }
724
//        si el filtro es incompatible con la BBDD
725
//            return false
726
//        si el orden es incompatible con la BBDD
727
//            return false
728
//        si filtro u orden usan la funcion foreing_value
729
//            return false;
730
        return true;
731
    }
732 46507 jjdelcerro
733
    public static Map<String, EditableFeatureAttributeDescriptor> getAllExtraColumns(FeatureType setType, FeatureQuery query) {
734
        Map<String, EditableFeatureAttributeDescriptor> allExtraColumns = new HashMap<>();
735
        for (EditableFeatureAttributeDescriptor column : setType.getExtraColumns().getColumns()) {
736
            allExtraColumns.put(column.getName(), column);
737
        }
738
        if(query != null){
739
            for (EditableFeatureAttributeDescriptor column : query.getExtraColumn().getColumns()) {
740
                allExtraColumns.put(column.getName(), column);
741
            }
742
        }
743
        return allExtraColumns;
744
    }
745
746
    private static Iterable<FeatureAttributeDescriptor> attributeDescriptorsIterable(Iterable... iterables) {
747
        return new ChainedIterable<>(iterables);
748
    }
749
750
    private static Iterable<FeatureAttributeDescriptor> attributeDescriptorsIterable(FeatureType setType, FeatureQuery query) {
751
        if( query == null ) {
752
            return new ChainedIterable<>(setType);
753
        }
754
        return attributeDescriptorsIterable(setType, query.getExtraColumns());
755
    }
756
757
    private static boolean existsAttributeDescriptor(String attrName, FeatureType setType, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns)  {
758
        return getAttributeDescriptor(attrName, setType, allExtraColumns)!=null;
759
    }
760
761
    private static FeatureAttributeDescriptor getAttributeDescriptor(String attrName, FeatureType setType, Map<String, EditableFeatureAttributeDescriptor> extraColumns) {
762
        FeatureAttributeDescriptor attr = null;
763
        if( setType!=null ) {
764
            attr = setType.getAttributeDescriptorFromAll(attrName);
765
        }
766
        if(attr == null && extraColumns!=null){
767
            attr = extraColumns.get(attrName);
768
        }
769
        return attr;
770
    }
771
772
    private static void expandColumns(ExpressionBuilder.Value value, boolean expand, List<ExpressionBuilder.Value> valuesToRemoveFeatureType) {
773
        if(!expand){
774
            // En el OrderBy no queremos que se sustituya el nombre del campo calculado
775
            // por su expresion. Se encarga el formater y lo evitamos quitandole el ftype
776
            // al value.
777
            valuesToRemoveFeatureType.add(value);
778
        }
779
    }
780
    private static void addTableNameToColumns(SQLBuilder sqlbuilder, ExpressionBuilder.Value expression, boolean add) {
781 46518 fdiaz
        sqlbuilder.setProperties(expression, null, PROP_ADD_TABLE_NAME_TO_COLUMNS, add);
782 46507 jjdelcerro
    }
783
784
785 46518 fdiaz
    private static ExpressionBuilder.Value createValueFromAttribute(SQLBuilder sqlbuilder, FeatureAttributeDescriptor attr, TableNameBuilder tableBuilder) {
786 46517 fdiaz
        switch(attr.getRelationType()) {
787
            case DynField.RELATION_TYPE_AGGREGATE:
788
            case DynField.RELATION_TYPE_COMPOSITION:
789
                return VALUE_NULL;
790
            case DynField.RELATION_TYPE_NONE:
791
            case DynField.RELATION_TYPE_IDENTITY:
792
            case DynField.RELATION_TYPE_COLLABORATION:
793
                break;
794 46507 jjdelcerro
        }
795 46517 fdiaz
796 46507 jjdelcerro
        if( attr.isComputed() ) {
797
            FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
798
            if( !(attrEmulator instanceof FeatureAttributeEmulatorExpression) ) {
799
                return VALUE_NULL;
800
            }
801
            FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
802
            Expression exp = x.getExpression();
803
            if (exp == null || exp.isEmpty() ) {
804
                return VALUE_NULL;
805
            }
806
            Code code = exp.getCode();
807 46517 fdiaz
808
            SymbolTable symbolTable = ExpressionEvaluatorLocator.getExpressionEvaluatorManager().getInmutableSymbolTable();
809
810
            code.link(symbolTable);
811 46507 jjdelcerro
            ExpressionBuilder.Value value = code.toValue(sqlbuilder.expression());
812
            return value;
813
        }
814
        // Es un campo de la tabla
815 46518 fdiaz
        Column column = sqlbuilder.column(tableBuilder, attr.getName());
816 46507 jjdelcerro
        return column;
817
    }
818
819 43020 jjdelcerro
}