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

History | View | Annotate | Download (26.9 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.ExpressionEvaluatorLocator;
16
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
17
import org.gvsig.expressionevaluator.Function;
18
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
19
import org.gvsig.expressionevaluator.SymbolTable;
20
import org.gvsig.fmap.dal.DataTypes;
21
import org.gvsig.fmap.dal.SQLBuilder;
22
import org.gvsig.fmap.dal.exception.DataException;
23
import org.gvsig.fmap.dal.exception.InitializeException;
24
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
25
import org.gvsig.fmap.dal.feature.FeatureQueryOrder;
26
import org.gvsig.fmap.dal.feature.FeatureType;
27
import org.gvsig.fmap.dal.feature.ForeingKey;
28

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

    
63
@SuppressWarnings("UseSpecificCatch")
64
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
65

    
66
    private static final boolean ALLOW_AUTOMATIC_VALUES = true;
67
    private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
68
    private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
69

    
70
    private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class);
71

    
72
    private ResulSetControler resulSetControler = null;
73

    
74
    // Quien ha creado este helper.
75
    // Se le reenviaran las notificaciones del recurso asociado al helper.
76
    private ResourceConsumer helperClient = null;
77

    
78
    private GeometryManager geometryManager = null;
79

    
80
    private JDBCConnectionParameters connectionParameters;
81

    
82
    private JDBCStoreProvider store;
83

    
84
    protected OperationsFactory operationsFactory = null;
85

    
86
    protected SRSSolver srssolver;
87

    
88
    public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
89
        this.connectionParameters = connectionParameters;
90

    
91
        // If a particular treatment is required to convert SRS to the 
92
        // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
93
        this.srssolver = new SRSSolverDumb(this);
94
    }
95

    
96
    protected void initialize(
97
            ResourceConsumer helperClient,
98
            JDBCConnectionParameters connectionParameters,
99
            JDBCStoreProvider store
100
    ) {
101
        this.store = store;
102
        this.helperClient = helperClient;
103
        this.connectionParameters = connectionParameters;
104
        initializeResource(connectionParameters);
105
    }
106

    
107
    protected String getResourceType() {
108
        return JDBCResource.NAME;
109
    }
110

    
111
    @Override
112
    public String getProviderName() {
113
        return JDBCLibrary.NAME;
114
    }
115

    
116
    @Override
117
    public GeometrySupportType getGeometrySupportType() {
118
        // El proveedor generico de JDBC guadara las geoemtrias como WKT
119
        return GeometrySupportType.WKT;
120
    }
121

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

    
129
    @Override
130
    public boolean allowNestedOperations() {
131
        return false;
132
    }
133

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

    
140
    @Override
141
    public JDBCSQLBuilderBase createSQLBuilder() {
142
        return new JDBCSQLBuilderBase(this);
143
    }
144

    
145
    @Override
146
    public String getQuoteForIdentifiers() {
147
        return QUOTE_FOR_USE_IN_IDENTIFIERS;
148
    }
149

    
150
    @Override
151
    public boolean allowAutomaticValues() {
152
        return ALLOW_AUTOMATIC_VALUES;
153
    }
154

    
155
    @Override
156
    public boolean supportOffsetInSelect() {
157
        // Asumimos que la BBDD soporta OFFSET
158
        return true;
159
    }
160

    
161
    @Override
162
    public String getQuoteForStrings() {
163
        return QUOTE_FOR_USE_IN_STRINGS;
164
    }
165

    
166
    protected boolean supportCaller(Code.Caller caller) {
167
        if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) {
168
            return true;
169
        }
170
        Function function = caller.function();
171
        if (function == null) {
172
            return false;
173
        }
174
        if (!function.isSQLCompatible()) {
175
            return false;
176
        }
177
        if (!this.hasSpatialFunctions()) {
178
            if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
179
                return false;
180
            }
181
        }
182
        return true;
183
    }
184

    
185
    @Override
186
    public boolean supportFilter(final FeatureType type, Evaluator filter) {
187
        // No podemos filtrar cuando:
188
        // - Hay una subquery especificada en los parametros 
189
        // - Si hay un filtro y el getSQL devuelbe null.
190
        // - Si se esta usando alguna funcion no-sql en el getSQL
191
        // - Si se estan usando funciones OGC y no soportamos funciones espaciales
192
        // - Si se esta usando algun campo calculado en la expresion de filtro.
193
        // 
194
        // Un proveedor especifico podria sobreescribir el metodo,
195
        // para hilar mas fino al comprobar si soporta el filtro o no.
196
        //
197

    
198
        if (this.useSubquery()) {
199
            // Se esta usando una subquery en los parametros de acceso a la
200
            // BBDD, asi que no podemos filtrar.
201
            return false;
202
        }
203
        if (filter == null) {
204
            // No hay que filtrar nada, asi que se p?ede.
205
            return true;
206
        }
207
        String sql = filter.getSQL();
208
        if (StringUtils.isEmpty(sql)) {
209
            // Hay un filtro, pero la SQL es null, con lo que no podemos
210
            // filtrar por el.
211
            return false;
212
        }
213

    
214
        // Ahora vamos a comprobar que las funciones que se usan son 
215
        // compatibles sql, y que no se usan funciones OGC si el 
216
        // proveedor dice que no soporta funciones espaciales.
217
        // Tambien comprobaremos que el filtro no usa ningun campo calculado.
218
        final MutableBoolean isCompatible = new MutableBoolean(true);
219
        ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
220
        Code code = manager.compile(sql);
221
        SymbolTable symbolTable = manager.createSymbolTable();
222
        code.link(symbolTable);
223
        try {
224
            code.accept(new Visitor() {
225
                @Override
226
                public void visit(Object code_obj) throws VisitCanceledException, BaseException {
227
                    Code code = (Code) code_obj;
228
                    switch (code.code()) {
229
                        case Code.CALLER:
230
                            Code.Caller caller = (Code.Caller) code;
231
                            if (!supportCaller(caller)) {
232
                                isCompatible.setValue(false);
233
                                throw new VisitCanceledException();
234
                            }
235
                            break;
236

    
237
                        case Code.IDENTIFIER:
238
                            Code.Identifier identifier = (Code.Identifier) code;
239
                            if (type != null) {
240
                                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
241
                                if (attrdesc != null) {
242
                                    if (attrdesc.isComputed()) {
243
                                        isCompatible.setValue(false);
244
                                        throw new VisitCanceledException();
245
                                    }
246
                                }
247
                            }
248
                            break;
249
                    }
250
                }
251
            });
252

    
253
        } catch (VisitCanceledException ex) {
254
            // Do nothing
255
        } catch (Exception ex) {
256
            LOGGER.warn("Can't calculate if is SQL compatible.", ex);
257
        }
258

    
259
        return isCompatible.booleanValue();
260
    }
261

    
262
    @Override
263
    public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
264
        if (this.useSubquery()) {
265
            return false;
266
        }
267
        if (order == null) {
268
            return true;
269
        }
270
        for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
271
            if (member.hasEvaluator()) {
272
                if (!this.supportFilter(type, member.getEvaluator())) {
273
                    return false;
274
                }
275
            }
276
        }
277
        return true;
278
    }
279

    
280
    @Override
281
    public OperationsFactory getOperations() {
282
        if (this.operationsFactory == null) {
283
            this.operationsFactory = new OperationsFactoryBase(this);
284
        }
285
        return operationsFactory;
286
    }
287

    
288
    protected void initializeResource(JDBCConnectionParameters params) {
289
//        Object[] resourceParams = new Object[]{
290
//            params.getUrl(),
291
//            params.getHost(),
292
//            params.getPort(),
293
//            params.getDBName(),
294
//            params.getUser(),
295
//            params.getPassword(),
296
//            params.getJDBCDriverClassName()
297
//        };
298
//
299
//        try {
300
//            ResourceManagerProviderServices manager
301
//                    = (ResourceManagerProviderServices) DALLocator.getResourceManager();
302
//            JDBCResource resource = (JDBCResource) manager.createAddResource(
303
//                    this.getResourceType(),
304
//                    resourceParams
305
//            );
306
//            this.resource = resource;
307
//            this.resource.addConsumer(this);
308
//        } catch (InitializeException ex) {
309
//            logger.debug("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
310
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
311
//        }
312

    
313
    }
314

    
315
    @Override
316
    public String getSourceId() {
317
        return this.store.getSourceId();
318
    }
319

    
320
    @Override
321
    public JDBCResource getResource() {
322
        return null;
323
//        return this.resource;
324
    }
325

    
326
    @Override
327
    public Connection getConnection() throws AccessResourceException {
328
        throw new NotYetImplemented();
329
    }
330

    
331
    @Override
332
    public Connection getConnectionWritable() throws AccessResourceException {
333
        return this.getConnection();
334
    }
335

    
336
    @Override
337
    public String getConnectionURL() {
338
        return null;
339
    }
340

    
341
    @Override
342
    public JDBCConnectionParameters getConnectionParameters() {
343
        return connectionParameters;
344
    }
345

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

    
358
    @Override
359
    public void closeConnectionQuietly(Connection connection) {
360
        if (connection != null) {
361
            LOGGER.debug("Clossing connection quietly " + connection.hashCode());
362
            try {
363
                connection.close();
364
            } catch (Exception ex) {
365
                LOGGER.warn("Can't close connection.", ex);
366
            }
367
        }
368
    }
369

    
370
    @Override
371
    protected void doDispose() throws BaseException {
372
        JDBCUtils.closeQuietly(this);
373
    }
374

    
375
    @Override
376
    public void close() throws Exception {
377
//        this.resource.removeConsumer(this);
378
        JDBCUtils.closeQuietly(this.resulSetControler);
379
//        this.resource = null;
380
        this.resulSetControler = null;
381
    }
382

    
383
    @Override
384
    public boolean closeResourceRequested(ResourceProvider resource) {
385
        return this.helperClient.closeResourceRequested(resource);
386
    }
387

    
388
    @Override
389
    public void resourceChanged(ResourceProvider resource) {
390
        this.helperClient.resourceChanged(resource);
391
    }
392

    
393
    @Override
394
    public GeometryManager getGeometryManager() {
395
        if (this.geometryManager == null) {
396
            this.geometryManager = GeometryLocator.getGeometryManager();
397
        }
398
        return this.geometryManager;
399
    }
400

    
401
    @Override
402
    public ResulSetControler getResulSetControler() {
403
        if (this.resulSetControler == null) {
404
            this.resulSetControler = new ResulSetControlerBase(this);
405
        }
406
        return this.resulSetControler;
407
    }
408

    
409
    @Override
410
    public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException {
411
        fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames());
412
    }
413

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

    
451
    @Override
452
    public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException {
453
        return getGeometryFromColumn(rs.get(), index);
454
    }
455

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

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

    
483
            }
484
        } catch (Exception ex) {
485
            throw new JDBCCantFetchValueException(ex);
486
        }
487
    }
488

    
489
    @Override
490
    public FeatureProvider createFeature(FeatureType featureType) throws DataException {
491
        return this.store.getStoreServices().createDefaultFeatureProvider(featureType);
492
    }
493

    
494
    @Override
495
    public boolean useSubquery() {
496
        if (this.store == null) {
497
            return false;
498
        }
499
        return !StringUtils.isEmpty(this.store.getParameters().getSQL());
500
    }
501

    
502
    @Override
503
    public SRSSolver getSRSSolver() {
504
        return this.srssolver;
505
    }
506

    
507
    @Override
508
    public JDBCStoreProvider createProvider(
509
            JDBCStoreParameters parameters,
510
            DataStoreProviderServices providerServices
511
    ) throws InitializeException {
512

    
513
        JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
514
                parameters,
515
                providerServices,
516
                DBHelper.newMetadataContainer(JDBCLibrary.NAME),
517
                this
518
        );
519
        this.initialize(theStore, parameters, theStore);
520
        return theStore;
521
    }
522

    
523
    @Override
524
    public JDBCServerExplorer createServerExplorer(
525
            JDBCServerExplorerParameters parameters,
526
            DataServerExplorerProviderServices providerServices
527
    ) throws InitializeException {
528

    
529
        JDBCServerExplorer explorer = new JDBCServerExplorerBase(
530
                parameters,
531
                providerServices,
532
                this
533
        );
534
        this.initialize(explorer, parameters, null);
535
        return explorer;
536
    }
537

    
538
    @Override
539
    public JDBCNewStoreParameters createNewStoreParameters() {
540
        return new JDBCNewStoreParameters();
541
    }
542

    
543
    @Override
544
    public JDBCStoreParameters createOpenStoreParameters() {
545
        return new JDBCStoreParameters();
546
    }
547

    
548
    @Override
549
    public JDBCServerExplorerParameters createServerExplorerParameters() {
550
        return new JDBCServerExplorerParameters();
551
    }
552

    
553
    @Override
554
    public String getSourceId(JDBCStoreParameters parameters) {
555
        return parameters.getHost() + ":"
556
                + parameters.getDBName() + ":"
557
                + parameters.getSchema() + ":"
558
                + parameters.tableID();
559
    }
560

    
561
    @Override
562
    public boolean isThreadSafe() {
563
        return true;
564
    }
565

    
566
    @Override
567
    public String[] replaceForeingValueFunction(SQLBuilder sqlbuilder, FeatureType type) {
568
        // See test SQLBuilderTest->testForeingValue()
569
        final ExpressionBuilder where = sqlbuilder.select().where();
570
        if (where == null || where.isEmpty()) {
571
            return null;
572
        }
573
        final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
574
        final ExpressionBuilder expbuilder = sqlbuilder.expression();
575

    
576
        final List<String> foreing_value_args = new ArrayList<>();
577
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
578

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

    
640
        // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
641
        // hacer nada.
642
        if (foreing_value_args.isEmpty()) {
643
            return null;
644
        }
645

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

    
678
        // Realizamos los reemplazos calculados previamente (value_replacements).
679
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
680
            ExpressionBuilder.Value target = replaceValue[0];
681
            ExpressionBuilder.Value replacement = replaceValue[1];
682
            sqlbuilder.select().replace(target, replacement);
683
        }
684

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

    
708
                sqlbuilder.select().from()
709
                        .left_join(
710
                                foreingTable,
711
                                expbuilder.eq(
712
                                        sqlbuilder.column(mainTable, columnNameLocal),
713
                                        sqlbuilder.column(foreingTable, foreingKey.getCodeName())
714
                                )
715
                        );
716
                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
717
            }
718
        }
719

    
720
        return foreing_value_args.toArray(new String[foreing_value_args.size()]);
721
    }
722
}