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

History | View | Annotate | Download (45.9 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.sql.ResultSetMetaData;
31
import java.util.ArrayList;
32
import java.util.Collections;
33
import java.util.HashMap;
34
import java.util.HashSet;
35
import java.util.List;
36
import java.util.Map;
37
import java.util.function.Predicate;
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.mutable.MutableBoolean;
42
import org.apache.commons.lang3.mutable.MutableObject;
43
import org.apache.commons.lang3.tuple.Pair;
44
import org.gvsig.expressionevaluator.Code;
45
import org.gvsig.expressionevaluator.Expression;
46
import org.gvsig.expressionevaluator.ExpressionBuilder;
47
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
48
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
49
import org.gvsig.expressionevaluator.Function;
50
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
51
import org.gvsig.expressionevaluator.SymbolTable;
52
import static org.gvsig.fmap.dal.DataManager.FUNCTION_EXISTS;
53
import static org.gvsig.fmap.dal.DataManager.FUNCTION_FOREING_VALUE;
54
import org.gvsig.fmap.dal.DataTypes;
55
import org.gvsig.fmap.dal.SQLBuilder;
56
import org.gvsig.fmap.dal.exception.DataException;
57
import org.gvsig.fmap.dal.exception.InitializeException;
58
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
59
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
60
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
61
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
62
import org.gvsig.fmap.dal.feature.FeatureType;
63
import org.gvsig.fmap.dal.feature.ForeingKey;
64
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
65
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
66
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
67
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
68
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
69
import org.gvsig.fmap.dal.serverexplorer.filesystem.FilesystemStoreParameters;
70
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
71
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
72
import org.gvsig.fmap.dal.spi.DataTransactionServices;
73
import org.gvsig.fmap.dal.store.db.DBHelper;
74
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
75
import org.gvsig.fmap.dal.store.jdbc.JDBCLibrary;
76
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
77
import org.gvsig.fmap.dal.store.jdbc.JDBCResource;
78
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
79
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
80
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
81
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
82
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
83
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
84
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
85
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
86
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
87
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
88
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
89
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
90
import org.gvsig.fmap.geom.Geometry;
91
import org.gvsig.fmap.geom.GeometryLocator;
92
import org.gvsig.fmap.geom.GeometryManager;
93
import org.gvsig.tools.dispose.impl.AbstractDisposable;
94
import org.gvsig.tools.evaluator.Evaluator;
95
import org.gvsig.tools.exception.BaseException;
96
import org.gvsig.tools.exception.NotYetImplemented;
97
import org.gvsig.tools.visitor.FilteredVisitable;
98
import org.gvsig.tools.visitor.VisitCanceledException;
99
import org.gvsig.tools.visitor.Visitor;
100
import org.slf4j.Logger;
101
import org.slf4j.LoggerFactory;
102
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$CONSTANT;
103
import static org.gvsig.expressionevaluator.ExpressionBuilder.FUNCTION_$IDENTIFIER;
104
import org.gvsig.fmap.dal.DataManager;
105
import org.gvsig.fmap.dal.SQLBuilder.Column;
106
import org.gvsig.fmap.dal.SQLBuilder.OrderByBuilder;
107
import org.gvsig.fmap.dal.SQLBuilder.SelectBuilder;
108
import org.gvsig.fmap.dal.SQLBuilder.SelectColumnBuilder;
109
import org.gvsig.fmap.dal.store.jdbc2.JDBCConnection;
110
import org.gvsig.fmap.dal.store.jdbc2.spi.expressionbuilder.formatters.ComputedAttribute;
111
import org.gvsig.tools.util.ContainerUtils;
112

    
113
@SuppressWarnings("UseSpecificCatch")
114
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
115

    
116
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
117
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
118
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
119
  public static final String DONT_ADD_TABLE_NAME_TO_COLUMNS = "DONT_ADD_TABLE_NAME";
120

    
121
  private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
122

    
123
  private ResulSetControler resulSetControler = null;
124

    
125
  // Quien ha creado este helper.
126
  // Se le reenviaran las notificaciones del recurso asociado al helper.
127
  private ResourceConsumer helperClient = null;
128

    
129
  private GeometryManager geometryManager = null;
130

    
131
  private JDBCConnectionParameters connectionParameters;
132

    
133
  private JDBCStoreProvider store;
134

    
135
  protected OperationsFactory operationsFactory = null;
136

    
137
  protected SRSSolver srssolver;
138
  
139
  protected FeatureType providerFeatureType = null;
140
  
141
  protected DataTransactionServices transaction;
142

    
143
  public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
144
        if (connectionParameters == null) {
145
            throw new IllegalArgumentException("Connection parameters can't be null.");
146
        }
147
        this.connectionParameters = connectionParameters;
148

    
149
        // If a particular treatment is required to convert SRS to the 
150
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
151
        this.srssolver = new SRSSolverDumb(this);
152
    }
153

    
154
  protected void initialize(
155
          ResourceConsumer helperClient,
156
          JDBCConnectionParameters connectionParameters,
157
          JDBCStoreProvider store
158
  ) {
159
    this.store = store;
160
    this.helperClient = helperClient;
161
    this.connectionParameters = connectionParameters;
162
    initializeResource(connectionParameters);
163
  }
164

    
165
  protected String getResourceType() {
166
    return JDBCResource.NAME;
167
  }
168

    
169
  @Override
170
  public String getProviderName() {
171
    return JDBCLibrary.NAME;
172
  }
173

    
174
  @Override
175
  public GeometrySupportType getGeometrySupportType() {
176
    // El proveedor generico de JDBC guadara las geoemtrias como WKT
177
    return GeometrySupportType.WKT;
178
  }
179
  
180
    /**
181
     * @return the providerFeatureType
182
     */
183
  @Override
184
    public FeatureType getProviderFeatureType() {
185
        return providerFeatureType;
186
    }
187

    
188
    /**
189
     * @param providerFeatureType the providerFeatureType to set
190
     */
191
  @Override
192
    public void setProviderFeatureType(FeatureType providerFeatureType) {
193
        this.providerFeatureType = providerFeatureType;
194
    }
195

    
196

    
197
  @Override
198
  public boolean hasSpatialFunctions() {
199
    // Por defecto el proveedor generico de JDBC asume que la BBDD no 
200
    // tiene soporte para funciones espaciales.
201
    return false;
202
  }
203

    
204
  @Override
205
  public boolean allowNestedOperations() {
206
    return false;
207
  }
208

    
209
  @Override
210
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
211
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
212
    return true;
213
  }
214

    
215
  @Override
216
  public JDBCSQLBuilderBase createSQLBuilder() {
217
    return new JDBCSQLBuilderBase(this);
218
  }
219

    
220
  @Override
221
  public String getQuoteForIdentifiers() {
222
    return QUOTE_FOR_USE_IN_IDENTIFIERS;
223
  }
224

    
225
  @Override
226
  public boolean allowAutomaticValues() {
227
    return ALLOW_AUTOMATIC_VALUES;
228
  }
229

    
230
  @Override
231
  public boolean supportOffsetInSelect() {
232
    // Asumimos que la BBDD soporta OFFSET
233
    return true;
234
  }
235

    
236
  @Override
237
  public String getQuoteForStrings() {
238
    return QUOTE_FOR_USE_IN_STRINGS;
239
  }
240

    
241
  protected boolean supportCaller(Code.Callable caller) {
242
    if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) {
243
      return true;
244
    }
245
    Function function = caller.function();
246
    if (function == null) {
247
      return false;
248
    }
249
    if (!function.isSQLCompatible()) {
250
      return false;
251
    }
252
    if (!this.hasSpatialFunctions()) {
253
      if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
254
        return false;
255
      }
256
    }
257
    return true;
258
  }
259

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

    
291
        if (StringUtils.isEmpty(sql)) {
292
            return false;
293
        }
294

    
295
//        if (this.useSubquery()) {
296
//            // Se esta usando una subquery en los parametros de acceso a la
297
//            // BBDD, asi que no podemos filtrar.
298
//            return false;
299
//        }
300

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

    
360
        } catch (VisitCanceledException ex) {
361
            // Do nothing
362
        } catch (Exception ex) {
363
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
364
        }
365

    
366
        return isCompatible.booleanValue();
367
    }
368

    
369
  @Override
370
  public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
371
    if (this.useSubquery()) {
372
      return false;
373
    }
374
    if (order == null) {
375
      return true;
376
    }
377
    for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
378
      if (member.hasEvaluator()) {
379
        if (!this.supportFilter(type, member.getEvaluator())) {
380
          return false;
381
        }
382
      }
383
    }
384
    return true;
385
  }
386

    
387
  @Override
388
  public OperationsFactory getOperations() {
389
    if (this.operationsFactory == null && !isClosed()) {
390
      this.operationsFactory = new OperationsFactoryBase(this);
391
    }
392
    return operationsFactory;
393
  }
394

    
395
  protected void initializeResource(JDBCConnectionParameters params) {
396
//        Object[] resourceParams = new Object[]{
397
//            params.getUrl(),
398
//            params.getHost(),
399
//            params.getPort(),
400
//            params.getDBName(),
401
//            params.getUser(),
402
//            params.getPassword(),
403
//            params.getJDBCDriverClassName()
404
//        };
405
//
406
//        try {
407
//            ResourceManagerProviderServices manager
408
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
409
//            JDBCResource resource = (JDBCResource) manager.createAddResource(
410
//                    this.getResourceType(),
411
//                    resourceParams
412
//            );
413
//            this.resource = resource;
414
//            this.resource.addConsumer(this);
415
//        } catch (InitializeException ex) {
416
//            logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
417
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
418
//        }
419

    
420
  }
421

    
422
  @Override
423
  public String getSourceId() {
424
    return this.store.getSourceId();
425
  }
426

    
427
  @Override
428
  public JDBCResource getResource() {
429
    return null;
430
//        return this.resource;
431
  }
432

    
433
  @Override
434
  public JDBCConnection getConnection() throws AccessResourceException {
435
    throw new NotYetImplemented();
436
  }
437

    
438
  @Override
439
  public JDBCConnection getConnectionWritable() throws AccessResourceException {
440
    return this.getConnection();
441
  }
442

    
443
  @Override
444
  public String getConnectionURL() {
445
    return null;
446
  }
447

    
448
  @Override
449
  public JDBCConnectionParameters getConnectionParameters() {
450
    return connectionParameters;
451
  }
452

    
453
  @Override
454
  protected void doDispose() throws BaseException {
455
    JDBCUtils.closeQuietly(this);
456
  }
457

    
458
  @Override
459
  public void close() throws Exception {
460
    JDBCUtils.closeQuietly(this.resulSetControler);
461
    this.resulSetControler = null;
462
    this.operationsFactory = null;
463
    this.srssolver = null;
464
    this.connectionParameters = null;
465
    this.helperClient = null;
466
    this.geometryManager = null;
467
    this.providerFeatureType = null;
468
    this.store = null;
469
    this.transaction = null;
470
  }
471
  
472
  public boolean isClosed() {
473
      return this.srssolver == null;
474
  }
475

    
476
  @Override
477
  public boolean closeResourceRequested(ResourceProvider resource) {
478
    return this.helperClient.closeResourceRequested(resource);
479
  }
480

    
481
  @Override
482
  public void resourceChanged(ResourceProvider resource) {
483
    this.helperClient.resourceChanged(resource);
484
  }
485

    
486
  @Override
487
  public GeometryManager getGeometryManager() {
488
    if (this.geometryManager == null) {
489
      this.geometryManager = GeometryLocator.getGeometryManager();
490
    }
491
    return this.geometryManager;
492
  }
493

    
494
  @Override
495
  public ResulSetControler getResulSetControler() {
496
    if (this.resulSetControler == null) {
497
      this.resulSetControler = new ResulSetControlerBase(this);
498
    }
499
    return this.resulSetControler;
500
  }
501

    
502
  @Override
503
  public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
504
    fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
505
  }
506

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

    
548
  @Override
549
  public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
550
    return getGeometryFromColumn(rs.get(), index);
551
  }
552

    
553
  @Override
554
  public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
555
    try {
556
      Object value;
557
      Geometry geom;
558
      switch (this.getGeometrySupportType()) {
559
        case NATIVE:
560
        case WKB:
561
          value = rs.getBytes(index);
562
          if (value == null) {
563
            return null;
564
          }
565
          geom = this.getGeometryManager().createFrom((byte[]) value);
566
          if( geom == null ) {
567
              LOGGER.debug("Can't get geometry from bytes of column "+index);
568
          }
569
          return geom;
570

    
571

    
572
        case EWKB:
573
          value = rs.getBytes(index);
574
          if (value == null) {
575
            return null;
576
          }
577
          geom = this.getGeometryManager().createFrom((byte[]) value);
578
          if( geom == null ) {
579
              LOGGER.debug("Can't get geometry from bytes of column "+index);
580
          }
581
          return geom;
582
        case WKT:
583
        default:
584
          value = rs.getString(index);
585
          if (value == null) {
586
            return null;
587
          }
588
          geom = this.getGeometryManager().createFrom((String) value);
589
          if( geom == null ) {
590
              LOGGER.debug("Can't get geometry from bytes of column "+index);
591
          }
592
          return geom;
593

    
594
      }
595
    } catch (Exception ex) {
596
      throw new JDBCCantFetchValueException(ex);
597
    }
598
  }
599

    
600
  @Override
601
  public FeatureProvider createFeature(FeatureType featureType) throws DataException {
602
    return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
603
  }
604

    
605
  @Override
606
  public boolean useSubquery() {
607
    if (this.store == null) {
608
      return false;
609
    }
610
    return !StringUtils.isEmpty(this.store.getParameters().getSQL());
611
  }
612

    
613
  @Override
614
  public SRSSolver getSRSSolver() {
615
    return this.srssolver;
616
  }
617

    
618
  @Override
619
  public JDBCStoreProvider createProvider(
620
          JDBCStoreParameters parameters,
621
          DataStoreProviderServices providerServices
622
  ) throws InitializeException {
623

    
624
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
625
            parameters,
626
            providerServices,
627
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
628
            this
629
    );
630
    this.initialize(theStore, parameters, theStore);
631
    return theStore;
632
  }
633

    
634
  @Override
635
  public JDBCServerExplorer createServerExplorer(
636
          JDBCServerExplorerParameters parameters,
637
          DataServerExplorerProviderServices providerServices
638
  ) throws InitializeException {
639

    
640
    JDBCServerExplorer explorer = new JDBCServerExplorerBase(
641
            parameters,
642
            providerServices,
643
            this
644
    );
645
    this.initialize(explorer, parameters, null);
646
    return explorer;
647
  }
648

    
649
  @Override
650
  public JDBCNewStoreParameters createNewStoreParameters() {
651
    return new JDBCNewStoreParameters();
652
  }
653

    
654
  @Override
655
  public JDBCStoreParameters createOpenStoreParameters() {
656
    return new JDBCStoreParameters();
657
  }
658
  
659
  @Override
660
  public JDBCStoreParameters createOpenStoreParameters(JDBCServerExplorerParameters parameters) {
661
    JDBCStoreParameters params = this.createOpenStoreParameters();
662
    params.setHost(parameters.getHost());
663
    params.setPort(parameters.getPort());
664
    params.setDBName(parameters.getDBName());
665
    params.setUser(parameters.getUser());
666
    params.setPassword(parameters.getPassword());
667
    params.setCatalog(parameters.getCatalog());
668
    params.setSchema(parameters.getSchema());
669
    params.setJDBCDriverClassName(parameters.getJDBCDriverClassName());
670
    params.setUrl(parameters.getUrl());
671
    if( parameters instanceof FilesystemStoreParameters ) {
672
        File f = ((FilesystemStoreParameters) parameters).getFile();
673
        ((FilesystemStoreParameters) params).setFile(f);
674
    }
675
    params.setBatchSize(parameters.getBatchSize());
676
    return params;
677
  }
678
  
679

    
680
  @Override
681
  public JDBCServerExplorerParameters createServerExplorerParameters() {
682
    return new JDBCServerExplorerParameters();
683
  }
684

    
685
  @Override
686
  public String getSourceId(JDBCStoreParameters parameters) {
687
    return parameters.getHost() + ":"
688
            + parameters.getDBName() + ":"
689
            + parameters.getSchema() + ":"
690
            + parameters.tableID();
691
  }
692

    
693
  @Override
694
  public boolean isThreadSafe() {
695
    return true;
696
  }
697

    
698
  /** 
699
   * This method has been overriden in Oracle provider
700
   * 
701
   * @param sqlbuilder
702
   * @param type
703
   * @param extra_column_names 
704
   */
705
  @Override
706
  public void processSpecialFunctions(
707
          SQLBuilder sqlbuilder,
708
          FeatureType type,
709
          List<String> extra_column_names) {
710
    replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
711
    replaceExistsFunction(sqlbuilder, type, extra_column_names);
712
    addTableToColumnReferences(sqlbuilder, type);
713
  }
714

    
715
  @SuppressWarnings("Convert2Lambda")
716
  private void replaceExistsFunction(
717
          SQLBuilder sqlbuilder,
718
          FeatureType type,
719
          final List<String> extra_column_names) {
720
    
721
    //  Si lse encuentra una construccion del tipo:
722
    //    SELECT ... FROM ... WHERE ... EXISTS(list, 'EXISTS_ID') ...
723
    //  se traslada a:
724
    //    SELECT ... EXISTS(list) AS EXISTS_ID FROM ... WHERE ... EXISTS(list) ...
725
    //  Y se a?ade el valor ESISTS_ID a las columnas extra a recuperar de la consulta.
726
    //          
727
    
728
    final SQLBuilder.SelectBuilder select = sqlbuilder.select();
729
    final ExpressionBuilder where = select.where();
730
    if (where == null || where.isEmpty() || select.has_group_by() ) {
731
      return;
732
    }
733
    final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
734
    where.accept(new ExpressionBuilder.Visitor() {
735
      @Override
736
      public void visit(ExpressionBuilder.Visitable value) {
737
        if (!(value instanceof ExpressionBuilder.Function)) {
738
          return;
739
        }
740
        ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
741
        if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_EXISTS)) {
742
          return;
743
        }
744
        if (function.parameters().size() != 2) {
745
          return;
746
        }
747
        ExpressionBuilder.Value arg0 = function.parameters().get(0);
748
        ExpressionBuilder.Value arg1 = function.parameters().get(1);
749
        if (arg1 == null) {
750
          return;
751
        }
752
        String columnName = (String) ((ExpressionBuilder.Constant) arg1).value();
753
        SQLBuilder.SelectColumnBuilder column = select.column();
754
        column.value(
755
                sqlbuilder.expression().function(FUNCTION_EXISTS, arg0)
756
        );
757
        column.as(columnName);
758

    
759
        if( extra_column_names!=null ) {
760
          extra_column_names.add(columnName);
761
        }
762
      }
763
    }, null);
764
    if (value_replacements.isEmpty()) {
765
      return;
766
    }
767
    // Realizamos los reemplazos calculados previamente (value_replacements).
768
    for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
769
      ExpressionBuilder.Value target = replaceValue[0];
770
      ExpressionBuilder.Value replacement = replaceValue[1];
771
      sqlbuilder.select().replace(target, replacement);
772
    }
773
  }
774

    
775
  @SuppressWarnings("Convert2Lambda")
776
  protected void replaceForeingValueFunction(
777
          SQLBuilder sqlbuilder,
778
          FeatureType type,
779
          List<String> extra_column_names) {
780
    try {
781
      // See test SQLBuilderTest->testForeingValue()
782
      final ExpressionBuilder where = sqlbuilder.select().where();
783
//      if (where == null || where.isEmpty()) {
784
//        return;
785
//      }
786
      final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
787
      final ExpressionBuilder expbuilder = sqlbuilder.expression();
788

    
789
      final List<String> foreing_value_args;
790
      if (extra_column_names == null || sqlbuilder.select().has_group_by() || sqlbuilder.select().has_aggregate_functions() ) {
791
        foreing_value_args = new ArrayList<>();
792
      } else {
793
        foreing_value_args = extra_column_names;
794
      }
795
      final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
796

    
797
      MutableObject<Boolean> hasForeignValueFunctions = new MutableObject<>(false);
798
      
799
      // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
800
      // el argumento de esta asi como por que tendriamos que sustituirla 
801
      // una vez hechos los left joins que toquen.
802
      sqlbuilder.select().accept(new ExpressionBuilder.Visitor() {
803
        @Override
804
        public void visit(ExpressionBuilder.Visitable value) {
805
          // Requiere que sea la funcion "FOREING_VALUE con un solo 
806
          // argumento que sea una constante de tipo string.
807
          if (!(value instanceof ExpressionBuilder.Function)) {
808
            return;
809
          }
810
          ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
811
          if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_FOREING_VALUE)) {
812
            return;
813
          }
814
          if (function.parameters().size() != 1) {
815
            return;
816
          }
817
          ExpressionBuilder.Value arg = function.parameters().get(0);
818
          if (!(arg instanceof ExpressionBuilder.Constant)) {
819
            return;
820
          }
821
          Object arg_value = ((ExpressionBuilder.Constant) arg).value();
822
          if (!(arg_value instanceof CharSequence)) {
823
            return;
824
          }
825
          hasForeignValueFunctions.setValue(true);
826
          String foreing_value_arg = arg_value.toString();
827
          String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
828
          if (foreingNameParts.length != 2) {
829
            // De momento solo tratamos joins entre dos tablas.
830
            return;
831
          }
832
          String columnNameLocal = foreingNameParts[0];
833
          String columnNameForeing = foreingNameParts[1];
834
          FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
835
          if (attr==null) {
836
              throw new RuntimeException("Cannot find in feature type attribute:"+columnNameLocal);
837
          }
838
          if (!attr.isForeingKey()) {
839
            // Uhm... si el argumento no referencia a un campo que es
840
            // clave ajena no lo procesamos. 
841
            // ? Deberiamos lanzar un error ?
842
            return;
843
          }
844
          // Nos guardaremos por que hay que reemplazar la funcion 
845
          // FOREING_VALUE, y su argumento para mas tarde.
846
          ForeingKey foreingKey = attr.getForeingKey();
847
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
848
                  .database(table.getDatabase())
849
                  .schema(table.getSchema())
850
                  .name(foreingKey.getTableName());
851
          // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
852
          // que toca de la tabla a la que referencia la clave ajena.
853
          ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
854
          value_replacements.add(
855
                  new ExpressionBuilder.Value[]{
856
                    function,
857
                    function_replacement
858
                  }
859
          );
860
          if (!foreing_value_args.contains(foreing_value_arg)) {
861
            foreing_value_args.add(foreing_value_arg);
862
          }
863
        }
864
      }, null);
865
      
866
      // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
867
      // hacer nada.
868
      if ( !hasForeignValueFunctions.getValue() || foreing_value_args.isEmpty()) {
869
        return;
870
      }
871

    
872
      // Calculamos que referencias de columnas hemos de cambiar para 
873
      // que no aparezcan ambiguedades al hacer el join (nombres de
874
      // columna de una y otra tabla que coincidan).
875
      // Para las columnas que puedan dar conflicto se prepara un reemplazo 
876
      // de estas que tenga el nombre de tabla.
877
      for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
878
        if (variable == null || variable instanceof SQLBuilderBase.ColumnBase) {
879
          continue;
880
        }
881
        if (ContainerUtils.contains(extra_column_names, variable.name(), ContainerUtils.EQUALS_IGNORECASE_COMPARATOR)) {
882
            continue;
883
        }
884
        boolean alreadyReplaced = false;
885
        for (String foreingName : foreing_value_args) {
886
          String[] foreingNameParts = foreingName.split("[.]");
887
          if (foreingNameParts.length != 2) {
888
            continue;
889
          }
890
          String columnNameLocal = foreingNameParts[0];
891
          String columnNameForeing = foreingNameParts[1];
892
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)) {
893
              if(alreadyReplaced){
894
                  throw new RuntimeException("Column reference \""+ columnNameForeing+"\" is ambiguous");
895
              }
896
              alreadyReplaced = true;
897
              
898
              FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
899
              if (attr == null) {
900
                  throw new RuntimeException("Cannot find in feature type attribute:" + columnNameLocal);
901
              }
902
              if (attr.isForeingKey()) {
903
                  // Nos guardaremos por que hay que reemplazar la funcion 
904
                  // FOREING_VALUE, y su argumento para mas tarde.
905
                  ForeingKey foreingKey = attr.getForeingKey();
906
                  SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
907
                          .database(table.getDatabase())
908
                          .schema(table.getSchema())
909
                          .name(foreingKey.getTableName());
910
                  ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
911
                          foreingTable,
912
                          variable.name()
913
                  );
914
                  value_replacements.add(
915
                          new ExpressionBuilder.Value[]{
916
                              variable,
917
                              variable_replacement
918
                          }
919
                  );
920
              }
921
          }
922
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
923
            ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
924
                    table,
925
                    variable.name()
926
            );
927
            value_replacements.add(
928
                    new ExpressionBuilder.Value[]{
929
                      variable,
930
                      variable_replacement
931
                    }
932
            );
933
          }
934
        }
935
      }
936

    
937
      // Realizamos los reemplazos calculados previamente (value_replacements).
938
      for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
939
        ExpressionBuilder.Value target = replaceValue[0];
940
        ExpressionBuilder.Value replacement = replaceValue[1];
941
        sqlbuilder.select().replace(target, replacement);
942
      }
943

    
944
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
945
        // a los valores referenciados por la funcion FOREING_VALUE.
946
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
947
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
948
        HashSet usedLeftJoins = new HashSet();
949
      for (String foreingName : foreing_value_args) {
950
        String[] foreingNameParts = foreingName.split("[.]");
951
        if (foreingNameParts.length != 2) {
952
          continue;
953
        }
954
        String columnNameLocal = foreingNameParts[0];
955
        String columnNameForeing = foreingNameParts[1];
956
        FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
957
        if (attr.isForeingKey()) {
958
          ForeingKey foreingKey = attr.getForeingKey();
959
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
960
                  .database(table.getDatabase())
961
                  .schema(table.getSchema())
962
                  .name(foreingKey.getTableName());
963
          SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
964
                  .database(table.getDatabase())
965
                  .schema(table.getSchema())
966
                  .name(table.getName());
967
          
968
          if (!usedLeftJoins.contains(foreingTable.getName())) {
969
            sqlbuilder.select().from()
970
                    .left_join(
971
                            foreingTable,
972
                            expbuilder.eq(
973
                                    sqlbuilder.column(mainTable, columnNameLocal),
974
                                    sqlbuilder.column(foreingTable, foreingKey.getCodeName())
975
                            )
976
                    );
977
            usedLeftJoins.add(foreingTable.getName());
978
          }
979
          //No est? claro si debe a?adirse esta columna o no, OJO quitarlo o ponerlo altera los test de  H2
980
//          if ( !(sqlbuilder.select().has_group_by() || sqlbuilder.select().has_aggregate_functions()) ) {
981
//                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
982
//          }
983
        }
984
      }
985
      
986
    } catch (Throwable th) {
987
      LOGGER.warn("Can't replace FOREING_VALUE function.", th);
988
      throw th;
989
    } finally {
990
//      LOGGER.trace("Exit from replaceForeingValueFunction.");
991
    }
992
  }
993
 
994
    protected void addTableToColumnReferences(SQLBuilder sqlbuilder, FeatureType type) {
995
//        List<SelectBuilder> selectBuilders = new ArrayList<>();
996
//        selectBuilders.add(sqlbuilder.select());
997
//        sqlbuilder.select().accept((ExpressionBuilder.Visitable value) -> {
998
//            selectBuilders.add((SelectBuilder) value);
999
//
1000
//        }, (ExpressionBuilder.Visitable visitable) -> (visitable instanceof SelectBuilder));
1001
//        for (SelectBuilder selectBuilder : selectBuilders) {
1002
//            addTableToColumnReferencesInSingleSelect(sqlbuilder, selectBuilder);
1003
//        }
1004

    
1005

    
1006
        addTableToColumnReferencesInSingleSelect(sqlbuilder, sqlbuilder.select(), new ArrayList<>());
1007
        
1008
    }
1009
    
1010
    protected void addTableToColumnReferencesInSingleSelect(SQLBuilder sqlbuilder, SelectBuilder select, List<Column> parentColumns) {
1011
        
1012
        final SQLBuilder.TableNameBuilder table = select.from().table();
1013

    
1014
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
1015
        List<ExpressionBuilder.Variable> variables = new ArrayList<>();
1016
        List<ExpressionBuilder.Value> variablesToExclude = new ArrayList<>();
1017
        
1018
        select.accept((ExpressionBuilder.Visitable value) -> {
1019
            if(value instanceof ExpressionBuilder.Function) {
1020
                ExpressionBuilder.Function fn = (ExpressionBuilder.Function) value;
1021
                if(StringUtils.equals(fn.name(), ExpressionBuilder.FUNCTION_GETATTR)){
1022
                    ExpressionBuilder.Value p1 = fn.parameters().get(0);
1023
                    if(p1 instanceof ExpressionBuilder.Variable){
1024
                        ExpressionBuilder.Variable v = (ExpressionBuilder.Variable) p1;
1025
                        variablesToExclude.add(v);
1026
                    }
1027
                    ExpressionBuilder.Value p2 = fn.parameters().get(1);
1028
                    if(p2 instanceof ExpressionBuilder.Constant){
1029
                        ExpressionBuilder.Constant c = (ExpressionBuilder.Constant) p2;
1030
                        variablesToExclude.add(c);
1031
                    }
1032
                }
1033
            } else if(value instanceof Column) {
1034
                Column c = (Column)value;
1035
                if(c.table() == null || !c.table().has_name()){
1036
                    variables.add(c);
1037
                }
1038
                
1039
            } else if(value instanceof ExpressionBuilder.Variable) {
1040
                variables.add((ExpressionBuilder.Variable) value);
1041
//            } else if(value instanceof SelectColumnBuilder) {
1042
//                SelectColumnBuilder selectColumn = (SelectColumnBuilder)value;
1043
//                if(StringUtils.isNotBlank(selectColumn.getName()) && selectColumn.getTable().has_name()){
1044
//                    variablesToExclude.add(sel)
1045
//                }
1046
            }
1047

    
1048
        }, new ExpressionBuilder.VisitorFilter() {
1049
            @Override
1050
            public boolean skipChildren() {
1051
                return true;
1052
            }
1053
            
1054
            @Override
1055
            public boolean accept(ExpressionBuilder.Visitable visitable) {
1056
                if(select == visitable){
1057
                    return true;
1058
                }
1059
                if(visitable instanceof OrderByBuilder) {
1060
                    return false;
1061
                }
1062
                if(visitable instanceof SelectBuilder) {
1063
                    ArrayList<Column> columns = new ArrayList<>(parentColumns);
1064
                    for (SelectColumnBuilder column : select.getColumns()) {
1065
                        columns.add(sqlbuilder.column(
1066
                            table,
1067
                            column.getName()));
1068
                    }
1069
                    addTableToColumnReferencesInSingleSelect(sqlbuilder, (SelectBuilder) visitable, columns);
1070
                    return false;
1071
                }
1072
                return true;
1073
            }
1074
        });
1075
        
1076
        List<String> variableNamesToExclude = new ArrayList<>();
1077
        for(SQLBuilder.SelectColumnBuilder column : select.getColumns()){
1078
            if(StringUtils.isNotBlank(column.getAlias())){
1079
                variableNamesToExclude.add(column.getAlias());
1080
            } else {
1081
                parentColumns.add(sqlbuilder.column(
1082
                    table,
1083
                    column.getName()));
1084
            }
1085
        }
1086

    
1087
        for (ExpressionBuilder.Variable variable : variables) {
1088
            boolean found = false;
1089
            for (ExpressionBuilder.Value v : variablesToExclude) {
1090
                if(v == variable){
1091
                    found = true;
1092
                    break;
1093
                }
1094
            }
1095
            if(found){
1096
                continue;
1097
            }
1098
//            if(variableNamesToExclude.contains(variable.name())){
1099
//                continue;
1100
//            }
1101
            Column column = getColumn(parentColumns,variable.name());
1102
            if(column != null){
1103
                ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
1104
                        column.table(),
1105
                        variable.name()
1106
                );
1107
                value_replacements.add(
1108
                        new ExpressionBuilder.Value[]{
1109
                            variable,
1110
                            variable_replacement
1111
                        }
1112
                );
1113
            } else {
1114
                ExpressionBuilder.Variable variable_replacement = null;
1115
                if(variable instanceof SQLBuilder.Column){
1116
                    column = (SQLBuilder.Column) variable;
1117
                    if (column.table() == null || !column.table().has_name()) {
1118
                        variable_replacement = sqlbuilder.column(
1119
                                table,
1120
                                variable.name()
1121
                        );
1122
                    } else if(!column.table().has_schema()){
1123
                        SQLBuilder.TableNameBuilder t = sqlbuilder.table_name()
1124
                                .name(column.table().getName())
1125
                                .schema(table.getSchema()); //Mismo esquema que en el FROM
1126
                        variable_replacement = sqlbuilder.column(
1127
                                t,
1128
                                variable.name()
1129
                        );
1130
                    }
1131
                } else {
1132
                    variable_replacement = sqlbuilder.column(
1133
                            table,
1134
                            variable.name()
1135
                    );
1136
                }
1137
                if(variable_replacement != null){
1138
                    value_replacements.add(
1139
                            new ExpressionBuilder.Value[]{
1140
                                variable,
1141
                                variable_replacement
1142
                            }
1143
                    );
1144
                }
1145
            }
1146

    
1147
        }
1148

    
1149
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
1150
            ExpressionBuilder.Value target = replaceValue[0];
1151
            ExpressionBuilder.Value replacement = replaceValue[1];
1152
            Boolean dontAddTableName = (Boolean) target.getProperty(DONT_ADD_TABLE_NAME_TO_COLUMNS);
1153
            if(dontAddTableName != null && dontAddTableName){
1154
                continue;
1155
            }
1156
            select.replace(target, replacement);
1157
        }
1158

    
1159
    }
1160
    
1161
    protected Column getColumn(List<Column> columns, String name){
1162
        for (int i = columns.size()-1; i >= 0; i--) {
1163
            if(StringUtils.equalsIgnoreCase(columns.get(i).name(), name)){
1164
                return columns.get(i);
1165
            }
1166
        }
1167
        return null;
1168
    }
1169

    
1170
    
1171
    @Override
1172
    public void setTransaction(DataTransactionServices transaction) {
1173
        this.transaction = transaction;
1174
    }
1175
    
1176
    @Override
1177
    public String toString() {
1178
        try {
1179
            return String.format("%s %x %s", this.getClass().getSimpleName(), this.hashCode(), this.connectionParameters.getUrl());
1180
        } catch (Exception e) {
1181
            return super.toString();
1182
        }
1183
    }
1184

    
1185
  @Override
1186
    public String getConnectionProviderStatus() {
1187
        return "";
1188
    }
1189
        
1190
  @Override
1191
        public void expandCalculedColumns(JDBCSQLBuilderBase sqlbuilder) {
1192
            ComputedAttribute computedAttributeFormater = new ComputedAttribute(sqlbuilder, sqlbuilder.formatter());
1193
            for (int i = 0; i < 10; i++) {
1194
                List<Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value>> variablesToReplace = new ArrayList<>();
1195
                sqlbuilder.accept((ExpressionBuilder.Visitable value) -> {
1196
                    if (computedAttributeFormater.canApply((ExpressionBuilder.Value) value)) {
1197
                        ExpressionBuilder.Variable variable = (ExpressionBuilder.Variable) value;
1198
                        ExpressionBuilder.Value replace = computedAttributeFormater.expandedValue(variable);
1199
                        variablesToReplace.add(Pair.of(variable, replace));
1200
                    }
1201
                }, null);
1202
                if (variablesToReplace.isEmpty()) {
1203
                    break;
1204
                } 
1205
                for (Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value> entry : variablesToReplace) {
1206
                    ExpressionBuilder.Value variable = entry.getKey();
1207
                    ExpressionBuilder.Value replace = entry.getValue();
1208
                    Boolean dontAddTableName = (Boolean) variable.getProperty(DONT_ADD_TABLE_NAME_TO_COLUMNS);
1209
                    if (dontAddTableName != null && dontAddTableName) {
1210
                        continue;
1211
                    }
1212
                    sqlbuilder.select().replace(variable, replace);
1213
                }
1214
            }
1215
    }
1216
        
1217
    @Override
1218
    public DataTransactionServices getTransaction() {
1219
        return this.transaction;
1220
    }
1221

    
1222
    public ConnectionProvider getConnectionProvider() {
1223
        return new FakeConnectionProvider(connectionParameters);
1224
    }
1225
}