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 @ 44632
History | View | Annotate | Download (27.2 KB)
1 | 43020 | jjdelcerro | package org.gvsig.fmap.dal.store.jdbc2.spi; |
---|---|---|---|
2 | |||
3 | 44297 | jjdelcerro | import java.sql.Blob; |
4 | 43020 | jjdelcerro | 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 | 44376 | jjdelcerro | import java.util.ArrayList; |
9 | import java.util.List; |
||
10 | import org.apache.commons.lang3.ArrayUtils; |
||
11 | 43020 | jjdelcerro | import org.apache.commons.lang3.StringUtils; |
12 | 44191 | jjdelcerro | import org.apache.commons.lang3.mutable.MutableBoolean; |
13 | import org.gvsig.expressionevaluator.Code; |
||
14 | 44376 | jjdelcerro | import org.gvsig.expressionevaluator.ExpressionBuilder; |
15 | 44042 | jjdelcerro | 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 | 44191 | jjdelcerro | import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator; |
21 | import org.gvsig.expressionevaluator.ExpressionEvaluatorManager; |
||
22 | import org.gvsig.expressionevaluator.Function; |
||
23 | 44198 | jjdelcerro | import org.gvsig.expressionevaluator.SymbolTable; |
24 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.DataTypes; |
25 | 44376 | jjdelcerro | import org.gvsig.fmap.dal.SQLBuilder; |
26 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.exception.DataException; |
27 | import org.gvsig.fmap.dal.exception.InitializeException; |
||
28 | import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
||
29 | 44191 | jjdelcerro | import org.gvsig.fmap.dal.feature.FeatureQueryOrder; |
30 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.feature.FeatureType; |
31 | 44376 | jjdelcerro | import org.gvsig.fmap.dal.feature.ForeingKey; |
32 | 43020 | jjdelcerro | |
33 | import org.gvsig.fmap.dal.feature.spi.FeatureProvider; |
||
34 | 44376 | jjdelcerro | import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase; |
35 | 43020 | jjdelcerro | 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 | 44191 | jjdelcerro | import org.gvsig.tools.evaluator.Evaluator; |
60 | 43020 | jjdelcerro | import org.gvsig.tools.exception.BaseException; |
61 | 43377 | jjdelcerro | import org.gvsig.tools.exception.NotYetImplemented; |
62 | 44191 | jjdelcerro | import org.gvsig.tools.visitor.VisitCanceledException; |
63 | import org.gvsig.tools.visitor.Visitor; |
||
64 | 43020 | jjdelcerro | import org.slf4j.Logger; |
65 | import org.slf4j.LoggerFactory; |
||
66 | |||
67 | 44191 | jjdelcerro | @SuppressWarnings("UseSpecificCatch") |
68 | 43020 | jjdelcerro | 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 | 44191 | jjdelcerro | private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class); |
75 | 43020 | jjdelcerro | |
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 | 43035 | jjdelcerro | private JDBCConnectionParameters connectionParameters;
|
85 | 43020 | jjdelcerro | |
86 | 44403 | omartinez | private JDBCStoreProvider store;
|
87 | |||
88 | 43020 | jjdelcerro | protected OperationsFactory operationsFactory = null; |
89 | 44403 | omartinez | |
90 | 43606 | jjdelcerro | protected SRSSolver srssolver;
|
91 | 44403 | omartinez | |
92 | 43020 | jjdelcerro | public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
|
93 | this.connectionParameters = connectionParameters;
|
||
94 | 44403 | omartinez | |
95 | 43355 | jjdelcerro | // If a particular treatment is required to convert SRS to the
|
96 | // BBDD format, overwrite JDBCSRSsBase and use it instead of JDBCSRSsDumb.
|
||
97 | 43606 | jjdelcerro | this.srssolver = new SRSSolverDumb(this); |
98 | 43020 | jjdelcerro | } |
99 | |||
100 | protected void initialize( |
||
101 | ResourceConsumer helperClient, |
||
102 | JDBCConnectionParameters connectionParameters, |
||
103 | JDBCStoreProvider store |
||
104 | 44403 | omartinez | ) { |
105 | 43093 | jjdelcerro | this.store = store;
|
106 | 43020 | jjdelcerro | this.helperClient = helperClient;
|
107 | this.connectionParameters = connectionParameters;
|
||
108 | 44403 | omartinez | initializeResource(connectionParameters); |
109 | 43020 | jjdelcerro | } |
110 | 44403 | omartinez | |
111 | 43020 | jjdelcerro | protected String getResourceType() { |
112 | return JDBCResource.NAME;
|
||
113 | } |
||
114 | |||
115 | @Override
|
||
116 | public String getProviderName() { |
||
117 | return JDBCLibrary.NAME;
|
||
118 | } |
||
119 | 44403 | omartinez | |
120 | 43020 | jjdelcerro | @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 | 44191 | jjdelcerro | public boolean allowNestedOperations() { |
135 | return false; |
||
136 | } |
||
137 | 44403 | omartinez | |
138 | 44191 | jjdelcerro | @Override
|
139 | 43020 | jjdelcerro | 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 | 43687 | jjdelcerro | return new JDBCSQLBuilderBase(this); |
147 | 43020 | jjdelcerro | } |
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 | 44403 | omartinez | |
165 | 43020 | jjdelcerro | @Override
|
166 | public String getQuoteForStrings() { |
||
167 | return QUOTE_FOR_USE_IN_STRINGS;
|
||
168 | } |
||
169 | 44191 | jjdelcerro | |
170 | 44376 | jjdelcerro | protected boolean supportCaller(Code.Caller caller) { |
171 | 44403 | omartinez | if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) { |
172 | 44376 | jjdelcerro | return true; |
173 | } |
||
174 | Function function = caller.function(); |
||
175 | 44403 | omartinez | if (function == null) { |
176 | 44376 | jjdelcerro | return false; |
177 | } |
||
178 | 44403 | omartinez | if (!function.isSQLCompatible()) {
|
179 | 44376 | jjdelcerro | return false; |
180 | } |
||
181 | 44403 | omartinez | if (!this.hasSpatialFunctions()) { |
182 | if (StringUtils.equalsIgnoreCase(function.group(), Function.GROUP_OGC)) {
|
||
183 | 44376 | jjdelcerro | return false; |
184 | } |
||
185 | } |
||
186 | return true; |
||
187 | } |
||
188 | 44403 | omartinez | |
189 | 44191 | jjdelcerro | @Override
|
190 | 44198 | jjdelcerro | public boolean supportFilter(final FeatureType type, Evaluator filter) { |
191 | 44191 | jjdelcerro | // 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 | 44198 | jjdelcerro | // - Si se esta usando algun campo calculado en la expresion de filtro.
|
197 | 44191 | jjdelcerro | //
|
198 | // Un proveedor especifico podria sobreescribir el metodo,
|
||
199 | // para hilar mas fino al comprobar si soporta el filtro o no.
|
||
200 | //
|
||
201 | 44403 | omartinez | |
202 | 44191 | jjdelcerro | 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 | 44198 | jjdelcerro | // Tambien comprobaremos que el filtro no usa ningun campo calculado.
|
222 | 44191 | jjdelcerro | final MutableBoolean isCompatible = new MutableBoolean(true); |
223 | ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager(); |
||
224 | Code code = manager.compile(sql); |
||
225 | 44198 | jjdelcerro | SymbolTable symbolTable = manager.createSymbolTable(); |
226 | code.link(symbolTable); |
||
227 | 44191 | jjdelcerro | 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 | 44403 | omartinez | switch (code.code()) {
|
233 | 44198 | jjdelcerro | case Code.CALLER:
|
234 | Code.Caller caller = (Code.Caller) code; |
||
235 | 44403 | omartinez | if (!supportCaller(caller)) {
|
236 | 44191 | jjdelcerro | isCompatible.setValue(false);
|
237 | throw new VisitCanceledException(); |
||
238 | } |
||
239 | 44198 | jjdelcerro | break;
|
240 | 44403 | omartinez | |
241 | 44198 | jjdelcerro | case Code.IDENTIFIER:
|
242 | Code.Identifier identifier = (Code.Identifier) code; |
||
243 | 44403 | omartinez | if (type != null) { |
244 | 44198 | jjdelcerro | FeatureAttributeDescriptor attrdesc = type.getAttributeDescriptor(identifier.name()); |
245 | 44403 | omartinez | if (attrdesc != null) { |
246 | if (attrdesc.isComputed()) {
|
||
247 | 44198 | jjdelcerro | isCompatible.setValue(false);
|
248 | throw new VisitCanceledException(); |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | break;
|
||
253 | 44191 | jjdelcerro | } |
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 | 44403 | omartinez | |
263 | 44191 | jjdelcerro | return isCompatible.booleanValue();
|
264 | } |
||
265 | 44403 | omartinez | |
266 | 43020 | jjdelcerro | @Override
|
267 | 44198 | jjdelcerro | public boolean supportOrder(FeatureType type, FeatureQueryOrder order) { |
268 | 44191 | jjdelcerro | if (this.useSubquery()) { |
269 | return false; |
||
270 | } |
||
271 | if (order == null) { |
||
272 | return true; |
||
273 | } |
||
274 | 44403 | omartinez | for (FeatureQueryOrder.FeatureQueryOrderMember member : order.members()) {
|
275 | 44191 | jjdelcerro | if (member.hasEvaluator()) {
|
276 | 44403 | omartinez | if (!this.supportFilter(type, member.getEvaluator())) { |
277 | 44191 | jjdelcerro | return false; |
278 | } |
||
279 | 44403 | omartinez | } |
280 | 44191 | jjdelcerro | } |
281 | return true; |
||
282 | } |
||
283 | 44403 | omartinez | |
284 | 44191 | jjdelcerro | @Override
|
285 | 43020 | jjdelcerro | public OperationsFactory getOperations() {
|
286 | 44403 | omartinez | if (this.operationsFactory == null) { |
287 | 43020 | jjdelcerro | this.operationsFactory = new OperationsFactoryBase(this); |
288 | } |
||
289 | return operationsFactory;
|
||
290 | } |
||
291 | 44403 | omartinez | |
292 | 43020 | jjdelcerro | 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 | 44403 | omartinez | |
324 | 43020 | jjdelcerro | @Override
|
325 | public JDBCResource getResource() {
|
||
326 | return null; |
||
327 | // return this.resource;
|
||
328 | } |
||
329 | |||
330 | @Override
|
||
331 | public Connection getConnection() throws AccessResourceException { |
||
332 | 43377 | jjdelcerro | throw new NotYetImplemented(); |
333 | 43020 | jjdelcerro | } |
334 | |||
335 | @Override
|
||
336 | 43377 | jjdelcerro | public Connection getConnectionWritable() throws AccessResourceException { |
337 | return this.getConnection(); |
||
338 | } |
||
339 | |||
340 | @Override
|
||
341 | 43035 | jjdelcerro | public String getConnectionURL() { |
342 | return null; |
||
343 | } |
||
344 | |||
345 | 43358 | jjdelcerro | @Override
|
346 | 43035 | jjdelcerro | public JDBCConnectionParameters getConnectionParameters() {
|
347 | return connectionParameters;
|
||
348 | } |
||
349 | |||
350 | @Override
|
||
351 | 43020 | jjdelcerro | public void closeConnection(Connection connection) { |
352 | 44403 | omartinez | if (connection != null) { |
353 | LOGGER.debug("Clossing connection " + connection.hashCode());
|
||
354 | 43377 | jjdelcerro | try {
|
355 | connection.close(); |
||
356 | 44403 | omartinez | } catch (Exception ex) { |
357 | 44191 | jjdelcerro | LOGGER.warn("Can't close connection.", ex);
|
358 | 43377 | jjdelcerro | } |
359 | 44403 | omartinez | } |
360 | 43377 | jjdelcerro | } |
361 | 44191 | jjdelcerro | |
362 | 44198 | jjdelcerro | @Override
|
363 | 44191 | jjdelcerro | public void closeConnectionQuietly(Connection connection) { |
364 | 44403 | omartinez | if (connection != null) { |
365 | LOGGER.debug("Clossing connection quietly " + connection.hashCode());
|
||
366 | 44191 | jjdelcerro | try {
|
367 | connection.close(); |
||
368 | 44403 | omartinez | } catch (Exception ex) { |
369 | 44191 | jjdelcerro | LOGGER.warn("Can't close connection.", ex);
|
370 | } |
||
371 | 44403 | omartinez | } |
372 | 44191 | jjdelcerro | } |
373 | 44403 | omartinez | |
374 | 43020 | jjdelcerro | @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 | 44376 | jjdelcerro | fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames()); |
416 | 43020 | jjdelcerro | } |
417 | |||
418 | @Override
|
||
419 | 44376 | jjdelcerro | public void fetchFeature(FeatureProvider feature, ResultSet rs, FeatureAttributeDescriptor[] columns, String[] extraValueNames) throws DataException { |
420 | 43420 | jjdelcerro | Object value;
|
421 | 43020 | jjdelcerro | try {
|
422 | 44376 | jjdelcerro | int rsIndex = 1; |
423 | for (FeatureAttributeDescriptor column : columns) {
|
||
424 | 43358 | jjdelcerro | switch (column.getType()) {
|
425 | 43020 | jjdelcerro | case DataTypes.GEOMETRY:
|
426 | 44376 | jjdelcerro | value = this.getGeometryFromColumn(rs, rsIndex++);
|
427 | 43020 | jjdelcerro | break;
|
428 | default:
|
||
429 | 44376 | jjdelcerro | value = rs.getObject(rsIndex++); |
430 | 44403 | omartinez | if (value instanceof Blob) { |
431 | Blob blob = (Blob) value; |
||
432 | 44297 | jjdelcerro | value = blob.getBytes(0, (int) blob.length()); |
433 | blob.free(); |
||
434 | } |
||
435 | 43020 | jjdelcerro | } |
436 | 43358 | jjdelcerro | feature.set(column.getIndex(), value); |
437 | 43020 | jjdelcerro | } |
438 | 44403 | omartinez | if (ArrayUtils.isNotEmpty(extraValueNames)) {
|
439 | 44376 | jjdelcerro | feature.setExtraValueNames(extraValueNames); |
440 | for (int index = 0; index < extraValueNames.length; index++) { |
||
441 | value = rs.getObject(rsIndex++); |
||
442 | 44403 | omartinez | if (value instanceof Blob) { |
443 | Blob blob = (Blob) value; |
||
444 | 44376 | jjdelcerro | value = blob.getBytes(0, (int) blob.length()); |
445 | blob.free(); |
||
446 | } |
||
447 | feature.setExtraValue(index, value); |
||
448 | } |
||
449 | } |
||
450 | 43020 | jjdelcerro | } 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 | 43358 | jjdelcerro | case NATIVE:
|
466 | 43020 | jjdelcerro | 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 | 44403 | omartinez | |
498 | 43020 | jjdelcerro | @Override
|
499 | public boolean useSubquery() { |
||
500 | 44403 | omartinez | if (this.store == null) { |
501 | 43020 | jjdelcerro | return false; |
502 | } |
||
503 | return !StringUtils.isEmpty(this.store.getParameters().getSQL()); |
||
504 | 44403 | omartinez | } |
505 | |||
506 | 43355 | jjdelcerro | @Override
|
507 | 43606 | jjdelcerro | public SRSSolver getSRSSolver() {
|
508 | return this.srssolver; |
||
509 | } |
||
510 | 44403 | omartinez | |
511 | 43606 | jjdelcerro | @Override
|
512 | 43020 | jjdelcerro | public JDBCStoreProvider createProvider(
|
513 | 44403 | omartinez | JDBCStoreParameters parameters, |
514 | 43020 | jjdelcerro | DataStoreProviderServices providerServices |
515 | 44403 | omartinez | ) throws InitializeException {
|
516 | |||
517 | 44191 | jjdelcerro | JDBCStoreProviderBase theStore = new JDBCStoreProviderBase(
|
518 | 44403 | omartinez | parameters, |
519 | providerServices, |
||
520 | 43020 | jjdelcerro | DBHelper.newMetadataContainer(JDBCLibrary.NAME), |
521 | this
|
||
522 | ); |
||
523 | 44191 | jjdelcerro | this.initialize(theStore, parameters, theStore);
|
524 | return theStore;
|
||
525 | 43020 | jjdelcerro | } |
526 | |||
527 | @Override
|
||
528 | public JDBCServerExplorer createServerExplorer(
|
||
529 | 44403 | omartinez | JDBCServerExplorerParameters parameters, |
530 | 43020 | jjdelcerro | DataServerExplorerProviderServices providerServices |
531 | 44403 | omartinez | ) throws InitializeException {
|
532 | |||
533 | 43020 | jjdelcerro | JDBCServerExplorer explorer = new JDBCServerExplorerBase(
|
534 | 44403 | omartinez | parameters, |
535 | providerServices, |
||
536 | 43020 | jjdelcerro | 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 | 43358 | jjdelcerro | @Override
|
553 | 43020 | jjdelcerro | public JDBCServerExplorerParameters createServerExplorerParameters() {
|
554 | return new JDBCServerExplorerParameters(); |
||
555 | } |
||
556 | |||
557 | @Override
|
||
558 | public String getSourceId(JDBCStoreParameters parameters) { |
||
559 | 44403 | omartinez | return parameters.getHost() + ":" |
560 | + parameters.getDBName() + ":"
|
||
561 | + parameters.getSchema() + ":"
|
||
562 | + parameters.tableID(); |
||
563 | 43020 | jjdelcerro | } |
564 | 44198 | jjdelcerro | |
565 | @Override
|
||
566 | public boolean isThreadSafe() { |
||
567 | return true; |
||
568 | } |
||
569 | 44403 | omartinez | |
570 | 44376 | jjdelcerro | @Override
|
571 | public String[] replaceForeingValueFunction(SQLBuilder sqlbuilder, FeatureType type) { |
||
572 | // See test SQLBuilderTest->testForeingValue()
|
||
573 | final ExpressionBuilder where = sqlbuilder.select().where();
|
||
574 | 44403 | omartinez | if (where == null || where.isEmpty()) { |
575 | 44376 | jjdelcerro | return null; |
576 | } |
||
577 | final SQLBuilder.TableNameBuilder table = sqlbuilder.select().from().table();
|
||
578 | final ExpressionBuilder expbuilder = sqlbuilder.expression();
|
||
579 | 44403 | omartinez | |
580 | 44376 | jjdelcerro | 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 | 44403 | omartinez | if (!(value instanceof ExpressionBuilder.Function)) { |
592 | 44376 | jjdelcerro | return;
|
593 | } |
||
594 | ExpressionBuilder.Function function = (ExpressionBuilder.Function) value; |
||
595 | 44403 | omartinez | if (!StringUtils.equalsIgnoreCase(function.name(), "FOREING_VALUE")) { |
596 | 44376 | jjdelcerro | return;
|
597 | } |
||
598 | 44403 | omartinez | if (function.parameters().size() != 1) { |
599 | 44376 | jjdelcerro | return;
|
600 | 44403 | omartinez | } |
601 | 44376 | jjdelcerro | ExpressionBuilder.Value arg = function.parameters().get(0);
|
602 | 44403 | omartinez | if (!(arg instanceof ExpressionBuilder.Constant)) { |
603 | 44376 | jjdelcerro | return;
|
604 | } |
||
605 | Object arg_value = ((ExpressionBuilder.Constant) arg).value();
|
||
606 | 44403 | omartinez | if (!(arg_value instanceof CharSequence)) { |
607 | 44376 | jjdelcerro | return;
|
608 | } |
||
609 | String foreing_value_arg = arg_value.toString();
|
||
610 | String[] foreingNameParts = StringUtils.split(foreing_value_arg, "[.]"); |
||
611 | 44403 | omartinez | if (foreingNameParts.length != 2) { |
612 | 44376 | jjdelcerro | // 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 | 44403 | omartinez | if (!attr.isForeingKey()) {
|
619 | 44376 | jjdelcerro | // 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 | 44403 | omartinez | .schema(table.getSchema()) |
630 | 44376 | jjdelcerro | .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 | 44403 | omartinez | new ExpressionBuilder.Value[]{ |
636 | function, |
||
637 | function_replacement |
||
638 | } |
||
639 | 44376 | jjdelcerro | ); |
640 | foreing_value_args.add(foreing_value_arg); |
||
641 | } |
||
642 | }, null);
|
||
643 | 44403 | omartinez | |
644 | 44376 | jjdelcerro | // Si no habia ningun llamada a la funcion FOREING_VALUE, no hay que
|
645 | // hacer nada.
|
||
646 | 44403 | omartinez | if (foreing_value_args.isEmpty()) {
|
647 | 44376 | jjdelcerro | 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 | 44403 | omartinez | if (variable instanceof SQLBuilderBase.ColumnBase) { |
657 | 44376 | jjdelcerro | continue;
|
658 | } |
||
659 | for (String foreingName : foreing_value_args) { |
||
660 | String[] foreingNameParts = foreingName.split("[.]"); |
||
661 | 44403 | omartinez | if (foreingNameParts.length != 2) { |
662 | 44376 | jjdelcerro | continue;
|
663 | } |
||
664 | String columnNameLocal = foreingNameParts[0]; |
||
665 | String columnNameForeing = foreingNameParts[1]; |
||
666 | 44403 | omartinez | if (StringUtils.equalsIgnoreCase(variable.name(), columnNameForeing)
|
667 | || StringUtils.equalsIgnoreCase(variable.name(), columnNameLocal)) { |
||
668 | ExpressionBuilder.Variable variable_replacement = sqlbuilder.column( |
||
669 | table, |
||
670 | variable.name() |
||
671 | ); |
||
672 | value_replacements.add( |
||
673 | new ExpressionBuilder.Value[]{ |
||
674 | variable, |
||
675 | variable_replacement |
||
676 | } |
||
677 | ); |
||
678 | 44376 | jjdelcerro | } |
679 | } |
||
680 | } |
||
681 | 44403 | omartinez | |
682 | 44376 | jjdelcerro | // 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 | 44403 | omartinez | sqlbuilder.select().replace(target, replacement); |
687 | 44376 | jjdelcerro | } |
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 | 44403 | omartinez | if (foreingNameParts.length != 2) { |
696 | 44376 | jjdelcerro | continue;
|
697 | } |
||
698 | String columnNameLocal = foreingNameParts[0]; |
||
699 | String columnNameForeing = foreingNameParts[1]; |
||
700 | FeatureAttributeDescriptor attr = type.getAttributeDescriptor(columnNameLocal); |
||
701 | 44403 | omartinez | if (attr.isForeingKey()) {
|
702 | 44376 | jjdelcerro | ForeingKey foreingKey = attr.getForeingKey(); |
703 | SQLBuilder.TableNameBuilder foreingTable = sqlbuilder.createTableNameBuilder() |
||
704 | .database(table.getDatabase()) |
||
705 | 44403 | omartinez | .schema(table.getSchema()) |
706 | 44376 | jjdelcerro | .name(foreingKey.getTableName()); |
707 | SQLBuilder.TableNameBuilder mainTable = sqlbuilder.createTableNameBuilder() |
||
708 | .database(table.getDatabase()) |
||
709 | 44403 | omartinez | .schema(table.getSchema()) |
710 | 44376 | jjdelcerro | .name(table.getName()); |
711 | |||
712 | sqlbuilder.select().from() |
||
713 | 44403 | omartinez | .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 | 44376 | jjdelcerro | } |
722 | } |
||
723 | |||
724 | return foreing_value_args.toArray(new String[foreing_value_args.size()]); |
||
725 | } |
||
726 | 43020 | jjdelcerro | } |