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

History | View | Annotate | Download (27.3 KB)

1
package org.gvsig.fmap.dal.store.jdbc2.spi;
2

    
3
import java.sql.Blob;
4
import org.gvsig.fmap.dal.store.jdbc2.impl.ResulSetControlerBase;
5
import org.gvsig.fmap.dal.store.jdbc2.spi.operations.OperationsFactoryBase;
6
import java.sql.Connection;
7
import java.sql.ResultSet;
8
import java.util.ArrayList;
9
import java.util.List;
10
import org.apache.commons.lang3.ArrayUtils;
11
import org.apache.commons.lang3.StringUtils;
12
import org.apache.commons.lang3.mutable.MutableBoolean;
13
import org.gvsig.expressionevaluator.Code;
14
import org.gvsig.expressionevaluator.ExpressionBuilder;
15
import org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType;
16
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.EWKB;
17
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.NATIVE;
18
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.WKB;
19
import static org.gvsig.expressionevaluator.ExpressionBuilder.GeometrySupportType.WKT;
20
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
21
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
22
import org.gvsig.expressionevaluator.Function;
23
import org.gvsig.expressionevaluator.SymbolTable;
24
import org.gvsig.fmap.dal.DataTypes;
25
import org.gvsig.fmap.dal.SQLBuilder;
26
import org.gvsig.fmap.dal.exception.DataException;
27
import org.gvsig.fmap.dal.exception.InitializeException;
28
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
29
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
30
import org.gvsig.fmap.dal.feature.FeatureType;
31
import org.gvsig.fmap.dal.feature.ForeingKey;
32

    
33
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
34
import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase;
35
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
36
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
37
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
38
import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices;
39
import org.gvsig.fmap.dal.spi.DataStoreProviderServices;
40
import org.gvsig.fmap.dal.store.db.DBHelper;
41
import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters;
42
import org.gvsig.fmap.dal.store.jdbc.JDBCLibrary;
43
import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters;
44
import org.gvsig.fmap.dal.store.jdbc.JDBCResource;
45
import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters;
46
import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters;
47
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException;
48
import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper;
49
import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer;
50
import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider;
51
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
52
import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory;
53
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler;
54
import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry;
55
import org.gvsig.fmap.geom.Geometry;
56
import org.gvsig.fmap.geom.GeometryLocator;
57
import org.gvsig.fmap.geom.GeometryManager;
58
import org.gvsig.tools.dispose.impl.AbstractDisposable;
59
import org.gvsig.tools.evaluator.Evaluator;
60
import org.gvsig.tools.exception.BaseException;
61
import org.gvsig.tools.exception.NotYetImplemented;
62
import org.gvsig.tools.visitor.VisitCanceledException;
63
import org.gvsig.tools.visitor.Visitor;
64
import org.slf4j.Logger;
65
import org.slf4j.LoggerFactory;
66

    
67
@SuppressWarnings("UseSpecificCatch")
68
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
69

    
70
    private static final boolean ALLOW_AUTOMATIC_VALUES = true;
71
    private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
72
    private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
73

    
74
    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
75

    
76
    private ResulSetControler resulSetControler = null;
77

    
78
    // Quien ha creado este helper.
79
    // Se le reenviaran las notificaciones del recurso asociado al helper.
80
    private ResourceConsumer helperClient = null;
81

    
82
    private GeometryManager geometryManager = null;
83

    
84
    private JDBCConnectionParameters connectionParameters;
85

    
86
    private JDBCStoreProvider store;    
87
    
88
    protected OperationsFactory operationsFactory = null;
89
    
90
    protected SRSSolver srssolver;
91
    
92
    public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
93
        this.connectionParameters = connectionParameters;
94
        
95
        // If a particular treatment is required to convert SRS to the 
96
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
97
        this.srssolver = new SRSSolverDumb(this);
98
    }
99

    
100
    protected void initialize(
101
            ResourceConsumer helperClient,
102
            JDBCConnectionParameters connectionParameters,
103
            JDBCStoreProvider store
104
        ) {
105
        this.store = store;
106
        this.helperClient = helperClient;
107
        this.connectionParameters = connectionParameters;
108
        initializeResource(connectionParameters);        
109
    }
110
    
111
    protected String getResourceType() {
112
        return JDBCResource.NAME;
113
    }
114

    
115
    @Override
116
    public String getProviderName() {
117
        return JDBCLibrary.NAME;
118
    }
119
        
120
    @Override
121
    public GeometrySupportType getGeometrySupportType() {
122
        // El proveedor generico de JDBC guadara las geoemtrias como WKT
123
        return GeometrySupportType.WKT;
124
    }
125

    
126
    @Override
127
    public boolean hasSpatialFunctions() {
128
        // Por defecto el proveedor generico de JDBC asume que la BBDD no 
129
        // tiene soporte para funciones espaciales.
130
        return false;
131
    }
132

    
133
    @Override
134
    public boolean allowNestedOperations() {
135
        return false;
136
    }
137
    
138
    @Override
139
    public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
140
        // Como va a guardar las geometrias en WKT, puede escribirlas todas.
141
        return true;
142
    }
143

    
144
    @Override
145
    public JDBCSQLBuilderBase createSQLBuilder() {
146
        return new JDBCSQLBuilderBase(this);
147
    }
148

    
149
    @Override
150
    public String getQuoteForIdentifiers() {
151
        return QUOTE_FOR_USE_IN_IDENTIFIERS;
152
    }
153

    
154
    @Override
155
    public boolean allowAutomaticValues() {
156
        return ALLOW_AUTOMATIC_VALUES;
157
    }
158

    
159
    @Override
160
    public boolean supportOffsetInSelect() {
161
        // Asumimos que la BBDD soporta OFFSET
162
        return true;
163
    }
164
    
165
    @Override
166
    public String getQuoteForStrings() {
167
        return QUOTE_FOR_USE_IN_STRINGS;
168
    }
169

    
170
    protected boolean supportCaller(Code.Caller caller) {
171
        if( StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE") ) {
172
            return true;
173
        }
174
        Function function = caller.function();
175
        if( function==null ) {
176
            return false;
177
        }
178
        if( !function.isSQLCompatible() ) {
179
            return false;
180
        }
181
        if( !this.hasSpatialFunctions() ) {
182
            if( StringUtils.equalsIgnoreCase(function.group(),Function.GROUP_OGC) ) {
183
                return false;
184
            }
185
        }
186
        return true;
187
    }
188
    
189
    @Override
190
    public boolean supportFilter(final FeatureType type, Evaluator filter) {
191
        // No podemos filtrar cuando:
192
        // - Hay una subquery especificada en los parametros 
193
        // - Si hay un filtro y el getSQL devuelbe null.
194
        // - Si se esta usando alguna funcion no-sql en el getSQL
195
        // - Si se estan usando funciones OGC y no soportamos funciones espaciales
196
        // - Si se esta usando algun campo calculado en la expresion de filtro.
197
        // 
198
        // Un proveedor especifico podria sobreescribir el metodo,
199
        // para hilar mas fino al comprobar si soporta el filtro o no.
200
        //
201
        
202
        if (this.useSubquery()) {
203
            // Se esta usando una subquery en los parametros de acceso a la
204
            // BBDD, asi que no podemos filtrar.
205
            return false;
206
        }
207
        if (filter == null) {
208
            // No hay que filtrar nada, asi que se p?ede.
209
            return true;
210
        }
211
        String sql = filter.getSQL();
212
        if (StringUtils.isEmpty(sql)) {
213
            // Hay un filtro, pero la SQL es null, con lo que no podemos
214
            // filtrar por el.
215
            return false;
216
        }
217

    
218
        // Ahora vamos a comprobar que las funciones que se usan son 
219
        // compatibles sql, y que no se usan funciones OGC si el 
220
        // proveedor dice que no soporta funciones espaciales.
221
        // Tambien comprobaremos que el filtro no usa ningun campo calculado.
222
        final MutableBoolean isCompatible = new MutableBoolean(true);
223
        ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
224
        Code code = manager.compile(sql);
225
        SymbolTable symbolTable = manager.createSymbolTable();
226
        code.link(symbolTable);
227
        try {
228
            code.accept(new Visitor() {
229
                @Override
230
                public void visit(Object code_obj) throws VisitCanceledException, BaseException {
231
                    Code code = (Code) code_obj;
232
                    switch(code.code()) {
233
                        case Code.CALLER:
234
                            Code.Caller caller = (Code.Caller) code;
235
                            if( !supportCaller(caller) ) {
236
                                isCompatible.setValue(false);
237
                                throw new VisitCanceledException();
238
                            }
239
                            break;
240
                            
241
                        case Code.IDENTIFIER:
242
                            Code.Identifier identifier = (Code.Identifier) code;
243
                            if( type!=null ) {
244
                                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
245
                                if( attrdesc!=null ) {
246
                                    if( attrdesc.isComputed() ) {
247
                                        isCompatible.setValue(false);
248
                                        throw new VisitCanceledException();
249
                                    }
250
                                }
251
                            }
252
                            break;
253
                    }
254
                }
255
            });
256

    
257
        } catch (VisitCanceledException ex) {
258
            // Do nothing
259
        } catch (Exception ex) {
260
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
261
        }
262
        
263
        return isCompatible.booleanValue();
264
    }
265
    
266
    @Override
267
    public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
268
        if (this.useSubquery()) {
269
            return false;
270
        }
271
        if (order == null) {
272
            return true;
273
        }
274
        for( FeatureQueryOrder.FeatureQueryOrderMember member : order.members() ) {
275
            if (member.hasEvaluator()) {
276
                if( !this.supportFilter(type, member.getEvaluator()) ) {
277
                    return false;
278
                }
279
            }            
280
        }
281
        return true;
282
    }
283
    
284
    @Override
285
    public OperationsFactory getOperations() {
286
        if (this.operationsFactory== null) {
287
            this.operationsFactory = new OperationsFactoryBase(this);
288
        }
289
        return operationsFactory;
290
    }
291
    
292
    protected void initializeResource(JDBCConnectionParameters params) {
293
//        Object[] resourceParams = new Object[]{
294
//            params.getUrl(),
295
//            params.getHost(),
296
//            params.getPort(),
297
//            params.getDBName(),
298
//            params.getUser(),
299
//            params.getPassword(),
300
//            params.getJDBCDriverClassName()
301
//        };
302
//
303
//        try {
304
//            ResourceManagerProviderServices manager
305
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
306
//            JDBCResource resource = (JDBCResource) manager.createAddResource(
307
//                    this.getResourceType(),
308
//                    resourceParams
309
//            );
310
//            this.resource = resource;
311
//            this.resource.addConsumer(this);
312
//        } catch (InitializeException ex) {
313
//            logger.debug("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
314
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
315
//        }
316

    
317
    }
318

    
319
    @Override
320
    public String getSourceId() {
321
        return this.store.getSourceId();
322
    }
323
    
324
    @Override
325
    public JDBCResource getResource() {
326
        return null;
327
//        return this.resource;
328
    }
329

    
330
    @Override
331
    public Connection getConnection() throws AccessResourceException {
332
        throw new NotYetImplemented();
333
    }
334

    
335
    @Override
336
    public Connection getConnectionWritable() throws AccessResourceException {
337
        return this.getConnection();
338
    }
339

    
340
    @Override
341
    public String getConnectionURL() {
342
        return null;
343
    }
344

    
345
    @Override
346
    public JDBCConnectionParameters getConnectionParameters() {
347
        return connectionParameters;
348
    }
349

    
350
    @Override
351
    public void closeConnection(Connection connection) {
352
        if( connection != null ) {
353
            LOGGER.debug("Clossing connection "+connection.hashCode());
354
            try {
355
                connection.close();
356
            } catch(Exception ex) {
357
                LOGGER.warn("Can't close connection.", ex);
358
            }
359
       }
360
    }
361

    
362
    @Override
363
    public void closeConnectionQuietly(Connection connection) {
364
        if( connection != null ) {
365
            LOGGER.debug("Clossing connection quietly "+connection.hashCode());
366
            try {
367
                connection.close();
368
            } catch(Exception ex) {
369
                LOGGER.warn("Can't close connection.", ex);
370
            }
371
       }
372
    }
373
    
374
    @Override
375
    protected void doDispose() throws BaseException {
376
        JDBCUtils.closeQuietly(this);
377
    }
378

    
379
    @Override
380
    public void close() throws Exception {
381
//        this.resource.removeConsumer(this);
382
        JDBCUtils.closeQuietly(this.resulSetControler);
383
//        this.resource = null;
384
        this.resulSetControler = null;
385
    }
386

    
387
    @Override
388
    public boolean closeResourceRequested(ResourceProvider resource) {
389
        return this.helperClient.closeResourceRequested(resource);
390
    }
391

    
392
    @Override
393
    public void resourceChanged(ResourceProvider resource) {
394
        this.helperClient.resourceChanged(resource);
395
    }
396

    
397
    @Override
398
    public GeometryManager getGeometryManager() {
399
        if (this.geometryManager == null) {
400
            this.geometryManager = GeometryLocator.getGeometryManager();
401
        }
402
        return this.geometryManager;
403
    }
404

    
405
    @Override
406
    public ResulSetControler getResulSetControler() {
407
        if (this.resulSetControler == null) {
408
            this.resulSetControler = new ResulSetControlerBase(this);
409
        }
410
        return this.resulSetControler;
411
    }
412

    
413
    @Override
414
    public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
415
        fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
416
    }
417

    
418
    @Override
419
    public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException {
420
        Object value;
421
        try {
422
            int rsIndex = 1;
423
            for (FeatureAttributeDescriptor column : columns) {
424
                switch (column.getType()) {
425
                    case DataTypes.GEOMETRY:
426
                        value = this.getGeometryFromColumn(rs, rsIndex++);
427
                        break;
428
                    default:
429
                        value = rs.getObject(rsIndex++);
430
                        if( value instanceof Blob ) {
431
                            Blob blob = (Blob)value;
432
                            value = blob.getBytes(0, (int) blob.length());
433
                            blob.free();
434
                        }
435
                }
436
                feature.set(column.getIndex(), value);
437
            }
438
            if( ArrayUtils.isNotEmpty(extraValueNames) ) {
439
                feature.setExtraValueNames(extraValueNames);
440
                for (int index = 0; index < extraValueNames.length; index++) {
441
                    value = rs.getObject(rsIndex++);
442
                    if( value instanceof Blob ) {
443
                        Blob blob = (Blob)value;
444
                        value = blob.getBytes(0, (int) blob.length());
445
                        blob.free();
446
                    }
447
                    feature.setExtraValue(index, value);
448
                }
449
            }
450
        } catch (Exception ex) {
451
            throw new JDBCCantFetchValueException(ex);
452
        }
453
    }
454

    
455
    @Override
456
    public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
457
        return getGeometryFromColumn(rs.get(), index);
458
    }
459

    
460
    @Override
461
    public Geometry getGeometryFromColumn(ResultSet rs, int index) throws DataException {
462
        try {
463
            Object value;
464
            switch (this.getGeometrySupportType()) {
465
                case NATIVE:
466
                case WKB:
467
                    value = rs.getBytes(index);
468
                    if (value == null) {
469
                        return null;
470
                    }
471
                    return this.getGeometryManager().createFrom((byte[]) value);
472

    
473
                case EWKB:
474
                    value = rs.getBytes(index);
475
                    if (value == null) {
476
                        return null;
477
                    }
478
                    return this.getGeometryManager().createFrom((byte[]) value);
479
                case WKT:
480
                default:
481
                    value = rs.getString(index);
482
                    if (value == null) {
483
                        return null;
484
                    }
485
                    return this.getGeometryManager().createFrom((String) value);
486

    
487
            }
488
        } catch (Exception ex) {
489
            throw new JDBCCantFetchValueException(ex);
490
        }
491
    }
492

    
493
    @Override
494
    public FeatureProvider createFeature(FeatureType featureType) throws DataException {
495
        return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
496
    }
497
    
498
    @Override
499
    public boolean useSubquery() {
500
        if( this.store == null ) {
501
            return false;
502
        }
503
        return !StringUtils.isEmpty(this.store.getParameters().getSQL());
504
    }  
505
    
506
    @Override
507
    public SRSSolver getSRSSolver() {
508
        return this.srssolver;
509
    }
510
    
511
    @Override
512
    public JDBCStoreProvider createProvider(
513
            JDBCStoreParameters parameters, 
514
            DataStoreProviderServices providerServices
515
        ) throws InitializeException {
516
        
517
        JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
518
                parameters, 
519
                providerServices, 
520
                DBHelper.newMetadataContainer(JDBCLibrary.NAME),
521
                this
522
        );
523
        this.initialize(theStore, parameters, theStore);
524
        return theStore;
525
    }
526

    
527
    @Override
528
    public JDBCServerExplorer createServerExplorer(
529
            JDBCServerExplorerParameters parameters, 
530
            DataServerExplorerProviderServices providerServices
531
        ) throws InitializeException {
532
        
533
        JDBCServerExplorer explorer = new JDBCServerExplorerBase(
534
                parameters, 
535
                providerServices, 
536
                this
537
        );
538
        this.initialize(explorer, parameters, null);
539
        return explorer;
540
    }
541

    
542
    @Override
543
    public JDBCNewStoreParameters createNewStoreParameters() {
544
        return new JDBCNewStoreParameters();
545
    }
546

    
547
    @Override
548
    public JDBCStoreParameters createOpenStoreParameters() {
549
        return new JDBCStoreParameters();
550
    }
551

    
552
    @Override
553
    public JDBCServerExplorerParameters createServerExplorerParameters() {
554
        return new JDBCServerExplorerParameters();
555
    }
556

    
557
    @Override
558
    public String getSourceId(JDBCStoreParameters parameters) {
559
        return parameters.getHost() + ":" +
560
               parameters.getDBName() + ":" + 
561
               parameters.getSchema()+ ":" + 
562
               parameters.tableID();
563
    }
564

    
565
    @Override
566
    public boolean isThreadSafe() {
567
        return true;
568
    }
569
    
570
    @Override
571
    public String[] replaceForeingValueFunction(SQLBuilder sqlbuilder, FeatureType type) {
572
        // See test SQLBuilderTest->testForeingValue()
573
        final ExpressionBuilder where = sqlbuilder.select().where();
574
        if( where == null || where.isEmpty() ) {
575
            return null;
576
        }
577
        final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
578
        final ExpressionBuilder expbuilder = sqlbuilder.expression();
579
        
580
        final List<String> foreing_value_args = new ArrayList<>();
581
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
582

    
583
        // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
584
        // el argumento de esta asi como por que tendriamos que sustituirla 
585
        // una vez hechos los left joins que toquen.
586
        where.accept(new ExpressionBuilder.Visitor() {
587
            @Override
588
            public void visit(ExpressionBuilder.Visitable value) {
589
                // Requiere que sea la funcion "FOREING_VALUE con un solo 
590
                // argumento que sea una constante de tipo string.
591
                if( !(value instanceof ExpressionBuilder.Function) ) {
592
                    return;
593
                }
594
                ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
595
                if( !StringUtils.equalsIgnoreCase(function.name(), "FOREING_VALUE") ) {
596
                    return;
597
                }
598
                if( function.parameters().size()!=1 ) {
599
                    return;
600
                } 
601
                ExpressionBuilder.Value arg = function.parameters().get(0);
602
                if( !(arg instanceof ExpressionBuilder.Constant) ) {
603
                    return;
604
                }
605
                Object arg_value = ((ExpressionBuilder.Constant) arg).value();
606
                if( !(arg_value instanceof CharSequence) ) {
607
                    return;
608
                }
609
                String foreing_value_arg = arg_value.toString();
610
                String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
611
                if( foreingNameParts.length!=2 ) {
612
                    // De momento solo tratamos joins entre dos tablas.
613
                    return;
614
                }
615
                String columnNameLocal = foreingNameParts[0];
616
                String columnNameForeing = foreingNameParts[1];
617
                FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
618
                if( !attr.isForeingKey() ) {
619
                    // Uhm... si el argumento no referencia a un campo que es
620
                    // clave ajena no lo procesamos. 
621
                    // ? Deberiamos lanzar un error ?
622
                    return;
623
                }
624
                // Nos guardaremos por que hay que reemplazar la funcion 
625
                // FOREING_VALUE, y su argumento para mas tarde.
626
                ForeingKey foreingKey = attr.getForeingKey();
627
                SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
628
                        .database(table.getDatabase())
629
                        .schema(table.getSchema()) 
630
                        .name(foreingKey.getTableName());
631
                // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
632
                // que toca de la tabla a la que referencia la clave ajena.
633
                ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
634
                value_replacements.add(
635
                    new ExpressionBuilder.Value[] {
636
                        function,
637
                        function_replacement
638
                    }
639
                );
640
                foreing_value_args.add(foreing_value_arg);
641
            }
642
        }, null);
643
            
644
        // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
645
        // hacer nada.
646
        if( foreing_value_args.isEmpty() ) {
647
            return null;
648
        }
649

    
650
        // Calculamos que referencias de columnas hemos de cambiar para 
651
        // que no aparezcan ambiguedades al hacer el join (nombres de
652
        // columna de una y otra tabla que coincidan).
653
        // Para las columnas que puedan dar conflicto se prepara un reemplazo 
654
        // de estas que tenga el nombre de tabla.
655
        for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
656
            if( variable instanceof SQLBuilderBase.ColumnBase ) {
657
                continue;
658
            }
659
            for (String foreingName : foreing_value_args) {
660
                String[] foreingNameParts = foreingName.split("[.]");
661
                if( foreingNameParts.length != 2) {
662
                    continue;
663
                }
664
                String columnNameLocal = foreingNameParts[0];
665
                String columnNameForeing = foreingNameParts[1];
666
                if( !StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing) ) {
667
                    continue;
668
                }
669
                ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
670
                    table, 
671
                    variable.name()
672
                );
673
                value_replacements.add(
674
                    new ExpressionBuilder.Value[] {
675
                        variable,
676
                        variable_replacement
677
                    }
678
                );
679
            }
680
        }
681
        
682
        // Realizamos los reemplazos calculados previamente (value_replacements).
683
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
684
            ExpressionBuilder.Value target = replaceValue[0];
685
            ExpressionBuilder.Value replacement = replaceValue[1];
686
            sqlbuilder.select().replace(target, replacement);    
687
        }
688

    
689
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
690
        // a los valores referenciados por la funcion FOREING_VALUE.
691
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
692
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
693
        for (String foreingName : foreing_value_args) {
694
            String[] foreingNameParts = foreingName.split("[.]");
695
            if( foreingNameParts.length != 2) {
696
                continue;
697
            }
698
            String columnNameLocal = foreingNameParts[0];
699
            String columnNameForeing = foreingNameParts[1];
700
            FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
701
            if( attr.isForeingKey() ) {
702
                ForeingKey foreingKey = attr.getForeingKey();
703
                SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
704
                        .database(table.getDatabase())
705
                        .schema(table.getSchema()) 
706
                        .name(foreingKey.getTableName());
707
                SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
708
                        .database(table.getDatabase())
709
                        .schema(table.getSchema()) 
710
                        .name(table.getName());
711

    
712
                sqlbuilder.select().from()
713
                    .left_join(
714
                        foreingTable,
715
                        expbuilder.eq(
716
                                sqlbuilder.column(mainTable, columnNameLocal),
717
                                sqlbuilder.column(foreingTable,foreingKey.getCodeName()) 
718
                        )
719
                    );
720
                sqlbuilder.select().column().name(foreingTable,columnNameForeing);
721
            }
722
        }
723

    
724
        return foreing_value_args.toArray(new String[foreing_value_args.size()]);
725
    }
726
}