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

History | View | Annotate | Download (36.7 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
import org.apache.commons.lang3.StringUtils;
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.MultiPrimitive;
69
import org.gvsig.fmap.geom.exception.CreateGeometryException;
70
/*
71
import org.gvsig.fmap.geom.operation.fromwkb.FromWKB;
72
import org.gvsig.fmap.geom.operation.fromwkb.FromWKBGeometryOperationContext;
73
import org.gvsig.fmap.geom.operation.towkb.ToWKB;
74
import org.gvsig.fmap.geom.operation.towkb.ToWKBOperationContext;
75
*/
76
import org.gvsig.fmap.geom.primitive.Envelope;
77
import org.gvsig.fmap.geom.primitive.Primitive;
78
import org.gvsig.fmap.geom.type.GeometryType;
79
import org.gvsig.tools.dispose.impl.AbstractDisposable;
80
import org.gvsig.tools.exception.BaseException;
81
import org.slf4j.Logger;
82
import org.slf4j.LoggerFactory;
83

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

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

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

    
99
        protected GeometryManager geomManager = null;
100

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

    
104
        private String identifierQuoteString;
105

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

    
114
        }
115

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

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

    
154
        }
155

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

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

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

    
168
        }
169

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

    
191
                                        user.opendDone();
192

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

    
207
        }
208

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

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

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

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

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

    
250
        }
251

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

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

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

    
303
                                        result = action.action(conn);
304

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

    
313
                                        return result;
314

    
315
                                } catch (Exception e) {
316

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

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

    
339
        }
340

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

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

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

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

    
418
                return column;
419

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

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

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

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

    
470
                return column;
471

    
472
        }
473

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

    
489
                // Nothing to do
490

    
491
        }
492

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

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

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

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

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

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

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

    
586

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

    
596

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

    
602
        }
603

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

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

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

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

    
654

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

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

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

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

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

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

    
698
        }
699

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

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

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

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

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

    
723
            }
724

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

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

    
749
            }
750

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

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

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

    
773
        }
774

    
775
    }
776

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

    
789
                // TODO
790
                return null;
791

    
792
        }
793

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

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

    
809
        public class DalValueToJDBCException extends WriteException {
810

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

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

    
843
        }
844

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

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

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

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

    
931
    }
932

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1100

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

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

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

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

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

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

    
1159
                        };
1160

    
1161

    
1162

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

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

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

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

    
1187
                        };
1188

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

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

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

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

    
1213
                };
1214

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

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

    
1230
        protected List createGrantStatements(String tableName, String privilege, String theRoles) {
1231
            if( !tableName.startsWith("\"") ) {
1232
                tableName = "\"" + tableName +"\"";
1233
            }
1234
            List statements = new ArrayList();
1235
            String[] roles = StringUtils.split(theRoles,",");
1236
            for( int i=0; i<roles.length; i++) {
1237
                String statement = "GRANT "+ privilege + " ON TABLE " + tableName + " TO \"" + roles[i] + "\"";
1238
                statements.add(statement);
1239
            }
1240
            return statements;
1241
        } 
1242
        
1243
}