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

History | View | Annotate | Download (50.7 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 org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
66
import org.gvsig.fmap.dal.exception.DataException;
67
import org.gvsig.fmap.dal.exception.InitializeException;
68
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
69
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
70
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
71
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
72
import org.gvsig.fmap.dal.feature.FeatureType;
73
import org.gvsig.fmap.dal.feature.ForeingKey;
74
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
75
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
76
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
77
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
78
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
79
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemStoreParameters;
80
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
81
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
82
import org.gvsig.fmap.dal.spi.DataTransactionServices;
83
import org.gvsig.fmap.dal.store.db.DBHelper;
84
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
85
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
86
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParametersBase;
87
import org.gvsig.fmap.dal.store.jdbc.JDBCResourceBase;
88
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
89
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParametersBase;
90
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
91
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParametersBase;
92
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
93
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
94
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
95
import org.gvsig.fmap.dal.store.jdbc2.JDBCLibrary;
96
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
97
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
98
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
99
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
100
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
101
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
102
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
103
import org.gvsig.fmap.dal.store.jdbc2.spi.expressionbuilder.formatters.ComputedAttribute;
104
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
105
import org.gvsig.fmap.geom.Geometry;
106
import org.gvsig.fmap.geom.GeometryLocator;
107
import org.gvsig.fmap.geom.GeometryManager;
108
import org.gvsig.tools.dispose.impl.AbstractDisposable;
109
import org.gvsig.tools.evaluator.Evaluator;
110
import org.gvsig.tools.exception.BaseException;
111
import org.gvsig.tools.exception.NotYetImplemented;
112
import org.gvsig.tools.locator.LocatorException;
113
import org.gvsig.tools.util.ContainerUtils;
114
import org.gvsig.tools.visitor.FilteredVisitable;
115
import org.gvsig.tools.visitor.VisitCanceledException;
116
import org.gvsig.tools.visitor.Visitor;
117
import org.slf4j.Logger;
118
import org.slf4j.LoggerFactory;
119

    
120
@SuppressWarnings("UseSpecificCatch")
121
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
122

    
123
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
124
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
125
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
126

    
127
  private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
128

    
129
  private ResulSetControler resulSetControler = null;
130

    
131
  // Quien ha creado este helper.
132
  // Se le reenviaran las notificaciones del recurso asociado al helper.
133
  private ResourceConsumer helperClient = null;
134

    
135
  private GeometryManager geometryManager = null;
136

    
137
  private JDBCConnectionParameters connectionParameters;
138

    
139
  private JDBCStoreProvider store;
140

    
141
  protected OperationsFactory operationsFactory = null;
142

    
143
  protected SRSSolver srssolver;
144
  
145
  protected FeatureType providerFeatureType = null;
146
  
147
  protected DataTransactionServices transaction;
148

    
149
  public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
150
        if (connectionParameters == null) {
151
            throw new IllegalArgumentException("Connection parameters can't be null.");
152
        }
153
        this.connectionParameters = connectionParameters;
154

    
155
        // If a particular treatment is required to convert SRS to the 
156
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
157
        this.srssolver = new SRSSolverDumb(this);
158
    }
159

    
160
  protected void initialize(
161
          ResourceConsumer helperClient,
162
          JDBCConnectionParameters connectionParameters,
163
          JDBCStoreProvider store
164
  ) {
165
    this.store = store;
166
    this.helperClient = helperClient;
167
    this.connectionParameters = connectionParameters;
168
    initializeResource(connectionParameters);
169
  }
170

    
171
  protected String getResourceType() {
172
    return JDBCResourceBase.NAME;
173
  }
174

    
175
  @Override
176
  public String getProviderName() {
177
    return JDBCLibrary.NAME;
178
  }
179

    
180
  @Override
181
  public GeometrySupportType getGeometrySupportType() {
182
    // El proveedor generico de JDBC guadara las geoemtrias como WKT
183
    return GeometrySupportType.WKT;
184
  }
185
  
186
    /**
187
     * @return the providerFeatureType
188
     */
189
  @Override
190
    public FeatureType getProviderFeatureType() {
191
        return providerFeatureType;
192
    }
193

    
194
    /**
195
     * @param providerFeatureType the providerFeatureType to set
196
     */
197
  @Override
198
    public void setProviderFeatureType(FeatureType providerFeatureType) {
199
        this.providerFeatureType = providerFeatureType;
200
    }
201

    
202

    
203
  @Override
204
  public boolean hasSpatialFunctions() {
205
    // Por defecto el proveedor generico de JDBC asume que la BBDD no 
206
    // tiene soporte para funciones espaciales.
207
    return false;
208
  }
209

    
210
  @Override
211
  public boolean allowNestedOperations() {
212
    return false;
213
  }
214

    
215
  @Override
216
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
217
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
218
    return true;
219
  }
220

    
221
  @Override
222
  public JDBCSQLBuilderBase createSQLBuilder() {
223
    return new JDBCSQLBuilderBase(this);
224
  }
225

    
226
  @Override
227
  public String getQuoteForIdentifiers() {
228
    return QUOTE_FOR_USE_IN_IDENTIFIERS;
229
  }
230

    
231
  @Override
232
  public boolean allowAutomaticValues() {
233
    return ALLOW_AUTOMATIC_VALUES;
234
  }
235

    
236
  @Override
237
  public boolean supportOffsetInSelect() {
238
    // Asumimos que la BBDD soporta OFFSET
239
    return true;
240
  }
241

    
242
  @Override
243
  public String getQuoteForStrings() {
244
    return QUOTE_FOR_USE_IN_STRINGS;
245
  }
246

    
247
  protected boolean supportCaller(Code.Callable caller) {
248
    if (StringUtils.equalsIgnoreCase(caller.name(), FUNCTION_FOREING_VALUE) ||
249
            StringUtils.equalsIgnoreCase(caller.name(), FUNCTION_FOREIGN_VALUE)
250
            ) {
251
      return true;
252
    }
253
    Function function = caller.function();
254
    if (function == null) {
255
      return false;
256
    }
257
    if (!function.isSQLCompatible()) {
258
      return false;
259
    }
260
    if (!this.hasSpatialFunctions()) {
261
      if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
262
        return false;
263
      }
264
    }
265
    return true;
266
  }
267

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

    
299
        if (StringUtils.isEmpty(sql)) {
300
            return false;
301
        }
302

    
303
//        if (this.useSubquery()) {
304
//            // Se esta usando una subquery en los parametros de acceso a la
305
//            // BBDD, asi que no podemos filtrar.
306
//            return false;
307
//        }
308

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

    
368
        } catch (VisitCanceledException ex) {
369
            // Do nothing
370
        } catch (Exception ex) {
371
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
372
        }
373

    
374
        return isCompatible.booleanValue();
375
    }
376

    
377
  @Override
378
  public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
379
    if (this.useSubquery()) {
380
      return false;
381
    }
382
    if (order == null) {
383
      return true;
384
    }
385
    for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
386
      if (member.hasEvaluator()) {
387
        if (!this.supportFilter(type, member.getEvaluator())) {
388
          return false;
389
        }
390
      }
391
    }
392
    return true;
393
  }
394

    
395
  @Override
396
  public OperationsFactory getOperations() {
397
    if (this.operationsFactory == null && !isClosed()) {
398
      this.operationsFactory = new OperationsFactoryBase(this);
399
    }
400
    return operationsFactory;
401
  }
402

    
403
  protected void initializeResource(JDBCConnectionParameters params) {
404
//        Object[] resourceParams = new Object[]{
405
//            params.getUrl(),
406
//            params.getHost(),
407
//            params.getPort(),
408
//            params.getDBName(),
409
//            params.getUser(),
410
//            params.getPassword(),
411
//            params.getJDBCDriverClassName()
412
//        };
413
//
414
//        try {
415
//            ResourceManagerProviderServices manager
416
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
417
//            JDBCResourceBase resource = (JDBCResourceBase) manager.createAddResource(
418
//                    this.getResourceType(),
419
//                    resourceParams
420
//            );
421
//            this.resource = resource;
422
//            this.resource.addConsumer(this);
423
//        } catch (InitializeException ex) {
424
//            logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
425
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
426
//        }
427

    
428
  }
429

    
430
  @Override
431
  public String getSourceId() {
432
    return this.store.getSourceId();
433
  }
434

    
435
  @Override
436
  public JDBCResourceBase getResource() {
437
    return null;
438
//        return this.resource;
439
  }
440

    
441
  @Override
442
  public JDBCConnection getConnection() throws AccessResourceException {
443
    throw new NotYetImplemented();
444
  }
445

    
446
  @Override
447
  public JDBCConnection getConnectionWritable() throws AccessResourceException {
448
    return this.getConnection();
449
  }
450

    
451
  @Override
452
  public String getConnectionURL() {
453
    return null;
454
  }
455

    
456
  @Override
457
  public JDBCConnectionParameters getConnectionParameters() {
458
    return connectionParameters;
459
  }
460

    
461
  @Override
462
  protected void doDispose() throws BaseException {
463
    JDBCUtils.closeQuietly(this);
464
  }
465

    
466
  @Override
467
  public void close() throws Exception {
468
    JDBCUtils.closeQuietly(this.resulSetControler);
469
    this.resulSetControler = null;
470
    this.operationsFactory = null;
471
    this.srssolver = null;
472
    this.connectionParameters = null;
473
    this.helperClient = null;
474
    this.geometryManager = null;
475
    this.providerFeatureType = null;
476
    this.store = null;
477
    this.transaction = null;
478
  }
479
  
480
  public boolean isClosed() {
481
      return this.srssolver == null;
482
  }
483

    
484
  @Override
485
  public boolean closeResourceRequested(ResourceProvider resource) {
486
    return this.helperClient.closeResourceRequested(resource);
487
  }
488

    
489
  @Override
490
  public void resourceChanged(ResourceProvider resource) {
491
    this.helperClient.resourceChanged(resource);
492
  }
493

    
494
  @Override
495
  public GeometryManager getGeometryManager() {
496
    if (this.geometryManager == null) {
497
      this.geometryManager = GeometryLocator.getGeometryManager();
498
    }
499
    return this.geometryManager;
500
  }
501

    
502
  @Override
503
  public ResulSetControler getResulSetControler() {
504
    if (this.resulSetControler == null) {
505
      this.resulSetControler = new ResulSetControlerBase(this);
506
    }
507
    return this.resulSetControler;
508
  }
509

    
510
  @Override
511
  public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
512
    fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
513
  }
514

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

    
559
  @Override
560
  public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
561
    return getGeometryFromColumn(rs.get(), index);
562
  }
563

    
564
  @Override
565
  public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
566
    try {
567
      Object value;
568
      Geometry geom;
569
      switch (this.getGeometrySupportType()) {
570
        case NATIVE:
571
        case WKB:
572
          value = rs.getBytes(index);
573
          if (value == null) {
574
            return null;
575
          }
576
          geom = this.getGeometryManager().createFrom((byte[]) value);
577
          if( geom == null ) {
578
              LOGGER.debug("Can't get geometry from bytes of column "+index);
579
          }
580
          return geom;
581

    
582

    
583
        case EWKB:
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
        case WKT:
594
        default:
595
          value = rs.getString(index);
596
          if (value == null) {
597
            return null;
598
          }
599
          geom = this.getGeometryManager().createFrom((String) value);
600
          if( geom == null ) {
601
              LOGGER.debug("Can't get geometry from bytes of column "+index);
602
          }
603
          return geom;
604

    
605
      }
606
    } catch (Exception ex) {
607
      throw new JDBCCantFetchValueException(ex);
608
    }
609
  }
610

    
611
  @Override
612
  public FeatureProvider createFeature(FeatureType featureType) throws DataException {
613
    return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
614
  }
615

    
616
  @Override
617
  public boolean useSubquery() {
618
    if (this.store == null) {
619
      return false;
620
    }
621
    return !StringUtils.isEmpty(this.store.getParameters().getSQL());
622
  }
623

    
624
  @Override
625
  public SRSSolver getSRSSolver() {
626
    return this.srssolver;
627
  }
628

    
629
  @Override
630
  public JDBCStoreProvider createProvider(
631
          JDBCStoreParameters parameters,
632
          DataStoreProviderServices providerServices
633
  ) throws InitializeException {
634

    
635
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
636
            parameters,
637
            providerServices,
638
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
639
            this
640
    );
641
    this.initialize(theStore, parameters, theStore);
642
    return theStore;
643
  }
644

    
645
  @Override
646
  public JDBCServerExplorer createServerExplorer(
647
          JDBCServerExplorerParameters parameters,
648
          DataServerExplorerProviderServices providerServices
649
  ) throws InitializeException {
650

    
651
    JDBCServerExplorer explorer = new JDBCServerExplorerBase(
652
            parameters,
653
            providerServices,
654
            this
655
    );
656
    this.initialize(explorer, parameters, null);
657
    return explorer;
658
  }
659

    
660
  @Override
661
  public JDBCNewStoreParameters createNewStoreParameters() {
662
    return new JDBCNewStoreParametersBase();
663
  }
664

    
665
  @Override
666
  public JDBCStoreParameters createOpenStoreParameters() {
667
    return new JDBCStoreParametersBase();
668
  }
669
  
670
  @Override
671
  public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
672
    JDBCStoreParameters params = this.createOpenStoreParameters();
673
    params.setHost(parameters.getHost());
674
    params.setPort(parameters.getPort());
675
    params.setDBName(parameters.getDBName());
676
    params.setUser(parameters.getUser());
677
    params.setPassword(parameters.getPassword());
678
    params.setCatalog(parameters.getCatalog());
679
    params.setSchema(parameters.getSchema());
680
    params.setJDBCDriverClassName(parameters.getJDBCDriverClassName());
681
    params.setUrl(parameters.getUrl());
682
    if( parameters instanceof FilesystemStoreParameters ) {
683
        File f = ((FilesystemStoreParameters) parameters).getFile();
684
        ((FilesystemStoreParameters) params).setFile(f);
685
    }
686
    params.setBatchSize(parameters.getBatchSize());
687
    return params;
688
  }
689
  
690

    
691
  @Override
692
  public JDBCServerExplorerParameters createServerExplorerParameters() {
693
    return new JDBCServerExplorerParametersBase();
694
  }
695

    
696
  @Override
697
  public String getSourceId(JDBCStoreParameters params) {
698
        StringBuilder builder = new StringBuilder();
699
        builder.append(params.getTable());
700
        builder.append("(");
701
        boolean needComma = false;
702
        if( StringUtils.isNotBlank(params.getHost()) ) {
703
            builder.append("host=");
704
            builder.append(params.getHost());
705
            needComma = true;
706
        }
707
        if( params.getPort()>0 ) {
708
            if (needComma ) {
709
                builder.append(", ");
710
            }
711
            builder.append("port=");
712
            builder.append(params.getPort());
713
            needComma = true;
714
        }
715
        if( StringUtils.isNotBlank(params.getDBName()) ) {
716
            if (needComma ) {
717
                builder.append(", ");
718
            }
719
            builder.append("db=");
720
            builder.append(params.getDBName());
721
            needComma = true;
722
        }
723
        if( StringUtils.isNotBlank(params.getSchema()) ) {
724
            if (needComma ) {
725
                builder.append(", ");
726
            }
727
            builder.append("schema=");
728
            builder.append(params.getSchema());
729
            needComma = true;
730
        }
731
        builder.append(")");
732
        return builder.toString();
733
  }
734

    
735
  @Override
736
  public boolean isThreadSafe() {
737
    return true;
738
  }
739

    
740
  /** 
741
   * This method has been overriden in Oracle provider
742
   * 
743
   * @param sqlbuilder
744
   * @param type
745
   * @param extra_column_names 
746
   */
747
  @Override
748
  public void processSpecialFunctions(
749
          SQLBuilder sqlbuilder,
750
          FeatureType type,
751
          List<String> extra_column_names) {
752
    replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
753
    replaceExistsFunction(sqlbuilder, type, extra_column_names);
754
    addTableToColumnReferences(sqlbuilder, type);
755
  }
756

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

    
801
        if( extra_column_names!=null ) {
802
          extra_column_names.add(columnName);
803
        }
804
      }
805
    }, null);
806
    if (value_replacements.isEmpty()) {
807
      return;
808
    }
809
    // Realizamos los reemplazos calculados previamente (value_replacements).
810
    for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
811
      ExpressionBuilder.Value target = replaceValue[0];
812
      ExpressionBuilder.Value replacement = replaceValue[1];
813
      sqlbuilder.select().replace(target, replacement);
814
    }
815
  }
816

    
817
  @SuppressWarnings("Convert2Lambda")
818
  protected void replaceForeingValueFunction(
819
          SQLBuilder sqlbuilder,
820
          FeatureType type,
821
          List<String> extra_column_names) {
822
    try {
823
      // See test SQLBuilderTest->testForeingValue()
824
      final ExpressionBuilder where = sqlbuilder.select().where();
825
//      if (where == null || where.isEmpty()) {
826
//        return;
827
//      }
828
      final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
829
      final ExpressionBuilder expbuilder = sqlbuilder.expression();
830

    
831
      final List<String> foreing_value_args;
832
      if (extra_column_names == null || sqlbuilder.select().has_group_by() || sqlbuilder.select().has_aggregate_functions() ) {
833
        foreing_value_args = new ArrayList<>();
834
      } else {
835
        foreing_value_args = extra_column_names;
836
      }
837
      final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
838

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

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

    
980
      // Realizamos los reemplazos calculados previamente (value_replacements).
981
      for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
982
        ExpressionBuilder.Value target = replaceValue[0];
983
        ExpressionBuilder.Value replacement = replaceValue[1];
984
        sqlbuilder.select().replace(target, replacement);
985
      }
986

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

    
1043
        addTableToColumnReferencesInSingleSelect(
1044
                sqlbuilder, 
1045
                select, 
1046
                tables
1047
        );
1048
        
1049
    }
1050

    
1051
    private void collectTablesFromSelect(SelectBuilder select, List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables, FeatureType type) throws LocatorException {
1052
        DataManager dataManager = DALLocator.getDataManager();
1053
        
1054
        List<SQLBuilder.JoinBuilder> joins = select.from().getJoins();
1055
        if(!CollectionUtils.isEmpty(joins)){
1056
            for (SQLBuilder.JoinBuilder join : joins) {
1057
                SQLBuilder.TableNameBuilder joinTable = join.getTable();
1058
                FeatureType featureType = dataManager.getStoresRepository().getFeatureType(joinTable.getName());
1059
                if(featureType != null){
1060
                    tables.add(new ImmutablePair<>(joinTable,featureType));
1061
                }
1062
            }
1063
        }
1064
        SQLBuilder.TableNameBuilder table = select.from().table();
1065
        if(type == null){
1066
            type = dataManager.getStoresRepository().getFeatureType(table.getName());
1067
            
1068
        }
1069
        if(type != null){
1070
            tables.add(new ImmutablePair<>(table,type));
1071
        }
1072
    }
1073
    
1074
    protected void addTableToColumnReferencesInSingleSelect(SQLBuilder sqlbuilder, SelectBuilder select, List<Pair<SQLBuilder.TableNameBuilder,FeatureType>> outerTables) {
1075
        
1076
        final SQLBuilder.TableNameBuilder fromTable = select.from().table();
1077

    
1078
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
1079
        List<ExpressionBuilder.Variable> variables = new ArrayList<>();
1080
//        List<ExpressionBuilder.Value> variablesToExclude = new ArrayList<>();
1081
        
1082
        select.accept((ExpressionBuilder.Visitable value) -> {
1083
            if(value instanceof Column) {
1084
                Column c = (Column)value;
1085
                if(c.table() == null || !c.table().has_name() || !c.table().has_schema()){
1086
                    variables.add(c);
1087
                }
1088
                
1089
            } else if(value instanceof ExpressionBuilder.Variable) {
1090
                variables.add((ExpressionBuilder.Variable) value);
1091
            }
1092

    
1093
        }, new ExpressionBuilder.VisitorFilter() {
1094
            @Override
1095
            public boolean skipChildren() {
1096
                return true;
1097
            }
1098
            
1099
            @Override
1100
            public boolean accept(ExpressionBuilder.Visitable visitable) {
1101
                if(select == visitable){
1102
                    return true;
1103
                }
1104
                if(visitable instanceof SelectBuilder) {
1105
                    ArrayList<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables = new ArrayList<>(outerTables);
1106
                    collectTablesFromSelect((SelectBuilder) visitable, tables, null);
1107
                    
1108
                    addTableToColumnReferencesInSingleSelect(sqlbuilder, (SelectBuilder) visitable, tables);
1109
                    return false;
1110
                }
1111
                return true;
1112
            }
1113
        });
1114
        
1115
        for (ExpressionBuilder.Variable variable : variables) {
1116
            ExpressionBuilder.Variable variable_replacement = null;
1117
            
1118
            Pair<SQLBuilder.TableNameBuilder, FeatureType> tableNameAndFeatureType = getTableAndFeatureType(outerTables, variable.name());
1119
            if (tableNameAndFeatureType != null) {
1120
                SQLBuilder.TableNameBuilder variableTable = tableNameAndFeatureType.getLeft();
1121

    
1122
                if (variable instanceof SQLBuilder.Column) {
1123
                    Column column = (SQLBuilder.Column) variable;
1124
                    if (column.table() != null && column.table().has_name()) {
1125
                        if (column.table().has_schema()) {
1126
                            //La columna tiene tabla y esquema => No hacemos nada
1127
                        } else {
1128
                            if(column.table().getName().equals(variableTable.getName())){
1129
                                SQLBuilder.TableNameBuilder t = sqlbuilder.createTableNameBuilder()
1130
                                        .name(column.table().getName())
1131
                                        .schema(variableTable.getSchema());
1132
                                variable_replacement = sqlbuilder.column_from(
1133
                                        t,
1134
                                        variable
1135
                                );
1136
                            } else {
1137
                                if(fromTable.has_schema()){
1138
                                    SQLBuilder.TableNameBuilder t = sqlbuilder.createTableNameBuilder()
1139
                                            .name(column.table().getName())
1140
                                            .schema(fromTable.getSchema()); //Mismo esquema que en el FROM
1141
                                    variable_replacement = sqlbuilder.column_from(
1142
                                            t,
1143
                                            variable
1144
                                    );
1145
                                } else {
1146
                                    // No tiene esquema ni podemos averiguarlo => No hacemos nada
1147
                                }
1148
                            }
1149
                        }
1150
                    } else {
1151
                        column = sqlbuilder.column_from(tableNameAndFeatureType.getLeft(), variable);
1152
                        column.setProperty(SQLBuilder.PROP_FEATURE_TYPE, tableNameAndFeatureType.getRight());
1153
                        variable_replacement = column;
1154
                    }
1155
                } else {
1156
                    Column column = sqlbuilder.column_from(tableNameAndFeatureType.getLeft(), variable);
1157
                    column.setProperty(SQLBuilder.PROP_FEATURE_TYPE, tableNameAndFeatureType.getRight());
1158
                    variable_replacement = column;
1159
                }
1160

    
1161
            } else {
1162
                if (variable instanceof SQLBuilder.Column) {
1163
                    Column column = (SQLBuilder.Column) variable;
1164
                    if (column.table() == null || !column.table().has_name()) {
1165
                        variable_replacement = sqlbuilder.column_from(
1166
                                fromTable,
1167
                                variable
1168
                        );
1169
                    } else if (!column.table().has_schema()) {
1170
                        if(fromTable.has_schema()){
1171
                            SQLBuilder.TableNameBuilder t = sqlbuilder.createTableNameBuilder()
1172
                                    .name(column.table().getName())
1173
                                    .schema(fromTable.getSchema()); //Mismo esquema que en el FROM
1174
                            variable_replacement = sqlbuilder.column_from(
1175
                                    t,
1176
                                    variable
1177
                            );
1178
                        } else {
1179
                            // No tiene esquema ni podemos averiguarlo => No hacemos nada
1180
                        }
1181
                    }
1182
                } else {
1183
                    if( StringUtils.equals(fromTable.getName(), variable.name()) ) {
1184
                        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.");
1185
                    } else {
1186
                        variable_replacement = sqlbuilder.column_from(
1187
                                fromTable,
1188
                                variable
1189
                        );
1190
                    }
1191
                }
1192
            }
1193
            if(variable_replacement != null){
1194
                value_replacements.add(
1195
                        new ExpressionBuilder.Value[]{
1196
                            variable,
1197
                            variable_replacement
1198
                        }
1199
                );
1200
            }
1201

    
1202
        }
1203

    
1204
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
1205
            ExpressionBuilder.Value target = replaceValue[0];
1206
            ExpressionBuilder.Value replacement = replaceValue[1];
1207
            Boolean addTableName = (Boolean) target.getProperty(PROP_ADD_TABLE_NAME_TO_COLUMNS);
1208
            if(addTableName == null || !addTableName){
1209
                continue;
1210
            }
1211
            select.replace(target, replacement);
1212
        }
1213

    
1214
    }
1215
    
1216
    protected Pair<SQLBuilder.TableNameBuilder, FeatureType> getTableAndFeatureType(List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> outerTables, String columnName) {
1217
//        ListIterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> it = outerTables.listIterator(outerTables.size());
1218
//        
1219
//        while (it.hasPrevious()) {
1220
//            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = it.previous();
1221
//            FeatureType featureType = pair.getRight();
1222
//            if(featureType.get(columnName) != null){
1223
//                return pair;
1224
//            }
1225
//        }
1226
        
1227
//        ListIterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> it = new ReverseListIterator(outerTables);
1228
//        while (it.hasNext()) {
1229
//            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = it.next();
1230
//            FeatureType featureType = pair.getRight();
1231
//            if(featureType.get(columnName) != null){
1232
//                return pair;
1233
//            }
1234
//        }
1235
        
1236
        for (Iterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> iterator = new ReverseListIterator(outerTables); iterator.hasNext();) {
1237
            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = iterator.next();
1238
            FeatureType featureType = pair.getRight();
1239
            if(featureType.get(columnName) != null){
1240
                return pair;
1241
            }
1242
        }
1243

    
1244
        return null;
1245
    }
1246
    
1247
    @Override
1248
    public void setTransaction(DataTransactionServices transaction) {
1249
        this.transaction = transaction;
1250
    }
1251
    
1252
    @Override
1253
    public String toString() {
1254
        try {
1255
            ToStringBuilder builder = new ToStringBuilder(this);
1256
            builder.append("hash", String.format("%x", this.hashCode()));
1257
            builder.append("url", this.connectionParameters.getUrl());
1258
            return builder.toString();
1259
        } catch (Exception e) {
1260
            return super.toString();
1261
        }
1262
    }
1263

    
1264
  @Override
1265
    public String getConnectionProviderStatus() {
1266
        return "";
1267
    }
1268
        
1269
  @Override
1270
        public void expandCalculedColumns(JDBCSQLBuilderBase sqlbuilder) {
1271
            ComputedAttribute computedAttributeFormater = new ComputedAttribute(sqlbuilder, sqlbuilder.formatter());
1272
            for (int i = 0; i < 10; i++) {
1273
                List<Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value>> variablesToReplace = new ArrayList<>();
1274
                sqlbuilder.accept((ExpressionBuilder.Visitable value) -> {
1275
                    if (computedAttributeFormater.canApply((ExpressionBuilder.Value) value)) {
1276
                        ExpressionBuilder.Variable variable = (ExpressionBuilder.Variable) value;
1277
                        ExpressionBuilder.Value replace = computedAttributeFormater.expandedValue(variable);
1278
                        variablesToReplace.add(Pair.of(variable, replace));
1279
                    }
1280
                }, null);
1281
                if (variablesToReplace.isEmpty()) {
1282
                    break;
1283
                } 
1284
                for (Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value> entry : variablesToReplace) {
1285
                    ExpressionBuilder.Value variable = entry.getKey();
1286
                    ExpressionBuilder.Value replace = entry.getValue();
1287
                    Boolean addTableName = (Boolean) variable.getProperty(PROP_ADD_TABLE_NAME_TO_COLUMNS);
1288
                    if (addTableName != null && addTableName) {
1289
                        sqlbuilder.setProperties(replace, null, PROP_ADD_TABLE_NAME_TO_COLUMNS,true);
1290
                    }
1291
                    sqlbuilder.select().replace(variable, replace);
1292
                }
1293
            }
1294
    }
1295
        
1296
    @Override
1297
    public DataTransactionServices getTransaction() {
1298
        return this.transaction;
1299
    }
1300

    
1301
    public ConnectionProvider getConnectionProvider() {
1302
        return new FakeConnectionProvider(connectionParameters);
1303
    }
1304
    
1305
    protected String getConnectionProviderKey(JDBCConnectionParameters connectionParameters) {
1306
        String pass = Hex.encodeHexString((connectionParameters.getPassword()+"").getBytes());
1307
//        String pass = connectionParameters.getPassword();
1308
        return connectionParameters.getUrl() + ";user:"+connectionParameters.getUser()+"@"+pass;
1309
    }
1310
    
1311
}