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

History | View | Annotate | Download (23.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.jdbc2.spi.operations;
25

    
26
import java.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.util.ContainerUtils;
64

    
65
public class ResultSetForSetProviderOperation extends AbstractConnectionOperation {
66

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
396
        if (!StringUtils.isEmpty(baseOrder)) {
397
            select.order_by().custom(baseOrder);
398
        }
399
        //Si hay especificado un offset (se esta paginando) siempre deberemos ordenar por un campo con valores unicos.
400
        //Anadiremos la clave primaria siempre en este caso para asegurarnos de que el orden de los registros es siempre el mismo.
401
//        if (!select.has_order_by()) {
402
            // Si no tenemos order by comprobamos si lo necesitamos y lo a?adimos.
403
            if (offset > 0 || (offset == 0 && limit > 0)) {
404
                // No tengo claro que (offset==0 && limit>0) sea lo mas correcto,
405
                // Pero cuando se va a paginar y se pide la primera pagina offset es
406
                // 0 y limit>0, y si no ordenamos ya esa primera pagina los resultados
407
                // que se obtienen no son correctos, ya que la primera pagina se saca
408
                // sin ordenar y el resto ordenadas.
409
                // Probablemente deberiamos tener alguna otra forma de detectar que
410
                // estamos paginanado ya que asi no distinguimo si solo queremos 
411
                // obtener los primeros elementos sin importarnos su orden.
412
                if (select.has_group_by()) {
413
                    ExpressionBuilder.Value group = select.getGroups().get(0);
414
//                  if(!(group instanceof ExpressionBuilder.Function)) {
415
//                      expbuilder.getattr(this.table.getTable(), group.XXX);
416
//                  }
417
                    select.order_by().value(group).ascending();
418

    
419
                } else if (primaryKeys.isEmpty()) {
420
                    // Muy probablemente si no tiene pk sea una vista, asi que 
421
                    // pasaremos de ordenar y esperemos que la vista este ya ordenada.
422
                    select.disable_check_order_and_offset();
423
                } else {
424
                    for (String attrName : primaryKeys) {
425
                        // Se precisa indicar un orden para usar OFFSET.
426
                        if(select.getOrderBy(attrName)==null){
427
                            select.order_by().column(attrName).ascending();
428
                        }
429
                    }
430
                }
431
            }
432
//        }
433
        for (String attrName : primaryKeys) {
434
            if(select.getOrderBy(attrName)==null){
435
                select.order_by().column(attrName).ascending();
436
            }
437
        }
438
        if (limit > 0) {
439
            select.limit(limit);
440
        } else {
441
            select.limit(query == null ? null : query.getLimit());
442
        }
443
        if (offset > 0) {
444
            select.offset(offset);
445
        }
446
        sqlbuilder.setProperties(
447
                null,
448
                PROP_FEATURE_TYPE, this.storeType,
449
                PROP_TABLE, table,
450
                PROP_SYMBOLTABLE, this.query==null? null:this.query.getSymbolTable(),
451
                PROP_JDBCHELPER, this.helper,
452
                PROP_QUERY, this.query
453
        );
454
        for (ExpressionBuilder.Value value : valuesToRemoveFeatureType) {
455
            value.setProperty(PROP_FEATURE_TYPE, null);
456
        }
457
                this.helper.expandCalculedColumns(sqlbuilder);
458
        this.helper.processSpecialFunctions(sqlbuilder, storeType, extraColumnNames);
459
        String sql = sqlbuilder.toString();
460
        return sql;
461
    }
462

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

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

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

    
478
}