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 / JDBCHelperBase.java @ 46050

History | View | Annotate | Download (33.9 KB)

1 45065 jjdelcerro
/**
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 43020 jjdelcerro
package org.gvsig.fmap.dal.store.jdbc2.spi;
25
26 45165 jjdelcerro
import java.io.File;
27 44297 jjdelcerro
import java.sql.Blob;
28 45008 omartinez
import java.sql.Clob;
29 43020 jjdelcerro
import java.sql.Connection;
30
import java.sql.ResultSet;
31 44376 jjdelcerro
import java.util.ArrayList;
32 45162 omartinez
import java.util.HashSet;
33 44376 jjdelcerro
import java.util.List;
34 46010 jjdelcerro
import java.util.function.Predicate;
35 45008 omartinez
import org.apache.commons.io.IOUtils;
36 44376 jjdelcerro
import org.apache.commons.lang3.ArrayUtils;
37 43020 jjdelcerro
import org.apache.commons.lang3.StringUtils;
38 44191 jjdelcerro
import org.apache.commons.lang3.mutable.MutableBoolean;
39
import org.gvsig.expressionevaluator.Code;
40 46010 jjdelcerro
import org.gvsig.expressionevaluator.Expression;
41 44376 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionBuilder;
42 44191 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
43
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
44
import org.gvsig.expressionevaluator.Function;
45 44644 jjdelcerro
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
46 44198 jjdelcerro
import org.gvsig.expressionevaluator.SymbolTable;
47 44750 jjdelcerro
import static org.gvsig.fmap.dal.DataManager.FUNCTION_EXISTS;
48
import static org.gvsig.fmap.dal.DataManager.FUNCTION_FOREING_VALUE;
49 43020 jjdelcerro
import org.gvsig.fmap.dal.DataTypes;
50 44376 jjdelcerro
import org.gvsig.fmap.dal.SQLBuilder;
51 43020 jjdelcerro
import org.gvsig.fmap.dal.exception.DataException;
52
import org.gvsig.fmap.dal.exception.InitializeException;
53 46010 jjdelcerro
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
54 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
55 46010 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
56 44191 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
57 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.FeatureType;
58 44376 jjdelcerro
import org.gvsig.fmap.dal.feature.ForeingKey;
59 43020 jjdelcerro
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
60 44376 jjdelcerro
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
61 43020 jjdelcerro
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
62
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
63
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
64 45165 jjdelcerro
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemStoreParameters;
65 43020 jjdelcerro
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
66
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
67 45650 jjdelcerro
import org.gvsig.fmap.dal.spi.DataTransactionServices;
68 43020 jjdelcerro
import org.gvsig.fmap.dal.store.db.DBHelper;
69
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
70
import org.gvsig.fmap.dal.store.jdbc.JDBCLibrary;
71
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
72
import org.gvsig.fmap.dal.store.jdbc.JDBCResource;
73
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
74
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
75
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
76
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
77
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
78
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
79
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
80
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
81
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
82
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
83 45481 fdiaz
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
84
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
85 43020 jjdelcerro
import org.gvsig.fmap.geom.Geometry;
86
import org.gvsig.fmap.geom.GeometryLocator;
87
import org.gvsig.fmap.geom.GeometryManager;
88
import org.gvsig.tools.dispose.impl.AbstractDisposable;
89 44191 jjdelcerro
import org.gvsig.tools.evaluator.Evaluator;
90 43020 jjdelcerro
import org.gvsig.tools.exception.BaseException;
91 43377 jjdelcerro
import org.gvsig.tools.exception.NotYetImplemented;
92 46010 jjdelcerro
import org.gvsig.tools.visitor.FilteredVisitable;
93 44191 jjdelcerro
import org.gvsig.tools.visitor.VisitCanceledException;
94
import org.gvsig.tools.visitor.Visitor;
95 43020 jjdelcerro
import org.slf4j.Logger;
96
import org.slf4j.LoggerFactory;
97 46050 omartinez
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$CONSTANT;
98
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$IDENTIFIER;
99 43020 jjdelcerro
100 44191 jjdelcerro
@SuppressWarnings("UseSpecificCatch")
101 43020 jjdelcerro
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
102
103 44750 jjdelcerro
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
104
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
105
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
106 43020 jjdelcerro
107 44750 jjdelcerro
  private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
108 43020 jjdelcerro
109 44750 jjdelcerro
  private ResulSetControler resulSetControler = null;
110 43020 jjdelcerro
111 44750 jjdelcerro
  // Quien ha creado este helper.
112
  // Se le reenviaran las notificaciones del recurso asociado al helper.
113
  private ResourceConsumer helperClient = null;
114 43020 jjdelcerro
115 44750 jjdelcerro
  private GeometryManager geometryManager = null;
116 43020 jjdelcerro
117 44750 jjdelcerro
  private JDBCConnectionParameters connectionParameters;
118 43020 jjdelcerro
119 44750 jjdelcerro
  private JDBCStoreProvider store;
120 44403 omartinez
121 44750 jjdelcerro
  protected OperationsFactory operationsFactory = null;
122 44403 omartinez
123 44750 jjdelcerro
  protected SRSSolver srssolver;
124 45152 fdiaz
125
  protected FeatureType providerFeatureType = null;
126 45650 jjdelcerro
127
  protected DataTransactionServices transaction;
128 44403 omartinez
129 44750 jjdelcerro
  public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
130 45647 fdiaz
        if (connectionParameters == null) {
131
            throw new IllegalArgumentException("Connection parameters can't be null.");
132
        }
133
        this.connectionParameters = connectionParameters;
134 44403 omartinez
135 45647 fdiaz
        // If a particular treatment is required to convert SRS to the
136
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
137
        this.srssolver = new SRSSolverDumb(this);
138
    }
139 43020 jjdelcerro
140 44750 jjdelcerro
  protected void initialize(
141
          ResourceConsumer helperClient,
142
          JDBCConnectionParameters connectionParameters,
143
          JDBCStoreProvider store
144
  ) {
145
    this.store = store;
146
    this.helperClient = helperClient;
147
    this.connectionParameters = connectionParameters;
148
    initializeResource(connectionParameters);
149
  }
150 44403 omartinez
151 44750 jjdelcerro
  protected String getResourceType() {
152
    return JDBCResource.NAME;
153
  }
154 43020 jjdelcerro
155 44750 jjdelcerro
  @Override
156
  public String getProviderName() {
157
    return JDBCLibrary.NAME;
158
  }
159 44403 omartinez
160 44750 jjdelcerro
  @Override
161
  public GeometrySupportType getGeometrySupportType() {
162
    // El proveedor generico de JDBC guadara las geoemtrias como WKT
163
    return GeometrySupportType.WKT;
164
  }
165 45152 fdiaz
166
    /**
167
     * @return the providerFeatureType
168
     */
169
  @Override
170
    public FeatureType getProviderFeatureType() {
171
        return providerFeatureType;
172
    }
173 43020 jjdelcerro
174 45152 fdiaz
    /**
175
     * @param providerFeatureType the providerFeatureType to set
176
     */
177 44750 jjdelcerro
  @Override
178 45152 fdiaz
    public void setProviderFeatureType(FeatureType providerFeatureType) {
179
        this.providerFeatureType = providerFeatureType;
180
    }
181
182
183
  @Override
184 44750 jjdelcerro
  public boolean hasSpatialFunctions() {
185
    // Por defecto el proveedor generico de JDBC asume que la BBDD no
186
    // tiene soporte para funciones espaciales.
187
    return false;
188
  }
189 43020 jjdelcerro
190 44750 jjdelcerro
  @Override
191
  public boolean allowNestedOperations() {
192
    return false;
193
  }
194 44403 omartinez
195 44750 jjdelcerro
  @Override
196
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
197
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
198
    return true;
199
  }
200 43020 jjdelcerro
201 44750 jjdelcerro
  @Override
202
  public JDBCSQLBuilderBase createSQLBuilder() {
203
    return new JDBCSQLBuilderBase(this);
204
  }
205 43020 jjdelcerro
206 44750 jjdelcerro
  @Override
207
  public String getQuoteForIdentifiers() {
208
    return QUOTE_FOR_USE_IN_IDENTIFIERS;
209
  }
210 43020 jjdelcerro
211 44750 jjdelcerro
  @Override
212
  public boolean allowAutomaticValues() {
213
    return ALLOW_AUTOMATIC_VALUES;
214
  }
215 43020 jjdelcerro
216 44750 jjdelcerro
  @Override
217
  public boolean supportOffsetInSelect() {
218
    // Asumimos que la BBDD soporta OFFSET
219
    return true;
220
  }
221 44403 omartinez
222 44750 jjdelcerro
  @Override
223
  public String getQuoteForStrings() {
224
    return QUOTE_FOR_USE_IN_STRINGS;
225
  }
226 44191 jjdelcerro
227 44752 jjdelcerro
  protected boolean supportCaller(Code.Callable caller) {
228 44750 jjdelcerro
    if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) {
229
      return true;
230 44376 jjdelcerro
    }
231 44750 jjdelcerro
    Function function = caller.function();
232
    if (function == null) {
233
      return false;
234
    }
235
    if (!function.isSQLCompatible()) {
236
      return false;
237
    }
238
    if (!this.hasSpatialFunctions()) {
239
      if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
240
        return false;
241
      }
242
    }
243
    return true;
244
  }
245 44403 omartinez
246 44750 jjdelcerro
  @Override
247 46010 jjdelcerro
  @SuppressWarnings("Convert2Lambda")
248 44750 jjdelcerro
  public boolean supportFilter(final FeatureType type, Evaluator filter) {
249 45999 omartinez
    if (filter == null) {
250
      // No hay que filtrar nada, asi que se p?ede.
251
      return true;
252
    }
253
    String sql = filter.getSQL();
254 44750 jjdelcerro
    if (StringUtils.isEmpty(sql)) {
255
      // Hay un filtro, pero la SQL es null, con lo que no podemos
256
      // filtrar por el.
257
      return false;
258
    }
259 46010 jjdelcerro
    return this.supportExpression(type, sql);
260
  }
261
262
    @Override
263
    @SuppressWarnings("Convert2Lambda")
264
    public boolean supportExpression(final FeatureType type, String sql) {
265
        // La expression no es valida si:
266
        // - Hay una subquery especificada en los parametros
267
        // - Si la sql es null.
268
        // - Si se esta usando alguna funcion no-sql en el getSQL
269
        // - Si se esta invocando a metodos de un objeto
270
        // - Si se estan usando funciones OGC y no soportamos funciones espaciales
271
        // - Si se esta usando algun campo calculado en la expresion de filtro.
272
        //
273
        // Un proveedor especifico podria sobreescribir el metodo,
274
        // para hilar mas fino al comprobar si soporta el filtro o no.
275
        //
276 44191 jjdelcerro
277 46010 jjdelcerro
        if (StringUtils.isEmpty(sql)) {
278
            return false;
279
        }
280 44403 omartinez
281 46010 jjdelcerro
        if (this.useSubquery()) {
282
            // Se esta usando una subquery en los parametros de acceso a la
283
            // BBDD, asi que no podemos filtrar.
284
            return false;
285
        }
286
287
        // Ahora vamos a comprobar que las funciones que se usan son
288
        // compatibles sql, y que no se usan funciones OGC si el
289
        // proveedor dice que no soporta funciones espaciales.
290
        // Tambien comprobaremos que el filtro no usa ningun campo calculado.
291
        final MutableBoolean isCompatible = new MutableBoolean(true);
292
        ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
293
        Code code = manager.compile(sql);
294
        SymbolTable symbolTable = manager.createSymbolTable();
295
        code.link(symbolTable);
296
        try {
297
            code.accept(new Visitor() {
298
                @Override
299
                public void visit(Object code_obj) throws VisitCanceledException, BaseException {
300
                    Code code1 = (Code) code_obj;
301
                    switch (code1.code()) {
302
                        case Code.CALLABLE:
303
                            Code.Callable caller = (Code.Callable) code1;
304
                            if (!supportCaller(caller)) {
305
                                isCompatible.setValue(false);
306
                                throw new VisitCanceledException();
307
                            }
308
                            break;
309
                        case Code.METHOD:
310
                            isCompatible.setValue(false);
311
                            throw new VisitCanceledException();
312
                        case Code.IDENTIFIER:
313
                            Code.Identifier identifier = (Code.Identifier) code1;
314
                            if (type != null) {
315
                                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
316
                                if (attrdesc != null) {
317
                                    if (attrdesc.isComputed()) {
318
                                        FeatureAttributeEmulator emulator = attrdesc.getFeatureAttributeEmulator();
319
                                        if (!(emulator instanceof FeatureAttributeEmulatorExpression)) {
320
                                            isCompatible.setValue(false);
321
                                            throw new VisitCanceledException();
322
                                        }
323
                                        Expression expr = ((FeatureAttributeEmulatorExpression) emulator).getExpression();
324
                                        if (!supportExpression(type, expr.getPhrase())) {
325
                                            isCompatible.setValue(false);
326
                                            throw new VisitCanceledException();
327
                                        }
328
                                    }
329
                                }
330
                            }
331
                            break;
332
                    }
333 45999 omartinez
                }
334 46010 jjdelcerro
            }, new Predicate<FilteredVisitable>() {
335
                @Override
336
                public boolean test(FilteredVisitable code0) {
337
                    if (code0 instanceof Code.Callable) {
338
                        String name = ((Code.Callable) code0).name();
339 46050 omartinez
                        return StringUtils.equalsIgnoreCase(name, FUNCTION_$CONSTANT)
340
                                || StringUtils.equalsIgnoreCase(name, FUNCTION_$IDENTIFIER);
341 46010 jjdelcerro
                    }
342
                    return false;
343
                }
344
            });
345
346
        } catch (VisitCanceledException ex) {
347
            // Do nothing
348
        } catch (Exception ex) {
349
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
350 44191 jjdelcerro
        }
351 44403 omartinez
352 46010 jjdelcerro
        return isCompatible.booleanValue();
353 44191 jjdelcerro
    }
354 44403 omartinez
355 44750 jjdelcerro
  @Override
356
  public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
357
    if (this.useSubquery()) {
358
      return false;
359
    }
360
    if (order == null) {
361
      return true;
362
    }
363
    for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
364
      if (member.hasEvaluator()) {
365
        if (!this.supportFilter(type, member.getEvaluator())) {
366
          return false;
367 44191 jjdelcerro
        }
368 44750 jjdelcerro
      }
369 44191 jjdelcerro
    }
370 44750 jjdelcerro
    return true;
371
  }
372 44403 omartinez
373 44750 jjdelcerro
  @Override
374
  public OperationsFactory getOperations() {
375 45481 fdiaz
    if (this.operationsFactory == null && !isClosed()) {
376 44750 jjdelcerro
      this.operationsFactory = new OperationsFactoryBase(this);
377 43020 jjdelcerro
    }
378 44750 jjdelcerro
    return operationsFactory;
379
  }
380 44403 omartinez
381 44750 jjdelcerro
  protected void initializeResource(JDBCConnectionParameters params) {
382 43020 jjdelcerro
//        Object[] resourceParams = new Object[]{
383
//            params.getUrl(),
384
//            params.getHost(),
385
//            params.getPort(),
386
//            params.getDBName(),
387
//            params.getUser(),
388
//            params.getPassword(),
389
//            params.getJDBCDriverClassName()
390
//        };
391
//
392
//        try {
393
//            ResourceManagerProviderServices manager
394
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
395
//            JDBCResource resource = (JDBCResource) manager.createAddResource(
396
//                    this.getResourceType(),
397
//                    resourceParams
398
//            );
399
//            this.resource = resource;
400
//            this.resource.addConsumer(this);
401
//        } catch (InitializeException ex) {
402 44750 jjdelcerro
//            logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
403 43020 jjdelcerro
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
404
//        }
405
406 44750 jjdelcerro
  }
407 43020 jjdelcerro
408 44750 jjdelcerro
  @Override
409
  public String getSourceId() {
410
    return this.store.getSourceId();
411
  }
412 44403 omartinez
413 44750 jjdelcerro
  @Override
414
  public JDBCResource getResource() {
415
    return null;
416 43020 jjdelcerro
//        return this.resource;
417 44750 jjdelcerro
  }
418 43020 jjdelcerro
419 44750 jjdelcerro
  @Override
420
  public Connection getConnection() throws AccessResourceException {
421
    throw new NotYetImplemented();
422
  }
423 43020 jjdelcerro
424 44750 jjdelcerro
  @Override
425
  public Connection getConnectionWritable() throws AccessResourceException {
426
    return this.getConnection();
427
  }
428 43377 jjdelcerro
429 44750 jjdelcerro
  @Override
430
  public String getConnectionURL() {
431
    return null;
432
  }
433 43035 jjdelcerro
434 44750 jjdelcerro
  @Override
435
  public JDBCConnectionParameters getConnectionParameters() {
436
    return connectionParameters;
437
  }
438 43035 jjdelcerro
439 44750 jjdelcerro
  @Override
440
  public void closeConnection(Connection connection) {
441 45097 jjdelcerro
      JDBCUtils.close(connection);
442 44750 jjdelcerro
  }
443 44191 jjdelcerro
444 44750 jjdelcerro
  @Override
445
  public void closeConnectionQuietly(Connection connection) {
446 45097 jjdelcerro
      JDBCUtils.closeQuietly(connection);
447 44750 jjdelcerro
  }
448 44403 omartinez
449 44750 jjdelcerro
  @Override
450
  protected void doDispose() throws BaseException {
451
    JDBCUtils.closeQuietly(this);
452
  }
453 43020 jjdelcerro
454 44750 jjdelcerro
  @Override
455
  public void close() throws Exception {
456
    JDBCUtils.closeQuietly(this.resulSetControler);
457
    this.resulSetControler = null;
458 45481 fdiaz
    this.operationsFactory = null;
459
    this.srssolver = null;
460
    this.connectionParameters = null;
461
    this.helperClient = null;
462
    this.geometryManager = null;
463
    this.providerFeatureType = null;
464
    this.store = null;
465 44750 jjdelcerro
  }
466 45481 fdiaz
467
  public boolean isClosed() {
468
      return this.srssolver == null;
469
  }
470 43020 jjdelcerro
471 44750 jjdelcerro
  @Override
472
  public boolean closeResourceRequested(ResourceProvider resource) {
473
    return this.helperClient.closeResourceRequested(resource);
474
  }
475 43020 jjdelcerro
476 44750 jjdelcerro
  @Override
477
  public void resourceChanged(ResourceProvider resource) {
478
    this.helperClient.resourceChanged(resource);
479
  }
480 43020 jjdelcerro
481 44750 jjdelcerro
  @Override
482
  public GeometryManager getGeometryManager() {
483
    if (this.geometryManager == null) {
484
      this.geometryManager = GeometryLocator.getGeometryManager();
485 43020 jjdelcerro
    }
486 44750 jjdelcerro
    return this.geometryManager;
487
  }
488 43020 jjdelcerro
489 44750 jjdelcerro
  @Override
490
  public ResulSetControler getResulSetControler() {
491
    if (this.resulSetControler == null) {
492
      this.resulSetControler = new ResulSetControlerBase(this);
493 43020 jjdelcerro
    }
494 44750 jjdelcerro
    return this.resulSetControler;
495
  }
496 43020 jjdelcerro
497 44750 jjdelcerro
  @Override
498
  public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
499
    fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
500
  }
501 43020 jjdelcerro
502 44750 jjdelcerro
  @Override
503
  public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException {
504
    Object value;
505
    try {
506
      int rsIndex = 1;
507
      for (FeatureAttributeDescriptor column : columns) {
508
        switch (column.getType()) {
509
          case DataTypes.GEOMETRY:
510
            value = this.getGeometryFromColumn(rs, rsIndex++);
511
            break;
512
          default:
513
            value = rs.getObject(rsIndex++);
514
            if (value instanceof Blob) {
515
              Blob blob = (Blob) value;
516 45152 fdiaz
              value = blob.getBytes(1, (int) blob.length());
517 44750 jjdelcerro
              blob.free();
518 45008 omartinez
            } else if(value instanceof Clob) {
519
              Clob clob = (Clob) value;
520
              value = new String(IOUtils.toCharArray(clob.getCharacterStream()));
521
              clob.free();
522
          }
523 43020 jjdelcerro
        }
524 44750 jjdelcerro
        feature.set(column.getIndex(), value);
525
      }
526
      if (ArrayUtils.isNotEmpty(extraValueNames)) {
527
        feature.setExtraValueNames(extraValueNames);
528
        for (int index = 0; index < extraValueNames.length; index++) {
529
          value = rs.getObject(rsIndex++);
530
          if (value instanceof Blob) {
531
            Blob blob = (Blob) value;
532
            value = blob.getBytes(0, (int) blob.length());
533
            blob.free();
534
          }
535
          feature.setExtraValue(index, value);
536
        }
537
      }
538
    } catch (Exception ex) {
539
      throw new JDBCCantFetchValueException(ex);
540 43020 jjdelcerro
    }
541 44750 jjdelcerro
  }
542 43020 jjdelcerro
543 44750 jjdelcerro
  @Override
544
  public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
545
    return getGeometryFromColumn(rs.get(), index);
546
  }
547 43020 jjdelcerro
548 44750 jjdelcerro
  @Override
549
  public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
550
    try {
551
      Object value;
552
      switch (this.getGeometrySupportType()) {
553
        case NATIVE:
554
        case WKB:
555
          value = rs.getBytes(index);
556
          if (value == null) {
557
            return null;
558
          }
559
          return this.getGeometryManager().createFrom((byte[]) value);
560 43020 jjdelcerro
561 44750 jjdelcerro
        case EWKB:
562
          value = rs.getBytes(index);
563
          if (value == null) {
564
            return null;
565
          }
566
          return this.getGeometryManager().createFrom((byte[]) value);
567
        case WKT:
568
        default:
569
          value = rs.getString(index);
570
          if (value == null) {
571
            return null;
572
          }
573
          return this.getGeometryManager().createFrom((String) value);
574 43020 jjdelcerro
575 44750 jjdelcerro
      }
576
    } catch (Exception ex) {
577
      throw new JDBCCantFetchValueException(ex);
578 43020 jjdelcerro
    }
579 44750 jjdelcerro
  }
580 43020 jjdelcerro
581 44750 jjdelcerro
  @Override
582
  public FeatureProvider createFeature(FeatureType featureType) throws DataException {
583
    return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
584
  }
585 44403 omartinez
586 44750 jjdelcerro
  @Override
587
  public boolean useSubquery() {
588
    if (this.store == null) {
589
      return false;
590 44403 omartinez
    }
591 44750 jjdelcerro
    return !StringUtils.isEmpty(this.store.getParameters().getSQL());
592
  }
593 44403 omartinez
594 44750 jjdelcerro
  @Override
595
  public SRSSolver getSRSSolver() {
596
    return this.srssolver;
597
  }
598 44403 omartinez
599 44750 jjdelcerro
  @Override
600
  public JDBCStoreProvider createProvider(
601
          JDBCStoreParameters parameters,
602
          DataStoreProviderServices providerServices
603
  ) throws InitializeException {
604 44403 omartinez
605 44750 jjdelcerro
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
606
            parameters,
607
            providerServices,
608
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
609
            this
610
    );
611
    this.initialize(theStore, parameters, theStore);
612
    return theStore;
613
  }
614 43020 jjdelcerro
615 44750 jjdelcerro
  @Override
616
  public JDBCServerExplorer createServerExplorer(
617
          JDBCServerExplorerParameters parameters,
618
          DataServerExplorerProviderServices providerServices
619
  ) throws InitializeException {
620 44403 omartinez
621 44750 jjdelcerro
    JDBCServerExplorer explorer = new JDBCServerExplorerBase(
622
            parameters,
623
            providerServices,
624
            this
625
    );
626
    this.initialize(explorer, parameters, null);
627
    return explorer;
628
  }
629 43020 jjdelcerro
630 44750 jjdelcerro
  @Override
631
  public JDBCNewStoreParameters createNewStoreParameters() {
632
    return new JDBCNewStoreParameters();
633
  }
634 43020 jjdelcerro
635 44750 jjdelcerro
  @Override
636
  public JDBCStoreParameters createOpenStoreParameters() {
637
    return new JDBCStoreParameters();
638
  }
639 45165 jjdelcerro
640
  @Override
641
  public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
642
    JDBCStoreParameters params = this.createOpenStoreParameters();
643
    params.setHost(parameters.getHost());
644
    params.setPort(parameters.getPort());
645
    params.setDBName(parameters.getDBName());
646
    params.setUser(parameters.getUser());
647
    params.setPassword(parameters.getPassword());
648
    params.setCatalog(parameters.getCatalog());
649
    params.setSchema(parameters.getSchema());
650
    params.setJDBCDriverClassName(parameters.getJDBCDriverClassName());
651
    params.setUrl(parameters.getUrl());
652
    if( parameters instanceof FilesystemStoreParameters ) {
653
        File f = ((FilesystemStoreParameters) parameters).getFile();
654
        ((FilesystemStoreParameters) params).setFile(f);
655
    }
656
    return params;
657
  }
658
659 43020 jjdelcerro
660 44750 jjdelcerro
  @Override
661
  public JDBCServerExplorerParameters createServerExplorerParameters() {
662
    return new JDBCServerExplorerParameters();
663
  }
664 43020 jjdelcerro
665 44750 jjdelcerro
  @Override
666
  public String getSourceId(JDBCStoreParameters parameters) {
667
    return parameters.getHost() + ":"
668
            + parameters.getDBName() + ":"
669
            + parameters.getSchema() + ":"
670
            + parameters.tableID();
671
  }
672 44198 jjdelcerro
673 44750 jjdelcerro
  @Override
674
  public boolean isThreadSafe() {
675
    return true;
676
  }
677
678 45131 fdiaz
  /**
679
   * This method has been overriden in Oracle provider
680
   *
681
   * @param sqlbuilder
682
   * @param type
683
   * @param extra_column_names
684
   */
685 44750 jjdelcerro
  @Override
686
  public void processSpecialFunctions(
687
          SQLBuilder sqlbuilder,
688
          FeatureType type,
689
          List<String> extra_column_names) {
690
    replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
691
    replaceExistsFunction(sqlbuilder, type, extra_column_names);
692
  }
693
694 46010 jjdelcerro
  @SuppressWarnings("Convert2Lambda")
695 44750 jjdelcerro
  private void replaceExistsFunction(
696
          SQLBuilder sqlbuilder,
697
          FeatureType type,
698
          final List<String> extra_column_names) {
699 44748 jjdelcerro
700 44750 jjdelcerro
    //  Si lse encuentra una construccion del tipo:
701
    //    SELECT ... FROM ... WHERE ... EXISTS(list, 'EXISTS_ID') ...
702
    //  se traslada a:
703
    //    SELECT ... EXISTS(list) AS EXISTS_ID FROM ... WHERE ... EXISTS(list) ...
704
    //  Y se a?ade el valor ESISTS_ID a las columnas extra a recuperar de la consulta.
705
    //
706
707
    final SQLBuilder.SelectBuilder select = sqlbuilder.select();
708
    final ExpressionBuilder where = select.where();
709 44785 jjdelcerro
    if (where == null || where.isEmpty() || select.has_group_by() ) {
710 44750 jjdelcerro
      return;
711 44748 jjdelcerro
    }
712 44750 jjdelcerro
    final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
713
    where.accept(new ExpressionBuilder.Visitor() {
714
      @Override
715
      public void visit(ExpressionBuilder.Visitable value) {
716
        if (!(value instanceof ExpressionBuilder.Function)) {
717
          return;
718 44376 jjdelcerro
        }
719 44750 jjdelcerro
        ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
720
        if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_EXISTS)) {
721
          return;
722 44682 jjdelcerro
        }
723 44750 jjdelcerro
        if (function.parameters().size() != 2) {
724
          return;
725
        }
726
        ExpressionBuilder.Value arg0 = function.parameters().get(0);
727
        ExpressionBuilder.Value arg1 = function.parameters().get(1);
728
        if (arg1 == null) {
729
          return;
730
        }
731
        String columnName = (String) ((ExpressionBuilder.Constant) arg1).value();
732
        SQLBuilder.SelectColumnBuilder column = select.column();
733
        column.value(
734
                sqlbuilder.expression().function(FUNCTION_EXISTS, arg0)
735
        );
736
        column.as(columnName);
737 45131 fdiaz
738 44750 jjdelcerro
        if( extra_column_names!=null ) {
739
          extra_column_names.add(columnName);
740
        }
741
      }
742
    }, null);
743
    if (value_replacements.isEmpty()) {
744
      return;
745
    }
746
    // Realizamos los reemplazos calculados previamente (value_replacements).
747
    for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
748
      ExpressionBuilder.Value target = replaceValue[0];
749
      ExpressionBuilder.Value replacement = replaceValue[1];
750
      sqlbuilder.select().replace(target, replacement);
751
    }
752
  }
753 44376 jjdelcerro
754 46010 jjdelcerro
  @SuppressWarnings("Convert2Lambda")
755 45131 fdiaz
  protected void replaceForeingValueFunction(
756 44750 jjdelcerro
          SQLBuilder sqlbuilder,
757
          FeatureType type,
758
          List<String> extra_column_names) {
759
    try {
760
      // See test SQLBuilderTest->testForeingValue()
761
      final ExpressionBuilder where = sqlbuilder.select().where();
762 45155 omartinez
//      if (where == null || where.isEmpty()) {
763
//        return;
764
//      }
765 44750 jjdelcerro
      final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
766
      final ExpressionBuilder expbuilder = sqlbuilder.expression();
767 44403 omartinez
768 44750 jjdelcerro
      final List<String> foreing_value_args;
769 45162 omartinez
      if (extra_column_names == null || sqlbuilder.select().has_group_by()) {
770 44750 jjdelcerro
        foreing_value_args = new ArrayList<>();
771
      } else {
772
        foreing_value_args = extra_column_names;
773
      }
774
      final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
775
776
      // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
777
      // el argumento de esta asi como por que tendriamos que sustituirla
778
      // una vez hechos los left joins que toquen.
779 45155 omartinez
      sqlbuilder.select().accept(new ExpressionBuilder.Visitor() {
780 44750 jjdelcerro
        @Override
781
        public void visit(ExpressionBuilder.Visitable value) {
782
          // Requiere que sea la funcion "FOREING_VALUE con un solo
783
          // argumento que sea una constante de tipo string.
784
          if (!(value instanceof ExpressionBuilder.Function)) {
785 44682 jjdelcerro
            return;
786 44750 jjdelcerro
          }
787
          ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
788
          if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_FOREING_VALUE)) {
789
            return;
790
          }
791
          if (function.parameters().size() != 1) {
792
            return;
793
          }
794
          ExpressionBuilder.Value arg = function.parameters().get(0);
795
          if (!(arg instanceof ExpressionBuilder.Constant)) {
796
            return;
797
          }
798
          Object arg_value = ((ExpressionBuilder.Constant) arg).value();
799
          if (!(arg_value instanceof CharSequence)) {
800
            return;
801
          }
802
          String foreing_value_arg = arg_value.toString();
803
          String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
804
          if (foreingNameParts.length != 2) {
805
            // De momento solo tratamos joins entre dos tablas.
806
            return;
807
          }
808
          String columnNameLocal = foreingNameParts[0];
809
          String columnNameForeing = foreingNameParts[1];
810
          FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
811 45385 omartinez
          if (attr==null) {
812
              throw new RuntimeException("Cannot find in feature type attribute:"+columnNameLocal);
813
          }
814 44750 jjdelcerro
          if (!attr.isForeingKey()) {
815
            // Uhm... si el argumento no referencia a un campo que es
816
            // clave ajena no lo procesamos.
817
            // ? Deberiamos lanzar un error ?
818
            return;
819
          }
820
          // Nos guardaremos por que hay que reemplazar la funcion
821
          // FOREING_VALUE, y su argumento para mas tarde.
822
          ForeingKey foreingKey = attr.getForeingKey();
823
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
824
                  .database(table.getDatabase())
825
                  .schema(table.getSchema())
826
                  .name(foreingKey.getTableName());
827
          // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
828
          // que toca de la tabla a la que referencia la clave ajena.
829
          ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
830
          value_replacements.add(
831
                  new ExpressionBuilder.Value[]{
832
                    function,
833
                    function_replacement
834
                  }
835
          );
836 45155 omartinez
          if (!foreing_value_args.contains(foreing_value_arg)) {
837
            foreing_value_args.add(foreing_value_arg);
838
          }
839 44376 jjdelcerro
        }
840 44750 jjdelcerro
      }, null);
841 45155 omartinez
842 44750 jjdelcerro
      // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
843
      // hacer nada.
844
      if (foreing_value_args.isEmpty()) {
845
        return;
846
      }
847 44403 omartinez
848 44750 jjdelcerro
      // Calculamos que referencias de columnas hemos de cambiar para
849
      // que no aparezcan ambiguedades al hacer el join (nombres de
850
      // columna de una y otra tabla que coincidan).
851
      // Para las columnas que puedan dar conflicto se prepara un reemplazo
852
      // de estas que tenga el nombre de tabla.
853
      for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
854 46010 jjdelcerro
        if (variable == null || variable instanceof SQLBuilderBase.ColumnBase) {
855 44750 jjdelcerro
          continue;
856 44376 jjdelcerro
        }
857
        for (String foreingName : foreing_value_args) {
858 44750 jjdelcerro
          String[] foreingNameParts = foreingName.split("[.]");
859
          if (foreingNameParts.length != 2) {
860
            continue;
861
          }
862
          String columnNameLocal = foreingNameParts[0];
863
          String columnNameForeing = foreingNameParts[1];
864
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)
865
                  || StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
866
            ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
867
                    table,
868
                    variable.name()
869
            );
870
            value_replacements.add(
871
                    new ExpressionBuilder.Value[]{
872
                      variable,
873
                      variable_replacement
874
                    }
875
            );
876
          }
877
        }
878
      }
879 44376 jjdelcerro
880 44750 jjdelcerro
      // Realizamos los reemplazos calculados previamente (value_replacements).
881
      for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
882
        ExpressionBuilder.Value target = replaceValue[0];
883
        ExpressionBuilder.Value replacement = replaceValue[1];
884
        sqlbuilder.select().replace(target, replacement);
885
      }
886
887 45162 omartinez
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
888
        // a los valores referenciados por la funcion FOREING_VALUE.
889
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
890
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
891
        HashSet usedLeftJoins = new HashSet();
892 44750 jjdelcerro
      for (String foreingName : foreing_value_args) {
893
        String[] foreingNameParts = foreingName.split("[.]");
894
        if (foreingNameParts.length != 2) {
895
          continue;
896 44376 jjdelcerro
        }
897 44750 jjdelcerro
        String columnNameLocal = foreingNameParts[0];
898
        String columnNameForeing = foreingNameParts[1];
899
        FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
900
        if (attr.isForeingKey()) {
901
          ForeingKey foreingKey = attr.getForeingKey();
902
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
903
                  .database(table.getDatabase())
904
                  .schema(table.getSchema())
905
                  .name(foreingKey.getTableName());
906
          SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
907
                  .database(table.getDatabase())
908
                  .schema(table.getSchema())
909
                  .name(table.getName());
910 45162 omartinez
911
          if (!usedLeftJoins.contains(foreingTable.getName())) {
912
            sqlbuilder.select().from()
913
                    .left_join(
914
                            foreingTable,
915
                            expbuilder.eq(
916
                                    sqlbuilder.column(mainTable, columnNameLocal),
917
                                    sqlbuilder.column(foreingTable, foreingKey.getCodeName())
918
                            )
919
                    );
920
            usedLeftJoins.add(foreingTable.getName());
921
          }
922
          if (!sqlbuilder.select().has_group_by()) {
923
                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
924
          }
925 44750 jjdelcerro
        }
926 44682 jjdelcerro
      }
927 45385 omartinez
928
        for (SQLBuilder.SelectColumnBuilder column : sqlbuilder.select().getColumns()) {
929
            if(column.getTable()==null && column.getName()!=null) {
930
                column.name(table, column.getName());
931
            }
932
        }
933 44750 jjdelcerro
934
    } catch (Throwable th) {
935 45385 omartinez
      LOGGER.warn("Can't replace FOREING_VALUE function.", th);
936 44750 jjdelcerro
      throw th;
937
    } finally {
938
      LOGGER.trace("Exit from replaceForeingValueFunction.");
939 44376 jjdelcerro
    }
940 44750 jjdelcerro
  }
941 45650 jjdelcerro
942
    @Override
943
    public void setTransaction(DataTransactionServices transaction) {
944
        this.transaction = transaction;
945
    }
946 45717 fdiaz
947
    @Override
948
    public String toString() {
949
        try {
950
            return String.format("%s %x %s", this.getClass().getSimpleName(), this.hashCode(), this.connectionParameters.getUrl());
951
        } catch (Exception e) {
952
            return super.toString();
953
        }
954
    }
955
956 45736 fdiaz
    public String getConnectionProviderStatus() {
957
        return "";
958
    }
959 45650 jjdelcerro
960 43020 jjdelcerro
}