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 @ 44750
History | View | Annotate | Download (27.4 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 | 44191 | jjdelcerro | import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator; |
16 | import org.gvsig.expressionevaluator.ExpressionEvaluatorManager; |
||
17 | import org.gvsig.expressionevaluator.Function; |
||
18 | 44644 | jjdelcerro | import org.gvsig.expressionevaluator.GeometryExpressionBuilderHelper.GeometrySupportType; |
19 | 44198 | jjdelcerro | import org.gvsig.expressionevaluator.SymbolTable; |
20 | 44750 | jjdelcerro | 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; |
||
23 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.DataTypes; |
24 | 44376 | jjdelcerro | import org.gvsig.fmap.dal.SQLBuilder; |
25 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.exception.DataException; |
26 | import org.gvsig.fmap.dal.exception.InitializeException; |
||
27 | import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
||
28 | 44191 | jjdelcerro | import org.gvsig.fmap.dal.feature.FeatureQueryOrder; |
29 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.feature.FeatureType; |
30 | 44376 | jjdelcerro | import org.gvsig.fmap.dal.feature.ForeingKey; |
31 | 43020 | jjdelcerro | |
32 | import org.gvsig.fmap.dal.feature.spi.FeatureProvider; |
||
33 | 44376 | jjdelcerro | import org.gvsig.fmap.dal.feature.spi.SQLBuilderBase; |
34 | 43020 | jjdelcerro | import org.gvsig.fmap.dal.resource.exception.AccessResourceException; |
35 | import org.gvsig.fmap.dal.resource.spi.ResourceConsumer; |
||
36 | import org.gvsig.fmap.dal.resource.spi.ResourceProvider; |
||
37 | import org.gvsig.fmap.dal.spi.DataServerExplorerProviderServices; |
||
38 | import org.gvsig.fmap.dal.spi.DataStoreProviderServices; |
||
39 | import org.gvsig.fmap.dal.store.db.DBHelper; |
||
40 | import org.gvsig.fmap.dal.store.jdbc.JDBCConnectionParameters; |
||
41 | import org.gvsig.fmap.dal.store.jdbc.JDBCLibrary; |
||
42 | import org.gvsig.fmap.dal.store.jdbc.JDBCNewStoreParameters; |
||
43 | import org.gvsig.fmap.dal.store.jdbc.JDBCResource; |
||
44 | import org.gvsig.fmap.dal.store.jdbc.JDBCServerExplorerParameters; |
||
45 | import org.gvsig.fmap.dal.store.jdbc.JDBCStoreParameters; |
||
46 | import org.gvsig.fmap.dal.store.jdbc.exception.JDBCCantFetchValueException; |
||
47 | import org.gvsig.fmap.dal.store.jdbc2.JDBCHelper; |
||
48 | import org.gvsig.fmap.dal.store.jdbc2.JDBCServerExplorer; |
||
49 | import org.gvsig.fmap.dal.store.jdbc2.JDBCStoreProvider; |
||
50 | import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils; |
||
51 | import org.gvsig.fmap.dal.store.jdbc2.OperationsFactory; |
||
52 | import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler; |
||
53 | import org.gvsig.fmap.dal.store.jdbc2.ResulSetControler.ResultSetEntry; |
||
54 | import org.gvsig.fmap.geom.Geometry; |
||
55 | import org.gvsig.fmap.geom.GeometryLocator; |
||
56 | import org.gvsig.fmap.geom.GeometryManager; |
||
57 | import org.gvsig.tools.dispose.impl.AbstractDisposable; |
||
58 | 44191 | jjdelcerro | import org.gvsig.tools.evaluator.Evaluator; |
59 | 43020 | jjdelcerro | import org.gvsig.tools.exception.BaseException; |
60 | 43377 | jjdelcerro | import org.gvsig.tools.exception.NotYetImplemented; |
61 | 44191 | jjdelcerro | import org.gvsig.tools.visitor.VisitCanceledException; |
62 | import org.gvsig.tools.visitor.Visitor; |
||
63 | 43020 | jjdelcerro | import org.slf4j.Logger; |
64 | import org.slf4j.LoggerFactory; |
||
65 | |||
66 | 44191 | jjdelcerro | @SuppressWarnings("UseSpecificCatch") |
67 | 43020 | jjdelcerro | public class JDBCHelperBase extends AbstractDisposable implements ResourceConsumer, JDBCHelper { |
68 | |||
69 | 44750 | jjdelcerro | 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 = "'"; |
||
72 | 43020 | jjdelcerro | |
73 | 44750 | jjdelcerro | private static final Logger LOGGER = LoggerFactory.getLogger(JDBCHelperBase.class); |
74 | 43020 | jjdelcerro | |
75 | 44750 | jjdelcerro | private ResulSetControler resulSetControler = null; |
76 | 43020 | jjdelcerro | |
77 | 44750 | jjdelcerro | // Quien ha creado este helper.
|
78 | // Se le reenviaran las notificaciones del recurso asociado al helper.
|
||
79 | private ResourceConsumer helperClient = null; |
||
80 | 43020 | jjdelcerro | |
81 | 44750 | jjdelcerro | private GeometryManager geometryManager = null; |
82 | 43020 | jjdelcerro | |
83 | 44750 | jjdelcerro | private JDBCConnectionParameters connectionParameters;
|
84 | 43020 | jjdelcerro | |
85 | 44750 | jjdelcerro | private JDBCStoreProvider store;
|
86 | 44403 | omartinez | |
87 | 44750 | jjdelcerro | protected OperationsFactory operationsFactory = null; |
88 | 44403 | omartinez | |
89 | 44750 | jjdelcerro | protected SRSSolver srssolver;
|
90 | 44403 | omartinez | |
91 | 44750 | jjdelcerro | public JDBCHelperBase(JDBCConnectionParameters connectionParameters) {
|
92 | this.connectionParameters = connectionParameters;
|
||
93 | 44403 | omartinez | |
94 | 44750 | jjdelcerro | // 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 | } |
||
98 | 43020 | jjdelcerro | |
99 | 44750 | jjdelcerro | 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 | } |
||
109 | 44403 | omartinez | |
110 | 44750 | jjdelcerro | protected String getResourceType() { |
111 | return JDBCResource.NAME;
|
||
112 | } |
||
113 | 43020 | jjdelcerro | |
114 | 44750 | jjdelcerro | @Override
|
115 | public String getProviderName() { |
||
116 | return JDBCLibrary.NAME;
|
||
117 | } |
||
118 | 44403 | omartinez | |
119 | 44750 | jjdelcerro | @Override
|
120 | public GeometrySupportType getGeometrySupportType() {
|
||
121 | // El proveedor generico de JDBC guadara las geoemtrias como WKT
|
||
122 | return GeometrySupportType.WKT;
|
||
123 | } |
||
124 | 43020 | jjdelcerro | |
125 | 44750 | jjdelcerro | @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 | } |
||
131 | 43020 | jjdelcerro | |
132 | 44750 | jjdelcerro | @Override
|
133 | public boolean allowNestedOperations() { |
||
134 | return false; |
||
135 | } |
||
136 | 44403 | omartinez | |
137 | 44750 | jjdelcerro | @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 | } |
||
142 | 43020 | jjdelcerro | |
143 | 44750 | jjdelcerro | @Override
|
144 | public JDBCSQLBuilderBase createSQLBuilder() {
|
||
145 | return new JDBCSQLBuilderBase(this); |
||
146 | } |
||
147 | 43020 | jjdelcerro | |
148 | 44750 | jjdelcerro | @Override
|
149 | public String getQuoteForIdentifiers() { |
||
150 | return QUOTE_FOR_USE_IN_IDENTIFIERS;
|
||
151 | } |
||
152 | 43020 | jjdelcerro | |
153 | 44750 | jjdelcerro | @Override
|
154 | public boolean allowAutomaticValues() { |
||
155 | return ALLOW_AUTOMATIC_VALUES;
|
||
156 | } |
||
157 | 43020 | jjdelcerro | |
158 | 44750 | jjdelcerro | @Override
|
159 | public boolean supportOffsetInSelect() { |
||
160 | // Asumimos que la BBDD soporta OFFSET
|
||
161 | return true; |
||
162 | } |
||
163 | 44403 | omartinez | |
164 | 44750 | jjdelcerro | @Override
|
165 | public String getQuoteForStrings() { |
||
166 | return QUOTE_FOR_USE_IN_STRINGS;
|
||
167 | } |
||
168 | 44191 | jjdelcerro | |
169 | 44750 | jjdelcerro | protected boolean supportCaller(Code.Caller caller) { |
170 | if (StringUtils.equalsIgnoreCase(caller.name(), "FOREING_VALUE")) { |
||
171 | return true; |
||
172 | 44376 | jjdelcerro | } |
173 | 44750 | jjdelcerro | 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 | } |
||
187 | 44403 | omartinez | |
188 | 44750 | jjdelcerro | @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 | //
|
||
200 | 44403 | omartinez | |
201 | 44750 | jjdelcerro | 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 | } |
||
216 | 44191 | jjdelcerro | |
217 | 44750 | jjdelcerro | // 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;
|
||
239 | 44403 | omartinez | |
240 | 44750 | jjdelcerro | 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 | } |
||
249 | 44191 | jjdelcerro | } |
250 | 44750 | jjdelcerro | } |
251 | break;
|
||
252 | } |
||
253 | 44191 | jjdelcerro | } |
254 | 44750 | jjdelcerro | }); |
255 | 44403 | omartinez | |
256 | 44750 | jjdelcerro | } catch (VisitCanceledException ex) {
|
257 | // Do nothing
|
||
258 | } catch (Exception ex) { |
||
259 | LOGGER.warn("Can't calculate if is SQL compatible.", ex);
|
||
260 | 44191 | jjdelcerro | } |
261 | 44403 | omartinez | |
262 | 44750 | jjdelcerro | 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; |
||
277 | 44191 | jjdelcerro | } |
278 | 44750 | jjdelcerro | } |
279 | 44191 | jjdelcerro | } |
280 | 44750 | jjdelcerro | return true; |
281 | } |
||
282 | 44403 | omartinez | |
283 | 44750 | jjdelcerro | @Override
|
284 | public OperationsFactory getOperations() {
|
||
285 | if (this.operationsFactory == null) { |
||
286 | this.operationsFactory = new OperationsFactoryBase(this); |
||
287 | 43020 | jjdelcerro | } |
288 | 44750 | jjdelcerro | return operationsFactory;
|
289 | } |
||
290 | 44403 | omartinez | |
291 | 44750 | jjdelcerro | protected void initializeResource(JDBCConnectionParameters params) { |
292 | 43020 | jjdelcerro | // Object[] resourceParams = new Object[]{
|
293 | // params.getUrl(),
|
||
294 | // params.getHost(),
|
||
295 | // params.getPort(),
|
||
296 | // params.getDBName(),
|
||
297 | // params.getUser(),
|
||
298 | // params.getPassword(),
|
||
299 | // params.getJDBCDriverClassName()
|
||
300 | // };
|
||
301 | //
|
||
302 | // try {
|
||
303 | // ResourceManagerProviderServices manager
|
||
304 | // = (ResourceManagerProviderServices) DALLocator.getResourceManager();
|
||
305 | // JDBCResource resource = (JDBCResource) manager.createAddResource(
|
||
306 | // this.getResourceType(),
|
||
307 | // resourceParams
|
||
308 | // );
|
||
309 | // this.resource = resource;
|
||
310 | // this.resource.addConsumer(this);
|
||
311 | // } catch (InitializeException ex) {
|
||
312 | 44750 | jjdelcerro | // logger.trace("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
|
313 | 43020 | jjdelcerro | // throw new RuntimeException("Can't initialize resource (" + ArrayUtils.toString(resourceParams) + ").", ex);
|
314 | // }
|
||
315 | |||
316 | 44750 | jjdelcerro | } |
317 | 43020 | jjdelcerro | |
318 | 44750 | jjdelcerro | @Override
|
319 | public String getSourceId() { |
||
320 | return this.store.getSourceId(); |
||
321 | } |
||
322 | 44403 | omartinez | |
323 | 44750 | jjdelcerro | @Override
|
324 | public JDBCResource getResource() {
|
||
325 | return null; |
||
326 | 43020 | jjdelcerro | // return this.resource;
|
327 | 44750 | jjdelcerro | } |
328 | 43020 | jjdelcerro | |
329 | 44750 | jjdelcerro | @Override
|
330 | public Connection getConnection() throws AccessResourceException { |
||
331 | throw new NotYetImplemented(); |
||
332 | } |
||
333 | 43020 | jjdelcerro | |
334 | 44750 | jjdelcerro | @Override
|
335 | public Connection getConnectionWritable() throws AccessResourceException { |
||
336 | return this.getConnection(); |
||
337 | } |
||
338 | 43377 | jjdelcerro | |
339 | 44750 | jjdelcerro | @Override
|
340 | public String getConnectionURL() { |
||
341 | return null; |
||
342 | } |
||
343 | 43035 | jjdelcerro | |
344 | 44750 | jjdelcerro | @Override
|
345 | public JDBCConnectionParameters getConnectionParameters() {
|
||
346 | return connectionParameters;
|
||
347 | } |
||
348 | 43035 | jjdelcerro | |
349 | 44750 | jjdelcerro | @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 | } |
||
358 | 43377 | jjdelcerro | } |
359 | 44750 | jjdelcerro | } |
360 | 44191 | jjdelcerro | |
361 | 44750 | jjdelcerro | @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 | } |
||
370 | 44191 | jjdelcerro | } |
371 | 44750 | jjdelcerro | } |
372 | 44403 | omartinez | |
373 | 44750 | jjdelcerro | @Override
|
374 | protected void doDispose() throws BaseException { |
||
375 | JDBCUtils.closeQuietly(this);
|
||
376 | } |
||
377 | 43020 | jjdelcerro | |
378 | 44750 | jjdelcerro | @Override
|
379 | public void close() throws Exception { |
||
380 | 43020 | jjdelcerro | // this.resource.removeConsumer(this);
|
381 | 44750 | jjdelcerro | JDBCUtils.closeQuietly(this.resulSetControler);
|
382 | 43020 | jjdelcerro | // this.resource = null;
|
383 | 44750 | jjdelcerro | this.resulSetControler = null; |
384 | } |
||
385 | 43020 | jjdelcerro | |
386 | 44750 | jjdelcerro | @Override
|
387 | public boolean closeResourceRequested(ResourceProvider resource) { |
||
388 | return this.helperClient.closeResourceRequested(resource); |
||
389 | } |
||
390 | 43020 | jjdelcerro | |
391 | 44750 | jjdelcerro | @Override
|
392 | public void resourceChanged(ResourceProvider resource) { |
||
393 | this.helperClient.resourceChanged(resource);
|
||
394 | } |
||
395 | 43020 | jjdelcerro | |
396 | 44750 | jjdelcerro | @Override
|
397 | public GeometryManager getGeometryManager() {
|
||
398 | if (this.geometryManager == null) { |
||
399 | this.geometryManager = GeometryLocator.getGeometryManager();
|
||
400 | 43020 | jjdelcerro | } |
401 | 44750 | jjdelcerro | return this.geometryManager; |
402 | } |
||
403 | 43020 | jjdelcerro | |
404 | 44750 | jjdelcerro | @Override
|
405 | public ResulSetControler getResulSetControler() {
|
||
406 | if (this.resulSetControler == null) { |
||
407 | this.resulSetControler = new ResulSetControlerBase(this); |
||
408 | 43020 | jjdelcerro | } |
409 | 44750 | jjdelcerro | return this.resulSetControler; |
410 | } |
||
411 | 43020 | jjdelcerro | |
412 | 44750 | jjdelcerro | @Override
|
413 | public void fetchFeature(FeatureProvider feature, ResultSetEntry rs) throws DataException { |
||
414 | fetchFeature(feature, rs.get(), rs.getColumns(), rs.getExtraValueNames()); |
||
415 | } |
||
416 | 43020 | jjdelcerro | |
417 | 44750 | jjdelcerro | @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 | 43020 | jjdelcerro | } |
434 | } |
||
435 | 44750 | jjdelcerro | 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); |
||
451 | 43020 | jjdelcerro | } |
452 | 44750 | jjdelcerro | } |
453 | 43020 | jjdelcerro | |
454 | 44750 | jjdelcerro | @Override
|
455 | public Geometry getGeometryFromColumn(ResultSetEntry rs, int index) throws DataException { |
||
456 | return getGeometryFromColumn(rs.get(), index);
|
||
457 | } |
||
458 | 43020 | jjdelcerro | |
459 | 44750 | jjdelcerro | @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); |
||
471 | 43020 | jjdelcerro | |
472 | 44750 | jjdelcerro | 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); |
||
485 | 43020 | jjdelcerro | |
486 | 44750 | jjdelcerro | } |
487 | } catch (Exception ex) { |
||
488 | throw new JDBCCantFetchValueException(ex); |
||
489 | 43020 | jjdelcerro | } |
490 | 44750 | jjdelcerro | } |
491 | 43020 | jjdelcerro | |
492 | 44750 | jjdelcerro | @Override
|
493 | public FeatureProvider createFeature(FeatureType featureType) throws DataException { |
||
494 | return this.store.getStoreServices().createDefaultFeatureProvider(featureType); |
||
495 | } |
||
496 | 44403 | omartinez | |
497 | 44750 | jjdelcerro | @Override
|
498 | public boolean useSubquery() { |
||
499 | if (this.store == null) { |
||
500 | return false; |
||
501 | 44403 | omartinez | } |
502 | 44750 | jjdelcerro | return !StringUtils.isEmpty(this.store.getParameters().getSQL()); |
503 | } |
||
504 | 44403 | omartinez | |
505 | 44750 | jjdelcerro | @Override
|
506 | public SRSSolver getSRSSolver() {
|
||
507 | return this.srssolver; |
||
508 | } |
||
509 | 44403 | omartinez | |
510 | 44750 | jjdelcerro | @Override
|
511 | public JDBCStoreProvider createProvider(
|
||
512 | JDBCStoreParameters parameters, |
||
513 | DataStoreProviderServices providerServices |
||
514 | ) throws InitializeException {
|
||
515 | 44403 | omartinez | |
516 | 44750 | jjdelcerro | 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 | } |
||
525 | 43020 | jjdelcerro | |
526 | 44750 | jjdelcerro | @Override
|
527 | public JDBCServerExplorer createServerExplorer(
|
||
528 | JDBCServerExplorerParameters parameters, |
||
529 | DataServerExplorerProviderServices providerServices |
||
530 | ) throws InitializeException {
|
||
531 | 44403 | omartinez | |
532 | 44750 | jjdelcerro | JDBCServerExplorer explorer = new JDBCServerExplorerBase(
|
533 | parameters, |
||
534 | providerServices, |
||
535 | this
|
||
536 | ); |
||
537 | this.initialize(explorer, parameters, null); |
||
538 | return explorer;
|
||
539 | } |
||
540 | 43020 | jjdelcerro | |
541 | 44750 | jjdelcerro | @Override
|
542 | public JDBCNewStoreParameters createNewStoreParameters() {
|
||
543 | return new JDBCNewStoreParameters(); |
||
544 | } |
||
545 | 43020 | jjdelcerro | |
546 | 44750 | jjdelcerro | @Override
|
547 | public JDBCStoreParameters createOpenStoreParameters() {
|
||
548 | return new JDBCStoreParameters(); |
||
549 | } |
||
550 | 43020 | jjdelcerro | |
551 | 44750 | jjdelcerro | @Override
|
552 | public JDBCServerExplorerParameters createServerExplorerParameters() {
|
||
553 | return new JDBCServerExplorerParameters(); |
||
554 | } |
||
555 | 43020 | jjdelcerro | |
556 | 44750 | jjdelcerro | @Override
|
557 | public String getSourceId(JDBCStoreParameters parameters) { |
||
558 | return parameters.getHost() + ":" |
||
559 | + parameters.getDBName() + ":"
|
||
560 | + parameters.getSchema() + ":"
|
||
561 | + parameters.tableID(); |
||
562 | } |
||
563 | 44198 | jjdelcerro | |
564 | 44750 | jjdelcerro | @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) { |
||
582 | 44748 | jjdelcerro | |
583 | 44750 | jjdelcerro | // 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;
|
||
594 | 44748 | jjdelcerro | } |
595 | 44750 | jjdelcerro | 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;
|
||
601 | 44376 | jjdelcerro | } |
602 | 44750 | jjdelcerro | ExpressionBuilder.Function function = (ExpressionBuilder.Function) value; |
603 | if (!StringUtils.equalsIgnoreCase(function.name(), FUNCTION_EXISTS)) {
|
||
604 | return;
|
||
605 | 44682 | jjdelcerro | } |
606 | 44750 | jjdelcerro | 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 | } |
||
642 | 44376 | jjdelcerro | |
643 | 44750 | jjdelcerro | 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();
|
||
655 | 44403 | omartinez | |
656 | 44750 | jjdelcerro | 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)) { |
||
673 | 44682 | jjdelcerro | return;
|
674 | 44750 | jjdelcerro | } |
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); |
||
722 | 44376 | jjdelcerro | } |
723 | 44750 | jjdelcerro | }, null);
|
724 | 44376 | jjdelcerro | |
725 | 44750 | jjdelcerro | // 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 | } |
||
730 | 44403 | omartinez | |
731 | 44750 | jjdelcerro | // 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;
|
||
739 | 44376 | jjdelcerro | } |
740 | for (String foreingName : foreing_value_args) { |
||
741 | 44750 | jjdelcerro | 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 | } |
||
762 | 44376 | jjdelcerro | |
763 | 44750 | jjdelcerro | // 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;
|
||
778 | 44376 | jjdelcerro | } |
779 | 44750 | jjdelcerro | 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()); |
||
792 | 44376 | jjdelcerro | |
793 | 44750 | jjdelcerro | 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 | } |
||
803 | 44682 | jjdelcerro | } |
804 | 44750 | jjdelcerro | |
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.");
|
||
810 | 44376 | jjdelcerro | } |
811 | 44750 | jjdelcerro | } |
812 | 43020 | jjdelcerro | } |