Revision 44750 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

View differences:

JDBCHelperBase.java
17 17
import org.gvsig.expressionevaluator.Function;
18 18
import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType;
19 19
import org.gvsig.expressionevaluator.SymbolTable;
20
import static org.gvsig.fmap.dal.DataManager.FUNCTION_EXISTS;
21
import static org.gvsig.fmap.dal.DataManager.FUNCTION_FOREING_VALUE;
22
import static org.gvsig.fmap.dal.DataManager.FUNCTION_SELECT;
20 23
import org.gvsig.fmap.dal.DataTypes;
21 24
import org.gvsig.fmap.dal.SQLBuilder;
22 25
import org.gvsig.fmap.dal.exception.DataException;
......
63 66
@SuppressWarnings("UseSpecificCatch")
64 67
public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper {
65 68

  
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
  private static final boolean ALLOW_AUTOMATIC_VALUES = true;
70
  private static final String QUOTE_FOR_USE_IN_IDENTIFIERS = "\"";
71
  private static final String QUOTE_FOR_USE_IN_STRINGS = "'";
69 72

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

  
72
    private ResulSetControler resulSetControler = null;
75
  private ResulSetControler resulSetControler = null;
73 76

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

  
78
    private GeometryManager geometryManager = null;
81
  private GeometryManager geometryManager = null;
79 82

  
80
    private JDBCConnectionParameters connectionParameters;
83
  private JDBCConnectionParameters connectionParameters;
81 84

  
82
    private JDBCStoreProvider store;
85
  private JDBCStoreProvider store;
83 86

  
84
    protected OperationsFactory operationsFactory = null;
87
  protected OperationsFactory operationsFactory = null;
85 88

  
86
    protected SRSSolver srssolver;
89
  protected SRSSolver srssolver;
87 90

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

  
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
    }
94
    // If a particular treatment is required to convert SRS to the 
95
    // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
96
    this.srssolver = new SRSSolverDumb(this);
97
  }
95 98

  
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
    }
99
  protected void initialize(
100
          ResourceConsumer helperClient,
101
          JDBCConnectionParameters connectionParameters,
102
          JDBCStoreProvider store
103
  ) {
104
    this.store = store;
105
    this.helperClient = helperClient;
106
    this.connectionParameters = connectionParameters;
107
    initializeResource(connectionParameters);
108
  }
106 109

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

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

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

  
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
    }
125
  @Override
126
  public boolean hasSpatialFunctions() {
127
    // Por defecto el proveedor generico de JDBC asume que la BBDD no 
128
    // tiene soporte para funciones espaciales.
129
    return false;
130
  }
128 131

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

  
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
    }
137
  @Override
138
  public boolean canWriteGeometry(int geometryType, int geometrySubtype) {
139
    // Como va a guardar las geometrias en WKT, puede escribirlas todas.
140
    return true;
141
  }
139 142

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

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

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

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

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

  
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;
169
  protected boolean supportCaller(Code.Caller caller) {
170
    if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) {
171
      return true;
183 172
    }
173
    Function function = caller.function();
174
    if (function == null) {
175
      return false;
176
    }
177
    if (!function.isSQLCompatible()) {
178
      return false;
179
    }
180
    if (!this.hasSpatialFunctions()) {
181
      if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
182
        return false;
183
      }
184
    }
185
    return true;
186
  }
184 187

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

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

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

  
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
                    }
240
            case Code.IDENTIFIER:
241
              Code.Identifier identifier = (Code.Identifier) code;
242
              if (type != null) {
243
                FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name());
244
                if (attrdesc != null) {
245
                  if (attrdesc.isComputed()) {
246
                    isCompatible.setValue(false);
247
                    throw new VisitCanceledException();
248
                  }
250 249
                }
251
            });
252

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

  
259
        return isCompatible.booleanValue();
256
    } catch (VisitCanceledException ex) {
257
      // Do nothing
258
    } catch (Exception ex) {
259
      LOGGER.warn("Can't calculate if is SQL compatible.", ex);
260 260
    }
261 261

  
262
    @Override
263
    public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
264
        if (this.useSubquery()) {
265
            return false;
262
    return isCompatible.booleanValue();
263
  }
264

  
265
  @Override
266
  public boolean supportOrder(FeatureType type, FeatureQueryOrder order) {
267
    if (this.useSubquery()) {
268
      return false;
269
    }
270
    if (order == null) {
271
      return true;
272
    }
273
    for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
274
      if (member.hasEvaluator()) {
275
        if (!this.supportFilter(type, member.getEvaluator())) {
276
          return false;
266 277
        }
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
      }
278 279
    }
280
    return true;
281
  }
279 282

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

  
288
    protected void initializeResource(JDBCConnectionParameters params) {
291
  protected void initializeResource(JDBCConnectionParameters params) {
289 292
//        Object[] resourceParams = new Object[]{
290 293
//            params.getUrl(),
291 294
//            params.getHost(),
......
306 309
//            this.resource = resource;
307 310
//            this.resource.addConsumer(this);
308 311
//        } catch (InitializeException ex) {
309
//            logger.debug("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
312
//            logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
310 313
//            throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
311 314
//        }
312 315

  
313
    }
316
  }
314 317

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

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

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

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

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

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

  
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
        }
349
  @Override
350
  public void closeConnection(Connection connection) {
351
    if (connection != null) {
352
      LOGGER.trace("Clossing connection " + connection.hashCode());
353
      try {
354
        connection.close();
355
      } catch (Exception ex) {
356
        LOGGER.warn("Can't close connection.", ex);
357
      }
356 358
    }
359
  }
357 360

  
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
        }
361
  @Override
362
  public void closeConnectionQuietly(Connection connection) {
363
    if (connection != null) {
364
      LOGGER.trace("Clossing connection quietly " + connection.hashCode());
365
      try {
366
        connection.close();
367
      } catch (Exception ex) {
368
        LOGGER.warn("Can't close connection.", ex);
369
      }
368 370
    }
371
  }
369 372

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  
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
    }
516
    JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
517
            parameters,
518
            providerServices,
519
            DBHelper.newMetadataContainer(JDBCLibrary.NAME),
520
            this
521
    );
522
    this.initialize(theStore, parameters, theStore);
523
    return theStore;
524
  }
522 525

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

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

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

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

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

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

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

  
569
  @Override
570
  public void processSpecialFunctions(
571
          SQLBuilder sqlbuilder,
572
          FeatureType type,
573
          List<String> extra_column_names) {
574
    replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
575
    replaceExistsFunction(sqlbuilder, type, extra_column_names);
576
  }
577

  
578
  private void replaceExistsFunction(
579
          SQLBuilder sqlbuilder,
580
          FeatureType type,
581
          final List<String> extra_column_names) {
565 582
    
566
    @Override
567
    public void processSpecialFunctions(
568
            SQLBuilder sqlbuilder, 
569
            FeatureType type,
570
            List<String> extra_column_names) {
571
      replaceForeingValueFunction(sqlbuilder, type, extra_column_names);
572
      replaceExistsFunction(sqlbuilder, type, extra_column_names);
583
    //  Si lse encuentra una construccion del tipo:
584
    //    SELECT ... FROM ... WHERE ... EXISTS(list, 'EXISTS_ID') ...
585
    //  se traslada a:
586
    //    SELECT ... EXISTS(list) AS EXISTS_ID FROM ... WHERE ... EXISTS(list) ...
587
    //  Y se a?ade el valor ESISTS_ID a las columnas extra a recuperar de la consulta.
588
    //          
589
    
590
    final SQLBuilder.SelectBuilder select = sqlbuilder.select();
591
    final ExpressionBuilder where = select.where();
592
    if (where == null || where.isEmpty()) {
593
      return;
573 594
    }
574

  
575
    private void replaceExistsFunction(
576
            SQLBuilder sqlbuilder, 
577
            FeatureType type,
578
            List<String> extra_column_names) {
579
      // TODO: replaceExistsFunction
580
    }
581
    
582
    private void replaceForeingValueFunction(
583
            SQLBuilder sqlbuilder, 
584
            FeatureType type,
585
            List<String> extra_column_names) {
586
      try {
587
        // See test SQLBuilderTest->testForeingValue()
588
        final ExpressionBuilder where = sqlbuilder.select().where();
589
        if (where == null || where.isEmpty()) {
590
            return;
595
    final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
596
    where.accept(new ExpressionBuilder.Visitor() {
597
      @Override
598
      public void visit(ExpressionBuilder.Visitable value) {
599
        if (!(value instanceof ExpressionBuilder.Function)) {
600
          return;
591 601
        }
592
        final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
593
        final ExpressionBuilder expbuilder = sqlbuilder.expression();
594

  
595
        final List<String> foreing_value_args;
596
        if( extra_column_names==null ) {
597
          foreing_value_args = new ArrayList<>();
598
        } else {
599
          foreing_value_args = extra_column_names;
602
        ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
603
        if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_EXISTS)) {
604
          return;
600 605
        }
601
        final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
606
        if (function.parameters().size() != 2) {
607
          return;
608
        }
609
        ExpressionBuilder.Value arg0 = function.parameters().get(0);
610
        ExpressionBuilder.Value arg1 = function.parameters().get(1);
611
        if (arg1 == null) {
612
          return;
613
        }
614
        String columnName = (String) ((ExpressionBuilder.Constant) arg1).value();
615
        SQLBuilder.SelectColumnBuilder column = select.column();
616
        column.value(
617
                sqlbuilder.expression().function(FUNCTION_EXISTS, arg0)
618
        );
619
        column.as(columnName);
620
//        
621
//        value_replacements.add(
622
//                new ExpressionBuilder.Value[]{
623
//                  function,
624
//                  sqlbuilder.column(columnName)
625
//                }
626
//        );
627
        if( extra_column_names!=null ) {
628
          extra_column_names.add(columnName);
629
        }
630
      }
631
    }, null);
632
    if (value_replacements.isEmpty()) {
633
      return;
634
    }
635
    // Realizamos los reemplazos calculados previamente (value_replacements).
636
    for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
637
      ExpressionBuilder.Value target = replaceValue[0];
638
      ExpressionBuilder.Value replacement = replaceValue[1];
639
      sqlbuilder.select().replace(target, replacement);
640
    }
641
  }
602 642

  
603
        // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
604
        // el argumento de esta asi como por que tendriamos que sustituirla 
605
        // una vez hechos los left joins que toquen.
606
        where.accept(new ExpressionBuilder.Visitor() {
607
            @Override
608
            public void visit(ExpressionBuilder.Visitable value) {
609
                // Requiere que sea la funcion "FOREING_VALUE con un solo 
610
                // argumento que sea una constante de tipo string.
611
                if (!(value instanceof ExpressionBuilder.Function)) {
612
                    return;
613
                }
614
                ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
615
                if (!StringUtils.equalsIgnoreCase(function.name(), "FOREING_VALUE")) {
616
                    return;
617
                }
618
                if (function.parameters().size() != 1) {
619
                    return;
620
                }
621
                ExpressionBuilder.Value arg = function.parameters().get(0).getValue();
622
                if (!(arg instanceof ExpressionBuilder.Constant)) {
623
                    return;
624
                }
625
                Object arg_value = ((ExpressionBuilder.Constant) arg).value();
626
                if (!(arg_value instanceof CharSequence)) {
627
                    return;
628
                }
629
                String foreing_value_arg = arg_value.toString();
630
                String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
631
                if (foreingNameParts.length != 2) {
632
                    // De momento solo tratamos joins entre dos tablas.
633
                    return;
634
                }
635
                String columnNameLocal = foreingNameParts[0];
636
                String columnNameForeing = foreingNameParts[1];
637
                FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
638
                if (!attr.isForeingKey()) {
639
                    // Uhm... si el argumento no referencia a un campo que es
640
                    // clave ajena no lo procesamos. 
641
                    // ? Deberiamos lanzar un error ?
642
                    return;
643
                }
644
                // Nos guardaremos por que hay que reemplazar la funcion 
645
                // FOREING_VALUE, y su argumento para mas tarde.
646
                ForeingKey foreingKey = attr.getForeingKey();
647
                SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
648
                        .database(table.getDatabase())
649
                        .schema(table.getSchema())
650
                        .name(foreingKey.getTableName());
651
                // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
652
                // que toca de la tabla a la que referencia la clave ajena.
653
                ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
654
                value_replacements.add(
655
                        new ExpressionBuilder.Value[]{
656
                            function,
657
                            function_replacement
658
                        }
659
                );
660
                foreing_value_args.add(foreing_value_arg);
661
            }
662
        }, null);
643
  private void replaceForeingValueFunction(
644
          SQLBuilder sqlbuilder,
645
          FeatureType type,
646
          List<String> extra_column_names) {
647
    try {
648
      // See test SQLBuilderTest->testForeingValue()
649
      final ExpressionBuilder where = sqlbuilder.select().where();
650
      if (where == null || where.isEmpty()) {
651
        return;
652
      }
653
      final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
654
      final ExpressionBuilder expbuilder = sqlbuilder.expression();
663 655

  
664
        // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
665
        // hacer nada.
666
        if (foreing_value_args.isEmpty()) {
656
      final List<String> foreing_value_args;
657
      if (extra_column_names == null) {
658
        foreing_value_args = new ArrayList<>();
659
      } else {
660
        foreing_value_args = extra_column_names;
661
      }
662
      final List<ExpressionBuilder.Value[]> value_replacements = new ArrayList<>();
663

  
664
      // Buscamos las llamadas a la funcion "foreing_value" y nos quedamos
665
      // el argumento de esta asi como por que tendriamos que sustituirla 
666
      // una vez hechos los left joins que toquen.
667
      where.accept(new ExpressionBuilder.Visitor() {
668
        @Override
669
        public void visit(ExpressionBuilder.Visitable value) {
670
          // Requiere que sea la funcion "FOREING_VALUE con un solo 
671
          // argumento que sea una constante de tipo string.
672
          if (!(value instanceof ExpressionBuilder.Function)) {
667 673
            return;
674
          }
675
          ExpressionBuilder.Function function = (ExpressionBuilder.Function) value;
676
          if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_FOREING_VALUE)) {
677
            return;
678
          }
679
          if (function.parameters().size() != 1) {
680
            return;
681
          }
682
          ExpressionBuilder.Value arg = function.parameters().get(0);
683
          if (!(arg instanceof ExpressionBuilder.Constant)) {
684
            return;
685
          }
686
          Object arg_value = ((ExpressionBuilder.Constant) arg).value();
687
          if (!(arg_value instanceof CharSequence)) {
688
            return;
689
          }
690
          String foreing_value_arg = arg_value.toString();
691
          String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]");
692
          if (foreingNameParts.length != 2) {
693
            // De momento solo tratamos joins entre dos tablas.
694
            return;
695
          }
696
          String columnNameLocal = foreingNameParts[0];
697
          String columnNameForeing = foreingNameParts[1];
698
          FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
699
          if (!attr.isForeingKey()) {
700
            // Uhm... si el argumento no referencia a un campo que es
701
            // clave ajena no lo procesamos. 
702
            // ? Deberiamos lanzar un error ?
703
            return;
704
          }
705
          // Nos guardaremos por que hay que reemplazar la funcion 
706
          // FOREING_VALUE, y su argumento para mas tarde.
707
          ForeingKey foreingKey = attr.getForeingKey();
708
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
709
                  .database(table.getDatabase())
710
                  .schema(table.getSchema())
711
                  .name(foreingKey.getTableName());
712
          // Reemplzaremos la funcion FOREING_VALUE, por el acceso al campo
713
          // que toca de la tabla a la que referencia la clave ajena.
714
          ExpressionBuilder.Variable function_replacement = sqlbuilder.column(foreingTable, columnNameForeing);
715
          value_replacements.add(
716
                  new ExpressionBuilder.Value[]{
717
                    function,
718
                    function_replacement
719
                  }
720
          );
721
          foreing_value_args.add(foreing_value_arg);
668 722
        }
723
      }, null);
669 724

  
670
        // Calculamos que referencias de columnas hemos de cambiar para 
671
        // que no aparezcan ambiguedades al hacer el join (nombres de
672
        // columna de una y otra tabla que coincidan).
673
        // Para las columnas que puedan dar conflicto se prepara un reemplazo 
674
        // de estas que tenga el nombre de tabla.
675
        for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
676
            if (variable instanceof SQLBuilderBase.ColumnBase) {
677
                continue;
678
            }
679
            for (String foreingName : foreing_value_args) {
680
                String[] foreingNameParts = foreingName.split("[.]");
681
                if (foreingNameParts.length != 2) {
682
                    continue;
683
                }
684
                String columnNameLocal = foreingNameParts[0];
685
                String columnNameForeing = foreingNameParts[1];
686
                if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)
687
                        || StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
688
                    ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
689
                            table,
690
                            variable.name()
691
                    );
692
                    value_replacements.add(
693
                            new ExpressionBuilder.Value[]{
694
                                variable,
695
                                variable_replacement
696
                            }
697
                    );
698
                }
699
            }
700
        }
725
      // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
726
      // hacer nada.
727
      if (foreing_value_args.isEmpty()) {
728
        return;
729
      }
701 730

  
702
        // Realizamos los reemplazos calculados previamente (value_replacements).
703
        for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
704
            ExpressionBuilder.Value target = replaceValue[0];
705
            ExpressionBuilder.Value replacement = replaceValue[1];
706
            sqlbuilder.select().replace(target, replacement);
731
      // Calculamos que referencias de columnas hemos de cambiar para 
732
      // que no aparezcan ambiguedades al hacer el join (nombres de
733
      // columna de una y otra tabla que coincidan).
734
      // Para las columnas que puedan dar conflicto se prepara un reemplazo 
735
      // de estas que tenga el nombre de tabla.
736
      for (ExpressionBuilder.Variable variable : sqlbuilder.variables()) {
737
        if (variable instanceof SQLBuilderBase.ColumnBase) {
738
          continue;
707 739
        }
708

  
709
        // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
710
        // a los valores referenciados por la funcion FOREING_VALUE.
711
        // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
712
        // como columnas de la SQL para poder acceder a ellos si fuese necesario.
713 740
        for (String foreingName : foreing_value_args) {
714
            String[] foreingNameParts = foreingName.split("[.]");
715
            if (foreingNameParts.length != 2) {
716
                continue;
717
            }
718
            String columnNameLocal = foreingNameParts[0];
719
            String columnNameForeing = foreingNameParts[1];
720
            FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
721
            if (attr.isForeingKey()) {
722
                ForeingKey foreingKey = attr.getForeingKey();
723
                SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
724
                        .database(table.getDatabase())
725
                        .schema(table.getSchema())
726
                        .name(foreingKey.getTableName());
727
                SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
728
                        .database(table.getDatabase())
729
                        .schema(table.getSchema())
730
                        .name(table.getName());
741
          String[] foreingNameParts = foreingName.split("[.]");
742
          if (foreingNameParts.length != 2) {
743
            continue;
744
          }
745
          String columnNameLocal = foreingNameParts[0];
746
          String columnNameForeing = foreingNameParts[1];
747
          if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)
748
                  || StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) {
749
            ExpressionBuilder.Variable variable_replacement = sqlbuilder.column(
750
                    table,
751
                    variable.name()
752
            );
753
            value_replacements.add(
754
                    new ExpressionBuilder.Value[]{
755
                      variable,
756
                      variable_replacement
757
                    }
758
            );
759
          }
760
        }
761
      }
731 762

  
732
                sqlbuilder.select().from()
733
                        .left_join(
734
                                foreingTable,
735
                                expbuilder.eq(
736
                                        sqlbuilder.column(mainTable, columnNameLocal),
737
                                        sqlbuilder.column(foreingTable, foreingKey.getCodeName())
738
                                )
739
                        );
740
                sqlbuilder.select().column().name(foreingTable, columnNameForeing);
741
            }
763
      // Realizamos los reemplazos calculados previamente (value_replacements).
764
      for (ExpressionBuilder.Value[] replaceValue : value_replacements) {
765
        ExpressionBuilder.Value target = replaceValue[0];
766
        ExpressionBuilder.Value replacement = replaceValue[1];
767
        sqlbuilder.select().replace(target, replacement);
768
      }
769

  
770
      // A?adimos a la SQL los "LEFT JOIN" que toca para poder acceder
771
      // a los valores referenciados por la funcion FOREING_VALUE.
772
      // Ademas a?adimos los valores referenciados por la funcion FOREING_VALUE
773
      // como columnas de la SQL para poder acceder a ellos si fuese necesario.
774
      for (String foreingName : foreing_value_args) {
775
        String[] foreingNameParts = foreingName.split("[.]");
776
        if (foreingNameParts.length != 2) {
777
          continue;
742 778
        }
779
        String columnNameLocal = foreingNameParts[0];
780
        String columnNameForeing = foreingNameParts[1];
781
        FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal);
782
        if (attr.isForeingKey()) {
783
          ForeingKey foreingKey = attr.getForeingKey();
784
          SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder()
785
                  .database(table.getDatabase())
786
                  .schema(table.getSchema())
787
                  .name(foreingKey.getTableName());
788
          SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder()
789
                  .database(table.getDatabase())
790
                  .schema(table.getSchema())
791
                  .name(table.getName());
743 792

  
744
      } catch(Throwable th) {
745
        LOGGER.warn("Can't replace FORENG_VALUE function.", th);
746
        throw th;
747
      } finally {
748
        LOGGER.debug("Exist from replaceForeingValueFunction.");
793
          sqlbuilder.select().from()
794
                  .left_join(
795
                          foreingTable,
796
                          expbuilder.eq(
797
                                  sqlbuilder.column(mainTable, columnNameLocal),
798
                                  sqlbuilder.column(foreingTable, foreingKey.getCodeName())
799
                          )
800
                  );
801
          sqlbuilder.select().column().name(foreingTable, columnNameForeing);
802
        }
749 803
      }
804

  
805
    } catch (Throwable th) {
806
      LOGGER.warn("Can't replace FORENG_VALUE function.", th);
807
      throw th;
808
    } finally {
809
      LOGGER.trace("Exit from replaceForeingValueFunction.");
750 810
    }
811
  }
751 812
}

Also available in: Unified diff