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

History | View | Annotate | Download (53.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.fmap.dal.store.jdbc2.spi;
25

    
26
import java.io.File;
27
import java.sql.Blob;
28
import java.sql.Clob;
29
import java.sql.ResultSet;
30
import java.util.ArrayList;
31
import java.util.HashSet;
32
import java.util.Iterator;
33
import java.util.List;
34
import java.util.function.Predicate;
35
import org.apache.commons.codec.binary.Hex;
36
import org.apache.commons.collections.CollectionUtils;
37
import org.apache.commons.collections.iterators.ReverseListIterator;
38
import org.apache.commons.io.IOUtils;
39
import org.apache.commons.lang3.ArrayUtils;
40
import org.apache.commons.lang3.StringUtils;
41
import org.apache.commons.lang3.builder.ToStringBuilder;
42
import org.apache.commons.lang3.mutable.MutableBoolean;
43
import org.apache.commons.lang3.mutable.MutableObject;
44
import org.apache.commons.lang3.tuple.ImmutablePair;
45
import org.apache.commons.lang3.tuple.Pair;
46
import org.gvsig.expressionevaluator.Code;
47
import org.gvsig.expressionevaluator.Expression;
48
import org.gvsig.expressionevaluator.ExpressionBuilder;
49
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$CONSTANT;
50
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$IDENTIFIER;
51
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
52
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
53
import org.gvsig.expressionevaluator.Function;
54
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
55
import org.gvsig.expressionevaluator.SymbolTable;
56
import org.gvsig.fmap.dal.DALLocator;
57
import org.gvsig.fmap.dal.DataManager;
58
import static org.gvsig.fmap.dal.DataManager.FUNCTION_EXISTS;
59
import static org.gvsig.fmap.dal.DataManager.FUNCTION_FOREIGN_VALUE;
60
import static org.gvsig.fmap.dal.DataManager.FUNCTION_FOREING_VALUE;
61
import org.gvsig.fmap.dal.DataTypes;
62
import org.gvsig.fmap.dal.SQLBuilder;
63
import org.gvsig.fmap.dal.SQLBuilder.Column;
64
import static org.gvsig.fmap.dal.SQLBuilder.PROP_ADD_TABLE_NAME_TO_COLUMNS;
65
import static org.gvsig.fmap.dal.SQLBuilder.PROP_FEATURE_TYPE;
66
import static org.gvsig.fmap.dal.SQLBuilder.PROP_JDBCHELPER;
67
import static org.gvsig.fmap.dal.SQLBuilder.PROP_QUERY;
68
import org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
69
import org.gvsig.fmap.dal.SupportTransactionsHelper;
70
import org.gvsig.fmap.dal.exception.DataException;
71
import org.gvsig.fmap.dal.exception.InitializeException;
72
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
73
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
74
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
75
import org.gvsig.fmap.dal.feature.FeatureQuery;
76
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
77
import org.gvsig.fmap.dal.feature.FeatureType;
78
import org.gvsig.fmap.dal.feature.ForeingKey;
79
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
80
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
81
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
82
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
83
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
84
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemStoreParameters;
85
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
86
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
87
import org.gvsig.fmap.dal.spi.DataTransactionServices;
88
import org.gvsig.fmap.dal.store.db.DBHelper;
89
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
90
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
91
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParametersBase;
92
import org.gvsig.fmap.dal.store.jdbc.JDBCResourceBase;
93
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
94
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParametersBase;
95
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
96
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParametersBase;
97
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
98
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
99
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
100
import org.gvsig.fmap.dal.store.jdbc2.JDBCLibrary;
101
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
102
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
103
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
104
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
105
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
106
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
107
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
108
import org.gvsig.fmap.dal.store.jdbc2.spi.expressionbuilder.formatters.ComputedAttribute;
109
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
110
import org.gvsig.fmap.geom.Geometry;
111
import org.gvsig.fmap.geom.GeometryLocator;
112
import org.gvsig.fmap.geom.GeometryManager;
113
import org.gvsig.tools.dispose.impl.AbstractDisposable;
114
import org.gvsig.tools.evaluator.Evaluator;
115
import org.gvsig.tools.exception.BaseException;
116
import org.gvsig.tools.exception.NotYetImplemented;
117
import org.gvsig.tools.locator.LocatorException;
118
import org.gvsig.tools.util.ContainerUtils;
119
import org.gvsig.tools.visitor.FilteredVisitable;
120
import org.gvsig.tools.visitor.VisitCanceledException;
121
import org.gvsig.tools.visitor.Visitor;
122
import org.slf4j.Logger;
123
import org.slf4j.LoggerFactory;
124

    
125
@SuppressWarnings("UseSpecificCatch")
126
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
127

    
128
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
129
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
130
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
131

    
132
  protected static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
133

    
134
  private ResulSetControler resulSetControler = null;
135

    
136
  // Quien ha creado este helper.
137
  // Se le reenviaran las notificaciones del recurso asociado al helper.
138
  private ResourceConsumer helperClient = null;
139

    
140
  private GeometryManager geometryManager = null;
141

    
142
  private JDBCConnectionParameters connectionParameters;
143

    
144
  private JDBCStoreProvider store;
145

    
146
  protected OperationsFactory operationsFactory = null;
147

    
148
  protected SRSSolver srssolver;
149
  
150
  protected FeatureType providerFeatureType = null;
151
  
152
//  protected DataTransactionServices transaction;
153
  
154
  protected SupportTransactionsHelper transactionsHelper;
155

    
156
  public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
157
        if (connectionParameters == null) {
158
            throw new IllegalArgumentException("Connection parameters can't be null.");
159
        }
160
        this.connectionParameters = connectionParameters;
161
        this.transactionsHelper = new SupportTransactionsHelper();
162

    
163
        // If a particular treatment is required to convert SRS to the 
164
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
165
        this.srssolver = new SRSSolverDumb(this);
166
    }
167

    
168
  protected void initialize(
169
          ResourceConsumer helperClient,
170
          JDBCConnectionParameters connectionParameters,
171
          JDBCStoreProvider store
172
  ) {
173
    this.store = store;
174
    this.helperClient = helperClient;
175
    this.connectionParameters = connectionParameters;
176
    initializeResource(connectionParameters);
177
  }
178

    
179
  protected String getResourceType() {
180
    return JDBCResourceBase.NAME;
181
  }
182

    
183
  @Override
184
  public String getProviderName() {
185
    return JDBCLibrary.NAME;
186
  }
187

    
188
  @Override
189
  public GeometrySupportType getGeometrySupportType() {
190
    // El proveedor generico de JDBC guadara las geoemtrias como WKT
191
    return GeometrySupportType.WKT;
192
  }
193
  
194
    /**
195
     * @return the providerFeatureType
196
     */
197
  @Override
198
    public FeatureType getProviderFeatureType() {
199
        return providerFeatureType;
200
    }
201

    
202
    /**
203
     * @param providerFeatureType the providerFeatureType to set
204
     */
205
  @Override
206
    public void setProviderFeatureType(FeatureType providerFeatureType) {
207
        this.providerFeatureType = providerFeatureType;
208
    }
209

    
210

    
211
  @Override
212
  public boolean hasSpatialFunctions() {
213
    // Por defecto el proveedor generico de JDBC asume que la BBDD no 
214
    // tiene soporte para funciones espaciales.
215
    return false;
216
  }
217

    
218
  @Override
219
  public boolean allowNestedOperations() {
220
    return false;
221
  }
222

    
223
  @Override
224
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
225
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
226
    return true;
227
  }
228

    
229
  @Override
230
  public JDBCSQLBuilderBase createSQLBuilder() {
231
    return new JDBCSQLBuilderBase(this);
232
  }
233

    
234
  @Override
235
  public String getQuoteForIdentifiers() {
236
    return QUOTE_FOR_USE_IN_IDENTIFIERS;
237
  }
238

    
239
  @Override
240
  public boolean allowAutomaticValues() {
241
    return ALLOW_AUTOMATIC_VALUES;
242
  }
243

    
244
  @Override
245
  public boolean supportOffsetInSelect() {
246
    // Asumimos que la BBDD soporta OFFSET
247
    return true;
248
  }
249

    
250
  @Override
251
  public String getQuoteForStrings() {
252
    return QUOTE_FOR_USE_IN_STRINGS;
253
  }
254

    
255
  protected boolean supportCaller(Code.Callable caller) {
256
    if (StringUtils.equalsIgnoreCase(caller.name(), FUNCTION_FOREING_VALUE) ||
257
            StringUtils.equalsIgnoreCase(caller.name(), FUNCTION_FOREIGN_VALUE)
258
            ) {
259
      return true;
260
    }
261
    Function function = caller.function();
262
    if (function == null) {
263
      return false;
264
    }
265
    if (!function.isSQLCompatible()) {
266
      return false;
267
    }
268
    if (!this.hasSpatialFunctions()) {
269
      if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
270
        return false;
271
      }
272
    }
273
    return true;
274
  }
275

    
276
  @Override
277
  @SuppressWarnings("Convert2Lambda")
278
  public boolean supportFilter(final FeatureType type, Evaluator filter) {
279
    if (filter == null) {
280
      // No hay que filtrar nada, asi que se p?ede.
281
      return true;
282
    }
283
    String sql = filter.getSQL();
284
    if (StringUtils.isEmpty(sql)) {
285
      // Hay un filtro, pero la SQL es null, con lo que no podemos
286
      // filtrar por el.
287
      return false;
288
    }
289
    return this.supportExpression(type, sql);
290
  }
291
  
292
    @Override
293
    @SuppressWarnings("Convert2Lambda")
294
    public boolean supportExpression(final FeatureType type, String sql) {
295
        // La expression no es valida si:
296
        // - Hay una subquery especificada en los parametros 
297
        // - Si la sql es null.
298
        // - Si se esta usando alguna funcion no-sql en el getSQL
299
        // - Si se esta invocando a metodos de un objeto
300
        // - Si se estan usando funciones OGC y no soportamos funciones espaciales
301
        // - Si se esta usando algun campo calculado en la expresion de filtro.
302
        // 
303
        // Un proveedor especifico podria sobreescribir el metodo,
304
        // para hilar mas fino al comprobar si soporta el filtro o no.
305
        //
306

    
307
        if (StringUtils.isEmpty(sql)) {
308
            return false;
309
        }
310

    
311
//        if (this.useSubquery()) {
312
//            // Se esta usando una subquery en los parametros de acceso a la
313
//            // BBDD, asi que no podemos filtrar.
314
//            return false;
315
//        }
316

    
317
        // Ahora vamos a comprobar que las funciones que se usan son 
318
        // compatibles sql, y que no se usan funciones OGC si el 
319
        // proveedor dice que no soporta funciones espaciales.
320
        // Tambien comprobaremos que el filtro no usa ningun campo calculado.
321
        final MutableBoolean isCompatible = new MutableBoolean(true);
322
        ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
323
        Code code = manager.compile(sql);
324
        SymbolTable symbolTable = manager.createSymbolTable();
325
        code.link(symbolTable);
326
        try {
327
            code.accept(new Visitor() {
328
                @Override
329
                public void visit(Object code_obj) throws VisitCanceledException, BaseException {
330
                    Code code1 = (Code) code_obj;
331
                    switch (code1.code()) {
332
                        case Code.CALLABLE:
333
                            Code.Callable caller = (Code.Callable) code1;
334
                            if (!supportCaller(caller)) {
335
                                isCompatible.setValue(false);
336
                                LOGGER.debug("Expression not SQL compatible, use '"+caller.name()+"' ("+sql+").");
337
                                throw new VisitCanceledException();
338
                            }
339
                            break;
340
                        case Code.METHOD:
341
                            isCompatible.setValue(false);
342
                            LOGGER.debug("Expression not SQL compatible, use objects methods ("+sql+").");
343
                            throw new VisitCanceledException();
344
                        case Code.IDENTIFIER:
345
                            Code.Identifier identifier = (Code.Identifier) code1;
346
                            if (type != null) {
347
                                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
348
                                if (attrdesc != null) {
349
                                    if (attrdesc.isComputed()) {
350
                                        FeatureAttributeEmulator emulator = attrdesc.getFeatureAttributeEmulator();
351
                                        if (!(emulator instanceof FeatureAttributeEmulatorExpression)) {
352
                                            LOGGER.debug("Expression not SQL compatible, use calculated field '"+attrdesc.getName()+"' ("+sql+").");
353
                                            isCompatible.setValue(false);
354
                                            throw new VisitCanceledException();
355
                                        }
356
                                        Expression expr = ((FeatureAttributeEmulatorExpression) emulator).getExpression();
357
                                        if (!supportExpression(type, expr.getPhrase())) {
358
                                            LOGGER.debug("Expression not SQL compatible, use calculated field '"+attrdesc.getName()+"' ("+expr.getPhrase()+"/"+sql+").");
359
                                            isCompatible.setValue(false);
360
                                            throw new VisitCanceledException();
361
                                        }
362
                                    }
363
                                }
364
                            }
365
                            break;
366
                    }
367
                } 
368
            }, new Predicate<FilteredVisitable>() {
369
                @Override
370
                public boolean test(FilteredVisitable code0) {
371
                    if (code0 instanceof Code.Callable) {
372
                        String name = ((Code.Callable) code0).name();
373
                        return StringUtils.equalsIgnoreCase(name, FUNCTION_$CONSTANT)
374
                                || StringUtils.equalsIgnoreCase(name, FUNCTION_$IDENTIFIER);
375
                    }
376
                    return false;
377
                }
378
            });
379

    
380
        } catch (VisitCanceledException ex) {
381
            // Do nothing
382
        } catch (Exception ex) {
383
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
384
        }
385

    
386
        return isCompatible.booleanValue();
387
    }
388

    
389
  @Override
390
  public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
391
    if (this.useSubquery()) {
392
      return false;
393
    }
394
    if (order == null) {
395
      return true;
396
    }
397
    for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
398
      if (member.hasEvaluator()) {
399
        if (!this.supportFilter(type, member.getEvaluator())) {
400
          return false;
401
        }
402
      }
403
    }
404
    return true;
405
  }
406

    
407
  @Override
408
  public OperationsFactory getOperations() {
409
    if (this.operationsFactory == null && !isClosed()) {
410
      this.operationsFactory = new OperationsFactoryBase(this);
411
    }
412
    return operationsFactory;
413
  }
414

    
415
  protected void initializeResource(JDBCConnectionParameters params) {
416
//        Object[] resourceParams = new Object[]{
417
//            params.getUrl(),
418
//            params.getHost(),
419
//            params.getPort(),
420
//            params.getDBName(),
421
//            params.getUser(),
422
//            params.getPassword(),
423
//            params.getJDBCDriverClassName()
424
//        };
425
//
426
//        try {
427
//            ResourceManagerProviderServices manager
428
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
429
//            JDBCResourceBase resource = (JDBCResourceBase) manager.createAddResource(
430
//                    this.getResourceType(),
431
//                    resourceParams
432
//            );
433
//            this.resource = resource;
434
//            this.resource.addConsumer(this);
435
//        } catch (InitializeException ex) {
436
//            logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
437
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
438
//        }
439

    
440
  }
441

    
442
  @Override
443
  public String getSourceId() {
444
    return this.store.getSourceId();
445
  }
446

    
447
  @Override
448
  public JDBCResourceBase getResource() {
449
    return null;
450
//        return this.resource;
451
  }
452

    
453
  @Override
454
  public JDBCConnection getConnection() throws AccessResourceException {
455
    throw new NotYetImplemented();
456
  }
457

    
458
  @Override
459
  public JDBCConnection getConnectionWritable() throws AccessResourceException {
460
    return this.getConnection();
461
  }
462

    
463
  @Override
464
  public String getConnectionURL() {
465
    return null;
466
  }
467

    
468
  @Override
469
  public JDBCConnectionParameters getConnectionParameters() {
470
    return connectionParameters;
471
  }
472

    
473
  @Override
474
  protected void doDispose() throws BaseException {
475
    JDBCUtils.closeQuietly(this);
476
  }
477

    
478
  @Override
479
  public void close() throws Exception {
480
    JDBCUtils.closeQuietly(this.resulSetControler);
481
    this.resulSetControler = null;
482
    this.operationsFactory = null;
483
    this.srssolver = null;
484
    this.connectionParameters = null;
485
    this.helperClient = null;
486
    this.geometryManager = null;
487
    this.providerFeatureType = null;
488
    this.store = null;
489
//    this.transaction = null;
490
    this.transactionsHelper.setTransaction(null);
491
  }
492
  
493
  public boolean isClosed() {
494
      return this.srssolver == null;
495
  }
496

    
497
  @Override
498
  public boolean closeResourceRequested(ResourceProvider resource) {
499
    return this.helperClient.closeResourceRequested(resource);
500
  }
501

    
502
  @Override
503
  public void resourceChanged(ResourceProvider resource) {
504
    this.helperClient.resourceChanged(resource);
505
  }
506

    
507
  @Override
508
  public GeometryManager getGeometryManager() {
509
    if (this.geometryManager == null) {
510
      this.geometryManager = GeometryLocator.getGeometryManager();
511
    }
512
    return this.geometryManager;
513
  }
514

    
515
  @Override
516
  public ResulSetControler getResulSetControler() {
517
    if (this.resulSetControler == null) {
518
      this.resulSetControler = new ResulSetControlerBase(this);
519
    }
520
    return this.resulSetControler;
521
  }
522

    
523
  @Override
524
  public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
525
    fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
526
  }
527

    
528
  @Override
529
  public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException {
530
    Object value;
531
    try {
532
      int rsIndex = 1;
533
      for (FeatureAttributeDescriptor column : columns) {
534
        switch (column.getType()) {
535
          case DataTypes.GEOMETRY:
536
            value = this.getGeometryFromColumn(rs, rsIndex++);
537
            if (value != null){
538
                ((Geometry)value).setProjection(column.getSRS());
539
            }
540
            break;
541
          default:
542
            value = rs.getObject(rsIndex++);
543
            if (value instanceof Blob) {
544
              Blob blob = (Blob) value;
545
              value = blob.getBytes(1, (int) blob.length());
546
              blob.free();
547
            } else if(value instanceof Clob) {
548
              Clob clob = (Clob) value;
549
              value = new String(IOUtils.toCharArray(clob.getCharacterStream()));
550
              clob.free();
551
          }
552
        }
553
        feature.set(column.getIndex(), value);
554
      }
555
      if (ArrayUtils.isNotEmpty(extraValueNames)) {
556
        feature.setExtraValueNames(extraValueNames);
557
        for (int index = 0; index < extraValueNames.length; index++) {
558
          value = rs.getObject(rsIndex++);
559
          if (value instanceof Blob) {
560
            Blob blob = (Blob) value;
561
            value = blob.getBytes(0, (int) blob.length());
562
            blob.free();
563
          }
564
          feature.setExtraValue(index, value);
565
        }
566
      }
567
    } catch (Exception ex) {
568
      throw new JDBCCantFetchValueException(ex);
569
    }
570
  }
571

    
572
  @Override
573
  public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
574
    return getGeometryFromColumn(rs.get(), index);
575
  }
576

    
577
  @Override
578
  public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
579
    try {
580
      Object value;
581
      Geometry geom;
582
      switch (this.getGeometrySupportType()) {
583
        case WKB:
584
          value = rs.getBytes(index);
585
          if (value == null) {
586
            return null;
587
          }
588
          geom = this.getGeometryManager().createFrom((byte[]) value);
589
          if( geom == null ) {
590
              LOGGER.debug("Can't get geometry from bytes of column "+index);
591
          }
592
          return geom;
593

    
594
        case NATIVE:
595
        case EWKB:
596
          value = rs.getBytes(index);
597
          if (value == null) {
598
            return null;
599
          }
600
          geom = this.getGeometryManager().createFrom((byte[]) value);
601
          if( geom == null ) {
602
              LOGGER.debug("Can't get geometry from bytes of column "+index);
603
          }
604
          return geom;
605
        case WKT:
606
        default:
607
          value = rs.getString(index);
608
          if (value == null) {
609
            return null;
610
          }
611
          geom = this.getGeometryManager().createFrom((String) value);
612
          if( geom == null ) {
613
              LOGGER.debug("Can't get geometry from bytes of column "+index);
614
          }
615
          return geom;
616

    
617
      }
618
    } catch (Exception ex) {
619
      throw new JDBCCantFetchValueException(ex);
620
    }
621
  }
622

    
623
  @Override
624
  public FeatureProvider createFeature(FeatureType featureType) throws DataException {
625
    return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
626
  }
627

    
628
  @Override
629
  public boolean useSubquery() {
630
    if (this.store == null) {
631
      return false;
632
    }
633
    return !StringUtils.isEmpty(this.store.getParameters().getSQL());
634
  }
635

    
636
  @Override
637
  public SRSSolver getSRSSolver() {
638
    return this.srssolver;
639
  }
640

    
641
  @Override
642
  public JDBCStoreProvider createProvider(
643
          JDBCStoreParameters parameters,
644
          DataStoreProviderServices providerServices
645
  ) throws InitializeException {
646

    
647
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
648
            parameters,
649
            providerServices,
650
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
651
            this
652
    );
653
    this.initialize(theStore, parameters, theStore);
654
    return theStore;
655
  }
656

    
657
  @Override
658
  public JDBCServerExplorer createServerExplorer(
659
          JDBCServerExplorerParameters parameters,
660
          DataServerExplorerProviderServices providerServices
661
  ) throws InitializeException {
662

    
663
    JDBCServerExplorer explorer = new JDBCServerExplorerBase(
664
            parameters,
665
            providerServices,
666
            this
667
    );
668
    this.initialize(explorer, parameters, null);
669
    return explorer;
670
  }
671

    
672
  @Override
673
  public JDBCNewStoreParameters createNewStoreParameters() {
674
    return new JDBCNewStoreParametersBase();
675
  }
676

    
677
  @Override
678
  public JDBCStoreParameters createOpenStoreParameters() {
679
    return new JDBCStoreParametersBase();
680
  }
681
  
682
  @Override
683
  public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
684
    JDBCStoreParameters params = this.createOpenStoreParameters();
685
    params.setHost(parameters.getHost());
686
    params.setPort(parameters.getPort());
687
    params.setDBName(parameters.getDBName());
688
    params.setUser(parameters.getUser());
689
    params.setPassword(parameters.getPassword());
690
    params.setCatalog(parameters.getCatalog());
691
    params.setSchema(parameters.getSchema());
692
    params.setJDBCDriverClassName(parameters.getJDBCDriverClassName());
693
    params.setUrl(parameters.getUrl());
694
    if( parameters instanceof FilesystemStoreParameters ) {
695
        File f = ((FilesystemStoreParameters) parameters).getFile();
696
        ((FilesystemStoreParameters) params).setFile(f);
697
    }
698
    params.setBatchSize(parameters.getBatchSize());
699
    return params;
700
  }
701
  
702

    
703
  @Override
704
  public JDBCServerExplorerParameters createServerExplorerParameters() {
705
    return new JDBCServerExplorerParametersBase();
706
  }
707

    
708
  @Override
709
  public String getSourceId(JDBCStoreParameters params) {
710
        StringBuilder builder = new StringBuilder();
711
        builder.append(params.getTable());
712
        builder.append("(");
713
        boolean needComma = false;
714
        if( StringUtils.isNotBlank(params.getHost()) ) {
715
            builder.append("host=");
716
            builder.append(params.getHost());
717
            needComma = true;
718
        }
719
        if( params.getPort()>0 ) {
720
            if (needComma ) {
721
                builder.append(", ");
722
            }
723
            builder.append("port=");
724
            builder.append(params.getPort());
725
            needComma = true;
726
        }
727
        if( StringUtils.isNotBlank(params.getDBName()) ) {
728
            if (needComma ) {
729
                builder.append(", ");
730
            }
731
            builder.append("db=");
732
            builder.append(params.getDBName());
733
            needComma = true;
734
        }
735
        if( StringUtils.isNotBlank(params.getSchema()) ) {
736
            if (needComma ) {
737
                builder.append(", ");
738
            }
739
            builder.append("schema=");
740
            builder.append(params.getSchema());
741
            needComma = true;
742
        }
743
        builder.append(")");
744
        return builder.toString();
745
  }
746

    
747
  @Override
748
  public boolean isThreadSafe() {
749
    return true;
750
  }
751

    
752
  /** 
753
   * This method has been overriden in Oracle provider
754
   * 
755
   * @param sqlbuilder
756
   * @param type
757
   * @param extra_column_names 
758
   */
759
  @Override
760
  public void processSpecialFunctions(
761
          SQLBuilder sqlbuilder,
762
          FeatureType type,
763
          List<String> extra_column_names,
764
          FeatureQuery query) {
765
    replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
766
    replaceExistsFunction(sqlbuilder, type, extra_column_names);
767
    addTableToColumnReferences(sqlbuilder, type, query);
768
  }
769

    
770
  @SuppressWarnings("Convert2Lambda")
771
  protected void replaceExistsFunction(
772
          SQLBuilder sqlbuilder,
773
          FeatureType type,
774
          final List<String> extra_column_names) {
775
    
776
    //  Si lse encuentra una construccion del tipo:
777
    //    SELECT ... FROM ... WHERE ... EXISTS(list, 'EXISTS_ID') ...
778
    //  se traslada a:
779
    //    SELECT ... EXISTS(list) AS EXISTS_ID FROM ... WHERE ... EXISTS(list) ...
780
    //  Y se a?ade el valor ESISTS_ID a las columnas extra a recuperar de la consulta.
781
    //          
782
    
783
    final SQLBuilder.SelectBuilder select = sqlbuilder.select();
784
    final ExpressionBuilder where = select.where();
785
    if (where == null || where.isEmpty() || select.has_group_by() || select.has_aggregate_functions()) {
786
      return;
787
    }
788
    final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
789
    where.accept(new ExpressionBuilder.Visitor() {
790
      @Override
791
      public void visit(ExpressionBuilder.Visitable value) {
792
        if (!(value instanceof ExpressionBuilder.Function)) {
793
          return;
794
        }
795
        ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
796
        if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_EXISTS)) {
797
          return;
798
        }
799
        if (function.parameters().size() != 2) {
800
          return;
801
        }
802
        ExpressionBuilder.Value arg0 = function.parameters().get(0);
803
        ExpressionBuilder.Value arg1 = function.parameters().get(1);
804
        if (arg1 == null) {
805
          return;
806
        }
807
        String columnName = (String) ((ExpressionBuilder.Constant) arg1).value();
808
        SQLBuilder.SelectColumnBuilder column = select.column();
809
        column.value(
810
                sqlbuilder.expression().function(FUNCTION_EXISTS, arg0)
811
        );
812
        column.as(columnName);
813

    
814
        if( extra_column_names!=null ) {
815
          extra_column_names.add(columnName);
816
        }
817
      }
818
    }, null);
819
    if (value_replacements.isEmpty()) {
820
      return;
821
    }
822
    // Realizamos los reemplazos calculados previamente (value_replacements).
823
    for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
824
      ExpressionBuilder.Value target = replaceValue[0];
825
      ExpressionBuilder.Value replacement = replaceValue[1];
826
      sqlbuilder.select().replace(target, replacement);
827
    }
828
  }
829

    
830
  @SuppressWarnings("Convert2Lambda")
831
  protected void replaceForeingValueFunction(
832
          SQLBuilder sqlbuilder,
833
          FeatureType type,
834
          List<String> extra_column_names) {
835
    try {
836
      // See test SQLBuilderTest->testForeingValue()
837
      final ExpressionBuilder where = sqlbuilder.select().where();
838
//      if (where == null || where.isEmpty()) {
839
//        return;
840
//      }
841
      final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
842
      final ExpressionBuilder expbuilder = sqlbuilder.expression();
843

    
844
      final List<String> foreing_value_args;
845
      if (extra_column_names == null || sqlbuilder.select().has_group_by() || sqlbuilder.select().has_aggregate_functions() ) {
846
        foreing_value_args = new ArrayList<>();
847
      } else {
848
        foreing_value_args = extra_column_names;
849
      }
850
      final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
851

    
852
      MutableObject<Boolean> hasForeignValueFunctions = new MutableObject<>(false);
853
      
854
      // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
855
      // el argumento de esta asi como por que tendriamos que sustituirla 
856
      // una vez hechos los left joins que toquen.
857
      sqlbuilder.select().accept(new ExpressionBuilder.Visitor() {
858
        @Override
859
        public void visit(ExpressionBuilder.Visitable value) {
860
          // Requiere que sea la funcion "FOREING_VALUE con un solo 
861
          // argumento que sea una constante de tipo string.
862
          if (!(value instanceof ExpressionBuilder.Function)) {
863
            return;
864
          }
865
          ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
866
          if (!(StringUtils.equalsIgnoreCase(function.name(), FUNCTION_FOREIGN_VALUE)
867
                  || StringUtils.equalsIgnoreCase(function.name(), FUNCTION_FOREING_VALUE))) {
868
            return;
869
          }
870
          if (function.parameters().size() != 1) {
871
            return;
872
          }
873
          ExpressionBuilder.Value arg = function.parameters().get(0);
874
          if (!(arg instanceof ExpressionBuilder.Constant)) {
875
            return;
876
          }
877
          Object arg_value = ((ExpressionBuilder.Constant) arg).value();
878
          if (!(arg_value instanceof CharSequence)) {
879
            return;
880
          }
881
          hasForeignValueFunctions.setValue(true);
882
          String foreing_value_arg = arg_value.toString();
883
          String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
884
          if (foreingNameParts.length != 2) {
885
            // De momento solo tratamos joins entre dos tablas.
886
            return;
887
          }
888
          String columnNameLocal = foreingNameParts[0];
889
          String columnNameForeing = foreingNameParts[1];
890
          FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
891
          if (attr==null) {
892
              throw new RuntimeException("Cannot find in feature type attribute:"+columnNameLocal);
893
          }
894
          if (!attr.isForeingKey()) {
895
            // Uhm... si el argumento no referencia a un campo que es
896
            // clave ajena no lo procesamos. 
897
            // ? Deberiamos lanzar un error ?
898
            return;
899
          }
900
          // Nos guardaremos por que hay que reemplazar la funcion 
901
          // FOREING_VALUE, y su argumento para mas tarde.
902
          ForeingKey foreingKey = attr.getForeingKey();
903
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
904
                  .database(table.getDatabase())
905
                  .schema(table.getSchema())
906
                  .name(foreingKey.getTableName());
907
          // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
908
          // que toca de la tabla a la que referencia la clave ajena.
909
          ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
910
          value_replacements.add(
911
                  new ExpressionBuilder.Value[]{
912
                    function,
913
                    function_replacement
914
                  }
915
          );
916
          if (!foreing_value_args.contains(foreing_value_arg)) {
917
            foreing_value_args.add(foreing_value_arg);
918
          }
919
        }
920
      }, null);
921
      
922
      // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
923
      // hacer nada.
924
      if ( !hasForeignValueFunctions.getValue() || foreing_value_args.isEmpty()) {
925
        return;
926
      }
927

    
928
      // Calculamos que referencias de columnas hemos de cambiar para 
929
      // que no aparezcan ambiguedades al hacer el join (nombres de
930
      // columna de una y otra tabla que coincidan).
931
      // Para las columnas que puedan dar conflicto se prepara un reemplazo 
932
      // de estas que tenga el nombre de tabla.
933
      for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
934
        if (variable == null || variable instanceof SQLBuilderBase.ColumnBase) {
935
          continue;
936
        }
937
        if (ContainerUtils.contains(extra_column_names, variable.name(), ContainerUtils.EQUALS_IGNORECASE_COMPARATOR)) {
938
            continue;
939
        }
940
        boolean alreadyReplaced = false;
941
        for (String foreingName : foreing_value_args) {
942
          String[] foreingNameParts = foreingName.split("[.]");
943
          if (foreingNameParts.length != 2) {
944
            continue;
945
          }
946
          String columnNameLocal = foreingNameParts[0];
947
          String columnNameForeing = foreingNameParts[1];
948
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)) {
949
              if(alreadyReplaced){
950
                  throw new RuntimeException("Column reference \""+ columnNameForeing+"\" is ambiguous");
951
              }
952
              alreadyReplaced = true;
953
              
954
              FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
955
              if (attr == null) {
956
                  throw new RuntimeException("Cannot find in feature type attribute:" + columnNameLocal);
957
              }
958
              if (attr.isForeingKey()) {
959
                  // Nos guardaremos por que hay que reemplazar la funcion 
960
                  // FOREING_VALUE, y su argumento para mas tarde.
961
                  ForeingKey foreingKey = attr.getForeingKey();
962
                  SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
963
                          .database(table.getDatabase())
964
                          .schema(table.getSchema())
965
                          .name(foreingKey.getTableName());
966
                  ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
967
                          foreingTable,
968
                          variable.name()
969
                  );
970
                  value_replacements.add(
971
                          new ExpressionBuilder.Value[]{
972
                              variable,
973
                              variable_replacement
974
                          }
975
                  );
976
              }
977
          }
978
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
979
            ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
980
                    table,
981
                    variable.name()
982
            );
983
            value_replacements.add(
984
                    new ExpressionBuilder.Value[]{
985
                      variable,
986
                      variable_replacement
987
                    }
988
            );
989
          }
990
        }
991
      }
992

    
993
      // Realizamos los reemplazos calculados previamente (value_replacements).
994
      for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
995
        ExpressionBuilder.Value target = replaceValue[0];
996
        ExpressionBuilder.Value replacement = replaceValue[1];
997
        sqlbuilder.select().replace(target, replacement);
998
      }
999

    
1000
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
1001
        // a los valores referenciados por la funcion FOREING_VALUE.
1002
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
1003
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
1004
        HashSet usedLeftJoins = new HashSet();
1005
      for (String foreingName : foreing_value_args) {
1006
        String[] foreingNameParts = foreingName.split("[.]");
1007
        if (foreingNameParts.length != 2) {
1008
          continue;
1009
        }
1010
        String columnNameLocal = foreingNameParts[0];
1011
        String columnNameForeing = foreingNameParts[1];
1012
        FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
1013
        if (attr.isForeingKey()) {
1014
          ForeingKey foreingKey = attr.getForeingKey();
1015
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
1016
                  .database(table.getDatabase())
1017
                  .schema(table.getSchema())
1018
                  .name(foreingKey.getTableName());
1019
          SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
1020
                  .database(table.getDatabase())
1021
                  .schema(table.getSchema())
1022
                  .name(table.getName());
1023
          
1024
          if (!usedLeftJoins.contains(foreingTable.getName())) {
1025
            Column v = sqlbuilder.column(mainTable, columnNameLocal);
1026
            v.setProperty(PROP_FEATURE_TYPE, type);
1027
            v.setProperty(PROP_JDBCHELPER, this);
1028
            v.setProperty(PROP_QUERY, sqlbuilder.select().getProperty(PROP_QUERY));
1029
            
1030
            sqlbuilder.select().from()
1031
                    .left_join(
1032
                            foreingTable,
1033
                            expbuilder.eq(
1034
                                    v,
1035
                                    sqlbuilder.column(foreingTable, foreingKey.getCodeName())
1036
                            )
1037
                    );
1038
            usedLeftJoins.add(foreingTable.getName());
1039
          }
1040
          //No est? claro si debe a?adirse esta columna o no, OJO quitarlo o ponerlo altera los test de  H2
1041
          //Si se comentarizan ojo con extra_column_names ya que se habr?n a?adido columnas que no estar?n en la select
1042
          if ( !(sqlbuilder.select().has_group_by() || sqlbuilder.select().has_aggregate_functions()) ) {
1043
                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
1044
          }
1045
        }
1046
      }
1047
      
1048
    } catch (Throwable th) {
1049
      LOGGER.warn("Can't replace FOREIGN_VALUE function.", th);
1050
      throw th;
1051
    } finally {
1052
//      LOGGER.trace("Exit from replaceForeingValueFunction.");
1053
    }
1054
  }
1055
 
1056
    protected void addTableToColumnReferences(SQLBuilder sqlbuilder, FeatureType type, FeatureQuery query) {
1057
        SelectBuilder select = sqlbuilder.select();
1058
        List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables = new ArrayList<>();
1059
        collectTablesFromSelect(select, tables, type);
1060

    
1061
        addTableToColumnReferencesInSingleSelect(
1062
                sqlbuilder, 
1063
                select, 
1064
                tables,
1065
                query
1066
        );
1067
        
1068
    }
1069

    
1070
    private void collectTablesFromSelect(SelectBuilder select, List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables, FeatureType type) throws LocatorException {
1071
        DataManager dataManager = DALLocator.getDataManager();
1072
        
1073
        List<SQLBuilder.JoinBuilder> joins = select.from().getJoins();
1074
        if(!CollectionUtils.isEmpty(joins)){
1075
            for (SQLBuilder.JoinBuilder join : joins) {
1076
                SQLBuilder.TableNameBuilder joinTable = join.getTable();
1077
                FeatureType featureType = dataManager.getStoresRepository().getFeatureType(joinTable.getName());
1078
                if(featureType != null){
1079
                    tables.add(new ImmutablePair<>(joinTable,featureType));
1080
                }
1081
            }
1082
        }
1083
        SQLBuilder.TableNameBuilder table = select.from().table();
1084
        if(type == null){
1085
            type = dataManager.getStoresRepository().getFeatureType(table.getName());
1086
            
1087
        }
1088
        if(type != null){
1089
            tables.add(new ImmutablePair<>(table,type));
1090
        }
1091
    }
1092
    
1093
    private boolean isComputed(String name, FeatureType featureType, FeatureQuery query ) {
1094
        FeatureAttributeDescriptor attr = featureType.getAttributeDescriptor(name);
1095
        if( attr !=null ) {
1096
            return attr.isComputed();
1097
        }
1098
        if( query == null ) {
1099
            return false;
1100
        }
1101
        attr = query.getExtraColumns().get(name);
1102
        if( attr !=null ) {
1103
            return true; //attr.isComputed();
1104
        }
1105
        return false;
1106
    }
1107
    
1108
    protected void addTableToColumnReferencesInSingleSelect(SQLBuilder sqlbuilder, SelectBuilder select, List<Pair<SQLBuilder.TableNameBuilder,FeatureType>> outerTables, FeatureQuery query) {
1109
        
1110
        FeatureType mainFeatureType = CollectionUtils.isEmpty(outerTables)?null:outerTables.get(0).getRight();
1111
        
1112
        final SQLBuilder.TableNameBuilder fromTable = select.from().table();
1113

    
1114
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
1115
        List<ExpressionBuilder.Variable> variables = new ArrayList<>();
1116
//        List<ExpressionBuilder.Value> variablesToExclude = new ArrayList<>();
1117
        
1118
        select.accept((ExpressionBuilder.Visitable value) -> {
1119
            if(value instanceof Column) {
1120
                Column c = (Column)value;
1121
                if(c.table() == null || !c.table().has_name() || !c.table().has_schema()){
1122
                    variables.add(c);
1123
                }
1124
                
1125
            } else if(value instanceof ExpressionBuilder.Variable) {
1126
                variables.add((ExpressionBuilder.Variable) value);
1127
            }
1128

    
1129
        }, new ExpressionBuilder.VisitorFilter() {
1130
            @Override
1131
            public boolean skipChildren() {
1132
                return true;
1133
            }
1134
            
1135
            @Override
1136
            public boolean accept(ExpressionBuilder.Visitable visitable) {
1137
                if(select == visitable){
1138
                    return true;
1139
                }
1140
                if(visitable instanceof SelectBuilder) {
1141
                    ArrayList<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables = new ArrayList<>(outerTables);
1142
                    collectTablesFromSelect((SelectBuilder) visitable, tables, null);
1143
                    
1144
                    addTableToColumnReferencesInSingleSelect(sqlbuilder, (SelectBuilder) visitable, tables, query);
1145
                    return false;
1146
                }
1147
                return true;
1148
            }
1149
        });
1150
        
1151
        for (ExpressionBuilder.Variable variable : variables) {
1152
            ExpressionBuilder.Variable variable_replacement = null;
1153
            
1154
            Pair<SQLBuilder.TableNameBuilder, FeatureType> tableNameAndFeatureType = getTableAndFeatureType(outerTables, variable.name());
1155
            if (tableNameAndFeatureType != null) {
1156
                
1157
                if( !isComputed(variable.name(), tableNameAndFeatureType.getRight(), query) ) {
1158
                    
1159
                    SQLBuilder.TableNameBuilder variableTable = tableNameAndFeatureType.getLeft();
1160

    
1161
                    if (variable instanceof SQLBuilder.Column) {
1162
                        Column column = (SQLBuilder.Column) variable;
1163
                        if (column.table() != null && column.table().has_name()) {
1164
                            if (column.table().has_schema()) {
1165
                                //La columna tiene tabla y esquema => No hacemos nada
1166
                            } else {
1167
                                if(column.table().getName().equals(variableTable.getName())){
1168
                                    SQLBuilder.TableNameBuilder t = sqlbuilder.createTableNameBuilder()
1169
                                            .name(column.table().getName())
1170
                                            .schema(variableTable.getSchema());
1171
                                    variable_replacement = sqlbuilder.column_from(
1172
                                            t,
1173
                                            variable
1174
                                    );
1175
                                } else {
1176
                                    if(fromTable.has_schema()){
1177
                                        SQLBuilder.TableNameBuilder t = sqlbuilder.createTableNameBuilder()
1178
                                                .name(column.table().getName())
1179
                                                .schema(fromTable.getSchema()); //Mismo esquema que en el FROM
1180
                                        variable_replacement = sqlbuilder.column_from(
1181
                                                t,
1182
                                                variable
1183
                                        );
1184
                                    } else {
1185
                                        // No tiene esquema ni podemos averiguarlo => No hacemos nada
1186
                                    }
1187
                                }
1188
                            }
1189
                        } else {
1190
                            column = sqlbuilder.column_from(tableNameAndFeatureType.getLeft(), variable);
1191
                            column.setProperty(SQLBuilder.PROP_FEATURE_TYPE, tableNameAndFeatureType.getRight());
1192
                            variable_replacement = column;
1193
                        }
1194
                    } else {
1195
                        Column column = sqlbuilder.column_from(tableNameAndFeatureType.getLeft(), variable);
1196
                        column.setProperty(SQLBuilder.PROP_FEATURE_TYPE, tableNameAndFeatureType.getRight());
1197
                        variable_replacement = column;
1198
                    }
1199
                }
1200

    
1201
            } else {
1202
                if( !isComputed(variable.name(), mainFeatureType, query) ) {
1203
                    
1204
                    if (variable instanceof SQLBuilder.Column) {
1205
                        Column column = (SQLBuilder.Column) variable;
1206
                        if (column.table() == null || !column.table().has_name()) {
1207
                            variable_replacement = sqlbuilder.column_from(
1208
                                    fromTable,
1209
                                    variable
1210
                            );
1211
                        } else if (!column.table().has_schema()) {
1212
                            if(fromTable.has_schema()){
1213
                                SQLBuilder.TableNameBuilder t = sqlbuilder.createTableNameBuilder()
1214
                                        .name(column.table().getName())
1215
                                        .schema(fromTable.getSchema()); //Mismo esquema que en el FROM
1216
                                variable_replacement = sqlbuilder.column_from(
1217
                                        t,
1218
                                        variable
1219
                                );
1220
                            } else {
1221
                                // No tiene esquema ni podemos averiguarlo => No hacemos nada
1222
                            }
1223
                        }
1224
                    } else {
1225
                        if( StringUtils.equals(fromTable.getName(), variable.name()) ) {
1226
                            LOGGER.debug("Uff, la variable coincide con el nombre de la tabla...\nNo esta nada claro que haya que ponerle el prefijo,\nde momento no hacemos nada.");
1227
                        } else {
1228
                            variable_replacement = sqlbuilder.column_from(
1229
                                    fromTable,
1230
                                    variable
1231
                            );
1232
                        }
1233
                    }
1234
                }
1235
            }
1236
            if(variable_replacement != null){
1237
                value_replacements.add(
1238
                        new ExpressionBuilder.Value[]{
1239
                            variable,
1240
                            variable_replacement
1241
                        }
1242
                );
1243
            }
1244

    
1245
        }
1246

    
1247
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
1248
            ExpressionBuilder.Value target = replaceValue[0];
1249
            ExpressionBuilder.Value replacement = replaceValue[1];
1250
            Boolean addTableName = (Boolean) target.getProperty(PROP_ADD_TABLE_NAME_TO_COLUMNS);
1251
            if(addTableName == null || !addTableName){
1252
                continue;
1253
            }
1254
            select.replace(target, replacement);
1255
        }
1256

    
1257
    }
1258
    
1259
    protected Pair<SQLBuilder.TableNameBuilder, FeatureType> getTableAndFeatureType(List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> outerTables, String columnName) {
1260
//        ListIterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> it = outerTables.listIterator(outerTables.size());
1261
//        
1262
//        while (it.hasPrevious()) {
1263
//            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = it.previous();
1264
//            FeatureType featureType = pair.getRight();
1265
//            if(featureType.get(columnName) != null){
1266
//                return pair;
1267
//            }
1268
//        }
1269
        
1270
//        ListIterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> it = new ReverseListIterator(outerTables);
1271
//        while (it.hasNext()) {
1272
//            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = it.next();
1273
//            FeatureType featureType = pair.getRight();
1274
//            if(featureType.get(columnName) != null){
1275
//                return pair;
1276
//            }
1277
//        }
1278
        
1279
        for (Iterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> iterator = new ReverseListIterator(outerTables); iterator.hasNext();) {
1280
            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = iterator.next();
1281
            FeatureType featureType = pair.getRight();
1282
            if(featureType.get(columnName) != null){
1283
                return pair;
1284
            }
1285
        }
1286

    
1287
        return null;
1288
    }
1289
    
1290
    @Override
1291
    public void setTransaction(DataTransactionServices transaction) {
1292
        this.transactionsHelper.setTransaction(transaction);
1293
    }
1294
    
1295
    @Override
1296
    public String toString() {
1297
        try {
1298
            ToStringBuilder builder = new ToStringBuilder(this);
1299
            builder.append("hash", String.format("%x", this.hashCode()));
1300
            builder.append("url", this.connectionParameters.getUrl());
1301
            return builder.toString();
1302
        } catch (Exception e) {
1303
            return super.toString();
1304
        }
1305
    }
1306

    
1307
  @Override
1308
    public String getConnectionProviderStatus() {
1309
        return "";
1310
    }
1311
        
1312
  @Override
1313
        public void expandCalculedColumns(JDBCSQLBuilderBase sqlbuilder) {
1314
            ComputedAttribute computedAttributeFormater = new ComputedAttribute(sqlbuilder, sqlbuilder.formatter());
1315
            for (int i = 0; i < 10; i++) {
1316
                List<Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value>> variablesToReplace = new ArrayList<>();
1317
                sqlbuilder.accept((ExpressionBuilder.Visitable value) -> {
1318
                    if (computedAttributeFormater.canApply((ExpressionBuilder.Value) value)) {
1319
                        ExpressionBuilder.Variable variable = (ExpressionBuilder.Variable) value;
1320
                        ExpressionBuilder.Value replace = computedAttributeFormater.expandedValue(variable);
1321
                        variablesToReplace.add(Pair.of(variable, replace));
1322
                    }
1323
                }, null);
1324
                if (variablesToReplace.isEmpty()) {
1325
                    break;
1326
                } 
1327
                for (Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value> entry : variablesToReplace) {
1328
                    ExpressionBuilder.Value variable = entry.getKey();
1329
                    ExpressionBuilder.Value replace = entry.getValue();
1330
                    Boolean addTableName = (Boolean) variable.getProperty(PROP_ADD_TABLE_NAME_TO_COLUMNS);
1331
                    if (addTableName != null && addTableName) {
1332
                        sqlbuilder.setProperties(replace, null, PROP_ADD_TABLE_NAME_TO_COLUMNS,true);
1333
                    }
1334
                    sqlbuilder.select().replace(variable, replace);
1335
                }
1336
            }
1337
    }
1338
        
1339
    @Override
1340
    public DataTransactionServices getTransaction() {
1341
        return (DataTransactionServices) this.transactionsHelper.getTransaction();
1342
    }
1343

    
1344
    public ConnectionProvider getConnectionProvider() {
1345
        return new FakeConnectionProvider(connectionParameters);
1346
    }
1347
    
1348
    protected String getConnectionProviderKey(JDBCConnectionParameters connectionParameters) {
1349
        String pass = Hex.encodeHexString((connectionParameters.getPassword()+"").getBytes());
1350
//        String pass = connectionParameters.getPassword();
1351
        return connectionParameters.getUrl() + ";user:"+connectionParameters.getUser()+"@"+pass;
1352
    }
1353
    
1354
}