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 @ 47787

History | View | Annotate | Download (38.6 KB)

1
/**
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
package org.gvsig.fmap.dal.store.jdbc2.spi.operations;
25

    
26
import java.util.ArrayList;
27
import java.util.HashMap;
28
import java.util.List;
29
import java.util.Map;
30
import org.apache.commons.lang3.ArrayUtils;
31
import org.apache.commons.lang3.StringUtils;
32
import org.gvsig.expressionevaluator.Code;
33
import org.gvsig.expressionevaluator.Expression;
34
import org.gvsig.expressionevaluator.ExpressionBuilder;
35
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_LET;
36
import static org.gvsig.expressionevaluator.ExpressionBuilder.VALUE_NULL;
37
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
38
import org.gvsig.expressionevaluator.ExpressionUtils;
39
import org.gvsig.expressionevaluator.GeometryExpressionBuilder;
40
import org.gvsig.expressionevaluator.SymbolTable;
41
import org.gvsig.fmap.dal.SQLBuilder;
42
import org.gvsig.fmap.dal.SQLBuilder.Column;
43
import org.gvsig.fmap.dal.SQLBuilder.OrderByBuilder;
44
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
import org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
52
import org.gvsig.fmap.dal.SQLBuilder.TableNameBuilder;
53
import org.gvsig.fmap.dal.exception.DataException;
54
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
55
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
56
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
57
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
58
import org.gvsig.fmap.dal.feature.FeatureQuery;
59
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
60
import org.gvsig.fmap.dal.feature.FeatureQueryOrder.FeatureQueryOrderMember;
61
import org.gvsig.fmap.dal.feature.FeatureType;
62
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
63
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
64
import static org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer.QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER;
65
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
66
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
import org.gvsig.tools.dataTypes.DataTypeUtils;
70
import org.gvsig.tools.dynobject.DynField;
71
import org.gvsig.tools.evaluator.Evaluator;
72
import org.gvsig.tools.util.ChainedIterable;
73

    
74
public class ResultSetForSetProviderOperation extends AbstractConnectionOperation {
75

    
76
    private final TableReference table;
77
    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
            TableReference table,
89
            String baseFilter,
90
            String baseOrder,
91
            FeatureQuery query,
92
            FeatureType storeType,
93
            FeatureType setType,
94
            long limit,
95
            long offset,
96
            int fetchSize
97
    ) {
98
        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
        this.fetchSize = fetchSize;
108
    }
109

    
110
    @Override
111
    protected Object perform_operation() throws Exception {
112
        ResultSetEntry rs = createResultSet();
113
        return rs;
114
    }
115

    
116
    @Override
117
    public Object perform(JDBCConnection conn) throws DataException {
118
        throw new UnsupportedOperationException("Not supported yet.");
119
    }
120

    
121
    public String getSQL() {
122
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
123
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
124
        String sql = this.getSQL(sqlbuilder, columns, null);
125
        return sql;
126
    }
127

    
128
    public String getSQL(
129
            JDBCSQLBuilderBase sqlbuilder,
130
            List<FeatureAttributeDescriptor> columns,
131
            List<String> extraColumnNames
132
    ) {
133
        double tolerance = -1; //query.getScale(); 
134
        ExpressionBuilder expbuilder = sqlbuilder.expression();
135
        
136
        expbuilder.setProperty(PROP_FEATURE_TYPE, this.storeType);
137
        expbuilder.setProperty(PROP_TABLE, table);
138
        expbuilder.setProperty(PROP_TABLENAME, table.getTable());
139
        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
        SelectBuilder select = sqlbuilder.select();
144
        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
        
153
        if( shouldUseACustomSelect() ) {
154
            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, query);
203
        String sql = StringUtils.trim(sqlbuilder.toString());
204
        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
                    }
218
                }
219
            }
220
            if (!StringUtils.isEmpty(baseFilter)) {
221
                select.where().and(expbuilder.toValue(baseFilter));
222
            }
223
        }
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
                }
232
            }
233
        }
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
            }
246
            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
        }
257
        for (FeatureAttributeDescriptor attr : storeType) {
258
            columns.add(attr);
259
        }
260
        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
        }
276
        
277
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
278
        
279
        List<String> forcedColumns = new ArrayList<>(primaryKeys);
280

    
281
        String[] constantsAttributeNames = null;
282
        if (query != null && query.hasConstantsAttributeNames()) {
283
            constantsAttributeNames = query.getConstantsAttributeNames();
284
        }
285
        
286
        for (FeatureAttributeDescriptor attr : setType) {
287
            if (attr.isComputed()) {
288
                // 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
            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
                select.column().name(attr.getName()).as_geometry();
299
            } else {
300
                select.column().name(attr.getName());
301
            }
302
            columns.add(attr);
303
        }
304
        
305
        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
    }
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
        for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType, query) ) {
327
            if (attr.isComputed()) {
328
                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
                ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
342
                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
                    }
350
//                    if(query != null && (query.hasGroupByColumns() || query.hasAggregateFunctions())){
351
                        addTableNameToColumns(sqlbuilder, value, true);
352
//                    }
353
                }
354
            }
355
        }
356
                
357
    }
358

    
359
    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
            // 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
                }
382
            }
383
            
384
            for (Map.Entry<String, String> entry : query.getAggregateFunctions().entrySet()) {
385
                String aggregateFunction = entry.getValue();
386
                String aggregateExpression = entry.getKey();
387
                FeatureAttributeDescriptor attr = getAttributeDescriptor(aggregateExpression, setType, allExtraColumns);
388
                if (attr == null) {
389
                    //Aggregate_expression AS name -> name:=aggregate_expression
390
                    Expression exp = ExpressionUtils.createExpression(aggregateExpression);
391
                    Code code = exp.getCode();
392
                    if (!(code instanceof Code.Callable)) {
393
                        throw new RuntimeException("Not able to use aggregate function with this expression(1): " + aggregateExpression);
394
                    }
395
                    Code.Callable callable = (Code.Callable) code;
396
                    if (!callable.name().equalsIgnoreCase(FUNCTION_LET)) {
397
                        throw new RuntimeException("Not able to use aggregate function with this expression(2): " + aggregateExpression);
398
                    }
399
                    String name = ((Code.Identifier) callable.parameters().get(0)).name();
400
                    ExpressionBuilder.Value value = callable.parameters().get(1).toValue();
401
                    
402
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(aggregateFunction, value);                    
403
                    select.column(name).value(aggregateExp).as(name);
404
                } else {
405
                    SQLBuilder.SelectColumnBuilder column = select.column(attr.getName());
406
                    ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
407
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(aggregateFunction, value);
408
                    addTableNameToColumns(sqlbuilder, aggregateExp, true);
409
                    column.value(aggregateExp).as(attr.getName());
410
                }
411
            }
412
        }
413
    }
414

    
415
    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
            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
                }
437
            }
438

    
439
            for (String attrName : query.getGroupByColumns()) {
440
                FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName,setType, allExtraColumns);
441
                if ( attr == null ) {
442
                    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
                    }
448
                } else {
449
                    ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
450
                    if( value instanceof Column ) {
451
//                        ((Column)value).table(select.from().table());
452
                        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
                }
460
            }
461
        }
462
    }
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
        if (filter != null) {
481
            String sqlfilter = filter.getSQL();
482
            if (!StringUtils.isEmpty(sqlfilter)) {
483
                if (helper.supportFilter(storeType, filter)) {
484
                    ExpressionBuilder.Value value = expbuilder.toValue(sqlfilter);
485
                    addTableNameToColumns(sqlbuilder, value, true);
486
                    select.where().set(value);
487
                }
488
            }
489
        }
490
    }
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
        //
504
        // https://learnsql.com/cookbook/how-to-order-rows-by-group-sum-in-sql/
505
        //
506
        
507
        if(!select.from().table().has_name()){
508
            throw new IllegalStateException("Need table in FROM.");
509
        }
510
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
511
        
512
        if (query != null) {
513
            FeatureQueryOrder order = query.getOrder();
514
            if (order != null) {
515
                if (query.hasAggregateFunctions() || query.hasGroupByColumns()) {
516
                    for (FeatureQueryOrderMember member : order.members()) {
517
                        if(member == null){
518
                            continue;
519
                        }
520
                        String attrName = member.getAttributeName();
521
                        FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName, setType, allExtraColumns);                        
522
                        if( attr == null ) {
523
                            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
                        } else {
535
                            ExpressionBuilder.Value value;
536
                            if ( query.isAGroupByColumn(attrName) ) {
537
                                value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
538
//                                if( value instanceof Column ) {
539
//                                    ((Column)value).table(select.from().table());
540
//                                }
541
                            } else if ( query.isAggregate(attrName) ) {
542
                                String fn = query.getAggregateFunctions().get(attr.getName());
543
                                value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
544
//                                if( value instanceof Column ) {
545
//                                    ((Column)value).table(select.from().table());
546
//                                }
547
                                value = expbuilder.function(fn, value);
548
                            } else {
549
                                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
                            }
552
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
553
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
554
                                // 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

    
559
                            boolean expand;
560
                            if ( !query.isAggregate(attrName) ) {
561
                                if (select.has_aggregate_functions()) {
562
                                    expand = true;
563
                                } else {
564
                                    expand = false;
565
                                }
566
                            } else {
567
                                expand = false;
568
                            }
569
                            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
                        if(member == null){
582
                            continue;
583
                        }
584
                        String attrName = member.getAttributeName();
585
                        FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName, setType, allExtraColumns);                        
586
                        if( attr == null ) {
587
                            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
                        } else {
599
                            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
                        }
613
                    }
614
                }
615
            }
616
        }
617
        if( query!=null && !query.hasOrder() ) {
618
            // Si no hay orden y hay grupos ordenaremos por estos
619
            if( query.hasGroupByColumns() ) { // && isPaginated()) {
620
                // Cuando paginamos debemos ordenar por las columnas del groupby.
621
                // Ordenamos siempre para obtener el mismo resultado cuando paginamos
622
                // y no paginamos.
623
                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
                            value = createValueFromAttribute(sqlbuilder, attr, select.from().table());
629
//                            if( value instanceof Column ) {
630
//                                ((Column)value).table(select.from().table());
631
//                            }
632
                            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
                        }
642
                    }
643
                }
644
            }
645
        }
646

    
647
        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
            if (query == null || !(query.hasAggregateFunctions() || query.hasGroupByColumns())) {
653
                // Siempre ordenamos por la clave primaria
654
                Object x = query == null ? null : query.getQueryParameter(QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER);
655
                if (x == null || !DataTypeUtils.toBoolean(x)) {
656
                    for (String attrName : primaryKeys) {
657
                        if (select.getOrderBy(attrName) == null) {
658
                            ExpressionBuilder.Variable col = sqlbuilder.column(select.from().table(),attrName);
659
                            select.order_by()
660
                                    .value(col) //column(attrName)
661
                                    .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
                    }
672
                }
673
            }
674
        }
675

    
676
    }
677

    
678
    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

    
690
    public ResultSetEntry createResultSet() throws DataException {
691
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
692
        List<String> extraColumnNames = new ArrayList<>();
693

    
694
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
695
        String sql = this.getSQL(sqlbuilder, columns, extraColumnNames);
696

    
697
        ResultSetEntry resultSetEntry = this.helper.getResulSetControler().create(
698
                sql, fetchSize,
699
                columns.toArray(new FeatureAttributeDescriptor[columns.size()]),
700
                extraColumnNames.toArray(new String[extraColumnNames.size()])
701
        );
702
        return resultSetEntry;
703
    }
704

    
705
    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
    
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
        sqlbuilder.setProperties(expression, null, PROP_ADD_TABLE_NAME_TO_COLUMNS, add);
782
    }
783

    
784

    
785
    private static ExpressionBuilder.Value createValueFromAttribute(SQLBuilder sqlbuilder, FeatureAttributeDescriptor attr, TableNameBuilder tableBuilder) {
786
        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
        }
795
        
796
        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

    
808
            SymbolTable symbolTable = ExpressionEvaluatorLocator.getExpressionEvaluatorManager().getInmutableSymbolTable();
809
            
810
            code.link(symbolTable);
811
            ExpressionBuilder.Value value = code.toValue(sqlbuilder.expression());
812
            return value;
813
        }
814
        // Es un campo de la tabla
815
        Column column = sqlbuilder.column(tableBuilder, attr.getName());
816
        return column;
817
    }
818
    
819
}