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

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

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

    
115
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
116
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
117
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
118
  public static final String ADD_TABLE_NAME_TO_COLUMNS = "ADD_TABLE_NAME";
119

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

    
122
  private ResulSetControler resulSetControler = null;
123

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

    
128
  private GeometryManager geometryManager = null;
129

    
130
  private JDBCConnectionParameters connectionParameters;
131

    
132
  private JDBCStoreProvider store;
133

    
134
  protected OperationsFactory operationsFactory = null;
135

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

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

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

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

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

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

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

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

    
195

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
365
        return isCompatible.booleanValue();
366
    }
367

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

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

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

    
419
  }
420

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
570

    
571
        case EWKB:
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
        case WKT:
582
        default:
583
          value = rs.getString(index);
584
          if (value == null) {
585
            return null;
586
          }
587
          geom = this.getGeometryManager().createFrom((String) value);
588
          if( geom == null ) {
589
              LOGGER.debug("Can't get geometry from bytes of column "+index);
590
          }
591
          return geom;
592

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
943
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
944
        // a los valores referenciados por la funcion FOREING_VALUE.
945
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
946
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
947
        HashSet usedLeftJoins = new HashSet();
948
      for (String foreingName : foreing_value_args) {
949
        String[] foreingNameParts = foreingName.split("[.]");
950
        if (foreingNameParts.length != 2) {
951
          continue;
952
        }
953
        String columnNameLocal = foreingNameParts[0];
954
        String columnNameForeing = foreingNameParts[1];
955
        FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
956
        if (attr.isForeingKey()) {
957
          ForeingKey foreingKey = attr.getForeingKey();
958
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
959
                  .database(table.getDatabase())
960
                  .schema(table.getSchema())
961
                  .name(foreingKey.getTableName());
962
          SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
963
                  .database(table.getDatabase())
964
                  .schema(table.getSchema())
965
                  .name(table.getName());
966
          
967
          if (!usedLeftJoins.contains(foreingTable.getName())) {
968
            sqlbuilder.select().from()
969
                    .left_join(
970
                            foreingTable,
971
                            expbuilder.eq(
972
                                    sqlbuilder.column(mainTable, columnNameLocal),
973
                                    sqlbuilder.column(foreingTable, foreingKey.getCodeName())
974
                            )
975
                    );
976
            usedLeftJoins.add(foreingTable.getName());
977
          }
978
          //No est? claro si debe a?adirse esta columna o no, OJO quitarlo o ponerlo altera los test de  H2
979
          //Si se comentarizan ojo con extra_column_names ya que se habr?n a?adido columnas que no estar?n en la select
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
        SelectBuilder select = sqlbuilder.select();
996
        List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables = new ArrayList<>();
997
        collectTablesFromSelect(select, tables, type);
998

    
999
        addTableToColumnReferencesInSingleSelect(
1000
                sqlbuilder, 
1001
                select, 
1002
                tables
1003
        );
1004
        
1005
    }
1006

    
1007
    private void collectTablesFromSelect(SelectBuilder select, List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables, FeatureType type) throws LocatorException {
1008
        DataManager dataManager = DALLocator.getDataManager();
1009
        
1010
        List<SQLBuilder.JoinBuilder> joins = select.from().getJoins();
1011
        if(!CollectionUtils.isEmpty(joins)){
1012
            for (SQLBuilder.JoinBuilder join : joins) {
1013
                SQLBuilder.TableNameBuilder joinTable = join.getTable();
1014
                FeatureType featureType = dataManager.getStoresRepository().getFeatureType(joinTable.getName());
1015
                tables.add(new ImmutablePair<>(joinTable,featureType));
1016
            }
1017
        }
1018
        SQLBuilder.TableNameBuilder table = select.from().table();
1019
        if(type == null){
1020
            type = dataManager.getStoresRepository().getFeatureType(table.getName());
1021
        }
1022
        tables.add(new ImmutablePair<>(table,type));
1023
    }
1024
    
1025
    protected void addTableToColumnReferencesInSingleSelect(SQLBuilder sqlbuilder, SelectBuilder select, List<Pair<SQLBuilder.TableNameBuilder,FeatureType>> outerTables) {
1026
        
1027
        final SQLBuilder.TableNameBuilder table = select.from().table();
1028

    
1029
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
1030
        List<ExpressionBuilder.Variable> variables = new ArrayList<>();
1031
        List<ExpressionBuilder.Value> variablesToExclude = new ArrayList<>();
1032
        
1033
        select.accept((ExpressionBuilder.Visitable value) -> {
1034
            if(value instanceof Column) {
1035
                Column c = (Column)value;
1036
                if(c.table() == null || !c.table().has_name()){
1037
                    variables.add(c);
1038
                }
1039
                
1040
            } else if(value instanceof ExpressionBuilder.Variable) {
1041
                variables.add((ExpressionBuilder.Variable) value);
1042
            }
1043

    
1044
        }, new ExpressionBuilder.VisitorFilter() {
1045
            @Override
1046
            public boolean skipChildren() {
1047
                return true;
1048
            }
1049
            
1050
            @Override
1051
            public boolean accept(ExpressionBuilder.Visitable visitable) {
1052
                if(select == visitable){
1053
                    return true;
1054
                }
1055
                if(visitable instanceof SelectBuilder) {
1056
                    ArrayList<Pair<SQLBuilder.TableNameBuilder, FeatureType>> tables = new ArrayList<>(outerTables);
1057
                    collectTablesFromSelect(select, tables, null);
1058
                    
1059
                    addTableToColumnReferencesInSingleSelect(sqlbuilder, (SelectBuilder) visitable, tables);
1060
                    return false;
1061
                }
1062
                return true;
1063
            }
1064
        });
1065
        
1066
        for (ExpressionBuilder.Variable variable : variables) {
1067
            boolean found = false;
1068
            for (ExpressionBuilder.Value v : variablesToExclude) {
1069
                if(v == variable){
1070
                    found = true;
1071
                    break;
1072
                }
1073
            }
1074
            if(found){
1075
                continue;
1076
            }
1077

    
1078
            SQLBuilder.TableNameBuilder tableName = getTable(outerTables,variable.name());
1079
            if(tableName != null){
1080
                ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
1081
                        tableName,
1082
                        variable.name()
1083
                );
1084
                value_replacements.add(
1085
                        new ExpressionBuilder.Value[]{
1086
                            variable,
1087
                            variable_replacement
1088
                        }
1089
                );
1090
            } else {
1091
                ExpressionBuilder.Variable variable_replacement = null;
1092
                if(variable instanceof SQLBuilder.Column){
1093
                    Column column = (SQLBuilder.Column) variable;
1094
                    if (column.table() == null || !column.table().has_name()) {
1095
                        variable_replacement = sqlbuilder.column(
1096
                                table,
1097
                                variable.name()
1098
                        );
1099
                    } else if(!column.table().has_schema()){
1100
                        SQLBuilder.TableNameBuilder t = sqlbuilder.table_name()
1101
                                .name(column.table().getName())
1102
                                .schema(table.getSchema()); //Mismo esquema que en el FROM
1103
                        variable_replacement = sqlbuilder.column(
1104
                                t,
1105
                                variable.name()
1106
                        );
1107
                    }
1108
                } else {
1109
                    variable_replacement = sqlbuilder.column(
1110
                            table,
1111
                            variable.name()
1112
                    );
1113
                }
1114
                if(variable_replacement != null){
1115
                    value_replacements.add(
1116
                            new ExpressionBuilder.Value[]{
1117
                                variable,
1118
                                variable_replacement
1119
                            }
1120
                    );
1121
                }
1122
            }
1123

    
1124
        }
1125

    
1126
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
1127
            ExpressionBuilder.Value target = replaceValue[0];
1128
            ExpressionBuilder.Value replacement = replaceValue[1];
1129
            Boolean addTableName = (Boolean) target.getProperty(ADD_TABLE_NAME_TO_COLUMNS);
1130
            if(addTableName == null || !addTableName){
1131
                continue;
1132
            }
1133
            select.replace(target, replacement);
1134
        }
1135

    
1136
    }
1137
    
1138
    protected SQLBuilder.TableNameBuilder getTable(List<Pair<SQLBuilder.TableNameBuilder, FeatureType>> outerTables, String columnName) {
1139
        ListIterator<Pair<SQLBuilder.TableNameBuilder, FeatureType>> it = outerTables.listIterator(outerTables.size());
1140
        while (it.hasPrevious()) {
1141
            Pair<SQLBuilder.TableNameBuilder, FeatureType> pair = it.previous();
1142
            FeatureType featureType = pair.getRight();
1143
            if(featureType.get(columnName) != null){
1144
                return pair.getLeft();
1145
            }
1146
        }
1147
        return null;
1148
    }
1149
    
1150
    @Override
1151
    public void setTransaction(DataTransactionServices transaction) {
1152
        this.transaction = transaction;
1153
    }
1154
    
1155
    @Override
1156
    public String toString() {
1157
        try {
1158
            return String.format("%s %x %s", this.getClass().getSimpleName(), this.hashCode(), this.connectionParameters.getUrl());
1159
        } catch (Exception e) {
1160
            return super.toString();
1161
        }
1162
    }
1163

    
1164
  @Override
1165
    public String getConnectionProviderStatus() {
1166
        return "";
1167
    }
1168
        
1169
  @Override
1170
        public void expandCalculedColumns(JDBCSQLBuilderBase sqlbuilder) {
1171
            ComputedAttribute computedAttributeFormater = new ComputedAttribute(sqlbuilder, sqlbuilder.formatter());
1172
            for (int i = 0; i < 10; i++) {
1173
                List<Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value>> variablesToReplace = new ArrayList<>();
1174
                sqlbuilder.accept((ExpressionBuilder.Visitable value) -> {
1175
                    if (computedAttributeFormater.canApply((ExpressionBuilder.Value) value)) {
1176
                        ExpressionBuilder.Variable variable = (ExpressionBuilder.Variable) value;
1177
                        ExpressionBuilder.Value replace = computedAttributeFormater.expandedValue(variable);
1178
                        variablesToReplace.add(Pair.of(variable, replace));
1179
                    }
1180
                }, null);
1181
                if (variablesToReplace.isEmpty()) {
1182
                    break;
1183
                } 
1184
                for (Pair<ExpressionBuilder.Variable, ExpressionBuilder.Value> entry : variablesToReplace) {
1185
                    ExpressionBuilder.Value variable = entry.getKey();
1186
                    ExpressionBuilder.Value replace = entry.getValue();
1187
                    Boolean addTableName = (Boolean) variable.getProperty(ADD_TABLE_NAME_TO_COLUMNS);
1188
                    if (addTableName != null && addTableName) {
1189
                        sqlbuilder.setProperties(replace, null, ADD_TABLE_NAME_TO_COLUMNS,true);
1190
                    }
1191
                    sqlbuilder.select().replace(variable, replace);
1192
                }
1193
            }
1194
    }
1195
        
1196
    @Override
1197
    public DataTransactionServices getTransaction() {
1198
        return this.transaction;
1199
    }
1200

    
1201
    public ConnectionProvider getConnectionProvider() {
1202
        return new FakeConnectionProvider(connectionParameters);
1203
    }
1204
}