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 / InsertIntoTableFunction.java @ 45366

History | View | Annotate | Download (11.3 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.ArrayList;
27
import java.util.List;
28
import java.util.Objects;
29
import org.apache.commons.lang3.Range;
30
import org.apache.commons.lang3.StringUtils;
31
import org.apache.commons.lang3.tuple.ImmutablePair;
32
import org.apache.commons.lang3.tuple.Pair;
33
import org.gvsig.expressionevaluator.Code;
34
import org.gvsig.expressionevaluator.CodeBuilder;
35
import org.gvsig.expressionevaluator.Codes;
36
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_GETATTR;
37
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_TUPLE;
38
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
39
import org.gvsig.expressionevaluator.ExpressionUtils;
40
import org.gvsig.expressionevaluator.Interpreter;
41
import org.gvsig.expressionevaluator.Optimizer;
42
import org.gvsig.expressionevaluator.SymbolTable;
43
import org.gvsig.expressionevaluator.impl.DALFunctions;
44
import org.gvsig.expressionevaluator.spi.AbstractFunction;
45
import org.gvsig.fmap.dal.DALLocator;
46
import org.gvsig.fmap.dal.DataManager;
47
import static org.gvsig.fmap.dal.DataManager.FUNCTION_SELECT;
48
import org.gvsig.fmap.dal.DataStore;
49
import org.gvsig.expressionevaluator.ExpressionEvaluator;
50
import static org.gvsig.fmap.dal.expressionevaluator.FeatureSymbolTable.SYMBOL_CURRENT_TABLE;
51
import org.gvsig.fmap.dal.expressionevaluator.TableAttributeHandler;
52
import org.gvsig.fmap.dal.feature.EditableFeature;
53
import org.gvsig.fmap.dal.feature.Feature;
54
import org.gvsig.fmap.dal.feature.FeatureQuery;
55
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
56
import org.gvsig.fmap.dal.feature.FeatureSet;
57
import org.gvsig.fmap.dal.feature.FeatureStore;
58
import org.gvsig.fmap.dal.feature.impl.DefaultFeatureQueryOrder;
59
import org.gvsig.fmap.dal.impl.expressionevaluator.DefaultFeatureExpressionEvaluator;
60
import org.gvsig.tools.dispose.DisposeUtils;
61
import org.gvsig.tools.exception.BaseException;
62

    
63
/**
64
 *
65
 * @author jjdelcerro
66
 */
67
public class InsertIntoTableFunction 
68
        extends AbstractFunction 
69
        implements Optimizer.FunctionOptimizer
70
  {
71

    
72
  public InsertIntoTableFunction() {
73
    super(  DALFunctions.GROUP_DATA_ACCESS,
74
            DataManager.FUNCTION_INSERT_INTO_TABLE,
75
            Range.is(7),
76
            "Inserts in the indicated table the records obtained from the specified SELECT statement.\n" +
77
              "Return the number of inserted records.\n" +
78
              "This statement must always end with a semicolon.\n" +
79
              "See the SELECT statement for more information on this.",
80
            "INSERT INTO {{table_name}} SELECT * FROM table WHERE boolean_expression ORDER BY order_column LIMIT limit;",
81
            new String[]{
82
              "table_name - Name of the table in which to insert the records.",
83
              "column_names/asterisk - Names of the columns table to retrieve.",
84
              "table_name - Name of the table",
85
              "filter - boolean expression to apply as filter",
86
              "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.",
87
              "limit - Maximum number of features to return"
88
            },
89
            "Number",
90
            false
91
    );
92
  }
93

    
94
  @Override
95
  public boolean allowConstantFolding() {
96
    return false;
97
  }
98

    
99

    
100
  @Override
101
  public boolean useArgumentsInsteadObjects() {
102
    return true;
103
  }
104
  
105
  @Override
106
  public Object call(Interpreter interpreter, Object[] args) throws Exception {
107
    throw new UnsupportedOperationException();
108
  }
109

    
110
  private static final int COLUMNS = 1;
111
  private static final int TABLE = 2;
112
  private static final int WHERE = 3;
113
  private static final int ORDER = 4;
114
  private static final int ORDER_MODE = 5;
115
  private static final int LIMIT = 6;
116
  
117
  
118
  private Code.Callable getTupleOrNull(Codes args, int argn) {
119
    Code code = args.get(argn);
120
    if( code.code()==Code.CONSTANT ) {
121
      if( ((Code.Constant)code).value()!=null ) {
122
        throw new ExpressionRuntimeException("Tupple or null expected in argument "+argn+ " of function '" + FUNCTION_SELECT + "'.");
123
      }
124
      return null;
125
    }
126
    if( code.code()!=Code.CALLABLE ) {
127
      throw new ExpressionRuntimeException("Tupple or null expected in argument "+argn+ " of function '" + FUNCTION_SELECT + "'.");
128
    }
129
    Code.Callable caller = (Code.Callable) code;
130
    if( !StringUtils.equalsIgnoreCase(FUNCTION_TUPLE, caller.name()) ) {
131
      throw new ExpressionRuntimeException("Tupple or null expected in argument "+argn+ " of function '" + FUNCTION_SELECT + "'.");
132
    }
133
    return caller;
134
  }
135
  
136
  @Override
137
  @SuppressWarnings("UseSpecificCatch")
138
  public Object call(Interpreter interpreter, Codes args) throws Exception {
139
    FeatureStore targetStore = null;
140
    FeatureStore sourceStore = null;
141
    try {
142
      String targetTableName = Objects.toString(getObject(interpreter, args, 0),null);
143
      if( targetTableName == null ) {
144
        throw new ExpressionRuntimeException("Target table name can't be null.");    
145
      }
146
      DataManager dataManager = DALLocator.getDataManager();  
147
      targetStore = (FeatureStore) dataManager.getStoresRepository().getStore(targetTableName);
148
      if( targetStore==null ) {
149
        throw new ExpressionRuntimeException("Can't locate target table '"+targetTableName+"'.");    
150
      }
151

    
152

    
153
      Code.Identifier storeName =  (Code.Identifier) args.get(TABLE);
154
      Code columns = getTupleOrNull(args, COLUMNS);
155
      Code where = args.get(WHERE);
156
      Number limit = (Number) getObject(interpreter, args, LIMIT);
157
      Code.Callable order = getTupleOrNull(args, ORDER);
158
      Code.Callable order_mode = getTupleOrNull(args, ORDER_MODE);
159

    
160
      FeatureQueryOrder queryOrder = null;
161
      if( order!=null || order_mode!=null ) {
162
        for( int n=0 ; n<order.parameters().size(); n++) {
163
          String member = (String) interpreter.run(order.parameters().get(n));
164
          Boolean mode = (Boolean) interpreter.run(order_mode.parameters().get(n));
165
          if( queryOrder == null ) {
166
            queryOrder = new DefaultFeatureQueryOrder();
167
          }
168
          queryOrder.add(member, mode);
169
        }
170
      }
171

    
172
      // FIXME: add columns to query.addAttributeName() 
173
      DataStore store = this.getStore(storeName.name());
174
      if (store == null ) {
175
        throw new ExpressionRuntimeException("Cant locate the store '" + storeName + "' in function '" + FUNCTION_SELECT + "'.");
176
      }
177
      if (!(store instanceof FeatureStore)) {
178
        throw new ExpressionRuntimeException("The store'" + storeName + "' is not valid for function '" + FUNCTION_SELECT + "', a FeatureStore is required.");
179
      }
180
      sourceStore = (FeatureStore) store;
181
      FeatureSet features;
182
      if (where == null && queryOrder == null && limit==null ) {
183
        features = sourceStore.getFeatureSet();
184
      } else {
185
        FeatureQuery query = sourceStore.createFeatureQuery();
186
        if (where != null) {
187
          removeOuterTablesReferences(interpreter, where);
188
          ExpressionEvaluator filter = new DefaultFeatureExpressionEvaluator(where.toString());
189
          filter.toSymbolTable().addSymbolTable(interpreter.getSymbolTable());
190
          query.addFilter(filter);
191
        }
192
        if (queryOrder != null) {
193
          query.getOrder().copyFrom(queryOrder);
194
        }
195
        if( limit!=null ) {
196
          query.setLimit(limit.longValue());
197
        }
198
        query.retrievesAllAttributes();
199
        features = sourceStore.getFeatureSet(query);
200
      }
201
      
202
      long count = 0;
203
      targetStore.edit(FeatureStore.MODE_APPEND);
204
      for (Feature feature : features) {
205
        EditableFeature f = targetStore.createNewFeature();
206
        f.copyFrom(feature);
207
        targetStore.insert(f);
208
        count++;
209
      }
210
      targetStore.finishEditing();
211
      
212
      return count;
213
    } catch (ExpressionRuntimeException ex) {
214
      throw ex;
215
    } catch (Exception ex) {
216
      if( targetStore!=null && targetStore.isAppending() ) {
217
        targetStore.cancelEditing();
218
      }
219
      DisposeUtils.disposeQuietly(targetStore);
220
      DisposeUtils.disposeQuietly(sourceStore);
221
      throw new ExpressionRuntimeException("Problems calling '" + FUNCTION_SELECT + "' function", ex);
222
    }
223
  }
224

    
225
  protected DataStore getStore(String storeName) {
226
    DataManager dataManager = DALLocator.getDataManager();
227
    DataStore store = dataManager.getStoresRepository().getStore(storeName);
228
    return store;
229
  }
230

    
231
  private void removeOuterTablesReferences(Interpreter interpreter, Code where) {
232
    try {
233
      SymbolTable symbolTable = interpreter.getSymbolTable();
234
      TableAttributeHandler table = (TableAttributeHandler) symbolTable.value(SYMBOL_CURRENT_TABLE);
235
      List<Pair<Code,Code>>replaces = new ArrayList<>();
236
      CodeBuilder codeBuilder = ExpressionUtils.createCodeBuilder();
237
      where.accept((Object o) -> {
238
        Code code = (Code) o;
239
        if( code!=null && code.code() == Code.CALLABLE ) {
240
          Code.Callable caller = (Code.Callable) code;
241
          if( StringUtils.equalsIgnoreCase(caller.name(),FUNCTION_GETATTR) ) {
242
            Codes args = caller.parameters();
243
            Code arg0 = args.get(0);
244
            Code arg1 = args.get(1);
245
            if( arg0 instanceof Code.Identifier && arg1 instanceof Code.Identifier ) {
246
              Object tt = symbolTable.value(((Code.Identifier)arg0).name());
247
              if( tt instanceof TableAttributeHandler && 
248
                  StringUtils.equalsIgnoreCase(((TableAttributeHandler)tt).getName(), table.getName()) ) {
249
                String columnName = Objects.toString(((Code.Identifier)arg1).name(), null);
250
                if( columnName!=null ) {
251
                  Object value = table.get(columnName);
252
                  replaces.add(
253
                          new ImmutablePair<>(
254
                                  caller,
255
                                  codeBuilder.constant(value)
256
                          )
257
                  );
258
                }
259
              }
260
            }
261
          }
262
        }
263
      });
264
      for (Pair<Code, Code> replace : replaces) {
265
        if( replace!=null ) {
266
          where.replace(replace.getLeft(), replace.getRight());
267
        }
268
      }
269
    } catch (BaseException ex) {
270
      throw new ExpressionRuntimeException("Can't remove references to outer tables.", ex);
271
    }
272
                
273
  }
274

    
275
  @Override
276
  public Code optimize(Optimizer optimizer, Code.Callable caller) {
277
    return caller; // Don't optimize SELECT
278
  }
279
}