Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.impl / src / main / java / org / gvsig / expressionevaluator / impl / function / dataaccess / SelectFunction.java @ 47174

History | View | Annotate | Download (22.4 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.expressionevaluator.impl.function.dataaccess;
25

    
26
import java.util.Iterator;
27
import java.util.List;
28
import java.util.function.Function;
29
import java.util.function.Predicate;
30
import org.apache.commons.lang3.Range;
31
import org.apache.commons.lang3.StringUtils;
32
import org.gvsig.expressionevaluator.Code;
33
import org.gvsig.expressionevaluator.Code.Callable;
34
import org.gvsig.expressionevaluator.CodeBuilder;
35
import org.gvsig.expressionevaluator.Codes;
36
import org.gvsig.expressionevaluator.Expression;
37
import org.gvsig.expressionevaluator.ExpressionBuilder;
38
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_LET;
39
import org.gvsig.expressionevaluator.ExpressionEvaluator;
40
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
41
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
42
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
43
import org.gvsig.expressionevaluator.ExpressionUtils;
44
import org.gvsig.expressionevaluator.Formatter;
45
import org.gvsig.expressionevaluator.Interpreter;
46
import org.gvsig.expressionevaluator.MutableSymbolTable;
47
import org.gvsig.expressionevaluator.Optimizer;
48
import org.gvsig.expressionevaluator.impl.DALFunctions;
49
import org.gvsig.fmap.dal.DALLocator;
50
import org.gvsig.fmap.dal.DataManager;
51
import static org.gvsig.fmap.dal.DataManager.FUNCTION_SELECT;
52
import org.gvsig.fmap.dal.SQLBuilder;
53
import static org.gvsig.fmap.dal.SQLBuilder.PROP_FEATURE_TYPE;
54
import static org.gvsig.fmap.dal.SQLBuilder.PROP_SQLBUILDER;
55
import static org.gvsig.fmap.dal.SQLBuilder.PROP_TABLE;
56
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
57
import org.gvsig.fmap.dal.feature.Feature;
58
import org.gvsig.fmap.dal.feature.FeatureQuery;
59
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
60
import org.gvsig.fmap.dal.feature.FeatureStore;
61
import org.gvsig.fmap.dal.feature.FeatureType;
62
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureQueryOrder;
63
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
64
import org.gvsig.fmap.dal.impl.expressionevaluator.DefaultFeatureExpressionEvaluator;
65
import org.gvsig.timesupport.DataTypes;
66
import org.gvsig.tools.dispose.DisposeUtils;
67
import org.gvsig.tools.evaluator.EvaluatorData;
68
import org.gvsig.tools.evaluator.EvaluatorException;
69
import org.gvsig.tools.util.FilteredIterator;
70
import org.gvsig.tools.util.LimitIterator;
71

    
72
/**
73
 *
74
 * @author jjdelcerro
75
 */
76
@SuppressWarnings("UseSpecificCatch")
77
public class SelectFunction
78
        extends AbstractSelectFunction
79
        implements Optimizer.FunctionOptimizer {
80
      
81
    public static final int MODE_FROM_STORE = 0;
82
    public static final int MODE_FROM_SELECTION = 1;
83
    public static final int MODE_FROM_SELECTION_IF_NOT_EMPTY = 2;
84
    
85
    public static final String PROP_FEATURETYPESUPPLIER = "FEATURETYPESUPPLIER";
86

    
87
    private static final int COLUMNS = 0;
88
    private static final int TABLE = 1;
89
    private static final int WHERE = 2;
90
    private static final int ORDER = 3;
91
    private static final int ORDER_MODE = 4;
92
    private static final int LIMIT = 5;
93
    private static final int INTOVAR = 6;
94
    private static final int MODE_FROM = 7;
95
    
96

    
97
    public SelectFunction() {
98
        super(DALFunctions.GROUP_DATA_ACCESS,
99
                FUNCTION_SELECT,
100
                Range.between(6,8),
101
                "Returns a list of features of the table by applying the filter, order and limit indicated.\n"
102
                + "The syntax is:\n\n"
103
                + "SELECT * FROM table WHERE boolean_expression ORDER BY order_column LIMIT limit;\n\n"
104
                + "Indicate a filter expression with WHERE, an order or LIMIT is optional.\n"
105
                + "You can use an asterisk or enter the column names you want to retrieve separated by commas.\n"
106
                + "The SELECT statement must always end with a semicolon.",
107
                "SELECT * FROM table WHERE boolean_expression ORDER BY order_column LIMIT limit;",
108
                new String[]{
109
                    "column_names/asterisk - Names of the columns table to retrieve.",
110
                    "table_name - Name of the table",
111
                    "filter - boolean expression to apply as filter",
112
                    "order_column - the order used to retrieve the features. It is a list of column names separated by a comma. The column name can optionally be followed by ASC or DESC to indicate whether the order should be ascending or descending.",
113
                    "limit - Maximum number of features to return"
114
                },
115
                "List",
116
                true
117
        );
118
    }
119

    
120
    @Override
121
    public boolean isHidden() {
122
        return false;
123
    }
124

    
125
    @Override
126
    public boolean allowConstantFolding() {
127
        return false;
128
    }
129

    
130
    @Override
131
    public boolean useArgumentsInsteadObjects() {
132
        return true;
133
    }
134

    
135
    @Override
136
    public Object call(Interpreter interpreter, Object[] args) throws Exception {
137
        throw new UnsupportedOperationException();
138
    }
139

    
140
    protected Callable getColumnsCode(Codes args) {
141
        Code code = args.get(COLUMNS);
142
        if( code == null ) {
143
            return null;
144
        }
145
        CodeBuilder builder;
146
        Callable callable;
147
        switch (code.code()) {
148
            case Code.CONSTANT:
149
                if (((Code.Constant) code).value() != null) {
150
                    throw new ExpressionRuntimeException("Null expected in argument COLUMNS of function '" + this.name() + "'.");
151
                }
152
                return null;
153

    
154
            case Code.IDENTIFIER:
155
                builder = ExpressionUtils.createCodeBuilder();
156
                return builder.block(code);
157

    
158
            case Code.CALLABLE:
159
                callable = (Callable) code;
160
                if( callable.parameters().isEmpty() ) {
161
                    return null;
162
                }
163
                if (StringUtils.equalsIgnoreCase(callable.name(), "BLOCK")) {
164
                    return callable;
165
                }
166
                if (StringUtils.equalsIgnoreCase(callable.name(), "TUPLE")) {
167
                    return callable;
168
                }
169
                if (StringUtils.equalsIgnoreCase(callable.name(), "GETATTR")) {
170
                    builder = ExpressionUtils.createCodeBuilder();
171
                    return builder.block(code);
172
                }
173
                throw new ExpressionRuntimeException("BLOCK, TUPLE expected in argument COLUMNS of function '" + this.name() + "'.");
174

    
175

    
176
            default:
177
                throw new ExpressionRuntimeException("BLOCK or NULL expected in argument COLUMNS of function '" + this.name() + "'.");
178
        }
179
    }
180

    
181
    @Override
182
    public Object call(Interpreter interpreter, Codes args) throws Exception {
183

    
184
        String storeName = this.getIdentifier(args, TABLE);
185
        Callable columns = getColumnsCode(args);
186
        Code where = this.getWhereCode(args, WHERE);
187
        Number limit = (Number) getObject(interpreter, args, LIMIT);
188
        
189
        int mode_from = MODE_FROM_STORE;
190
        if( args.size() > MODE_FROM ) {
191
            mode_from = this.getInt(interpreter, args, MODE_FROM);
192
        }
193

    
194
        Callable order = getTupleOrNull(args, ORDER);
195
        Callable order_mode = getTupleOrNull(args, ORDER_MODE);
196
        FeatureQueryOrder queryOrder = null;
197
        FeatureStore featureStore;
198
        try {
199
            featureStore = this.getFeatureStore(storeName);
200
            if (featureStore == null) {
201
                throw new ExpressionRuntimeException("Cant locate the feature store '" + storeName + "' in function '" + this.name() + "'.");
202
            }
203
            if (order != null || order_mode != null) {
204
                for (int n = 0; n < order.parameters().size(); n++) {
205
                    if (queryOrder == null) {
206
                        queryOrder = new DefaultFeatureQueryOrder();
207
                    }
208
                    Boolean mode = (Boolean) ((Code.Constant)order_mode.parameters().get(n)).value();
209
                    Code memberCode = order.parameters().get(n);
210
                    if(memberCode.code() == Code.IDENTIFIER) {
211
                        queryOrder.add(((Code.Identifier)memberCode).name(), mode);
212
                    } else {
213
                        Code memberCode2 = replaceLocalVariables(interpreter, memberCode, featureStore.getDefaultFeatureTypeQuietly());
214
                        Expression exp = ExpressionUtils.createExpression(memberCode2.toString());
215
                        queryOrder.add(exp, mode);
216
                    }
217
                }
218
            }
219
            String intovar = null;
220
            if( args.size()>INTOVAR ) {
221
                intovar = this.getIdentifier(args, INTOVAR);
222
            }
223
            FeatureQuery query = featureStore.createFeatureQuery();
224
            if (where != null) {
225
                Code where2 = removeOuterTablesReferences(interpreter, where, featureStore.getDefaultFeatureTypeQuietly());
226
                ExpressionEvaluator filter = new DefaultFeatureExpressionEvaluator(where2.toString());
227
                filter.toSymbolTable().addSymbolTable(interpreter.getSymbolTable());
228
                query.addFilter(filter);
229
            }
230
            if (queryOrder != null) {
231
                query.getOrder().copyFrom(queryOrder);
232
            }
233
            if (limit != null) {
234
                query.setLimit(limit.longValue());
235
            }
236
            if( columns!=null ) {
237
                for (Code column : columns.parameters()) {
238
                    if( column instanceof Code.Identifier ) {
239
                        query.addAttributeName( ((Code.Identifier)column).name());
240
                    } else {
241
                        // TODO: Aqui habria que a?adir una columna calculada.
242
                        String name = "nose";
243
                        Code exp;
244
                        if( column instanceof Code.Callable && 
245
                                StringUtils.equals(((Code.Callable)column).name(),FUNCTION_LET) ) {
246
                            name = ((Code.Identifier)((Code.Callable)column).parameters().get(0)).name();
247
                            exp = ((Code.Callable)column).parameters().get(1);
248
                        } else {
249
                            exp = column;
250
                        }
251
                        EditableFeatureAttributeDescriptor fad = query.getExtraColumns().add(name, DataTypes.STRING);
252
                        fad.setFeatureAttributeEmulator(ExpressionUtils.createExpression(exp.toString()));
253
                    }
254
                }
255
            }
256
            
257
            switch(mode_from) {
258
                case MODE_FROM_SELECTION:
259
                    return select_from_selection(interpreter, featureStore, query, intovar);
260
                case MODE_FROM_SELECTION_IF_NOT_EMPTY:
261
                    return select_from_selection_if_not_empty(interpreter, featureStore, query, intovar);
262
                case MODE_FROM_STORE:
263
                default:
264
                    return select_from_store(interpreter, featureStore, query, intovar);
265
            }
266
        } catch (ExpressionRuntimeException ex) {
267
            throw ex;
268
        } catch (Exception ex) {
269
            throw new ExpressionRuntimeException("Problems calling '" + this.name() + "' function", ex);
270
        }
271
    }
272

    
273
    private Object select_from_selection(Interpreter interpreter, FeatureStore store, FeatureQuery query, String intovar) {
274
        try {
275
            if( store.isFeatureSelectionEmpty() ) {
276
                return null;
277
            }
278
            Iterable features = store.getFeatureSelection();
279
            if ( query.hasFilter() ) {
280
                final Iterable baseSelection = features;
281
                final ExpressionEvaluator evaluator = new DefaultFeatureExpressionEvaluator(
282
                        query.getExpressionFilter().toString()
283
                );
284
                evaluator.toSymbolTable().addSymbolTable(interpreter.getSymbolTable());
285
                final Predicate filter = (Predicate) (Object t) -> {
286
                    try {
287
                        return (boolean) evaluator.evaluate((EvaluatorData) t);
288
                    } catch (EvaluatorException ex1) {
289
                        throw new ExpressionRuntimeException("Can't evaluate expression for row.", ex1);
290
                    }
291
                };
292
                features = (Iterable) () -> new FilteredIterator(baseSelection.iterator(), filter);
293
            }
294
            if( query.hasLimit() ) {
295
                final Iterable baseSelection = features;
296
                features = (Iterable) () -> new LimitIterator(baseSelection.iterator(), query.getLimit());
297
            }
298
            if( intovar!=null ) {
299
                Feature f = null;
300
                Iterator<Feature> it = features.iterator();
301
                if( it.hasNext() ) {
302
                    f = it.next();
303
                }
304
                DisposeUtils.dispose(it);
305
                DisposeUtils.dispose(features);
306
                ((MutableSymbolTable)(interpreter.getSymbolTable())).setVar(intovar, f);
307
                return f;
308
            }
309
            return features;
310

    
311
        } catch (ExpressionRuntimeException ex) {
312
            throw ex;
313
        } catch (Exception ex) {
314
            throw new ExpressionRuntimeException("Problems calling '" + this.name() + "' function", ex);
315
        }
316
    }
317
    
318
    private Object select_from_store(Interpreter interpreter, FeatureStore store, FeatureQuery query, String intovar) throws Exception {
319
        // FIXME: add columns to query.addAttributeName() 
320
        query.retrievesAllAttributes();
321
        if( intovar!=null ) {
322
            Feature f = store.findFirst(query);
323
            ((MutableSymbolTable)(interpreter.getSymbolTable())).setVar(intovar, f);
324
            return f;
325
        }
326
        List<Feature> features = store.getFeatures(query);
327
        return features;
328
    }
329
    
330
    private Object select_from_selection_if_not_empty(Interpreter interpreter, FeatureStore store, FeatureQuery query, String intovar) throws Exception {
331
        if( store.isFeatureSelectionEmpty() ) {
332
          return select_from_store(interpreter, store, query, intovar);
333
        } 
334
        return select_from_selection(interpreter, store, query, intovar);
335
    }
336
    
337
    @Override
338
    public Code optimize(Optimizer optimizer, Callable caller) {
339
        return caller; // Don't optimize SELECT
340
    }
341
    
342
    public ExpressionBuilder.Value toValue(ExpressionBuilder builder, Codes args) {
343
        try {
344
            SQLBuilder sqlBuilder = (SQLBuilder) builder.getProperty(PROP_SQLBUILDER);
345
            if (sqlBuilder == null) {
346
                return super.toValue(builder, args);
347
            }
348
            Function<String, FeatureType> featureTypeSupplier = (Function<String, FeatureType>) builder.getProperty(PROP_FEATURETYPESUPPLIER);
349
            if (featureTypeSupplier == null) {
350
                featureTypeSupplier = (String tableName) -> {
351
                    DataManager dataManager = DALLocator.getDataManager();
352
                    FeatureType featureType = dataManager.getStoresRepository().getFeatureType(tableName);
353
                    return featureType;
354
                };
355
            }
356
            FeatureType featureType = null;
357
            SQLBuilder.SelectBuilder select = sqlBuilder.createSelectBuilder();
358
            String builderTableName = (String) builder.getProperty(SQLBuilder.PROP_TABLENAME);
359

    
360
            Code tableCode = args.get(TABLE);
361
            Callable columns = getColumnsCode(args);
362
            Code where = this.getWhereCode(args, WHERE);
363
            Callable order = getTupleOrNull(args, ORDER);
364
            Callable order_mode = getTupleOrNull(args, ORDER_MODE);
365
            Code limit = args.get(LIMIT);
366
            int mode_from = MODE_FROM_STORE;
367
            if( args.size() > MODE_FROM ) {
368
                Code codeModeFrom = args.get(MODE_FROM);
369
                mode_from = (Integer)((Code.Constant)codeModeFrom).value();
370
            }
371
            String intovar = null;
372
            if( args.size()>INTOVAR && args.get(INTOVAR)!=null ) {
373
                intovar = this.getIdentifier(args, INTOVAR);
374
            }
375
            
376
            if (tableCode != null) {
377
                String tableName = null;
378
                String tableNameHE = null;
379
                if( isHostExpression(tableCode) ) {
380
                    tableNameHE = tableCode.toString();
381
                } else {
382
                    tableName = this.getIdentifier(args, TABLE);
383
                }
384
                switch (mode_from) {
385
                    case MODE_FROM_SELECTION: {
386
                            SQLBuilder.FromBuilder from = select.from();
387
                            if( tableNameHE == null ) {
388
                                select.from().custom("SELECTION OF " + from.table().name(tableName).toString());    
389
                            } else {
390
                                select.from().custom("SELECTION OF " + tableNameHE);    
391
                            }
392
                        }
393
                        break;
394
                    case MODE_FROM_SELECTION_IF_NOT_EMPTY: {
395
                            SQLBuilder.FromBuilder from = select.from();
396
                            if( tableNameHE == null ) {
397
                                select.from().custom("SELECTION IF NOT EMPTY OF " + from.table().name(tableName).toString());    
398
                            } else {
399
                                select.from().custom("SELECTION IF NOT EMPTY OF " + tableNameHE);    
400
                            }
401
                        }
402
                        break;
403
                    case MODE_FROM_STORE:
404
                        if( tableNameHE == null ) {
405
                            select.from().table().name(tableName);
406
                        } else {
407
                            select.from().custom(tableNameHE);    
408
                        }
409
                        break;
410
                }
411
            }
412

    
413
            SQLBuilder.TableNameBuilder table = select.from().table();
414
            String tableName = table.getName();
415
            if (columns == null || columns.parameters().isEmpty()){
416
                if( intovar==null ) {
417
                    select.column().all();
418
                } else {
419
                    select.column().value(builder.custom("* INTO "+intovar));
420
                }
421
            } else {
422
                for (Code column : columns.parameters()) {
423
                    if( column == null ) {
424
                        select.column().value(builder.constant(null));
425
                    } else if (column instanceof Code.Identifier) {
426
                        String columnName = ((Code.Identifier) column).name();
427
                        if(featureType == null) {
428
                            if(StringUtils.equalsIgnoreCase(builderTableName, tableName)){
429
                                featureType = (FeatureType) builder.getProperty(SQLBuilder.PROP_FEATURE_TYPE);
430
                            } else {
431
                                featureType = featureTypeSupplier.apply(tableName);
432
                            }
433
                        }
434
                        if(featureType == null){
435
                            select.column().name(select.from().table(),columnName);
436
                        } else if(featureType.get(columnName) != null) {
437
                            select.column().name(select.from().table(),columnName);
438
                        } else {
439
                            select.column().name(columnName).table(null);
440
                        }
441
                    } else {
442
                        select.column().value(column.toValue(builder));
443
                    }
444
                }
445
            }
446

    
447
            if (where != null) {
448
                ExpressionBuilder.Value value = where.toValue(builder);
449
                select.where().value(value);
450
                sqlBuilder.setProperties(value, null, SQLBuilder.PROP_ADD_TABLE_NAME_TO_COLUMNS, true);
451
            }
452

    
453
            if (limit != null) {
454
                Object value = ((Code.Constant) limit).value();
455
                if(value != null){
456
                    select.limit(((Number) value).longValue());
457
                }
458
            }
459

    
460
            if (order != null || order_mode != null) {
461
                for (int n = 0; n < order.parameters().size(); n++) {
462
                    Code member = order.parameters().get(n);
463
                    Code.Constant mode = (Code.Constant) order_mode.parameters().get(n);
464
                    select.order_by().value(member.toValue(builder)).ascending((boolean) mode.value());
465
                }
466
            }
467
            if (featureType == null) {
468
                if (StringUtils.equalsIgnoreCase(builderTableName, tableName)) {
469
                    featureType = (FeatureType) builder.getProperty(SQLBuilder.PROP_FEATURE_TYPE);
470
                } else {
471
                    featureType = featureTypeSupplier.apply(tableName);
472
                }
473
            }
474

    
475
            sqlBuilder.setProperties(
476
                    select,
477
                    null,
478
                    PROP_FEATURE_TYPE, featureType,
479
                    PROP_TABLE, table
480
            );
481

    
482
            return builder.group(select);
483
        } catch (Exception ex) {
484
            return super.toValue(builder, args);
485
        }
486
    }
487

    
488
    @Override
489
    public String toString(Codes args, Formatter<Code> formatter) {
490
        SQLBuilderBase sqlbuilder = new SQLBuilderBase();
491
        ExpressionEvaluatorManager expressionManager = ExpressionEvaluatorLocator.getExpressionEvaluatorManager();
492
        ExpressionBuilder expressionBuilder = expressionManager.createExpressionBuilder();
493
        expressionBuilder.setProperty(PROP_SQLBUILDER, sqlbuilder);
494
        expressionBuilder.setProperty(PROP_FEATURETYPESUPPLIER, new Function<String, FeatureType>() {
495
            @Override
496
            public FeatureType apply(String t) {
497
                return null;
498
            }
499
        });
500
        
501
        ExpressionBuilder.Value values = this.toValue(expressionBuilder, args);
502
        
503
        return values.toString();
504
    }
505
    
506
}