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

History | View | Annotate | Download (23.2 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.sql.Connection;
27
import java.util.ArrayList;
28
import java.util.HashMap;
29
import java.util.List;
30
import java.util.Map;
31
import org.apache.commons.lang3.ArrayUtils;
32
import org.apache.commons.lang3.StringUtils;
33
import org.gvsig.expressionevaluator.Code;
34
import org.gvsig.expressionevaluator.Expression;
35
import org.gvsig.expressionevaluator.ExpressionBuilder;
36
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_LET;
37
import org.gvsig.expressionevaluator.ExpressionUtils;
38
import org.gvsig.expressionevaluator.SymbolTable;
39
import org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
40
import org.gvsig.fmap.dal.exception.DataException;
41
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
42
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
43
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
44
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
45
import org.gvsig.fmap.dal.feature.FeatureExtraColumns;
46
import org.gvsig.fmap.dal.feature.FeatureQuery;
47
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
48
import org.gvsig.fmap.dal.feature.FeatureQueryOrder.FeatureQueryOrderMember;
49
import org.gvsig.fmap.dal.feature.FeatureType;
50
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
51
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory.TableReference;
52
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
53
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
54
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_FEATURE_TYPE;
55
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_JDBCHELPER;
56
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_QUERY;
57
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_SYMBOLTABLE;
58
import static org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase.PROP_TABLE;
59
import org.gvsig.fmap.dal.store.jdbc2.spi.expressionbuilder.formatters.ComputedAttribute;
60
import org.gvsig.fmap.geom.DataTypes;
61
import org.gvsig.tools.dynobject.DynField;
62
import org.gvsig.tools.evaluator.Evaluator;
63
import org.gvsig.tools.lang.CloneableUtils;
64
import org.gvsig.tools.util.ContainerUtils;
65

    
66
public class ResultSetForSetProviderOperation extends AbstractConnectionOperation {
67

    
68
    private final TableReference table;
69
    private final String baseFilter;
70
    private final String baseOrder;
71
    private final FeatureType storeType;
72
    private final FeatureType setType;
73
    private final FeatureQuery query;
74
    private final long limit;
75
    private final long offset;
76
    private final int fetchSize;
77

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

    
102
    @Override
103
    protected Object perform_operation() throws Exception {
104
        ResultSetEntry rs = createResultSet();
105
        return rs;
106
    }
107

    
108
    @Override
109
    public Object perform(Connection conn) throws DataException {
110
        throw new UnsupportedOperationException("Not supported yet.");
111
    }
112

    
113
    public String getSQL() {
114
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
115
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
116
        String sql = this.getSQL(sqlbuilder, columns, null);
117
        return sql;
118
    }
119

    
120
    public String getSQL(
121
            JDBCSQLBuilderBase sqlbuilder,
122
            List<FeatureAttributeDescriptor> columns,
123
            List<String> extraColumnNames
124
    ) {
125
        double tolerance = -1; //query.getScale(); 
126
        ExpressionBuilder expbuilder = sqlbuilder.expression();
127
        SelectBuilder select = sqlbuilder.select();
128

    
129
        Map<String, EditableFeatureAttributeDescriptor> allExtraColumns = new HashMap<>();
130
        for (EditableFeatureAttributeDescriptor column : this.setType.getExtraColumns().getColumns()) {
131
            allExtraColumns.put(column.getName(), column);
132
        }
133
        if(query != null){
134
            for (EditableFeatureAttributeDescriptor column : this.query.getExtraColumn().getColumns()) {
135
                allExtraColumns.put(column.getName(), column);
136
            }
137
        }
138

    
139
        List<String> primaryKeys = new ArrayList<>();
140
        for (FeatureAttributeDescriptor attr : storeType.getPrimaryKey()) {
141
            primaryKeys.add(attr.getName());
142
        }
143
        List<String> forcedColumns = new ArrayList<>(primaryKeys);
144

    
145
        String[] constantsAttributeNames = null;
146
        if (query != null && query.hasConstantsAttributeNames()) {
147
            constantsAttributeNames = query.getConstantsAttributeNames();
148
        }
149
        ArrayList<ExpressionBuilder.Value> valuesToRemoveFeatureType = new ArrayList<>();
150
        for (FeatureAttributeDescriptor attr : setType) {
151
            if (attr.isComputed()) {
152
//              if(StringUtils.isNotBlank(System.getenv("ENABLE_COMPUTED_SQL_ATTR"))) { 
153
                if (attr.getRelationType() == DynField.RELATION_TYPE_NONE) {
154
                    FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
155
                    if (attrEmulator instanceof FeatureAttributeEmulatorExpression) {
156
                        FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
157
                        Expression exp = x.getExpression();
158

    
159
                        if (query != null && query.hasGroupByColumns()) {
160
                            String aggregate = query.getAggregate(this.table.getTable(), attr.getName());
161
                            if (this.query.isAGroupByColumn(attr.getName())) {
162
                                if (!select.has_column(attr.getName())) {
163
                                    select.column().value(exp.getCode().toValue()).as(attr.getName());
164
                                }
165
                                if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
166
                                    extraColumnNames.add(attr.getName());
167
                                }
168
                            } else if (aggregate == null) {
169
                                select.column().value(expbuilder.constant(null)).as(attr.getName());
170
                            } else {
171
                                String fn = this.query.getAggregateFunctions().get(attr.getName());
172
                                ExpressionBuilder.Function aggregateExp = expbuilder.function(fn, exp.getCode().toValue());
173
                                if (!select.has_column(attr.getName())) {
174
                                    select.column().value(aggregateExp).as(attr.getName());
175
                                }
176
                                if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
177
                                    extraColumnNames.add(attr.getName());
178
                                }
179
                            }
180
                        } else {
181
                            if (exp != null && !exp.isEmpty() && this.helper.supportExpression(setType, exp.getPhrase())) {
182
                                Code code = exp.getCode();
183
                                select.column()
184
                                        .value(code.toValue(expbuilder))
185
                                        .as(attr.getName());
186
                                if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
187
                                    extraColumnNames.add(attr.getName());
188
                                }
189
                            }
190

    
191
                        }
192
                    }
193
                }
194
//              }
195
                continue;
196
            }
197
            if (ArrayUtils.contains(constantsAttributeNames, attr.getName())) {
198
                continue;
199
            }
200
            if (attr.isPrimaryKey()) {
201
                forcedColumns.remove(attr.getName());
202
            }
203
            if (query != null && query.hasGroupByColumns()) {
204
                String aggregate = query.getAggregate(this.table.getTable(), attr.getName());
205
                if (this.query.isAGroupByColumn(attr.getName())) {
206
                    select.column().name(attr.getName());
207
                } else if (aggregate == null) {
208
                    select.column().value(expbuilder.constant(null)).as(attr.getName());
209
                } else {
210
                    select.column()
211
                            .value(ExpressionUtils.compile(aggregate).toValue(expbuilder))
212
                            .as(attr.getName());
213
                }
214
            } else {
215
                if (attr.getType() == DataTypes.GEOMETRY) {
216
                    select.column().name(attr.getName()).as_geometry();
217
                    //                if( tolerance<=0 || !sqlbuilder.getConfig().has_functionality(Config.ST_Simplify)) {
218
                    //                    select.column().name(attr.getName()).as_geometry();
219
                    //                } else {
220
                    //                    select.column().value(
221
                    //                        sqlbuilder.ST_Simplify( 
222
                    //                            sqlbuilder.column(attr.getName()),
223
                    //                            sqlbuilder.constant(tolerance)
224
                    //                        )
225
                    //                    ).as_geometry();
226
                    //                }
227
                } else {
228
                    select.column().name(attr.getName());
229
                }
230
            }
231
            columns.add(attr);
232
        }
233

    
234
        if (query != null && query.hasGroupByColumns()) {
235
            for (Map.Entry<String, String> entry : query.getAggregateFunctions().entrySet()) {
236
                Expression exp;
237
                FeatureAttributeDescriptor attr = allExtraColumns.get(entry.getKey());
238

    
239
                if (attr == null) {
240
                    attr = this.setType.getAttributeDescriptorFromAll(entry.getKey());
241

    
242
                    if (attr == null) {
243
                        exp = ExpressionUtils.createExpression(entry.getKey());
244
                        Code code = exp.getCode();
245
                        if (!(code instanceof Code.Callable)) {
246
                            throw new RuntimeException("Not able to use aggregate function with this expression(1): " + entry.getKey());
247
                        }
248
                        Code.Callable callable = (Code.Callable) code;
249
                        if (!callable.name().equalsIgnoreCase(FUNCTION_LET)) {
250
                            throw new RuntimeException("Not able to use aggregate function with this expression(2): " + entry.getKey());
251
                        }
252
                        String name = ((Code.Identifier) callable.parameters().get(0)).name();
253
                        ExpressionBuilder.Value aggregate = callable.parameters().get(1).toValue();
254

    
255
                        ExpressionBuilder.Function aggregateExp = expbuilder.function(entry.getValue(), aggregate);
256

    
257
                        select.remove_column(name);
258
                        select.column().value(aggregateExp).as(name);
259
                    }
260
                } else {
261
                    exp = ((FeatureAttributeEmulatorExpression) attr.getFeatureAttributeEmulator()).getExpression();
262
                    ExpressionBuilder.Function aggregateExp = expbuilder.function(entry.getValue(), exp.getCode().toValue());
263
                    if (!select.has_column(attr.getName())) {
264
                        select.column().value(aggregateExp).as(attr.getName());
265
                    }
266
                    if (extraColumnNames != null && !extraColumnNames.contains(attr.getName())) {
267
                        extraColumnNames.add(attr.getName());
268
                    }
269
                }
270
            }
271
            for (String attrName : query.getGroupByColumns()) {
272
                if (allExtraColumns.get(attrName) != null) { //from setType and query
273
                    EditableFeatureAttributeDescriptor attr = allExtraColumns.get(attrName);
274
                    ExpressionBuilder.Variable col = expbuilder.column(attrName);
275
                    select.group_by(col);
276
                    Expression exp = ((FeatureAttributeEmulatorExpression) attr.getFeatureAttributeEmulator()).getExpression();
277
                    if (!select.has_column(attrName)) {
278
                        select.column().value(exp.getCode().toValue()).as(attrName);
279
                    }
280
                    if (extraColumnNames!=null && !extraColumnNames.contains(attr.getName())) {
281
                        extraColumnNames.add(attrName);
282
                    }
283
                } else if (setType.get(attrName) != null && setType.getAttributeDescriptor(attrName).isComputed()) {
284
                    FeatureAttributeDescriptor attr = setType.getAttributeDescriptor(attrName);
285
                    ExpressionBuilder.Variable col = expbuilder.column(attrName);
286
                    select.group_by(col);
287
                    Expression exp = ((FeatureAttributeEmulatorExpression) attr.getFeatureAttributeEmulator()).getExpression();
288
                    if (!select.has_column(attrName)) {
289
                        select.column().value(exp.getCode().toValue()).as(attrName);
290
                    }
291
                    if (extraColumnNames!=null && !extraColumnNames.contains(attr.getName())) {
292
                        extraColumnNames.add(attrName);
293
                    }
294
                } else if (setType.get(attrName) == null) {
295
                    try {
296
                        Code code = ExpressionUtils.compile(attrName);
297
                        select.group_by(code.toValue());
298
                    } catch (Exception ex) {
299
                        throw new RuntimeException("Not able to create column by expression in groupby query", ex);
300
                    }
301
                } else {
302
                    ExpressionBuilder.Function atrcolumn = expbuilder.getattr(this.table.getTable(), attrName);
303
                    select.group_by(atrcolumn);
304
                }
305
            }
306
        } else {
307
            for (String attrName : forcedColumns) {
308
                select.column().name(attrName);
309
                columns.add(setType.getAttributeDescriptor(attrName));
310
            }
311
            if (this.query != null) {
312
                FeatureExtraColumns extraColumns = this.query.getExtraColumn();
313
                if (extraColumns != null && !extraColumns.isEmpty()) {
314
                    for (EditableFeatureAttributeDescriptor attr : extraColumns.getColumns()) {
315
                        if (!attr.isComputed()) {
316
                            continue;
317
                        }
318
                        FeatureAttributeEmulator attrEmulator = attr.getFeatureAttributeEmulator();
319
                        if (attrEmulator instanceof FeatureAttributeEmulatorExpression) {
320
                            FeatureAttributeEmulatorExpression x = (FeatureAttributeEmulatorExpression) attrEmulator;
321
                            Expression exp = x.getExpression();
322
                            if (exp != null && !exp.isEmpty() && this.helper.supportExpression(setType, exp.getPhrase())) {
323
                                Code code = exp.getCode();
324
                                select.column()
325
                                        .value(code.toValue(expbuilder))
326
                                        .as(attr.getName());
327
                                if (extraColumnNames!=null && !extraColumnNames.contains(attr.getName())) {
328
                                    extraColumnNames.add(attr.getName());
329
                                }
330
                            }
331
                        }
332
                    }
333
                }
334
            }
335
        }
336

    
337
        select.from().table()
338
                .database(this.table.getDatabase())
339
                .schema(this.table.getSchema())
340
                .name(this.table.getTable());
341
        select.from().subquery(this.table.getSubquery());
342

    
343
        Evaluator filter = query == null ? null : query.getFilter();
344
        if (filter != null) {
345
            String sqlfilter = filter.getSQL();
346
            if (!StringUtils.isEmpty(sqlfilter)) {
347
                if (this.helper.supportFilter(this.storeType, filter)) {
348
                    select.where().set(expbuilder.toValue(sqlfilter));
349
                }
350
            }
351
        }
352
        if (!StringUtils.isEmpty(baseFilter)) {
353
            select.where().and(expbuilder.toValue(baseFilter));
354
        }
355

    
356
        FeatureQueryOrder order = query == null ? null : query.getOrder();
357
        if (order != null) {
358
            for (FeatureQueryOrderMember member : order.members()) {
359
                String attrName = member.getAttributeName();
360
                if (member.hasEvaluator()) {
361
                    String sqlorder = member.getEvaluator().getSQL();
362
                    select.order_by()
363
                            .value(expbuilder.toValue(sqlorder))
364
                            .ascending(member.getAscending());
365
                } else {
366
                    if (allExtraColumns.get(attrName) != null) {
367
                        Expression exp = ((FeatureAttributeEmulatorExpression) allExtraColumns.get(attrName).getFeatureAttributeEmulator()).getExpression();
368
                        if (!select.has_column(attrName)) {
369
                            select.column().value(exp.getCode().toValue()).as(attrName);
370
                        }
371
                        if (extraColumnNames!=null && !extraColumnNames.contains(attrName)) {
372
                            extraColumnNames.add(attrName);
373
                        }
374
                    } else if (setType.get(attrName) != null && setType.getAttributeDescriptor(attrName).isComputed()) {
375
                        Expression exp = ((FeatureAttributeEmulatorExpression) setType.getAttributeDescriptor(attrName).getFeatureAttributeEmulator()).getExpression();
376
                        if (!select.has_column(attrName)) {
377
                            select.column().value(exp.getCode().toValue()).as(attrName);
378
                        }
379
                        if (extraColumnNames!=null && !extraColumnNames.contains(attrName)) {
380
                            extraColumnNames.add(attrName);
381
                        }
382
                    }
383
                                        ExpressionBuilder.Variable col = expbuilder.column(attrName);
384
                                        
385
                                        // En el groupBy no queremos que se sustituya el nombre del campo calculado
386
                                        // por su expresion. Se encarga el formater y lo evitamos quitandole el ftype
387
                                        // al value.
388
                                        valuesToRemoveFeatureType.add(col);
389
                                        select.order_by().value(col).ascending(member.getAscending());
390
//                    select.order_by()
391
//                            .column(member.getAttributeName())
392
//                            .ascending(member.getAscending());
393
                }
394
            }
395
        }
396

    
397
        if (!StringUtils.isEmpty(baseOrder)) {
398
            select.order_by().custom(baseOrder);
399
        }
400

    
401
        if (select.has_group_by()) { // && isPaginated()) {
402
            // Cuando paginamos debemos ordenar por las columnas del groupby.
403
            // Ordenamos siempre para obtener el mismo resultado cuando paginamos
404
            // y no paginamos.
405
            for (ExpressionBuilder.Value group : select.getGroups()) {
406
                if (select.getOrderBy(group) == null) {
407
                    ExpressionBuilder.Value v = (ExpressionBuilder.Value) CloneableUtils.cloneQuietly(group);
408
                    select.order_by().value(v).ascending();
409
                    valuesToRemoveFeatureType.add(v);
410
                }
411
            }
412
        }
413
        
414
        if (primaryKeys.isEmpty()) {
415
            // Muy probablemente si no tiene pk sea una vista, asi que 
416
            // pasaremos de ordenar y esperemos que la vista este ya ordenada.
417
            select.disable_check_order_and_offset();
418
        } else {
419
            // Siempre ordenamos por la clave primaria
420
            for (String attrName : primaryKeys) {
421
                if (select.getOrderBy(attrName) == null) {
422
                    select.order_by().column(attrName).ascending();
423
                }
424
            }
425
        }
426

    
427
        if (limit > 0) {
428
            select.limit(limit);
429
        } else {
430
            select.limit(query == null ? null : query.getLimit());
431
        }
432
        if (offset > 0) {
433
            select.offset(offset);
434
        }
435
        sqlbuilder.setProperties(
436
                null,
437
                PROP_FEATURE_TYPE, this.storeType,
438
                PROP_TABLE, table,
439
                PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
440
                PROP_JDBCHELPER, this.helper,
441
                PROP_QUERY, this.query
442
        );
443
        for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
444
            value.setProperty(PROP_FEATURE_TYPE, null);
445
        }
446
        this.helper.expandCalculedColumns(sqlbuilder);
447
        this.helper.processSpecialFunctions(sqlbuilder, storeType, extraColumnNames);
448
        String sql = sqlbuilder.toString();
449
        return sql;
450
    }
451
    
452
    private boolean isPaginated() {
453
        // No tengo claro que (offset==0 && limit>0) sea lo mas correcto,
454
        // Pero cuando se va a paginar y se pide la primera pagina offset es
455
        // 0 y limit>0, y si no ordenamos ya esa primera pagina los resultados
456
        // que se obtienen no son correctos, ya que la primera pagina se saca
457
        // sin ordenar y el resto ordenadas.
458
        // Probablemente deberiamos tener alguna otra forma de detectar que
459
        // estamos paginanado ya que asi no distinguimo si solo queremos 
460
        // obtener los primeros elementos sin importarnos su orden.
461
        return (offset > 0 || (offset == 0 && limit > 0));
462
    }
463

    
464
    public ResultSetEntry createResultSet() throws DataException {
465
        List<FeatureAttributeDescriptor> columns = new ArrayList<>();
466
        List<String> extraColumnNames = new ArrayList<>();
467

    
468
        JDBCSQLBuilderBase sqlbuilder = createSQLBuilder();
469
        String sql = this.getSQL(sqlbuilder, columns, extraColumnNames);
470

    
471
        ResultSetEntry resultSetEntry = this.helper.getResulSetControler().create(
472
                sql, fetchSize,
473
                columns.toArray(new FeatureAttributeDescriptor[columns.size()]),
474
                extraColumnNames.toArray(new String[extraColumnNames.size()])
475
        );
476
        return resultSetEntry;
477
    }
478

    
479
}