Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.db / org.gvsig.fmap.dal.db.jdbc / src / main / java / org / gvsig / fmap / dal / store / jdbc / JDBCHelper.java @ 41483

History | View | Annotate | Download (34.3 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.jdbc;
25

    
26
import java.sql.Connection;
27
import java.sql.DatabaseMetaData;
28
import java.sql.ResultSet;
29
import java.sql.ResultSetMetaData;
30
import java.sql.SQLException;
31
import java.sql.Statement;
32
import java.sql.Time;
33
import java.util.ArrayList;
34
import java.util.Arrays;
35
import java.util.Date;
36
import java.util.List;
37
import java.util.logging.Level;
38

    
39
import org.cresques.cts.IProjection;
40
import org.gvsig.fmap.dal.DALLocator;
41
import org.gvsig.fmap.dal.DataTypes;
42
import org.gvsig.fmap.dal.NewDataStoreParameters;
43
import org.gvsig.fmap.dal.exception.CloseException;
44
import org.gvsig.fmap.dal.exception.DataException;
45
import org.gvsig.fmap.dal.exception.InitializeException;
46
import org.gvsig.fmap.dal.exception.OpenException;
47
import org.gvsig.fmap.dal.exception.ReadException;
48
import org.gvsig.fmap.dal.exception.WriteException;
49
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
50
import org.gvsig.fmap.dal.feature.EditableFeatureType;
51
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
52
import org.gvsig.fmap.dal.feature.FeatureType;
53
import org.gvsig.fmap.dal.feature.exception.UnsupportedDataTypeException;
54
import org.gvsig.fmap.dal.resource.ResourceAction;
55
import org.gvsig.fmap.dal.resource.exception.AccessResourceException;
56
import org.gvsig.fmap.dal.resource.exception.ResourceExecuteException;
57
import org.gvsig.fmap.dal.resource.spi.ResourceConsumer;
58
import org.gvsig.fmap.dal.resource.spi.ResourceManagerProviderServices;
59
import org.gvsig.fmap.dal.resource.spi.ResourceProvider;
60
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCException;
61
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCExecuteSQLException;
62
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCSQLException;
63
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCTransactionCommitException;
64
import org.gvsig.fmap.dal.store.jdbc.exception.JDBCTransactionRollbackException;
65
import org.gvsig.fmap.geom.Geometry;
66
import org.gvsig.fmap.geom.GeometryLocator;
67
import org.gvsig.fmap.geom.GeometryManager;
68
import org.gvsig.fmap.geom.aggregate.Aggregate;
69
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
70
import org.gvsig.fmap.geom.exception.CreateGeometryException;
71
/*
72
import org.gvsig.fmap.geom.operation.fromwkb.FromWKB;
73
import org.gvsig.fmap.geom.operation.fromwkb.FromWKBGeometryOperationContext;
74
import org.gvsig.fmap.geom.operation.towkb.ToWKB;
75
import org.gvsig.fmap.geom.operation.towkb.ToWKBOperationContext;
76
*/
77
import org.gvsig.fmap.geom.primitive.Envelope;
78
import org.gvsig.fmap.geom.primitive.Primitive;
79
import org.gvsig.fmap.geom.type.GeometryType;
80
import org.gvsig.tools.dispose.impl.AbstractDisposable;
81
import org.gvsig.tools.exception.BaseException;
82
import org.slf4j.Logger;
83
import org.slf4j.LoggerFactory;
84

    
85
/**
86
 * @author jmvivo
87
 *
88
 */
89
public class JDBCHelper extends AbstractDisposable implements ResourceConsumer {
90

    
91
        private static Logger logger = LoggerFactory.getLogger(JDBCHelper.class);
92

    
93
        protected JDBCHelperUser user;
94
        protected boolean isOpen;
95
        protected String name;
96
        protected String defaultSchema;
97
        protected JDBCConnectionParameters params;
98
        private JDBCResource resource;
99

    
100
        protected GeometryManager geomManager = null;
101

    
102
        private Boolean allowAutomaticValues = null;
103
        private Boolean supportsUnions = null;
104

    
105
        private String identifierQuoteString;
106

    
107
        protected JDBCHelper(JDBCHelperUser consumer,
108
                        JDBCConnectionParameters params) throws InitializeException {
109
                this.geomManager = GeometryLocator.getGeometryManager();
110
                this.user = consumer;
111
                this.name = user.getProviderName();
112
                this.params = params;
113
                initializeResource();
114

    
115
        }
116

    
117
        protected void initializeResource() throws InitializeException {
118
                ResourceManagerProviderServices manager = (ResourceManagerProviderServices) DALLocator
119
                                .getResourceManager();
120
                JDBCResource resource = (JDBCResource) manager
121
                                .createAddResource(
122
                                JDBCResource.NAME, new Object[] { params.getUrl(),
123
                                                params.getHost(), params.getPort(), params.getDBName(),
124
                                                params.getUser(), params.getPassword(),
125
                                                params.getJDBCDriverClassName() });
126
                this.setResource(resource);
127

    
128
        }
129

    
130
        protected final void setResource(JDBCResource resource) {
131
                this.resource = resource;
132
                this.resource.addConsumer(this);
133
        }
134

    
135
        public boolean closeResourceRequested(ResourceProvider resource) {
136
                return user.closeResourceRequested(resource);
137
        }
138

    
139
        public void resourceChanged(ResourceProvider resource) {
140
                user.resourceChanged(resource);
141

    
142
        }
143

    
144
        /**
145
         * open the resource
146
         *
147
         * @return true if the resourse was open in this call
148
         * @throws OpenException
149
         */
150
        public boolean open() throws OpenException {
151
                if (isOpen) {
152
                        return false;
153
                }
154
                // try {
155
                // begin();
156
                // } catch (ResourceExecuteException e1) {
157
                // throw new OpenException(name, e1);
158
                // }
159
                try {
160
                        getResource().execute(new ResourceAction() {
161
                                public Object run() throws Exception {
162
                                        getResource().connect();
163
                                        getResource().notifyOpen();
164

    
165
                                        user.opendDone();
166

    
167
                                        isOpen = true;
168
                                        return null;
169
                                }
170
                                public String toString() {
171
                                    return "open";
172
                                }
173
                        });
174
                        return true;
175
                } catch (ResourceExecuteException e) {
176
                        throw new OpenException(name, e);
177
                        // } finally {
178
                        // end();
179
                }
180

    
181
        }
182

    
183
        public JDBCResource getResource() {
184
                return resource;
185
        }
186

    
187
        public void close() throws CloseException {
188
                if (!isOpen) {
189
                        return;
190
                }
191
                // try {
192
                // begin();
193
                // } catch (ResourceExecuteException e) {
194
                // throw new CloseException(name, e);
195
                // }
196
                try {
197
                        getResource().execute(new ResourceAction() {
198
                                public Object run() throws Exception {
199
                                        isOpen = false;
200

    
201
                                        resource.notifyClose();
202
                                        user.closeDone();
203
                                        return null;
204
                                }
205
                        });
206
                } catch (ResourceExecuteException e) {
207
                        throw new CloseException(this.name, e);
208
                        // } finally {
209
                        // end();
210
                }
211
        }
212

    
213
        // public void end() {
214
        // resource.end();
215
        // }
216
        //
217
        // public void begin() throws ResourceExecuteException {
218
        // this.resource.begin();
219
        // }
220

    
221
        public Connection getConnection() throws AccessResourceException {
222
                return resource.getJDBCConnection();
223

    
224
        }
225

    
226
        @Override
227
        protected void doDispose() throws BaseException {
228
                this.close();
229
                resource.removeConsumer(this);
230
        }
231

    
232
        public boolean isOpen() {
233
                return isOpen;
234
        }
235

    
236
        /**
237
         * Executes an atomic action that uses an DB Connection.<br>
238
         *
239
         * This methos prepares a connection and close it at the end of execution of
240
         * action.<br>
241
         *
242
         * if <code>action</code> is an instance of {@link TransactionalAction} the
243
         * action will be execute inside of a DB transaction.
244
         *
245
         *
246
         * @param action
247
         * @throws Exception
248
         */
249
        public Object doConnectionAction(final ConnectionAction action)
250
                        throws Exception {
251
                this.open();
252
//                this.begin();
253
                return getResource().execute(new ResourceAction() {
254
                        public Object run() throws Exception {
255
                                Object result = null;
256
                                Connection conn = null;
257
                                boolean beginTrans = false;
258
                                try {
259
                                        conn = getConnection();
260
                                        if (action instanceof TransactionalAction) {
261
                                                // XXX OJO esta condicion NO ES FIABLE
262
                                                if (!conn.getAutoCommit()) {
263
                                                        if (!((TransactionalAction) action)
264
                                                                        .continueTransactionAllowed()) {
265
                                                                // FIXME exception
266
                                                                throw new Exception();
267
                                                        }
268
                                                }
269
                                                try {
270
                                                        conn.setAutoCommit(false);
271
                                                } catch (SQLException e) {
272
                                                        throw new JDBCSQLException(e);
273
                                                }
274
                                                beginTrans = true;
275
                                        }
276

    
277
                                        result = action.action(conn);
278

    
279
                                        if (beginTrans) {
280
                                                try {
281
                                                        conn.commit();
282
                                                } catch (SQLException e) {
283
                                                        throw new JDBCTransactionCommitException(e);
284
                                                }
285
                                        }
286

    
287
                                        return result;
288

    
289
                                } catch (Exception e) {
290

    
291
                                        if (beginTrans) {
292
                                                try {
293
                                                        conn.rollback();
294
                                                } catch (Exception e1) {
295
                                                        throw new JDBCTransactionRollbackException(e1, e);
296
                                                }
297
                                        }
298
                                        throw e;
299

    
300
                                } finally {
301
                                        try {
302
                                                if (conn != null) {
303
                                                        conn.close();
304
                                                }
305
                                        } catch (Exception e1) {
306
                                                logger.error("Exception on close connection", e1);
307
                                        }
308
                                        // this.end();
309
                                }
310
                        }
311
                });
312

    
313
        }
314

    
315
        protected String getDefaultSchema(Connection conn) throws JDBCException {
316
                return defaultSchema;
317
        }
318

    
319
        protected EditableFeatureAttributeDescriptor createAttributeFromJDBC(
320
                        EditableFeatureType fType, Connection conn,
321
                        ResultSetMetaData rsMetadata, int colIndex)
322
        throws java.sql.SQLException {
323

    
324
                EditableFeatureAttributeDescriptor column;
325
                switch (rsMetadata.getColumnType(colIndex)) {
326
                case java.sql.Types.INTEGER:
327
                        column = fType.add(rsMetadata.getColumnName(colIndex),
328
                                        DataTypes.INT);
329
                        break;
330
                case java.sql.Types.BIGINT:
331
                        column = fType.add(rsMetadata.getColumnName(colIndex),
332
                                        DataTypes.LONG);
333
                        break;
334
                case java.sql.Types.REAL:
335
                        column = fType.add(rsMetadata.getColumnName(colIndex),
336
                                        DataTypes.DOUBLE);
337
                        break;
338
                case java.sql.Types.DOUBLE:
339
                        column = fType.add(rsMetadata.getColumnName(colIndex),
340
                                        DataTypes.DOUBLE);
341
                        break;
342
                case java.sql.Types.CHAR:
343
                        column = fType.add(rsMetadata.getColumnName(colIndex),
344
                                        DataTypes.STRING);
345
                        break;
346
                case java.sql.Types.VARCHAR:
347
                case java.sql.Types.LONGVARCHAR:
348
                        column = fType.add(rsMetadata.getColumnName(colIndex),
349
                                        DataTypes.STRING);
350
                        break;
351
                case java.sql.Types.FLOAT:
352
                        column = fType.add(rsMetadata.getColumnName(colIndex),
353
                                        DataTypes.FLOAT);
354
                        break;
355
        case java.sql.Types.NUMERIC:
356
            column = fType.add(rsMetadata.getColumnName(colIndex),
357
                    DataTypes.FLOAT);
358
            break;
359
                case java.sql.Types.DECIMAL:
360
                        column = fType.add(rsMetadata.getColumnName(colIndex),
361
                                        DataTypes.FLOAT);
362
                        break;
363
                case java.sql.Types.DATE:
364
                        column = fType.add(rsMetadata.getColumnName(colIndex),
365
                                        DataTypes.DATE);
366
                        break;
367
                case java.sql.Types.TIME:
368
                        column = fType.add(rsMetadata.getColumnName(colIndex),
369
                                        DataTypes.TIME);
370
                        break;
371
                case java.sql.Types.TIMESTAMP:
372
                        column = fType.add(rsMetadata.getColumnName(colIndex),
373
                                        DataTypes.TIMESTAMP);
374
                        break;
375
                case java.sql.Types.BOOLEAN:
376
                case java.sql.Types.BIT:
377
                        column = fType.add(rsMetadata.getColumnName(colIndex),
378
                                        DataTypes.BOOLEAN);
379
                        break;
380
                case java.sql.Types.BLOB:
381
                case java.sql.Types.BINARY:
382
                case java.sql.Types.LONGVARBINARY:
383
                        column = fType.add(rsMetadata.getColumnName(colIndex),
384
                                        DataTypes.BYTEARRAY);
385
                        break;
386

    
387
                default:
388
                    column = createAttributeFromJDBCNativeType(fType, rsMetadata, colIndex);
389
                        break;
390
                }
391

    
392
                return column;
393

    
394
        }
395
        
396
        
397
        protected EditableFeatureAttributeDescriptor createAttributeFromJDBCNativeType(
398
        EditableFeatureType fType, ResultSetMetaData rsMetadata, int colIndex)
399
        throws SQLException {
400
        EditableFeatureAttributeDescriptor column;
401
        column = fType.add(rsMetadata.getColumnName(colIndex),
402
                DataTypes.OBJECT);
403
        column.setAdditionalInfo("SQLType", new Integer(rsMetadata
404
                .getColumnType(colIndex)));
405
        column.setAdditionalInfo("SQLTypeName", rsMetadata
406
                .getColumnTypeName(colIndex));
407
        return column;
408
    }
409
        
410

    
411
        protected EditableFeatureAttributeDescriptor getAttributeFromJDBC(
412
                        EditableFeatureType fType, Connection conn,
413
                        ResultSetMetaData rsMetadata, int colIndex) throws JDBCException {
414
                EditableFeatureAttributeDescriptor column;
415
                try {
416

    
417
                        column = createAttributeFromJDBC(fType, conn, rsMetadata, colIndex);
418
                        // column.setCaseSensitive(rsMetadata.isCaseSensitive(colIndex));
419
                        // column.setSqlType(rsMetadata.getColumnType(colIndex));
420
                        column.setAllowNull(
421
                                        rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable);
422
                        column.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
423
                        column.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
424
                        // column.setWritable(rsMetadata.isWritable(colIndex));
425
                        // column.setClassName(rsMetadata.getColumnClassName(colIndex));
426
                        // column.setCatalogName(rsMetadata.getCatalogName(colIndex));
427
                        // column.setDefinitelyWritable(rsMetadata
428
                        // .isDefinitelyWritable(colIndex));
429
                        // column.setLabel(rsMetadata.getColumnLabel(colIndex));
430
                        // column.setSchemaName(rsMetadata.getSchemaName(colIndex));
431
                        // column.setTableName(rsMetadata.getTableName(colIndex));
432
                        // column.setCatalogName(rsMetadata.getCatalogName(colIndex));
433
                        // column.setSqlTypeName();
434
                        // column.setSearchable(rsMetadata.isSearchable(colIndex));
435
                        // column.setSigned(rsMetadata.isSigned(colIndex));
436
                        // column.setCurrency(rsMetadata.isCurrency(colIndex));
437
                        column.setPrecision(rsMetadata.getPrecision(colIndex));
438
                        column.setSize(rsMetadata.getColumnDisplaySize(colIndex));
439

    
440
                } catch (java.sql.SQLException e) {
441
                        throw new JDBCSQLException(e);
442
                }
443

    
444
                return column;
445

    
446
        }
447

    
448
        /**
449
         * Fill <code>featureType</code> geometry attributes with SRS and ShapeType
450
         * information
451
         *
452
         * <b>Override this if provider has native eometry support</b>
453
         *
454
         * @param conn
455
         * @param rsMetadata
456
         * @param featureType
457
         * @throws ReadException
458
         */
459
        protected void loadSRS_and_shapeType(Connection conn,
460
                        ResultSetMetaData rsMetadata, EditableFeatureType featureType,
461
                        String baseSchema, String baseTable) throws JDBCException {
462

    
463
                // Nothing to do
464

    
465
        }
466

    
467
        public void loadFeatureType(EditableFeatureType featureType,
468
                        JDBCStoreParameters storeParams) throws DataException {
469
                if (storeParams.getSQL() != null
470
                                && storeParams.getSQL().trim().length() > 0) {
471
                        loadFeatureType(featureType, storeParams, storeParams.getSQL(),
472
                                        null, null);
473
                } else {
474
                        String sql = "Select * from " + storeParams.tableID()
475
                                        + " where false";
476
                        loadFeatureType(featureType, storeParams, sql, storeParams
477
                                        .getSchema(), storeParams.getTable());
478
                }
479
        }
480

    
481
        public void loadFeatureType(final EditableFeatureType featureType,
482
                        final JDBCStoreParameters storeParams, final String sql,
483
                        final String schema, final String table) throws DataException {
484
                this.open();
485
//                this.begin();
486
                getResource().execute(new ResourceAction() {
487
                        public Object run() throws Exception {
488
                                Connection conn = null;
489
                                try {
490
                                        conn = getConnection();
491
                                        
492
                                        String[] pks = storeParams.getPkFields();
493
                                        if (pks == null || pks.length < 1) {
494
                                                if (storeParams.getTable() != null
495
                                                                && storeParams.getTable().trim().length() > 0) {
496
                                                        pks = getPksFrom(conn, storeParams);
497
                                                        
498
                                                }
499
                                        }
500
                                        
501
                                        loadFeatureType(conn, featureType, sql, pks, storeParams
502
                                                        .getDefaultGeometryField(), schema, table);
503
                                        if (storeParams.getCRS()!=null && ((EditableFeatureAttributeDescriptor)featureType.getDefaultGeometryAttribute()) != null){
504
                                                ((EditableFeatureAttributeDescriptor)featureType.getDefaultGeometryAttribute()).setSRS(storeParams.getCRS());
505
                                        }
506
                                        
507
                                } finally {
508
                                        try {
509
                                                conn.close();
510
                                        } catch (Exception e) {
511
                                        }
512
//                        this.end();
513
                                }
514
                                return null;
515
                        }
516
                });
517
        }
518

    
519
        protected String[] getPksFrom(Connection conn, JDBCStoreParameters params)
520
                throws JDBCException {
521
                try{
522
                        DatabaseMetaData metadata = conn.getMetaData();
523
                        ResultSet rsPrimaryKeys = null;
524
                        ResultSet rs = null;
525
                        String catalog = params.getCatalog();
526
                        String schema = params.getSchema();
527

    
528
                        try{
529
                                rs = metadata.getTables(catalog,
530
                                                schema, params.getTable(), null);
531

    
532
                                if (!rs.next()) {
533
                                        // No tables found with default values, ignoring catalog
534
                                        rs.close();
535
                                        catalog = null;
536
                                        schema = null;
537
                                        rs = metadata
538
                                                        .getTables(catalog, schema, params.getTable(), null);
539

    
540
                                        if (!rs.next()) {
541
                                                // table not found
542
                                                return null;
543
                                        } else if (rs.next()){
544
                                                // More that one, cant identify
545
                                                return null;
546
                                        }
547

    
548
                                } else if (rs.next()) {
549
                                        // More that one, cant identify
550
                                        return null;
551
                                }
552
                                rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, params
553
                                                .getTable());
554
                                List pks = new ArrayList();
555
                                while (rsPrimaryKeys.next()){
556
                                        pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
557
                                }
558
                                return (String[]) pks.toArray(new String[pks.size()]);
559

    
560

    
561
                        } finally {
562
                                try{if (rs != null) {
563
                                        rs.close();
564
                                }} catch (SQLException ex) {logger.warn("Exception closing tables rs", ex);};
565
                                try{if (rsPrimaryKeys != null) {
566
                                        rsPrimaryKeys.close();
567
                                }} catch (SQLException ex) {logger.warn("Exception closing pk rs", ex);};
568
                        }
569

    
570

    
571
                } catch (SQLException e) {
572
                        logger.warn("Unable to get pk from DatabaseMetada", e);
573
                        return getPksFromInformationSchema(conn, params);
574
                }
575

    
576
        }
577

    
578
        protected String[] getPksFromInformationSchema(Connection conn,
579
                        JDBCStoreParameters params)
580
                        throws JDBCException {
581
                Statement st;
582
                StringBuffer sql = new StringBuffer();
583
                ResultSet rs;
584
                ArrayList list = new ArrayList();
585

    
586
                /*
587
                 select column_name as primary_key
588
                        from information_schema.table_constraints t_cons
589
                                inner join information_schema.key_column_usage c on
590
                                        c.constraint_catalog = t_cons.table_catalog and
591
                                    c.table_schema = t_cons.table_schema and
592
                                    c.table_name = t_cons.table_name and
593
                                        c.constraint_name = t_cons.constraint_name
594
                                where t_cons.table_schema = <schema>
595
                                and t_cons.constraint_catalog = <catalog>
596
                                 and t_cons.table_name = <table>
597
                                 and constraint_type = 'PRIMARY KEY'
598
                 */
599
                /*
600
                 * SELECT column_name FROM INFORMATION_SCHEMA.constraint_column_usage
601
                 * left join INFORMATION_SCHEMA.table_constraints on
602
                 * (INFORMATION_SCHEMA.table_constraints.constraint_name =
603
                 * INFORMATION_SCHEMA.constraint_column_usage.constraint_name and
604
                 * INFORMATION_SCHEMA.table_constraints.table_name =
605
                 * INFORMATION_SCHEMA.constraint_column_usage.table_name and
606
                 * INFORMATION_SCHEMA.table_constraints.table_schema =
607
                 * INFORMATION_SCHEMA.constraint_column_usage.table_schema) WHERE
608
                 * INFORMATION_SCHEMA.constraint_column_usage.table_name like
609
                 * 'muni10000_peq' AND
610
                 * INFORMATION_SCHEMA.constraint_column_usage.table_schema like 'public'
611
                 * AND INFORMATION_SCHEMA.constraint_column_usage.table_catalog like
612
                 * 'gis' AND constraint_type='PRIMARY KEY'
613
                 */
614

    
615
                sql.append("select column_name as primary_key ");
616
                sql.append("from information_schema.table_constraints t_cons ");
617
                sql.append("inner join information_schema.key_column_usage c on ");
618
                sql.append("c.constraint_catalog = t_cons.constraint_catalog and ");
619
                sql.append("c.table_schema = t_cons.table_schema and ");
620
                sql.append("c.table_name = t_cons.table_name and ");
621
                sql.append("c.constraint_name = t_cons.constraint_name ");
622
                sql.append("WHERE t_cons.table_name like '");
623

    
624
                sql.append(params.getTable());
625
                sql.append("' ");
626
                String schema = null;
627

    
628

    
629
                if (params.getSchema() == null || params.getSchema() == "") {
630
                        schema = getDefaultSchema(conn);
631
                } else {
632
                        schema = params.getSchema();
633
                }
634
                if (schema != null) {
635
                        sql.append(" and t_cons.table_schema like '");
636
                        sql.append(schema);
637
                        sql.append("' ");
638
                }
639

    
640
                if (params.getCatalog() != null && params.getCatalog() != "") {
641
                        sql
642
                                        .append(" and t_cons.constraint_catalog like '");
643
                        sql.append(params.getCatalog());
644
                        sql.append("' ");
645
                }
646

    
647
                sql.append("' and constraint_type = 'PRIMARY KEY'");
648

    
649
                // System.out.println(sql.toString());
650
                try {
651
                        st = conn.createStatement();
652
                        try {
653
                                rs = st.executeQuery(sql.toString());
654
                        } catch (java.sql.SQLException e) {
655
                                throw new JDBCExecuteSQLException(sql.toString(), e);
656
                        }
657
                        while (rs.next()) {
658
                                list.add(rs.getString(1));
659
                        }
660
                        rs.close();
661
                        st.close();
662

    
663
                } catch (java.sql.SQLException e) {
664
                        throw new JDBCSQLException(e);
665
                }
666
                if (list.size() == 0) {
667
                        return null;
668
                }
669

    
670
                return (String[]) list.toArray(new String[0]);
671

    
672
        }
673

    
674
        protected void loadFeatureType(Connection conn,
675
                        EditableFeatureType featureType, String sql, String[] pks,
676
                        String defGeomName, String schema, String table)
677
                        throws DataException {
678

    
679
                Statement stAux = null;
680
                ResultSet rs = null;
681
                try {
682

    
683
                        stAux = conn.createStatement();
684
                        stAux.setFetchSize(1);
685

    
686
                        try {
687
                                rs = stAux.executeQuery(sql);
688
                        } catch (SQLException e) {
689
                                throw new JDBCExecuteSQLException(sql, e);
690
                        }
691
                        ResultSetMetaData rsMetadata = rs.getMetaData();
692

    
693
                        List pksList = null;
694
                        if (pks != null) {
695
                                pksList = Arrays.asList(pks);
696

    
697
                        }
698

    
699
                        int i;
700
                        int geometriesColumns = 0;
701
                        String lastGeometry = null;
702

    
703
                        EditableFeatureAttributeDescriptor attr;
704
            boolean firstGeometryAttrFound = false;
705
                        for (i = 1; i <= rsMetadata.getColumnCount(); i++) {
706
                                attr = getAttributeFromJDBC(featureType, conn, rsMetadata, i);
707
                                if (pksList != null && pksList.contains(attr.getName())) {
708
                                        attr.setIsPrimaryKey(true);
709
                                }
710
                                if (attr.getType() == DataTypes.GEOMETRY) {
711
                    geometriesColumns++;
712
                    lastGeometry = attr.getName();
713
                    // Set the default geometry attribute if it is the one
714
                    // given as parameter or it is the first one, just in case.
715
                    if (!firstGeometryAttrFound
716
                        || lastGeometry.equals(defGeomName)) {
717
                        firstGeometryAttrFound = true;
718
                        featureType
719
                            .setDefaultGeometryAttributeName(lastGeometry);
720
                    }
721
                                }
722

    
723
                        }
724

    
725
                        if (geometriesColumns > 0) {
726
                                loadSRS_and_shapeType(conn, rsMetadata, featureType, schema,
727
                                                table);
728
                        }
729

    
730
                        if (defGeomName == null && geometriesColumns == 1) {
731
                                featureType.setDefaultGeometryAttributeName(lastGeometry);
732
                                defGeomName = lastGeometry;
733
                        }
734

    
735
                } catch (java.sql.SQLException e) {
736
                        throw new JDBCSQLException(e); // FIXME exception
737
                } finally {
738
                        try {
739
                                rs.close();
740
                        } catch (Exception e) {
741
                        }
742
                        try {
743
                                stAux.close();
744
                        } catch (Exception e) {
745
                        }
746

    
747
                }
748

    
749
        }
750

    
751
        /**
752
         * Override if provider has geometry support
753
         *
754
         * @param storeParams
755
         * @param geometryAttrName
756
         * @param limit
757
         * @return
758
         * @throws DataException
759
         */
760
        public Envelope getFullEnvelopeOfField(JDBCStoreParameters storeParams,
761
                        String geometryAttrName, Envelope limit) throws DataException {
762

    
763
                // TODO
764
                return null;
765

    
766
        }
767

    
768
        public Geometry getGeometry(byte[] buffer) throws BaseException {
769
                if (buffer == null) {
770
                        return null;
771
                }
772
                return geomManager.createFrom(buffer);
773
        }
774

    
775
        public String escapeFieldName(String field) {
776
                if (field.matches("[a-z][a-z0-9_]*")) {
777
                        return field;
778
                }
779
                String quote = getIdentifierQuoteString();
780
                return quote + field + quote;
781
        }
782

    
783
        public class DalValueToJDBCException extends WriteException {
784

    
785
                /**
786
                 * 
787
                 */
788
                private static final long serialVersionUID = -3608973505723097889L;
789
                private final static String MESSAGE_FORMAT = "Can't convert value of attribute '%(attributeName)' to JDBC type. %(problem) (attribute type '%(attributeType)', value class '%(valueClass)').";
790
                private final static String MESSAGE_KEY = "_Cant_convert_value_of_attribute_XattributeNameX_to_JDBC_type_XproblemX_attribute_type_XattributeTypeX_value_class_XvalueClassX";
791

    
792
                public DalValueToJDBCException(FeatureAttributeDescriptor attributeDescriptor, Object object, Throwable cause) {
793
                    this(attributeDescriptor, object, (String)null, cause);
794
                }
795
                
796
                public DalValueToJDBCException(FeatureAttributeDescriptor attributeDescriptor, Object object, String problem, Throwable cause) {
797
                        super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
798
                        if( attributeDescriptor != null ) {
799
                                setValue("attributeName",attributeDescriptor.getName());
800
                                setValue("attributeType",attributeDescriptor.getDataTypeName());
801
                        } else {
802
                                setValue("attributeName","unknown");
803
                                setValue("attributeType","unknown");
804
                        }
805
                        if( object!=null ) {
806
                                setValue("valueClass", object.getClass().getName());
807
                        } else {
808
                                setValue("valueClass", "null");
809
                        }
810
                        if( problem!=null ) {
811
                            setValue("problem","");
812
                        } else {
813
                            setValue("problem",problem);
814
                        }
815
                }
816

    
817
        }
818

    
819
    protected Geometry coerce(GeometryType type, Geometry geometry) {
820
        try {
821
            GeometryType geomType = geometry.getGeometryType();
822
            if (geomType.isTypeOf(type)) {
823
                return geometry;
824
            }
825

    
826
            if (type.isTypeOf(Geometry.TYPES.MULTISURFACE)) {
827
                if (geomType.isTypeOf(Geometry.TYPES.SURFACE)) {
828
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
829
                    geom2.addPrimitive((Primitive) geometry);
830
                    return geom2;
831
                }
832
            } else if (type.isTypeOf(Geometry.TYPES.MULTICURVE)) {
833
                if (geomType.isTypeOf(Geometry.TYPES.CURVE)) {
834
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
835
                    geom2.addPrimitive((Primitive) geometry);
836
                    return geom2;
837
                }
838
            } else if (type.isTypeOf(Geometry.TYPES.MULTIPOINT)) {
839
                if (geomType.isTypeOf(Geometry.TYPES.POINT)) {
840
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
841
                    geom2.addPrimitive((Primitive) geometry);
842
                    return geom2;
843
                }
844
            }
845
        } catch (CreateGeometryException ex) {
846
            // Do nothing, return the same geometry.
847
            logger.debug("Can't coerce geometry '"+geometry+"' to '"+type+"'.",ex);
848
        }
849
        return geometry;
850
    }
851
        
852
    public Object dalValueToJDBC(
853
            FeatureAttributeDescriptor attributeDescriptor, Object value)
854
            throws WriteException {
855

    
856
        try {
857
            if (value == null) {
858
                return null;
859
            }
860
            switch (attributeDescriptor.getType()) {
861
                case DataTypes.TIME:
862
                    return new Time(((Date)value).getTime());
863

    
864
                case DataTypes.GEOMETRY:
865
                    IProjection srs = null;
866
                    byte[] wkb = null;
867
                    Geometry geom = null;
868
                    try {
869
                        geom = (Geometry)value;
870
                        Geometry geom1 = coerce(attributeDescriptor.getGeomType(), geom);
871
                        srs = attributeDescriptor.getSRS();
872
                        if (srs != null) {
873
                            wkb = geom1.convertToWKBForcingType(getProviderSRID(srs), attributeDescriptor.getGeomType().getType());
874
                        } else {
875
                            wkb = geom1.convertToWKB();
876
                        }
877
                    } catch (Exception e) {
878
                        String problem = "";
879
                        if (geom != null) {
880
                            Geometry.ValidationStatus vs = geom.getValidationStatus();
881
                            problem = vs.getMessage();
882
                        }
883
                        throw new DalValueToJDBCException(attributeDescriptor, value, problem, e);
884
                    }
885
                    return wkb;
886
                
887
                default:
888
                    return value;
889
            }
890
            
891
        } catch(DalValueToJDBCException ex) {
892
            throw ex;
893
        } catch (Exception e) {
894
            throw new DalValueToJDBCException(attributeDescriptor, value, e);
895
        }
896

    
897
    }
898

    
899
        public String getSqlColumnTypeDescription(FeatureAttributeDescriptor attr) {
900
                switch (attr.getType()) {
901
                case DataTypes.STRING:
902
                        if (attr.getSize() < 1 || attr.getSize() > 255) {
903
                                return "text";
904
                        } else {
905
                                return "varchar(" + attr.getSize() + ")";
906
                        }
907
                case DataTypes.BOOLEAN:
908
                        return "bool";
909

    
910
                case DataTypes.BYTE:
911
                        return "smallint";
912

    
913
                case DataTypes.DATE:
914
                        return "date";
915

    
916
                case DataTypes.TIMESTAMP:
917
                        return "timestamp";
918

    
919
                case DataTypes.TIME:
920
                        return "time";
921

    
922
                case DataTypes.BYTEARRAY:
923
                case DataTypes.GEOMETRY:
924
                        return "blob";
925

    
926
                case DataTypes.DOUBLE:
927
//                        if (attr.getPrecision() > 0) {
928
//                            return "double precision(" + attr.getPrecision() + ')';
929
//                        } else {
930
                    //It works with PostgreSQL and MySQL. Check with others
931
                            return "double precision";
932
//                        }
933
                case DataTypes.FLOAT:
934
                        return "real";
935

    
936
                case DataTypes.INT:
937
                        if (attr.isAutomatic() && allowAutomaticValues()) {
938
                                return "serial";
939
                        } else {
940
                                return "integer";
941
                        }
942
                case DataTypes.LONG:
943
                        if (attr.isAutomatic()) {
944
                                return "bigserial";
945
                        } else {
946
                                return "bigint";
947
                        }
948

    
949
                default:
950
                        String typeName = (String) attr.getAdditionalInfo("SQLTypeName");
951
                        if (typeName != null) {
952
                                return typeName;
953
                        }
954

    
955
                        throw new UnsupportedDataTypeException(attr.getDataTypeName(), attr
956
                                        .getType());
957
                }
958
        }
959

    
960
        public int getProviderSRID(String srs) {
961
                return -1;
962
        }
963

    
964
        public int getProviderSRID(IProjection srs) {
965
                return -1;
966
        }
967

    
968
        public String getSqlFieldName(FeatureAttributeDescriptor attribute) {
969
                return escapeFieldName(attribute.getName());
970
        }
971

    
972
        public String getSqlFieldDescription(FeatureAttributeDescriptor attr)
973
                        throws DataException {
974

    
975
                /**
976
                 * column_name data_type [ DEFAULT default_expr ] [ column_constraint [
977
                 * ... ] ]
978
                 *
979
                 * where column_constraint is:
980
                 *
981
                 * [ CONSTRAINT constraint_name ] { NOT NULL | NULL | UNIQUE | PRIMARY
982
                 * KEY | CHECK (expression) | REFERENCES reftable [ ( refcolumn ) ] [
983
                 * MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE action ] [ ON
984
                 * UPDATE action ] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY
985
                 * DEFERRED | INITIALLY IMMEDIATE ]
986
                 */
987

    
988
                StringBuilder strb = new StringBuilder();
989
                // name
990
                strb.append(escapeFieldName(attr.getName()));
991
                strb.append(" ");
992

    
993
                // Type
994
                strb.append(this.getSqlColumnTypeDescription(attr));
995
                strb.append(" ");
996

    
997
                boolean allowNull = attr.allowNull()
998
                                && !(attr.isPrimaryKey() || attr.isAutomatic());
999
                // Default
1000
                if (attr.getDefaultValue() == null) {
1001
                        if (allowNull) {
1002
                                strb.append("DEFAULT NULL ");
1003
                        }
1004
                } else {
1005
                        String value = getDefaltFieldValueString(attr);
1006
                        strb.append("DEFAULT '");
1007
                        strb.append(value);
1008
                        strb.append("' ");
1009
                }
1010

    
1011
                // Null
1012
                if (allowNull) {
1013
                        strb.append("NULL ");
1014
                } else {
1015
                        strb.append("NOT NULL ");
1016
                }
1017

    
1018
                // Primery key
1019
                if (attr.isPrimaryKey()) {
1020
                        strb.append("PRIMARY KEY ");
1021
                }
1022
                return strb.toString();
1023
        }
1024

    
1025
        /**
1026
         * @deprecated use getDefaultFieldValueString this has a type writer error.
1027
         */
1028
        protected String getDefaltFieldValueString(FeatureAttributeDescriptor attr)
1029
                        throws WriteException {
1030
                return getDefaultFieldValueString(attr);
1031
        }
1032

    
1033
        protected String getDefaultFieldValueString(FeatureAttributeDescriptor attr)
1034
                        throws WriteException {
1035
                return dalValueToJDBC(attr, attr.getDefaultValue()).toString();
1036
        }
1037

    
1038
        public String compoundLimitAndOffset(long limit, long offset) {
1039
                StringBuilder sql = new StringBuilder();
1040
                // limit
1041
                if (limit > 0) {
1042
                        sql.append(" limit ");
1043
                        sql.append(limit);
1044
                        sql.append(' ');
1045
                }
1046

    
1047
                // offset
1048
                if (offset > 0) {
1049
                        sql.append(" offset ");
1050
                        sql.append(offset);
1051
                        sql.append(' ');
1052
                }
1053
                return sql.toString();
1054
        }
1055

    
1056
        public boolean supportOffset() {
1057
                return true;
1058
        }
1059

    
1060
        public List getAdditionalSqlToCreate(NewDataStoreParameters ndsp,
1061
                        FeatureType fType) {
1062
                // TODO Auto-generated method stub
1063
                return null;
1064
        }
1065

    
1066

    
1067
        public String stringJoin(List listToJoin,String sep){
1068
                StringBuilder strb = new StringBuilder();
1069
                stringJoin(listToJoin,sep,strb);
1070
                return strb.toString();
1071
        }
1072

    
1073
        public void stringJoin(List listToJoin, String sep, StringBuilder strb) {
1074
                if (listToJoin.size() < 1) {
1075
                        return;
1076
                }
1077
                if (listToJoin.size() > 1) {
1078
                        for (int i = 0; i < listToJoin.size() - 1; i++) {
1079
                                strb.append(listToJoin.get(i));
1080
                                strb.append(sep);
1081
                        }
1082
                }
1083
                strb.append(listToJoin.get(listToJoin.size() - 1));
1084
        }
1085

    
1086
        /**
1087
         * Inform that provider has supports for geometry store and operations
1088
         * natively
1089
         *
1090
         * @return
1091
         */
1092
        protected boolean supportsGeometry() {
1093
                return false;
1094
        }
1095

    
1096
        public boolean allowAutomaticValues() {
1097
                if (allowAutomaticValues == null) {
1098
                        ConnectionAction action = new ConnectionAction(){
1099

    
1100
                                public Object action(Connection conn) throws DataException {
1101

    
1102
                                        ResultSet rs;
1103
                                        try {
1104
                                                DatabaseMetaData meta = conn.getMetaData();
1105
                                                rs = meta.getTypeInfo();
1106
                                                try{
1107
                                                        while (rs.next()) {
1108
                                                                if (rs.getInt("DATA_TYPE") == java.sql.Types.INTEGER) {
1109
                                                                        if (rs.getBoolean("AUTO_INCREMENT")) {
1110
                                                                                return Boolean.TRUE;
1111
                                                                        } else {
1112
                                                                                return Boolean.FALSE;
1113
                                                                        }
1114
                                                                }
1115
                                                        }
1116
                                                }finally{
1117
                                                        try{ rs.close();} catch (SQLException ex) {logger.error("Exception closing resulset", ex);};
1118
                                                }
1119
                                        } catch (SQLException e) {
1120
                                                throw new JDBCSQLException(e);
1121
                                        }
1122
                                        return Boolean.FALSE;
1123
                                }
1124

    
1125
                        };
1126

    
1127

    
1128

    
1129
                        try {
1130
                                allowAutomaticValues = (Boolean) doConnectionAction(action);
1131
                        } catch (Exception e) {
1132
                                logger.error("Exception checking for automatic integers", e);
1133
                                allowAutomaticValues = Boolean.FALSE;
1134
                        }
1135
                }
1136
                return allowAutomaticValues.booleanValue();
1137
        }
1138

    
1139
        public boolean supportsUnion() {
1140
                if (supportsUnions == null) {
1141
                        ConnectionAction action = new ConnectionAction() {
1142

    
1143
                                public Object action(Connection conn) throws DataException {
1144

    
1145
                                        try {
1146
                                                DatabaseMetaData meta = conn.getMetaData();
1147
                                                return new Boolean(meta.supportsUnion());
1148
                                        } catch (SQLException e) {
1149
                                                throw new JDBCSQLException(e);
1150
                                        }
1151
                                }
1152

    
1153
                        };
1154

    
1155
                        try {
1156
                                supportsUnions = (Boolean) doConnectionAction(action);
1157
                        } catch (Exception e) {
1158
                                logger.error("Exception checking for unions support", e);
1159
                                supportsUnions = Boolean.FALSE;
1160
                        }
1161
                }
1162
                return supportsUnions.booleanValue();
1163
        }
1164

    
1165
        protected String getIdentifierQuoteString() {
1166
                if (identifierQuoteString == null) {
1167
                ConnectionAction action = new ConnectionAction() {
1168

    
1169
                        public Object action(Connection conn) throws DataException {
1170

    
1171
                                try {
1172
                                        DatabaseMetaData meta = conn.getMetaData();
1173
                                        return meta.getIdentifierQuoteString();
1174
                                } catch (SQLException e) {
1175
                                        throw new JDBCSQLException(e);
1176
                                }
1177
                        }
1178

    
1179
                };
1180

    
1181
                try {
1182
                        identifierQuoteString = (String) doConnectionAction(action);
1183
                } catch (Exception e) {
1184
                        logger.error("Exception checking for unions support", e);
1185
                        identifierQuoteString = " ";
1186
                        }
1187
                }
1188
                return identifierQuoteString;
1189
        }
1190

    
1191
        protected boolean isReservedWord(String field) {
1192
                // TODO
1193
                return false;
1194
        }
1195

    
1196
}