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

History | View | Annotate | Download (36.8 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 org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
45
import org.gvsig.fmap.dal.exception.DataException;
46
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
47
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
48
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
49
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
50
import org.gvsig.fmap.dal.feature.FeatureQuery;
51
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
52
import org.gvsig.fmap.dal.feature.FeatureQueryOrder.FeatureQueryOrderMember;
53
import org.gvsig.fmap.dal.feature.FeatureType;
54
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
55
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
56
import static org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer.QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER;
57
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
58
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
59
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
60
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_FEATURE_TYPE;
61
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_JDBCHELPER;
62
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_QUERY;
63
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_SYMBOLTABLE;
64
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_TABLE;
65
import org.gvsig.fmap.geom.DataTypes;
66
import org.gvsig.tools.dataTypes.DataTypeUtils;
67
import org.gvsig.tools.dynobject.DynField;
68
import org.gvsig.tools.evaluator.Evaluator;
69
import org.gvsig.tools.util.ChainedIterable;
70
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase.ADD_TABLE_NAME_TO_COLUMNS;
71

    
72
public class ResultSetForSetProviderOperation extends AbstractConnectionOperation {
73

    
74
    private final TableReference table;
75
    private final String baseFilter;
76
    private final String baseOrder;
77
    private final FeatureType storeType;
78
    private final FeatureType setType;
79
    private final FeatureQuery query;
80
    private final long limit;
81
    private final long offset;
82
    private final int fetchSize;
83

    
84
    public ResultSetForSetProviderOperation(
85
            JDBCHelper helper,
86
            TableReference table,
87
            String baseFilter,
88
            String baseOrder,
89
            FeatureQuery query,
90
            FeatureType storeType,
91
            FeatureType setType,
92
            long limit,
93
            long offset,
94
            int fetchSize
95
    ) {
96
        super(helper);
97
        this.table = table;
98
        this.baseFilter = baseFilter;
99
        this.baseOrder = baseOrder;
100
        this.storeType = storeType;
101
        this.setType = setType;
102
        this.query = query;
103
        this.limit = limit;
104
        this.offset = offset;
105
        this.fetchSize = fetchSize;
106
    }
107

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

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

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

    
126
    public String getSQL(
127
            JDBCSQLBuilderBase sqlbuilder,
128
            List<FeatureAttributeDescriptor> columns,
129
            List<String> extraColumnNames
130
    ) {
131
        double tolerance = -1; //query.getScale(); 
132
        ExpressionBuilder expbuilder = sqlbuilder.expression();
133
        
134
        expbuilder.setProperty(PROP_FEATURE_TYPE, this.storeType);
135
        expbuilder.setProperty(PROP_TABLE, table);
136
        expbuilder.setProperty(PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable());
137
        expbuilder.setProperty(PROP_JDBCHELPER, this.helper);
138
        expbuilder.setProperty(PROP_QUERY, this.query);
139
        
140
        SelectBuilder select = sqlbuilder.select();
141
        select.from().table()
142
                .database(this.table.getDatabase())
143
                .schema(this.table.getSchema())
144
                .name(this.table.getTable());
145
        select.from().subquery(this.table.getSubquery());
146

    
147

    
148
        List<ExpressionBuilder.Value> valuesToRemoveFeatureType = new ArrayList<>();
149
        
150
        if( shouldUseACustomSelect() ) {
151
            return processCustomSelect(select, expbuilder, sqlbuilder, valuesToRemoveFeatureType, columns);
152
        }
153
        
154

    
155
        Map<String, EditableFeatureAttributeDescriptor> allExtraColumns = getAllExtraColumns(this.setType, this.query);
156

    
157
        List<String> primaryKeys = new ArrayList<>();
158
        for (FeatureAttributeDescriptor attr : storeType.getPrimaryKey()) {
159
            primaryKeys.add(attr.getName());
160
        }
161
        
162
        //Don't change order of this processes
163
        process1_SimpleFields(helper, setType, query, sqlbuilder, select, columns, primaryKeys, extraColumnNames);
164
        process2_ComputedFields(helper, setType, query, sqlbuilder, select, extraColumnNames);
165
        process3_Where(helper, storeType, query, sqlbuilder, select);
166
        process4_Aggregates(this.table, this.setType, this.query, allExtraColumns, sqlbuilder, select, extraColumnNames);
167
        process5_GroupBys(this.table, this.setType, this.query, allExtraColumns, sqlbuilder, select, extraColumnNames);
168
        process6_OrderBy(storeType, setType, query, allExtraColumns, sqlbuilder, select, primaryKeys, extraColumnNames, valuesToRemoveFeatureType);
169
        
170
        if (!StringUtils.isEmpty(baseFilter)) {
171
            select.where().and(expbuilder.toValue(baseFilter));
172
        }
173

    
174
        if (!StringUtils.isEmpty(baseOrder)) {
175
            select.order_by().custom(baseOrder);
176
        }
177

    
178
        
179
        if (limit > 0) {
180
            select.limit(limit);
181
        } else {
182
            select.limit(query == null ? null : query.getLimit());
183
        }
184
        if (offset > 0) {
185
            select.offset(offset);
186
        }
187
        sqlbuilder.setProperties(
188
                null,
189
                PROP_FEATURE_TYPE, this.storeType,
190
                PROP_TABLE, table,
191
                PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
192
                PROP_JDBCHELPER, this.helper,
193
                PROP_QUERY, this.query
194
        );
195
        for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
196
            value.setProperty(PROP_FEATURE_TYPE, null);
197
        }
198
        this.helper.expandCalculedColumns(sqlbuilder);
199
        this.helper.processSpecialFunctions(sqlbuilder, storeType, extraColumnNames);
200
        String sql = StringUtils.trim(sqlbuilder.toString());
201
        LOGGER.debug(sql);
202
        return sql;
203
    }
204

    
205
    private String processCustomSelect(SelectBuilder select, ExpressionBuilder expbuilder, JDBCSQLBuilderBase sqlbuilder, List<ExpressionBuilder.Value> valuesToRemoveFeatureType, List<FeatureAttributeDescriptor> columns) {
206
        String sql = table.getSubquery();
207
        if( StringUtils.containsAny(sql, "${where_filter}", "${and_filter}") ) {
208
            Evaluator filter = query.getFilter();
209
            if (filter != null) {
210
                String sqlfilter = filter.getSQL();
211
                if (!StringUtils.isEmpty(sqlfilter)) {
212
                    if (this.helper.supportFilter(this.storeType, filter)) {
213
                        select.where().set(expbuilder.toValue(sqlfilter));
214
                    }
215
                }
216
            }
217
            if (!StringUtils.isEmpty(baseFilter)) {
218
                select.where().and(expbuilder.toValue(baseFilter));
219
            }
220
        }
221
        if( StringUtils.containsAny(sql, "${order_by_orderspec}", "${comma_orderspec}") ) {
222
            FeatureQueryOrder order = query.getOrder();
223
            if (order != null) {
224
                for (FeatureQueryOrderMember member : order.members()) {
225
                    String attrName = member.getAttributeName();
226
                    ExpressionBuilder.Variable col = expbuilder.column(attrName);
227
                    select.order_by().value(col).ascending(member.getAscending());
228
                }
229
            }
230
        }
231
        if( select.has_where() || select.has_order_by() ) {
232
            sqlbuilder.setProperties(
233
                    null,
234
                    PROP_FEATURE_TYPE, this.storeType,
235
                    PROP_TABLE, table,
236
                    PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
237
                    PROP_JDBCHELPER, this.helper,
238
                    PROP_QUERY, this.query
239
            );
240
            for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
241
                value.setProperty(PROP_FEATURE_TYPE, null);
242
            }
243
            if( select.has_where()) {
244
                String s = select.where().toString();
245
                sql = StringUtils.replace(sql, "${where_filter}", "WHERE " + s);
246
                sql = StringUtils.replace(sql, "${and_filter}", "AND (" + s + ")");
247
            }
248
            if( select.has_order_by() ) {
249
                String s = select.order_by().toString();
250
                sql = StringUtils.replace(sql, "${order_by_orderspec}", "ORDER BY " + s);
251
                sql = StringUtils.replace(sql, "${comma_orderspec}", ", "+ s);
252
            }
253
        }
254
        for (FeatureAttributeDescriptor attr : storeType) {
255
            columns.add(attr);
256
        }
257
        return sql;
258
    }
259
    
260
    public static void process1_SimpleFields(
261
            JDBCHelper helper, 
262
            FeatureType setType, 
263
            FeatureQuery query, 
264
            SQLBuilder sqlbuilder, 
265
            SelectBuilder select, 
266
            List<FeatureAttributeDescriptor> columns,
267
            List<String> primaryKeys,
268
            List<String> extraColumnNames
269
        ){
270
        if(!select.from().table().has_name()){
271
            throw new IllegalStateException("Need table in FROM.");
272
        }
273
        
274
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
275
        
276
        List<String> forcedColumns = new ArrayList<>(primaryKeys);
277

    
278
        String[] constantsAttributeNames = null;
279
        if (query != null && query.hasConstantsAttributeNames()) {
280
            constantsAttributeNames = query.getConstantsAttributeNames();
281
        }
282
        
283
        for (FeatureAttributeDescriptor attr : setType) {
284
            if (attr.isComputed()) {
285
                // Campos calculados se a?aden despues para asegurarnos que
286
                // primeros se a?aden los campos reales y al final los calculados
287
                continue;
288
            }
289
            if (ArrayUtils.contains(constantsAttributeNames, attr.getName())) {
290
                continue;
291
            }
292
            if (attr.isPrimaryKey()) {
293
                forcedColumns.remove(attr.getName());
294
            }
295
            if (attr.getType() == DataTypes.GEOMETRY) {
296
                select.column().name(attr.getName()).as_geometry();
297
            } else {
298
                select.column().name(attr.getName());
299
            }
300
            columns.add(attr);
301
        }
302
        
303
        if (query == null || !query.hasGroupByColumns()) {
304
            for (String attrName : forcedColumns) {
305
                select.column().name(attrName);
306
                FeatureAttributeDescriptor attr = setType.getAttributeDescriptor(attrName);
307
                // Ojo, que cuando estamos pintando una vista NO TENEMOS LA PK y attr vale null!!!!
308
                // No se si deberiamos forzar a que cuando se crea un subfeaturetype se meta siempre
309
                // la pk, o simplemente nunca asumir que la vamos a tener.
310
                if (attr != null) {
311
                    columns.add(attr);
312
                }
313
            }
314
        }
315
    }
316

    
317
    public static void process2_ComputedFields(JDBCHelper helper, FeatureType setType, FeatureQuery query, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames){
318
        if(!select.from().table().has_name()){
319
            throw new IllegalStateException("Need table in FROM.");
320
        }
321
//        En las columnas del select, si hay funciones de agregado en el select o un GROUP BY
322
//        los campos calculados que no lleven funci?n de agregado no deben incluir el nombre de tabla
323

    
324
        for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType, query) ) {
325
            if (attr.isComputed()) {
326
                ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr);
327
                if( value != VALUE_NULL ) {
328
                    select.column()
329
                            .name(attr.getName())
330
                            .value(value)
331
                            .as(attr.getName());
332
                    if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
333
                        extraColumnNames.add(attr.getName());
334
                    }
335
//                    if(query != null && (query.hasGroupByColumns() || query.hasAggregateFunctions())){
336
                        addTableNameToColumns(sqlbuilder, value, true);
337
//                    }
338
                }
339
            }
340
        }
341
                
342
    }
343

    
344
    public static void process4_Aggregates(TableReference table, FeatureType setType, FeatureQuery query, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames) throws RuntimeException {
345
        if(query == null){
346
            return;
347
        }
348
        if(!select.from().table().has_name()){
349
            throw new IllegalStateException("Need table in FROM.");
350
        }
351
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
352
        if (query.hasAggregateFunctions() ) {
353
            // Ponemos NULL AS... en las columnas que no tengan funcion de argregado y no esten en el group by.
354
            for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType,query) )  {
355
                final String name = attr.getName();
356
                if( select.has_column(name) ) {
357
//                    if( attr.isComputed() ) {
358
//                        if (!query.isAggregate(name) ) {
359
//                            select.column(name).value(VALUE_NULL).as(name);
360
//                        }
361
//                    } else {
362
                        if ( !query.isAGroupByColumn(name) && !query.isAggregate(name) ) {
363
                            select.column(name).value(VALUE_NULL).as(name);
364
                        }
365
//                    }
366
                }
367
            }
368
            
369
            for (Map.Entry<String, String> entry : query.getAggregateFunctions().entrySet()) {
370
                String aggregateFunction = entry.getValue();
371
                String aggregateExpression = entry.getKey();
372
                FeatureAttributeDescriptor attr = getAttributeDescriptor(aggregateExpression, setType, allExtraColumns);
373
                if (attr == null) {
374
                    //Aggregate_expression AS name -> name:=aggregate_expression
375
                    Expression exp = ExpressionUtils.createExpression(aggregateExpression);
376
                    Code code = exp.getCode();
377
                    if (!(code instanceof Code.Callable)) {
378
                        throw new RuntimeException("Not able to use aggregate function with this expression(1): " + aggregateExpression);
379
                    }
380
                    Code.Callable callable = (Code.Callable) code;
381
                    if (!callable.name().equalsIgnoreCase(FUNCTION_LET)) {
382
                        throw new RuntimeException("Not able to use aggregate function with this expression(2): " + aggregateExpression);
383
                    }
384
                    String name = ((Code.Identifier) callable.parameters().get(0)).name();
385
                    ExpressionBuilder.Value value = callable.parameters().get(1).toValue();
386
                    
387
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(aggregateFunction, value);                    
388
                    select.column(name).value(aggregateExp).as(name);
389
                } else {
390
                    SQLBuilder.SelectColumnBuilder column = select.column(attr.getName());
391
                    ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr);
392
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(aggregateFunction, value);
393
                    addTableNameToColumns(sqlbuilder, aggregateExp, true);
394
                    column.value(aggregateExp).as(attr.getName());
395
                }
396
            }
397
        }
398
    }
399

    
400
    public static void process5_GroupBys(TableReference table, FeatureType setType, FeatureQuery query, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames) throws RuntimeException {
401
        if(query == null){
402
            return;
403
        }
404
        if(!select.from().table().has_name()){
405
            throw new IllegalStateException("Need table in FROM.");
406
        }
407
        if (query.hasGroupByColumns()) { // if tiene agrupacion
408
            GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
409
            for (FeatureAttributeDescriptor attr : attributeDescriptorsIterable(setType,query.getExtraColumns()) ) {
410
                final String name = attr.getName();
411
                if( select.has_column(name) ) {
412
//                    if( attr.isComputed() ) {
413
//                        if (!query.isAggregate(name) ) {
414
//                            select.column(name).value(VALUE_NULL).as(name);
415
//                        }
416
//                    } else {
417
                        if ( !query.isAGroupByColumn(name) && !query.isAggregate(name) ) {
418
                            select.column(name).value(VALUE_NULL).as(name);
419
                        }
420
//                    }
421
                }
422
            }
423

    
424
            for (String attrName : query.getGroupByColumns()) {
425
                FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName,setType, allExtraColumns);
426
                if ( attr == null ) {
427
                    try {
428
                        Code code = ExpressionUtils.compile(attrName);
429
                        select.group_by(code.toValue());
430
                    } catch (Exception ex) {
431
                        throw new RuntimeException("Not able to create column by expression in groupby query", ex);
432
                    }
433
                } else {
434
                    ExpressionBuilder.Value value = createValueFromAttribute(sqlbuilder, attr);
435
                    if( value instanceof Column ) {
436
                        ((Column)value).table(select.from().table());
437
                        if( select.getColumns().isEmpty() ) {
438
                            // Tratamos de asegurarnos de que al menos hay una columna en el select
439
                            select.column(attr.getName());
440
                        }
441
                    }
442
                    addTableNameToColumns(sqlbuilder, value, true);
443
                    select.group_by(value);
444
                }
445
            }
446
        }
447
    }
448
    
449
    public static void process3_Where(
450
            JDBCHelper helper, 
451
            FeatureType storeType, 
452
            FeatureQuery query, 
453
            SQLBuilder sqlbuilder,
454
            SelectBuilder select
455
        ) {
456
        if(query == null){
457
            return;
458
        }
459
        if(!select.from().table().has_name()){
460
            throw new IllegalStateException("Need table in FROM.");
461
        }
462
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
463
        
464
        Evaluator filter = query.getFilter();
465
        if (filter != null) {
466
            String sqlfilter = filter.getSQL();
467
            if (!StringUtils.isEmpty(sqlfilter)) {
468
                if (helper.supportFilter(storeType, filter)) {
469
                    ExpressionBuilder.Value value = expbuilder.toValue(sqlfilter);
470
                    addTableNameToColumns(sqlbuilder, value, true);
471
                    select.where().set(value);
472
                }
473
            }
474
        }
475
    }
476
    
477
    public static void process6_OrderBy(
478
            FeatureType storeType, 
479
            FeatureType setType, 
480
            FeatureQuery query, 
481
            Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, 
482
            SQLBuilder sqlbuilder,
483
            SelectBuilder select, 
484
            List<String> primaryKeys,
485
            List<String> extraColumnNames,
486
            List<ExpressionBuilder.Value> valuesToRemoveFeatureType
487
        ) {
488
        //
489
        // https://learnsql.com/cookbook/how-to-order-rows-by-group-sum-in-sql/
490
        //
491
        
492
        if(!select.from().table().has_name()){
493
            throw new IllegalStateException("Need table in FROM.");
494
        }
495
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
496
        
497
        if (query != null) {
498
            FeatureQueryOrder order = query.getOrder();
499
            if (order != null) {
500
                if (query.hasAggregateFunctions() || query.hasGroupByColumns()) {
501
                    for (FeatureQueryOrderMember member : order.members()) {
502
                        String attrName = member.getAttributeName();
503
                        FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName, setType, allExtraColumns);                        
504
                        if( attr == null ) {
505
                            // Order-by una expression en el query
506
                            String sqlorder = member.getEvaluator().getSQL();
507
                            select.order_by()
508
                                    .value(expbuilder.toValue(sqlorder))
509
                                    .ascending(member.getAscending());
510
                        } else {
511
                            ExpressionBuilder.Value value;
512
                            if ( query.isAGroupByColumn(attrName) ) {
513
                                value = createValueFromAttribute(sqlbuilder, attr);
514
                                if( value instanceof Column ) {
515
                                    ((Column)value).table(select.from().table());
516
                                }
517
                            } else if ( query.isAggregate(attrName) ) {
518
                                String fn = query.getAggregateFunctions().get(attr.getName());
519
                                value = createValueFromAttribute(sqlbuilder, attr);
520
                                if( value instanceof Column ) {
521
                                    ((Column)value).table(select.from().table());
522
                                }
523
                                value = expbuilder.function(fn, value);
524
                            } else {
525
                                continue;
526
//                                throw new RuntimeException("Can't order by column '" + attrName + "', it does not have an aggregate function nor is it grouping by it.");
527
                            }
528
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
529
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
530
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
531
                                // si el campo no admite nulos pasamos de indicarselo.
532
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
533
                            }
534

    
535
                            boolean expand;
536
                            if ( !query.isAggregate(attrName) ) {
537
                                if (select.has_aggregate_functions()) {
538
                                    expand = true;
539
                                } else {
540
                                    expand = false;
541
                                }
542
                            } else {
543
                                expand = false;
544
                            }
545
                            if (select.isGroupBy(attrName)) {
546
                                expand = false;
547
                            }
548

    
549
                            expandColumns(value, expand, valuesToRemoveFeatureType);
550
                            addTableNameToColumns(sqlbuilder, value, !attr.isComputed());                                
551
                            select.order_by().value(value).ascending(member.getAscending()).nulls(nullsMode);
552
                        }
553
                    }
554

    
555
                } else {
556
                    for (FeatureQueryOrderMember member : order.members()) {
557
                        String attrName = member.getAttributeName();
558
                        FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName, setType, allExtraColumns);                        
559
                        if( attr == null ) {
560
                            // Order-by una expression en el query
561
                            String sqlorder = member.getEvaluator().getSQL();
562
                            select.order_by()
563
                                    .value(expbuilder.toValue(sqlorder))
564
                                    .ascending(member.getAscending());
565
                        } else {
566
                            Column column = sqlbuilder.column(attr.getName());
567
                            if( !attr.isComputed() ) {
568
                                column.table(select.from().table());
569
                            }
570
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
571
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
572
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
573
                                // si el campo no admite nulos pasamos de indicarselo.
574
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
575
                            }
576
                            expandColumns(column, false, valuesToRemoveFeatureType);
577
                            addTableNameToColumns(sqlbuilder, column, !attr.isComputed());                                
578
                            select.order_by().value(column).ascending(member.getAscending()).nulls(nullsMode);
579
                        }
580
                    }
581
                }
582
            }
583
        }
584
        if( query!=null && !query.hasOrder() ) {
585
            // Si no hay orden y hay grupos ordenaremos por estos
586
            if( query.hasGroupByColumns() ) { // && isPaginated()) {
587
                // Cuando paginamos debemos ordenar por las columnas del groupby.
588
                // Ordenamos siempre para obtener el mismo resultado cuando paginamos
589
                // y no paginamos.
590
                for (String attrName : query.getGroupByColumns()) {
591
                    FeatureAttributeDescriptor attr = getAttributeDescriptor(attrName,setType, allExtraColumns);
592
                    if ( attr != null ) {
593
                        if( query.getOrder().get(attrName)==null ) {
594
                            ExpressionBuilder.Value value;
595
                            value = createValueFromAttribute(sqlbuilder, attr);
596
                            if( value instanceof Column ) {
597
                                ((Column)value).table(select.from().table());
598
                            }
599
                            int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
600
                            if ( !attr.allowNull() || attr.isPrimaryKey() ) {
601
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
602
                                // si el campo no admite nulos pasamos de indicarselo.
603
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
604
                            }
605
                            expandColumns(value, true, valuesToRemoveFeatureType);
606
                            addTableNameToColumns(sqlbuilder, value, true);                                
607
                            select.order_by().value(value).ascending(true).nulls(nullsMode);
608
                        }
609
                    }
610
                }
611
            }
612
        }
613

    
614
        if (primaryKeys.isEmpty()) {
615
            // Muy probablemente si no tiene pk sea una vista, asi que 
616
            // pasaremos de ordenar y esperemos que la vista este ya ordenada.
617
            select.disable_check_order_and_offset();
618
        } else {
619
            if (query == null || !(query.hasAggregateFunctions() || query.hasGroupByColumns())) {
620
                // Siempre ordenamos por la clave primaria
621
                Object x = query == null ? null : query.getQueryParameter(QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER);
622
                if (x == null || !DataTypeUtils.toBoolean(x)) {
623
                    for (String attrName : primaryKeys) {
624
                        if (select.getOrderBy(attrName) == null) {
625
                            ExpressionBuilder.Variable col = sqlbuilder.column(select.from().table(),attrName);
626
                            select.order_by()
627
                                    .value(col) //column(attrName)
628
                                    .ascending()
629
                                    .nulls(OrderByBuilder.MODE_NULLS_NOT_SPECIFIED)
630
                                    ;
631
                            // No tengo claro que pasa si se esta agrupando, y no se ha
632
                            // incluido en el agrupamiento al PK. En ese caso el select
633
                            // que se genera tendria una pinta tal que asi:
634
                            //   SELECT NULL as pk, ... ORDER BY pk ASC
635
                            // Probablemente en ese caso no tendriamos que meter la PK
636
                            // en el order-by ya que creo que no cambiaria el resultado.
637
                        }
638
                    }
639
                }
640
            }
641
        }
642

    
643
    }
644

    
645
    private boolean isPaginated() {
646
        // No tengo claro que (offset==0 && limit>0) sea lo mas correcto,
647
        // Pero cuando se va a paginar y se pide la primera pagina offset es
648
        // 0 y limit>0, y si no ordenamos ya esa primera pagina los resultados
649
        // que se obtienen no son correctos, ya que la primera pagina se saca
650
        // sin ordenar y el resto ordenadas.
651
        // Probablemente deberiamos tener alguna otra forma de detectar que
652
        // estamos paginanado ya que asi no distinguimo si solo queremos 
653
        // obtener los primeros elementos sin importarnos su orden.
654
        return (offset > 0 || (offset == 0 && limit > 0));
655
    }
656

    
657
    public ResultSetEntry createResultSet() throws DataException {
658
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
659
        List<String> extraColumnNames = new ArrayList<>();
660

    
661
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
662
        String sql = this.getSQL(sqlbuilder, columns, extraColumnNames);
663

    
664
        ResultSetEntry resultSetEntry = this.helper.getResulSetControler().create(
665
                sql, fetchSize,
666
                columns.toArray(new FeatureAttributeDescriptor[columns.size()]),
667
                extraColumnNames.toArray(new String[extraColumnNames.size()])
668
        );
669
        return resultSetEntry;
670
    }
671

    
672
    private boolean shouldUseACustomSelect() {
673
        if( !table.hasSubquery() ) {
674
            return false;
675
        }
676
        if( this.query == null ) {
677
            return false;
678
        }
679
        if( this.query.isUseSubquery() ) {
680
            return false;
681
        }
682
        if( this.query.hasGroupByColumns() ) {
683
            return false;
684
        }
685
        if( this.query.hasAggregateFunctions() ) {
686
            return false;
687
        }        
688
        if( !this.query.hasFilter() && !this.query.hasOrder() ) {
689
            return true;
690
        }
691
//        si el filtro es incompatible con la BBDD
692
//            return false
693
//        si el orden es incompatible con la BBDD
694
//            return false
695
//        si filtro u orden usan la funcion foreing_value 
696
//            return false;
697
        return true;
698
    }
699
    
700
    public static Map<String, EditableFeatureAttributeDescriptor> getAllExtraColumns(FeatureType setType, FeatureQuery query) {
701
        Map<String, EditableFeatureAttributeDescriptor> allExtraColumns = new HashMap<>();
702
        for (EditableFeatureAttributeDescriptor column : setType.getExtraColumns().getColumns()) {
703
            allExtraColumns.put(column.getName(), column);
704
        }
705
        if(query != null){
706
            for (EditableFeatureAttributeDescriptor column : query.getExtraColumn().getColumns()) {
707
                allExtraColumns.put(column.getName(), column);
708
            }
709
        }
710
        return allExtraColumns;
711
    }
712

    
713
    private static Iterable<FeatureAttributeDescriptor> attributeDescriptorsIterable(Iterable... iterables) {
714
        return new ChainedIterable<>(iterables);
715
    }
716

    
717
    private static Iterable<FeatureAttributeDescriptor> attributeDescriptorsIterable(FeatureType setType, FeatureQuery query) {
718
        if( query == null ) {
719
            return new ChainedIterable<>(setType);
720
        }
721
        return attributeDescriptorsIterable(setType, query.getExtraColumns());
722
    }
723

    
724
    private static boolean existsAttributeDescriptor(String attrName, FeatureType setType, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns)  {
725
        return getAttributeDescriptor(attrName, setType, allExtraColumns)!=null;
726
    }
727

    
728
    private static FeatureAttributeDescriptor getAttributeDescriptor(String attrName, FeatureType setType, Map<String, EditableFeatureAttributeDescriptor> extraColumns) {
729
        FeatureAttributeDescriptor attr = null;
730
        if( setType!=null ) {
731
            attr = setType.getAttributeDescriptorFromAll(attrName);
732
        }
733
        if(attr == null && extraColumns!=null){
734
            attr = extraColumns.get(attrName);
735
        }
736
        return attr;
737
    }
738

    
739
    private static void expandColumns(ExpressionBuilder.Value value, boolean expand, List<ExpressionBuilder.Value> valuesToRemoveFeatureType) {
740
        if(!expand){
741
            // En el OrderBy no queremos que se sustituya el nombre del campo calculado
742
            // por su expresion. Se encarga el formater y lo evitamos quitandole el ftype
743
            // al value.
744
            valuesToRemoveFeatureType.add(value);
745
        }
746
    }
747
    private static void addTableNameToColumns(SQLBuilder sqlbuilder, ExpressionBuilder.Value expression, boolean add) {
748
        sqlbuilder.setProperties(expression, null, ADD_TABLE_NAME_TO_COLUMNS, add);
749
    }
750

    
751

    
752
    private static ExpressionBuilder.Value createValueFromAttribute(SQLBuilder sqlbuilder, FeatureAttributeDescriptor attr) {
753
        switch(attr.getRelationType()) {
754
            case DynField.RELATION_TYPE_AGGREGATE:
755
            case DynField.RELATION_TYPE_COMPOSITION:
756
                return VALUE_NULL;
757
            case DynField.RELATION_TYPE_NONE:
758
            case DynField.RELATION_TYPE_IDENTITY:
759
            case DynField.RELATION_TYPE_COLLABORATION:
760
                break;
761
        }
762
        
763
        if( attr.isComputed() ) {
764
            FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
765
            if( !(attrEmulator instanceof FeatureAttributeEmulatorExpression) ) {
766
                return VALUE_NULL;
767
            }
768
            FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
769
            Expression exp = x.getExpression();
770
            if (exp == null || exp.isEmpty() ) {
771
                return VALUE_NULL;
772
            }
773
            Code code = exp.getCode();
774

    
775
            SymbolTable symbolTable = ExpressionEvaluatorLocator.getExpressionEvaluatorManager().getInmutableSymbolTable();
776
            
777
            code.link(symbolTable);
778
            ExpressionBuilder.Value value = code.toValue(sqlbuilder.expression());
779
            return value;
780
        }
781
        // Es un campo de la tabla
782
        Column column = sqlbuilder.column(attr.getName());
783
        return column;
784
    }
785
    
786
}