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

History | View | Annotate | Download (21 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 org.gvsig.expressionevaluator.ExpressionEvaluator;
39
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
40
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
41
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
42
import org.gvsig.expressionevaluator.ExpressionUtils;
43
import org.gvsig.expressionevaluator.Formatter;
44
import org.gvsig.expressionevaluator.Interpreter;
45
import org.gvsig.expressionevaluator.MutableSymbolTable;
46
import org.gvsig.expressionevaluator.Optimizer;
47
import org.gvsig.expressionevaluator.impl.DALFunctions;
48
import org.gvsig.fmap.dal.DALLocator;
49
import org.gvsig.fmap.dal.DataManager;
50
import static org.gvsig.fmap.dal.DataManager.FUNCTION_SELECT;
51
import org.gvsig.fmap.dal.SQLBuilder;
52
import static org.gvsig.fmap.dal.SQLBuilder.PROP_FEATURE_TYPE;
53
import static org.gvsig.fmap.dal.SQLBuilder.PROP_SQLBUILDER;
54
import static org.gvsig.fmap.dal.SQLBuilder.PROP_TABLE;
55
import org.gvsig.fmap.dal.feature.Feature;
56
import org.gvsig.fmap.dal.feature.FeatureQuery;
57
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
58
import org.gvsig.fmap.dal.feature.FeatureStore;
59
import org.gvsig.fmap.dal.feature.FeatureType;
60
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureQueryOrder;
61
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
62
import org.gvsig.fmap.dal.impl.expressionevaluator.DefaultFeatureExpressionEvaluator;
63
import org.gvsig.tools.dispose.DisposeUtils;
64
import org.gvsig.tools.evaluator.EvaluatorData;
65
import org.gvsig.tools.evaluator.EvaluatorException;
66
import org.gvsig.tools.util.FilteredIterator;
67
import org.gvsig.tools.util.LimitIterator;
68

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

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

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

    
117
    @Override
118
    public boolean isHidden() {
119
        return false;
120
    }
121

    
122
    @Override
123
    public boolean allowConstantFolding() {
124
        return false;
125
    }
126

    
127
    @Override
128
    public boolean useArgumentsInsteadObjects() {
129
        return true;
130
    }
131

    
132
    @Override
133
    public Object call(Interpreter interpreter, Object[] args) throws Exception {
134
        throw new UnsupportedOperationException();
135
    }
136

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

    
151
            case Code.IDENTIFIER:
152
                builder = ExpressionUtils.createCodeBuilder();
153
                return builder.block(code);
154

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

    
172

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

    
178
    @Override
179
    public Object call(Interpreter interpreter, Codes args) throws Exception {
180

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

    
191
        Callable order = getTupleOrNull(args, ORDER);
192
        Callable order_mode = getTupleOrNull(args, ORDER_MODE);
193
        FeatureQueryOrder queryOrder = null;
194
        FeatureStore featureStore;
195
        try {
196
            featureStore = this.getFeatureStore(storeName);
197
            if (featureStore == null) {
198
                throw new ExpressionRuntimeException("Cant locate the feature store '" + storeName + "' in function '" + this.name() + "'.");
199
            }
200
            if (order != null || order_mode != null) {
201
                for (int n = 0; n < order.parameters().size(); n++) {
202
                    if (queryOrder == null) {
203
                        queryOrder = new DefaultFeatureQueryOrder();
204
                    }
205
                    Boolean mode = (Boolean) ((Code.Constant)order_mode.parameters().get(n)).value();
206
                    Code memberCode = order.parameters().get(n);
207
                    if(memberCode.code() == Code.IDENTIFIER) {
208
                        queryOrder.add(((Code.Identifier)memberCode).name(), mode);
209
                    } else {
210
                        Code memberCode2 = replaceLocalVariables(interpreter, memberCode, featureStore.getDefaultFeatureTypeQuietly());
211
                        Expression exp = ExpressionUtils.createExpression(memberCode2.toString());
212
                        queryOrder.add(exp, mode);
213
                    }
214
                }
215
            }
216
            String intovar = null;
217
            if( args.size()>INTOVAR ) {
218
                intovar = this.getIdentifier(args, INTOVAR);
219
            }
220
            FeatureQuery query = featureStore.createFeatureQuery();
221
            if (where != null) {
222
                Code where2 = removeOuterTablesReferences(interpreter, where, featureStore.getDefaultFeatureTypeQuietly());
223
                ExpressionEvaluator filter = new DefaultFeatureExpressionEvaluator(where2.toString());
224
                filter.toSymbolTable().addSymbolTable(interpreter.getSymbolTable());
225
                query.addFilter(filter);
226
            }
227
            if (queryOrder != null) {
228
                query.getOrder().copyFrom(queryOrder);
229
            }
230
            if (limit != null) {
231
                query.setLimit(limit.longValue());
232
            }
233
            
234
            switch(mode_from) {
235
                case MODE_FROM_SELECTION:
236
                    return select_from_selection(interpreter, featureStore, query, intovar);
237
                case MODE_FROM_SELECTION_IF_NOT_EMPTY:
238
                    return select_from_selection_if_not_empty(interpreter, featureStore, query, intovar);
239
                case MODE_FROM_STORE:
240
                default:
241
                    return select_from_store(interpreter, featureStore, query, intovar);
242
            }
243
        } catch (ExpressionRuntimeException ex) {
244
            throw ex;
245
        } catch (Exception ex) {
246
            throw new ExpressionRuntimeException("Problems calling '" + this.name() + "' function", ex);
247
        }
248
    }
249

    
250
    private Object select_from_selection(Interpreter interpreter, FeatureStore store, FeatureQuery query, String intovar) {
251
        try {
252
            if( store.isFeatureSelectionEmpty() ) {
253
                return null;
254
            }
255
            Iterable features = store.getFeatureSelection();
256
            if ( query.hasFilter() ) {
257
                final Iterable baseSelection = features;
258
                final ExpressionEvaluator evaluator = new DefaultFeatureExpressionEvaluator(
259
                        query.getExpressionFilter().toString()
260
                );
261
                evaluator.toSymbolTable().addSymbolTable(interpreter.getSymbolTable());
262
                final Predicate filter = (Predicate) (Object t) -> {
263
                    try {
264
                        return (boolean) evaluator.evaluate((EvaluatorData) t);
265
                    } catch (EvaluatorException ex1) {
266
                        throw new ExpressionRuntimeException("Can't evaluate expression for row.", ex1);
267
                    }
268
                };
269
                features = (Iterable) () -> new FilteredIterator(baseSelection.iterator(), filter);
270
            }
271
            if( query.hasLimit() ) {
272
                final Iterable baseSelection = features;
273
                features = (Iterable) () -> new LimitIterator(baseSelection.iterator(), query.getLimit());
274
            }
275
            if( intovar!=null ) {
276
                Feature f = null;
277
                Iterator<Feature> it = features.iterator();
278
                if( it.hasNext() ) {
279
                    f = it.next();
280
                }
281
                DisposeUtils.dispose(it);
282
                DisposeUtils.dispose(features);
283
                ((MutableSymbolTable)(interpreter.getSymbolTable())).setVar(intovar, f);
284
                return f;
285
            }
286
            return features;
287

    
288
        } catch (ExpressionRuntimeException ex) {
289
            throw ex;
290
        } catch (Exception ex) {
291
            throw new ExpressionRuntimeException("Problems calling '" + this.name() + "' function", ex);
292
        }
293
    }
294
    
295
    private Object select_from_store(Interpreter interpreter, FeatureStore store, FeatureQuery query, String intovar) throws Exception {
296
        // FIXME: add columns to query.addAttributeName() 
297
        query.retrievesAllAttributes();
298
        if( intovar!=null ) {
299
            Feature f = store.findFirst(query);
300
            ((MutableSymbolTable)(interpreter.getSymbolTable())).setVar(intovar, f);
301
            return f;
302
        }
303
        List<Feature> features = store.getFeatures(query);
304
        return features;
305
    }
306
    
307
    private Object select_from_selection_if_not_empty(Interpreter interpreter, FeatureStore store, FeatureQuery query, String intovar) throws Exception {
308
        if( store.isFeatureSelectionEmpty() ) {
309
          return select_from_store(interpreter, store, query, intovar);
310
        } 
311
        return select_from_selection(interpreter, store, query, intovar);
312
    }
313
    
314
    @Override
315
    public Code optimize(Optimizer optimizer, Callable caller) {
316
        return caller; // Don't optimize SELECT
317
    }
318
    
319
    public ExpressionBuilder.Value toValue(ExpressionBuilder builder, Codes args) {
320
        try {
321
            SQLBuilder sqlBuilder = (SQLBuilder) builder.getProperty(PROP_SQLBUILDER);
322
            if (sqlBuilder == null) {
323
                return super.toValue(builder, args);
324
            }
325
            Function<String, FeatureType> featureTypeSupplier = (Function<String, FeatureType>) builder.getProperty(PROP_FEATURETYPESUPPLIER);
326
            if (featureTypeSupplier == null) {
327
                featureTypeSupplier = (String tableName) -> {
328
                    DataManager dataManager = DALLocator.getDataManager();
329
                    FeatureType featureType = dataManager.getStoresRepository().getFeatureType(tableName);
330
                    return featureType;
331
                };
332
            }
333
            FeatureType featureType = null;
334
            SQLBuilder.SelectBuilder select = sqlBuilder.createSelectBuilder();
335
            String builderTableName = (String) builder.getProperty(SQLBuilder.PROP_TABLENAME);
336

    
337
            Code tableCode = args.get(TABLE);
338
            Callable columns = getColumnsCode(args);
339
            Code where = this.getWhereCode(args, WHERE);
340
            Callable order = getTupleOrNull(args, ORDER);
341
            Callable order_mode = getTupleOrNull(args, ORDER_MODE);
342
            Code limit = args.get(LIMIT);
343
            int mode_from = MODE_FROM_STORE;
344
            if( args.size() > MODE_FROM ) {
345
                Code codeModeFrom = args.get(MODE_FROM);
346
                mode_from = (Integer)((Code.Constant)codeModeFrom).value();
347
            }
348
            String intovar = null;
349
            if( args.size()>INTOVAR && args.get(INTOVAR)!=null ) {
350
                intovar = this.getIdentifier(args, INTOVAR);
351
            }
352
            
353
            if (tableCode != null) {
354
                String tableName = null;
355
                String tableNameHE = null;
356
                if( isHostExpression(tableCode) ) {
357
                    tableNameHE = tableCode.toString();
358
                } else {
359
                    tableName = this.getIdentifier(args, TABLE);
360
                }
361
                switch (mode_from) {
362
                    case MODE_FROM_SELECTION: {
363
                            SQLBuilder.FromBuilder from = select.from();
364
                            if( tableNameHE == null ) {
365
                                select.from().custom("SELECTION OF " + from.table().name(tableName).toString());    
366
                            } else {
367
                                select.from().custom("SELECTION OF " + tableNameHE);    
368
                            }
369
                        }
370
                        break;
371
                    case MODE_FROM_SELECTION_IF_NOT_EMPTY: {
372
                            SQLBuilder.FromBuilder from = select.from();
373
                            if( tableNameHE == null ) {
374
                                select.from().custom("SELECTION IF NOT EMPTY OF " + from.table().name(tableName).toString());    
375
                            } else {
376
                                select.from().custom("SELECTION IF NOT EMPTY OF " + tableNameHE);    
377
                            }
378
                        }
379
                        break;
380
                    case MODE_FROM_STORE:
381
                        if( tableNameHE == null ) {
382
                            select.from().table().name(tableName);
383
                        } else {
384
                            select.from().custom(tableNameHE);    
385
                        }
386
                        break;
387
                }
388
            }
389

    
390
            SQLBuilder.TableNameBuilder table = select.from().table();
391
            String tableName = table.getName();
392
            if (columns == null || columns.parameters().isEmpty()){
393
                if( intovar==null ) {
394
                    select.column().all();
395
                } else {
396
                    select.column().value(builder.custom("* INTO "+intovar));
397
                }
398
            } else {
399
                for (Code column : columns.parameters()) {
400
                    if (column instanceof Code.Identifier) {
401
                        String columnName = ((Code.Identifier) column).name();
402
                        if(featureType == null) {
403
                            if(StringUtils.equalsIgnoreCase(builderTableName, tableName)){
404
                                featureType = (FeatureType) builder.getProperty(SQLBuilder.PROP_FEATURE_TYPE);
405
                            } else {
406
                                featureType = featureTypeSupplier.apply(tableName);
407
                            }
408
                        }
409
                        if(featureType == null){
410
                            select.column().name(select.from().table(),columnName);
411
                        } else if(featureType.get(columnName) != null) {
412
                            select.column().name(select.from().table(),columnName);
413
                        } else {
414
                            select.column().name(columnName).table(null);
415
                        }
416
                    } else {
417
                        select.column().value(column.toValue(builder));
418
                    }
419
                }
420
            }
421

    
422
            if (where != null) {
423
                ExpressionBuilder.Value value = where.toValue(builder);
424
                select.where().value(value);
425
                sqlBuilder.setProperties(value, null, SQLBuilder.PROP_ADD_TABLE_NAME_TO_COLUMNS, true);
426
            }
427

    
428
            if (limit != null) {
429
                Object value = ((Code.Constant) limit).value();
430
                if(value != null){
431
                    select.limit(((Number) value).longValue());
432
                }
433
            }
434

    
435
            if (order != null || order_mode != null) {
436
                for (int n = 0; n < order.parameters().size(); n++) {
437
                    Code member = order.parameters().get(n);
438
                    Code.Constant mode = (Code.Constant) order_mode.parameters().get(n);
439
                    select.order_by().value(member.toValue(builder)).ascending((boolean) mode.value());
440
                }
441
            }
442
            if (featureType == null) {
443
                if (StringUtils.equalsIgnoreCase(builderTableName, tableName)) {
444
                    featureType = (FeatureType) builder.getProperty(SQLBuilder.PROP_FEATURE_TYPE);
445
                } else {
446
                    featureType = featureTypeSupplier.apply(tableName);
447
                }
448
            }
449

    
450
            sqlBuilder.setProperties(
451
                    select,
452
                    null,
453
                    PROP_FEATURE_TYPE, featureType,
454
                    PROP_TABLE, table
455
            );
456

    
457
            return builder.group(select);
458
        } catch (Exception ex) {
459
            return super.toValue(builder, args);
460
        }
461
    }
462

    
463
    @Override
464
    public String toString(Codes args, Formatter<Code> formatter) {
465
        SQLBuilderBase sqlbuilder = new SQLBuilderBase();
466
        ExpressionEvaluatorManager expressionManager = ExpressionEvaluatorLocator.getExpressionEvaluatorManager();
467
        ExpressionBuilder expressionBuilder = expressionManager.createExpressionBuilder();
468
        expressionBuilder.setProperty(PROP_SQLBUILDER, sqlbuilder);
469
        expressionBuilder.setProperty(PROP_FEATURETYPESUPPLIER, new Function<String, FeatureType>() {
470
            @Override
471
            public FeatureType apply(String t) {
472
                return null;
473
            }
474
        });
475
        
476
        ExpressionBuilder.Value values = this.toValue(expressionBuilder, args);
477
        
478
        return values.toString();
479
    }
480
    
481
}