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

History | View | Annotate | Download (35 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 org.gvsig.expressionevaluator.ExpressionUtils;
37
import org.gvsig.expressionevaluator.GeometryExpressionBuilder;
38
import org.gvsig.fmap.dal.SQLBuilder;
39
import org.gvsig.fmap.dal.SQLBuilder.Column;
40
import org.gvsig.fmap.dal.SQLBuilder.OrderByBuilder;
41
import org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
42
import org.gvsig.fmap.dal.exception.DataException;
43
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
44
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
45
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
46
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
47
import org.gvsig.fmap.dal.feature.FeatureExtraColumns;
48
import org.gvsig.fmap.dal.feature.FeatureQuery;
49
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
50
import org.gvsig.fmap.dal.feature.FeatureQueryOrder.FeatureQueryOrderMember;
51
import org.gvsig.fmap.dal.feature.FeatureType;
52
import org.gvsig.fmap.dal.feature.SQLBuilder_save;
53
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
54
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
55
import static org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer.QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER;
56
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
57
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
58
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCHelperBase.DONT_ADD_TABLE_NAME_TO_COLUMNS;
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.lang.CloneableUtils;
70

    
71
public class ResultSetForSetProviderOperation extends AbstractConnectionOperation {
72

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

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

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

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

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

    
125
    public String getSQL(
126
            JDBCSQLBuilderBase sqlbuilder,
127
            List<FeatureAttributeDescriptor> columns,
128
            List<String> extraColumnNames
129
    ) {
130
        double tolerance = -1; //query.getScale(); 
131
        ExpressionBuilder expbuilder = sqlbuilder.expression();
132
        SelectBuilder select = sqlbuilder.select();
133
        select.from().table()
134
                .database(this.table.getDatabase())
135
                .schema(this.table.getSchema())
136
                .name(this.table.getTable());
137
        select.from().subquery(this.table.getSubquery());
138

    
139

    
140
        List<ExpressionBuilder.Value> valuesToRemoveFeatureType = new ArrayList<>();
141
        
142
        if( shouldUseACustomSelect() ) {
143
            return processCustomSelect(select, expbuilder, sqlbuilder, valuesToRemoveFeatureType, columns);
144
        }
145
        
146

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

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

    
166
        if (!StringUtils.isEmpty(baseOrder)) {
167
            select.order_by().custom(baseOrder);
168
        }
169

    
170
        
171
        if (limit > 0) {
172
            select.limit(limit);
173
        } else {
174
            select.limit(query == null ? null : query.getLimit());
175
        }
176
        if (offset > 0) {
177
            select.offset(offset);
178
        }
179
        sqlbuilder.setProperties(
180
                null,
181
                PROP_FEATURE_TYPE, this.storeType,
182
                PROP_TABLE, table,
183
                PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
184
                PROP_JDBCHELPER, this.helper,
185
                PROP_QUERY, this.query
186
        );
187
        for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
188
            value.setProperty(PROP_FEATURE_TYPE, null);
189
        }
190
        this.helper.expandCalculedColumns(sqlbuilder);
191
        this.helper.processSpecialFunctions(sqlbuilder, storeType, extraColumnNames);
192
        String sql = sqlbuilder.toString();
193
        LOGGER.debug(sql);
194
        return sql;
195
    }
196

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

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

    
325
    public static void process2_ComputedFields(JDBCHelper helper, FeatureType setType, FeatureQuery query, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames){
326
        //No procesamos los campos calculados que tengan un agregado, 
327
        //ya se encargar? de ello el m?todo processAggregatesAndGroupBys
328
        if(!select.from().table().has_name()){
329
            throw new IllegalStateException("Need table in FROM.");
330
        }
331
//        En las columnas del select, si hay funciones de agregado en el select o un GROUP BY
332
//        los campos calculados que no lleven funci?n de agregado no deben incluir el nombre de tabla
333

    
334
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
335
        for (FeatureAttributeDescriptor attr : setType) {
336
            if (attr.isComputed()) {
337
                if (attr.getRelationType() == DynField.RELATION_TYPE_NONE) {
338
                    FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
339
                    if (attrEmulator instanceof FeatureAttributeEmulatorExpression) {
340
                        FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
341
                        Expression exp = x.getExpression();
342
                        
343
//                        if(query == null || !query.isAggregate(attr.getName())){
344
                            if (exp != null && !exp.isEmpty() && helper.supportExpression(setType, exp.getPhrase())) {
345
                                Code code = exp.getCode();
346
                                ExpressionBuilder.Value value = code.toValue(expbuilder);
347
                                select.column()
348
                                        .name(attr.getName())
349
                                        .value(value)
350
                                        .as(attr.getName());
351
                                if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
352
                                    extraColumnNames.add(attr.getName());
353
                                }
354
                                if(query != null && (query.hasGroupByColumns() || query.hasAggregateFunctions())){
355
                                    sqlbuilder.setProperties(value, null, DONT_ADD_TABLE_NAME_TO_COLUMNS, true);
356
                                }
357
                            }
358
//                        }
359
                    }
360
                }
361
            }
362
        }
363
        
364
        if (query != null) {
365
            FeatureExtraColumns extraColumns = query.getExtraColumn();
366
            if (extraColumns != null && !extraColumns.isEmpty()) {
367
                for (EditableFeatureAttributeDescriptor attr : extraColumns.getColumns()) {
368
                    if (!attr.isComputed()) {
369
                        continue;
370
                    }
371
                    FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
372
                    if (attrEmulator instanceof FeatureAttributeEmulatorExpression) {
373
                        FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
374
                        Expression exp = x.getExpression();
375
//                        if(!query.isAggregate(attr.getName())){
376
                            if (exp != null && !exp.isEmpty() && helper.supportExpression(setType, exp.getPhrase())) {
377
                                Code code = exp.getCode();
378
                                ExpressionBuilder.Value value = code.toValue(expbuilder);
379
                                select.column()
380
                                        .name(attr.getName())
381
                                        .value(value)
382
                                        .as(attr.getName());
383
                                if (extraColumnNames!=null && !extraColumnNames.contains(attr.getName())) {
384
                                    extraColumnNames.add(attr.getName());
385
                                }
386
                                if(query.hasGroupByColumns() || query.hasAggregateFunctions()){
387
                                    sqlbuilder.setProperties(value, null, DONT_ADD_TABLE_NAME_TO_COLUMNS, true);
388
                                }
389
                            }
390
//                        }
391
                    }
392
                }
393
            }
394
        }
395
        
396
    }
397

    
398
    public static Map<String, EditableFeatureAttributeDescriptor> getAllExtraColumns(FeatureType setType, FeatureQuery query) {
399
        Map<String, EditableFeatureAttributeDescriptor> allExtraColumns = new HashMap<>();
400
        for (EditableFeatureAttributeDescriptor column : setType.getExtraColumns().getColumns()) {
401
            allExtraColumns.put(column.getName(), column);
402
        }
403
        if(query != null){
404
            for (EditableFeatureAttributeDescriptor column : query.getExtraColumn().getColumns()) {
405
                allExtraColumns.put(column.getName(), column);
406
            }
407
        }
408
        return allExtraColumns;
409
    }
410

    
411
    public static void process4_Aggregates(TableReference table, FeatureType setType, FeatureQuery query, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames) throws RuntimeException {
412
        if(query == null){
413
            return;
414
        }
415
        if(!select.from().table().has_name()){
416
            throw new IllegalStateException("Need table in FROM.");
417
        }
418
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
419
        // Hay que tener en cuenta que puede haber un MAX(CAMPO) sin que se especifique un GROUP BY
420
        if (query.hasAggregateFunctions() ) {
421
            for (FeatureAttributeDescriptor attr : setType) {
422
                if (query.isAggregate(attr.getName()) ) {
423
                    continue;
424
                }
425
//                if (!query.isAGroupByColumn(attr.getName()) && select.has_column(attr.getName()) ) {
426
                if (select.has_column(attr.getName()) ) {
427
                    select.column(attr.getName()).value(expbuilder.constant(null)).as(attr.getName());
428
                }
429
            }
430
            for (FeatureAttributeDescriptor attr : query.getExtraColumns()) {
431
                if (query.isAggregate(attr.getName()) ) {
432
                    continue;
433
                }
434
//                if (!query.isAGroupByColumn(attr.getName()) && select.has_column(attr.getName()) ) {
435
                if (select.has_column(attr.getName()) ) {
436
                    select.column(attr.getName()).value(expbuilder.constant(null)).as(attr.getName());
437
                }
438
            }
439

    
440
            
441
            for (Map.Entry<String, String> entry : query.getAggregateFunctions().entrySet()) {
442
                Expression exp;
443
                FeatureAttributeDescriptor attr = allExtraColumns.get(entry.getKey());
444
                if (attr == null) {
445
                    attr = setType.getAttributeDescriptorFromAll(entry.getKey());
446
                }
447
                if (attr == null) {
448
                    //Aggregate_expression AS name -> name:=aggregate_expression
449
                    exp = ExpressionUtils.createExpression(entry.getKey());
450
                    Code code = exp.getCode();
451
                    if (!(code instanceof Code.Callable)) {
452
                        throw new RuntimeException("Not able to use aggregate function with this expression(1): " + entry.getKey());
453
                    }
454
                    Code.Callable callable = (Code.Callable) code;
455
                    if (!callable.name().equalsIgnoreCase(FUNCTION_LET)) {
456
                        throw new RuntimeException("Not able to use aggregate function with this expression(2): " + entry.getKey());
457
                    }
458
                    String name = ((Code.Identifier) callable.parameters().get(0)).name();
459
                    ExpressionBuilder.Value aggregate = callable.parameters().get(1).toValue();
460
                    
461
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(entry.getValue(), aggregate);
462
                    
463
                    select.column(name).value(aggregateExp).as(name);
464
                } else {
465
                    SQLBuilder.SelectColumnBuilder column = select.column(attr.getName());
466
                    if(column.getValue() != null){
467
                        ExpressionBuilder.Function aggregateExp = expbuilder.function(entry.getValue(), column.getValue());
468
                        sqlbuilder.setProperties(aggregateExp, null, DONT_ADD_TABLE_NAME_TO_COLUMNS, false);
469
                        column.value(aggregateExp).as(column.getName());
470
                    } else {
471
                        ExpressionBuilder.Function aggregateExp = expbuilder.function(
472
                                entry.getValue(), 
473
                                sqlbuilder.column(select.from().table(), column.getName()) 
474
                        );
475
                        column.value(aggregateExp).as(column.getName());
476
                    }
477
                }
478
            }
479
        }
480
    }
481
    
482
    public static void process5_GroupBys(TableReference table, FeatureType setType, FeatureQuery query, Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, SQLBuilder sqlbuilder, SelectBuilder select, List<String> extraColumnNames) throws RuntimeException {
483
        if(query == null){
484
            return;
485
        }
486
        if(!select.from().table().has_name()){
487
            throw new IllegalStateException("Need table in FROM.");
488
        }
489
        if (query.hasGroupByColumns()) { // if tiene agrupacion
490
            GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
491
            for (FeatureAttributeDescriptor attr : setType) {
492
                if (query.isAggregate(attr.getName()) ) {
493
                    continue;
494
                }
495
                if (!query.isAGroupByColumn(attr.getName()) && select.has_column(attr.getName()) ) {
496
                    select.column(attr.getName()).value(expbuilder.constant(null)).as(attr.getName());
497
                }
498
            }
499
            for (FeatureAttributeDescriptor attr : query.getExtraColumns()) {
500
                if (query.isAggregate(attr.getName()) ) {
501
                    continue;
502
                }
503
                if (!query.isAGroupByColumn(attr.getName()) && select.has_column(attr.getName()) ) {
504
                    select.column(attr.getName()).value(expbuilder.constant(null)).as(attr.getName());
505
                }
506
            }
507

    
508
            for (String attrName : query.getGroupByColumns()) {
509
                if (allExtraColumns.get(attrName) == null && setType.get(attrName) == null) {
510
                    try {
511
                        Code code = ExpressionUtils.compile(attrName);
512
                        select.group_by(code.toValue());
513
                    } catch (Exception ex) {
514
                        throw new RuntimeException("Not able to create column by expression in groupby query", ex);
515
                    }
516
                } else {
517
                    ExpressionBuilder.Variable col = sqlbuilder.column(select.from().table(),attrName);
518
                    select.group_by(col);
519
                }
520
            }
521
        }
522
    }
523
    
524
    public static void process3_Where(
525
            JDBCHelper helper, 
526
            FeatureType storeType, 
527
            FeatureQuery query, 
528
            SQLBuilder sqlbuilder,
529
            SelectBuilder select
530
        ) {
531
        if(query == null){
532
            return;
533
        }
534
        if(!select.from().table().has_name()){
535
            throw new IllegalStateException("Need table in FROM.");
536
        }
537
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
538
        
539
        Evaluator filter = query.getFilter();
540
        if (filter != null) {
541
            String sqlfilter = filter.getSQL();
542
            if (!StringUtils.isEmpty(sqlfilter)) {
543
                if (helper.supportFilter(storeType, filter)) {
544
                    select.where().set(expbuilder.toValue(sqlfilter));
545
                }
546
            }
547
        }
548
    }
549
    
550
    public static void process6_OrderBy(
551
            FeatureType storeType, 
552
            FeatureType setType, 
553
            FeatureQuery query, 
554
            Map<String, EditableFeatureAttributeDescriptor> allExtraColumns, 
555
            SQLBuilder sqlbuilder,
556
            SelectBuilder select, 
557
            List<String> primaryKeys,
558
            List<String> extraColumnNames,
559
            List<ExpressionBuilder.Value> valuesToRemoveFeatureType
560
        ) {
561
        if(!select.from().table().has_name()){
562
            throw new IllegalStateException("Need table in FROM.");
563
        }
564
        GeometryExpressionBuilder expbuilder = sqlbuilder.expression();
565

    
566
        
567
        if (query != null) {
568
            FeatureQueryOrder order = query.getOrder();
569
            if (order != null) {
570
                for (FeatureQueryOrderMember member : order.members()) {
571
                    String attrName = member.getAttributeName();
572
                    FeatureAttributeDescriptor attr = setType.getAttributeDescriptorFromAll(attrName);
573
                    if(attr == null){
574
                        attr = allExtraColumns.get(attrName);
575
                    }
576
                    if (member.hasEvaluator()) {
577
                        // Order-by una expression en el query
578
                        String sqlorder = member.getEvaluator().getSQL();
579
                        select.order_by()
580
                                .value(expbuilder.toValue(sqlorder))
581
                                .ascending(member.getAscending());
582
                    } else {
583
                        int nullsMode = OrderByBuilder.MODE_NULLS_LAST;
584
                        Column col = null;
585
                        if (attr != null && attr.isComputed()) {
586
                            // Order-by por una columna extra o por un campo calculado del set/store
587
                            Expression exp = ((FeatureAttributeEmulatorExpression) attr.getFeatureAttributeEmulator()).getExpression();
588
                            if (!select.has_column(attrName)) {
589
                                select.column().value(exp.getCode().toValue()).as(attrName);
590
                            }
591
                            if (extraColumnNames != null && !extraColumnNames.contains(attrName)) {
592
                                extraColumnNames.add(attrName);
593
                            }
594
                            col = sqlbuilder.column(attrName);
595
                        } else {
596
                            if (attr != null && (!attr.allowNull() || attr.isPrimaryKey())) {
597
                                // Puede ser costoso ordenar los nulos al principio o final, asi que
598
                                // si el campo no admite nulos pasamos de indicarselo.
599
                                nullsMode = OrderByBuilder.MODE_NULLS_NOT_SPECIFIED;
600
                            }
601
                            col = sqlbuilder.column(select.from().table(), attrName);
602
                        }
603
                        
604
                        if(select.has_group_by() || select.has_aggregate_functions()){
605
                            col.table(null);
606
                            sqlbuilder.setProperties(col, null, DONT_ADD_TABLE_NAME_TO_COLUMNS, true);
607
                        }
608
                        
609
                        boolean expand = false;
610
                        
611
                        if((attr != null && !query.isAggregate(attrName))){
612
                            if(select.has_aggregate_functions()){
613
                                expand = true;
614
                            } else {
615
                                expand = false;
616
                            }
617
                        } else {
618
                          expand = false;  
619
                        }
620
                        
621
                        if(select.isGroupBy(col.name())) {
622
                            expand = false;
623
                        } 
624
                        
625
                        if(!expand){
626
                            // En el OrderBy no queremos que se sustituya el nombre del campo calculado
627
                            // por su expresion. Se encarga el formater y lo evitamos quitandole el ftype
628
                            // al value.
629
                            valuesToRemoveFeatureType.add(col);
630
                        }
631
                        
632
                        
633
                        
634
                        
635
//                        if(!select.has_group_by() || select.isGroupBy(col.name()) || (attr != null && query.isAggregate(attrName))){ 
636
//                            // En el OrderBy no queremos que se sustituya el nombre del campo calculado
637
//                            // por su expresion. Se encarga el formater y lo evitamos quitandole el ftype
638
//                            // al value.
639
//                            valuesToRemoveFeatureType.add(col);
640
//                        }
641
                        select.order_by().value(col).ascending(member.getAscending()).nulls(nullsMode);
642
                    }
643
                }
644
            }
645
        }
646
        if(!select.has_order_by()) {
647
            // Si no hay orden y hay grupos ordenaremos por estos
648
            if (select.has_group_by()) { // && isPaginated()) {
649
                // Cuando paginamos debemos ordenar por las columnas del groupby.
650
                // Ordenamos siempre para obtener el mismo resultado cuando paginamos
651
                // y no paginamos.
652
                for (ExpressionBuilder.Value group : select.getGroups()) {
653
                    if (select.getOrderBy(group) == null) {
654
                        ExpressionBuilder.Value v = (ExpressionBuilder.Value) CloneableUtils.cloneQuietly(group);
655
                        if (v instanceof Column) {
656
                            ((Column) v).table(null);
657
                        }
658
                        select.order_by().value(v).ascending().nulls(OrderByBuilder.MODE_NULLS_LAST);
659
                        valuesToRemoveFeatureType.add(v);
660
                    }
661
                }
662
            }
663
        }
664

    
665
        if (primaryKeys.isEmpty()) {
666
            // Muy probablemente si no tiene pk sea una vista, asi que 
667
            // pasaremos de ordenar y esperemos que la vista este ya ordenada.
668
            select.disable_check_order_and_offset();
669
        } else {
670
            if (query == null || !(query.hasAggregateFunctions() || query.hasGroupByColumns())) {
671
                // Siempre ordenamos por la clave primaria
672
                Object x = query == null ? null : query.getQueryParameter(QUERY_DONT_ADD_PRIMARY_KEY_IN_THE_ORDER);
673
                if (x == null || !DataTypeUtils.toBoolean(x)) {
674
                    for (String attrName : primaryKeys) {
675
                        if (select.getOrderBy(attrName) == null) {
676
                            ExpressionBuilder.Variable col = sqlbuilder.column(select.from().table(),attrName);
677
                            select.order_by()
678
                                    .value(col) //column(attrName)
679
                                    .ascending()
680
                                    .nulls(OrderByBuilder.MODE_NULLS_NOT_SPECIFIED)
681
                                    ;
682
                            // No tengo claro que pasa si se esta agrupando, y no se ha
683
                            // incluido en el agrupamiento al PK. En ese caso el select
684
                            // que se genera tendria una pinta tal que asi:
685
                            //   SELECT NULL as pk, ... ORDER BY pk ASC
686
                            // Probablemente en ese caso no tendriamos que meter la PK
687
                            // en el order-by ya que creo que no cambiaria el resultado.
688
                        }
689
                    }
690
                }
691
            }
692
        }
693

    
694
    }
695
    
696
    private boolean isPaginated() {
697
        // No tengo claro que (offset==0 && limit>0) sea lo mas correcto,
698
        // Pero cuando se va a paginar y se pide la primera pagina offset es
699
        // 0 y limit>0, y si no ordenamos ya esa primera pagina los resultados
700
        // que se obtienen no son correctos, ya que la primera pagina se saca
701
        // sin ordenar y el resto ordenadas.
702
        // Probablemente deberiamos tener alguna otra forma de detectar que
703
        // estamos paginanado ya que asi no distinguimo si solo queremos 
704
        // obtener los primeros elementos sin importarnos su orden.
705
        return (offset > 0 || (offset == 0 && limit > 0));
706
    }
707

    
708
    public ResultSetEntry createResultSet() throws DataException {
709
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
710
        List<String> extraColumnNames = new ArrayList<>();
711

    
712
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
713
        String sql = this.getSQL(sqlbuilder, columns, extraColumnNames);
714

    
715
        ResultSetEntry resultSetEntry = this.helper.getResulSetControler().create(
716
                sql, fetchSize,
717
                columns.toArray(new FeatureAttributeDescriptor[columns.size()]),
718
                extraColumnNames.toArray(new String[extraColumnNames.size()])
719
        );
720
        return resultSetEntry;
721
    }
722

    
723
    private boolean shouldUseACustomSelect() {
724
        if( !table.hasSubquery() ) {
725
            return false;
726
        }
727
        if( this.query == null ) {
728
            return false;
729
        }
730
        if( this.query.isUseSubquery() ) {
731
            return false;
732
        }
733
        if( this.query.hasGroupByColumns() ) {
734
            return false;
735
        }
736
        if( this.query.hasAggregateFunctions() ) {
737
            return false;
738
        }        
739
        if( !this.query.hasFilter() && !this.query.hasOrder() ) {
740
            return true;
741
        }
742
//        si el filtro es incompatible con la BBDD
743
//            return false
744
//        si el orden es incompatible con la BBDD
745
//            return false
746
//        si filtro u orden usan la funcion foreing_value 
747
//            return false;
748
        return true;
749
    }
750
}