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

History | View | Annotate | Download (11.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.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.fmap.dal.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.impl.expressionevaluator.DefaultExpressionEvaluator;
59
import org.gvsig.tools.dispose.DisposeUtils;
60
import org.gvsig.tools.exception.BaseException;
61

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

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

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

    
98

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

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

    
151

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

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

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

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

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

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