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

History | View | Annotate | Download (36 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.PreparedStatement;
29
import java.sql.ResultSet;
30
import java.sql.ResultSetMetaData;
31
import java.sql.SQLException;
32
import java.sql.Statement;
33
import java.util.ArrayList;
34
import java.util.Arrays;
35
import java.util.Date;
36
import java.util.List;
37

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

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

    
89
        private static Logger logger = LoggerFactory.getLogger(JDBCHelper.class);
90

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

    
98
        protected GeometryManager geomManager = null;
99

    
100
        private Boolean allowAutomaticValues = null;
101
        private Boolean supportsUnions = null;
102

    
103
        private String identifierQuoteString;
104

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

    
113
        }
114

    
115
        public static ResultSet executeQuery(Statement st, String sql) throws SQLException {
116
            logger.debug("execute SQL: "+sql);
117
            ResultSet rs = st.executeQuery(sql);
118
            return rs;
119
        }
120
        
121
        public static void execute(Statement st, String sql) throws SQLException {
122
            logger.debug("execute SQL: "+sql);
123
            st.execute(sql);
124
        }
125
        
126
        public static ResultSet executeQuery(PreparedStatement st, String sql) throws SQLException {
127
            logger.debug("execute SQL: "+sql);
128
            ResultSet rs = st.executeQuery();
129
            return rs;
130
        }
131

    
132
        public static void execute(PreparedStatement st, String sql) throws SQLException {
133
            logger.debug("execute SQL: "+sql);
134
            st.execute();
135
        }
136
        
137
        public static int executeUpdate(PreparedStatement st) throws SQLException {
138
            return st.executeUpdate();
139
        }
140
        
141
        
142
        protected void initializeResource() throws InitializeException {
143
                ResourceManagerProviderServices manager = (ResourceManagerProviderServices) DALLocator
144
                                .getResourceManager();
145
                JDBCResource resource = (JDBCResource) manager
146
                                .createAddResource(
147
                                JDBCResource.NAME, new Object[] { params.getUrl(),
148
                                                params.getHost(), params.getPort(), params.getDBName(),
149
                                                params.getUser(), params.getPassword(),
150
                                                params.getJDBCDriverClassName() });
151
                this.setResource(resource);
152

    
153
        }
154

    
155
        protected final void setResource(JDBCResource resource) {
156
                this.resource = resource;
157
                this.resource.addConsumer(this);
158
        }
159

    
160
        public boolean closeResourceRequested(ResourceProvider resource) {
161
                return user.closeResourceRequested(resource);
162
        }
163

    
164
        public void resourceChanged(ResourceProvider resource) {
165
                user.resourceChanged(resource);
166

    
167
        }
168

    
169
        /**
170
         * open the resource
171
         *
172
         * @return true if the resourse was open in this call
173
         * @throws OpenException
174
         */
175
        public boolean open() throws OpenException {
176
                if (isOpen) {
177
                        return false;
178
                }
179
                // try {
180
                // begin();
181
                // } catch (ResourceExecuteException e1) {
182
                // throw new OpenException(name, e1);
183
                // }
184
                try {
185
                        getResource().execute(new ResourceAction() {
186
                                public Object run() throws Exception {
187
                                        getResource().connect();
188
                                        getResource().notifyOpen();
189

    
190
                                        user.opendDone();
191

    
192
                                        isOpen = true;
193
                                        return null;
194
                                }
195
                                public String toString() {
196
                                    return "open";
197
                                }
198
                        });
199
                        return true;
200
                } catch (ResourceExecuteException e) {
201
                        throw new OpenException(name, e);
202
                        // } finally {
203
                        // end();
204
                }
205

    
206
        }
207

    
208
        public JDBCResource getResource() {
209
                return resource;
210
        }
211

    
212
        public void close() throws CloseException {
213
                if (!isOpen) {
214
                        return;
215
                }
216
                // try {
217
                // begin();
218
                // } catch (ResourceExecuteException e) {
219
                // throw new CloseException(name, e);
220
                // }
221
                try {
222
                        getResource().execute(new ResourceAction() {
223
                                public Object run() throws Exception {
224
                                        isOpen = false;
225

    
226
                                        resource.notifyClose();
227
                                        user.closeDone();
228
                                        return null;
229
                                }
230
                        });
231
                } catch (ResourceExecuteException e) {
232
                        throw new CloseException(this.name, e);
233
                        // } finally {
234
                        // end();
235
                }
236
        }
237

    
238
        // public void end() {
239
        // resource.end();
240
        // }
241
        //
242
        // public void begin() throws ResourceExecuteException {
243
        // this.resource.begin();
244
        // }
245

    
246
        public Connection getConnection() throws AccessResourceException {
247
                return resource.getJDBCConnection();
248

    
249
        }
250

    
251
        @Override
252
        protected void doDispose() throws BaseException {
253
                this.close();
254
                resource.removeConsumer(this);
255
        }
256

    
257
        public boolean isOpen() {
258
                return isOpen;
259
        }
260

    
261
        /**
262
         * Executes an atomic action that uses an DB Connection.<br>
263
         *
264
         * This methos prepares a connection and close it at the end of execution of
265
         * action.<br>
266
         *
267
         * if <code>action</code> is an instance of {@link TransactionalAction} the
268
         * action will be execute inside of a DB transaction.
269
         *
270
         *
271
         * @param action
272
         * @throws Exception
273
         */
274
        public Object doConnectionAction(final ConnectionAction action)
275
                        throws Exception {
276
                this.open();
277
//                this.begin();
278
                return getResource().execute(new ResourceAction() {
279
                        public Object run() throws Exception {
280
                                Object result = null;
281
                                Connection conn = null;
282
                                boolean beginTrans = false;
283
                                try {
284
                                        conn = getConnection();
285
                                        if (action instanceof TransactionalAction) {
286
                                                // XXX OJO esta condicion NO ES FIABLE
287
                                                if (!conn.getAutoCommit()) {
288
                                                        if (!((TransactionalAction) action)
289
                                                                        .continueTransactionAllowed()) {
290
                                                                // FIXME exception
291
                                                                throw new Exception();
292
                                                        }
293
                                                }
294
                                                try {
295
                                                        conn.setAutoCommit(false);
296
                                                } catch (SQLException e) {
297
                                                        throw new JDBCSQLException(e);
298
                                                }
299
                                                beginTrans = true;
300
                                        }
301

    
302
                                        result = action.action(conn);
303

    
304
                                        if (beginTrans) {
305
                                                try {
306
                                                        conn.commit();
307
                                                } catch (SQLException e) {
308
                                                        throw new JDBCTransactionCommitException(e);
309
                                                }
310
                                        }
311

    
312
                                        return result;
313

    
314
                                } catch (Exception e) {
315

    
316
                                        if (beginTrans) {
317
                                                try {
318
                                                        conn.rollback();
319
                                                } catch (Exception e1) {
320
                                                        throw new JDBCTransactionRollbackException(e1, e);
321
                                                }
322
                                        }
323
                                        throw e;
324

    
325
                                } finally {
326
                                        try {
327
                                                if (conn != null) {
328
                                                        conn.close();
329
                                                }
330
                                        } catch (Exception e1) {
331
                                                logger.error("Exception on close connection", e1);
332
                                        }
333
                                        // this.end();
334
                                }
335
                        }
336
                });
337

    
338
        }
339

    
340
        protected String getDefaultSchema(Connection conn) throws JDBCException {
341
                return defaultSchema;
342
        }
343

    
344
        protected EditableFeatureAttributeDescriptor createAttributeFromJDBC(
345
                        EditableFeatureType fType, Connection conn,
346
                        ResultSetMetaData rsMetadata, int colIndex)
347
        throws java.sql.SQLException {
348

    
349
                EditableFeatureAttributeDescriptor column;
350
                switch (rsMetadata.getColumnType(colIndex)) {
351
                case java.sql.Types.INTEGER:
352
                        column = fType.add(rsMetadata.getColumnName(colIndex),
353
                                        DataTypes.INT);
354
                        break;
355
                case java.sql.Types.BIGINT:
356
                        column = fType.add(rsMetadata.getColumnName(colIndex),
357
                                        DataTypes.LONG);
358
                        break;
359
                case java.sql.Types.REAL:
360
                        column = fType.add(rsMetadata.getColumnName(colIndex),
361
                                        DataTypes.DOUBLE);
362
                        break;
363
                case java.sql.Types.DOUBLE:
364
                        column = fType.add(rsMetadata.getColumnName(colIndex),
365
                                        DataTypes.DOUBLE);
366
                        break;
367
                case java.sql.Types.CHAR:
368
                        column = fType.add(rsMetadata.getColumnName(colIndex),
369
                                        DataTypes.STRING);
370
                        break;
371
                case java.sql.Types.VARCHAR:
372
                case java.sql.Types.LONGVARCHAR:
373
                        column = fType.add(rsMetadata.getColumnName(colIndex),
374
                                        DataTypes.STRING);
375
                        break;
376
                case java.sql.Types.FLOAT:
377
                        column = fType.add(rsMetadata.getColumnName(colIndex),
378
                                        DataTypes.FLOAT);
379
                        break;
380
        case java.sql.Types.NUMERIC:
381
            column = fType.add(rsMetadata.getColumnName(colIndex),
382
                    DataTypes.FLOAT);
383
            break;
384
                case java.sql.Types.DECIMAL:
385
                        column = fType.add(rsMetadata.getColumnName(colIndex),
386
                                        DataTypes.FLOAT);
387
                        break;
388
                case java.sql.Types.DATE:
389
                        column = fType.add(rsMetadata.getColumnName(colIndex),
390
                                        DataTypes.DATE);
391
                        break;
392
                case java.sql.Types.TIME:
393
                        column = fType.add(rsMetadata.getColumnName(colIndex),
394
                                        DataTypes.TIME);
395
                        break;
396
                case java.sql.Types.TIMESTAMP:
397
                        column = fType.add(rsMetadata.getColumnName(colIndex),
398
                                        DataTypes.TIMESTAMP);
399
                        break;
400
                case java.sql.Types.BOOLEAN:
401
                case java.sql.Types.BIT:
402
                        column = fType.add(rsMetadata.getColumnName(colIndex),
403
                                        DataTypes.BOOLEAN);
404
                        break;
405
                case java.sql.Types.BLOB:
406
                case java.sql.Types.BINARY:
407
                case java.sql.Types.LONGVARBINARY:
408
                        column = fType.add(rsMetadata.getColumnName(colIndex),
409
                                        DataTypes.BYTEARRAY);
410
                        break;
411

    
412
                default:
413
                    column = createAttributeFromJDBCNativeType(fType, rsMetadata, colIndex);
414
                        break;
415
                }
416

    
417
                return column;
418

    
419
        }
420
        
421
        
422
        protected EditableFeatureAttributeDescriptor createAttributeFromJDBCNativeType(
423
        EditableFeatureType fType, ResultSetMetaData rsMetadata, int colIndex)
424
        throws SQLException {
425
        EditableFeatureAttributeDescriptor column;
426
        column = fType.add(rsMetadata.getColumnName(colIndex),
427
                DataTypes.OBJECT);
428
        column.setAdditionalInfo("SQLType", new Integer(rsMetadata
429
                .getColumnType(colIndex)));
430
        column.setAdditionalInfo("SQLTypeName", rsMetadata
431
                .getColumnTypeName(colIndex));
432
        return column;
433
    }
434
        
435

    
436
        protected EditableFeatureAttributeDescriptor getAttributeFromJDBC(
437
                        EditableFeatureType fType, Connection conn,
438
                        ResultSetMetaData rsMetadata, int colIndex) throws JDBCException {
439
                EditableFeatureAttributeDescriptor column;
440
                try {
441

    
442
                        column = createAttributeFromJDBC(fType, conn, rsMetadata, colIndex);
443
                        // column.setCaseSensitive(rsMetadata.isCaseSensitive(colIndex));
444
                        // column.setSqlType(rsMetadata.getColumnType(colIndex));
445
                        column.setAllowNull(
446
                                        rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable);
447
                        column.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
448
                        column.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
449
                        // column.setWritable(rsMetadata.isWritable(colIndex));
450
                        // column.setClassName(rsMetadata.getColumnClassName(colIndex));
451
                        // column.setCatalogName(rsMetadata.getCatalogName(colIndex));
452
                        // column.setDefinitelyWritable(rsMetadata
453
                        // .isDefinitelyWritable(colIndex));
454
                        // column.setLabel(rsMetadata.getColumnLabel(colIndex));
455
                        // column.setSchemaName(rsMetadata.getSchemaName(colIndex));
456
                        // column.setTableName(rsMetadata.getTableName(colIndex));
457
                        // column.setCatalogName(rsMetadata.getCatalogName(colIndex));
458
                        // column.setSqlTypeName();
459
                        // column.setSearchable(rsMetadata.isSearchable(colIndex));
460
                        // column.setSigned(rsMetadata.isSigned(colIndex));
461
                        // column.setCurrency(rsMetadata.isCurrency(colIndex));
462
                        column.setPrecision(rsMetadata.getPrecision(colIndex));
463
                        column.setSize(rsMetadata.getColumnDisplaySize(colIndex));
464

    
465
                } catch (java.sql.SQLException e) {
466
                        throw new JDBCSQLException(e);
467
                }
468

    
469
                return column;
470

    
471
        }
472

    
473
        /**
474
         * Fill <code>featureType</code> geometry attributes with SRS and ShapeType
475
         * information
476
         *
477
         * <b>Override this if provider has native eometry support</b>
478
         *
479
         * @param conn
480
         * @param rsMetadata
481
         * @param featureType
482
         * @throws ReadException
483
         */
484
        protected void loadSRS_and_shapeType(Connection conn,
485
                        ResultSetMetaData rsMetadata, EditableFeatureType featureType,
486
                        String baseSchema, String baseTable) throws JDBCException {
487

    
488
                // Nothing to do
489

    
490
        }
491

    
492
        public void loadFeatureType(EditableFeatureType featureType,
493
                        JDBCStoreParameters storeParams) throws DataException {
494
                if (storeParams.getSQL() != null
495
                                && storeParams.getSQL().trim().length() > 0) {
496
                        loadFeatureType(featureType, storeParams, storeParams.getSQL(),
497
                                        null, null);
498
                } else {
499
                        String sql = "Select * from " + storeParams.tableID()
500
                                        + " where false";
501
                        loadFeatureType(featureType, storeParams, sql, storeParams
502
                                        .getSchema(), storeParams.getTable());
503
                }
504
        }
505

    
506
        public void loadFeatureType(final EditableFeatureType featureType,
507
                        final JDBCStoreParameters storeParams, final String sql,
508
                        final String schema, final String table) throws DataException {
509
                this.open();
510
//                this.begin();
511
                getResource().execute(new ResourceAction() {
512
                        public Object run() throws Exception {
513
                                Connection conn = null;
514
                                try {
515
                                        conn = getConnection();
516
                                        
517
                                        String[] pks = storeParams.getPkFields();
518
                                        if (pks == null || pks.length < 1) {
519
                                                if (storeParams.getTable() != null
520
                                                                && storeParams.getTable().trim().length() > 0) {
521
                                                        pks = getPksFrom(conn, storeParams);
522
                                                        
523
                                                }
524
                                        }
525
                                        
526
                                        loadFeatureType(conn, featureType, sql, pks, storeParams
527
                                                        .getDefaultGeometryField(), schema, table);
528
                                        if (storeParams.getCRS()!=null && ((EditableFeatureAttributeDescriptor)featureType.getDefaultGeometryAttribute()) != null){
529
                                                ((EditableFeatureAttributeDescriptor)featureType.getDefaultGeometryAttribute()).setSRS(storeParams.getCRS());
530
                                        }
531
                                        
532
                                } finally {
533
                                        try {
534
                                                conn.close();
535
                                        } catch (Exception e) {
536
                                        }
537
//                        this.end();
538
                                }
539
                                return null;
540
                        }
541
                });
542
        }
543

    
544
        protected String[] getPksFrom(Connection conn, JDBCStoreParameters params)
545
                throws JDBCException {
546
                try{
547
                        DatabaseMetaData metadata = conn.getMetaData();
548
                        ResultSet rsPrimaryKeys = null;
549
                        ResultSet rs = null;
550
                        String catalog = params.getCatalog();
551
                        String schema = params.getSchema();
552

    
553
                        try{
554
                                rs = metadata.getTables(catalog,
555
                                                schema, params.getTable(), null);
556

    
557
                                if (!rs.next()) {
558
                                        // No tables found with default values, ignoring catalog
559
                                        rs.close();
560
                                        catalog = null;
561
                                        schema = null;
562
                                        rs = metadata
563
                                                        .getTables(catalog, schema, params.getTable(), null);
564

    
565
                                        if (!rs.next()) {
566
                                                // table not found
567
                                                return null;
568
                                        } else if (rs.next()){
569
                                                // More that one, cant identify
570
                                                return null;
571
                                        }
572

    
573
                                } else if (rs.next()) {
574
                                        // More that one, cant identify
575
                                        return null;
576
                                }
577
                                rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, params
578
                                                .getTable());
579
                                List pks = new ArrayList();
580
                                while (rsPrimaryKeys.next()){
581
                                        pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
582
                                }
583
                                return (String[]) pks.toArray(new String[pks.size()]);
584

    
585

    
586
                        } finally {
587
                                try{if (rs != null) {
588
                                        rs.close();
589
                                }} catch (SQLException ex) {logger.warn("Exception closing tables rs", ex);};
590
                                try{if (rsPrimaryKeys != null) {
591
                                        rsPrimaryKeys.close();
592
                                }} catch (SQLException ex) {logger.warn("Exception closing pk rs", ex);};
593
                        }
594

    
595

    
596
                } catch (SQLException e) {
597
                        logger.warn("Unable to get pk from DatabaseMetada", e);
598
                        return getPksFromInformationSchema(conn, params);
599
                }
600

    
601
        }
602

    
603
        protected String[] getPksFromInformationSchema(Connection conn,
604
                        JDBCStoreParameters params)
605
                        throws JDBCException {
606
                Statement st;
607
                StringBuffer sql = new StringBuffer();
608
                ResultSet rs;
609
                ArrayList list = new ArrayList();
610

    
611
                /*
612
                 select column_name as primary_key
613
                        from information_schema.table_constraints t_cons
614
                                inner join information_schema.key_column_usage c on
615
                                        c.constraint_catalog = t_cons.table_catalog and
616
                                    c.table_schema = t_cons.table_schema and
617
                                    c.table_name = t_cons.table_name and
618
                                        c.constraint_name = t_cons.constraint_name
619
                                where t_cons.table_schema = <schema>
620
                                and t_cons.constraint_catalog = <catalog>
621
                                 and t_cons.table_name = <table>
622
                                 and constraint_type = 'PRIMARY KEY'
623
                 */
624
                /*
625
                 * SELECT column_name FROM INFORMATION_SCHEMA.constraint_column_usage
626
                 * left join INFORMATION_SCHEMA.table_constraints on
627
                 * (INFORMATION_SCHEMA.table_constraints.constraint_name =
628
                 * INFORMATION_SCHEMA.constraint_column_usage.constraint_name and
629
                 * INFORMATION_SCHEMA.table_constraints.table_name =
630
                 * INFORMATION_SCHEMA.constraint_column_usage.table_name and
631
                 * INFORMATION_SCHEMA.table_constraints.table_schema =
632
                 * INFORMATION_SCHEMA.constraint_column_usage.table_schema) WHERE
633
                 * INFORMATION_SCHEMA.constraint_column_usage.table_name like
634
                 * 'muni10000_peq' AND
635
                 * INFORMATION_SCHEMA.constraint_column_usage.table_schema like 'public'
636
                 * AND INFORMATION_SCHEMA.constraint_column_usage.table_catalog like
637
                 * 'gis' AND constraint_type='PRIMARY KEY'
638
                 */
639

    
640
                sql.append("select column_name as primary_key ");
641
                sql.append("from information_schema.table_constraints t_cons ");
642
                sql.append("inner join information_schema.key_column_usage c on ");
643
                sql.append("c.constraint_catalog = t_cons.constraint_catalog and ");
644
                sql.append("c.table_schema = t_cons.table_schema and ");
645
                sql.append("c.table_name = t_cons.table_name and ");
646
                sql.append("c.constraint_name = t_cons.constraint_name ");
647
                sql.append("WHERE t_cons.table_name like '");
648

    
649
                sql.append(params.getTable());
650
                sql.append("' ");
651
                String schema = null;
652

    
653

    
654
                if (params.getSchema() == null || params.getSchema() == "") {
655
                        schema = getDefaultSchema(conn);
656
                } else {
657
                        schema = params.getSchema();
658
                }
659
                if (schema != null) {
660
                        sql.append(" and t_cons.table_schema like '");
661
                        sql.append(schema);
662
                        sql.append("' ");
663
                }
664

    
665
                if (params.getCatalog() != null && params.getCatalog() != "") {
666
                        sql
667
                                        .append(" and t_cons.constraint_catalog like '");
668
                        sql.append(params.getCatalog());
669
                        sql.append("' ");
670
                }
671

    
672
                sql.append("' and constraint_type = 'PRIMARY KEY'");
673

    
674
                // System.out.println(sql.toString());
675
                try {
676
                        st = conn.createStatement();
677
                        try {
678
                                rs = JDBCHelper.executeQuery(st, sql.toString());
679
                        } catch (java.sql.SQLException e) {
680
                                throw new JDBCExecuteSQLException(sql.toString(), e);
681
                        }
682
                        while (rs.next()) {
683
                                list.add(rs.getString(1));
684
                        }
685
                        rs.close();
686
                        st.close();
687

    
688
                } catch (java.sql.SQLException e) {
689
                        throw new JDBCSQLException(e);
690
                }
691
                if (list.size() == 0) {
692
                        return null;
693
                }
694

    
695
                return (String[]) list.toArray(new String[0]);
696

    
697
        }
698

    
699
    protected void loadFeatureType(Connection conn,
700
            EditableFeatureType featureType, String sql, String[] pks,
701
            String defGeomName, String schema, String table)
702
            throws DataException {
703

    
704
        Statement stAux = null;
705
        ResultSet rs = null;
706
        try {
707

    
708
            stAux = conn.createStatement();
709
            stAux.setFetchSize(1);
710

    
711
            try {
712
                rs = JDBCHelper.executeQuery(stAux, sql);
713
            } catch (SQLException e) {
714
                throw new JDBCExecuteSQLException(sql, e);
715
            }
716
            ResultSetMetaData rsMetadata = rs.getMetaData();
717

    
718
            List pksList = null;
719
            if (pks != null) {
720
                pksList = Arrays.asList(pks);
721

    
722
            }
723

    
724
            int i;
725
            int geometriesColumns = 0;
726
            String lastGeometry = null;
727

    
728
            EditableFeatureAttributeDescriptor attr;
729
            boolean firstGeometryAttrFound = false;
730
            for (i = 1; i <= rsMetadata.getColumnCount(); i++) {
731
                attr = getAttributeFromJDBC(featureType, conn, rsMetadata, i);
732
                if (pksList != null && pksList.contains(attr.getName())) {
733
                    attr.setIsPrimaryKey(true);
734
                }
735
                if (attr.getType() == DataTypes.GEOMETRY) {
736
                    geometriesColumns++;
737
                    lastGeometry = attr.getName();
738
                    // Set the default geometry attribute if it is the one
739
                    // given as parameter or it is the first one, just in case.
740
                    if (!firstGeometryAttrFound
741
                            || lastGeometry.equals(defGeomName)) {
742
                        firstGeometryAttrFound = true;
743
                        featureType
744
                                .setDefaultGeometryAttributeName(lastGeometry);
745
                    }
746
                }
747

    
748
            }
749

    
750
            if (geometriesColumns > 0) {
751
                loadSRS_and_shapeType(conn, rsMetadata, featureType, schema,
752
                        table);
753
            }
754

    
755
            if (defGeomName == null && geometriesColumns == 1) {
756
                featureType.setDefaultGeometryAttributeName(lastGeometry);
757
                defGeomName = lastGeometry;
758
            }
759

    
760
        } catch (java.sql.SQLException e) {
761
            throw new JDBCSQLException(e); // FIXME exception
762
        } finally {
763
            try {
764
                rs.close();
765
            } catch (Exception e) {
766
            }
767
            try {
768
                stAux.close();
769
            } catch (Exception e) {
770
            }
771

    
772
        }
773

    
774
    }
775

    
776
        /**
777
         * Override if provider has geometry support
778
         *
779
         * @param storeParams
780
         * @param geometryAttrName
781
         * @param limit
782
         * @return
783
         * @throws DataException
784
         */
785
        public Envelope getFullEnvelopeOfField(JDBCStoreParameters storeParams,
786
                        String geometryAttrName, Envelope limit) throws DataException {
787

    
788
                // TODO
789
                return null;
790

    
791
        }
792

    
793
        public Geometry getGeometry(byte[] buffer) throws BaseException {
794
                if (buffer == null) {
795
                        return null;
796
                }
797
                return geomManager.createFrom(buffer);
798
        }
799

    
800
        public String escapeFieldName(String field) {
801
                if (field.matches("[a-z][a-z0-9_]*")) {
802
                        return field;
803
                }
804
                String quote = getIdentifierQuoteString();
805
                return quote + field + quote;
806
        }
807

    
808
        public class DalValueToJDBCException extends WriteException {
809

    
810
                /**
811
                 * 
812
                 */
813
                private static final long serialVersionUID = -3608973505723097889L;
814
                private final static String MESSAGE_FORMAT = "Can't convert value of attribute '%(attributeName)' to JDBC type. %(problem) (attribute type '%(attributeType)', value class '%(valueClass)').";
815
                private final static String MESSAGE_KEY = "_Cant_convert_value_of_attribute_XattributeNameX_to_JDBC_type_XproblemX_attribute_type_XattributeTypeX_value_class_XvalueClassX";
816

    
817
                public DalValueToJDBCException(FeatureAttributeDescriptor attributeDescriptor, Object object, Throwable cause) {
818
                    this(attributeDescriptor, object, (String)null, cause);
819
                }
820
                
821
                public DalValueToJDBCException(FeatureAttributeDescriptor attributeDescriptor, Object object, String problem, Throwable cause) {
822
                        super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
823
                        if( attributeDescriptor != null ) {
824
                                setValue("attributeName",attributeDescriptor.getName());
825
                                setValue("attributeType",attributeDescriptor.getDataTypeName());
826
                        } else {
827
                                setValue("attributeName","unknown");
828
                                setValue("attributeType","unknown");
829
                        }
830
                        if( object!=null ) {
831
                                setValue("valueClass", object.getClass().getName());
832
                        } else {
833
                                setValue("valueClass", "null");
834
                        }
835
                        if( problem!=null ) {
836
                            setValue("problem","");
837
                        } else {
838
                            setValue("problem",problem);
839
                        }
840
                }
841

    
842
        }
843

    
844
    protected Geometry coerce(GeometryType type, Geometry geometry) {
845
        try {
846
            GeometryType geomType = geometry.getGeometryType();
847
            if (geomType.isTypeOf(type)) {
848
                return geometry;
849
            }
850

    
851
            if (type.isTypeOf(Geometry.TYPES.MULTISURFACE)) {
852
                if (geomType.isTypeOf(Geometry.TYPES.SURFACE)) {
853
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
854
                    geom2.addPrimitive((Primitive) geometry);
855
                    return geom2;
856
                }
857
            } else if (type.isTypeOf(Geometry.TYPES.MULTICURVE)) {
858
                if (geomType.isTypeOf(Geometry.TYPES.CURVE)) {
859
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
860
                    geom2.addPrimitive((Primitive) geometry);
861
                    return geom2;
862
                }
863
            } else if (type.isTypeOf(Geometry.TYPES.MULTIPOINT)) {
864
                if (geomType.isTypeOf(Geometry.TYPES.POINT)) {
865
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
866
                    geom2.addPrimitive((Primitive) geometry);
867
                    return geom2;
868
                }
869
            }
870
        } catch (CreateGeometryException ex) {
871
            // Do nothing, return the same geometry.
872
            logger.debug("Can't coerce geometry '"+geometry+"' to '"+type+"'.",ex);
873
        }
874
        return geometry;
875
    }
876
        
877
    public Object dalValueToJDBC(
878
            FeatureAttributeDescriptor attributeDescriptor, Object value)
879
            throws WriteException {
880

    
881
        try {
882
            if (value == null) {
883
                return null;
884
            }
885
            Date jdate = null;
886
            switch (attributeDescriptor.getType()) {
887
                case DataTypes.DATE:
888
                    jdate = (Date)value;
889
                    java.sql.Date sqldate = new java.sql.Date(jdate.getTime());
890
                    return sqldate;
891
                    
892
                case DataTypes.TIME:
893
                    jdate = (Date)value;
894
                    java.sql.Time sqltime = new java.sql.Time(jdate.getTime());
895
                    return sqltime;
896

    
897
                case DataTypes.GEOMETRY:
898
                    IProjection srs = null;
899
                    byte[] wkb = null;
900
                    Geometry geom = null;
901
                    try {
902
                        geom = (Geometry)value;
903
                        Geometry geom1 = coerce(attributeDescriptor.getGeomType(), geom);
904
                        srs = attributeDescriptor.getSRS();
905
                        if (srs != null) {
906
                            wkb = geom1.convertToWKBForcingType(getProviderSRID(srs), attributeDescriptor.getGeomType().getType());
907
                        } else {
908
                            wkb = geom1.convertToWKB();
909
                        }
910
                    } catch (Exception e) {
911
                        String problem = "";
912
                        if (geom != null) {
913
                            Geometry.ValidationStatus vs = geom.getValidationStatus();
914
                            problem = vs.getMessage();
915
                        }
916
                        throw new DalValueToJDBCException(attributeDescriptor, value, problem, e);
917
                    }
918
                    return wkb;
919
                
920
                default:
921
                    return value;
922
            }
923
            
924
        } catch(DalValueToJDBCException ex) {
925
            throw ex;
926
        } catch (Exception e) {
927
            throw new DalValueToJDBCException(attributeDescriptor, value, e);
928
        }
929

    
930
    }
931

    
932
        public String getSqlColumnTypeDescription(FeatureAttributeDescriptor attr) {
933
                switch (attr.getType()) {
934
                case DataTypes.STRING:
935
                        if (attr.getSize() < 1 || attr.getSize() > 255) {
936
                                return "text";
937
                        } else {
938
                                return "varchar(" + attr.getSize() + ")";
939
                        }
940
                case DataTypes.BOOLEAN:
941
                        return "bool";
942

    
943
                case DataTypes.BYTE:
944
                        return "smallint";
945

    
946
                case DataTypes.DATE:
947
                        return "date";
948

    
949
                case DataTypes.TIMESTAMP:
950
                        return "timestamp";
951

    
952
                case DataTypes.TIME:
953
                        return "time";
954

    
955
                case DataTypes.BYTEARRAY:
956
                case DataTypes.GEOMETRY:
957
                        return "blob";
958

    
959
                case DataTypes.DOUBLE:
960
//                        if (attr.getPrecision() > 0) {
961
//                            return "double precision(" + attr.getPrecision() + ')';
962
//                        } else {
963
                    //It works with PostgreSQL and MySQL. Check with others
964
                            return "double precision";
965
//                        }
966
                case DataTypes.FLOAT:
967
                        return "real";
968

    
969
                case DataTypes.INT:
970
                        if (attr.isAutomatic() && allowAutomaticValues()) {
971
                                return "serial";
972
                        } else {
973
                                return "integer";
974
                        }
975
                case DataTypes.LONG:
976
                        if (attr.isAutomatic()) {
977
                                return "bigserial";
978
                        } else {
979
                                return "bigint";
980
                        }
981

    
982
                default:
983
                        String typeName = (String) attr.getAdditionalInfo("SQLTypeName");
984
                        if (typeName != null) {
985
                                return typeName;
986
                        }
987

    
988
                        throw new UnsupportedDataTypeException(attr.getDataTypeName(), attr
989
                                        .getType());
990
                }
991
        }
992

    
993
        public int getProviderSRID(String srs) {
994
                return -1;
995
        }
996

    
997
        public int getProviderSRID(IProjection srs) {
998
                return -1;
999
        }
1000

    
1001
        public String getSqlFieldName(FeatureAttributeDescriptor attribute) {
1002
                return escapeFieldName(attribute.getName());
1003
        }
1004

    
1005
        public String getSqlFieldDescription(FeatureAttributeDescriptor attr)
1006
                        throws DataException {
1007

    
1008
                /**
1009
                 * column_name data_type [ DEFAULT default_expr ] [ column_constraint [
1010
                 * ... ] ]
1011
                 *
1012
                 * where column_constraint is:
1013
                 *
1014
                 * [ CONSTRAINT constraint_name ] { NOT NULL | NULL | UNIQUE | PRIMARY
1015
                 * KEY | CHECK (expression) | REFERENCES reftable [ ( refcolumn ) ] [
1016
                 * MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE action ] [ ON
1017
                 * UPDATE action ] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY
1018
                 * DEFERRED | INITIALLY IMMEDIATE ]
1019
                 */
1020

    
1021
                StringBuilder strb = new StringBuilder();
1022
                // name
1023
                strb.append(escapeFieldName(attr.getName()));
1024
                strb.append(" ");
1025

    
1026
                // Type
1027
                strb.append(this.getSqlColumnTypeDescription(attr));
1028
                strb.append(" ");
1029

    
1030
                boolean allowNull = attr.allowNull()
1031
                                && !(attr.isPrimaryKey() || attr.isAutomatic());
1032
                // Default
1033
                if (attr.getDefaultValue() == null) {
1034
                        if (allowNull) {
1035
                                strb.append("DEFAULT NULL ");
1036
                        }
1037
                } else {
1038
                        String value = getDefaltFieldValueString(attr);
1039
                        strb.append("DEFAULT '");
1040
                        strb.append(value);
1041
                        strb.append("' ");
1042
                }
1043

    
1044
                // Null
1045
                if (allowNull) {
1046
                        strb.append("NULL ");
1047
                } else {
1048
                        strb.append("NOT NULL ");
1049
                }
1050

    
1051
                // Primery key
1052
                if (attr.isPrimaryKey()) {
1053
                        strb.append("PRIMARY KEY ");
1054
                }
1055
                return strb.toString();
1056
        }
1057

    
1058
        /**
1059
         * @deprecated use getDefaultFieldValueString this has a type writer error.
1060
         */
1061
        protected String getDefaltFieldValueString(FeatureAttributeDescriptor attr)
1062
                        throws WriteException {
1063
                return getDefaultFieldValueString(attr);
1064
        }
1065

    
1066
        protected String getDefaultFieldValueString(FeatureAttributeDescriptor attr)
1067
                        throws WriteException {
1068
                return dalValueToJDBC(attr, attr.getDefaultValue()).toString();
1069
        }
1070

    
1071
        public String compoundLimitAndOffset(long limit, long offset) {
1072
                StringBuilder sql = new StringBuilder();
1073
                // limit
1074
                if (limit > 0) {
1075
                        sql.append(" limit ");
1076
                        sql.append(limit);
1077
                        sql.append(' ');
1078
                }
1079

    
1080
                // offset
1081
                if (offset > 0) {
1082
                        sql.append(" offset ");
1083
                        sql.append(offset);
1084
                        sql.append(' ');
1085
                }
1086
                return sql.toString();
1087
        }
1088

    
1089
        public boolean supportOffset() {
1090
                return true;
1091
        }
1092

    
1093
        public List getAdditionalSqlToCreate(NewDataStoreParameters ndsp,
1094
                        FeatureType fType) {
1095
                // TODO Auto-generated method stub
1096
                return null;
1097
        }
1098

    
1099

    
1100
        public String stringJoin(List listToJoin,String sep){
1101
                StringBuilder strb = new StringBuilder();
1102
                stringJoin(listToJoin,sep,strb);
1103
                return strb.toString();
1104
        }
1105

    
1106
        public void stringJoin(List listToJoin, String sep, StringBuilder strb) {
1107
                if (listToJoin.size() < 1) {
1108
                        return;
1109
                }
1110
                if (listToJoin.size() > 1) {
1111
                        for (int i = 0; i < listToJoin.size() - 1; i++) {
1112
                                strb.append(listToJoin.get(i));
1113
                                strb.append(sep);
1114
                        }
1115
                }
1116
                strb.append(listToJoin.get(listToJoin.size() - 1));
1117
        }
1118

    
1119
        /**
1120
         * Inform that provider has supports for geometry store and operations
1121
         * natively
1122
         *
1123
         * @return
1124
         */
1125
        protected boolean supportsGeometry() {
1126
                return false;
1127
        }
1128

    
1129
        public boolean allowAutomaticValues() {
1130
                if (allowAutomaticValues == null) {
1131
                        ConnectionAction action = new ConnectionAction(){
1132

    
1133
                                public Object action(Connection conn) throws DataException {
1134

    
1135
                                        ResultSet rs;
1136
                                        try {
1137
                                                DatabaseMetaData meta = conn.getMetaData();
1138
                                                rs = meta.getTypeInfo();
1139
                                                try{
1140
                                                        while (rs.next()) {
1141
                                                                if (rs.getInt("DATA_TYPE") == java.sql.Types.INTEGER) {
1142
                                                                        if (rs.getBoolean("AUTO_INCREMENT")) {
1143
                                                                                return Boolean.TRUE;
1144
                                                                        } else {
1145
                                                                                return Boolean.FALSE;
1146
                                                                        }
1147
                                                                }
1148
                                                        }
1149
                                                }finally{
1150
                                                        try{ rs.close();} catch (SQLException ex) {logger.error("Exception closing resulset", ex);};
1151
                                                }
1152
                                        } catch (SQLException e) {
1153
                                                throw new JDBCSQLException(e);
1154
                                        }
1155
                                        return Boolean.FALSE;
1156
                                }
1157

    
1158
                        };
1159

    
1160

    
1161

    
1162
                        try {
1163
                                allowAutomaticValues = (Boolean) doConnectionAction(action);
1164
                        } catch (Exception e) {
1165
                                logger.error("Exception checking for automatic integers", e);
1166
                                allowAutomaticValues = Boolean.FALSE;
1167
                        }
1168
                }
1169
                return allowAutomaticValues.booleanValue();
1170
        }
1171

    
1172
        public boolean supportsUnion() {
1173
                if (supportsUnions == null) {
1174
                        ConnectionAction action = new ConnectionAction() {
1175

    
1176
                                public Object action(Connection conn) throws DataException {
1177

    
1178
                                        try {
1179
                                                DatabaseMetaData meta = conn.getMetaData();
1180
                                                return new Boolean(meta.supportsUnion());
1181
                                        } catch (SQLException e) {
1182
                                                throw new JDBCSQLException(e);
1183
                                        }
1184
                                }
1185

    
1186
                        };
1187

    
1188
                        try {
1189
                                supportsUnions = (Boolean) doConnectionAction(action);
1190
                        } catch (Exception e) {
1191
                                logger.error("Exception checking for unions support", e);
1192
                                supportsUnions = Boolean.FALSE;
1193
                        }
1194
                }
1195
                return supportsUnions.booleanValue();
1196
        }
1197

    
1198
        protected String getIdentifierQuoteString() {
1199
                if (identifierQuoteString == null) {
1200
                ConnectionAction action = new ConnectionAction() {
1201

    
1202
                        public Object action(Connection conn) throws DataException {
1203

    
1204
                                try {
1205
                                        DatabaseMetaData meta = conn.getMetaData();
1206
                                        return meta.getIdentifierQuoteString();
1207
                                } catch (SQLException e) {
1208
                                        throw new JDBCSQLException(e);
1209
                                }
1210
                        }
1211

    
1212
                };
1213

    
1214
                try {
1215
                        identifierQuoteString = (String) doConnectionAction(action);
1216
                } catch (Exception e) {
1217
                        logger.error("Exception checking for unions support", e);
1218
                        identifierQuoteString = " ";
1219
                        }
1220
                }
1221
                return identifierQuoteString;
1222
        }
1223

    
1224
        protected boolean isReservedWord(String field) {
1225
                // TODO
1226
                return false;
1227
        }
1228

    
1229
}